Commit 0010991c 0010991cc712eb88cdbbe22d9c4021f74a72e4cd by Sergey Poznyakoff

Implemented

1 parent 985fa48d
1 /* GNU Mailutils -- a suite of utilities for electronic mail 1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. 2 Copyright (C) 1999, 2000, 2001, 2002, 2003,
3 2004 Free Software Foundation, Inc.
3 4
4 This library is free software; you can redistribute it and/or 5 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public 6 modify it under the terms of the GNU Lesser General Public
...@@ -15,24 +16,25 @@ ...@@ -15,24 +16,25 @@
15 License along with this library; if not, write to the Free Software 16 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17 18
18 /* First draft by Jeff Bailey based on mbox by Alain Magloire */ 19 /* First draft by Sergey Poznyakoff */
19 20
20 #ifdef HAVE_CONFIG_H 21 #ifdef HAVE_CONFIG_H
21 # include <config.h> 22 # include <config.h>
22 #endif 23 #endif
23 24
25 #ifdef ENABLE_MAILDIR
26
27 #include <sys/types.h>
24 #include <stdlib.h> 28 #include <stdlib.h>
25 #include <stdio.h> 29 #include <stdio.h>
26 #include <time.h> 30 #include <time.h>
27 #include <sys/stat.h> 31 #include <sys/stat.h>
32 #include <sys/time.h>
28 #include <fcntl.h> 33 #include <fcntl.h>
29 #include <unistd.h> 34 #include <unistd.h>
30 #include <signal.h>
31 #include <time.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <limits.h>
35 #include <errno.h> 35 #include <errno.h>
36 #include <ctype.h>
37 #include <dirent.h>
36 38
37 #ifdef WITH_PTHREAD 39 #ifdef WITH_PTHREAD
38 # ifdef HAVE_PTHREAD_H 40 # ifdef HAVE_PTHREAD_H
...@@ -41,184 +43,597 @@ ...@@ -41,184 +43,597 @@
41 # endif 43 # endif
42 #endif 44 #endif
43 45
44 #ifdef HAVE_ALLOCA_H 46 #include <string.h>
45 # include <alloca.h>
46 #endif
47
48 #ifdef HAVE_STRINGS_H 47 #ifdef HAVE_STRINGS_H
49 # include <strings.h> 48 # include <strings.h>
50 #endif 49 #endif
51 50
52 #include <mailbox0.h>
53 #include <registrar0.h>
54
55 #include <mailutils/address.h>
56 #include <mailutils/attribute.h> 51 #include <mailutils/attribute.h>
57 #include <mailutils/body.h> 52 #include <mailutils/body.h>
58 #include <mailutils/debug.h> 53 #include <mailutils/debug.h>
59 #include <mailutils/envelope.h> 54 #include <mailutils/envelope.h>
60 #include <mailutils/errno.h>
61 #include <mailutils/error.h> 55 #include <mailutils/error.h>
62 #include <mailutils/header.h> 56 #include <mailutils/header.h>
63 #include <mailutils/locker.h>
64 #include <mailutils/message.h> 57 #include <mailutils/message.h>
65 #include <mailutils/mutil.h> 58 #include <mailutils/mutil.h>
66 #include <mailutils/observer.h>
67 #include <mailutils/property.h> 59 #include <mailutils/property.h>
68 #include <mailutils/stream.h> 60 #include <mailutils/stream.h>
69 #include <mailutils/url.h> 61 #include <mailutils/url.h>
62 #include <mailutils/observer.h>
63 #include <mailutils/errno.h>
64 #include <mailbox0.h>
65 #include <registrar0.h>
66 #include <amd.h>
70 67
71 /* Mailbox concrete implementation. */ 68 struct _maildir_message
72 static int maildir_open __P ((mailbox_t, int)); 69 {
73 static int maildir_close __P ((mailbox_t)); 70 struct _amd_message amd_message;
74 static void maildir_destroy __P ((mailbox_t)); 71 int newflag;
75 static int maildir_get_message __P ((mailbox_t, size_t, message_t *)); 72 char *file_name;
76 /* static int maildir_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */ 73 };
77 static int maildir_append_message __P ((mailbox_t, message_t)); 74
78 static int maildir_messages_count __P ((mailbox_t, size_t *)); 75
79 static int maildir_messages_recent __P ((mailbox_t, size_t *)); 76 /* Attribute handling.
80 static int maildir_message_unseen __P ((mailbox_t, size_t *)); 77 FIXME: P (Passed), D (Draft) and F (Flagged) are not handled */
81 static int maildir_expunge __P ((mailbox_t)); 78
82 static int maildir_save_attributes __P ((mailbox_t)); 79 static struct info_map {
83 static int maildir_uidvalidity __P ((mailbox_t, unsigned long *)); 80 char letter;
84 static int maildir_uidnext __P ((mailbox_t, size_t *)); 81 int flag;
85 static int maildir_scan __P ((mailbox_t, size_t, size_t *)); 82 } info_map[] = {
86 static int maildir_is_updated __P ((mailbox_t)); 83 { 'R', MU_ATTRIBUTE_READ },
87 static int maildir_get_size __P ((mailbox_t, off_t *)); 84 { 'S', MU_ATTRIBUTE_SEEN },
85 { 'T', MU_ATTRIBUTE_DELETED },
86 { 0 },
87 };
88 #define info_map_size (sizeof (info_map) / sizeof (info_map[0]))
88 89
89 int 90 static int
90 _mailbox_maildir_init (mailbox_t mailbox) 91 info_map_letter (int c)
91 { 92 {
93 struct info_map *p;
92 94
93 if (mailbox == NULL) 95 for (p = info_map; p < info_map + info_map_size; p++)
94 return EINVAL; 96 if (p->letter == c)
97 return p->flag;
98 return 0;
99 }
95 100
96 /* Overloading the defaults. */ 101 /* NOTE: BUF must be at least 7 bytes long */
97 mailbox->_destroy = maildir_destroy; 102 static int
103 flags_to_info (int flags, char *buf)
104 {
105 struct info_map *p;
106
107 for (p = info_map; p < info_map + info_map_size; p++)
108 if (p->flag & flags)
109 *buf++ = p->letter;
110 *buf = 0;
111 return 0;
112 }
98 113
99 mailbox->_open = maildir_open; 114 static int
100 mailbox->_close = maildir_close; 115 info_to_flags (char *buf)
116 {
117 int flags = 0;
118 struct info_map *p;
101 119
102 /* Overloading of the entire mailbox object methods. */ 120 for (p = info_map; p < info_map + info_map_size; p++)
103 mailbox->_get_message = maildir_get_message; 121 if (strchr (buf, p->letter))
104 mailbox->_append_message = maildir_append_message; 122 flags |= p->flag;
105 mailbox->_messages_count = maildir_messages_count; 123 return 0;
106 mailbox->_messages_recent = maildir_messages_recent; 124 }
107 mailbox->_message_unseen = maildir_message_unseen;
108 mailbox->_expunge = maildir_expunge;
109 mailbox->_save_attributes = maildir_save_attributes;
110 mailbox->_uidvalidity = maildir_uidvalidity;
111 mailbox->_uidnext = maildir_uidnext;
112 125
113 mailbox->_scan = maildir_scan; 126
114 mailbox->_is_updated = maildir_is_updated; 127 static int
128 maildir_message_cmp (struct _amd_message *a, struct _amd_message *b)
129 {
130 unsigned long na = strtoul (((struct _maildir_message *) a)->file_name,
131 NULL, 10);
132 unsigned long nb = strtoul (((struct _maildir_message *) b)->file_name,
133 NULL, 10);
134 if (na > nb)
135 return 1;
136 if (na < nb)
137 return -1;
138 return 0;
139 }
115 140
116 mailbox->_get_size = maildir_get_size; 141 void
142 msg_free (struct _amd_message *amsg)
143 {
144 struct _maildir_message *mp = (struct _maildir_message *) amsg;
145 free (mp->file_name);
146 }
117 147
118 return 0; /* okdoke */ 148 char *
149 maildir_gethostname ()
150 {
151 char hostname[256];
152 char *hp;
153 char *p;
154 size_t s;
155
156 if (gethostname (hostname, sizeof hostname) < 0)
157 strcpy (hostname, "localhost");
158
159 for (s = 0, p = hostname; *p; p++)
160 if (*p == '/' || *p == ':')
161 s += 4;
162
163 if (s)
164 {
165 char *q;
166
167 hp = malloc (strlen (hostname)) + s + 1;
168 for (p = hostname, q = hp; *p; p++)
169 switch (*p)
170 {
171 case '/':
172 memcpy (q, "\\057", 4);
173 q += 4;
174 break;
175
176 case ':':
177 memcpy (q, "\\072", 4);
178 q += 4;
179 break;
180
181 default:
182 *q++ = *p++;
183 }
184 *q = 0;
185 }
186 else
187 hp = strdup (hostname);
188 return hp;
119 } 189 }
120 190
121 /* Destruct maildir setup */ 191 int
122 static void 192 read_random (void *buf, size_t size)
123 maildir_destroy (mailbox_t mailbox)
124 { 193 {
125 return; 194 int rc;
195 int fd = open ("/dev/urandom", O_RDONLY);
196 if (fd == -1)
197 return -1;
198 rc = read (fd, buf, size);
199 close (fd);
200 return rc != size;
126 } 201 }
127 202
128 /* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall 203 #define PERMS 0700
129 back to normal file. */ 204 #define TMPSUF "tmp"
130 static int 205 #define CURSUF "cur"
131 maildir_open (mailbox_t mailbox, int flags) 206 #define NEWSUF "new"
207
208 static char *
209 mkfilename (char *directory, char *suffix, char *name)
132 { 210 {
133 return -1; 211 size_t size = strlen (directory) + 1 + strlen (suffix) + 1;
212 char *tmp;
213
214 if (name)
215 size += 1 + strlen (name);
216
217 tmp = malloc (size);
218 sprintf (tmp, "%s/%s", directory, suffix);
219 if (name)
220 {
221 strcat (tmp, "/");
222 strcat (tmp, name);
223 }
224 return tmp;
134 } 225 }
135 226
136 static int 227 static char *
137 maildir_close (mailbox_t mailbox) 228 mk_info_filename (char *directory, char *suffix, char *name, int flags)
138 { 229 {
139 return -1; 230 char fbuf[9];
231 char *tmp;
232 int namelen;
233 size_t size;
234
235 tmp = strchr (name, ':');
236 if (!tmp)
237 namelen = strlen (name);
238 else
239 namelen = tmp - name;
240
241 size = strlen (directory)
242 + 1 + strlen (suffix)
243 + 1 + namelen + 1;
244
245 flags_to_info (flags, fbuf);
246 size += 3 + strlen (fbuf);
247
248 tmp = malloc (size);
249 if (fbuf[0])
250 sprintf (tmp, "%s/%s/%*.*s:2", directory, suffix, namelen, namelen, name);
251 else
252 sprintf (tmp, "%s/%s/%*.*s:2,%s", directory, suffix, namelen, namelen, name, fbuf);
253 return tmp;
140 } 254 }
141 255
142 /* Cover function that call the real thing, maildir_scan(), with 256 char *
143 notification set. */ 257 maildir_uniq (struct _amd_data *amd, int fd)
144 static int
145 maildir_scan (mailbox_t mailbox, size_t msgno, size_t *pcount)
146 { 258 {
147 return 0; 259 char buffer[PATH_MAX];
260 int ind = 0;
261 #define FMT(fmt,val) do {\
262 ind += snprintf(buffer+ind, sizeof buffer-ind, fmt, val); \
263 } while (0)
264 #define PFX(pfx,fmt,val) do {\
265 if (ind < sizeof buffer-1) {\
266 buffer[ind++] = pfx;\
267 FMT(fmt,val);\
268 }\
269 } while (0)
270 #define COPY(s) do {\
271 char *p; \
272 for (p = s; ind < sizeof buffer-1 && *p;) \
273 buffer[ind++] = *p++;\
274 } while (0);
275 char *hostname = maildir_gethostname ();
276 struct timeval tv;
277 unsigned long n;
278 struct stat st;
279
280 gettimeofday (&tv, NULL);
281 FMT ("%lu", tv.tv_sec);
282 COPY (".");
283
284 if (read_random (&n, 32))
285 PFX ('R', "%lX", n);
286
287 if (fd > 0 && fstat (fd, &st) == 0)
288 {
289 PFX ('I', "%lX", (unsigned long) st.st_ino);
290 PFX ('V', "%lX", (unsigned long) st.st_dev);
291 }
292
293 PFX ('M', "%lu", tv.tv_usec);
294 PFX ('P', "%lu", (unsigned long) getpid ());
295 PFX ('Q', "%lu", (unsigned long) amd->msg_count);
296 PFX ('.', "%s", hostname);
297 free (hostname);
298
299 buffer[ind] = 0;
300
301 return strdup (buffer);
148 } 302 }
149 303
150 /* FIXME: How to handle a shrink ? meaning, the &^$^@%#@^& user start two 304 char *
151 browsers and deleted emails in one session. My views is that we should 305 maildir_message_name (struct _amd_message *amsg, int deleted)
152 scream bloody murder and hunt them with a machette. But for now just play
153 dumb, but maybe the best approach is to pack our things and leave
154 .i.e exit()/abort(). */
155 static int
156 maildir_is_updated (mailbox_t mailbox)
157 { 306 {
158 return -1; 307 struct _maildir_message *msg = (struct _maildir_message *) amsg;
308 return mkfilename (amsg->amd->name, msg->newflag ? NEWSUF : CURSUF, msg->file_name);
159 } 309 }
160 310
161 static int 311 static void
162 maildir_expunge (mailbox_t mailbox) 312 maildir_msg_free (struct _amd_message *amsg)
163 { 313 {
164 return -1; 314 struct _maildir_message *mp = (struct _maildir_message *) amsg;
315 free (mp->file_name);
165 } 316 }
166 317
167 static int 318 /* According to http://www.qmail.org/qmail-manual-html/man5/maildir.html
168 maildir_get_size (mailbox_t mailbox, off_t *psize) 319 a file in tmp may be safely removed if it has not been accessed in 36
320 hours */
321
322 static void
323 maildir_delete_file (char *dirname, char *filename)
169 { 324 {
170 return -1; 325 struct stat st;
326 char *name = mkfilename (dirname, filename, NULL);
327
328 if (stat (name, &st) == 0)
329 {
330 if (time (NULL) - st.st_atime > 36 * 3600)
331 remove (name);
332 }
333 free (name);
171 } 334 }
172 335
173 static int 336
174 maildir_save_attributes (mailbox_t mailbox) 337
338 int
339 maildir_opendir (DIR **dir, char *name, int permissions)
175 { 340 {
176 return -1; 341 *dir = opendir (name);
342 if (!*dir)
343 {
344 if (errno == ENOENT)
345 {
346 if (mkdir (name, permissions))
347 return errno;
348 *dir = opendir (name);
349 if (!*dir)
350 return errno;
351 }
352 else
353 return errno;
354 }
355 return 0;
177 } 356 }
178 357
358
359 #define NTRIES 30
360
361 /* Delivery to "dir/new" */
362
179 static int 363 static int
180 maildir_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) 364 maildir_msg_init (struct _amd_data *amd, struct _amd_message *amm)
181 { 365 {
182 return -1; 366 struct _maildir_message *msg = (struct _maildir_message *) amm;
367 char *name;
368 struct stat st;
369 int i;
370
371 /* chdir (amd->name); FIXME */
372 name = maildir_uniq (amd, -1);
373
374 for (i = 0; i < NTRIES; i++)
375 {
376 if (stat (name, &st) < 0 && errno == ENOENT)
377 {
378 msg->file_name = name;
379 return 0;
380 }
381 sleep (2);
382 }
383
384 free (name);
385 return MU_ERR_BAD_RESUMPTION;
183 } 386 }
184 387
185 static int 388 static int
186 maildir_append_message (mailbox_t mailbox, message_t msg) 389 maildir_msg_finish_delivery (struct _amd_data *amd, struct _amd_message *amm)
187 { 390 {
188 return -1; 391 struct _maildir_message *msg = (struct _maildir_message *) amm;
392 char *oldname = mkfilename (amd->name, TMPSUF, msg->file_name);
393 char *newname = mkfilename (amd->name, NEWSUF, msg->file_name);
394
395 unlink (newname);
396 if (link (oldname, newname))
397 {
398 unlink (oldname);
399 msg->newflag = 1;
400 }
401 free (oldname);
402 free (newname);
403 return 0;
189 } 404 }
190 405
191 static int 406
192 maildir_messages_count (mailbox_t mailbox, size_t *pcount) 407 /* Maldir scanning */
408
409 int
410 maildir_flush (struct _amd_data *amd)
193 { 411 {
194 return -1; 412 int rc;
413 DIR *dir;
414 struct dirent *entry;
415 char *tmpname = malloc (strlen (amd->name) + sizeof TMPSUF);
416
417 strcpy (tmpname, amd->name);
418 strcat (tmpname, TMPSUF);
419
420 rc = maildir_opendir (&dir, tmpname, PERMS);
421 if (rc)
422 {
423 free (tmpname);
424 return rc;
425 }
426
427 while ((entry = readdir (dir)))
428 {
429 switch (entry->d_name[0])
430 {
431 case '.':
432 break;
433
434 default:
435 maildir_delete_file (tmpname, entry->d_name);
436 break;
437 }
438 }
439
440 free (tmpname);
441
442 closedir (dir);
443 return 0;
195 } 444 }
196 445
197 /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN 446 int
198 ('O' in the Status header), i.e. a message that is first seen 447 maildir_deliver_new (struct _amd_data *amd, DIR *dir)
199 by the current session (see attributes.h) */
200 static int
201 maildir_messages_recent (mailbox_t mailbox, size_t *pcount)
202 { 448 {
203 return -1; 449 struct dirent *entry;
450
451 while ((entry = readdir (dir)))
452 {
453 char *oldname, *newname;
454
455 switch (entry->d_name[0])
456 {
457 case '.':
458 break;
459
460 default:
461 oldname = mkfilename (amd->name, NEWSUF, entry->d_name);
462 newname = mk_info_filename (amd->name, CURSUF, entry->d_name, 0);
463 rename (oldname, newname);
464 free (oldname);
465 free (newname);
466 }
467 }
468 return 0;
204 } 469 }
205 470
206 /* An "unseen" message is the one that has not been read yet */ 471 static struct _maildir_message *
207 static int 472 maildir_message_lookup (struct _amd_data *amd, char *file_name)
208 maildir_message_unseen (mailbox_t mailbox, size_t *pmsgno)
209 { 473 {
210 return -1; 474 struct _maildir_message *msg;
475 char *p = strchr (file_name, ':');
476 size_t length = p ? p - file_name : strlen (file_name);
477 int rc;
478
479 for (msg = (struct _maildir_message *) amd->msg_head;
480 msg
481 && strlen (msg->file_name) >= length
482 && (rc = memcmp (msg->file_name, file_name, length)) < 0;
483 msg = (struct _maildir_message *) msg->amd_message.next)
484 ;
485 return rc == 0 ? msg : NULL;
211 } 486 }
212 487
213 static int 488 static int
214 maildir_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) 489 maildir_scan_dir (struct _amd_data *amd, DIR *dir)
215 { 490 {
216 return -1; 491 struct _maildir_message *msg;
492 struct dirent *entry;
493
494 while ((entry = readdir (dir)))
495 {
496 char *p;
497 int insert;
498
499 switch (entry->d_name[0])
500 {
501 case '.':
502 break;
503
504 default:
505 msg = maildir_message_lookup (amd, entry->d_name);
506 if (msg)
507 {
508 free (msg->file_name);
509 msg->newflag = 0;
510 insert = 0;
511 }
512 else
513 {
514 msg = calloc (1, sizeof(*msg));
515 insert = 1;
516 }
517
518 msg->file_name = strdup (entry->d_name);
519
520 p = strchr (msg->file_name, ':');
521 if (p && strcmp (p+1, "2,") == 0)
522 msg->amd_message.attr_flags = info_to_flags (p+3);
523 else
524 msg->amd_message.attr_flags = 0;
525 msg->amd_message.deleted = msg->amd_message.attr_flags & MU_ATTRIBUTE_DELETED;
526 if (insert)
527 _amd_message_insert (amd, (struct _amd_message*) msg);
528 }
529 }
530 return 0;
217 } 531 }
218 532
219 static int 533 static int
220 maildir_uidnext (mailbox_t mailbox, size_t *puidnext) 534 maildir_scan0 (mailbox_t mailbox, size_t msgno ARG_UNUSED, size_t *pcount,
535 int do_notify)
221 { 536 {
222 return -1; 537 struct _amd_data *amd = mailbox->data;
538 DIR *dir;
539 int status = 0;
540 char *name;
541 struct stat st;
542
543 if (amd == NULL)
544 return EINVAL;
545
546 monitor_wrlock (mailbox->monitor);
547
548 /* 1st phase: Flush tmp/ */
549 maildir_flush (amd);
550
551 /* 2nd phase: Scan and deliver messages from new */
552 name = mkfilename (amd->name, NEWSUF, NULL);
553
554 status = maildir_opendir (&dir, name, PERMS);
555 if (status == 0)
556 {
557 maildir_deliver_new (amd, dir);
558 closedir (dir);
559 }
560 free (name);
561
562 name = mkfilename (amd->name, CURSUF, NULL);
563 /* 3rd phase: Scan cur/ */
564 status = maildir_opendir (&dir, name, PERMS);
565 if (status == 0)
566 {
567 status = maildir_scan_dir (amd, dir);
568 closedir (dir);
569 }
570 free (name);
571
572 if (do_notify)
573 {
574 struct _amd_message *mp;
575 for (mp = amd->msg_head; mp; mp = mp->next)
576 {
577 DISPATCH_ADD_MSG(mailbox, amd);
578 }
579 }
580
581 if (stat (amd->name, &st) == 0)
582 amd->mtime = st.st_mtime;
583
584 if (pcount)
585 *pcount = amd->msg_count;
586
587 /* Reset the uidvalidity. */
588 if (amd->msg_count > 0)
589 {
590 if (amd->uidvalidity == 0)
591 {
592 amd->uidvalidity = (unsigned long) time (NULL);
593 /* FIXME amd->uidnext = amd->msg_count + 1;*/
594 /* Tell that we have been modified for expunging. */
595 if (amd->msg_head)
596 {
597 amd_message_stream_open (amd->msg_head);
598 amd_message_stream_close (amd->msg_head);
599 amd->msg_head->attr_flags |= MU_ATTRIBUTE_MODIFIED;
600 }
601 }
602 }
603
604 /* Clean up the things */
605 amd_cleanup (mailbox);
606 return status;
223 } 607 }
608
224 609
610 int
611 _mailbox_maildir_init (mailbox_t mailbox)
612 {
613 int rc;
614 struct _amd_data *amd;
615
616 rc = amd_init_mailbox (mailbox, sizeof (struct _amd_data), &amd);
617 if (rc)
618 return rc;
619
620 amd->msg_size = sizeof (struct _maildir_message);
621 amd->msg_free = maildir_msg_free;
622 amd->msg_init_delivery = maildir_msg_init;
623 amd->msg_finish_delivery = maildir_msg_finish_delivery;
624 amd->msg_file_name = maildir_message_name;
625 amd->scan0 = maildir_scan0;
626 amd->msg_cmp = maildir_message_cmp;
627 amd->message_uid = NULL; /* FIXME */
628 amd->next_uid = NULL; /* FIXME */
629
630 /* Set our properties. */
631 {
632 property_t property = NULL;
633 mailbox_get_property (mailbox, &property);
634 property_set_value (property, "TYPE", "MAILDIR", 1);
635 }
636
637 return 0;
638 }
639 #endif
......