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

  ViewVC Help
Powered by ViewVC 1.1.5