imapext-2007

view src/osdep/unix/maildir.c @ 1:28a55bc1110c

[mq]: imapext
author yuuji@gentei.org
date Mon, 14 Sep 2009 19:23:11 +0900
parents
children
line source
1 /*
2 * Maildir Module for PINE 4.0x - fourth release, use with CARE!
3 *
4 * Author: Mattias Larsson <ml@techno.org>
5 *
6 * Version: 21.07.98
7 *
8 * Please read the README.maildir file before using this module!
9 *
10 * If you have any questions, please e-mail ml@techno.org
11 *
12 * Multiple inboxes patch by Dean Gaudet <dgaudet@arctic.org>
13 *
14 * =================================================
15 *
16 * Based on the IMAP2 maildir routines by:
17 *
18 * Author: Eric Green
19 * Bloodhounds International Inc.
20 * thrytis@imaxx.net
21 *
22 * Additional contributions from:
23 * Aidas Kasparas (kaspar@soften.ktu.lt)
24 *
25 * Date: 27 April 1997
26 * Last Edited: 13 June 1997
27 *
28 * Based (heavily) on mh.c and other c-client library files by Mark Crispin:
29 *
30 * Mark Crispin
31 * Networks and Distributed Computing
32 * Computing & Communications
33 * University of Washington
34 * Administration Building, AG-44
35 * Seattle, WA 98195
36 * Internet: MRC@CAC.Washington.EDU
37 *
38 * Copyright 1995 by the University of Washington
39 *
40 * Permission to use, copy, modify, and distribute this software and its
41 * documentation for any purpose and without fee is hereby granted, provided
42 * that the above copyright notice appears in all copies and that both the
43 * above copyright notice and this permission notice appear in supporting
44 * documentation, and that the name of the University of Washington not be
45 * used in advertising or publicity pertaining to distribution of the software
46 * without specific, written prior permission. This software is made
47 * available "as is", and
48 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
49 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
50 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
51 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
52 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
53 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
54 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
55 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
56 *
57 */
59 /* CONFIGURABLE OPTIONS - PLEASE CHECK THESE OUT */
61 #define NO_MAILDIR_FIDDLE /* disallow Maildir with Maildir in the
62 name. This is useful in an ISP setup
63 using the IMAP daemon. #undef it if you
64 are running a normal pine and know what
65 you are doing */
67 /* #define NO_ABSOLUTE_PATHS /* if you define this, all paths
68 use your HOMEDIR is the root instead
69 of the actual root of the machine. This
70 is also useful in an ISP setup with
71 IMAP */
73 #undef NO_UID_VALIDITIY /* define this if you want the UID's not
74 to be persistent over sessions. Use this
75 if you use another client to read the
76 maildir that screws up the special way
77 in which we store UIDs. Do not enable
78 unless you are sure you need it. */
80 /* END CONFIGURATION */
82 #define MTA_DEBUG /* debugging sent to stdout */
83 #undef MTA_DEBUG
85 #include <stdio.h>
86 #include <ctype.h>
87 #include <errno.h>
88 extern int errno; /* just in case */
89 #include "mail.h"
90 #include "osdep.h"
91 #include <pwd.h>
92 #include <sys/stat.h>
93 #include <sys/time.h>
94 #include <sys/types.h>
95 #include <dirent.h>
96 #include <utime.h>
97 #include "maildir.h"
98 #include "misc.h"
99 #include "dummy.h"
101 /* Driver dispatch used by MAIL */
103 DRIVER maildirdriver = {
104 "maildir", /* driver name */
105 /* driver flags */
106 DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE,
107 (DRIVER *) NIL, /* next driver */
108 maildir_valid, /* mailbox is valid for us */
109 maildir_parameters, /* manipulate parameters */
110 NIL, /* scan mailboxes */
111 maildir_list, /* find mailboxes */
112 maildir_lsub, /* find subscribed mailboxes */
113 maildir_sub, /* subscribe to mailbox */
114 maildir_unsub, /* unsubscribe from mailbox */
115 maildir_create, /* create mailbox */
116 maildir_delete, /* delete mailbox */
117 maildir_rename, /* rename mailbox */
118 NIL, /* status of mailbox */
119 maildir_open, /* open mailbox */
120 maildir_close, /* close mailbox */
121 maildir_fast, /* fetch message "fast" attributes */
122 NIL, /* fetch message flags */
123 NIL, /* fetch overview */
124 NIL, /* fetch message envelopes */
125 maildir_fetchheader, /* fetch message header */
126 maildir_fetchtext, /* fetch message body */
127 NIL, /* fetch partial message text */
128 NIL, /* unique identifier */
129 NIL, /* message number */
130 NIL, /* modify flags */
131 maildir_flagmsg, /* per-message modify flags */
132 NIL, /* search for message based on criteria */
133 NIL, /* sort messages */
134 NIL, /* thread messages */
135 maildir_ping, /* ping mailbox to see if still alive */
136 maildir_check, /* check for new messages */
137 maildir_expunge, /* expunge deleted messages */
138 maildir_copy, /* copy messages to another mailbox */
139 maildir_append, /* append string message to mailbox */
140 maildir_gc /* garbage collect stream */
141 };
143 /* prototype stream */
144 MAILSTREAM maildirproto = {&maildirdriver};
146 /* Check validity of mailbox
147 */
149 DRIVER *maildir_valid (char *name)
150 {
151 return maildir_isvalid(name,T) ? &maildirdriver : NIL;
152 }
154 int maildir_isvalid (char *name,long justname)
155 {
156 char tmp[MAILTMPLEN];
157 struct stat sbuf;
159 if (!name || (!*name) ||
160 ((*name == '#') &&
161 (*(name+1) == 0 ||
162 (*(name+1) != 'm' && *(name+1) != 'M') ||
163 (*(name+2) != 'd' && *(name+1) != 'D') ||
164 *(name+3) != '/')) || (*name == '.'))
165 return NIL;
167 /* okay, anything containing the name Maildir will be ignored
168 this is to prevent anyone from fiddling with their incoming Maildir
169 directly, it should be accessed via the INBOX alias */
171 #ifdef NO_MAILDIR_FIDDLE
172 if (strstr(name, "Maildir")) {
173 return NIL;
174 }
175 #endif
176 /* If we are requested only to check
177 if the name is appropriate then we
178 have done! */
179 if (justname && *name == '#') return T;
182 /* must be valid local mailbox */
183 if ((*name != '*') && (*name != '{') &&
184 maildir_file (tmp,name) &&
185 /* assume its maildir if its a dir */
186 stat (tmp,&sbuf) == 0 && S_ISDIR (sbuf.st_mode))
187 return T;
189 /* INBOX is for default Maildir */
190 if (!strcmp (ucase (strncpy (tmp,name,MAILTMPLEN)), "INBOX") &&
191 (stat (maildir_file (tmp,name),&sbuf) == 0) &&
192 S_ISDIR (sbuf.st_mode))
193 return T;
195 return NIL;
196 }
197 #ifdef QMAIL
198 #ifndef QMAILCONTROL
199 # define QMAILCONTROL "/var/qmail/control"
200 #endif
201 #ifndef DOTQMAIL
202 # ifdef POSTFIX
203 # define DOTQMAIL ".forward"
204 # else
205 # define DOTQMAIL ".qmail"
206 # endif
207 #endif
208 #ifndef XADDR_DELIM
209 # ifdef POSTFIX
210 # define XADDR_DELIM "+"
211 # else
212 # define XADDR_DELIM "-"
213 # endif
214 #endif
216 /* Convert virtual domain user
217 */
218 char* conv_virtualdomain(char *account) {
219 char *dom = strchr(account, '@'), *p;
220 char vd[MAILTMPLEN+1], rewrite[MAILTMPLEN+1], previous[MAILTMPLEN+1];
221 FILE *vdfd;
222 int match=0;
223 snprintf(vd, MAILTMPLEN, "%s/%s", QMAILCONTROL, "virtualdomains");
224 if (NIL == dom) return account;
225 dom++; /* set position of domain part beginning */
226 if (dom && NIL != (vdfd = fopen (vd, "r"))) {
227 char buf[MAILTMPLEN+1], *s;
228 int l = strlen(dom);
229 int L = strlen(account);
230 while ((s=fgets(buf, MAILTMPLEN, vdfd))) {
231 if (p=strchr(s, '#'))
232 *p = '\0'; /* zap comments */
233 if (!strchr(buf, ':'))
234 continue;
235 while (s && (strrchr(s, '\n') || strrchr(s, '\r') || strrchr(s, ' ')))
236 s[strlen(s)-1] = '\0';
237 if (!strncmp(account, s, L) && s[L] == ':' && s[L+1]) { /* user matches */
238 match = 3;
239 snprintf(rewrite, MAILTMPLEN, "%s%s%s", s+L+1, XADDR_DELIM, account);
240 break;
241 }
242 if (!strncmp(dom, s, l) && s[l] == ':' && s[l+1]) { /* domain matches */
243 match = 2;
244 snprintf(rewrite, MAILTMPLEN, "%s%s%s", s+l+1, XADDR_DELIM, account);
245 continue;
246 }
247 if (match < 2 && s[0] == '.') { /* if domain described in wildcard */
248 if (p=strchr(s, ':')) {
249 *p = '\0';
250 if (!strcmp(dom+(strlen(dom)-strlen(s)), s)) {
251 if (match == 0
252 || strlen(previous) < strlen(s)) {
253 match = 1;
254 strncpy(previous, s, MAILTMPLEN);
255 snprintf(rewrite, MAILTMPLEN, "%s%s%s", p+1, XADDR_DELIM, account);
256 }
257 }
258 }
259 }
260 }
261 fclose(vdfd);
262 if (match) {
263 p = strchr(rewrite, '@');
264 /* fprintf(stderr, "m=%d, rwr=[%s]\n", match, rewrite); */
265 if (p) {
266 *p = '\0';
267 }
268 /* fprintf(stderr, "rwr=[%s]\n", rewrite); */
269 s = cpystr(rewrite);
270 memset(vd, 0, sizeof(vd));
271 memset(rewrite, 0, sizeof(rewrite));
272 memset(previous, 0, sizeof(previous));
273 return s;
274 }
275 }
276 return account; /* return itself */
277 }
278 /* -------------------------------------------------
279 Qmail virtual domain hack ends
280 ------------------------------------------------- */
281 #endif
283 char *maildir_getmaildir() {
284 static char dir[MAILTMPLEN];
285 extern char *mymailsuffix();
286 static char qfile[MAILTMPLEN];
287 FILE *qmail;
288 dir[0] = '\0';
289 snprintf(qfile, MAILTMPLEN, "%s/%s", myhomedir(), DOTQMAIL);
290 strncat(qfile, mymailsuffix(), MAILTMPLEN);
291 qmail = fopen(qfile, "r");
292 if (NULL != qmail) { /* if ~/.qmail file exists */
293 char buf[MAILTMPLEN], *s;
294 while ((s=fgets(buf, MAILTMPLEN, qmail))) {
295 /* Skip preceding spaces and trailing newlines */
296 while (*s && (*s == ' ' || *s == '\t')) {s++;}
297 while (*s && (s[strlen(s)-1] == '\r' || s[strlen(s)-1] == '\n')) {
298 s[strlen(s)-1] = '\0';
299 }
300 if (*s == '#') continue;
301 if (s[strlen(s)-1] != '/') continue; /* Skip non maildir format */
302 if (*s == '~' && *(s+1) == '/')
303 *s = '.'; /* Convert `~/' to `./' (for Postfix) */
304 strncpy(dir, s, MAILTMPLEN);
305 break;
306 }
307 fclose(qmail);
308 }
309 #ifdef DEFAULTMAILDIR
310 else {
311 strncpy(dir, DEFAULTMAILDIR, MAILTMPLEN);
312 }
313 #endif
314 if (dir[0]) {
315 return dir;
316 } else {
317 #ifdef DEFAULTMAILDIR
318 snprintf(dir, MAILTMPLEN, "%s/%s/", myhomedir(), DEFAULTMAILDIR);
319 #else
320 snprintf(dir, MAILTMPLEN, "%s/%s/", myhomedir(), MAILDIRPATH);
321 #endif
322 return dir;
323 }
324 }
326 /* Maildir mail generate file string
327 */
329 char *maildir_file (char *dst,char *name)
330 {
331 char tmp[MAILTMPLEN];
333 if (strlen (name) > 3 && /* safe do other comparisons */
334 (*name == '#') &&
335 (name[1] == 'm' || name[1] == 'M') &&
336 (name[2] == 'd' || name[2] == 'D') &&
337 (name[3] == '/'))
338 name += 4;
340 #ifdef NO_ABSOLUTE_PATHS
341 if (*name == '/') {
342 /* we do not want to accept / absolute paths, so lets strip the first
343 / ... */
344 snprintf(dst, MAILTMPLEN, "%s/%s/cur", myhomedir(), name+1);
346 /* strncpy (dst, name, MAILTMPLEN - 2);
347 strncat (dst, "/cur", MAILTMPLEN - 2);
348 dst[MAILTMPLEN - 1] = '\0'; */
349 }
350 else
351 snprintf (dst, MAILTMPLEN, "%s/%s/cur",myhomedir (),
352 strcmp (ucase (strcnpy (tmp, name, MAILTMPLEN)), "INBOX") ? name : maildir_getmaildir());
353 #else
354 if (*name == '/') {
355 strncpy (dst, name, MAILTMPLEN - 2);
356 strncat (dst, "/cur", MAILTMPLEN - 2);
357 dst[MAILTMPLEN - 1] = '\0';
358 }
359 else {
360 char *maildir = maildir_getmaildir();
361 if (*maildir == '/') {
362 snprintf (dst,MAILTMPLEN,"%scur",maildir);
363 } else {
364 snprintf (dst,MAILTMPLEN,"%s/%s/cur",myhomedir (),
365 strcmp (ucase (strncpy (tmp, name, MAILTMPLEN)), "INBOX") ? name : maildir);
366 }
367 }
369 #endif
371 #ifdef MTA_DEBUG
372 printf("maildir_file '%s'\n", dst);
373 #endif
374 return dst;
375 }
377 /* Maildir open
378 */
380 MAILSTREAM *maildir_open (MAILSTREAM *stream)
381 {
382 char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];
384 if (!stream) return &maildirproto;
385 if (LOCAL) { /* recycle stream */
386 maildir_close (stream, 0);
387 stream->dtb = &maildirdriver;
388 mail_free_cache (stream);
389 stream->uid_last = 0; /* default UID validity */
390 stream->uid_validity = time (0);
391 }
393 stream->uid_validity = 0; /* was time(0) */
395 if (stream->uid_last < time(0))
396 stream->uid_last = time (0);
398 stream->local = fs_get (sizeof (MAILDIRLOCAL));
399 LOCAL->inbox = !strcmp (ucase (strncpy (tmp,stream->mailbox,MAILTMPLEN)),"INBOX") ||
400 !strcmp (stream->mailbox,maildir_file (tmp2,"INBOX"));
401 LOCAL->dir = cpystr (maildir_file (tmp,stream->mailbox)); /* copy dir name */
402 /* make temporary buffer */
403 LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE-1) + 1);
404 LOCAL->scantime = 0; /* not scanned yet */
405 stream->sequence++;
406 stream->nmsgs = stream->recent = 0;
408 maildir_ping_core (stream);
409 maildir_ping (stream);
410 /* if (maildir_ping (stream) && !(stream->nmsgs || stream->silent))
411 printf("Mailbox is empty\n");
412 */
413 return stream;
415 }
417 /* Maildir ping mailbox
418 */
420 long maildir_ping_core (MAILSTREAM *stream)
421 {
422 char tmp[MAILTMPLEN];
423 MESSAGECACHE *elt;
424 struct stat sbuf, sbuf2;
425 DIR *dir;
426 struct direct *d;
427 int reloadall = NIL;
428 int uidinvalid = NIL;
429 unsigned long old;
430 long i;
431 long nmsgs = stream->nmsgs;
432 long recent = stream->recent;
433 long nfiles = stream->nmsgs;
434 int silent = stream->silent;
435 char *s, *s2;
436 mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
438 /* maildir_copynew (LOCAL->dir);
439 */
441 if (stat (LOCAL->dir,&sbuf) < 0) {
442 snprintf (tmp,MAILTMPLEN,"Unable to open maildir: %s",strerror (errno));
443 mm_log (tmp,ERROR);
444 return NIL;
445 }
447 /* okay, lets try to figure out the Maildir UID validity. This is done
448 by checking the last modification time of the file .uidvalidity
449 in the rootdir of the Maildir. Any program reordering the files
450 in the directory have to touch this file */
452 snprintf(tmp, MAILTMPLEN, "%s/../.uidvalidity", LOCAL->dir);
454 if (stat (tmp,&sbuf2) < 0) {
455 /* no uid validity file found, if uid_validity == 0, we have
456 to set it to something other than 0, and then create the
457 .uidvalidity file for future accesses */
459 if (stream->uid_validity == 0) {
460 FILE *fl;
461 struct utimbuf tbuf;
463 stream->uid_validity = time(0);
464 tbuf.actime = stream->uid_validity;
465 tbuf.modtime = stream->uid_validity;
467 if ((fl = fopen(tmp, "w"))) {
468 fclose(fl);
469 chmod (tmp, S_IRUSR|S_IWUSR);
470 utime(tmp, &tbuf);
471 }
472 }
473 uidinvalid = T; /* UID's are invalid, update them */
474 } else {
475 /* valid file, lets set UID if uid_validity = 0 */
476 if (stream->uid_validity == 0) {
477 stream->uid_validity = sbuf2.st_mtime;
478 }
479 }
481 #ifdef NO_UID_VALIDITY
482 uidinvalid = T; /* force the UIDs to be invalid and reset every time,
483 useful in an environment without imap servers and
484 clients that screw up the UIDs.. i'd leave it to
485 OFF until I really need it though... */
486 #endif
488 stream->silent = T;
489 if (sbuf.st_ctime != LOCAL->scantime) {
490 /* update the message list */
491 /* int maildir_namesort(const void *,const void *);
492 int (*mn)(struct direct **, struct direct **) = maildir_namesort; */
493 struct direct **names = NIL;
494 nfiles = scandir (LOCAL->dir,&names,maildir_select,maildir_namesort);
496 for (i = 0; i < nfiles; i++) {
498 /* check if file has executable bit set */
499 snprintf(tmp, MAILTMPLEN, "%s/%s", LOCAL->dir, names[i]->d_name);
500 stat (tmp,&sbuf2);
501 if (sbuf2.st_mode & S_IXUSR) {
502 /* executable bit set, modtime is uid */
503 if (sbuf2.st_mtime > stream->uid_last)
504 stream->uid_last = sbuf2.st_mtime+1;
505 }
506 /* this is kept for backwards compatibility */
507 if ((s = strstr (names[i]->d_name,":3,")))
508 s += 3;
509 if (s && (s2 = strstr (s, ",U"))) {
510 s2 += 2;
511 sscanf(s2, "%d", &old);
512 if (old > stream->uid_last) {
513 stream->uid_last = old+1;
514 }
515 }
517 }
519 mm_critical (stream); /* go critical */
520 old = stream->uid_last;
521 LOCAL->scantime = sbuf.st_ctime;
523 /* check if old files same */
524 for (i = 0; i < stream->nmsgs; i++) {
526 if (strcmp ((char *) mail_elt (stream, i + 1)->maildirp,
527 names[i]->d_name)) {
528 reloadall = T;
529 break;
530 }
531 }
533 if (reloadall) { /* files are out of order, rebuild cache */
535 i = 1;
536 while (i <= stream->nmsgs)
537 /* clean out cache */
538 if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))) {
539 fs_give ((void **) &elt->maildirp);
540 mail_expunged (stream,i);
541 }
542 else
543 i++;
545 mm_log ("Warning: Mailbox has changed in an unexpected way. Reloading.",
546 WARN);
547 stream->nmsgs = 0;
548 }
549 nmsgs = stream->nmsgs;
551 stream->nmsgs = nfiles; /* hm? */
553 for (i = nmsgs; i < nfiles; i++) {
555 mail_exists(stream, i+1);
556 /* if newly seen, add to list */
557 (elt = mail_elt (stream, i + 1))->maildirp = (long) cpystr (names[i]->d_name);
558 elt->valid = T;
560 /* grab the flags */
561 if ((s = strstr (names[i]->d_name,":3,"))) {
562 s += 3;
563 if (strchr (s,'F'))
564 elt->flagged = T;
565 if (strchr (s,'R'))
566 elt->answered = T;
567 if (strchr (s,'S'))
568 elt->seen = T;
569 if (strchr (s,'T'))
570 elt->deleted = T;
571 } else if ((s = strstr (names[i]->d_name,":2,"))) {
572 /* this is the :2, id where all files go nowadays */
573 s += 3;
574 if (strchr (s,'F'))
575 elt->flagged = T;
576 if (strchr (s,'R'))
577 elt->answered = T;
578 if (strchr (s,'S'))
579 elt->seen = T;
580 if (strchr (s,'T'))
581 elt->deleted = T;
582 snprintf(tmp, MAILTMPLEN, "%s/%s", LOCAL->dir, names[i]->d_name);
583 stat (tmp,&sbuf2);
584 if (sbuf2.st_mode & S_IXUSR) {
585 /* executable bit set, modtime is uid */
586 elt->private.uid = sbuf2.st_mtime;
587 }
588 /* and if we could not retrieve UID from modtime, or if
589 UIDs are invalid, go here */
590 if (elt->private.uid == 0 || uidinvalid) {
591 stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
592 maildir_flagmsg(stream, elt);
593 }
594 s = 0; /* make sure next if statement does not trigger */
595 }
597 if (s)
598 if ((s2 = strstr (s, ",U"))) {
599 s2 += 2;
600 sscanf(s2, "%d", &elt->private.uid);
601 if (elt->private.uid == 0 || uidinvalid) {
602 stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
603 maildir_flagmsg(stream, elt);
604 }
606 } else { /* assign new UID */
607 stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
608 elt->recent = T;
609 recent++;
610 maildir_flagmsg(stream, elt); /* store the UID that we assigned to it */
611 }
615 }
617 mm_nocritical (stream); /* release critical */
618 /* free the names stuff */
619 for (i = 0; i < nfiles; i++)
620 fs_give ((void **) &names[i]);
621 if (names)
622 fs_give ((void **) &names);
623 }
624 stream->silent = silent;
625 mail_exists(stream,nfiles);
626 /* if (!reloadall) */
627 mail_recent (stream,recent);
629 return T; /* return that we are alive */
630 }
632 long maildir_ping (MAILSTREAM *stream)
633 {
634 maildir_copynew (LOCAL->dir);
635 return maildir_ping_core (stream);
636 }
638 void maildir_copynew (const char *mailbox)
639 {
640 char tmp[MAILTMPLEN],file[MAILTMPLEN],newfile[MAILTMPLEN];
641 DIR *dir;
642 struct dirent *d;
643 struct stat sbuf;
645 snprintf (tmp,MAILTMPLEN,"%s/../new",mailbox);
646 if (!(dir = opendir (tmp)))
647 return;
649 while (d = readdir (dir)) {
650 if (d->d_name[0] == '.')
651 continue; /* skip .files */
653 snprintf (file,MAILTMPLEN,"%s/%s",tmp,d->d_name);
654 /* make sure this is a normal file */
655 if (stat (file,&sbuf) == 0 && S_ISREG (sbuf.st_mode)) {
657 if (strstr (d->d_name,":3,")) /* this message already has flags */
658 snprintf (newfile,MAILTMPLEN,"%s/%s",mailbox,d->d_name);
659 else
660 snprintf (newfile,MAILTMPLEN,"%s/%s:3,",mailbox,d->d_name);
662 /* move the new mail to the cur dir */
663 if (link (file,newfile) == -1)
664 mm_log("Unable to read new mail!",WARN);
665 else
666 unlink (file);
667 }
668 }
669 closedir (dir);
670 }
672 int maildir_select (struct direct *name)
673 {
674 if (name->d_name[0] != '.')
675 return T;
677 return NIL;
678 }
680 /* int maildir_namesort (struct direct **d1,struct direct **d2) */
681 int maildir_namesort (const void *d1, const void *d2)
682 {
683 return strcmp ((*(struct direct **)d1)->d_name,
684 (*(struct direct **)d2)->d_name);
685 }
688 /* Maildir garbage collect stream
689 */
691 void maildir_gc (MAILSTREAM *stream,long gcflags)
692 {
693 unsigned long i;
695 if (gcflags & GC_TEXTS) { /* garbage collect texts? */
696 /* flush texts from cache */
697 /* if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
698 // if (stream->text) fs_give ((void **) &stream->text);
699 // stream->msgno = 0; invalidate stream text
700 */
701 }
702 }
704 /* Maildir close
705 */
707 void maildir_close (MAILSTREAM *stream, long options)
708 {
709 MESSAGECACHE *elt;
710 int i;
711 mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
713 /* CL_EXPUNGE OPTION SUPPORT HERE SOMEWHERE! */
714 /* clean out the cached paths */
715 for (i = 1; i <= stream->nmsgs; i++)
716 if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) && elt->maildirp) {
717 fs_give ((void **) &elt->maildirp);
718 elt->maildirp = 0; /* otherwise pine coredumps */
719 }
721 if (LOCAL) { /* only if a stream is open */
722 if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
723 maildir_gc (stream,GC_TEXTS); /* free local cache */
724 /* free local scratch buffer */
725 if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
726 /* nuke the local data */
727 fs_give ((void **) &stream->local);
728 stream->dtb = NIL; /* log out the DTB */
729 }
730 }
732 void maildir_check (MAILSTREAM *stream)
733 {
734 /* Perhaps in the future this will preserve flags */
735 if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL);
736 }
738 long maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags)
739 {
740 unsigned long i;
741 MESSAGECACHE *elt;
742 /* UID call "impossible" */
743 if (flags & FT_UID) return NIL;
744 elt = mail_elt (stream,msgno);/* get elt */
745 /* snarf message if don't have it yet */
746 if (!elt->private.msg.text.text.data) {
747 maildir_fetchheader (stream,msgno,&i,flags);
748 if (!elt->private.msg.text.text.data) return NIL;
749 }
750 if (!(flags & FT_PEEK)) { /* mark as seen */
751 mail_elt (stream,msgno)->seen = T;
752 maildir_flagmsg (stream, mail_elt(stream,msgno));
753 mm_flags (stream,msgno);
754 }
755 if (!elt->private.msg.text.text.data) return NIL;
756 INIT (bs,mail_string,elt->private.msg.text.text.data,
757 elt->private.msg.text.text.size);
758 return T;
759 }
762 /* Maildir fetch message header
763 */
765 char *maildir_fetchheader (MAILSTREAM *stream,unsigned long msgno,
766 unsigned long *length, long flags)
767 {
768 unsigned long i,hdrsize;
769 int fd;
770 char *t;
771 char tmp[MAILTMPLEN];
772 char *s,*b;
773 struct stat sbuf;
774 struct tm *tm;
775 MESSAGECACHE *elt;
776 *length = 0; /* default to empty */
777 if (flags & FT_UID) return "";/* UID call "impossible" */
778 elt = mail_elt (stream,msgno);/* get elt */
779 if (!elt->private.msg.header.text.data) {
781 /* maildir_gc (stream,GC_TEXTS); invalidate current cache */
782 /* build message file name */
783 snprintf (tmp,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
784 if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) {
785 fstat (fd,&sbuf); /* get size of message */
786 /* make plausible IMAPish date string */
787 tm = gmtime (&sbuf.st_mtime);
788 elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
789 elt->year = tm->tm_year + 1900 - BASEYEAR;
790 elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
791 elt->seconds = tm->tm_sec;
792 elt->zhours = 0; elt->zminutes = 0;
793 /* slurp message */
794 read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
795 s[sbuf.st_size] = '\0'; /* tie off file */
796 close (fd); /* close file */
798 for (i = 0,b = s; *b && !(i && (*b == '\n')); i = (*b++ == '\n'));
799 hdrsize = (*b ? ++b:b)-s; /* number of header bytes */
801 elt->rfc822_size = /* size of entire message in CRLF form */
802 (elt->private.msg.header.text.size =
803 strcrlfcpy ((char **) &elt->private.msg.header.text.data,&i,s,
804 hdrsize)) +
805 (elt->private.msg.text.text.size =
806 strcrlfcpy ((char **) &elt->private.msg.text.text.data,&i,b,
807 sbuf.st_size - hdrsize));
808 fs_give ((void **) &s);
809 } else return "";
811 }
813 *length = elt->private.msg.header.text.size;
814 return (char *) elt->private.msg.header.text.data;
815 }
817 void maildir_fast (MAILSTREAM *stream,char *sequence,long flags)
818 {
819 unsigned long i,j;
820 /* ugly and slow */
821 if (stream && LOCAL && ((flags & FT_UID) ?
822 mail_uid_sequence (stream,sequence) :
823 mail_sequence (stream,sequence)))
824 for (i = 1; i <= stream->nmsgs; i++)
825 if (mail_elt (stream,i)->sequence) maildir_fetchheader (stream,i,&j,NIL);
826 }
828 /* Maildir find list of subscribed mailboxes
829 * Accepts: mail stream
830 * pattern to search
831 */
833 void maildir_list (MAILSTREAM *stream,char *ref, char *pat)
834 {
835 DIR *dp;
836 struct direct *d;
837 struct stat sbuf;
838 char *s,*s2,*cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN],subdir[MAILTMPLEN],rname[MAILTMPLEN];
840 strncpy(subdir, pat, MAILTMPLEN);
842 s = subdir;
843 s2 = 0;
844 while ((s = strstr(s, "/"))) {
845 s2 = s;
846 s++;
847 }
849 if (s2) { /* pointer to last / */
850 *s2 = '\0'; /* snip off string */
851 s2++;
852 /*syslog(LOG_INFO, "dir: '%s' pat: '%s'", subdir, s2);*/
853 snprintf(name, MAILTMPLEN, "%s/%s", myhomedir(), subdir);
854 } else {
855 s2 = pat;
856 snprintf(name, MAILTMPLEN, "%s", myhomedir());
857 strcpy(subdir, "");
858 }
860 maildir_listwork(name, stream, s2, subdir, 1);
862 if (pmatch ("INBOX", pat))
863 mm_list (stream,NIL,"INBOX",LATT_NOINFERIORS);
865 }
867 void maildir_listwork(char *name1, MAILSTREAM *stream, char *s2, char *subdir, int flag)
868 {
869 DIR *dp;
870 struct direct *d;
871 struct stat sbuf;
872 char *s,*cp,*np,curdir[MAILTMPLEN],rname[MAILTMPLEN],name[MAILTMPLEN];
873 char tmpdir[MAILTMPLEN];
875 strncpy(name, name1, MAILTMPLEN);
877 /*syslog(LOG_INFO, "opendir: '%s'", name);
878 //syslog(LOG_INFO, "subdir: '%s'", subdir);
879 */
880 if (dp = opendir (name)) { /* open directory */
881 while (d = readdir (dp)) /* scan directory, ignore all . names */
882 if (d->d_name[0] != '.' &&
883 strcmp(d->d_name, "Maildir")) {/* build file name */
884 /* we do not want to list Maildir to customers, because
885 that might give them weird ideas :) */
887 if (subdir[0] != '\0' && subdir[0] != '%' && subdir[0] != '*')
888 snprintf(name, MAILTMPLEN, "%s/%s", subdir, d->d_name);
889 else
890 snprintf(name, MAILTMPLEN, "%s", d->d_name);
891 /*syslog(LOG_INFO, "ls: '%s'", name);*/
893 /* this matching might not be correctly implemented */
894 if (maildir_file(curdir, name) &&
895 stat (curdir,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
896 /*syslog(LOG_INFO, "here for some stange reason");*/
897 if (pmatch(d->d_name, s2)) {
898 mm_list (stream,'/',name,NIL);
899 /* call function recursive if last char % */
900 /*syslog(LOG_INFO, "s2: '%s'",s2);*/
901 if (s2[strlen(s2)-1] == '%'
902 || s2[strlen(s2)-1] == '*') {
903 snprintf(rname, MAILTMPLEN, "%s/%s", name1, name);
904 /*syslog(LOG_INFO, "rname: '%s'", rname);*/
905 maildir_listwork(rname, stream, "%", name, 1);
906 }
907 }
908 } else
909 if (strlen(curdir) > 4 && pmatch(d->d_name, s2)) {
910 int fnlen = strlen (curdir);
911 curdir[fnlen - 4] = '\0';
912 /*syslog(LOG_INFO, "fname: '%s'", curdir);*/
913 if (stat (curdir,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
914 /* okay, do not list with NoSelect, since Netscape
915 does not f*cking honor this flag.. stupid netscape */
916 /*mm_list (stream,'/',name,LATT_NOSELECT);*/
918 /* call function recursive if last char % */
919 /*syslog(LOG_INFO, "s2: '%s'",s2);*/
920 if (s2[strlen(s2)-1] == '%'
921 || s2[strlen(s2)-1] == '*') {
922 snprintf(rname, MAILTMPLEN, "%s/%s", name1, name);
923 /*syslog(LOG_INFO, "rname: '%s'", rname);*/
924 maildir_listwork(rname, stream, "%", name, 1);
925 }
927 }
928 }
930 }
931 closedir (dp); /* all done, flush directory */
932 }
934 }
940 void *maildir_parameters (long function,void *value)
941 {
942 return NIL;
943 }
945 long maildir_create (MAILSTREAM *stream,char *mailbox)
946 {
947 char tmp[MAILTMPLEN];
948 char err[MAILTMPLEN];
949 char *s, *s2;
950 int fnlen, i;
951 char *subdir_names[] = {"/cur","/new","/tmp",NULL};
953 /* must not already exist */
954 if (access (maildir_file (tmp,mailbox),F_OK) == 0) {
955 snprintf (err,MAILTMPLEN,"Can't create mailbox %s: mailbox already exists",mailbox);
956 mm_log (err,ERROR);
957 return NIL;
958 }
960 maildir_file (tmp,mailbox); /* get file name */
961 fnlen = strlen (tmp);
962 /*syslog(LOG_INFO, "fname: '%s'", tmp);*/
963 tmp[fnlen - 4] = '\0'; /* making main directory's name */
964 fnlen -= 4;
966 /* okay, try to add support for adding hiearchys of directories, this
967 is done by scanning for /'s.... */
969 /*syslog(LOG_INFO, "tmp '%s'", tmp);*/
970 s = tmp;
972 while ((s = strstr(s, "/")) != 0) {
973 /*syslog(LOG_INFO, "Before make: '%s'", s);*/
974 *s = '\0';
975 /*syslog(LOG_INFO, "Trying to make: '%s'", tmp);*/
976 if (mkdir (tmp,0700) && *s != '\0') /* trying to make the dir */
977 if (errno != EEXIST) {
978 snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
979 mailbox,tmp,strerror (errno));
980 mm_log (err,ERROR);
981 return NIL;
982 }
983 *s = '/';
984 s++;
985 }
987 if (mkdir (tmp,0700)) { /* try to make new dir */
988 snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
989 mailbox,tmp,strerror (errno));
990 mm_log (err,ERROR);
991 return NIL;
992 }
994 /*syslog(LOG_INFO, "create maildir");*/
995 for (i = 0; subdir_names[i]; i++) {
996 strncpy (tmp + fnlen,subdir_names[i],MAILTMPLEN);
998 if (mkdir (tmp,0700)) { /* try to make new dir */
999 snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
1000 mailbox,tmp,strerror (errno));
1001 mm_log (err,ERROR);
1002 return NIL;
1006 return T; /* return success */
1009 void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
1011 char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN];
1012 struct utimbuf tbuf;
1013 char *s;
1015 /* build the new filename */
1016 snprintf (oldfile,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
1017 if ((s = strchr ((char *) elt->maildirp,':'))) *s = '\0';
1018 snprintf (fn,MAILTMPLEN,"%s:2,%s%s%s%s",(char *) elt->maildirp,elt->flagged ? "F" : "",
1019 elt->answered ? "R" : "",elt->seen ? "S" : "",
1020 elt->deleted ? "T" : "");
1021 snprintf (newfile,MAILTMPLEN,"%s/%s",LOCAL->dir,fn);
1022 /* rename the file with new flags */
1023 if (rename (oldfile,newfile) < 0) {
1024 snprintf(oldfile,MAILTMPLEN,"Unable to write flags to disk: %s",strerror (errno));
1025 mm_log(oldfile,ERROR);
1026 return;
1028 /* update the file name in cache */
1029 fs_give ((void **) &elt->maildirp);
1030 elt->maildirp = (long) cpystr (fn);
1032 /* fix the UID on the file */
1033 tbuf.actime = elt->private.uid;
1034 tbuf.modtime = elt->private.uid;
1035 chmod (newfile, S_IRUSR|S_IWUSR|S_IXUSR);
1036 utime (newfile, &tbuf);
1040 void maildir_expunge (MAILSTREAM *stream)
1042 MESSAGECACHE *elt;
1043 unsigned long i = 1;
1044 unsigned long n = 0;
1045 unsigned long recent = stream->recent;
1047 maildir_gc (stream,GC_TEXTS); /* invalidate texts */
1048 mm_critical (stream); /* go critical */
1049 while (i <= stream->nmsgs) { /* for each message */
1050 /* if deleted, need to trash it */
1051 if ((elt = mail_elt (stream,i))->deleted) {
1052 snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
1053 if (unlink (LOCAL->buf)) {/* try to delete the message */
1054 snprintf (LOCAL->buf,MAILTMPLEN,"Expunge of message %ld failed, aborted: %s",i,
1055 strerror (errno));
1056 mm_log (LOCAL->buf,WARN);
1057 break;
1059 /* free the cached filename */
1060 if (elt->maildirp) {
1061 fs_give ((void **) &elt->maildirp);
1062 elt->maildirp = 0; /* otherwise pine coredumps */
1064 if (elt->recent) --recent;/* if recent, note one less recent message */
1065 mail_expunged (stream,i); /* notify upper levels */
1066 n++; /* count up one more expunged message */
1068 else i++; /* otherwise try next message */
1070 if (n) { /* output the news if any expunged */
1071 snprintf (LOCAL->buf,MAILTMPLEN,"Expunged %ld messages",n);
1072 mm_log (LOCAL->buf,(long) NIL);
1074 else mm_log ("No messages deleted, so no update needed",(long) NIL);
1075 mm_nocritical (stream); /* release critical */
1076 /* notify upper level of new mailbox size */
1077 mail_exists (stream,stream->nmsgs);
1078 mail_recent (stream,recent);
1081 /* dont forget to process options in here */
1083 /* modified by sakira@sundent.mine.nu (03.01.13) */
1084 long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
1086 STRING st;
1087 MESSAGECACHE *elt;
1088 struct stat sbuf;
1089 int fd;
1090 unsigned long i;
1091 char flags[MAILTMPLEN], date[MAILTMPLEN];
1093 /* copy the messages */
1094 if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) :
1095 mail_sequence (stream,sequence))
1096 for (i = 1; i <= stream->nmsgs; i++)
1097 if ((elt = mail_elt (stream,i))->sequence) {
1098 sprintf (LOCAL->buf, "%s/%s", LOCAL->dir, (char *) elt->maildirp);
1099 /* snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp); */
1100 if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
1101 fstat (fd,&sbuf); /* get size of message */
1103 if (!elt->day) {
1104 struct tm *tm = gmtime (&sbuf.st_mtime);
1105 elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
1106 elt->year = tm->tm_year + 1900 - BASEYEAR;
1107 elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
1108 elt->seconds = tm->tm_sec;
1109 elt->zhours = 0; elt->zminutes = 0;
1112 if (sbuf.st_size > LOCAL->buflen) {
1113 fs_give((void **) &LOCAL->buf);
1114 LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
1117 /* slurp message */
1118 read(fd, LOCAL->buf, sbuf.st_size);
1119 LOCAL->buf[sbuf.st_size] = '\0';
1120 close(fd);
1121 /* read (fd, s = (char *) fs_get (sbuf.st_size +1),sbuf.st_size);
1122 s[sbuf.st_size] = '\0';
1123 close (fd); */
1125 INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size);
1126 flags[0] = flags[1] = '\0';
1127 if (elt->seen) strcat(flags, " \\Seen");
1128 if (elt->deleted) strcat(flags, " \\Deleted");
1129 if (elt->answered) strcat(flags, " \\Answered");
1130 if (elt->draft) strcat(flags, " \\Draft");
1131 flags[0] = '(';
1132 strcat(flags, ")");
1133 mail_date(date, elt);
1134 /* snprintf (LOCAL->buf,MAILTMPLEN,"%s%s%s%s%s)",
1135 elt->seen ? " \\Seen" : "",
1136 elt->deleted ? " \\Deleted" : "",
1137 elt->flagged ? " \\Flagged" : "",
1138 elt->answered ? " \\Answered" : "",
1139 (elt->seen || elt->deleted || elt->flagged || elt->answered) ?
1140 "" : " ");
1141 LOCAL->buf[0] = '(';
1142 mail_date (tmp,elt); */
1144 if (!mail_append_full (NIL, mailbox, flags, date, &st)) return NIL;
1145 if (options & CP_MOVE) elt->deleted = T;
1147 /* if (!maildir_append (stream,mailbox,LOCAL->buf,tmp,&st)) {
1148 fs_give ((void **) &s);
1149 return NIL;
1150 } */
1151 /* fs_give ((void **) &s); */ /* give back temporary space */
1153 return T; /* return success */
1156 #if 0
1157 long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
1159 STRING st;
1160 MESSAGECACHE *elt;
1161 struct stat sbuf;
1162 int fd;
1163 long i;
1164 char *s,tmp[MAILTMPLEN];
1165 /* copy the messages */
1166 if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) :
1167 mail_sequence (stream,sequence))
1168 for (i = 1; i <= stream->nmsgs; i++)
1169 if ((elt = mail_elt (stream,i))->sequence) {
1170 snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
1171 if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
1172 fstat (fd,&sbuf); /* get size of message */
1173 /* slurp message */
1174 read (fd,s = (char *) fs_get (sbuf.st_size +1),sbuf.st_size);
1175 s[sbuf.st_size] = '\0'; /* tie off file */
1176 close (fd); /* flush message file */
1177 INIT (&st,mail_string,(void *) s,sbuf.st_size);
1178 snprintf (LOCAL->buf,MAILTMPLEN,"%s%s%s%s%s)",
1179 elt->seen ? " \\Seen" : "",
1180 elt->deleted ? " \\Deleted" : "",
1181 elt->flagged ? " \\Flagged" : "",
1182 elt->answered ? " \\Answered" : "",
1183 (elt->seen || elt->deleted || elt->flagged || elt->answered) ?
1184 "" : " ");
1185 LOCAL->buf[0] = '('; /* open list */
1186 mail_date (tmp,elt); /* generate internal date */
1187 if (!maildir_append (stream,mailbox,LOCAL->buf,tmp,&st)) {
1188 fs_give ((void **) &s); /* give back temporary space */
1189 return NIL;
1191 fs_give ((void **) &s); /* give back temporary space */
1193 return T; /* return success */
1195 #endif
1197 long maildir_append (MAILSTREAM *stream, char *mailbox, append_t af, void *data)
1199 int fd;
1200 char c, *s;
1201 char *flags, *date;
1202 char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN];
1203 MESSAGECACHE elt;
1204 STRING *message;
1205 long i;
1206 long size = 0;
1207 long ret = LONGT;
1208 short uf = 0;
1210 /*
1211 This is intentionaly made static. Users can ask to save a LOT of messages
1212 at once and this program can do that within one second. Dan's assumption
1213 that time+pid+hostname always will be unique stops being true in this
1214 case. So we will add yet another number to host part of message file's
1215 name. Hostname is used only to make filename unique and Dan explicitly
1216 says that "<...> Other than this [skipping filenames starting at dot] ,
1217 readers should not attempt to parse filenames. <...>". Therefore this
1218 addition should be no problem. Am I right, Dan? --AK
1219 */
1221 static unsigned int transact = 0;
1223 if (!stream) stream = &maildirproto;
1225 if (flags) /* get flags if given */
1226 uf = maildir_getflags (user_flags (&maildirproto),flags);
1228 /* N.B.: can't use LOCAL->buf for tmp */
1229 /* make sure valid mailbox */
1230 if (!maildir_isvalid (mailbox, NIL)) {
1231 snprintf (tmp,MAILTMPLEN,"Not a valid Maildir mailbox: %s",mailbox);
1232 mm_log (tmp,ERROR);
1233 return NIL;
1236 if (!(*af)(stream, data, &flags, &date, &message)) return NIL;
1238 mm_critical(stream); /* go critical */
1239 do {
1240 if (!SIZE(message)) { /* guard againt zero-length */
1241 mm_log("Append of zero-length message", ERROR);
1242 ret = NIL;
1243 break;
1246 /* build file name we will use */
1247 snprintf (file,MAILTMPLEN,"%u.%d.%09u.%s:3,%s%s%s%s",
1248 time (0),getpid (),transact++,mylocalhost (),
1249 uf&fFLAGGED ? "F" : "",uf&fANSWERED ? "R" : "",
1250 uf&fSEEN ? "S" : "",uf&fDELETED ? "T" : "");
1251 /* build tmp file name */
1252 snprintf (path1,MAILTMPLEN,"%s/../tmp/%s",maildir_file (tmp,mailbox),file);
1254 if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
1255 snprintf (tmp,MAILTMPLEN,"Can't open append mailbox: %s",strerror (errno));
1256 mm_log (tmp,ERROR);
1257 return NIL;
1260 i = SIZE (message); /* get size of message */
1261 s = (char *) fs_get (i + 1); /* get space for the data */
1262 /* copy the data w/o CR's */
1263 while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
1264 /* write the data */
1265 if ((write (fd,s,size) < 0) || fsync (fd)) {
1266 unlink (path1); /* delete message */
1267 snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
1268 mm_log (tmp,ERROR);
1269 ret = NIL;
1271 /* build final filename to use */
1272 snprintf (path2,MAILTMPLEN,"%s/../new/%s",maildir_file (tmp,mailbox),file);
1273 if (link (path1,path2) < 0) {
1274 snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
1275 mm_log (tmp,ERROR);
1276 ret = NIL;
1278 unlink (path1);
1279 fs_give ((void **) &s); /* flush the buffer */
1280 close (fd); /* close the file */
1282 if (ret) {
1283 if (!(*af)(stream, data, &flags, &date, &message)) ret = NIL;
1285 } while(ret && message);
1287 mm_nocritical (stream); /* release critical */
1288 return ret;
1291 #if 0
1292 long maildir_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
1293 STRING *message)
1295 int fd;
1296 char c,*s;
1297 char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN];
1298 MESSAGECACHE elt;
1299 long i;
1300 long size = 0;
1301 long ret = LONGT;
1302 short uf = 0;
1304 /*
1305 This is intentionaly made static. Users can ask to save a LOT of messages
1306 at once and this program can do that within one second. Dan's assumption
1307 that time+pid+hostname always will be unique stops being true in this
1308 case. So we will add yet another number to host part of message file's
1309 name. Hostname is used only to make filename unique and Dan explicitly
1310 says that "<...> Other than this [skipping filenames starting at dot] ,
1311 readers should not attempt to parse filenames. <...>". Therefore this
1312 addition should be no problem. Am I right, Dan? --AK
1313 */
1315 static unsigned int transact = 0;
1317 if (flags) /* get flags if given */
1318 uf = maildir_getflags (user_flags (&maildirproto),flags);
1320 /* if (date) { want to preserve date?
1321 //syslog(LOG_INFO, "date: '%s'", date);
1322 // yes, parse date into an elt
1323 if (!mail_parse_date (&elt,date)) {
1324 snprintf (tmp,MAILTMPLEN,"Bad date in append: %s",date);
1325 mm_log (tmp,ERROR);
1326 return NIL;
1328 } */
1329 /* N.B.: can't use LOCAL->buf for tmp */
1330 /* make sure valid mailbox */
1331 if (!maildir_isvalid (mailbox, NIL)) {
1332 snprintf (tmp,MAILTMPLEN,"Not a valid Maildir mailbox: %s",mailbox);
1333 mm_log (tmp,ERROR);
1334 return NIL;
1336 /* build file name we will use */
1337 snprintf (file,MAILTMPLEN,"%u.%d.%09u.%s:3,%s%s%s%s",
1338 time (0),getpid (),transact++,mylocalhost (),
1339 uf&fFLAGGED ? "F" : "",uf&fANSWERED ? "R" : "",
1340 uf&fSEEN ? "S" : "",uf&fDELETED ? "T" : "");
1341 /* build tmp file name */
1342 snprintf (path1,MAILTMPLEN,"%s/../tmp/%s",maildir_file (tmp,mailbox),file);
1344 if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
1345 snprintf (tmp,MAILTMPLEN,"Can't open append mailbox: %s",strerror (errno));
1346 mm_log (tmp,ERROR);
1347 return NIL;
1349 i = SIZE (message); /* get size of message */
1350 s = (char *) fs_get (i + 1); /* get space for the data */
1351 /* copy the data w/o CR's */
1352 while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
1353 mm_critical (stream); /* go critical */
1354 /* write the data */
1355 if ((write (fd,s,size) < 0) || fsync (fd)) {
1356 unlink (path1); /* delete message */
1357 snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
1358 mm_log (tmp,ERROR);
1359 ret = NIL;
1361 /* build final filename to use */
1362 snprintf (path2,MAILTMPLEN,"%s/../new/%s",maildir_file (tmp,mailbox),file);
1363 if (link (path1,path2) < 0) {
1364 snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
1365 mm_log (tmp,ERROR);
1366 ret = NIL;
1368 unlink (path1);
1370 close (fd); /* close the file */
1371 mm_nocritical (stream); /* release critical */
1372 fs_give ((void **) &s); /* flush the buffer */
1373 return ret;
1375 #endif
1377 short bezerk_getflags (MAILSTREAM *stream,char *flag)
1379 char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
1380 short f = 0;
1381 short i,j;
1382 if (flag && *flag) { /* no-op if no flag string */
1383 /* check if a list and make sure valid */
1384 if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
1385 mm_log ("Bad flag list",ERROR);
1386 return NIL;
1388 /* copy the flag string w/o list construct */
1389 strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
1390 tmp[j] = '\0';
1391 t = ucase (tmp); /* uppercase only from now on */
1393 while (t && *t) { /* parse the flags */
1394 if (*t == '\\') { /* system flag? */
1395 switch (*++t) { /* dispatch based on first character */
1396 case 'S': /* possible \Seen flag */
1397 if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
1398 t += 4; /* skip past flag name */
1399 break;
1400 case 'D': /* possible \Deleted flag */
1401 if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
1402 t[5] == 'E' && t[6] == 'D') i = fDELETED;
1403 t += 7; /* skip past flag name */
1404 break;
1405 case 'F': /* possible \Flagged flag */
1406 if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
1407 t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
1408 t += 7; /* skip past flag name */
1409 break;
1410 case 'A': /* possible \Answered flag */
1411 if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
1412 t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
1413 t += 8; /* skip past flag name */
1414 break;
1415 default: /* unknown */
1416 i = 0;
1417 break;
1419 /* add flag to flags list */
1420 if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
1422 else { /* no user flags yet */
1423 return f;
1424 t = strtok (t," "); /* isolate flag name */
1425 /* snprintf (err,MAILTMPLEN,"Unknown flag: %.80s",t); */
1426 t = strtok (NIL," "); /* get next flag */
1427 /* mm_log (err,ERROR); */
1431 return f;
1434 short maildir_getflags (MAILSTREAM *stream,char *flag)
1436 return bezerk_getflags (stream,flag); /* nothing exciting, reuse old code */
1439 long maildir_delete (MAILSTREAM *stream,char *mailbox)
1441 DIR *dirp;
1442 struct direct *d;
1443 int i,j;
1444 char tmp[MAILTMPLEN],err[MAILTMPLEN];
1445 char *subdir_names[] = {"cur/","new/","tmp/",NULL};
1447 /* check if mailbox even exists */
1448 if (!maildir_isvalid (mailbox,NIL)) {
1449 /* sprintf (tmp,"Can't delete mailbox %s: no such mailbox",mailbox);
1450 mm_log (tmp,ERROR);
1451 return NIL; */
1452 /*syslog(LOG_INFO, "Invalid maildir in delete()"); */
1453 return T; /* well.. a stupid hack to get by a problem in netscape ..
1454 it remembers folders locally, and if a folder is deleted on
1455 another machine, you have no way removing it on any other
1456 netscapes... */
1459 /* get name of directory */
1460 i = strlen (maildir_file (tmp,mailbox)) + 1;
1461 for (j = 0; subdir_names[j]; j++) {
1462 strncpy (tmp + i - 4,subdir_names[j], MAILTMPLEN);
1463 if (dirp = opendir (tmp)) { /* open directory */
1464 while (d = readdir (dirp)) /* empty the directory */
1465 if (strcmp (d->d_name,".") && strcmp (d->d_name,"..")) {
1466 strncpy (tmp + i,d->d_name, MAILTMPLEN);
1467 /*syslog(LOG_INFO, "unlink1: '%s'");*/
1468 unlink (tmp);
1470 closedir (dirp); /* flush directory */
1472 /* remove the subdir */
1473 tmp[i + 3] = '\0';
1474 /*syslog(LOG_INFO, "tmp: '%s'", tmp);*/
1475 if (rmdir (tmp)) {
1476 /* sprintf (err,"Can't delete directory %s: %s",tmp,strerror (errno));
1477 mm_log (err,ERROR);*/
1481 /* try to remove the directory */
1482 *(tmp + i - 5) = '\0';
1483 /*syslog(LOG_INFO, "tmp2: '%s'", tmp);*/
1484 if (rmdir (tmp)) {
1485 /* sprintf (err,"Can't delete mailbox %s: %s",mailbox,strerror (errno));
1486 mm_log (err,ERROR);
1487 return NIL; */
1489 return T; /* return success */
1492 long maildir_rename (MAILSTREAM *stream,char *old,char *new)
1494 char tmp[MAILTMPLEN],tmpnew[MAILTMPLEN];
1496 /* old mailbox name must be valid */
1497 if (!maildir_isvalid (old,NIL)) {
1498 snprintf (tmp,MAILTMPLEN,"Can't rename mailbox %s: no such mailbox",old);
1499 mm_log (tmp,ERROR);
1500 return NIL;
1503 /* new mailbox name must not exist */
1504 if (access (maildir_file (tmp,new),F_OK) == 0) {
1505 snprintf (tmp,MAILTMPLEN,"Can't rename to mailbox %s: destination already exists",new);
1506 mm_log (tmp,ERROR);
1507 return NIL;
1510 /* try to rename the directory */
1511 if (rename (maildir_file (tmp,old),maildir_file (tmpnew,new))) {
1512 snprintf (tmp,MAILTMPLEN,"Can't rename mailbox %s to %s: %s",old,new,strerror (errno));
1513 mm_log (tmp,ERROR);
1514 return NIL;
1516 return T; /* return success */
1519 long maildir_sub (MAILSTREAM *stream,char *mailbox)
1521 char tmp[MAILTMPLEN];
1522 return sm_subscribe (mailbox);
1525 long maildir_unsub (MAILSTREAM *stream,char *mailbox)
1527 char tmp[MAILTMPLEN];
1528 return sm_unsubscribe (mailbox);
1531 void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat)
1533 void *sdb = NIL;
1534 char *s;
1535 /* get canonical form of name */
1536 if ((s = sm_read (&sdb))) {
1537 do if (pmatch_full (s,pat,'/')) mm_lsub (stream,'/',s,NIL);
1538 while (s = sm_read (&sdb)); /* until no more subscriptions */

UW-IMAP'd extensions by yuuji