mx.c   mx.c 
/* ========================================================================
* Copyright 1988-2006 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* ========================================================================
*/
/* /*
* Program: MX mail routines * Program: MX mail routines
* *
* Author(s): Mark Crispin * Author(s): Mark Crispin
* Networks and Distributed Computing * Networks and Distributed Computing
* Computing & Communications * Computing & Communications
* University of Washington * University of Washington
* Administration Building, AG-44 * Administration Building, AG-44
* Seattle, WA 98195 * Seattle, WA 98195
* Internet: MRC@CAC.Washington.EDU * Internet: MRC@CAC.Washington.EDU
* *
* Date: 3 May 1996 * Date: 3 May 1996
* Last Edited: 4 November 2004 * Last Edited: 13 September 2006
*
* The IMAP toolkit provided in this Distribution is
* Copyright 1988-2004 University of Washington.
* The full text of our legal notices is contained in the file called
* CPYRIGHT, included with this Distribution.
*/ */
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
extern int errno; /* just in case */ extern int errno; /* just in case */
#include "mail.h" #include "mail.h"
#include "osdep.h" #include "osdep.h"
#include <pwd.h> #include <pwd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include "mx.h"
#include "misc.h" #include "misc.h"
#include "dummy.h" #include "dummy.h"
#include "fdstring.h"
/* Index file */
#define MXINDEXNAME "/.mxindex"
#define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME)
/* MX I/O stream local data */ /* MX I/O stream local data */
typedef struct mx_local { typedef struct mx_local {
int fd; /* file descriptor of open index */ int fd; /* file descriptor of open index */
char *dir; /* spool directory name */ char *dir; /* spool directory name */
unsigned char *buf; /* temporary buffer */ unsigned char *buf; /* temporary buffer */
unsigned long buflen; /* current size of temporary buffer */ unsigned long buflen; /* current size of temporary buffer */
unsigned long cachedtexts; /* total size of all cached texts */ unsigned long cachedtexts; /* total size of all cached texts */
time_t scantime; /* last time directory scanned */ time_t scantime; /* last time directory scanned */
} MXLOCAL; } MXLOCAL;
/* Convenient access to local data */ /* Convenient access to local data */
#define LOCAL ((MXLOCAL *) stream->local) #define LOCAL ((MXLOCAL *) stream->local)
/* Function prototypes */ /* Function prototypes */
DRIVER *mx_valid (char *name); DRIVER *mx_valid (char *name);
int mx_isvalid (char *name,char *tmp); int mx_isvalid (char *name,char *tmp);
int mx_namevalid (char *name);
void *mx_parameters (long function,void *value); void *mx_parameters (long function,void *value);
long mx_dirfmttest (char *name);
void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
long mx_scan_contents (char *name,char *contents,unsigned long csiz,
unsigned long fsiz);
void mx_list (MAILSTREAM *stream,char *ref,char *pat); void mx_list (MAILSTREAM *stream,char *ref,char *pat);
void mx_lsub (MAILSTREAM *stream,char *ref,char *pat); void mx_lsub (MAILSTREAM *stream,char *ref,char *pat);
void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
long mx_subscribe (MAILSTREAM *stream,char *mailbox); long mx_subscribe (MAILSTREAM *stream,char *mailbox);
long mx_unsubscribe (MAILSTREAM *stream,char *mailbox); long mx_unsubscribe (MAILSTREAM *stream,char *mailbox);
long mx_create (MAILSTREAM *stream,char *mailbox); long mx_create (MAILSTREAM *stream,char *mailbox);
long mx_delete (MAILSTREAM *stream,char *mailbox); long mx_delete (MAILSTREAM *stream,char *mailbox);
long mx_rename (MAILSTREAM *stream,char *old,char *newname); long mx_rename (MAILSTREAM *stream,char *old,char *newname);
int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name) ;
MAILSTREAM *mx_open (MAILSTREAM *stream); MAILSTREAM *mx_open (MAILSTREAM *stream);
void mx_close (MAILSTREAM *stream,long options); void mx_close (MAILSTREAM *stream,long options);
void mx_fast (MAILSTREAM *stream,char *sequence,long flags); void mx_fast (MAILSTREAM *stream,char *sequence,long flags);
char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt); char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt);
char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *leng th, char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *leng th,
long flags); long flags);
long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) ; long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) ;
void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
long mx_ping (MAILSTREAM *stream); long mx_ping (MAILSTREAM *stream);
void mx_check (MAILSTREAM *stream); void mx_check (MAILSTREAM *stream);
void mx_expunge (MAILSTREAM *stream); long mx_expunge (MAILSTREAM *stream,char *sequence,long options);
long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
long options); long options);
long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
STRING *st,SEARCHSET *set);
int mx_select (struct direct *name);
int mx_numsort (const void *d1,const void *d2); int mx_numsort (const void *d1,const void *d2);
char *mx_file (char *dst,char *name); char *mx_file (char *dst,char *name);
long mx_lockindex (MAILSTREAM *stream); long mx_lockindex (MAILSTREAM *stream);
void mx_unlockindex (MAILSTREAM *stream); void mx_unlockindex (MAILSTREAM *stream);
void mx_setdate (char *file,MESSAGECACHE *elt); void mx_setdate (char *file,MESSAGECACHE *elt);
/* MX mail routines */ /* MX mail routines */
/* Driver dispatch used by MAIL */ /* Driver dispatch used by MAIL */
DRIVER mxdriver = { DRIVER mxdriver = {
"mx", /* driver name */ "mx", /* driver name */
/* driver flags */ /* driver flags */
DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING, DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT,
(DRIVER *) NIL, /* next driver */ (DRIVER *) NIL, /* next driver */
mx_valid, /* mailbox is valid for us */ mx_valid, /* mailbox is valid for us */
mx_parameters, /* manipulate parameters */ mx_parameters, /* manipulate parameters */
mx_scan, /* scan mailboxes */ mx_scan, /* scan mailboxes */
mx_list, /* find mailboxes */ mx_list, /* find mailboxes */
mx_lsub, /* find subscribed mailboxes */ mx_lsub, /* find subscribed mailboxes */
mx_subscribe, /* subscribe to mailbox */ mx_subscribe, /* subscribe to mailbox */
mx_unsubscribe, /* unsubscribe from mailbox */ mx_unsubscribe, /* unsubscribe from mailbox */
mx_create, /* create mailbox */ mx_create, /* create mailbox */
mx_delete, /* delete mailbox */ mx_delete, /* delete mailbox */
skipping to change at line 129 skipping to change at line 149
mx_ping, /* ping mailbox to see if still alive */ mx_ping, /* ping mailbox to see if still alive */
mx_check, /* check for new messages */ mx_check, /* check for new messages */
mx_expunge, /* expunge deleted messages */ mx_expunge, /* expunge deleted messages */
mx_copy, /* copy messages to another mailbox */ mx_copy, /* copy messages to another mailbox */
mx_append, /* append string message to mailbox */ mx_append, /* append string message to mailbox */
NIL /* garbage collect stream */ NIL /* garbage collect stream */
}; };
/* prototype stream */ /* prototype stream */
MAILSTREAM mxproto = {&mxdriver}; MAILSTREAM mxproto = {&mxdriver};
/* MX mail validate mailbox /* MX mail validate mailbox
* Accepts: mailbox name * Accepts: mailbox name
* Returns: our driver if name is valid, NIL otherwise * Returns: our driver if name is valid, NIL otherwise
*/ */
DRIVER *mx_valid (char *name) DRIVER *mx_valid (char *name)
{ {
char tmp[MAILTMPLEN]; char tmp[MAILTMPLEN];
return mx_isvalid (name,tmp) ? &mxdriver : NIL; return mx_isvalid (name,tmp) ? &mxdriver : NIL;
} }
/* MX mail test for valid mailbox /* MX mail test for valid mailbox
* Accepts: mailbox name * Accepts: mailbox name
* temporary buffer to use * temporary buffer to use
* Returns: T if valid, NIL otherwise * Returns: T if valid, NIL otherwise
*/ */
int mx_isvalid (char *name,char *tmp) int mx_isvalid (char *name,char *tmp)
{ {
struct stat sbuf; struct stat sbuf;
errno = NIL; /* zap error */ errno = NIL; /* zap error */
/* validate name as directory */ /* validate name as directory */
return (!stat (MXINDEX (tmp,name),&sbuf) && return ((strlen (name) <= NETMAXMBX) && !stat (mx_file (tmp,name),&sbuf)
&&
((sbuf.st_mode & S_IFMT) == S_IFDIR) &&
!stat (MXINDEX (tmp,name),&sbuf) &&
((sbuf.st_mode & S_IFMT) == S_IFREG)); ((sbuf.st_mode & S_IFMT) == S_IFREG));
} }
/* MX mail test for valid mailbox
* Accepts: mailbox name
* Returns: T if valid, NIL otherwise
*/
int mx_namevalid (char *name)
{
char *s;
/* make sure valid name */
for (s = name; s && *s;) {
if (isdigit (*s)) s++; /* digit, check this node further... */
else if (*s == '/') break; /* all digit node, barf */
/* non-digit, skip to next node or return */
else if (!((s = strchr (s+1,'/')) && *++s)) return T;
}
return NIL; /* all numeric or empty node */
}
/* MX manipulate driver parameters /* MX manipulate driver parameters
* Accepts: function code * Accepts: function code
* function-dependent value * function-dependent value
* Returns: function-dependent return value * Returns: function-dependent return value
*/ */
void *mx_parameters (long function,void *value) void *mx_parameters (long function,void *value)
{ {
return NIL; void *ret = NIL;
switch ((int) function) {
case GET_INBOXPATH:
if (value) ret = mailboxfile ((char *) value,"~/INBOX");
break;
case GET_DIRFMTTEST:
ret = (void *) mx_dirfmttest;
break;
case GET_SCANCONTENTS:
ret = (void *) mx_scan_contents;
break;
}
return ret;
} }
/* MX test for directory format internal node
* Accepts: candidate node name
* Returns: T if internal name, NIL otherwise
*/
long mx_dirfmttest (char *name)
{
int c;
/* success if index name or all-numberic */
if (strcmp (name,MXINDEXNAME+1))
while (c = *name++) if (!isdigit (c)) return NIL;
return LONGT;
}
/* MX scan mailboxes /* MX scan mailboxes
* Accepts: mail stream * Accepts: mail stream
* reference * reference
* pattern to search * pattern to search
* string to scan * string to scan
*/ */
void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
{ {
if (stream) MM_LOG ("Scan not valid for mx mailboxes",ERROR); if (stream) dummy_scan (NIL,ref,pat,contents);
}
/* MX scan mailbox for contents
* Accepts: mailbox name
* desired contents
* contents size
* file size (ignored)
* Returns: NIL if contents not found, T if found
*/
long mx_scan_contents (char *name,char *contents,unsigned long csiz,
unsigned long fsiz)
{
long i,nfiles;
void *a;
char *s;
long ret = NIL;
size_t namelen = strlen (name);
struct stat sbuf;
struct direct **names = NIL;
if ((nfiles = scandir (name,&names,mx_select,mx_numsort)) > 0)
for (i = 0; i < nfiles; ++i) {
if (!ret) {
sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) +
2),
"%s/%s",name,names[i]->d_name);
if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
fs_give ((void **) &s);
}
fs_give ((void **) &names[i]);
}
/* free directory list */
if (a = (void *) names) fs_give ((void **) &a);
return ret;
} }
/* MX list mailboxes /* MX list mailboxes
* Accepts: mail stream * Accepts: mail stream
* reference * reference
* pattern to search * pattern to search
*/ */
void mx_list (MAILSTREAM *stream,char *ref,char *pat) void mx_list (MAILSTREAM *stream,char *ref,char *pat)
{ {
char *s,test[MAILTMPLEN],file[MAILTMPLEN]; if (stream) dummy_list (NIL,ref,pat);
long i = 0;
/* get canonical form of name */
if (stream && dummy_canonicalize (test,ref,pat)) {
/* found any wildcards? */
if (s = strpbrk (test,"%*")) {
/* yes, copy name up to that point */
strncpy (file,test,i = s - test);
file[i] = '\0'; /* tie off */
}
else strcpy (file,test); /* use just that name then */
/* find directory name */
if (s = strrchr (file,'/')) {
*s = '\0'; /* found, tie off at that point */
s = file;
}
/* do the work */
mx_list_work (stream,s,test,0);
}
} }
/* MX list subscribed mailboxes /* MX list subscribed mailboxes
* Accepts: mail stream * Accepts: mail stream
* reference * reference
* pattern to search * pattern to search
*/ */
void mx_lsub (MAILSTREAM *stream,char *ref,char *pat) void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
{ {
if (stream) dummy_lsub (NIL,ref,pat); if (stream) dummy_lsub (NIL,ref,pat);
} }
/* MX list mailboxes worker routine
* Accepts: mail stream
* directory name to search
* search pattern
* search level
*/
void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
{
DIR *dp;
struct direct *d;
struct stat sbuf;
char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
if (dir && *dir) { /* make mailbox and directory names */
sprintf (name,"%s/",dir); /* print name starts at directory */
mx_file (curdir,dir); /* path starts from mx_file() name */
}
else { /* no directory, use mailbox home dir */
mx_file (curdir,mailboxdir (name,NIL,NIL));
name[0] = '\0'; /* dummy name */
}
if (dp = opendir (curdir)) { /* open directory */
np = name + strlen (name);
cp = curdir + strlen (strcat (curdir,"/"));
while (d = readdir (dp)) { /* scan, ignore . and numeric names */
if ((d->d_name[0] != '.') && !mx_select (d)) {
if (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)) {
strcpy (cp,d->d_name);/* make directory name */
strcpy (np,d->d_name);/* make mx name of directory name */
if (dmatch (name,pat,'/') && !stat (curdir,&sbuf) &&
((sbuf.st_mode &= S_IFMT) == S_IFDIR))
mx_list_work (stream,name,pat,level+1);
}
}
else if (!strcmp (d->d_name,MXINDEXNAME+1) && pmatch_full (dir,pat,'/
'))
mm_list (stream,'/',dir,NIL);
}
closedir (dp); /* all done, flush directory */
}
}
/* MX mail subscribe to mailbox /* MX mail subscribe to mailbox
* Accepts: mail stream * Accepts: mail stream
* mailbox to add to subscription list * mailbox to add to subscription list
* Returns: T on success, NIL on failure * Returns: T on success, NIL on failure
*/ */
long mx_subscribe (MAILSTREAM *stream,char *mailbox) long mx_subscribe (MAILSTREAM *stream,char *mailbox)
{ {
return sm_subscribe (mailbox); return sm_subscribe (mailbox);
} }
skipping to change at line 285 skipping to change at line 325
return sm_unsubscribe (mailbox); return sm_unsubscribe (mailbox);
} }
/* MX mail create mailbox /* MX mail create mailbox
* Accepts: mail stream * Accepts: mail stream
* mailbox name to create * mailbox name to create
* Returns: T on success, NIL on failure * Returns: T on success, NIL on failure
*/ */
long mx_create (MAILSTREAM *stream,char *mailbox) long mx_create (MAILSTREAM *stream,char *mailbox)
{ {
DRIVER *test;
int fd; int fd;
char *s,tmp[MAILTMPLEN],mbx[MAILTMPLEN]; char *s,tmp[MAILTMPLEN];
/* assume error */ int mask = umask (0);
sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox long ret = NIL;
); if (!mx_namevalid (mailbox)) /* validate name */
/* make sure valid name */ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailb
for (s = mailbox; s && *s;) { ox);
if (isdigit (*s)) s++; /* digit, check this node further... */
/* all digit node, barf */
else if (*s == '/') s = NIL;
/* non-digit in node, skip to next node */
else if (s = strchr (s+1,'/')) s++;
else tmp[0] = NIL; /* no more nodes, good name */
}
if (tmp[0]); /* was there an error in the name? */
/* must not already exist */ /* must not already exist */
else if (mx_isvalid (mailbox,tmp)) else if ((test = mail_valid (NIL,mailbox,NIL)) &&
strcmp (test->name,"dummy"))
sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailb ox); sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailb ox);
/* create directory */ /* create directory */
else if (!dummy_create_path (stream,strcat (mx_file (mbx,mailbox),"/"), else if (!dummy_create_path (stream,MXINDEX (tmp,mailbox),
get_dir_protection (mailbox))) get_dir_protection (mailbox)))
sprintf (tmp,"Can't create mailbox leaf %.80s: %s", sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno))
mailbox,strerror (errno)); ;
else { /* create index file */ else { /* success */
int mask = umask (0); /* set index protection */
if (((fd = open (MXINDEX (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL, set_mbx_protections (mailbox,tmp);
(int) mail_parameters (NIL,GET_MBXPROTECTION,mailbox))) /* tie off directory name */
<0) *(s = strrchr (tmp,'/') + 1) = '\0';
|| close (fd)) /* set directory protection */
sprintf (tmp,"Can't create mailbox index %.80s: %s", set_mbx_protections (mailbox,tmp);
mailbox,strerror (errno)); ret = LONGT;
else { /* success */
set_mbx_protections (mailbox,mbx);
set_mbx_protections (mailbox,tmp);
tmp[0] = NIL;
}
umask (mask); /* restore mask */
} }
if (!tmp[0]) return LONGT; /* success */ umask (mask); /* restore mask */
MM_LOG (tmp,ERROR); /* some error */ if (!ret) MM_LOG (tmp,ERROR); /* some error */
return NIL; return ret;
} }
/* MX mail delete mailbox /* MX mail delete mailbox
* mailbox name to delete * mailbox name to delete
* Returns: T on success, NIL on failure * Returns: T on success, NIL on failure
*/ */
long mx_delete (MAILSTREAM *stream,char *mailbox) long mx_delete (MAILSTREAM *stream,char *mailbox)
{ {
DIR *dirp; DIR *dirp;
struct direct *d; struct direct *d;
skipping to change at line 353 skipping to change at line 380
else { /* get directory name */ else { /* get directory name */
*(s = strrchr (tmp,'/')) = '\0'; *(s = strrchr (tmp,'/')) = '\0';
if (dirp = opendir (tmp)) { /* open directory */ if (dirp = opendir (tmp)) { /* open directory */
*s++ = '/'; /* restore delimiter */ *s++ = '/'; /* restore delimiter */
/* massacre messages */ /* massacre messages */
while (d = readdir (dirp)) if (mx_select (d)) { while (d = readdir (dirp)) if (mx_select (d)) {
strcpy (s,d->d_name); /* make path */ strcpy (s,d->d_name); /* make path */
unlink (tmp); /* sayonara */ unlink (tmp); /* sayonara */
} }
closedir (dirp); /* flush directory */ closedir (dirp); /* flush directory */
} *(s = strrchr (tmp,'/')) = '\0';
/* try to remove the directory */ if (rmdir (tmp)) { /* try to remove the directory */
if (rmdir (mx_file (tmp,mailbox))) { sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno))
sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno)); ;
mm_log (tmp,WARN); mm_log (tmp,WARN);
}
} }
return T; /* always success */ return T; /* always success */
} }
MM_LOG (tmp,ERROR); /* something failed */ MM_LOG (tmp,ERROR); /* something failed */
return NIL; return NIL;
} }
/* MX mail rename mailbox /* MX mail rename mailbox
* Accepts: MX mail stream * Accepts: MX mail stream
* old mailbox name * old mailbox name
* new mailbox name * new mailbox name
* Returns: T on success, NIL on failure * Returns: T on success, NIL on failure
*/ */
long mx_rename (MAILSTREAM *stream,char *old,char *newname) long mx_rename (MAILSTREAM *stream,char *old,char *newname)
{ {
char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
struct stat sbuf; struct stat sbuf;
if (!mx_isvalid (old,tmp)) if (!mx_isvalid (old,tmp))
sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
else if (!mx_namevalid (newname))
sprintf (tmp,"Can't rename to mailbox %.80s: invalid MX-format name",
newname);
/* new mailbox name must not be valid */ /* new mailbox name must not be valid */
else if (mx_isvalid (newname,tmp)) else if (mx_isvalid (newname,tmp))
sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists ", sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists ",
newname); newname);
/* success if can rename the directory */ else {
else { /* found superior to destination name? */ mx_file (tmp,old); /* build old directory name */
if (s = strrchr (mx_file (tmp1,newname),'/')) { mx_file (tmp1,newname); /* and new directory name */
c = *++s; /* remember first character of infer /* easy if not INBOX */
ior */ if (compare_cstring (old,"INBOX")) {
*s = '\0'; /* tie off to get just superior */ /* found superior to destination name? */
if (s = strrchr (mx_file (tmp1,newname),'/')) {
c = *++s; /* remember first character of inferior */
*s = '\0'; /* tie off to get just superior */
/* name doesn't exist, create it */ /* name doesn't exist, create it */
if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
!dummy_create_path (stream,tmp1,get_dir_protection (newname))) !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
return NIL; return NIL;
*s = c; /* restore full name */ *s = c; /* restore full name */
}
if (!rename (tmp,tmp1)) return LONGT;
} }
if (!rename (mx_file (tmp,old),tmp1)) { /* RFC 3501 requires this */
/* recreate file if renamed INBOX */ else if (dummy_create_path (stream,strcat (tmp1,"/"),
if (!compare_cstring (old,"INBOX")) mx_create (NIL,"INBOX"); get_dir_protection (newname))) {
return T; void *a;
int i,n,lasterror;
struct direct **names = NIL;
size_t srcl = strlen (tmp);
size_t dstl = strlen (tmp1);
/* rename each mx file to new directory */
for (i = lasterror = 0,n = scandir (tmp,&names,mx_select,mx_numsort);
i < n; ++i) {
if (mx_rename_work (tmp,srcl,tmp1,dstl,names[i]->d_name))
lasterror = errno;
fs_give ((void **) &names[i]);
}
/* free directory list */
if (a = (void *) names) fs_give ((void **) &a);
if (lasterror || mx_rename_work (tmp,srcl,tmp1,dstl,MXINDEXNAME+1))
errno = lasterror;
else return mx_create (NIL,"INBOX");
} }
sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
old,newname,strerror (errno)); old,newname,strerror (errno));
} }
MM_LOG (tmp,ERROR); /* something failed */ MM_LOG (tmp,ERROR); /* something failed */
return NIL; return NIL;
} }
/* MX rename worker routine
* Accepts: source directory name
* source directory name length
* destination directory name
* destination directory name length
* name of node to move
* Returns: zero if success, non-zero if error
*/
int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name)
{
int ret;
size_t len = strlen (name);
char *s = (char *) fs_get (srcl + len + 2);
char *d = (char *) fs_get (dstl + len + 1);
sprintf (s,"%s/%s",src,name);
sprintf (d,"%s%s",dst,name);
ret = rename (s,d);
fs_give ((void **) &s);
fs_give ((void **) &d);
return ret;
}
/* MX mail open /* MX mail open
* Accepts: stream to open * Accepts: stream to open
* Returns: stream on success, NIL on failure * Returns: stream on success, NIL on failure
*/ */
MAILSTREAM *mx_open (MAILSTREAM *stream) MAILSTREAM *mx_open (MAILSTREAM *stream)
{ {
char tmp[MAILTMPLEN]; char tmp[MAILTMPLEN];
/* return prototype for OP_PROTOTYPE call */ /* return prototype for OP_PROTOTYPE call */
if (!stream) return user_flags (&mxproto); if (!stream) return user_flags (&mxproto);
if (stream->local) fatal ("mx recycle stream"); if (stream->local) fatal ("mx recycle stream");
stream->local = fs_get (sizeof (MXLOCAL)); stream->local = fs_get (sizeof (MXLOCAL));
/* note if an INBOX or not */ /* note if an INBOX or not */
stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
mx_file (tmp,stream->mailbox);/* get directory name */ mx_file (tmp,stream->mailbox);/* get directory name */
LOCAL->dir = cpystr (tmp); /* copy directory name for later */ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
/* make temporary buffer */ /* make temporary buffer */
LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); LOCAL->buf = (char *) fs_get (CHUNKSIZE);
LOCAL->buflen = CHUNKSIZE - 1;
LOCAL->scantime = 0; /* not scanned yet */ LOCAL->scantime = 0; /* not scanned yet */
LOCAL->fd = -1; /* no index yet */ LOCAL->fd = -1; /* no index yet */
LOCAL->cachedtexts = 0; /* no cached texts */ LOCAL->cachedtexts = 0; /* no cached texts */
stream->sequence++; /* bump sequence number */ stream->sequence++; /* bump sequence number */
/* parse mailbox */ /* parse mailbox */
stream->nmsgs = stream->recent = 0; stream->nmsgs = stream->recent = 0;
if (mx_ping (stream) && !(stream->nmsgs || stream->silent)) if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
MM_LOG ("Mailbox is empty",(long) NIL); MM_LOG ("Mailbox is empty",(long) NIL);
stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
skipping to change at line 446 skipping to change at line 522
/* MX mail close /* MX mail close
* Accepts: MAIL stream * Accepts: MAIL stream
* close options * close options
*/ */
void mx_close (MAILSTREAM *stream,long options) void mx_close (MAILSTREAM *stream,long options)
{ {
if (LOCAL) { /* only if a file is open */ if (LOCAL) { /* only if a file is open */
int silent = stream->silent; int silent = stream->silent;
stream->silent = T; /* note this stream is dying */ stream->silent = T; /* note this stream is dying */
if (options & CL_EXPUNGE) mx_expunge (stream); if (options & CL_EXPUNGE) mx_expunge (stream,NIL,NIL);
if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
/* free local scratch buffer */ /* free local scratch buffer */
if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
/* nuke the local data */ /* nuke the local data */
fs_give ((void **) &stream->local); fs_give ((void **) &stream->local);
stream->dtb = NIL; /* log out the DTB */ stream->dtb = NIL; /* log out the DTB */
stream->silent = silent; /* reset silent state */ stream->silent = silent; /* reset silent state */
} }
} }
/* MX mail fetch fast information /* MX mail fetch fast information
skipping to change at line 485 skipping to change at line 561
* message cache element * message cache element
* Returns: name of message file * Returns: name of message file
*/ */
char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt) char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
{ {
struct stat sbuf; struct stat sbuf;
struct tm *tm; struct tm *tm;
/* build message file name */ /* build message file name */
sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
if (!elt->rfc822_size) { /* have size yet? */ /* have size yet? */
stat (LOCAL->buf,&sbuf); /* get size of message */ if (!elt->rfc822_size && !stat (LOCAL->buf,&sbuf)) {
/* make plausible IMAPish date string */ /* make plausible IMAPish date string */
tm = gmtime (&sbuf.st_mtime); tm = gmtime (&sbuf.st_mtime);
elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
elt->year = tm->tm_year + 1900 - BASEYEAR; elt->year = tm->tm_year + 1900 - BASEYEAR;
elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
elt->seconds = tm->tm_sec; elt->seconds = tm->tm_sec;
elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0; elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
elt->rfc822_size = sbuf.st_size; elt->rfc822_size = sbuf.st_size;
} }
return LOCAL->buf; /* return file name */ return LOCAL->buf; /* return file name */
skipping to change at line 644 skipping to change at line 720
recent++; /* bump recent count */ recent++; /* bump recent count */
} }
} }
fs_give ((void **) &names[i]); fs_give ((void **) &names[i]);
} }
/* free directory */ /* free directory */
if (s = (void *) names) fs_give ((void **) &s); if (s = (void *) names) fs_give ((void **) &s);
} }
stream->nmsgs = nmsgs; /* don't upset mail_uid() */ stream->nmsgs = nmsgs; /* don't upset mail_uid() */
/* if INBOX, snarf from system INBOX */ /* if INBOX, snarf from system INBOX */
if (mx_lockindex (stream) && stream->inbox) { if (mx_lockindex (stream) && stream->inbox &&
!strcmp (sysinbox (),stream->mailbox)) {
old = stream->uid_last; old = stream->uid_last;
/* paranoia check */
if (!strcmp (sysinbox (),stream->mailbox)) {
stream->silent = silent;
return NIL;
}
MM_CRITICAL (stream); /* go critical */ MM_CRITICAL (stream); /* go critical */
stat (sysinbox (),&sbuf); /* see if anything there */ /* see if anything in system inbox */
/* can get sysinbox mailbox? */ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
&& (!sysibx->rdonly) && (r = sysibx->nmsgs)) { !sysibx->rdonly && (r = sysibx->nmsgs)) {
for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
/* build file name we will use */ /* build file name we will use */
sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
/* snarf message from Berkeley mailbox */ /* snarf message from Berkeley mailbox */
selt = mail_elt (sysibx,i); selt = mail_elt (sysibx,i);
if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
S_IREAD|S_IWRITE)) >= 0) && (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
>= 0) &&
(s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) && (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
(write (fd,s,j) == j) && (write (fd,s,j) == j) &&
(s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) && (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
(write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
/* swell the cache */ /* swell the cache */
mail_exists (stream,++nmsgs); mail_exists (stream,++nmsgs);
stream->uid_last = /* create new elt, note its file number */ stream->uid_last = /* create new elt, note its file number */
(elt = mail_elt (stream,nmsgs))->private.uid = old; (elt = mail_elt (stream,nmsgs))->private.uid = old;
recent++; /* bump recent count */ recent++; /* bump recent count */
/* set up initial flags and date */ /* set up initial flags and date */
skipping to change at line 685 skipping to change at line 758
elt->deleted = selt->deleted; elt->deleted = selt->deleted;
elt->flagged = selt->flagged; elt->flagged = selt->flagged;
elt->answered = selt->answered; elt->answered = selt->answered;
elt->draft = selt->draft; elt->draft = selt->draft;
elt->day = selt->day;elt->month = selt->month;elt->year = selt->ye ar; elt->day = selt->day;elt->month = selt->month;elt->year = selt->ye ar;
elt->hours = selt->hours;elt->minutes = selt->minutes; elt->hours = selt->hours;elt->minutes = selt->minutes;
elt->seconds = selt->seconds; elt->seconds = selt->seconds;
elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
elt->zoccident = selt->zoccident; elt->zoccident = selt->zoccident;
mx_setdate (LOCAL->buf,elt); mx_setdate (LOCAL->buf,elt);
sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
} }
else { /* failed to snarf */ else { /* failed to snarf */
if (fd) { /* did it ever get opened? */ if (fd) { /* did it ever get opened? */
close (fd); /* close descriptor */ close (fd); /* close descriptor */
unlink (LOCAL->buf);/* flush this file */ unlink (LOCAL->buf);/* flush this file */
} }
stream->silent = silent; sprintf (tmp,"Message copy to MX mailbox failed: %.80s",
return NIL; /* note that something is badly wrong */ s,strerror (errno));
mm_log (tmp,ERROR);
r = 0; /* stop the snarf in its tracks */
} }
sprintf (tmp,"%lu",i); /* delete it from the sysinbox */
mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
} }
stat (LOCAL->dir,&sbuf); /* update scan time */ /* update scan time */
LOCAL->scantime = sbuf.st_ctime; if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
mail_expunge (sysibx); /* now expunge all those messages */ mail_expunge (sysibx); /* now expunge all those messages */
} }
if (sysibx) mail_close (sysibx); if (sysibx) mail_close (sysibx);
MM_NOCRITICAL (stream); /* release critical */ MM_NOCRITICAL (stream); /* release critical */
} }
mx_unlockindex (stream); /* done with index */ mx_unlockindex (stream); /* done with index */
stream->silent = silent; /* can pass up events now */ stream->silent = silent; /* can pass up events now */
mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
mail_recent (stream,recent); mail_recent (stream,recent);
return T; /* return that we are alive */ return T; /* return that we are alive */
skipping to change at line 721 skipping to change at line 796
* Accepts: MAIL stream * Accepts: MAIL stream
*/ */
void mx_check (MAILSTREAM *stream) void mx_check (MAILSTREAM *stream)
{ {
if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL); if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
} }
/* MX mail expunge mailbox /* MX mail expunge mailbox
* Accepts: MAIL stream * Accepts: MAIL stream
* sequence to expunge if non-NIL
* expunge options
* Returns: T, always
*/ */
void mx_expunge (MAILSTREAM *stream) long mx_expunge (MAILSTREAM *stream,char *sequence,long options)
{ {
long ret;
MESSAGECACHE *elt; MESSAGECACHE *elt;
unsigned long i = 1; unsigned long i = 1;
unsigned long n = 0; unsigned long n = 0;
unsigned long recent = stream->recent; unsigned long recent = stream->recent;
if (mx_lockindex (stream)) { /* lock the index */ if (ret = (sequence ? ((options & EX_UID) ?
mail_uid_sequence (stream,sequence) :
mail_sequence (stream,sequence)) : LONGT) &&
mx_lockindex (stream)) { /* lock the index */
MM_CRITICAL (stream); /* go critical */ MM_CRITICAL (stream); /* go critical */
while (i <= stream->nmsgs) {/* for each message */ while (i <= stream->nmsgs) {/* for each message */
/* if deleted, need to trash it */ elt = mail_elt (stream,i);/* if deleted, need to trash it */
if ((elt = mail_elt (stream,i))->deleted) { if (elt->deleted && (sequence ? elt->sequence : T)) {
sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
if (unlink (LOCAL->buf)) {/* try to delete the message */ if (unlink (LOCAL->buf)) {/* try to delete the message */
sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i , sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i ,
strerror (errno)); strerror (errno));
MM_LOG (LOCAL->buf,(long) NIL); MM_LOG (LOCAL->buf,(long) NIL);
break; break;
} }
/* note uncached */ /* note uncached */
LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
elt->private.msg.header.text.size : 0) + elt->private.msg.header.text.size : 0) +
skipping to change at line 760 skipping to change at line 842
} }
else i++; /* otherwise try next message */ else i++; /* otherwise try next message */
} }
if (n) { /* output the news if any expunged */ if (n) { /* output the news if any expunged */
sprintf (LOCAL->buf,"Expunged %lu messages",n); sprintf (LOCAL->buf,"Expunged %lu messages",n);
MM_LOG (LOCAL->buf,(long) NIL); MM_LOG (LOCAL->buf,(long) NIL);
} }
else MM_LOG ("No messages deleted, so no update needed",(long) NIL); else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
MM_NOCRITICAL (stream); /* release critical */ MM_NOCRITICAL (stream); /* release critical */
mx_unlockindex (stream); /* finished with index */ mx_unlockindex (stream); /* finished with index */
}
/* notify upper level of new mailbox size */ /* notify upper level of new mailbox size */
mail_exists (stream,stream->nmsgs); mail_exists (stream,stream->nmsgs);
mail_recent (stream,recent); mail_recent (stream,recent);
}
return ret;
} }
/* MX mail copy message(s) /* MX mail copy message(s)
* Accepts: MAIL stream * Accepts: MAIL stream
* sequence * sequence
* destination mailbox * destination mailbox
* copy options * copy options
* Returns: T if copy successful, else NIL * Returns: T if copy successful, else NIL
*/ */
long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
{ {
FDDATA d;
STRING st; STRING st;
MESSAGECACHE *elt; MESSAGECACHE *elt;
MAILSTREAM *astream;
struct stat sbuf; struct stat sbuf;
int fd; int fd;
unsigned long i,j; unsigned long i,j,uid,uidv;
char *t,flags[MAILTMPLEN],date[MAILTMPLEN]; char *t,tmp[MAILTMPLEN];
long ret;
mailproxycopy_t pc =
(mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
/* make sure valid mailbox */
if (!mx_valid (mailbox)) switch (errno) {
case NIL: /* no error in stat() */
if (pc) return (*pc) (stream,sequence,mailbox,options);
sprintf (LOCAL->buf,"Not a MX-format mailbox: %.80s",mailbox);
MM_LOG (LOCAL->buf,ERROR);
return NIL;
default: /* some stat() error */
MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
return NIL;
}
/* copy the messages */ /* copy the messages */
if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
mail_sequence (stream,sequence)) mail_sequence (stream,sequence))));
for (i = 1; i <= stream->nmsgs; i++) /* acquire stream to append to */
else if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
sprintf (tmp,"Can't open copy mailbox: %s",strerror (errno));
MM_LOG (tmp,ERROR);
ret = NIL;
}
else {
MM_CRITICAL (stream); /* go critical */
if (!(ret = mx_lockindex (astream)))
MM_LOG ("Message copy failed: unable to lock index",ERROR);
else {
copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
SEARCHSET *source = cu ? mail_newsearchset () : NIL;
SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); i++)
if ((elt = mail_elt (stream,i))->sequence) { if ((elt = mail_elt (stream,i))->sequence) {
if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))<0) return N if (ret = ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))
IL; >= 0)) {
fstat (fd,&sbuf); /* get size of message */ fstat (fd,&sbuf); /* get size of message */
/* is buffer big enough? */ d.fd = fd; /* set up file descriptor */
if (sbuf.st_size > LOCAL->buflen) { d.pos = 0; /* start of file */
fs_give ((void **) &LOCAL->buf); d.chunk = LOCAL->buf;
LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1); d.chunksize = CHUNKSIZE;
} INIT (&st,fd_string,&d,sbuf.st_size);
/* slurp message */
read (fd,LOCAL->buf,sbuf.st_size);
/* tie off file */
LOCAL->buf[sbuf.st_size] = '\0';
close (fd); /* flush message file */
INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size);
/* init flag string */ /* init flag string */
flags[0] = flags[1] = '\0'; tmp[0] = tmp[1] = '\0';
if (j = elt->user_flags) do if (j = elt->user_flags) do
if (t = stream->user_flags[find_rightmost_bit (&j)]) if (t = stream->user_flags[find_rightmost_bit (&j)])
strcat (strcat (flags," "),t); strcat (strcat (tmp," "),t);
while (j); while (j);
if (elt->seen) strcat (flags," \\Seen"); if (elt->seen) strcat (tmp," \\Seen");
if (elt->deleted) strcat (flags," \\Deleted"); if (elt->deleted) strcat (tmp," \\Deleted");
if (elt->flagged) strcat (flags," \\Flagged"); if (elt->flagged) strcat (tmp," \\Flagged");
if (elt->answered) strcat (flags," \\Answered"); if (elt->answered) strcat (tmp," \\Answered");
if (elt->draft) strcat (flags," \\Draft"); if (elt->draft) strcat (tmp," \\Draft");
flags[0] = '('; /* open list */ tmp[0] = '('; /* open list */
strcat (flags,")"); /* close list */ strcat (tmp,")"); /* close list */
mail_date (date,elt); /* generate internal date */ if (ret = mx_append_msg (astream,tmp,elt,&st,dest)) {
if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL; /* add to source set if needed */
if (options & CP_MOVE) elt->deleted = T; if (source) mail_append_set (source,mail_uid (stream,i));
/* delete if doing a move */
if (options & CP_MOVE) elt->deleted = T;
}
}
} }
return T; /* return success */ /* return sets if doing COPYUID */
if (cu && ret) (*cu) (stream,mailbox,astream->uid_validity,source,des
t);
else { /* flush any sets we may have built */
mail_free_searchset (&source);
mail_free_searchset (&dest);
}
mx_unlockindex (astream); /* unlock index */
}
MM_NOCRITICAL (stream);
mail_close (astream); /* finished with append stream */
}
return ret; /* return success */
} }
/* MX mail append message from stringstruct /* MX mail append message from stringstruct
* Accepts: MAIL stream * Accepts: MAIL stream
* destination mailbox * destination mailbox
* append callback * append callback
* data for callback * data for callback
* Returns: T if append successful, else NIL * Returns: T if append successful, else NIL
*/ */
long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
{ {
MESSAGECACHE *elt,selt; MESSAGECACHE elt;
MAILSTREAM *astream; MAILSTREAM *astream;
int fd; char *flags,*date,tmp[MAILTMPLEN];
char *flags,*date,*s,tmp[MAILTMPLEN];
STRING *message; STRING *message;
long f,i,size;
unsigned long uf;
long ret = LONGT; long ret = LONGT;
/* default stream to prototype */ /* default stream to prototype */
if (!stream) stream = user_flags (&mxproto); if (!stream) stream = user_flags (&mxproto);
/* N.B.: can't use LOCAL->buf for tmp */ /* N.B.: can't use LOCAL->buf for tmp */
/* make sure valid mailbox */ /* make sure valid mailbox */
if (!mx_isvalid (mailbox,tmp)) switch (errno) { if (!mx_isvalid (mailbox,tmp)) switch (errno) {
case ENOENT: /* no such file? */ case ENOENT: /* no such file? */
if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX"); if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX");
else { else {
MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL ); MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL );
skipping to change at line 866 skipping to change at line 986
MM_LOG (tmp,ERROR); MM_LOG (tmp,ERROR);
return NIL; return NIL;
} }
/* get first message */ /* get first message */
if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) { if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
MM_LOG (tmp,ERROR); MM_LOG (tmp,ERROR);
return NIL; return NIL;
} }
MM_CRITICAL (stream); /* go critical */ MM_CRITICAL (astream); /* go critical */
/* lock the index */ /* lock the index */
if (mx_lockindex (astream)) do { if (!(ret = mx_lockindex (astream)))
if (!SIZE (message)) { /* guard against zero-length */ MM_LOG ("Message append failed: unable to lock index",ERROR);
MM_LOG ("Append of zero-length message",ERROR); else {
ret = NIL; appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
break; SEARCHSET *dst = au ? mail_newsearchset () : NIL;
} do {
/* parse flags */ /* guard against zero-length */
f = mail_parse_flags (astream,flags,&uf); if (!(ret = SIZE (message)))
if (date) { /* want to preserve date? */ MM_LOG ("Append of zero-length message",ERROR);
/* yes, parse date into an elt */ else if (date && !(ret = mail_parse_date (&elt,date))) {
if (!mail_parse_date (&selt,date)) {
sprintf (tmp,"Bad date in append: %.80s",date); sprintf (tmp,"Bad date in append: %.80s",date);
MM_LOG (tmp,ERROR); MM_LOG (tmp,ERROR);
ret = NIL;
break;
} }
} else ret = mx_append_msg (astream,flags,date ? &elt : NIL,message,dst
mx_file (tmp,mailbox); /* make message name */ )&&
sprintf (tmp + strlen (tmp),"/%lu",++astream->uid_last); MM_APPEND (af) (stream,data,&flags,&date,&message);
if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { } while (ret && message);
sprintf (tmp,"Can't create append message: %s",strerror (errno)); /* return sets if doing APPENDUID */
MM_LOG (tmp,ERROR); if (au && ret) (*au) (mailbox,astream->uid_validity,dst);
ret = NIL; else mail_free_searchset (&dst);
break; mx_unlockindex (astream); /* unlock index */
} }
/* copy message */ MM_NOCRITICAL (astream); /* release critical */
s = (char *) fs_get (size = SIZE (message)); mail_close (astream);
for (i = 0; i < size; s[i++] = SNX (message)); return ret;
/* write the data */ }
if ((write (fd,s,size) < 0) || fsync (fd)) { /* MX mail append single message
unlink (tmp); /* delete mailbox */ * Accepts: MAIL stream
* flags for new message if non-NIL
* elt with source date if non-NIL
* stringstruct of message text
* searchset to place UID
* Returns: T if success, NIL if failure
*/
long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
STRING *st,SEARCHSET *set)
{
char tmp[MAILTMPLEN];
int fd;
unsigned long uf;
long f = mail_parse_flags (stream,flags,&uf);
mx_file (tmp,stream->mailbox);/* make message file name */
sprintf (tmp + strlen (tmp),"/%lu",++stream->uid_last);
if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
(int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
sprintf (tmp,"Can't create append message: %s",strerror (errno));
MM_LOG (tmp,ERROR);
return NIL;
}
while (SIZE (st)) { /* copy the file */
if (st->cursize && (write (fd,st->curpos,st->cursize) < 0)) {
unlink (tmp); /* delete file */
close (fd); /* close the file */
sprintf (tmp,"Message append failed: %s",strerror (errno)); sprintf (tmp,"Message append failed: %s",strerror (errno));
MM_LOG (tmp,ERROR); MM_LOG (tmp,ERROR);
ret = NIL; return NIL;
} }
fs_give ((void **) &s); /* flush the buffer */ SETPOS (st,GETPOS (st) + st->cursize);
close (fd); /* close the file */ }
if (ret) { /* set the date for this message */ close (fd); /* close the file */
if (date) mx_setdate (tmp,&selt); if (elt) mx_setdate (tmp,elt);/* set file date */
/* swell the cache */ /* swell the cache */
mail_exists (astream,++astream->nmsgs); mail_exists (stream,++stream->nmsgs);
/* copy flags */ /* copy flags */
(elt = mail_elt (astream,astream->nmsgs))->private.uid=astream->uid_l mail_append_set (set,(elt = mail_elt (stream,stream->nmsgs))->private.uid
ast; =
if (f&fSEEN) elt->seen = T; stream->uid_last);
if (f&fDELETED) elt->deleted = T; if (f&fSEEN) elt->seen = T;
if (f&fFLAGGED) elt->flagged = T; if (f&fDELETED) elt->deleted = T;
if (f&fANSWERED) elt->answered = T; if (f&fFLAGGED) elt->flagged = T;
if (f&fDRAFT) elt->draft = T; if (f&fANSWERED) elt->answered = T;
elt->user_flags |= uf; if (f&fDRAFT) elt->draft = T;
/* get next message */ elt->user_flags |= uf;
if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL; return LONGT;
}
} while (ret && message);
else {
MM_LOG ("Message append failed: unable to lock index",ERROR);
ret = NIL;
}
mx_unlockindex (astream); /* unlock index */
MM_NOCRITICAL (stream); /* release critical */
mail_close (astream);
return ret;
} }
/* Internal routines */ /* Internal routines */
/* MX file name selection test /* MX file name selection test
* Accepts: candidate directory entry * Accepts: candidate directory entry
* Returns: T to use file name, NIL to skip it * Returns: T to use file name, NIL to skip it
*/ */
int mx_select (struct direct *name) int mx_select (struct direct *name)
{ {
skipping to change at line 982 skipping to change at line 1114
* Accepts: MAIL stream * Accepts: MAIL stream
* Returns: T if success, NIL if failure * Returns: T if success, NIL if failure
*/ */
long mx_lockindex (MAILSTREAM *stream) long mx_lockindex (MAILSTREAM *stream)
{ {
unsigned long uf,sf,uid; unsigned long uf,sf,uid;
int k = 0; int k = 0;
unsigned long msgno = 1; unsigned long msgno = 1;
struct stat sbuf; struct stat sbuf;
char *s,*t,*idx,tmp[MAILTMPLEN]; char *s,*t,*idx,tmp[2*MAILTMPLEN];
MESSAGECACHE *elt; MESSAGECACHE *elt;
blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,N IL); blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,N IL);
if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */ if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
(LOCAL->fd = open (strcat (strcpy (tmp,LOCAL->dir),MXINDEXNAME), (LOCAL->fd = open (strcat (strcpy (tmp,LOCAL->dir),MXINDEXNAME),
O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) { O_RDWR|O_CREAT,
(int) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
>= 0) {
(*bn) (BLOCK_FILELOCK,NIL); (*bn) (BLOCK_FILELOCK,NIL);
flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */ flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
(*bn) (BLOCK_NONE,NIL); (*bn) (BLOCK_NONE,NIL);
fstat (LOCAL->fd,&sbuf); /* get size of index */ fstat (LOCAL->fd,&sbuf); /* get size of index */
/* slurp index */ /* slurp index */
read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_si ze); read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_si ze);
idx[sbuf.st_size] = '\0'; /* tie off index */ idx[sbuf.st_size] = '\0'; /* tie off index */
/* parse index */ /* parse index */
if (sbuf.st_size) while (s && *s) switch (*s) { if (sbuf.st_size) while (s && *s) switch (*s) {
case 'V': /* UID validity record */ case 'V': /* UID validity record */
skipping to change at line 1050 skipping to change at line 1184
user_flags (stream); /* init stream with default user flags */ user_flags (stream); /* init stream with default user flags */
} }
fs_give ((void **) &idx); /* flush index */ fs_give ((void **) &idx); /* flush index */
} }
return (LOCAL->fd >= 0) ? T : NIL; return (LOCAL->fd >= 0) ? T : NIL;
} }
/* MX write and unlock index /* MX write and unlock index
* Accepts: MAIL stream * Accepts: MAIL stream
*/ */
#define MXIXBUFLEN 2048
void mx_unlockindex (MAILSTREAM *stream) void mx_unlockindex (MAILSTREAM *stream)
{ {
unsigned long i,j; unsigned long i,j;
off_t size = 0; off_t size = 0;
char *s,tmp[MAILTMPLEN + 64]; char *s,tmp[MXIXBUFLEN + 64];
MESSAGECACHE *elt; MESSAGECACHE *elt;
if (LOCAL->fd >= 0) { if (LOCAL->fd >= 0) {
lseek (LOCAL->fd,0,L_SET); /* rewind file */ lseek (LOCAL->fd,0,L_SET); /* rewind file */
/* write header */ /* write header */
sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last); sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]); sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]);
/* write messages */ /* write messages */
for (i = 1; i <= stream->nmsgs; i++) { for (i = 1; i <= stream->nmsgs; i++) {
/* filled buffer? */ /* filled buffer? */
if (((s += strlen (s)) - tmp) > MAILTMPLEN) { if (((s += strlen (s)) - tmp) > MXIXBUFLEN) {
write (LOCAL->fd,tmp,j = s - tmp); write (LOCAL->fd,tmp,j = s - tmp);
size += j; size += j;
*(s = tmp) = '\0'; /* dump out and restart buffer */ *(s = tmp) = '\0'; /* dump out and restart buffer */
} }
elt = mail_elt (stream,i); elt = mail_elt (stream,i);
sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsig ned) sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsig ned)
((fSEEN * elt->seen) + (fDELETED * elt->deleted) + ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
(fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
(fDRAFT * elt->draft))); (fDRAFT * elt->draft)));
} }
 End of changes. 75 change blocks. 
256 lines changed or deleted 394 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/