diff -N -r -c imap-2004c1/Makefile imap-2004c1-local/Makefile *** imap-2004c1/Makefile Sat Dec 25 14:05:25 2004 --- imap-2004c1-local/Makefile Mon Feb 21 17:41:02 2005 *************** *** 135,141 **** # mbox if file "mbox" exists on the home directory, automatically moves mail # from the spool directory to "mbox" and uses "mbox" as INBOX. ! EXTRADRIVERS=mbox # Plaintext password type. Defines how plaintext password authentication is --- 135,141 ---- # mbox if file "mbox" exists on the home directory, automatically moves mail # from the spool directory to "mbox" and uses "mbox" as INBOX. ! EXTRADRIVERS=maildir # Plaintext password type. Defines how plaintext password authentication is *************** *** 170,176 **** # # SSLTYPE=nopwd is now the default as required by RFC 3501 ! SSLTYPE=nopwd # IP protocol version --- 170,176 ---- # # SSLTYPE=nopwd is now the default as required by RFC 3501 ! SSLTYPE=unix # IP protocol version diff -N -r -c imap-2004c1/src/c-client/mail.h imap-2004c1-local/src/c-client/mail.h *** imap-2004c1/src/c-client/mail.h Tue Dec 7 02:21:30 2004 --- imap-2004c1-local/src/c-client/mail.h Mon Feb 21 17:41:02 2005 *************** *** 789,794 **** --- 789,795 ---- unsigned int spare7 : 1; /* seventh spare bit */ unsigned int spare8 : 1; /* eighth spare bit */ void *sparep; /* spare pointer */ + char *maildirp; unsigned long user_flags; /* user-assignable flags */ } MESSAGECACHE; diff -N -r -c imap-2004c1/src/osdep/unix/Makefile imap-2004c1-local/src/osdep/unix/Makefile *** imap-2004c1/src/osdep/unix/Makefile Fri Nov 5 17:32:26 2004 --- imap-2004c1-local/src/osdep/unix/Makefile Mon Feb 21 17:44:30 2005 *************** *** 29,36 **** # Extended flags needed for SSL. You may need to modify. ! SSLDIR=/usr/local/ssl ! SSLCERTS=$(SSLDIR)/certs SSLKEYS=$(SSLCERTS) SSLINCLUDE=$(SSLDIR)/include SSLLIB=$(SSLDIR)/lib --- 29,36 ---- # Extended flags needed for SSL. You may need to modify. ! SSLDIR=/usr/local ! SSLCERTS=/etc/ssl SSLKEYS=$(SSLCERTS) SSLINCLUDE=$(SSLDIR)/include SSLLIB=$(SSLDIR)/lib *************** *** 48,54 **** SSLCFLAGS= -I$(SSLINCLUDE) -I$(SSLINCLUDE)/openssl\ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" -DSSL_KEY_DIRECTORY=\"$(SSLKEYS)\" ! SSLLDFLAGS= -L$(SSLLIB) -lssl $(SSLCRYPTO) $(SSLRSA) # Extended flags needed for non-standard passwd types. You may need to modify. --- 48,54 ---- SSLCFLAGS= -I$(SSLINCLUDE) -I$(SSLINCLUDE)/openssl\ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" -DSSL_KEY_DIRECTORY=\"$(SSLKEYS)\" ! SSLLDFLAGS= -R ${SSLLIB} -L$(SSLLIB) -lssl $(SSLCRYPTO) $(SSLRSA) # Extended flags needed for non-standard passwd types. You may need to modify. *************** *** 115,121 **** ARCHIVE=c-client.a BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ ! rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o CFLAGS=-g --- 115,121 ---- ARCHIVE=c-client.a BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ ! rfc822.o nntp.o smtp.o imap4r1.o pop3.o maildir.o \ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o CFLAGS=-g diff -N -r -c imap-2004c1/src/osdep/unix/dummy.c imap-2004c1-local/src/osdep/unix/dummy.c *** imap-2004c1/src/osdep/unix/dummy.c Wed Nov 10 19:16:23 2004 --- imap-2004c1-local/src/osdep/unix/dummy.c Mon Feb 21 17:41:02 2005 *************** *** 202,207 **** --- 202,208 ---- void *sdb = NIL; char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN]; int showuppers = pat[strlen (pat) - 1] == '%'; + /* get canonical form of name */ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do if (*s != '{') { *************** *** 256,271 **** struct stat sbuf; int ismx; char tmp[MAILTMPLEN]; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; if (dp = opendir (tmp)) { /* do nothing if can't open directory */ /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'/')) dummy_listed (stream,'/',dir,LATT_NOSELECT,contents); ! /* scan directory, ignore . and .. */ ismx = (!stat (strcat (tmp,MXINDEXNAME),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); ! if (!dir || dir[strlen (dir) - 1] == '/') while (d = readdir (dp)) if (((d->d_name[0] != '.') || (((int) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL : (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]) && --- 257,316 ---- struct stat sbuf; int ismx; char tmp[MAILTMPLEN]; + char tmp2[MAILTMPLEN]; + char *pt; + int ismaildir; /* punt if bogus name */ if (!mailboxdir (tmp,dir,NIL)) return; + + + ismaildir = maildir_valid( tmp ); + + strcpy(tmp2, tmp); + pt = tmp2 + strlen(tmp2) - 2; + while(pt > tmp2) { + if ( *pt == '/' ) { + *pt = 0; + break; + } + pt--; + } + + pt = tmp + strlen(tmp) - 5; + + /* if it's a valid maildir box, we need to make some other tests + to see if we should decend...*/ + + if ( maildir_valid( tmp2 ) && + ( ! ( strcmp(pt, "/new/") && + strcmp(pt, "/tmp/") && + strcmp(pt, "/cur/") && + ( *pt != '.' ) + ) + ) + ) + { + return; + } + if (dp = opendir (tmp)) { /* do nothing if can't open directory */ /* list it if not at top-level */ if (!level && dir && pmatch_full (dir,pat,'/')) dummy_listed (stream,'/',dir,LATT_NOSELECT,contents); ! /* scan directory, ignore . and .. , and cur, and new, and tmp*/ ismx = (!stat (strcat (tmp,MXINDEXNAME),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)); ! if (!dir || dir[strlen (dir) - 1] == '/') while (d = readdir (dp)) { ! ! /* if this is a maildir, we skip new, cur & tmp. */ ! if ( ismaildir && ! ! ( strcmp( d->d_name, "new") && ! strcmp( d->d_name, "tmp") && ! strcmp( d->d_name, "cur") && ! ( *pt != '.' ) ) ) { ! continue; ! } ! if (((d->d_name[0] != '.') || (((int) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL : (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]) && *************** *** 308,313 **** --- 353,359 ---- } } } + } closedir (dp); /* all done, flush directory */ } } *************** *** 527,536 **** else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); ! if ((sbuf.st_mode & S_IFMT) != S_IFREG) sprintf (err,"Can't open %.80s: not a selectable mailbox", stream->mailbox); ! else if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format", stream->mailbox,tmp); } --- 573,582 ---- else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); ! if (((sbuf.st_mode & S_IFMT) != S_IFREG) && ((sbuf.st_mode & S_IFMT) != S_IFDIR)) sprintf (err,"Can't open %.80s: not a selectable mailbox", stream->mailbox); ! else if (((sbuf.st_mode & S_IFMT) != S_IFDIR) && sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format", stream->mailbox,tmp); } diff -N -r -c imap-2004c1/src/osdep/unix/env_unix.c imap-2004c1-local/src/osdep/unix/env_unix.c *** imap-2004c1/src/osdep/unix/env_unix.c Mon Sep 13 17:31:19 2004 --- imap-2004c1-local/src/osdep/unix/env_unix.c Mon Feb 21 17:41:02 2005 *************** *** 806,812 **** * Returns: my home directory name */ ! static char *mymailboxdir () { char *home = myhomedir (); /* initialize if first time */ --- 806,812 ---- * Returns: my home directory name */ ! char *mymailboxdir () { char *home = myhomedir (); /* initialize if first time */ diff -N -r -c imap-2004c1/src/osdep/unix/maildir.c imap-2004c1-local/src/osdep/unix/maildir.c *** imap-2004c1/src/osdep/unix/maildir.c Wed Dec 31 19:00:00 1969 --- imap-2004c1-local/src/osdep/unix/maildir.c Thu Jun 30 21:46:43 2005 *************** *** 0 **** --- 1,2536 ---- + /* + * Maildir Module for PINE 4.0x - fourth release, use with CARE! + * + * Author: Mattias Larsson + * + * Version: 21.07.98 + * + * Please read the README.maildir file before using this module! + * + * If you have any questions, please e-mail ml@techno.org + * + * Multiple inboxes patch by Dean Gaudet + * + * ================================================= + * + * Based on the IMAP2 maildir routines by: + * + * Author: Eric Green + * Bloodhounds International Inc. + * thrytis@imaxx.net + * + * Large portions re-written by: + * Robert Banz + * University of Maryland, Baltimore County + * banz@umbc.edu + * + * Additional contributions from: + * Aidas Kasparas (kaspar@soften.ktu.lt) + * + * Date: 27 April 1997 + * Last Edited: 08 November 2003 + * + * Based (heavily) on mh.c and other c-client library files by Mark Crispin: + * + * Mark Crispin + * Networks and Distributed Computing + * Computing & Communications + * University of Washington + * Administration Building, AG-44 + * Seattle, WA 98195 + * Internet: MRC@CAC.Washington.EDU + * + * Copyright 1995 by the University of Washington + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appears in all copies and that both the + * above copyright notice and this permission notice appear in supporting + * documentation, and that the name of the University of Washington not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. This software is made + * available "as is", and + * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, + * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN + * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + + /* #define MAILDIR_DEBUG */ + + /* CONFIGURABLE OPTIONS - PLEASE CHECK THESE OUT */ + + #define NO_MAILDIR_FIDDLE /* disallow Maildir with Maildir in the + name. This is useful in an ISP setup + using the IMAP daemon. #undef it if you + are running a normal pine and know what + you are doing */ + + #undef NO_ABSOLUTE_PATHS /* if you define this, all paths + use your HOMEDIR is the root instead + of the actual root of the machine. This + is also useful in an ISP setup with + IMAP */ + + #undef NO_UID_VALIDITIY /* define this if you want the UID's not + to be persistent over sessions. Use this + if you use another client to read the + maildir that screws up the special way + in which we store UIDs. Do not enable + unless you are sure you need it. */ + + #define MAILDIR_USE_SHORTNAME /* define this if you want the driver + to use a non-fully-qualified hostname + in the message file name */ + + + /* END CONFIGURATION */ + + int uidinvalidthing; + + #define MTA_DEBUG /* debugging sent to stdout */ + #undef MTA_DEBUG + + #include + #include + #include + extern int errno; /* just in case */ + #include "mail.h" + #include "osdep.h" + #include + #include + #include + #include + #include + #include "maildir.h" + #include "misc.h" + #include "dummy.h" + + /* Driver dispatch used by MAIL */ + + DRIVER maildirdriver = { + "maildir", /* driver name */ + /* driver flags */ + DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE, + (DRIVER *) NIL, /* next driver */ + maildir_valid, /* mailbox is valid for us */ + maildir_parameters, /* manipulate parameters */ + maildir_scan, /* scan mailboxes */ + maildir_list, /* find mailboxes */ + maildir_lsub, /* find subscribed mailboxes */ + maildir_sub, /* subscribe to mailbox */ + NIL, /* unsubscribe from mailbox */ + maildir_create, /* create mailbox */ + maildir_delete, /* delete mailbox */ + maildir_rename, /* rename mailbox */ + mail_status_default, /* status of mailbox */ + maildir_open, /* open mailbox */ + maildir_close, /* close mailbox */ + maildir_fast, /* fetch message "fast" attributes */ + NIL, /* fetch message flags */ + NIL, /* fetch overview */ + NIL, /* fetch message envelopes */ + maildir_fetchheader, /* fetch message header */ + maildir_fetchtext, /* fetch message body */ + NIL, /* fetch partial message text */ + NIL, /* unique identifier */ + NIL, /* message number */ + NIL, /* modify flags */ + maildir_flagmsg, /* per-message modify flags */ + NIL, /* search for message based on criteria */ + NIL, /* sort messages */ + NIL, /* thread messages */ + maildir_ping, /* ping mailbox to see if still alive */ + maildir_check, /* check for new messages */ + maildir_expunge, /* expunge deleted messages */ + maildir_copy, /* copy messages to another mailbox */ + maildir_append, /* append string message to mailbox */ + maildir_gc /* garbage collect stream */ + }; + + /* extended flags we support */ + + char *extend_flags[] = { + "Junk", + "NonJunk", + "$Label1", + "$Label2", + "$Label3", + "$Label4", + "$Label5", + "Passed", + 0 + }; + + char extend_flag_keys[] = { + 'J', + 'N', + '1', + '2', + '3', + '4', + '5', + 'P', + 0 + }; + + char *standard_flags[] = { + "\\Deleted", + "\\Seen", + "\\Flagged", + "\\Answered", + "\\Draft", + 0 + }; + + char standard_flag_keys[] = { + 'T', + 'S', + 'F', + 'R', + 'D', + 0 + }; + + + /* prototype stream */ + MAILSTREAM maildirproto = {&maildirdriver}; + + /* Check validity of mailbox + */ + + DRIVER *maildir_valid (char *name) + { + return maildir_isvalid(name,T) ? &maildirdriver : NIL; + } + + int maildir_isvalid (char *name,long justname) + { + char tmp[MAILTMPLEN]; + struct stat sbuf; + + + if (!name || (!*name) || + ((*name == '#') && + (*(name+1) == 0 || + (*(name+1) != 'm' && *(name+1) != 'M') || + (*(name+2) != 'd' && *(name+1) != 'D') || + *(name+3) != '/')) || (*name == '.')) + return NIL; + + /* okay, anything containing the name Maildir will be ignored + this is to prevent anyone from fiddling with their incoming Maildir + directly, it should be accessed via the INBOX alias */ + + #ifdef NO_MAILDIR_FIDDLE + if (strstr(name, "Maildir")) { + return NIL; + } + #endif + /* If we are requested only to check + if the name is appropriate then we + have done! */ + if (justname && *name == '#') return T; + + + /* must be valid local mailbox */ + if ((*name != '*') && (*name != '{') && + maildir_file (tmp,name) && + /* assume its maildir if its a dir */ + stat (tmp,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) + return T; + + /* INBOX is for default Maildir */ + if (!strcmp (ucase (strcpy (tmp,name)), "INBOX") && + (stat (maildir_file (tmp,name),&sbuf) == 0) && + S_ISDIR (sbuf.st_mode)) + return T; + + return NIL; + } + + /* Maildir mail generate file string + * BTW, one day we'll make this NOT suck and copy the code from the UNIX + * driver that works right. + */ + + char *maildir_file (char *dst,char *name) + { + char tmp[MAILTMPLEN]; + char *s; + struct passwd *pw; + + if (strlen (name) > 3 && /* safe do other comparisons */ + (*name == '#') && + (name[1] == 'm' || name[1] == 'M') && + (name[2] == 'd' || name[2] == 'D') && + (name[3] == '/')) + name += 4; + + /* if there's that ~/ stuff before, chop that off too... */ + /* mulberry seems to like to use the ~/ namespace... */ + + if ( ! strncmp (name, "~/", 2 ) ) { + name += 2; + } + + #ifdef UMBC_ENV + if ( *name == '~' ) { + ++name; + + for ( s = dst; *name && (*name != '/'); *s++ = *name++ ); + + *s++ = '\0'; + + if (( pw = getpwnam(dst)) && pw->pw_dir) { + if (*name) name++; /* skip past the slash */ + /* canonicalize case of INBOX */ + + if ( ! strncmp( "/afs/", pw->pw_dir, 5 ) ) { + *(pw->pw_dir + strlen(pw->pw_dir) - 5) = '\0'; + } + if ( (s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0'; + /* don't allow ~root/ if restricted root */ + if ( !*pw->pw_dir) dst = NIL; + /* build final name w/ subdir if needed */ + else sprintf (dst,"%s/Mail/%s/cur",pw->pw_dir,name); + + return dst; + + } + + else dst = NIL; /* no such user */ + + return dst; + + } + #endif + + #ifdef UMBC_ENV + /* if someone has the Mail/ or /Mail/ in the path already, + lop it off. */ + + if ( ! strncmp (name, "Mail/", 5) ) { + name += 5; + } else if ( ! strncmp (name, "/Mail/", 6 ) ) { + name += 6; + } + #endif + + #ifdef NO_ABSOLUTE_PATHS + if (*name == '/') { + sprintf(dst,"%s/%s/cur", (char *) mymailboxdir(), name+1); + } + else + sprintf (dst,"%s/%s/cur", (char *) mymailboxdir (), + strcmp (ucase (strcpy (tmp, name)), "INBOX") ? name : MAILDIRPATH); + #else + if (*name == '/') { + strncpy (dst, name, MAILTMPLEN - 2); + strncat (dst, "/cur", MAILTMPLEN - 2); + dst[MAILTMPLEN - 1] = '\0'; + } + else + sprintf (dst,"%s/%s/cur", (char *) mymailboxdir (), + strcmp (ucase (strcpy (tmp, name)), "INBOX") ? name : MAILDIRPATH); + + #endif + + return dst; + } + + /* Maildir open + */ + + MAILSTREAM *maildir_open (MAILSTREAM *stream) + { + char tmp[MAILTMPLEN],tmp2[MAILTMPLEN]; + int i; + + if (!stream) return &maildirproto; + if (LOCAL) { /* recycle stream */ + maildir_close (stream, 0); + stream->dtb = &maildirdriver; + mail_free_cache (stream); + stream->uid_last = 0; /* default UID validity */ + stream->uid_validity = time (0); + } + + stream->uid_validity = 0; /* was time(0) */ + + stream->local = fs_get (sizeof (MAILDIRLOCAL)); + LOCAL->inbox = !strcmp (ucase (strcpy (tmp,stream->mailbox)),"INBOX") || + !strcmp (stream->mailbox,maildir_file (tmp2,"INBOX")); + LOCAL->dir = cpystr (maildir_file (tmp,stream->mailbox)); /* copy dir name */ + /* make temporary buffer */ + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); + LOCAL->lastbody = (char *) fs_get( MAXMESSAGESIZE * 2 ); + LOCAL->lastheader = (char *) fs_get ( MAXMESSAGESIZE * 2); + LOCAL->lastheaderlen = LOCAL->lastbodylen = MAXMESSAGESIZE * 2; + LOCAL->lastmsg = (char *) fs_get ( MAXMESSAGESIZE + 1); + *LOCAL->lastmsg = '\0'; + LOCAL->scantime = 0; /* not scanned yet */ + LOCAL->ld = -1; + LOCAL->toobigflag = 0; + LOCAL->uidinvalid = 1; + stream->sequence++; + stream->nmsgs = stream->recent = 0; + + syslog(LOG_NOTICE, "maildir_open: %s", LOCAL->dir); + + for ( i=0; extend_flags[i]; i++ ) + stream->user_flags[i] = strdup(extend_flags[i]); + + if ( !stream->rdonly ) { + stream->perm_seen = stream->perm_deleted = stream->perm_flagged = + stream->perm_answered = stream->perm_draft = T; + stream->perm_user_flags = 0xffffffff; + stream->kwd_create = NIL; + } + + #ifdef UMBC_ENV + if (unix_check_locked(stream)) { + return NIL; + } + #endif + + maildir_ping (stream); + + return stream; + + } + + /* Maildir ping mailbox + */ + + long maildir_ping_core (MAILSTREAM *stream) + { + char tmp[MAILTMPLEN]; + char tmperror[MAILTMPLEN]; + MESSAGECACHE *elt; + struct stat sbuf, sbuf2; + int reloadall = NIL; + long i; + long recent = stream->recent; + long nfiles = 0; + int playcount = 0; + struct direct **names = NIL; + mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); + unsigned long tmpuid; + int docopynew = 1; + + LOCAL->toobigflag = 0; + + invaliduids: /* if our UID state is invalid, we can loop back to + here after we've deleted the .uidvalidity */ + + if (stat (LOCAL->dir,&sbuf) < 0) { + sprintf (tmp,"Unable to open maildir: %s",strerror (errno)); + mm_log (tmp,ERROR); + return NIL; + } + + /* okay, lets try to figure out the Maildir UID validity. This is done + by checking the last modification time of the file .uidvalidity + in the rootdir of the Maildir. Any program reordering the files + in the directory have to touch this file */ + + sprintf(tmp, "%s/../.uidvalidity", LOCAL->dir); + + if (stat (tmp,&sbuf2) < 0) { + /* no uid validity file found, if uid_validity == 0, we have + to set it to something other than 0, and then create the + .uidvalidity file for future accesses */ + + if (stream->uid_validity == 0) { + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "UIDs considered invalid"); + #endif + FILE *fl; + struct utimbuf tbuf; + stream->uid_validity = time(0); + tbuf.actime = stream->uid_validity; + tbuf.modtime = stream->uid_validity; + LOCAL->scantime = 0; + if ((fl = fopen(tmp, "w"))) { + fclose(fl); + chmod (tmp, S_IRUSR|S_IWUSR); + utime(tmp, &tbuf); + } else { + mm_log("Unable to create/access .uidvalidity file.", WARN); + } + } + + LOCAL->uidinvalid = T; /* UID's are invalid, update them */ + + /* also take this chance to reset the "last UID" to 1. */ + + stream->uid_last = 1; + + maildir_set_lastuid(stream); + + } else { + /* valid file, lets set UID if uid_validity = 0 */ + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "UIDs considered valid"); + #endif + + stream->uid_validity = sbuf2.st_mtime; + + LOCAL->uidinvalid = NIL; + + /* if the UIDs are considered valid, we also want to pull in the + st_mtime of the file .uidlast, if it's there */ + + /* note, we never really ever use this */ + stream->uid_last = maildir_get_lastuid( stream ); + + } + + /* if nothing has changed since we last looked, then we don't have to + * do anything, right */ + + if (sbuf.st_mtime == LOCAL->scantime) { + + /* scan new */ + if ( maildir_copynew(stream) > 0 ) { + docopynew = 0; + goto playitagain; + } + maildir_unlock_nextuid(stream); + return T; + } + + #ifdef NO_UID_VALIDITY + LOCAL->uidinvalid = T; /* force the UIDs to be invalid and reset every time, + useful in an environment without imap servers and + clients that screw up the UIDs.. i'd leave it to + OFF until I really need it though... */ + #endif + + stream->silent = T; + + playitagain: + + { + int msgptr = 1; + int fileptr = 0; + + /* clear these out if they haven't been */ + if ( names ) { + for (i = 0; i < nfiles; i++) + fs_give ((void **) &names[i]); + if (names) + fs_give ((void **) &names); + } + + if ( playcount > 3 ) { + sprintf(tmperror, "imapd aborting scanning maildir box %s", LOCAL->dir); + mm_fatal(tmperror); + exit(-1); + } + + ++playcount; + + uidinvalidthing = LOCAL->uidinvalid; + + nfiles = scandir( LOCAL->dir, &names, maildir_select, maildir_namesort); + + if ( uidinvalidthing != LOCAL->uidinvalid ) { + LOCAL->uidinvalid = T; + stream->uid_validity = 0; + /* blow away the .uidvalidity file */ + sprintf(tmp, "%s/../.uidvalidity", LOCAL->dir); + unlink(tmp); + goto invaliduids; + } + + if ( nfiles < 0 ) { + sprintf(tmperror, "scandir %s failed: %s", LOCAL->dir, strerror(errno)); + mm_fatal(tmperror); + exit(-1); + } + + /* figure out the max uid on the disk (we'll need this later */ + + /* the starting min uid should be 1 */ + if ( stream->uid_last == 0 ) { + stream->uid_last = 1; + } + + if ( ! LOCAL->uidinvalid ) { /* if the UIDs are already invalid, we don't have + to do this stuff. */ + + for (i = 0; i < nfiles; i++) { + + if ( ( tmpuid = maildir_get_uid(names[i]->d_name, LOCAL->dir) ) > 0 ) { + if ( tmpuid > stream->uid_last ) { + stream->uid_last = tmpuid; + } + } + + } + + /* if the lastuid stored on disk is < the lastuid we've + found scanning the directory, then + write the lastuid to the disk */ + + if ( maildir_get_lastuid( stream ) < stream->uid_last ) { + maildir_set_lastuid(stream); + } + + } + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "got uid_last = %d", stream->uid_last); + #endif + + /* compare what we're reading in from disk with what we have in + the message cache, updating things as necessary. + + we iterate through the directory, comparing to the message + cache as we go. If something turns up funky, we might + do a complete reload */ + + + /* we can do this as long as we're not at the end of either of our lists + */ + + while( msgptr <= stream->nmsgs && + fileptr < nfiles ) { + + char msgshortname[MAILTMPLEN]; + char fileshortname[MAILTMPLEN]; + char *c; + + MESSAGECACHE *elt; + + /* retrieve mail elt */ + elt = mail_elt(stream, msgptr); + + /* get the names of the files */ + + strcpy(msgshortname, FILEP(elt)); + strcpy(fileshortname, names[fileptr]->d_name); + + /* quick assertion check. The msgptr's uid MUST be >= fileptr's UID. + if this is not true, someone appended to the BEGINNING of the + mailbox, and we don't like that, do we? */ + + if ( maildir_get_uid( FILEP(elt), LOCAL->dir) > + maildir_get_uid( names[fileptr]->d_name, LOCAL->dir) ) { + mm_log("Disk UID < MsgCache UID", WARN); + reloadall = T; + break; + } + + + /* lop off everything up until the : if it exists */ + + if ( c = strchr( msgshortname, ':' ) ) + *c = 0; + + if ( c = strchr( fileshortname, ':' ) ) + *c = 0; + + /* compare the filenames, if they're equal, we increment both + pointers */ + + if ( ! strcmp( msgshortname, fileshortname ) ) { + + maildir_cache_msg( stream, names[fileptr]->d_name, + msgptr, LOCAL->uidinvalid ); + + ++fileptr; + ++msgptr; + + continue; + } else { + + /* delete the message from the cache, since it might be deleted. */ + /* note, now we don't have to increment msgptr... */ + if ( elt->recent ) { + --recent; + } + fs_give((void **) FILEP_PTR(elt)); + + stream->silent = NIL; + mail_expunged( stream, msgptr ); + stream->silent = T; + } + + } + + if ( reloadall ) { + + msgptr = 1; + + while ( msgptr <= stream->nmsgs ) { + MESSAGECACHE *elt; + elt = mail_elt( stream, msgptr ); + if ( elt->recent ) { + --recent; + } + fs_give((void **) FILEP_PTR(elt)); + mail_expunged( stream, msgptr ); + } + + reloadall = NIL; + + goto playitagain; + + } + + /* if there's unprocessed stuff at the end of the message cache */ + /* note, this is where the "trivial" stuff happens ;) */ + while ( msgptr <= stream->nmsgs ) { + + elt = mail_elt(stream, msgptr); + if ( elt->recent ) { + --recent; + } + fs_give((void **) FILEP_PTR(elt)); + + /* delete the message from the cache */ + stream->silent = NIL; + mail_expunged( stream, msgptr ); + stream->silent = T; + /* don't increment msgptr... stream->nmsgs just got decremented.*/ + } + + /* if there's unprocessed stuff at the end of the file list */ + + while( fileptr < nfiles ) { + + if ( maildir_cache_msg( stream, + names[fileptr]->d_name, + -1, LOCAL->uidinvalid ) == MD_CACHE_RECENT ) { + ++recent; + } + + ++fileptr; + } + + + /* free the names stuff */ + for (i = 0; i < nfiles; i++) + fs_give ((void **) &names[i]); + if (names) + fs_give ((void **) &names); + + /* only after we've read our existing stuff in, do we look at + the new directory and process it */ + + + if ( docopynew && ( maildir_copynew ( stream ) > 0 ) ) { + /* we need to re-scan the directory now... I know, I know... */ + docopynew = 0; + goto playitagain; + } + + stream->silent = NIL; + + mail_exists (stream, stream->nmsgs); + mail_recent (stream, recent); + + /* update uidlast file */ + maildir_set_lastuid(stream); + + + } + + /* update the scantime, we'll need this for later */ + + LOCAL->scantime = sbuf.st_mtime; + + if ( LOCAL->ld >= 0 ) { + maildir_unlock_nextuid(stream); + } + + return T; /* return that we are alive */ + + + } + + static unsigned long file_lastuid = 0; + + unsigned long maildir_get_lastuid( MAILSTREAM *stream ) { + + char tmp[MAILTMPLEN]; + struct stat sbuf; + + sprintf(tmp, "%s/../.uidlast", LOCAL->dir); + + if ( stat(tmp, &sbuf) ) { + + return 1; + + } else { + + file_lastuid = sbuf.st_mtime; + + return (unsigned long) sbuf.st_mtime; + + } + + } + + void maildir_set_lastuid( MAILSTREAM *stream ) { + + int fd; + struct utimbuf tbuf; + char tmp[MAILTMPLEN]; + + sprintf(tmp, "%s/../.uidlast", LOCAL->dir); + + tbuf.actime = stream->uid_last; + tbuf.modtime = stream->uid_last; + + if ( stream->uid_last == file_lastuid ) { + return; + } + + maildir_lock_nextuid(stream); + + /* set it, if we can */ + + if ( utime( tmp, &tbuf ) ) { + if ( errno == ENOENT ) { + /* create the file */ + if ( ( fd = open( tmp, O_WRONLY|O_CREAT|O_EXCL, 0644 ) ) >= 0 ) { + close(fd); + if ( utime(tmp, &tbuf) ) { + syslog(LOG_ERR, "maildir: problem updating last UID errno %d", errno); + } + } else { + syslog(LOG_ERR, "maildir: problem creating last UID file %d", errno); + } + } + } + + } + + + + long maildir_ping (MAILSTREAM *stream) + { + #ifdef UMBC_ENV + if ( unix_check_locked( stream ) ) { + return NIL; + } + #endif + return maildir_ping_core (stream); + } + + int maildir_copynew (MAILSTREAM *stream) + { + char tmp[MAILTMPLEN],file[MAILTMPLEN],newfile[MAILTMPLEN]; + + char *name_base; + char *name_flags; + long uid; + + char tmperror[MAILTMPLEN]; + + char *t; + + int i; + int recent = 0; + long nfiles = 0; + struct direct **names = NIL; + + sprintf (tmp,"%s/../new", LOCAL->dir); + + nfiles = scandir( tmp, &names, maildir_select, maildir_ctimesort ); + + if ( nfiles < 0 ) { + sprintf( tmperror, "scandir %s failed: %s", LOCAL->dir, strerror(errno)); + mm_fatal(tmperror); + exit(-1); + } + + if ( nfiles > 0 ) { + + /* then there is something; try to lock the UID file, we'll need it... + then re-scan the directory. Don't ask. */ + if ( maildir_lock_nextuid(stream) < 0 ) { + mm_fatal("Can't lock nextuid in copynew"); + exit(-1); + } + + /* after we've got the lock, now re-scan because things may have changed. + */ + if ( names ) { + for ( i = 0; i < nfiles; i++ ) + fs_give((void**) &names[i]); + if ( names ) + fs_give((void **) &names); + } + + nfiles = scandir( tmp, &names, maildir_select, maildir_ctimesort ); + + if ( nfiles < 0 ) { + sprintf( tmperror, "scandir %s failed: %s", LOCAL->dir, strerror(errno)); + mm_fatal(tmperror); + exit(-1); + } + + if ( nfiles == 0 ) { + return 1; /* we probably had new things */ + } + } + + + + + for ( i = 0; i < nfiles; i++ ) { + + name_base = strdup(names[i]->d_name); + + if ( t = strstr( name_base, ":") ) { + name_flags = strdup( t ); + *t = 0; + } else { + name_flags = ""; + } + + if ( t = strstr( name_base, ",U") ) { + /* we can just throw this away, we're going to assign a new one + anyhow! */ + *t = 0; + } + + uid = maildir_get_nextuid(stream); + + if ( uid <= 0 ) { + MM_LOG("Can't get next uid, copynew failed.", WARN); + break; + } + + sprintf( newfile, "%s/%s,U%u%s", + LOCAL->dir, + name_base, + uid, + name_flags ); + + + sprintf( file, "%s/%s", tmp, names[i]->d_name ); + + if ( maildir_movemail(file, newfile) == -1 ) { + + if ( errno == EFBIG ) { + LOCAL->toobigflag = 1; + mm_log("Mailbox is too large -- cannot copy new mail", WARN); + break; + } + + mm_log("Unable to read new mail!", WARN); + + syslog(LOG_ERR, "error linking: %d, %s, %s", errno, file, newfile); + + } else { /* everything is ok, the message is moved. Now cache it. */ + + ++recent; + + } + + } + + if ( names ) { + for ( i = 0; i < nfiles; i++ ) + fs_give((void**) &names[i]); + if ( names ) + fs_give((void **) &names); + } + + return recent; + + } + + int maildir_select (const struct dirent *name) + { + if (name->d_name[0] != '.') + return T; + + return NIL; + } + + int maildir_ctimesort (const struct dirent **d1, const struct dirent **d2) { + + /* since the beginning of the filename should be a unix timestamp, + we'll just sort on that to shortcut the stat. it's good to + be king. + */ + + unsigned long t1; + unsigned long t2; + t1 = strtoul((*d1)->d_name, NULL, 10); + t2 = strtoul((*d2)->d_name, NULL, 10); + + if ( t1 == t2 ) { + return strcmp((*d1)->d_name, + (*d2)->d_name); + } else if ( t1 > t2 ) { + return 1; + } else { + return -1; + } + } + + + + int maildir_namesort (const struct dirent **d1,const struct dirent **d2) + { + + unsigned long t1, t2; + + if ( ! uidinvalidthing ) { + + /* first, try to sort by UIDs */ + t1 = maildir_get_uid((*d1)->d_name, NULL); + t2 = maildir_get_uid((*d2)->d_name, NULL); + + } else { + t1 = t2 = 0; + } + if ( ( t1 == 0 ) && + ( t2 == 0 ) ) { + /* do the original sort */ + t1 = strtoul((*d1)->d_name, NULL, 10); + t2 = strtoul((*d2)->d_name, NULL, 10); + if (t1 == t2) { + return strcmp ((*d1)->d_name,(*d2)->d_name); + } + else if (t1 > t2) { + return 1; + } + return -1; + + } else if ( t1 == t2 ) { + uidinvalidthing++; + /* this should never happen */ + syslog(LOG_ERR, "maildir_namesort saw two messages with the same UID %s, %s", (*d1)->d_name, (*d2)->d_name); + return strcmp( (*d1)->d_name, (*d2)->d_name) ; + } else if ( t1 == 0 ) { + return 1; + } else if ( t2 == 0 ) { + return -1; + } else if ( t1 > t2 ) { + return 1; + } else { + return -1; + } + + } + + + /* Maildir garbage collect stream + */ + + void maildir_gc (MAILSTREAM *stream,long gcflags) + { + + if (gcflags & GC_TEXTS) { /* garbage collect texts? */ + /* flush texts from cache */ + /* if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr); + // if (stream->text) fs_give ((void **) &stream->text); + // stream->msgno = 0; invalidate stream text + */ + } + } + + /* Maildir close + */ + + void maildir_close (MAILSTREAM *stream, long options) + { + MESSAGECACHE *elt; + int i; + int silent = stream->silent; + mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); + + stream->silent = T; + + if ( options & CL_EXPUNGE) maildir_expunge(stream); + + for (i = 1; i <= stream->nmsgs; i++) + if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) && FILEP(elt)) { + fs_give ((void **) FILEP_PTR(elt)); + FILEP(elt) = 0; /* otherwise pine coredumps */ + } + + if (LOCAL) { /* only if a stream is open */ + if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); + maildir_gc (stream,GC_TEXTS); /* free local cache */ + /* free local scratch buffer */ + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); + if (LOCAL->lastheader) fs_give((void **) &LOCAL->lastheader); + if (LOCAL->lastbody) fs_give((void **) &LOCAL->lastbody); + if (LOCAL->lastmsg) fs_give((void **) &LOCAL->lastmsg); + /* nuke the local data */ + fs_give ((void **) &stream->local); + stream->dtb = NIL; /* log out the DTB */ + } + + stream->silent = silent; + } + + void maildir_check (MAILSTREAM *stream) + { + /* Perhaps in the future this will preserve flags */ + if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL); + } + + long maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags) + { + MESSAGECACHE *elt; + + /* UID call "impossible" */ + if (flags & FT_UID) return NIL; + elt = mail_elt (stream,msgno);/* get elt */ + + if ( ! maildir_fetch_work( stream, elt ) ) { + return NIL; + } + + if (!(flags & FT_PEEK)) { /* mark as seen */ + mail_elt (stream,msgno)->seen = T; + maildir_flagmsg (stream, mail_elt(stream,msgno)); + mm_flags (stream,msgno); + } + + INIT(bs, mail_string, LOCAL->lastbody, elt->private.msg.text.text.size); + + return T; + + } + + int maildir_fetch_work ( MAILSTREAM *stream, MESSAGECACHE *elt ) { + + char tmp[MAILTMPLEN]; + static char *s = NULL; + static unsigned long s_size = 0; + unsigned long i, hdrsize; + char *b; + struct stat sbuf; + struct tm *tm; + int fd; + + DIR *dir; + struct dirent *stuff; + char *msgprefix; + char *pp; + + sprintf(tmp, "%s/%s", LOCAL->dir, (char *) FILEP(elt) ); + + if ( ! strcmp(tmp, LOCAL->lastmsg) ) { + return T; + } + + /* check to see if we can open the file in 'tmp' */ + + fd = open ( tmp, O_RDONLY, NIL ); + + if ( fd < 0 ) { + + msgprefix = cpystr( (char *) FILEP(elt) ); + + if ( pp = strchr( msgprefix, ',' ) ) { + *pp = '\0'; + } + + if ( errno == ENOENT ) { + /* try to find the new filename, someone may have + updated the flags since we last checked. */ + if ( dir = opendir( LOCAL->dir ) ) { + while ( stuff = readdir(dir) ) { + if ( ! strncmp( stuff->d_name, msgprefix, strlen(msgprefix) ) ) { + /* we have a match, update the FILEP */ + sprintf(LOCAL->buf, "maildir_fetch_work: Filename changed on disk: mailbox %s, oldfile %s, newfile %s", + LOCAL->dir, + (char *) FILEP(elt), + stuff->d_name); + mm_log(LOCAL->buf, WARN); + /* call maildir_cache_msg, as this will update the flags + and such for us. */ + maildir_cache_msg( stream, stuff->d_name, + elt->msgno, 0 ); + break; + } + } + + if ( dir == NULL ) { + sprintf(LOCAL->buf, "maildir_fetch_work: Can't seem to find new file: mailbox %s, oldfile %s", + LOCAL->dir, + (char *) FILEP(elt) + ); + mm_log(LOCAL->buf, WARN); + } else { + sprintf(tmp, "%s/%s", LOCAL->dir, (char *) FILEP(elt) ); + + fd = open ( tmp, O_RDONLY, NIL ); + } + + closedir(dir); + + } else { + sprintf(LOCAL->buf, "maildir_fetch_work: can't open directory %s", + LOCAL->dir); + mm_fatal(LOCAL->buf); + exit(-1); + } + + } + + } + + + if ( fd >= 0 ) { + + fstat(fd, &sbuf); + /* make plausible IMAPish date string */ + tm = gmtime (&sbuf.st_mtime); + elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; + elt->year = tm->tm_year + 1900 - BASEYEAR; + elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; + elt->seconds = tm->tm_sec; + elt->zhours = 0; elt->zminutes = 0; + + /* this can only get bigger. */ + if ( s_size < ( sbuf.st_size + 1 ) ) { + s_size = sbuf.st_size + 1; + if ( s ) + fs_give ((void **) &s); + s = fs_get (s_size); + } + + if ( read(fd, s, sbuf.st_size) != sbuf.st_size ) { + mm_fatal("Short message read."); + close(fd); + exit(-1); + } + + s[sbuf.st_size] = '\0'; + + close(fd); + + /* store the thing in the stuff. */ + strcpy( LOCAL->lastmsg, tmp ); + + /* scan for the beginning of the body */ + + + for (i = 0,b = s; *b && !(i && (*b == '\n')); i = (*b++ == '\n')); + hdrsize = ( *b ? ++b : b)-s; + + elt->private.msg.header.text.size = + strcrlfcpy( &LOCAL->lastheader, &LOCAL->lastheaderlen, s, hdrsize ); + + if ( ! elt->private.msg.header.text.data ) { + elt->private.msg.header.text.data = fs_get(elt->private.msg.header.text.size + 1 ); + strcpy( (char *) elt->private.msg.header.text.data, LOCAL->lastheader ); + } + + elt->private.msg.text.text.size = + strcrlfcpy( &LOCAL->lastbody, &LOCAL->lastbodylen, b, sbuf.st_size - hdrsize ); + + elt->rfc822_size = + elt->private.msg.header.text.size + + elt->private.msg.text.text.size; + + return T; + + } else if ( errno == ETIMEDOUT ) { + mm_fatal ("Cannot open message file, mailbox unavailable."); + exit ( -1 ); + } else { + return NIL; + } + } + + + + /* Maildir fetch message header + */ + + + char *maildir_fetchheader (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags) + { + MESSAGECACHE *elt; + *length = 0; /* default to empty */ + if (flags & FT_UID) return "";/* UID call "impossible" */ + elt = mail_elt (stream,msgno);/* get elt */ + + + if ( elt->private.msg.header.text.data ) { + *length = elt->private.msg.header.text.size; + return (char *) elt->private.msg.header.text.data; + } + + /* "do something" if we don't have header data, or, + if we have our body flag set, don't have a body */ + + if ( ! maildir_fetch_work( stream, elt ) ) { + return ""; + } + + *length = elt->private.msg.header.text.size; + + return LOCAL->lastheader; + + } + + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags) + { + unsigned long i,j; + /* ugly and slow */ + if (stream && LOCAL && ((flags & FT_UID) ? + mail_uid_sequence (stream,sequence) : + mail_sequence (stream,sequence))) + for (i = 1; i <= stream->nmsgs; i++) + if (mail_elt (stream,i)->sequence) maildir_fetchheader (stream,i,&j,NIL); + } + + void maildir_scan( MAILSTREAM *stream, char *ref, char *pat, char *contents ) + { + dummy_scan(stream, ref, pat, contents); + } + + /* Maildir find list of subscribed mailboxes + * Accepts: mail stream + * pattern to search + */ + + void maildir_list (MAILSTREAM *stream,char *ref, char *pat) + { + return; + } + + void *maildir_parameters (long function,void *value) + { + return NIL; + } + + long maildir_create (MAILSTREAM *stream,char *mailbox) + { + char tmp[MAILTMPLEN]; + char err[MAILTMPLEN]; + char *s; + int fnlen, i; + char *subdir_names[] = {"/cur","/new","/tmp",NULL}; + + #ifdef UMBC_ENV + if ( unix_check_locked ( stream ) ) { + return NIL; + } + #endif + + /* must not already exist */ + if (access (maildir_file (tmp,mailbox),F_OK) == 0) { + sprintf (err,"Can't create mailbox %s: mailbox already exists",mailbox); + mm_log (err,ERROR); + return NIL; + } + + maildir_file (tmp,mailbox); /* get file name */ + fnlen = strlen (tmp); + tmp[fnlen - 4] = '\0'; /* making main directory's name */ + fnlen -= 4; + + /* okay, try to add support for adding hiearchys of directories, this + is done by scanning for /'s.... */ + + s = tmp; + + while ((s = strstr(s, "/")) != 0) { + *s = '\0'; + if (mkdir (tmp,0700) && *s != '\0') /* trying to make the dir */ + if (errno != EEXIST) { + sprintf (err,"Can't create mailbox %s: %s %s", + mailbox,tmp,strerror (errno)); + mm_log (err,ERROR); + return NIL; + } + *s = '/'; + s++; + } + + if (mkdir (tmp,0700)) { /* try to make new dir */ + sprintf (err,"Can't create mailbox %s: %s %s", + mailbox,tmp,strerror (errno)); + mm_log (err,ERROR); + return NIL; + } + + for (i = 0; subdir_names[i]; i++) { + strcpy (tmp + fnlen,subdir_names[i]); + + if (mkdir (tmp,0700)) { /* try to make new dir */ + sprintf (err,"Can't create mailbox %s: %s %s", + mailbox,tmp,strerror (errno)); + mm_log (err,ERROR); + return NIL; + } + } + + return T; /* return success */ + } + + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) + { + char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN]; + char *s; + char tmpbuf[MAILTMPLEN]; + char *flagstring; + + /* if we're in a TOOBIG state, we just return... */ + + if ( LOCAL->toobigflag ) { + return; + } + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "in maildir_flagmsg for %d", elt->private.uid); + #endif + + sprintf(oldfile, "%s/%s", LOCAL->dir, FILEP(elt)); + + /* chop the flags off the filename, we won't need these anymore */ + + if ((s = strchr ((char *) FILEP(elt), ':'))) *s = 0; + + /* look to see if we've got our UID encoded into the filename */ + + if ( maildir_get_uid( FILEP(elt), NULL ) != elt->private.uid ) { + + /* chop off the ",U" part if it's there */ + if (( s = strstr(FILEP(elt), ",U") ) ) + *s = 0; + + /* build a base filename */ + + sprintf( tmpbuf, "%s,U%u", FILEP(elt), elt->private.uid ); + + + } else { + + /* the base filename hasn't changed */ + + strcpy( tmpbuf, FILEP(elt) ); + + } + + /* append the flags */ + + sprintf ( fn, "%s:2,%s", tmpbuf, + flagstring = maildir_make_flagstring ( elt->deleted, + elt->seen, + elt->flagged, + elt->answered, + elt->draft, + elt->user_flags ) + ); + + fs_give( ( void **) &flagstring ); + + sprintf (newfile,"%s/%s",LOCAL->dir,fn); + + /* our filename has changed, and so probably have our flags */ + if ( strcmp(newfile, oldfile) ) { + + mm_flags(stream, elt->msgno); + + /* rename the file with new flags */ + /* but only do it if we actually have to. syscalls are expensive. */ + if ( rename (oldfile,newfile) < 0 ) { + if ( errno == EFBIG ) { + mm_log("Mailbox is too large: cannot update message status!", ERROR); + LOCAL->toobigflag = 1; + return; + } + if ( errno == ENOENT ) { + /* the message could have been flagged on disk by another process, + or the like. Let them know. */ + sprintf(oldfile,"Unable to write flags: perhaps another process has updated them?"); + mm_log(oldfile, ERROR); + return; + } else { + sprintf(oldfile,"Unable to write flags to disk: %s",strerror (errno)); + mm_log(oldfile,ERROR); + } + } + + } + + if ( strcmp( FILEP(elt), fn ) ) { + /* update the file name in cache */ + fs_give ((void **) FILEP_PTR(elt)); + FILEP(elt) = cpystr (fn); + } + + + } + + void maildir_expunge (MAILSTREAM *stream) + { + MESSAGECACHE *elt; + unsigned long i = 1; + unsigned long n = 0; + unsigned long recent = stream->recent; + + #ifdef UMBC_ENV + if ( unix_check_locked ( stream ) ) { + return; + } + #endif + + maildir_gc (stream,GC_TEXTS); /* invalidate texts */ + while (i <= stream->nmsgs) { /* for each message */ + /* if deleted, need to trash it */ + if ((elt = mail_elt (stream,i))->deleted) { + sprintf (LOCAL->buf,"%s/%s",LOCAL->dir,(char *) FILEP(elt)); + if (unlink (LOCAL->buf)) {/* try to delete the message */ + sprintf (LOCAL->buf,"Expunge of message %ld failed, aborted: %s",i, + strerror (errno)); + mm_log (LOCAL->buf,WARN); + break; + } + /* free the cached filename */ + if (FILEP(elt)) { + fs_give ((void **) FILEP_PTR(elt)); + FILEP(elt) = 0; /* otherwise pine coredumps */ + } + if (elt->recent) --recent;/* if recent, note one less recent message */ + mail_expunged (stream,i); /* notify upper levels */ + n++; /* count up one more expunged message */ + } + else i++; /* otherwise try next message */ + } + if (n) { /* output the news if any expunged */ + sprintf (LOCAL->buf,"Expunged %ld messages",n); + mm_log (LOCAL->buf,(long) NIL); + } + else mm_log ("No messages deleted, so no update needed",(long) NIL); + /* notify upper level of new mailbox size */ + mail_exists (stream,stream->nmsgs); + mail_recent (stream,recent); + } + + /* dont forget to process options in here */ + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) + { + STRING st; + MESSAGECACHE *elt; + struct stat sbuf; + int fd; + long i; + char *fs = NULL; + char *flagstring = NULL; + unsigned char *bits = NULL; + char *s,tmp[MAILTMPLEN]; + mailproxycopy_t pc = ( mailproxycopy_t ) mail_parameters(stream, + GET_MAILPROXYCOPY, NIL); /* make sure valid mailbox */ + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "maildir_copy mailbox parameter is %s", mailbox); + #endif + + if ( ! maildir_isvalid (mailbox, NIL) ) + + switch (errno) { + /* case ENOENT: + mm_notify( stream, "[TRYCREATE] Must create mailbox before copy", NIL); + return NIL; */ + case EINVAL: + if (pc) return (*pc) (stream, sequence, mailbox, options); + sprintf( LOCAL->buf, "Invalid Maildir-format mailbox name: %.80s", mailbox); + mm_log(LOCAL->buf, ERROR); + return NIL; + default: + if (pc) return (*pc) (stream, sequence, mailbox, options); + sprintf (LOCAL->buf, "Not a Maildir-format mailbox: %.80s", mailbox); + mm_log(LOCAL->buf, ERROR); + return NIL; + } + + /* copy the messages */ + if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : + mail_sequence (stream,sequence)) + for (i = 1; i <= stream->nmsgs; i++) + if ((elt = mail_elt (stream,i))->sequence) { + + /* "fetch" the message */ + maildir_fetch_work(stream, elt); + + sprintf (LOCAL->buf,"%s/%s",LOCAL->dir,(char *) FILEP(elt)); + if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) { + if ( errno == ETIMEDOUT ) { + mm_log("cannot open message, connection timed out.", ERROR); + } + return NIL; + } + fstat (fd,&sbuf); /* get size of message */ + /* slurp message */ + read (fd,s = (char *) fs_get (sbuf.st_size +1),sbuf.st_size); + s[sbuf.st_size] = '\0'; /* tie off file */ + close (fd); /* flush message file */ + INIT (&st,mail_string,(void *) s,sbuf.st_size); + sprintf (LOCAL->buf, "(%s)", + flagstring = maildir_unparse_bits ( + bits = maildir_keys_to_bits( fs = maildir_make_flagstring( + elt->deleted, + elt->seen, + elt->flagged, + elt->answered, + elt->draft, + elt->user_flags ) ) ) ); + fs_give ((void **) &bits); + fs_give ((void **) &flagstring); + fs_give ((void **) &fs); + mail_date (tmp,elt); /* generate internal date */ + if (!maildir_append_old (stream,mailbox,LOCAL->buf,tmp,&st)) { + fs_give ((void **) &s); /* give back temporary space */ + return NIL; + } + fs_give ((void **) &s); /* give back temporary space */ + } + return T; /* return success */ + } + + long maildir_append_old (MAILSTREAM *stream,char *mailbox,char *flags,char *date, + STRING *message) + { + int fd; + char c,*s; + char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; + MESSAGECACHE elt; + long i; + long size = 0; + long ret = LONGT; + time_t timestamp; + unsigned char *flagbits = NULL; + char *fs = NULL; + + /* + This is intentionaly made static. Users can ask to save a LOT of messages + at once and this program can do that within one second. Dan's assumption + that time+pid+hostname always will be unique stops being true in this + case. So we will add yet another number to host part of message file's + name. Hostname is used only to make filename unique and Dan explicitly + says that "<...> Other than this [skipping filenames starting at dot] , + readers should not attempt to parse filenames. <...>". Therefore this + addition should be no problem. Am I right, Dan? --AK + */ + + static unsigned int transact = 0; + + /* get flags if given */ + flagbits = maildir_parse_flags( flags ); + + if (date) { /* want to preserve date? */ + /* yes, parse date into an elt */ + if (!mail_parse_date (&elt,date)) { + sprintf (tmp,"Bad date in append: %s",date); + mm_log (tmp,ERROR); + return NIL; + } + } + /* N.B.: can't use LOCAL->buf for tmp */ + /* make sure valid mailbox */ + if (!maildir_isvalid (mailbox, NIL)) { + sprintf (tmp,"Not a valid Maildir mailbox: %s",mailbox); + mm_log (tmp,ERROR); + return NIL; + } + /* build file name we will use */ + sprintf (file,"%u.%d.%d.%s:2,%s", + time (0),getpid (),transact++, maildir_mylocalhost (), + fs = maildir_bits_to_keys( flagbits ) ); + + /* build tmp file name */ + sprintf (path1,"%s/../tmp/%s",maildir_file (tmp,mailbox),file); + + if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { + sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); + mm_log (tmp,ERROR); + return NIL; + } + i = SIZE (message); /* get size of message */ + s = (char *) fs_get (i + 1); /* get space for the data */ + /* copy the data w/o CR's */ + while (i--) if ((c = SNX (message)) != '\015') s[size++] = c; + mm_critical (stream); /* go critical */ + /* write the data */ + if ((write (fd,s,size) < 0) || fsync (fd)) { + unlink (path1); /* delete message */ + sprintf (tmp,"Message append failed: %s",strerror (errno)); + mm_log (tmp,ERROR); + ret = NIL; + } + + /* utime message to preserved date (in elt) */ + if ( date ) { + struct utimbuf tbuf; + timestamp = mail_longdate ( &elt ); + tbuf.actime = timestamp; + tbuf.modtime = timestamp; + utime( path1, &tbuf ); + } + + /* build final filename to use */ + sprintf (path2,"%s/../new/%s",maildir_file (tmp,mailbox),file); + + if (maildir_movemail(path1, path2) < 0 ) { + sprintf (tmp,"Message append failed: %s",strerror (errno)); + mm_log (tmp,ERROR); + ret = NIL; + } + + close (fd); /* close the file */ + mm_nocritical (stream); /* release critical */ + fs_give ((void **) &s); /* flush the buffer */ + fs_give ((void **) &flagbits); + fs_give ((void **) &fs ); + return ret; + } + + long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data) + { + int fd; + char c,*s; + char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; + MESSAGECACHE elt; + long i; + long size = 0; + long ret = LONGT; + STRING *message; + char *flags; + char *date; + time_t timestamp; + unsigned char *flagbits = NULL; + char *fs = NULL; + + /* + This is intentionaly made static. Users can ask to save a LOT of messages + at once and this program can do that within one second. Dan's assumption + that time+pid+hostname always will be unique stops being true in this + case. So we will add yet another number to host part of message file's + name. Hostname is used only to make filename unique and Dan explicitly + says that "<...> Other than this [skipping filenames starting at dot] , + readers should not attempt to parse filenames. <...>". Therefore this + addition should be no problem. Am I right, Dan? --AK + */ + + static unsigned int transact = 0; + + /* N.B.: can't use LOCAL->buf for tmp */ + /* make sure valid mailbox */ + if (!maildir_isvalid (mailbox, NIL)) { + sprintf (tmp,"Not a valid Maildir mailbox: %s",mailbox); + mm_log (tmp,ERROR); + return NIL; + } + + + if (!MM_APPEND(af) (stream, data, &flags, &date, &message)) return NIL; + + do { + + size = 0; + + if (date) { /* want to preserve date? */ + /* yes, parse date into an elt */ + if (!mail_parse_date (&elt,date)) { + sprintf (tmp,"Bad date in append: %s",date); + mm_log (tmp,ERROR); + return NIL; + } + } + + flagbits = maildir_parse_flags( flags ); + + /* build file name we will use */ + sprintf (file,"%u.%d.%09u.%s:2,%s", + time (0),getpid (),transact++,maildir_mylocalhost (), + fs = maildir_bits_to_keys( flagbits ) ); + + /* build tmp file name */ + sprintf (path1,"%s/../tmp/%s",maildir_file (tmp,mailbox),file); + + if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { + sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); + mm_log (tmp,ERROR); + return NIL; + } + + i = SIZE (message); /* get size of message */ + + s = (char *) fs_get (i + 1); /* get space for the data */ + /* copy the data w/o CR's */ + + while (i--) if ((c = SNX (message)) != '\015') s[size++] = c; + + mm_critical (stream); /* go critical */ + /* write the data */ + + if ((write (fd,s,size) < 0) || fsync (fd)) { + unlink (path1); /* delete message */ + sprintf (tmp,"Message append failed: %s",strerror (errno)); + mm_log (tmp,ERROR); + ret = NIL; + } + + /* utime message to preserved date (in elt) */ + if ( date ) { + struct utimbuf tbuf; + timestamp = mail_longdate(&elt); + tbuf.actime = timestamp; + tbuf.modtime = timestamp; + utime( path1, &tbuf ); + } + + /* build final filename to use */ + sprintf (path2,"%s/../new/%s",maildir_file (tmp,mailbox),file); + + if (maildir_movemail(path1, path2) < 0 ) { + sprintf (tmp,"Message append failed: %s",strerror (errno)); + mm_log (tmp,ERROR); + ret = NIL; + } + + close (fd); /* close the file */ + mm_nocritical (stream); /* release critical */ + fs_give ((void **) &s); /* flush the buffer */ + + fs_give ((void **) &fs ); + fs_give ((void **) &flagbits); + + MM_APPEND(af)(stream,data,&flags,&date,&message); + + } while (message); + + return ret; + } + + long maildir_delete (MAILSTREAM *stream,char *mailbox) + { + DIR *dirp; + struct direct *d; + int i,j; + char tmp[MAILTMPLEN],err[MAILTMPLEN]; + char new[MAILTMPLEN]; + char *subdir_names[] = {"tmp/","new/","cur/",NULL}; + + + /* check if mailbox even exists */ + if (!maildir_isvalid (mailbox,NIL)) { + sprintf (tmp,"Can't delete mailbox %s: no such mailbox",mailbox); + mm_log (tmp,ERROR); + return NIL; + } + + /* rename the folder */ + + sprintf(new, ".delete.%d", time(0)); + + if ( ! maildir_rename( stream, mailbox, new ) ) { + return NIL; + } + + mm_critical(stream); + + #ifdef MAILDIR_DEBUG + syslog(LOG_ERR, "maildir_delete op: %s ( %s )", mailbox, new ); + #endif + + /* get name of directory */ + i = strlen (maildir_file (tmp,new)) + 1; + + for (j = 0; subdir_names[j]; j++) { + + strcpy (tmp + i - 4,subdir_names[j]); + + if (dirp = opendir (tmp)) { /* open directory */ + while (d = readdir (dirp)) /* empty the directory */ + if (strcmp (d->d_name,".") && strcmp (d->d_name,"..")) { + strcpy (tmp + i,d->d_name); + unlink (tmp); + } + closedir (dirp); /* flush directory */ + } + + /* remove the subdir */ + tmp[i] = 0; + + if ( rmdir(tmp) ) { + sprintf(err, "Cannot delete folder component %s, %s", tmp, + strerror(errno) ); + mm_log(err, ERROR); + syslog(LOG_ERR, "%s", err); + } + + } + + /* blow away .uidvalidity & .uidlast */ + + strcpy(tmp + i - 4, ".uidvalidity"); + unlink(tmp); + strcpy(tmp + i - 4, ".uidlast"); + unlink(tmp); + + /* try to remove the directory */ + *(tmp + i - 5) = '\0'; + + if (rmdir (tmp)) { + sprintf (err,"Can't delete mailbox %s: %s",mailbox,strerror (errno)); + mm_log (err,ERROR); + mm_nocritical(stream); + return NIL; + } + mm_nocritical(stream); + return T; /* return success */ + } + + long maildir_rename (MAILSTREAM *stream,char *old,char *new) + { + char tmp[MAILTMPLEN],tmpnew[MAILTMPLEN],tmpold[MAILTMPLEN]; + + /* old mailbox name must be valid */ + if (!maildir_isvalid (old,NIL)) { + sprintf (tmp,"Can't rename mailbox %s: no such mailbox",old); + mm_log (tmp,ERROR); + return NIL; + } + + /* new mailbox name must not exist */ + if (access (maildir_file (tmp,new),F_OK) == 0) { + sprintf (tmp,"Can't rename to mailbox %s: destination already exists",new); + mm_log (tmp,ERROR); + return NIL; + } + + /* try to rename the directory */ + /* but fail miserably because whomever + wrote this little bit of code didn't + consider what maildir_file really + returned. */ + + maildir_file(tmpold, old); + maildir_file(tmpnew, new); + + + if ( ! ( tmpold && tmpnew ) ) { + sprintf(tmp, "Couldn't convert mailbox name to path"); + mm_log(tmp, ERROR); + return NIL; + } + + /* chop /cur off of what's in tmpold & tmpnew */ + maildir_chopcur(tmpold); + maildir_chopcur(tmpnew); + + if (rename ( tmpold , tmpnew )) { + sprintf (tmp,"Can't rename mailbox %s to %s: %s",tmpold,tmpnew,strerror (errno)); + mm_log (tmp,ERROR); + return NIL; + } + return T; /* return success */ + } + + long maildir_sub (MAILSTREAM *stream,char *mailbox) + { + char *s,tmp[MAILTMPLEN]; + struct stat sbuf; + /* must be valid local mailbox */ + if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) + ) return sm_subscribe (mailbox); + sprintf (tmp,"Can't subscribe %s: not a mailbox",mailbox); + MM_LOG (tmp,ERROR); + return NIL; + } + + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat) + { + if ( stream ) dummy_lsub(NIL, ref, pat); + /* why was this stuff here? */ + #if 0 + void *sdb = NIL; + char *s; + + /* get canonical form of name */ + if ((s = sm_read (&sdb))) { + do if (pmatch_full (s,pat,'/')) mm_lsub (stream,'/',s,NIL); + while (s = sm_read (&sdb)); /* until no more subscriptions */ + } + #endif + } + + unsigned long maildir_get_uid( char *fn, char *dir) { + + char *p; + unsigned long uid; + char *thingy; + + if ( uidinvalidthing ) { + return 0; + } + + if ( ( thingy = strstr( fn, ",U" ) ) && + ( strlen(thingy) > 2 ) ) { + + uid = strtoul(thingy + 2, &p, 10); + + if ( uid > 0 ) { + return uid; + } + + } + + return 0; + + } + + int maildir_movemail ( char *src, char *dest ) { + + + /* ok, we moved this to another function in case we wanted to get + frisky, and actually be able to handle cross-device renames and such */ + + if ( rename(src, dest) == -1 ) { + + if ( errno == EXDEV ) { + + /* copy the file and delete it -- right now, we're just going to log + an error */ + + syslog(LOG_ERR, "maildir: tried to move a message across a volume boundry %s, %s", src, dest ); + + } + + if ( errno == EFBIG ) { + + syslog(LOG_ERR, "maildir: folder too big %s", dest ); + + } + + + return -1; + + } + + return 0; + + } + + int maildir_cache_msg ( MAILSTREAM *stream, + char *file, + int idnum, + int uidinvalid ) { + + MESSAGECACHE *elt; + int new = 0; + int update = 0; + char *s; + int silent; + int deleted, seen, flagged, answered, draft; + unsigned long user_flags; + char *flagstring; + char *oldflagstring; + unsigned char *bits; + + unsigned long uid; + + + /* before we create an elt, see if we can actually populate a UID here. */ + uid = maildir_get_uid( file , NULL); + + if ( uid <= 0 ) { + /* then we need to be able to get the next UID before we can continue */ + uid = maildir_get_nextuid(stream); + if ( uid <= 0 ) { + /* we can't get a nextuid, so we can't add it :( */ + MM_LOG("Can't get next uid...", WARN); + return MD_CACHE_ERROR; + } + } + + + if ( idnum < 0 ) { /* it's an add */ + + idnum = stream->nmsgs + 1; + + silent = stream->silent; + /* stream->silent = NIL; */ + mail_exists(stream, idnum); + /* stream->silent = silent; */ + + ++new; + + } + + /* populate the appropraite data */ + + elt = mail_elt(stream, idnum); + + FILEP(elt) = cpystr( file ); + + if ( uid != elt->private.uid ) { + update++; + elt->private.uid = uid; + } + + if ( elt->valid ) { + oldflagstring = maildir_make_flagstring ( elt->deleted, + elt->seen, + elt->flagged, + elt->answered, + elt->draft, + elt->user_flags ); + } else { + oldflagstring = cpystr(""); + } + + elt->valid = T; + + if ((s = strstr (file,":3,")) || + (s = strstr (file,":2,")) ) { + s += 3; + flagstring = s; + } else { + flagstring = ""; + } + + /* interpret flagstring */ + maildir_interpret_bits( bits = maildir_keys_to_bits ( flagstring ), + &deleted, + &seen, + &flagged, + &answered, + &draft, + &user_flags ); + + elt->deleted = deleted; + elt->seen = seen; + elt->flagged = flagged; + elt->answered = answered; + elt->draft = draft; + elt->user_flags = user_flags; + + /* check to see if the flags changed from the previous. + we can do this by just comparing the old and new flag + keys */ + + + if ( ( ! new ) && + strcmp( oldflagstring, flagstring ) ) { + silent = stream->silent; + /* stream->silent = NIL; xxx possibly leave this out*/ + mm_flags( stream, idnum ); + stream->silent = silent; + } + + if ( update ) { + maildir_flagmsg(stream, elt); + } + + /* clear stuff that we've used */ + fs_give ( (void **) &bits ); + fs_give ( (void **) &oldflagstring ); + + if ( new ) { + elt->recent = T; + return MD_CACHE_RECENT; + } else { + return MD_CACHE_OK; + } + + } + + int maildir_lock_nextuid(MAILSTREAM *stream) { + + char lockfile[MAILTMPLEN]; + int fd; + int retry = 60; + struct utimbuf timep; + + if ( LOCAL->ld >= 0 ) { + return LOCAL->ld; + } + + sprintf(lockfile, "%s/../.uidlast", LOCAL->dir); + + if ( ( fd = open(lockfile, O_WRONLY) ) < 0 ) { + if ( errno != ENOENT ) { + mm_fatal("Can't open last UID file!"); + return LOCAL->ld = -1; + } else { + fd = open(lockfile, O_WRONLY|O_CREAT, 0600); + if( fd < 0 ) { + mm_fatal("Can't create last UID file!"); + return LOCAL->ld -1; + } + /* might as well set the time on it to 1 */ + timep.actime = 1; + timep.modtime = 1; + utime( lockfile, &timep); + } + } + + /* ok, it's open, we can lock it */ + /* get lock on UID file */ + while( flock(fd, LOCK_EX|LOCK_NB) ) { + + if ( errno == EWOULDBLOCK ) { + syslog(LOG_ERR, "maildir: waiting for lock on %s", lockfile); + sleep(1); + retry--; + } else { + syslog(LOG_ERR, "maildir: error locking %s", lockfile); + close(fd); + return LOCAL->ld = -2; + } + + if ( retry == 0 ) { + /* don't bother, we probably can't get the lock */ + MM_LOG("Cannot get UID lock, unable to assign new UIDs at this time.", WARN); + close(fd); + return LOCAL->ld = -2; + } + + } + + /* we have the lock! */ + return LOCAL->ld = fd; + } + + void maildir_unlock_nextuid(MAILSTREAM *stream) { + + if ( LOCAL->ld >= 0 ) { + maildir_set_lastuid( stream ); + + flock(LOCAL->ld, LOCK_UN); + close(LOCAL->ld); + LOCAL->ld = -1; + } + } + + + unsigned long maildir_get_nextuid( MAILSTREAM *stream ) { + + /* get a lock on .uidnext */ + + int fd; + char lockfile[MAILTMPLEN]; + + sprintf(lockfile, "%s/../.uidlast", LOCAL->dir); + + /* if we're already locked, just increment uid_last and return + it */ + + if ( LOCAL->ld >= 0 ) { + return ++stream->uid_last; + } + + fd = maildir_lock_nextuid(stream); + + if ( fd >= 0 ) { + /* get last UID */ + stream->uid_last = maildir_get_lastuid( stream ); + return ++stream->uid_last; + } else { + return 0; + } + } + + void maildir_chopcur( char *path ) { + + char *c; + c = path + strlen(path); + + while(c != path) { + if ( *c == '/' ) { + *c = 0; + return; + } + --c; + } + + } + + + char *maildir_unparse_bits( unsigned char *bits ) { + + char *string; + unsigned int len = 0; + int i; + + /* calculate the length of the string we'll need */ + for(i = 0; extend_flags[i]; ++i) { + if ( bits[extend_flag_keys[i]] ) { + len += strlen(extend_flags[i]) + 2; + } + } + for(i = 0; standard_flags[i]; ++i) { + if ( bits[standard_flag_keys[i]] ) { + len += strlen(standard_flags[i]) + 2; + } + } + + string = fs_get( len ); *string = '\0'; + + for(i = 0; extend_flags[i]; ++i) { + if ( bits[extend_flag_keys[i]] ) { + strcat( string, extend_flags[i]); + strcat( string, " "); + } + } + for(i = 0; standard_flags[i]; ++i) { + if ( bits[standard_flag_keys[i]] ) { + strcat( string, standard_flags[i] ); + strcat( string, " "); + } + } + + return string; + + } + + unsigned char *maildir_parse_flags( char *flags ) { + + /* first, if it starts with a '(', it's a list, if it doesn't, + it's a single flag */ + + unsigned char *bits; + char *s; + int i; + char *newflags; + char warning[255]; + + bits = fs_get(sizeof(unsigned char) * 255); + + memset( bits, 0, sizeof(unsigned char) * 255 ); + + if ( ! flags ) { + return bits; + } + + if ( *flags == '(' ) { + ++flags; + if ( s = strchr(flags, ')' ) ) { + *s = 0; + } + } + + /* these are separated by spaces. */ + + while( *flags ) { + + if ( *flags == ' ' ) { + ++flags; + } + + if ( s = strchr(flags, ' ') ) { + *s = 0; + newflags = s + 1; + } else { + newflags = flags + strlen(flags); + } + + for(i = 0; standard_flags[i]; ++i) { + if ( ! strcasecmp(standard_flags[i], flags) ) { + bits[standard_flag_keys[i]] = 1; + flags = newflags; + } + } + + if ( flags != newflags ) { + for ( i = 0; extend_flags[i]; ++i) { + if ( !strcasecmp(extend_flags[i], flags) ) { + bits[extend_flag_keys[i]] = 1; + flags = newflags; + } + } + } + + if ( flags != newflags ) { + sprintf(warning, "Unparsable flag: %s", flags); + mm_log(warning, WARN); + } + + flags = newflags; + + } + + return bits; + + } + + + /* takes a string of maildir flag keys, and returns an array + * where the array key is the ASCII value of the bit, and + * a true or false value if it's set or not + * + * remember to fs_give the result! + */ + + unsigned char *maildir_keys_to_bits( char *keys ) { + + unsigned char *bits = fs_get( sizeof(unsigned char) * 255 ); + + memset(bits, 0, sizeof(unsigned char) * 255 ); + + while(*keys) { + bits[(unsigned char) *keys] = 1; + ++keys; + } + + return bits; + } + + /* takes an array of maildir "bits" and converts them to the + maildir style keys. Remember to free the result. + */ + + char *maildir_bits_to_keys( unsigned char *bits ) { + int i; + char *j; + char *string = fs_get(256); + j = string; + *j = 0; + for (i = 1; i < 255; ++i ) { + if ( bits[i] ) { + *j = i; + ++j; + *j = 0; + } + } + return string; + } + + /* takes a string, and compares it against the flag tables, + returning the character (bit) that it maps to... return 0 + if it doesn't work */ + unsigned char maildir_flag_to_key ( char *flag ) { + + int i; + + for (i = 0; extend_flags[i]; ++i ) { + if ( !strcmp(flag, extend_flags[i]) ) { + return extend_flag_keys[i]; + } + } + + for (i = 0; standard_flags[i]; ++i ) { + if ( !strcmp(flag, standard_flags[i] ) ) { + return standard_flag_keys[i]; + } + } + + return 0; + } + + /* take a key and return the text flag */ + char *maildir_key_to_flag ( unsigned char key ) { + + static char **lookup_array = 0; + int i; + + if ( ! lookup_array ) { + lookup_array = (char **) fs_get(sizeof(char *) * 255); + memset(lookup_array, 0, sizeof(char *) * 255); + for(i = 0; extend_flags[i]; ++i ) { + lookup_array[(unsigned char) extend_flag_keys[i]] = extend_flags[i]; + } + for(i = 0; standard_flags[i]; ++i) { + lookup_array[(unsigned char) standard_flag_keys[i]] = standard_flags[i]; + } + } + + return lookup_array[key]; + } + + void maildir_interpret_bits( unsigned char *bits, + int *deleted, + int *seen, + int *flagged, + int *answered, + int *draft, + unsigned long *user_flags ) { + + int i; + + *deleted = bits['T'] ? 1 : 0; + *seen = bits['S'] ? 1 : 0; + *flagged = bits['F'] ? 1 : 0; + *answered = bits['R'] ? 1 : 0; + *draft = bits['D'] ? 1 : 0; + + /* extract the user_flags value */ + + *user_flags = 0; + + for ( i = 0; extend_flag_keys[i]; ++i ) { + if ( bits[extend_flag_keys[i]] ) + *user_flags |= ((long) 1) << i; + } + + } + + char *maildir_make_flagstring( int deleted, + int seen, + int flagged, + int answered, + int draft, + unsigned long user_flags ) { + + unsigned char bits[255]; + int i; + + memset(bits, 0, 255); + + bits['T'] = deleted ? 1 : 0; + bits['S'] = seen ? 1 : 0; + bits['F'] = flagged ? 1 : 0; + bits['R'] = answered ? 1 : 0; + bits['D'] = draft ? 1 : 0; + + /* now the user flags */ + for( i = 0; extend_flags[i]; ++i ) { + if ( user_flags & (((long) 1) << i) ) { + bits[extend_flag_keys[i]] = 1; + } + } + + return maildir_bits_to_keys( bits ); + + } + + char *maildir_mylocalhost() { + + static char *hostname = NIL; + char *t; + + #ifdef MAILDIR_USE_SHORTNAME + if ( hostname ) { + return hostname; + } else { + hostname = strdup(mylocalhost()); + t = strstr( hostname, "."); + if ( t ) { + *t = 0; + } + return hostname; + } + #else + return mylocalhost(); + #endif + + } diff -N -r -c imap-2004c1/src/osdep/unix/maildir.h imap-2004c1-local/src/osdep/unix/maildir.h *** imap-2004c1/src/osdep/unix/maildir.h Wed Dec 31 19:00:00 1969 --- imap-2004c1-local/src/osdep/unix/maildir.h Mon Feb 21 17:50:35 2005 *************** *** 0 **** --- 1,94 ---- + /* + * Please read maildir.c for license and information + * + */ + + #define MAILDIRPATH "Mail/inbox" + + typedef struct maildir_local { + unsigned int inbox : 1; /* if it is an INBOX or not */ + unsigned int dirty : 1; /* diskcopy needs updating */ + char *dir; /* mail directory name */ + char *buf; /* temporary buffer */ + char *hdr; /* current header */ + unsigned long buflen; /* current size of temporary buffer */ + time_t scantime; /* last time directory scanned */ + char *lastheader; + unsigned long lastheaderlen; + unsigned long lastbodylen; + char *lastbody; + char *lastmsg; + int ld; + int toobigflag; + int uidinvalid; + } MAILDIRLOCAL; + + + #define MD_CACHE_OK 1 + #define MD_CACHE_RECENT 2 + #define MD_CACHE_NOTFOUND 3 + #define MD_CACHE_ERROR 4 + + + /* Convenient access to local data */ + + #define LOCAL ((MAILDIRLOCAL *) stream->local) + + #define FILEP(x) x->maildirp + #define FILEP_PTR(x) &x->maildirp + + /* Function prototypes */ + + DRIVER *maildir_valid (char *name); + int maildir_isvalid (char *name,long justname); + MAILSTREAM *maildir_open (MAILSTREAM *stream); + void maildir_gc (MAILSTREAM *stream,long gcflags); + void maildir_close (MAILSTREAM *stream, long options); + long maildir_ping (MAILSTREAM *stream); + long maildir_ping_core (MAILSTREAM *stream); + void maildir_check (MAILSTREAM *stream); + long maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); + char *maildir_fetchheader (MAILSTREAM *stream,unsigned long msgno, + unsigned long *length, long flags); + void maildir_fast (MAILSTREAM *stream,char *sequence,long flags); + void maildir_list (MAILSTREAM *stream,char *ref,char *pat); + void *maildir_parameters (long function,void *value); + long maildir_create (MAILSTREAM *stream,char *mailbox); + void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); + void maildir_expunge (MAILSTREAM *stream); + long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); + long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data); + + short maildir_getflags (MAILSTREAM *stream,char *flag); + long maildir_delete (MAILSTREAM *stream,char *mailbox); + long maildir_rename (MAILSTREAM *stream,char *old,char *new); + long maildir_sub (MAILSTREAM *stream,char *mailbox); + long maildir_unsub (MAILSTREAM *stream,char *mailbox); + void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); + void maildir_scan (MAILSTREAM *, char *, char *, char *); + /* utility functions */ + char *maildir_file (char *dst,char *name); + int maildir_copynew (MAILSTREAM *stream); + int maildir_select (const struct dirent *name); + int maildir_namesort (const struct dirent **d1,const struct dirent **d2); + int maildir_ctimesort (const struct dirent **d1, const struct dirent **d2); + void maildir_listwork(char *name, MAILSTREAM *stream, char *s2, char *subdir, int flag); + unsigned long maildir_get_uid( char *, char *); + unsigned long maildir_get_lastuid( MAILSTREAM *); + void maildir_set_lastuid( MAILSTREAM *); + unsigned long maildir_get_nextuid( MAILSTREAM *stream ); + long maildir_append_old (MAILSTREAM *stream,char *mailbox,char *flags,char *date, STRING *message); + void maildir_chopcur( char *path ); + int maildir_fetch_work (MAILSTREAM *stream, MESSAGECACHE *elt ); + + unsigned char *maildir_keys_to_bits ( char *keys ); + char *maildir_bits_to_keys ( unsigned char *bits ); + unsigned char maildir_flag_to_key(char *flag); + char *maildir_key_to_flag(unsigned char key); + void maildir_interpret_bits(unsigned char *bits, int *deleted, int *seen, int *flagged, int *answered, int *draft, unsigned long *user_flags ); + char *maildir_make_flagstring(int deleted, int seen, int flagged, int answered, int draft, unsigned long user_flags); + char *maildir_unparse_bits( unsigned char *); + unsigned char *maildir_parse_flags( char *); + void maildir_unlock_nextuid(MAILSTREAM *); + int maildir_lock_nextuid(MAILSTREAM *); + char *maildir_mylocalhost();