/[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 - (hide 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 weaselp 153 /* 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 rabbi 221 Added by drt@un.bewaff.net - http://c0re.jp/
6 weaselp 153
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 weaselp 162 #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 weaselp 153 #include <unistd.h>
24 weaselp 162 #endif
25 weaselp 153 #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 weaselp 156 #include <assert.h>
33 weaselp 153
34 weaselp 162 #if defined(S_IFDIR) && !defined(S_ISDIR)
35     #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
36     #endif
37    
38 weaselp 180 #ifndef SHORTNAMES
39    
40 weaselp 153 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 weaselp 163 strcatn(tmp, dir, PATHMAX);
49     if (append)
50     strcatn(tmp, append, PATHMAX);
51 weaselp 153
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 weaselp 155 #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 weaselp 153 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 weaselp 155 char basename[MAX_BASENAME];
80     char tmpname[MAX_SUBNAME];
81     char newname[MAX_SUBNAME];
82     int messagesize;
83 weaselp 156 char olddirectory[PATHMAX] = "";
84 weaselp 163 char normalizedmaildir[PATHMAX];
85 weaselp 153
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 weaselp 163 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 weaselp 153 returnValue = -1;
100     goto realend;
101     }
102    
103 weaselp 155 messagesize = message->length;
104    
105 weaselp 156 /* Step 1: chdir to maildir (and save current dir) */
106     if (getcwd(olddirectory, PATHMAX) == NULL) {
107 weaselp 154 returnValue = -1;
108     goto realend;
109     }
110 weaselp 156 olddirectory[PATHMAX-1] = '\0';
111 weaselp 163 if(chdir(normalizedmaildir) != 0) {
112 weaselp 153 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 weaselp 155 snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u",
121     time(NULL), getpid(), namecounter++, hostname, messagesize);
122     basename[MAX_BASENAME-1] = '\0';
123 weaselp 163 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 weaselp 153
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 weaselp 155 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 weaselp 153 */
186 weaselp 155 if(unlink(tmpname) != 0) {
187     /* unlinking failed */
188 weaselp 153 returnValue = -1;
189     goto functionExit;
190 weaselp 155 }
191     break;
192 weaselp 153 }
193 weaselp 155
194     if (count > 5) {
195     /* Too many retries - give up */
196     errlog(ERRORMSG, "Can't move message to %s/new/\n", maildir);
197 weaselp 153 returnValue = -1;
198     goto functionExit;
199     }
200 weaselp 155
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 weaselp 163 strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME);
207     strcatn(newname, basename, MAX_SUBNAME);
208 weaselp 153 }
209     #else /* POSIX */
210 weaselp 180 /* On non POSIX systems we simply use rename(). Let's hope DJB
211 weaselp 153 * 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 weaselp 156 assert(olddirectory[0] != '\0');
224     if(chdir(olddirectory) != 0)
225 weaselp 153 returnValue = -1;
226    
227     realend:
228    
229     return returnValue;
230     }
231    
232 weaselp 180 #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 weaselp 153 #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