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

  ViewVC Help
Powered by ViewVC 1.1.5