/[pkg-mixmaster]/trunk/Mix/Src/maildir.c
ViewVC logotype

Contents of /trunk/Mix/Src/maildir.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 710 - (show annotations) (download)
Wed Jan 21 00:47:21 2004 UTC (9 years, 4 months ago) by rabbi
File MIME type: text/plain
File size: 8273 byte(s)
Updated copyright dates, doc issues, misc fixes.
1 /* Mixmaster version 3.0 -- (C) 1999 - 2004 Anonymizer Inc. and others.
2
3 Mixmaster may be redistributed and modified under certain conditions.
4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
5 ANY KIND, either express or implied. See the file COPYRIGHT for
6 details.
7
8 Maildir support routines
9 $Id: $ */
10
11
12 /* Maildir support for Mixmaster 3 - see
13 http://www.qmail.org/man/man5/maildir.html and
14 http://cr.yp.to/proto/maildir.html
15
16 Added by and (C) 2001 Doobee R. Tzeck
17 drt@un.bewaff.net - http://c0re.jp/
18
19 To test it try:
20 $ gcc maildir.c -DUNITTEST -o test_maildir
21 $ ./test_maildir
22 this should print a single line saying "OK"
23 */
24
25 #include "mix3.h"
26
27 #ifdef WIN32
28 #include <io.h>
29 #include <direct.h>
30 #include <process.h>
31 #define sleep(s) Sleep(s*1000)
32 #define S_IWUSR _S_IWRITE
33 #define S_IRUSR _S_IREAD
34 #else /* end of WIN32 */
35 #include <unistd.h>
36 #endif /* else not WIN32 */
37 #include <fcntl.h>
38 #include <time.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <errno.h>
43 #include <stdarg.h>
44 #include <assert.h>
45
46 #if defined(S_IFDIR) && !defined(S_ISDIR)
47 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
48 #endif /* defined(S_IFDIR) && !defined(S_ISDIR) */
49
50 #ifndef SHORTNAMES
51
52 static unsigned long namecounter = 0;
53
54 int checkDirectory(char *dir, char *append, int create) {
55 char tmp[PATHMAX];
56 struct stat buf;
57 int err;
58
59 tmp[0] = '\0';
60 strcatn(tmp, dir, PATHMAX);
61 if (append)
62 strcatn(tmp, append, PATHMAX);
63
64 err = stat(tmp, &buf);
65 if (err == -1) {
66 if (create) {
67 #ifndef POSIX
68 err = mkdir(tmp);
69 #else /* end of not POSIX */
70 err = mkdir(tmp, S_IRWXU);
71 #endif /* else if POSIX */
72 if (err == 0)
73 errlog(NOTICE, "Creating directory %s.\n", tmp);
74 } else
75 err = 1;
76 } else if (!S_ISDIR(buf.st_mode))
77 err = -1;
78
79 return err;
80 }
81
82 /* Write "message" to "maildir", retunr 0 on success, -1 on failure */
83 #define MAX_BASENAME 113 /* actual length should be smaller than 111 bytes */
84 #define MAX_SUBNAME 123 /* actual length should be smaller than 115 bytes */
85 int maildirWrite(char *maildir, BUFFER *message, int create) {
86 int fd;
87 int count;
88 int returnValue;
89 char hostname[64];
90 struct stat statbuf;
91 char basename[MAX_BASENAME];
92 char tmpname[MAX_SUBNAME];
93 char newname[MAX_SUBNAME];
94 int messagesize;
95 char olddirectory[PATHMAX] = "";
96 char normalizedmaildir[PATHMAX];
97
98 /* Declare a handler for SIGALRM so we can time out. */
99 /* set_handler(SIGALRM, alarm_handler); */
100 /* alarm(86400); */
101
102 hostname[0] = '\0';
103 gethostname(hostname, 63);
104 hostname[63] = '\0';
105
106 mixfile(normalizedmaildir, maildir);
107 if ((checkDirectory(normalizedmaildir, NULL, create) != 0) ||
108 (checkDirectory(normalizedmaildir, "tmp", create) != 0) ||
109 (checkDirectory(normalizedmaildir, "cur", create) != 0) ||
110 (checkDirectory(normalizedmaildir, "new", create) != 0)) {
111 returnValue = -1;
112 goto realend;
113 }
114
115 messagesize = message->length;
116
117 /* Step 1: chdir to maildir (and save current dir) */
118 if (getcwd(olddirectory, PATHMAX) == NULL) {
119 returnValue = -1;
120 goto realend;
121 }
122 olddirectory[PATHMAX-1] = '\0';
123 if(chdir(normalizedmaildir) != 0) {
124 returnValue = -1;
125 goto functionExit;
126 }
127
128 /* Step 2: Stat the temporary file. Wait for ENOENT as a response. */
129 for (count = 0;; count++) {
130 tmpname[0] = '\0';
131 newname[0] = '\0';
132 snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u",
133 time(NULL), getpid(), namecounter++, hostname, messagesize);
134 basename[MAX_BASENAME-1] = '\0';
135 strcatn(tmpname, "tmp" DIRSEPSTR, MAX_SUBNAME);
136 strcatn(tmpname, basename, MAX_SUBNAME);
137 strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME);
138 strcatn(newname, basename, MAX_SUBNAME);
139
140 if (stat(tmpname, &statbuf) == 0)
141 errno = EEXIST;
142 else if (errno == ENOENT) {
143 /* Step 4: create the file (at least try) */
144 fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
145 if (fd >= 0)
146 break; /* we managed to open the file */
147 }
148
149 if (count > 5) {
150 /* Too many retries - give up */
151 errlog(ERRORMSG, "Can't create message in %s\n", maildir);
152 returnValue = -1;
153 goto functionExit;
154 }
155
156 /* Step 3: sleep and retry */
157 sleep(2);
158 }
159
160 /* Step 5: write file */
161 if(write(fd, message->data, message->length) != message->length) {
162 returnValue = -1;
163 goto functionExit;
164 }
165
166 /* on NFS this could fail */
167 #ifndef WIN32
168 if((fsync(fd) != 0) || (close(fd) != 0)) {
169 #else /* end of not WIN32 */
170 if((_commit(fd) != 0) || (close(fd) != 0)) {
171 #endif /* else if WIN32 */
172 returnValue = -1;
173 goto functionExit;
174 }
175
176 /* Step 6: move message to 'cur' */
177 #ifdef POSIX
178 for (count = 0;; count++) {
179 if(link(tmpname, newname) != 0) {
180 if (errno == EXDEV || errno == EPERM) {
181 /* We probably are on coda or some other filesystem that does not allow
182 * hardlinks. rename() the file instead of link() and unlink()
183 * I know, It's evil (PP).
184 */
185 if (rename(tmpname, newname) != 0) {
186 returnValue = -1;
187 goto functionExit;
188 };
189 break;
190 } else if (errno != EEXIST) {
191 returnValue = -1;
192 goto functionExit;
193 }
194 } else {
195 /* We successfully linked the message in new/. Now let's get
196 * rid of our tmp/ entry
197 */
198 if(unlink(tmpname) != 0) {
199 /* unlinking failed */
200 returnValue = -1;
201 goto functionExit;
202 }
203 break;
204 }
205
206 if (count > 5) {
207 /* Too many retries - give up */
208 errlog(ERRORMSG, "Can't move message to %s/new/\n", maildir);
209 returnValue = -1;
210 goto functionExit;
211 }
212
213 sleep(2);
214 newname[0] = '\0';
215 snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u",
216 time(NULL), getpid(), namecounter++, hostname, messagesize);
217 basename[MAX_BASENAME-1] = '\0';
218 strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME);
219 strcatn(newname, basename, MAX_SUBNAME);
220 }
221 #else /* end of POSIX */
222 /* On non POSIX systems we simply use rename(). Let's hope DJB
223 * never finds out
224 */
225 if (rename(tmpname, newname) != 0) {
226 returnValue = -1;
227 goto functionExit;
228 };
229 #endif /* else if not POSIX */
230
231 returnValue = 0;
232
233 functionExit:
234 /* return to original directory */
235 assert(olddirectory[0] != '\0');
236 if(chdir(olddirectory) != 0)
237 returnValue = -1;
238
239 realend:
240
241 return returnValue;
242 }
243
244 #else /* end of SHORTNAMES */
245 int maildirWrite(char *maildir, BUFFER *message, int create) {
246 {
247 errlog(ERRORMSG, "Maildir delivery does not work with SHORTNAMES.\n");
248 return -1;
249 }
250 #endif /* else if not SHORTNAMES */
251
252
253 #ifdef UNITTEST
254
255 #ifdef NDEBUG
256 #undef NDEBUG
257 #endif /* NDEBUG */
258
259 #include <dirent.h>
260
261 /* mock-up of errlog for unittest */
262 void errlog(int type, char *fmt,...)
263 {
264 va_list ap;
265
266 va_start(ap, fmt);
267 vfprintf(stderr, fmt, ap);
268 va_end(ap);
269 }
270
271 /* main for unittest */
272 int main()
273 {
274 int i, count = 23;
275 int fd;
276 DIR *d;
277 struct dirent *de;
278 BUFFER message;
279 char text[] = "From: nobody@un.bewaff.net\nTo: hackers@c0re.jp\nSubject: testing\n\nthis is just a test\n";
280 char buf[1024];
281
282 /* create buffer with test data */
283 message.data = text;
284 message.length = strlen(text);
285
286 /* write <count> messages to maildir */
287 for(i = 0; i < count; i++)
288 assert(maildirWrite("Maildir.test_maildir", message, 1) == 0);
289
290 /* read them back */
291 assert((d = opendir("Maildir.test_maildir/new")) != NULL);
292 for (i = 0; i < count + 2; i++)
293 {
294 de = readdir(d);
295 if(de->d_name[0] != '.')
296 {
297 buf[0] = '\0';
298 strcat(buf, "Maildir.test_maildir/new/");
299 strcat(buf, de->d_name);
300 fd = open(buf, O_RDONLY);
301 assert(unlink(buf) == 0);
302 assert(read(fd, buf, strlen(text)) == strlen(text));
303 buf[strlen(text)] = '\0';
304 /* check if they match the original message */
305 assert(strcmp(text, buf) == 0);
306 close(fd);
307 }
308 }
309
310 /* no files left in directory? */
311 assert(readdir(d) == NULL);
312
313 /* delete maildir */
314 assert(rmdir("Maildir.test_maildir/tmp") == 0);
315 assert(rmdir("Maildir.test_maildir/new") == 0);
316 assert(rmdir("Maildir.test_maildir/cur") == 0);
317 assert(rmdir("Maildir.test_maildir") == 0);
318
319 /* check if writing to a non existant maildir yilds an error */
320 assert(maildirWrite("Maildir.test_maildir", &message, 0) == -1);
321
322 puts("OK");
323 }
324 #endif /* UNITTEST */

  ViewVC Help
Powered by ViewVC 1.1.5