mx.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:33k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: MX mail routines
  3.  *
  4.  * Author(s): Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 3 May 1996
  13.  * Last Edited: 12 November 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #include <stdio.h>
  36. #include <ctype.h>
  37. #include <errno.h>
  38. extern int errno; /* just in case */
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <pwd.h>
  42. #include <sys/stat.h>
  43. #include <sys/time.h>
  44. #include "mx.h"
  45. #include "misc.h"
  46. #include "dummy.h"
  47. /* MX mail routines */
  48. /* Driver dispatch used by MAIL */
  49. DRIVER mxdriver = {
  50.   "mx", /* driver name */
  51. /* driver flags */
  52.   DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF,
  53.   (DRIVER *) NIL, /* next driver */
  54.   mx_valid, /* mailbox is valid for us */
  55.   mx_parameters, /* manipulate parameters */
  56.   mx_scan, /* scan mailboxes */
  57.   mx_list, /* find mailboxes */
  58.   mx_lsub, /* find subscribed mailboxes */
  59.   mx_subscribe, /* subscribe to mailbox */
  60.   mx_unsubscribe, /* unsubscribe from mailbox */
  61.   mx_create, /* create mailbox */
  62.   mx_delete, /* delete mailbox */
  63.   mx_rename, /* rename mailbox */
  64.   NIL, /* status of mailbox */
  65.   mx_open, /* open mailbox */
  66.   mx_close, /* close mailbox */
  67.   mx_fast, /* fetch message "fast" attributes */
  68.   NIL, /* fetch message flags */
  69.   NIL, /* fetch overview */
  70.   NIL, /* fetch message envelopes */
  71.   mx_header, /* fetch message header only */
  72.   mx_text, /* fetch message body only */
  73.   NIL, /* fetch partial message test */
  74.   NIL, /* unique identifier */
  75.   NIL, /* message number */
  76.   mx_flag, /* modify flags */
  77.   mx_flagmsg, /* per-message modify flags */
  78.   NIL, /* search for message based on criteria */
  79.   NIL, /* sort messages */
  80.   NIL, /* thread messages */
  81.   mx_ping, /* ping mailbox to see if still alive */
  82.   mx_check, /* check for new messages */
  83.   mx_expunge, /* expunge deleted messages */
  84.   mx_copy, /* copy messages to another mailbox */
  85.   mx_append, /* append string message to mailbox */
  86.   NIL /* garbage collect stream */
  87. };
  88. /* prototype stream */
  89. MAILSTREAM mxproto = {&mxdriver};
  90. /* MX mail validate mailbox
  91.  * Accepts: mailbox name
  92.  * Returns: our driver if name is valid, NIL otherwise
  93.  */
  94. DRIVER *mx_valid (char *name)
  95. {
  96.   char tmp[MAILTMPLEN];
  97.   return mx_isvalid (name,tmp) ? &mxdriver : NIL;
  98. }
  99. /* MX mail test for valid mailbox
  100.  * Accepts: mailbox name
  101.  *     temporary buffer to use
  102.  * Returns: T if valid, NIL otherwise
  103.  */
  104. int mx_isvalid (char *name,char *tmp)
  105. {
  106.   struct stat sbuf;
  107.   errno = NIL; /* zap error */
  108. /* validate name as directory */
  109.   return (!stat (MXINDEX (tmp,name),&sbuf) &&
  110.   ((sbuf.st_mode & S_IFMT) == S_IFREG));
  111. }
  112. /* MX manipulate driver parameters
  113.  * Accepts: function code
  114.  *     function-dependent value
  115.  * Returns: function-dependent return value
  116.  */
  117. void *mx_parameters (long function,void *value)
  118. {
  119.   return NIL;
  120. }
  121. /* MX scan mailboxes
  122.  * Accepts: mail stream
  123.  *     reference
  124.  *     pattern to search
  125.  *     string to scan
  126.  */
  127. void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  128. {
  129.   if (stream) mm_log ("Scan not valid for mx mailboxes",ERROR);
  130. }
  131. /* MX list mailboxes
  132.  * Accepts: mail stream
  133.  *     reference
  134.  *     pattern to search
  135.  */
  136. void mx_list (MAILSTREAM *stream,char *ref,char *pat)
  137. {
  138.   char *s,test[MAILTMPLEN],file[MAILTMPLEN];
  139.   long i = 0;
  140. /* get canonical form of name */
  141.   if (stream && dummy_canonicalize (test,ref,pat)) {
  142. /* found any wildcards? */
  143.     if (s = strpbrk (test,"%*")) {
  144. /* yes, copy name up to that point */
  145.       strncpy (file,test,i = s - test);
  146.       file[i] = ''; /* tie off */
  147.     }
  148.     else strcpy (file,test); /* use just that name then */
  149. /* find directory name */
  150.     if (s = strrchr (file,'/')) {
  151.       *s = ''; /* found, tie off at that point */
  152.       s = file;
  153.     }
  154. /* do the work */
  155.     mx_list_work (stream,s,test,0);
  156.   }
  157. }
  158. /* MX list subscribed mailboxes
  159.  * Accepts: mail stream
  160.  *     reference
  161.  *     pattern to search
  162.  */
  163. void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
  164. {
  165.   if (stream) dummy_lsub (NIL,ref,pat);
  166. }
  167. /* MX list mailboxes worker routine
  168.  * Accepts: mail stream
  169.  *     directory name to search
  170.  *     search pattern
  171.  *     search level
  172.  */
  173. void mx_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
  174. {
  175.   DIR *dp;
  176.   struct direct *d;
  177.   struct stat sbuf;
  178.   char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
  179. /* make mailbox and directory names */
  180.   if ((np = name + strlen (strcpy (name,dir ? dir : ""))) != name) *np++ = '/';
  181.   cp = curdir + strlen (strcat ((mx_file(curdir,dir ? dir:myhomedir())),"/"));
  182.   if (dp = opendir (curdir)) { /* open directory */
  183.     while (d = readdir (dp)) { /* scan, ignore . and numeric names */
  184.       if ((d->d_name[0] != '.') && !mx_select (d)) {
  185. if (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)) {
  186.   strcpy (cp,d->d_name);/* make directory name */
  187.   strcpy (np,d->d_name);/* make mx name of directory name */
  188.   if (dmatch (name,pat,'/') && !stat (curdir,&sbuf) &&
  189.       ((sbuf.st_mode &= S_IFMT) == S_IFDIR))
  190.     mx_list_work (stream,name,pat,level+1);
  191. }
  192.       }
  193.       else if (!strcmp (d->d_name,MXINDEXNAME+1) && pmatch_full (dir,pat,'/'))
  194. mm_list (stream,'/',dir,NIL);
  195.     }
  196.     closedir (dp); /* all done, flush directory */
  197.   }
  198. }
  199. /* MX mail subscribe to mailbox
  200.  * Accepts: mail stream
  201.  *     mailbox to add to subscription list
  202.  * Returns: T on success, NIL on failure
  203.  */
  204. long mx_subscribe (MAILSTREAM *stream,char *mailbox)
  205. {
  206.   return sm_subscribe (mailbox);
  207. }
  208. /* MX mail unsubscribe to mailbox
  209.  * Accepts: mail stream
  210.  *     mailbox to delete from subscription list
  211.  * Returns: T on success, NIL on failure
  212.  */
  213. long mx_unsubscribe (MAILSTREAM *stream,char *mailbox)
  214. {
  215.   return sm_unsubscribe (mailbox);
  216. }
  217. /* MX mail create mailbox
  218.  * Accepts: mail stream
  219.  *     mailbox name to create
  220.  * Returns: T on success, NIL on failure
  221.  */
  222. long mx_create (MAILSTREAM *stream,char *mailbox)
  223. {
  224.   int fd;
  225.   char *s,tmp[MAILTMPLEN],mbx[MAILTMPLEN];
  226. /* assume error */
  227.   sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox);
  228. /* make sure valid name */
  229.   for (s = mailbox; s && *s;) {
  230.     if (isdigit (*s)) s++; /* digit, check this node further... */
  231. /* all digit node, barf */
  232.     else if (*s == '/') s = NIL;
  233. /* non-digit in node, skip to next node */
  234.     else if (s = strchr (s+1,'/')) s++;
  235.     else tmp[0] = NIL; /* no more nodes, good name */
  236.   }
  237.   if (tmp[0]); /* was there an error in the name? */
  238. /* must not already exist */
  239.   else if (mx_isvalid (mailbox,tmp))
  240.     sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
  241. /* create directory */
  242.   else if (!dummy_create_path (stream,strcat (mx_file (mbx,mailbox),"/")))
  243.     sprintf (tmp,"Can't create mailbox leaf %.80s: %s",
  244.      mailbox,strerror (errno));
  245. /* create index file */
  246.   else if (((fd = open (MXINDEX (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL,
  247. (int) mail_parameters (NIL,GET_MBXPROTECTION,mailbox)))
  248.     <0 ) || close (fd))
  249.     sprintf (tmp,"Can't create mailbox index %.80s: %s",
  250.      mailbox,strerror (errno));
  251. /* success */
  252.   else return set_mbx_protections (mailbox,mbx) &&
  253.     set_mbx_protections (mailbox,tmp);
  254.   mm_log (tmp,ERROR); /* some error */
  255.   return NIL;
  256. }
  257. /* MX mail delete mailbox
  258.  *     mailbox name to delete
  259.  * Returns: T on success, NIL on failure
  260.  */
  261. long mx_delete (MAILSTREAM *stream,char *mailbox)
  262. {
  263.   DIR *dirp;
  264.   struct direct *d;
  265.   char *s;
  266.   char tmp[MAILTMPLEN];
  267.   if (!mx_isvalid (mailbox,tmp))
  268.     sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
  269. /* delete index */
  270.   else if (unlink (MXINDEX (tmp,mailbox)))
  271.     sprintf (tmp,"Can't delete mailbox %.80s index: %s",
  272.      mailbox,strerror (errno));
  273.   else { /* get directory name */
  274.     *(s = strrchr (tmp,'/')) = '';
  275.     if (dirp = opendir (tmp)) { /* open directory */
  276.       *s++ = '/'; /* restore delimiter */
  277. /* massacre messages */
  278.       while (d = readdir (dirp)) if (mx_select (d)) {
  279. strcpy (s,d->d_name); /* make path */
  280. unlink (tmp); /* sayonara */
  281.       }
  282.       closedir (dirp); /* flush directory */
  283.     }
  284. /* success if can remove the directory */
  285.     if (!rmdir (mx_file (tmp,mailbox))) return T;
  286.     sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
  287.   }
  288.   mm_log (tmp,ERROR); /* something failed */
  289.   return NIL;
  290. }
  291. /* MX mail rename mailbox
  292.  * Accepts: MX mail stream
  293.  *     old mailbox name
  294.  *     new mailbox name
  295.  * Returns: T on success, NIL on failure
  296.  */
  297. long mx_rename (MAILSTREAM *stream,char *old,char *newname)
  298. {
  299.   char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
  300.   struct stat sbuf;
  301.   if (!mx_isvalid (old,tmp))
  302.     sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
  303. /* new mailbox name must not be valid */
  304.   else if (mx_isvalid (newname,tmp))
  305.     sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
  306.      newname);
  307. /* success if can rename the directory */
  308.   else { /* found superior to destination name? */
  309.     if (s = strrchr (mx_file (tmp1,newname),'/')) {
  310.       c = *++s; /* remember first character of inferior */
  311.       *s = ''; /* tie off to get just superior */
  312. /* name doesn't exist, create it */
  313.       if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
  314.   !dummy_create (stream,tmp1)) return NIL;
  315.       *s = c; /* restore full name */
  316.     }
  317.     if (!rename (mx_file (tmp,old),tmp1)) {
  318. /* recreate file if renamed INBOX */
  319.       if (!strcmp (ucase (strcpy (tmp,old)),"INBOX"))
  320. mx_create (NIL,"INBOX");
  321.       return T;
  322.     }
  323.     sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
  324.      old,newname,strerror (errno));
  325.   }
  326.   mm_log (tmp,ERROR); /* something failed */
  327.   return NIL;
  328. }
  329. /* MX mail open
  330.  * Accepts: stream to open
  331.  * Returns: stream on success, NIL on failure
  332.  */
  333. MAILSTREAM *mx_open (MAILSTREAM *stream)
  334. {
  335.   char tmp[MAILTMPLEN];
  336. /* return prototype for OP_PROTOTYPE call */
  337.   if (!stream) return user_flags (&mxproto);
  338.   if (stream->local) fatal ("mx recycle stream");
  339.   stream->local = fs_get (sizeof (MXLOCAL));
  340. /* note if an INBOX or not */
  341.   stream->inbox = !strcmp (ucase (strcpy (tmp,stream->mailbox)),"INBOX");
  342.   mx_file (tmp,stream->mailbox);/* get directory name */
  343.   LOCAL->dir = cpystr (tmp); /* copy directory name for later */
  344. /* make temporary buffer */
  345.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  346.   LOCAL->scantime = 0; /* not scanned yet */
  347.   LOCAL->fd = -1; /* no index yet */
  348.   LOCAL->cachedtexts = 0; /* no cached texts */
  349.   stream->sequence++; /* bump sequence number */
  350. /* parse mailbox */
  351.   stream->nmsgs = stream->recent = 0;
  352.   if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
  353.     mm_log ("Mailbox is empty",(long) NIL);
  354.   stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
  355.     stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
  356.   stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
  357.   stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
  358.     NIL : T; /* can we create new user flags? */
  359.   return stream; /* return stream to caller */
  360. }
  361. /* MX mail close
  362.  * Accepts: MAIL stream
  363.  *     close options
  364.  */
  365. void mx_close (MAILSTREAM *stream,long options)
  366. {
  367.   if (LOCAL) { /* only if a file is open */
  368.     int silent = stream->silent;
  369.     stream->silent = T; /* note this stream is dying */
  370.     if (options & CL_EXPUNGE) mx_expunge (stream);
  371.     if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
  372. /* free local scratch buffer */
  373.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  374. /* nuke the local data */
  375.     fs_give ((void **) &stream->local);
  376.     stream->dtb = NIL; /* log out the DTB */
  377.     stream->silent = silent; /* reset silent state */
  378.   }
  379. }
  380. /* MX mail fetch fast information
  381.  * Accepts: MAIL stream
  382.  *     sequence
  383.  *     option flags
  384.  */
  385. void mx_fast (MAILSTREAM *stream,char *sequence,long flags)
  386. {
  387.   unsigned long i;
  388.   MESSAGECACHE *elt;
  389.   if (stream && LOCAL &&
  390.       ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
  391.        mail_sequence (stream,sequence)))
  392.     for (i = 1; i <= stream->nmsgs; i++)
  393.       if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt);
  394. }
  395. /* MX mail fetch fast information
  396.  * Accepts: MAIL stream
  397.  *     message cache element
  398.  * Returns: name of message file
  399.  */
  400. char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
  401. {
  402.   struct stat sbuf;
  403.   struct tm *tm;
  404. /* build message file name */
  405.   sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
  406.   if (!elt->rfc822_size) { /* have size yet? */
  407.     stat (LOCAL->buf,&sbuf); /* get size of message */
  408. /* make plausible IMAPish date string */
  409.     tm = gmtime (&sbuf.st_mtime);
  410.     elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
  411.     elt->year = tm->tm_year + 1900 - BASEYEAR;
  412.     elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
  413.     elt->seconds = tm->tm_sec;
  414.     elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
  415.     elt->rfc822_size = sbuf.st_size;
  416.   }
  417.   return LOCAL->buf; /* return file name */
  418. }
  419. /* MX mail fetch message header
  420.  * Accepts: MAIL stream
  421.  *     message # to fetch
  422.  *     pointer to returned header text length
  423.  *     option flags
  424.  * Returns: message header in RFC822 format
  425.  */
  426. char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
  427.  long flags)
  428. {
  429.   unsigned long i;
  430.   int fd;
  431.   MESSAGECACHE *elt;
  432.   *length = 0; /* default to empty */
  433.   if (flags & FT_UID) return "";/* UID call "impossible" */
  434.   elt = mail_elt (stream,msgno);/* get elt */
  435.   if (!elt->private.msg.header.text.data) {
  436. /* purge cache if too big */
  437.     if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
  438.       mail_gc (stream,GC_TEXTS);/* just can't keep that much */
  439.       LOCAL->cachedtexts = 0;
  440.     }
  441.     if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return "";
  442. /* is buffer big enough? */
  443.     if (elt->rfc822_size > LOCAL->buflen) {
  444.       fs_give ((void **) &LOCAL->buf);
  445.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
  446.     }
  447. /* slurp message */
  448.     read (fd,LOCAL->buf,elt->rfc822_size);
  449. /* tie off file */
  450.     LOCAL->buf[elt->rfc822_size] = '';
  451.     close (fd); /* flush message file */
  452. /* find end of header */
  453.     if (elt->rfc822_size < 4) i = 0;
  454.     else for (i = 4; (i < elt->rfc822_size) &&
  455.       !((LOCAL->buf[i - 4] == '15') &&
  456. (LOCAL->buf[i - 3] == '12') &&
  457. (LOCAL->buf[i - 2] == '15') &&
  458. (LOCAL->buf[i - 1] == '12')); i++);
  459. /* copy header */
  460.     cpytxt (&elt->private.msg.header.text,LOCAL->buf,i);
  461.     cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i);
  462. /* add to cached size */
  463.     LOCAL->cachedtexts += elt->rfc822_size;
  464.   }
  465.   *length = elt->private.msg.header.text.size;
  466.   return (char *) elt->private.msg.header.text.data;
  467. }
  468. /* MX mail fetch message text (body only)
  469.  * Accepts: MAIL stream
  470.  *     message # to fetch
  471.  *     pointer to returned stringstruct
  472.  *     option flags
  473.  * Returns: T on success, NIL on failure
  474.  */
  475. long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  476. {
  477.   unsigned long i;
  478.   MESSAGECACHE *elt;
  479. /* UID call "impossible" */
  480.   if (flags & FT_UID) return NIL;
  481.   elt = mail_elt (stream,msgno);
  482. /* snarf message if don't have it yet */
  483.   if (!elt->private.msg.text.text.data) {
  484.     mx_header (stream,msgno,&i,flags);
  485.     if (!elt->private.msg.text.text.data) return NIL;
  486.   }
  487. /* mark as seen */
  488.   if (!(flags & FT_PEEK) && mx_lockindex (stream)) {
  489.     elt->seen = T;
  490.     mx_unlockindex (stream);
  491.     mm_flags (stream,msgno);
  492.   }
  493.   INIT (bs,mail_string,elt->private.msg.text.text.data,
  494. elt->private.msg.text.text.size);
  495.   return T;
  496. }
  497. /* MX mail modify flags
  498.  * Accepts: MAIL stream
  499.  *     sequence
  500.  *     flag(s)
  501.  *     option flags
  502.  */
  503. void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
  504. {
  505.   mx_unlockindex (stream); /* finished with index */
  506. }
  507. /* MX per-message modify flags
  508.  * Accepts: MAIL stream
  509.  *     message cache element
  510.  */
  511. void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
  512. {
  513.   mx_lockindex (stream); /* lock index if not already locked */
  514. }
  515. /* MX mail ping mailbox
  516.  * Accepts: MAIL stream
  517.  * Returns: T if stream alive, else NIL
  518.  */
  519. long mx_ping (MAILSTREAM *stream)
  520. {
  521.   MAILSTREAM *sysibx = NIL;
  522.   MESSAGECACHE *elt,*selt;
  523.   struct stat sbuf;
  524.   char *s,tmp[MAILTMPLEN];
  525.   int fd;
  526.   unsigned long i,j,r,old;
  527.   long nmsgs = stream->nmsgs;
  528.   long recent = stream->recent;
  529.   int silent = stream->silent;
  530.   if (stat (LOCAL->dir,&sbuf)) return NIL;
  531.   stream->silent = T; /* don't pass up mm_exists() events yet */
  532.   if (sbuf.st_ctime != LOCAL->scantime) {
  533.     struct direct **names = NIL;
  534.     long nfiles = scandir (LOCAL->dir,&names,mx_select,mx_numsort);
  535.     if (nfiles < 0) nfiles = 0; /* in case error */
  536.     old = stream->uid_last;
  537. /* note scanned now */
  538.     LOCAL->scantime = sbuf.st_ctime;
  539. /* scan directory */
  540.     for (i = 0; i < nfiles; ++i) {
  541. /* if newly seen, add to list */
  542.       if ((j = atoi (names[i]->d_name)) > old) {
  543. /* swell the cache */
  544. mail_exists (stream,++nmsgs);
  545. stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
  546. elt->valid = T; /* note valid flags */
  547. if (old) { /* other than the first pass? */
  548.   elt->recent = T; /* yup, mark as recent */
  549.   recent++; /* bump recent count */
  550. }
  551.       }
  552.       fs_give ((void **) &names[i]);
  553.     }
  554. /* free directory */
  555.     if (s = (void *) names) fs_give ((void **) &s);
  556.   }
  557.   stream->nmsgs = nmsgs; /* don't upset mail_uid() */
  558. /* if INBOX, snarf from system INBOX  */
  559.   if (mx_lockindex (stream) && stream->inbox) {
  560.     old = stream->uid_last;
  561. /* paranoia check */
  562.     if (!strcmp (sysinbox (),stream->mailbox)) {
  563.       stream->silent = silent;
  564.       return NIL;
  565.     }
  566.     mm_critical (stream); /* go critical */
  567.     stat (sysinbox (),&sbuf); /* see if anything there */
  568. /* can get sysinbox mailbox? */
  569.     if (sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT))
  570. && (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
  571.       for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
  572. /* build file name we will use */
  573. sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
  574. /* snarf message from Berkeley mailbox */
  575. selt = mail_elt (sysibx,i);
  576. if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
  577.  S_IREAD|S_IWRITE)) >= 0) &&
  578.     (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
  579.     (write (fd,s,j) == j) &&
  580.     (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
  581.     (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
  582. /* swell the cache */
  583.   mail_exists (stream,++nmsgs);
  584.   stream->uid_last = /* create new elt, note its file number */
  585.     (elt = mail_elt (stream,nmsgs))->private.uid = old;
  586.   recent++; /* bump recent count */
  587. /* set up initial flags and date */
  588.   elt->valid = elt->recent = T;
  589.   elt->seen = selt->seen;
  590.   elt->deleted = selt->deleted;
  591.   elt->flagged = selt->flagged;
  592.   elt->answered = selt->answered;
  593.   elt->draft = selt->draft;
  594.   elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
  595.   elt->hours = selt->hours;elt->minutes = selt->minutes;
  596.   elt->seconds = selt->seconds;
  597.   elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
  598.   elt->zoccident = selt->zoccident;
  599.   mx_setdate (LOCAL->buf,elt);
  600. }
  601. else { /* failed to snarf */
  602.   if (fd) { /* did it ever get opened? */
  603.     close (fd); /* close descriptor */
  604.     unlink (LOCAL->buf);/* flush this file */
  605.   }
  606.   stream->silent = silent;
  607.   return NIL; /* note that something is badly wrong */
  608. }
  609. sprintf (tmp,"%lu",i); /* delete it from the sysinbox */
  610. mail_flag (sysibx,tmp,"\Deleted",ST_SET);
  611.       }
  612.       stat (LOCAL->dir,&sbuf); /* update scan time */
  613.       LOCAL->scantime = sbuf.st_ctime;      
  614.       mail_expunge (sysibx); /* now expunge all those messages */
  615.     }
  616.     if (sysibx) mail_close (sysibx);
  617.     mm_nocritical (stream); /* release critical */
  618.   }
  619.   mx_unlockindex (stream); /* done with index */
  620.   stream->silent = silent; /* can pass up events now */
  621.   mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
  622.   mail_recent (stream,recent);
  623.   return T; /* return that we are alive */
  624. }
  625. /* MX mail check mailbox
  626.  * Accepts: MAIL stream
  627.  */
  628. void mx_check (MAILSTREAM *stream)
  629. {
  630.   if (mx_ping (stream)) mm_log ("Check completed",(long) NIL);
  631. }
  632. /* MX mail expunge mailbox
  633.  * Accepts: MAIL stream
  634.  */
  635. void mx_expunge (MAILSTREAM *stream)
  636. {
  637.   MESSAGECACHE *elt;
  638.   unsigned long i = 1;
  639.   unsigned long n = 0;
  640.   unsigned long recent = stream->recent;
  641.   if (mx_lockindex (stream)) { /* lock the index */
  642.     mm_critical (stream); /* go critical */
  643.     while (i <= stream->nmsgs) {/* for each message */
  644. /* if deleted, need to trash it */
  645.       if ((elt = mail_elt (stream,i))->deleted) {
  646. sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
  647. if (unlink (LOCAL->buf)) {/* try to delete the message */
  648.   sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
  649.    strerror (errno));
  650.   mm_log (LOCAL->buf,(long) NIL);
  651.   break;
  652. }
  653. /* note uncached */
  654. LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
  655. elt->private.msg.header.text.size : 0) +
  656.        (elt->private.msg.text.text.data ?
  657. elt->private.msg.text.text.size : 0));
  658. mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
  659. if(elt->recent)--recent;/* if recent, note one less recent message */
  660. mail_expunged(stream,i);/* notify upper levels */
  661. n++; /* count up one more expunged message */
  662.       }
  663.       else i++; /* otherwise try next message */
  664.     }
  665.     if (n) { /* output the news if any expunged */
  666.       sprintf (LOCAL->buf,"Expunged %lu messages",n);
  667.       mm_log (LOCAL->buf,(long) NIL);
  668.     }
  669.     else mm_log ("No messages deleted, so no update needed",(long) NIL);
  670.     mm_nocritical (stream); /* release critical */
  671.     mx_unlockindex (stream); /* finished with index */
  672.   }
  673. /* notify upper level of new mailbox size */
  674.   mail_exists (stream,stream->nmsgs);
  675.   mail_recent (stream,recent);
  676. }
  677. /* MX mail copy message(s)
  678.  * Accepts: MAIL stream
  679.  *     sequence
  680.  *     destination mailbox
  681.  *     copy options
  682.  * Returns: T if copy successful, else NIL
  683.  */
  684. long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  685. {
  686.   STRING st;
  687.   MESSAGECACHE *elt;
  688.   struct stat sbuf;
  689.   int fd;
  690.   unsigned long i,j;
  691.   char *t,flags[MAILTMPLEN],date[MAILTMPLEN];
  692. /* copy the messages */
  693.   if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
  694.       mail_sequence (stream,sequence))
  695.     for (i = 1; i <= stream->nmsgs; i++) 
  696.       if ((elt = mail_elt (stream,i))->sequence) {
  697. if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))<0) return NIL;
  698. fstat (fd,&sbuf); /* get size of message */
  699. /* is buffer big enough? */
  700. if (sbuf.st_size > LOCAL->buflen) {
  701.   fs_give ((void **) &LOCAL->buf);
  702.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
  703. }
  704. /* slurp message */
  705. read (fd,LOCAL->buf,sbuf.st_size);
  706. /* tie off file */
  707. LOCAL->buf[sbuf.st_size] = '';
  708. close (fd); /* flush message file */
  709. INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size);
  710. /* init flag string */
  711. flags[0] = flags[1] = '';
  712. if (j = elt->user_flags) do
  713.   if (t = stream->user_flags[find_rightmost_bit (&j)])
  714.     strcat (strcat (flags," "),t);
  715. while (j);
  716. if (elt->seen) strcat (flags," \Seen");
  717. if (elt->deleted) strcat (flags," \Deleted");
  718. if (elt->flagged) strcat (flags," \Flagged");
  719. if (elt->answered) strcat (flags," \Answered");
  720. if (elt->draft) strcat (flags," \Draft");
  721. flags[0] = '('; /* open list */
  722. strcat (flags,")"); /* close list */
  723. mail_date (date,elt); /* generate internal date */
  724. if (!mail_append_full (NIL,mailbox,flags,date,&st)) return NIL;
  725. if (options & CP_MOVE) elt->deleted = T;
  726.       }
  727.   return T; /* return success */
  728. }
  729. /* MX mail append message from stringstruct
  730.  * Accepts: MAIL stream
  731.  *     destination mailbox
  732.  *     stringstruct of messages to append
  733.  * Returns: T if append successful, else NIL
  734.  */
  735. long mx_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  736. STRING *message)
  737. {
  738.   MESSAGECACHE *elt,selt;
  739.   MAILSTREAM *astream;
  740.   int fd;
  741.   char *s,tmp[MAILTMPLEN];
  742.   long f;
  743.   long i = 0;
  744.   long size = SIZE (message);
  745.   long ret = LONGT;
  746.   unsigned long uf;
  747.   if (date) { /* want to preserve date? */
  748. /* yes, parse date into an elt */
  749.     if (!mail_parse_date (&selt,date)) {
  750.       sprintf (tmp,"Bad date in append: %.80s",date);
  751.       mm_log (tmp,ERROR);
  752.       return NIL;
  753.     }
  754.   }
  755. /* N.B.: can't use LOCAL->buf for tmp */
  756. /* make sure valid mailbox */
  757.   if (!mx_isvalid (mailbox,tmp)) switch (errno) {
  758.   case ENOENT: /* no such file? */
  759.     if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  760. ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  761. ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  762. ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  763. ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
  764.       mx_create (NIL,"INBOX");
  765.     else {
  766.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  767.       return NIL;
  768.     }
  769. /* falls through */
  770.   case 0: /* merely empty file? */
  771.     break;
  772.   case EINVAL:
  773.     sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox);
  774.     mm_log (tmp,ERROR);
  775.     return NIL;
  776.   default:
  777.     sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox);
  778.     mm_log (tmp,ERROR);
  779.     return NIL;
  780.   }
  781.   if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
  782.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  783.     mm_log (tmp,ERROR);
  784.     return NIL;
  785.   }
  786. /* parse flags */
  787.   f = mail_parse_flags (astream,flags,&uf);
  788.   if (mx_lockindex (astream)) { /* lock the index */
  789.     mx_file (tmp,mailbox); /* make message name */
  790.     sprintf (tmp + strlen (tmp),"/%lu",++astream->uid_last);
  791.     if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
  792.       sprintf (tmp,"Can't create append message: %s",strerror (errno));
  793.       mm_log (tmp,ERROR);
  794.       return NIL;
  795.     }
  796.     s = (char *) fs_get (size); /* copy message */
  797.     for (i = 0; i < size; s[i++] = SNX (message));
  798.     mm_critical (stream); /* go critical */
  799. /* write the data */
  800.     if ((write (fd,s,size) < 0) || fsync (fd)) {
  801.       unlink (tmp); /* delete mailbox */
  802.       sprintf (tmp,"Message append failed: %s",strerror (errno));
  803.       mm_log (tmp,ERROR);
  804.       ret = NIL;
  805.     }
  806.     close (fd); /* close the file */
  807. /* set the date for this message */
  808.     if (date) mx_setdate (tmp,&selt);
  809. /* swell the cache */
  810.     mail_exists (astream,++astream->nmsgs);
  811. /* copy flags */
  812.     (elt = mail_elt (astream,astream->nmsgs))->private.uid = astream->uid_last;
  813.     if (f&fSEEN) elt->seen = T;
  814.     if (f&fDELETED) elt->deleted = T;
  815.     if (f&fFLAGGED) elt->flagged = T;
  816.     if (f&fANSWERED) elt->answered = T;
  817.     if (f&fDRAFT) elt->draft = T;
  818.     elt->user_flags |= uf;
  819.     mx_unlockindex (astream); /* unlock index */
  820.   }
  821.   else {
  822.     mm_log ("Message append failed: unable to lock index",ERROR);
  823.     ret = NIL;
  824.   }
  825.   mm_nocritical (stream); /* release critical */
  826.   fs_give ((void **) &s); /* flush the buffer */
  827.   mail_close (astream);
  828.   return ret;
  829. }
  830. /* Internal routines */
  831. /* MX file name selection test
  832.  * Accepts: candidate directory entry
  833.  * Returns: T to use file name, NIL to skip it
  834.  */
  835. int mx_select (struct direct *name)
  836. {
  837.   char c;
  838.   char *s = name->d_name;
  839.   while (c = *s++) if (!isdigit (c)) return NIL;
  840.   return T;
  841. }
  842. /* MX file name comparision
  843.  * Accepts: first candidate directory entry
  844.  *     second candidate directory entry
  845.  * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
  846.  */
  847. int mx_numsort (const void *d1,const void *d2)
  848. {
  849.   return atoi ((*(struct direct **) d1)->d_name) -
  850.     atoi ((*(struct direct **) d2)->d_name);
  851. }
  852. /* MX mail build file name
  853.  * Accepts: destination string
  854.  *          source
  855.  * Returns: destination
  856.  */
  857. char *mx_file (char *dst,char *name)
  858. {
  859.   char *s;
  860.   if (!(mailboxfile (dst,name) && *dst)) sprintf (dst,"%s/INBOX",myhomedir ());
  861. /* tie off unnecessary trailing / */
  862.   else if ((s = strrchr (dst,'/')) && !s[1]) *s = '';
  863.   return dst;
  864. }
  865. /* MX read and lock index
  866.  * Accepts: MAIL stream
  867.  * Returns: T if success, NIL if failure
  868.  */
  869. long mx_lockindex (MAILSTREAM *stream)
  870. {
  871.   unsigned long uf,sf,uid;
  872.   int k = 0;
  873.   unsigned long msgno = 1;
  874.   struct stat sbuf;
  875.   char *s,*t,*idx,tmp[MAILTMPLEN];
  876.   MESSAGECACHE *elt;
  877.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  878.   if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
  879.       (LOCAL->fd = open (strcat (strcpy (tmp,LOCAL->dir),MXINDEXNAME),
  880.  O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) {
  881.     (*bn) (BLOCK_FILELOCK,NIL);
  882.     flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
  883.     (*bn) (BLOCK_NONE,NIL);
  884.     fstat (LOCAL->fd,&sbuf); /* get size of index */
  885. /* slurp index */
  886.     read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
  887.     idx[sbuf.st_size] = ''; /* tie off index */
  888. /* parse index */
  889.     if (sbuf.st_size) while (s && *s) switch (*s) {
  890.     case 'V': /* UID validity record */
  891.       stream->uid_validity = strtoul (s+1,&s,16);
  892.       break;
  893.     case 'L': /* UID last record */
  894.       stream->uid_last = strtoul (s+1,&s,16);
  895.       break;
  896.     case 'K': /* keyword */
  897. /* find end of keyword */
  898.       if (s = strchr (t = ++s,'n')) {
  899. *s++ = ''; /* tie off keyword */
  900. /* copy keyword */
  901. if ((k < NUSERFLAGS) && !stream->user_flags[k])
  902.   stream->user_flags[k] = cpystr (t);
  903. k++; /* one more keyword */
  904.       }
  905.       break;
  906.     case 'M': /* message status record */
  907.       uid = strtoul (s+1,&s,16);/* get UID for this message */
  908.       if (*s == ';') { /* get user flags */
  909. uf = strtoul (s+1,&s,16);
  910. if (*s == '.') { /* get system flags */
  911.   sf = strtoul (s+1,&s,16);
  912.   while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid))
  913.     msgno++; /* find message number for this UID */
  914.   if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) {
  915.     (elt = mail_elt (stream,msgno))->valid = T;
  916.     elt->user_flags=uf; /* set user and system flags in elt */
  917.     if (sf & fSEEN) elt->seen = T;
  918.     if (sf & fDELETED) elt->deleted = T;
  919.     if (sf & fFLAGGED) elt->flagged = T;
  920.     if (sf & fANSWERED) elt->answered = T;
  921.     if (sf & fDRAFT) elt->draft = T;
  922.   }
  923.   break;
  924. }
  925.       }
  926.     default: /* bad news */
  927.       sprintf (tmp,"Error in index: %.80s",s);
  928.       mm_log (tmp,ERROR);
  929.       *s = NIL; /* ignore remainder of index */
  930.     }
  931.     else { /* new index */
  932.       stream->uid_validity = time (0);
  933.       user_flags (stream); /* init stream with default user flags */
  934.     }
  935.     fs_give ((void **) &idx); /* flush index */
  936.   }
  937.   return (LOCAL->fd >= 0) ? T : NIL;
  938. }
  939. /* MX write and unlock index
  940.  * Accepts: MAIL stream
  941.  */
  942. void mx_unlockindex (MAILSTREAM *stream)
  943. {
  944.   unsigned long i,j;
  945.   off_t size = 0;
  946.   char *s,tmp[MAILTMPLEN + 64];
  947.   MESSAGECACHE *elt;
  948.   if (LOCAL->fd >= 0) {
  949.     lseek (LOCAL->fd,0,L_SET); /* rewind file */
  950. /* write header */
  951.     sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
  952.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
  953.       sprintf (s += strlen (s),"K%sn",stream->user_flags[i]);
  954. /* write messages */
  955.     for (i = 1; i <= stream->nmsgs; i++) {
  956. /* filled buffer? */
  957.       if (((s += strlen (s)) - tmp) > MAILTMPLEN) {
  958. write (LOCAL->fd,tmp,j = s - tmp);
  959. size += j;
  960. *(s = tmp) = ''; /* dump out and restart buffer */
  961.       }
  962.       elt = mail_elt (stream,i);
  963.       sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned)
  964.       ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  965.        (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
  966.        (fDRAFT * elt->draft)));
  967.     }
  968. /* write tail end of buffer */
  969.     if ((s += strlen (s)) != tmp) {
  970.       write (LOCAL->fd,tmp,j = s - tmp);
  971.       size += j;
  972.     }
  973.     ftruncate (LOCAL->fd,size);
  974.     flock (LOCAL->fd,LOCK_UN); /* unlock the index */
  975.     close (LOCAL->fd); /* finished with file */
  976.     LOCAL->fd = -1; /* no index now */
  977.   }
  978. }
  979. /* Set date for message
  980.  * Accepts: file name
  981.  *     elt containing date
  982.  */
  983. void mx_setdate (char *file,MESSAGECACHE *elt)
  984. {
  985.   time_t tp[2];
  986.   tp[0] = time (0); /* atime is now */
  987.   tp[1] = mail_longdate (elt); /* modification time */
  988.   utime (file,tp); /* set the times */
  989. }