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

  ViewVC Help
Powered by ViewVC 1.1.5