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

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: MBX mail routines
  3.  *
  4.  * Author: 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 October 1995
  13.  * Last Edited: 15 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 "mbx.h"
  45. #include "misc.h"
  46. #include "dummy.h"
  47. /* MBX mail routines */
  48. /* Driver dispatch used by MAIL */
  49. DRIVER mbxdriver = {
  50.   "mbx", /* driver name */
  51.   DR_LOCAL|DR_MAIL|DR_CRLF, /* driver flags */
  52.   (DRIVER *) NIL, /* next driver */
  53.   mbx_valid, /* mailbox is valid for us */
  54.   mbx_parameters, /* manipulate parameters */
  55.   mbx_scan, /* scan mailboxes */
  56.   mbx_list, /* list mailboxes */
  57.   mbx_lsub, /* list subscribed mailboxes */
  58.   NIL, /* subscribe to mailbox */
  59.   NIL, /* unsubscribe from mailbox */
  60.   mbx_create, /* create mailbox */
  61.   mbx_delete, /* delete mailbox */
  62.   mbx_rename, /* rename mailbox */
  63.   mbx_status, /* status of mailbox */
  64.   mbx_open, /* open mailbox */
  65.   mbx_close, /* close mailbox */
  66.   mbx_flags, /* fetch message "fast" attributes */
  67.   mbx_flags, /* fetch message flags */
  68.   NIL, /* fetch overview */
  69.   NIL, /* fetch message envelopes */
  70.   mbx_header, /* fetch message header */
  71.   mbx_text, /* fetch message body */
  72.   NIL, /* fetch partial message text */
  73.   NIL, /* unique identifier */
  74.   NIL, /* message number */
  75.   mbx_flag, /* modify flags */
  76.   mbx_flagmsg, /* per-message modify flags */
  77.   NIL, /* search for message based on criteria */
  78.   NIL, /* sort messages */
  79.   NIL, /* thread messages */
  80.   mbx_ping, /* ping mailbox to see if still alive */
  81.   mbx_check, /* check for new messages */
  82.   mbx_expunge, /* expunge deleted messages */
  83.   mbx_copy, /* copy messages to another mailbox */
  84.   mbx_append, /* append string message to mailbox */
  85.   NIL /* garbage collect stream */
  86. };
  87. /* prototype stream */
  88. MAILSTREAM mbxproto = {&mbxdriver};
  89. /* MBX mail validate mailbox
  90.  * Accepts: mailbox name
  91.  * Returns: our driver if name is valid, NIL otherwise
  92.  */
  93. DRIVER *mbx_valid (char *name)
  94. {
  95.   char tmp[MAILTMPLEN];
  96.   return mbx_isvalid (name,tmp) ? &mbxdriver : NIL;
  97. }
  98. /* MBX mail test for valid mailbox
  99.  * Accepts: mailbox name
  100.  *     scratch buffer
  101.  * Returns: T if valid, NIL otherwise
  102.  */
  103. int mbx_isvalid (char *name,char *tmp)
  104. {
  105.   int fd;
  106.   int ret = NIL;
  107.   char *s,hdr[HDRSIZE];
  108.   struct stat sbuf;
  109.   time_t tp[2];
  110.   errno = EINVAL; /* assume invalid argument */
  111.   if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
  112.       ((fd = open (tmp,O_RDONLY,NIL)) >= 0)) {
  113.     errno = -1; /* bogus format */
  114.     /* I love cretinous C compilers -- don't you? */
  115.     if (read (fd,hdr,HDRSIZE) == HDRSIZE)
  116.       if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
  117.   (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '15') &&
  118.   (hdr[6] == '12') && isxdigit (hdr[7]) && isxdigit (hdr[8]))
  119. if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
  120.     isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
  121.     isxdigit (hdr[15]) && isxdigit (hdr[16]))
  122.   if (isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
  123.       isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
  124.       isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
  125.       (hdr[23] == '15') && (hdr[24] == '12')) ret = T;
  126.     close (fd); /* close the file */
  127.     tp[0] = sbuf.st_atime; /* preserve atime and mtime */
  128.     tp[1] = sbuf.st_mtime;
  129.     utime (tmp,tp); /* set the times */
  130.   }
  131. /* in case INBOX but not mbx format */
  132.   else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
  133.    ((name[1] == 'N') || (name[1] == 'n')) &&
  134.    ((name[2] == 'B') || (name[2] == 'b')) &&
  135.    ((name[3] == 'O') || (name[3] == 'o')) &&
  136.    ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
  137.   return ret; /* return what we should */
  138. }
  139. /* MBX manipulate driver parameters
  140.  * Accepts: function code
  141.  *     function-dependent value
  142.  * Returns: function-dependent return value
  143.  */
  144. void *mbx_parameters (long function,void *value)
  145. {
  146.   switch ((int) function) {
  147.   case SET_ONETIMEEXPUNGEATPING:
  148.     if (value && (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expunged))
  149.       ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->fullcheck = T;
  150.   case GET_ONETIMEEXPUNGEATPING:
  151.     if (value) value = (void *)
  152.       ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->fullcheck;
  153.     break;
  154.   }
  155.   return value;
  156. }
  157. /* MBX mail scan mailboxes
  158.  * Accepts: mail stream
  159.  *     reference
  160.  *     pattern to search
  161.  *     string to scan
  162.  */
  163. void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  164. {
  165.   if (stream) dummy_scan (NIL,ref,pat,contents);
  166. }
  167. /* MBX mail list mailboxes
  168.  * Accepts: mail stream
  169.  *     reference
  170.  *     pattern to search
  171.  */
  172. void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
  173. {
  174.   if (stream) dummy_list (NIL,ref,pat);
  175. }
  176. /* MBX mail list subscribed mailboxes
  177.  * Accepts: mail stream
  178.  *     reference
  179.  *     pattern to search
  180.  */
  181. void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
  182. {
  183.   if (stream) dummy_lsub (NIL,ref,pat);
  184. }
  185. /* MBX mail create mailbox
  186.  * Accepts: MAIL stream
  187.  *     mailbox name to create
  188.  * Returns: T on success, NIL on failure
  189.  */
  190. long mbx_create (MAILSTREAM *stream,char *mailbox)
  191. {
  192.   char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE];
  193.   long ret = NIL;
  194.   int i,fd;
  195.   if (!(s = mbx_file (mbx,mailbox))) {
  196.     sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
  197.     mm_log (mbx,ERROR);
  198.   }
  199. /* create underlying file */
  200.   else if (dummy_create_path (stream,s)) {
  201. /* done if made directory */
  202.     if ((s = strrchr (s,'/')) && !s[1]) return T;
  203.     if ((fd = open (mbx,O_WRONLY,
  204.     (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
  205.       sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
  206.       mm_log (tmp,ERROR);
  207.       unlink (mbx); /* delete the file */
  208.     }
  209.     else { /* initialize header */
  210.       memset (tmp,'',HDRSIZE);
  211.       sprintf (s = tmp,"*mbx*1512%08lx000000001512",
  212.        (unsigned long) time (0));
  213.       for (i = 0; i < NUSERFLAGS; ++i)
  214. sprintf (s += strlen (s),"%s1512",(t = default_user_flag (i)) ?
  215.  t : "");
  216.       if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
  217. sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
  218.  mbx,strerror (errno));
  219. mm_log (tmp,ERROR);
  220. unlink (mbx); /* delete the file */
  221.       }
  222.       else ret = T; /* success */
  223.     }
  224.     close (fd); /* close file, set proper protections */
  225.   }
  226.   return ret ? set_mbx_protections (mailbox,mbx) : NIL;
  227. }
  228. /* MBX mail delete mailbox
  229.  * Accepts: MAIL stream
  230.  *     mailbox name to delete
  231.  * Returns: T on success, NIL on failure
  232.  */
  233. long mbx_delete (MAILSTREAM *stream,char *mailbox)
  234. {
  235.   return mbx_rename (stream,mailbox,NIL);
  236. }
  237. /* MBX mail rename mailbox
  238.  * Accepts: MAIL stream
  239.  *     old mailbox name
  240.  *     new mailbox name (or NIL for delete)
  241.  * Returns: T on success, NIL on failure
  242.  */
  243. long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
  244. {
  245.   long ret = T;
  246.   char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  247.   int ld;
  248.   int fd = open (mbx_file (file,old),O_RDWR,NIL);
  249.   struct stat sbuf;
  250.   if (fd < 0) { /* open mailbox */
  251.     sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
  252.     mm_log (tmp,ERROR);
  253.     return NIL;
  254.   }
  255. /* get parse/append permission */
  256.   if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
  257.     mm_log ("Unable to lock rename mailbox",ERROR);
  258.     return NIL;
  259.   }
  260. /* lock out other users */
  261.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  262.     close (fd); /* couldn't lock, give up on it then */
  263.     sprintf (tmp,"Mailbox %.80s is in use by another process",old);
  264.     mm_log (tmp,ERROR);
  265.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  266.     return NIL;
  267.   }
  268.   if (newname) { /* want rename? */
  269.     if (!((s = mbx_file (tmp,newname)) && *s)) {
  270.       sprintf (tmp,"Can't rename mailbox %.80s to %.80s: invalid name",
  271.        old,newname);
  272.       mm_log (tmp,ERROR);
  273.       ret = NIL; /* set failure */
  274.     }
  275.     if (s = strrchr (s,'/')) { /* found superior to destination name? */
  276.       c = *++s; /* remember first character of inferior */
  277.       *s = ''; /* tie off to get just superior */
  278. /* name doesn't exist, create it */
  279.       if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
  280.   !dummy_create (stream,tmp)) ret = NIL;
  281.       else *s = c; /* restore full name */
  282.     }
  283. /* rename the file */
  284.     if (ret && rename (file,tmp)) {
  285.       sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
  286.        strerror (errno));
  287.       mm_log (tmp,ERROR);
  288.       ret = NIL; /* set failure */
  289.     }
  290.   }
  291.   else if (unlink (file)) {
  292.     sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
  293.     mm_log (tmp,ERROR);
  294.     ret = NIL; /* set failure */
  295.   }
  296.   flock (fd,LOCK_UN); /* release lock on the file */
  297.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  298.   close (fd); /* close the file */
  299. /* recreate file if renamed INBOX */
  300.   if (ret && !strcmp (ucase (strcpy (tmp,old)),"INBOX"))
  301.     mbx_create (NIL,"INBOX");
  302.   return ret; /* return success */
  303. }
  304. /* MBX Mail status
  305.  * Accepts: mail stream
  306.  *     mailbox name
  307.  *     status flags
  308.  * Returns: T on success, NIL on failure
  309.  */
  310. long mbx_status (MAILSTREAM *stream,char *mbx,long flags)
  311. {
  312.   MAILSTATUS status;
  313.   unsigned long i;
  314.   MAILSTREAM *tstream = NIL;
  315.   MAILSTREAM *systream = NIL;
  316. /* make temporary stream (unless this mbx) */
  317.   if (!stream && !(stream = tstream =
  318.    mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
  319.   status.flags = flags; /* return status values */
  320.   status.messages = stream->nmsgs;
  321.   status.recent = stream->recent;
  322.   if (flags & SA_UNSEEN) /* must search to get unseen messages */
  323.     for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
  324.       if (!mail_elt (stream,i)->seen) status.unseen++;
  325.   status.uidnext = stream->uid_last + 1;
  326.   status.uidvalidity = stream->uid_validity;
  327. /* calculate post-snarf results */
  328.   if (!status.recent && stream->inbox &&
  329.       (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
  330.     status.messages += systream->nmsgs;
  331.     status.recent += systream->recent;
  332.     if (flags & SA_UNSEEN) /* must search to get unseen messages */
  333.       for (i = 1; i <= systream->nmsgs; i++)
  334. if (!mail_elt (systream,i)->seen) status.unseen++;
  335. /* kludge but probably good enough */
  336.     status.uidnext += systream->nmsgs;
  337.   }
  338. /* pass status to main program */
  339.   mm_status (stream,mbx,&status);
  340.   if (tstream) mail_close (tstream);
  341.   if (systream) mail_close (systream);
  342.   return T; /* success */
  343. }
  344. /* MBX mail open
  345.  * Accepts: stream to open
  346.  * Returns: stream on success, NIL on failure
  347.  */
  348. MAILSTREAM *mbx_open (MAILSTREAM *stream)
  349. {
  350.   int fd,ld;
  351.   short silent;
  352.   char tmp[MAILTMPLEN];
  353.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  354. /* return prototype for OP_PROTOTYPE call */
  355.   if (!stream) return user_flags (&mbxproto);
  356.   if (stream->local) fatal ("mbx recycle stream");
  357.   if (stream->rdonly ||
  358.       (fd = open (mbx_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  359.     if ((fd = open (mbx_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  360.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  361.       mm_log (tmp,ERROR);
  362.       return NIL;
  363.     }
  364.     else if (!stream->rdonly) { /* got it, but readonly */
  365.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  366.       stream->rdonly = T;
  367.     }
  368.   }
  369.   stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
  370.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  371.   LOCAL->buflen = MAXMESSAGESIZE;
  372. /* note if an INBOX or not */
  373.   stream->inbox = !strcmp(ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  374.   fs_give ((void **) &stream->mailbox);
  375.   stream->mailbox = cpystr (tmp);
  376. /* get parse/append permission */
  377.   if ((ld = lockfd (fd,tmp,LOCK_EX)) < 0) {
  378.     mm_log ("Unable to lock open mailbox",ERROR);
  379.     return NIL;
  380.   }
  381.   (*bn) (BLOCK_FILELOCK,NIL);
  382.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  383.   (*bn) (BLOCK_NONE,NIL);
  384.   unlockfd (ld,tmp); /* release shared parse permission */
  385.   LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
  386. /* time not set up yet */
  387.   LOCAL->lastsnarf = LOCAL->filetime = 0;
  388.   LOCAL->fullcheck = LOCAL->flagcheck = NIL;
  389.   stream->sequence++; /* bump sequence number */
  390. /* parse mailbox */
  391.   stream->nmsgs = stream->recent = 0;
  392.   silent = stream->silent; /* defer events */
  393.   stream->silent = T;
  394.   if (mbx_ping (stream) && !stream->nmsgs)
  395.     mm_log ("Mailbox is empty",(long) NIL);
  396.   stream->silent = silent; /* now notify upper level */
  397.   mail_exists (stream,stream->nmsgs);
  398.   mail_recent (stream,stream->recent);
  399.   if (!LOCAL) return NIL; /* failure if stream died */
  400.   stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
  401.     stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
  402.   stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
  403.   stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
  404.     NIL : T; /* can we create new user flags? */
  405.   return stream; /* return stream to caller */
  406. }
  407. /* MBX mail close
  408.  * Accepts: MAIL stream
  409.  *     close options
  410.  */
  411. void mbx_close (MAILSTREAM *stream,long options)
  412. {
  413.   if (stream && LOCAL) { /* only if a file is open */
  414.     int silent = stream->silent;
  415.     stream->silent = T; /* note this stream is dying */
  416.     if (options & CL_EXPUNGE) mbx_expunge (stream);
  417.     stream->silent = silent; /* restore previous status */
  418.     flock (LOCAL->fd,LOCK_UN); /* unlock local file */
  419.     close (LOCAL->fd); /* close the local file */
  420. /* free local text buffer */
  421.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  422. /* nuke the local data */
  423.     fs_give ((void **) &stream->local);
  424.     stream->dtb = NIL; /* log out the DTB */
  425.   }
  426. }
  427. /* MBX mail fetch flags
  428.  * Accepts: MAIL stream
  429.  *     sequence
  430.  *     option flags
  431.  * Sniffs at file to see if some other process changed the flags
  432.  */
  433. void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
  434. {
  435.   unsigned long i;
  436.   if (mbx_ping (stream) &&  /* ping mailbox, get new status for messages */
  437.       ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
  438.        mail_sequence (stream,sequence)))
  439.     for (i = 1; i <= stream->nmsgs; i++) 
  440.       if (mail_elt (stream,i)->sequence) mbx_elt (stream,i,NIL);
  441. }
  442. /* MBX mail fetch message header
  443.  * Accepts: MAIL stream
  444.  *     message # to fetch
  445.  *     pointer to returned header text length
  446.  *     option flags
  447.  * Returns: message header in RFC822 format
  448.  */
  449. char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
  450.   long flags)
  451. {
  452.   *length = 0; /* default to empty */
  453.   if (flags & FT_UID) return "";/* UID call "impossible" */
  454. /* get to header position */
  455.   lseek (LOCAL->fd,mbx_hdrpos (stream,msgno,length),L_SET);
  456. /* is buffer big enough? */
  457.   if (*length > LOCAL->buflen) {
  458.     fs_give ((void **) &LOCAL->buf);
  459.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
  460.   }
  461.   LOCAL->buf[*length] = ''; /* tie off string */
  462. /* slurp the data */
  463.   read (LOCAL->fd,LOCAL->buf,*length);
  464.   return LOCAL->buf;
  465. }
  466. /* MBX mail fetch message text (body only)
  467.  * Accepts: MAIL stream
  468.  *     message # to fetch
  469.  *     pointer to returned header text length
  470.  *     option flags
  471.  * Returns: T, always
  472.  */
  473. long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  474. {
  475.   unsigned long i,j;
  476.   MESSAGECACHE *elt;
  477. /* UID call "impossible" */
  478.   if (flags & FT_UID) return NIL;
  479. /* get message status */
  480.   elt = mbx_elt (stream,msgno,NIL);
  481. /* if message not seen */
  482.   if (!(flags & FT_PEEK) && !elt->seen) {
  483.     elt->seen = T; /* mark message as seen */
  484. /* recalculate status */
  485.     mbx_update_status (stream,msgno,mus_SYNC);
  486.     mm_flags (stream,msgno);
  487.   }
  488. /* find header position */
  489.   i = mbx_hdrpos (stream,msgno,&j);
  490. /* go to text position */
  491.   lseek (LOCAL->fd,i + j,L_SET);
  492. /* is buffer big enough? */
  493.   if ((i = elt->rfc822_size - j) > LOCAL->buflen) {
  494.     fs_give ((void **) &LOCAL->buf);
  495.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  496.   }
  497.   read (LOCAL->fd,LOCAL->buf,i);/* slurp the data */
  498.   LOCAL->buf[i] = ''; /* tie off string */
  499. /* set up stringstruct */
  500.   INIT (bs,mail_string,LOCAL->buf,i);
  501.   return T; /* success */
  502. }
  503. /* MBX mail modify flags
  504.  * Accepts: MAIL stream
  505.  *     sequence
  506.  *     flag(s)
  507.  *     option flags
  508.  */
  509. void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
  510. {
  511.   struct stat sbuf;
  512.   if (!stream->rdonly) { /* make sure the update takes */
  513.     fsync (LOCAL->fd);
  514.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  515.     LOCAL->filetime = sbuf.st_mtime;
  516.   }
  517.   if ((LOCAL->ffuserflag < NUSERFLAGS)&&stream->user_flags[LOCAL->ffuserflag])
  518.     mbx_update_header (stream); /* update header */
  519. }
  520. /* MBX mail per-message modify flags
  521.  * Accepts: MAIL stream
  522.  *     message cache element
  523.  */
  524. void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
  525. {
  526.   struct stat sbuf;
  527. /* maybe need to do a checkpoint? */
  528.   if (LOCAL->filetime && !LOCAL->flagcheck) {
  529.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  530.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
  531.     LOCAL->filetime = 0; /* don't do this test for any other messages */
  532.   }
  533. /* recalculate status */
  534.   mbx_update_status (stream,elt->msgno,NIL);
  535. }
  536. /* MBX mail ping mailbox
  537.  * Accepts: MAIL stream
  538.  * Returns: T if stream still alive, NIL if not
  539.  */
  540. long mbx_ping (MAILSTREAM *stream)
  541. {
  542.   unsigned long i = 1;
  543.   long r = T;
  544.   int ld;
  545.   char lock[MAILTMPLEN];
  546.   struct stat sbuf;
  547.   if (stream && LOCAL) { /* only if stream already open */
  548.     int snarf = stream->inbox && !stream->rdonly;
  549.     fstat (LOCAL->fd,&sbuf); /* get current file poop */
  550.     if (!LOCAL->fullcheck) { /* don't bother if already full checking */
  551.       if (LOCAL->expunged && mail_parameters (NIL,GET_EXPUNGEATPING,NIL))
  552. LOCAL->fullcheck = T; /* upgrade to expunged message checking */
  553.       else if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
  554. LOCAL->flagcheck = T; /* upgrade to flag checking */
  555.     }
  556. /* sweep mailbox for changed message status */
  557.     if (LOCAL->fullcheck || LOCAL->flagcheck) {
  558.       while (i <= stream->nmsgs) if (mbx_elt (stream,i,LOCAL->fullcheck)) ++i;
  559.       LOCAL->flagcheck = NIL; /* got all the updates */
  560.       if (LOCAL->fullcheck) LOCAL->fullcheck = LOCAL->expunged = NIL;
  561.     }
  562.     if ((snarf || (i = (sbuf.st_size - LOCAL->filesize) || !stream->nmsgs)) &&
  563. ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
  564. /* parse new messages in mailbox */
  565.       if (i) r = mbx_parse (stream);
  566.       if (LOCAL && snarf) { /* snarf new messages if still OK */
  567. mbx_snarf (stream);
  568. r = mbx_parse (stream); /* parse snarfed messages */
  569.       }
  570.       unlockfd (ld,lock); /* release shared parse/append permission */
  571.     }
  572.     else if ((sbuf.st_ctime > sbuf.st_atime)||(sbuf.st_ctime > sbuf.st_mtime)){
  573.       time_t tp[2]; /* whack the times if necessary */
  574.       LOCAL->filetime = tp[0] = tp[1] = time (0);
  575.       utime (stream->mailbox,tp);
  576.     }
  577.   }
  578.   return r; /* return result of the parse */
  579. }
  580. /* MBX mail check mailbox (reparses status too)
  581.  * Accepts: MAIL stream
  582.  */
  583. void mbx_check (MAILSTREAM *stream)
  584. {
  585. /* mark that a check is desired */
  586.   if (LOCAL) LOCAL->fullcheck = T;
  587.   if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
  588. }
  589. /* MBX mail snarf messages from system inbox
  590.  * Accepts: MAIL stream, already locked
  591.  */
  592. void mbx_snarf (MAILSTREAM *stream)
  593. {
  594.   unsigned long i = 0;
  595.   unsigned long j,r,hdrlen,txtlen;
  596.   struct stat sbuf;
  597.   char *hdr,*txt,tmp[MAILTMPLEN];
  598.   MESSAGECACHE *elt;
  599.   MAILSTREAM *sysibx = NIL;
  600. /* give up if can't get exclusive permission */
  601.   if ((time (0) < (LOCAL->lastsnarf + 30)) ||
  602.       !strcmp (sysinbox (),stream->mailbox)) return;
  603.   mm_critical (stream); /* go critical */
  604. /* see if anything there */
  605.   if (!stat (sysinbox (),&sbuf) && sbuf.st_size) {
  606.     fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
  607. /* sizes match and can get sysibx mailbox? */
  608.     if ((sbuf.st_size == LOCAL->filesize) && 
  609. (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
  610. (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
  611. /* yes, go to end of file in our mailbox */
  612.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  613. /* for each message in sysibx mailbox */
  614.       while (r && (++i <= sysibx->nmsgs)) {
  615. /* snarf message from system INBOX */
  616. hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
  617. txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
  618. if (j = hdrlen + txtlen) {
  619. /* calculate header line */
  620.   mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
  621.   sprintf (LOCAL->buf + strlen (LOCAL->buf),
  622.    ",%lu;00000000%04x-000000001512",j,(unsigned)
  623.    ((fSEEN * elt->seen) +
  624.     (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) +
  625.     (fANSWERED * elt->answered) + (fDRAFT * elt->draft)));
  626. /* copy message */
  627.   if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
  628.       (write (LOCAL->fd,hdr,hdrlen) < 0) ||
  629.       (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
  630. }
  631. fs_give ((void **) &hdr);
  632.       }
  633. /* make sure all the updates take */
  634.       if (fsync (LOCAL->fd)) r = 0;
  635.       if (r) { /* delete all the messages we copied */
  636. if (r == 1) strcpy (tmp,"1");
  637. else sprintf (tmp,"1:%lu",r);
  638. mail_setflag (sysibx,tmp,"\Deleted");
  639. mail_expunge (sysibx); /* now expunge all those messages */
  640.       }
  641.       else {
  642. sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  643. mm_log (LOCAL->buf,ERROR);
  644. ftruncate (LOCAL->fd,sbuf.st_size);
  645.       }
  646.       fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
  647.       LOCAL->filetime = sbuf.st_mtime;
  648.     }
  649.     if (sysibx) mail_close (sysibx);
  650.   }
  651.   mm_nocritical (stream); /* release critical */
  652.   LOCAL->lastsnarf = time (0); /* note time of last snarf */
  653. }
  654. /* MBX mail expunge mailbox
  655.  * Accepts: MAIL stream
  656.  */
  657. void mbx_expunge (MAILSTREAM *stream)
  658. {
  659.   struct stat sbuf;
  660.   off_t pos,ppos;
  661.   int ld;
  662.   unsigned long i,j,k,m,n,delta,reclaimed;
  663.   unsigned long recent = 0;
  664.   char lock[MAILTMPLEN];
  665.   MESSAGECACHE *elt;
  666.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  667. /* do nothing if stream dead */
  668.   if (!mbx_ping (stream)) return;
  669.   if (stream->rdonly) { /* won't do on readonly files! */
  670.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  671.     return;
  672.   }
  673.   if (LOCAL->filetime && !LOCAL->flagcheck) {
  674.     fstat (LOCAL->fd,&sbuf); /* get current write time */
  675.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
  676.   }
  677.   /* The cretins who designed flock() created a window of vulnerability in
  678.    * upgrading locks from shared to exclusive or downgrading from exclusive
  679.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  680.    * flock() actually *releases* the former lock.  Obviously they never talked
  681.    * to any database guys.  Fortunately, we have the parse/append permission
  682.    * lock.  If we require this lock before going exclusive on the mailbox,
  683.    * another process can not sneak in and steal the exclusive mailbox lock on
  684.    * us, because it will block on trying to get parse/append permission first.
  685.    */
  686. /* get parse/append permission */
  687.   if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) {
  688.     mm_log ("Unable to lock expunge mailbox",ERROR);
  689.     return;
  690.   }
  691. /* get exclusive access */
  692.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  693.     (*bn) (BLOCK_FILELOCK,NIL);
  694.     flock (LOCAL->fd,LOCK_SH); /* recover previous lock */
  695.     (*bn) (BLOCK_NONE,NIL);
  696.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  697.     for (i = 1,n = reclaimed = 0; i <= stream->nmsgs; ) {
  698.       if (elt = mbx_elt (stream,i,T)) {
  699. if (elt->deleted) {
  700.   mbx_update_status (stream,elt->msgno,mus_EXPUNGE);
  701.   mail_expunged(stream,i);/* notify upper levels */
  702.   n++; /* count up one more expunged message */
  703. }
  704. else {
  705.   i++; /* preserved message */
  706.   if (elt->recent) ++recent;
  707. }
  708.       }
  709.       else n++; /* count up one more expunged message */
  710.     }
  711.     fsync (LOCAL->fd); /* force disk update */
  712.   }
  713.   else {
  714.     mm_critical (stream); /* go critical */
  715.     for (i = 1,n = delta = reclaimed = 0,pos = ppos = HDRSIZE;
  716.  i <= stream->nmsgs; ) {
  717.       elt = mbx_elt (stream,i,NIL);
  718. /* note if message not at predicted location */
  719.       if (m = elt->private.special.offset - ppos) {
  720. ppos = elt->private.special.offset;
  721. reclaimed += m; /* note reclaimed message space */
  722. delta += m; /* and as expunge delta  */
  723.       }
  724. /* number of bytes to smash or preserve */
  725.       ppos += (k = elt->private.special.text.size + elt->rfc822_size);
  726.       if (elt->deleted) { /* if deleted */
  727. delta += k; /* number of bytes to delete */
  728. mail_expunged(stream,i);/* notify upper levels */
  729. n++; /* count up one more expunged message */
  730.       }
  731.       else if (i++ && delta) { /* preserved message */
  732. if (elt->recent) ++recent;
  733. /* first byte to preserve */
  734. j = elt->private.special.offset;
  735. do { /* read from source position */
  736.   m = min (k,LOCAL->buflen);
  737.   lseek (LOCAL->fd,j,L_SET);
  738.   read (LOCAL->fd,LOCAL->buf,m);
  739.   pos = j - delta; /* write to destination position */
  740.   while (T) {
  741.     lseek (LOCAL->fd,pos,L_SET);
  742.     if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
  743.     mm_notify (stream,strerror (errno),WARN);
  744.     mm_diskerror (stream,errno,T);
  745.   }
  746.   pos += m; /* new position */
  747.   j += m; /* next chunk, perhaps */
  748. } while (k -= m); /* until done */
  749. /* note the new address of this text */
  750. elt->private.special.offset -= delta;
  751.       }
  752. /* preserved but no deleted messages yet */
  753.       else pos = elt->private.special.offset + k;
  754.     }
  755. /* deltaed file size match position? */
  756.     if (m = (LOCAL->filesize -= delta) - pos) {
  757.       reclaimed += m; /* probably an fEXPUNGED msg */
  758.       LOCAL->filesize = pos; /* set correct size */
  759.     }
  760. /* truncate file after last message */
  761.     ftruncate (LOCAL->fd,LOCAL->filesize);
  762.     fsync (LOCAL->fd); /* force disk update */
  763.     mm_nocritical (stream); /* release critical */
  764.     (*bn) (BLOCK_FILELOCK,NIL);
  765.     flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
  766.     (*bn) (BLOCK_NONE,NIL);
  767.     unlockfd (ld,lock); /* release exclusive parse/append permission */
  768.   }
  769.   fstat (LOCAL->fd,&sbuf); /* get new write time */
  770.   LOCAL->filetime = sbuf.st_mtime;
  771.   if (n) { /* if expunged any messages */
  772.     sprintf (LOCAL->buf,"Expunged %lu messages",n);
  773.     mm_log (LOCAL->buf,(long) NIL);
  774.   }
  775.   else if (reclaimed) { /* or if any prior expunged space reclaimed */
  776.     sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
  777.     mm_log (LOCAL->buf,(long) NIL);
  778.   }
  779.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  780. /* notify upper level of new mailbox size */
  781.   mail_exists (stream,stream->nmsgs);
  782.   mail_recent (stream,recent);
  783. }
  784. /* MBX mail copy message(s)
  785.  * Accepts: MAIL stream
  786.  *     sequence
  787.  *     destination mailbox
  788.  *     copy options
  789.  * Returns: T if success, NIL if failed
  790.  */
  791. long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  792. {
  793.   struct stat sbuf;
  794.   time_t tp[2];
  795.   MESSAGECACHE *elt;
  796.   unsigned long i,j,k;
  797.   long ret = LONGT;
  798.   int fd,ld;
  799.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  800.   mailproxycopy_t pc =
  801.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  802. /* make sure valid mailbox */
  803.   if (!mbx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  804.   case ENOENT: /* no such file? */
  805.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  806.     return NIL;
  807.   case EINVAL:
  808.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  809.     sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
  810.     mm_log (LOCAL->buf,ERROR);
  811.     return NIL;
  812.   default:
  813.     if (pc) return (*pc) (stream,sequence,mailbox,options);
  814.     sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
  815.     mm_log (LOCAL->buf,ERROR);
  816.     return NIL;
  817.   }
  818.   if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
  819. mail_sequence (stream,sequence))) return NIL;
  820. /* got file? */  
  821.   if ((fd=open(mbx_file(file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0) {
  822.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  823.     mm_log (LOCAL->buf,ERROR);
  824.     return NIL;
  825.   }
  826.   mm_critical (stream); /* go critical */
  827. /* get parse/append permission */
  828.   if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
  829.     mm_log ("Unable to lock copy mailbox",ERROR);
  830.     return NIL;
  831.   }
  832.   fstat (fd,&sbuf); /* get current file size */
  833.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  834. /* for each requested message */
  835.   for (i = 1; ret && (i <= stream->nmsgs); i++) 
  836.     if ((elt = mail_elt (stream,i))->sequence) {
  837.       lseek (LOCAL->fd,elt->private.special.offset +
  838.      elt->private.special.text.size,L_SET);
  839.       mail_date(LOCAL->buf,elt);/* build target header */
  840.       sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-000000001512",
  841.        elt->rfc822_size,elt->user_flags,(unsigned)
  842.        ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  843. (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
  844. (fDRAFT * elt->draft)));
  845. /* write target header */
  846.       if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0))
  847. for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
  848.   read (LOCAL->fd,LOCAL->buf,j);
  849.   ret = write (fd,LOCAL->buf,j) >= 0;
  850. }
  851.     }
  852. /* make sure all the updates take */
  853.   if (!(ret && (ret = !fsync (fd)))) {
  854.     sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  855.     mm_log (LOCAL->buf,ERROR);
  856.     ftruncate (fd,sbuf.st_size);
  857.   }
  858.   tp[0] = sbuf.st_atime; /* preserve atime and mtime */
  859.   tp[1] = sbuf.st_mtime;
  860.   utime (file,tp); /* set the times */
  861.   close (fd); /* close the file */
  862.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  863.   mm_nocritical (stream); /* release critical */
  864. /* delete all requested messages */
  865.   if (ret && (options & CP_MOVE)) {
  866.     for (i = 1; i <= stream->nmsgs; ) if (elt = mbx_elt (stream,i,T)) {
  867.       if (elt->sequence) { /* want to do this message? */
  868. elt->deleted = T; /* mark message deleted */
  869. /* recalculate status */
  870. mbx_update_status (stream,i,NIL);
  871.       }
  872.       i++; /* move to next message */
  873.     }
  874.     if (!stream->rdonly) { /* make sure the update takes */
  875.       fsync (LOCAL->fd);
  876.       fstat (LOCAL->fd,&sbuf); /* get current write time */
  877.       LOCAL->filetime = sbuf.st_mtime;
  878.     }
  879.   }
  880.   return ret;
  881. }
  882. /* MBX mail append message from stringstruct
  883.  * Accepts: MAIL stream
  884.  *     destination mailbox
  885.  *     stringstruct of messages to append
  886.  * Returns: T if append successful, else NIL
  887.  */
  888. long mbx_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  889.  STRING *message)
  890. {
  891.   struct stat sbuf;
  892.   int fd,ld;
  893.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  894.   time_t tp[2];
  895.   MESSAGECACHE elt;
  896.   long i = 0;
  897.   long size = SIZE (message);
  898.   long ret = LONGT;
  899.   unsigned long uf = 0;
  900.   long f = mail_parse_flags (stream ? stream : user_flags (&mbxproto),
  901.      flags,&uf);
  902. /* reverse bits (dontcha wish we had CIRC?) */
  903.   if (date) { /* want to preserve date? */
  904. /* yes, parse date into an elt */
  905.     if (!mail_parse_date (&elt,date)) {
  906.       sprintf (tmp,"Bad date in append: %.80s",date);
  907.       mm_log (tmp,ERROR);
  908.       return NIL;
  909.     }
  910.   }
  911. /* N.B.: can't use LOCAL->buf for tmp */
  912. /* make sure valid mailbox */
  913.   if (!mbx_isvalid (mailbox,tmp)) switch (errno) {
  914.   case ENOENT: /* no such file? */
  915.     if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  916. ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  917. ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  918. ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  919. ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
  920.       mbx_create (NIL,"INBOX");
  921.     else {
  922.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  923.       return NIL;
  924.     }
  925. /* falls through */
  926.   case 0: /* merely empty file? */
  927.     break;
  928.   case EINVAL:
  929.     sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
  930.     mm_log (tmp,ERROR);
  931.     return NIL;
  932.   default:
  933.     sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
  934.     mm_log (tmp,ERROR);
  935.     return NIL;
  936.   }
  937.   if ((fd = open (mbx_file (file,mailbox),O_RDWR|O_CREAT,
  938.   S_IREAD|S_IWRITE)) < 0) {
  939.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  940.     mm_log (tmp,ERROR);
  941.     return NIL;
  942.   }
  943. /* get parse/append permission */
  944.   if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
  945.     mm_log ("Unable to lock append mailbox",ERROR);
  946.     return NIL;
  947.   }
  948.   mm_critical (stream); /* go critical */
  949.   fstat (fd,&sbuf); /* get current file size */
  950.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  951.   if (date) mail_date(tmp,&elt);/* write preseved date */
  952.   else internal_date (tmp); /* get current date in IMAP format */
  953. /* add remainder of header */
  954.   sprintf (tmp+26,",%lu;%08lx%04lx-000000001512",(unsigned long) size,uf,
  955.    (unsigned long) f);
  956.   size += (i = strlen (tmp)); /* size of buffer */
  957.   s = (char *) fs_get (size); /* get buffer for message */
  958.   strncpy (s,tmp,i); /* copy message */
  959.   while (i < size) s[i++] = SNX (message);
  960. /* write message */
  961.   if ((write (fd,s,size) < 0) || fsync (fd)) {
  962.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  963.     mm_log (tmp,ERROR);
  964.     ftruncate (fd,sbuf.st_size);
  965.     ret = NIL;
  966.   }
  967.   tp[0] = sbuf.st_atime; /* preserve atime and mtime */
  968.   tp[1] = sbuf.st_mtime;
  969.   utime (file,tp); /* set the times */
  970.   close (fd); /* close the file */
  971.   unlockfd (ld,lock); /* release exclusive parse/append permission */
  972.   mm_nocritical (stream); /* release critical */
  973.   fs_give ((void **) &s); /* flush the buffer */
  974.   return ret;
  975. }
  976. /* Internal routines */
  977. /* MBX mail generate file string
  978.  * Accepts: temporary buffer to write into
  979.  *     mailbox name string
  980.  * Returns: local file string or NIL if failure
  981.  */
  982. char *mbx_file (char *dst,char *name)
  983. {
  984.   char *s = mailboxfile (dst,name);
  985.   return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s;
  986. }
  987. /* MBX mail parse mailbox
  988.  * Accepts: MAIL stream
  989.  * Returns: T if parse OK
  990.  *     NIL if failure, stream aborted
  991.  */
  992. long mbx_parse (MAILSTREAM *stream)
  993. {
  994.   struct stat sbuf;
  995.   MESSAGECACHE *elt = NIL;
  996.   char c,*s,*t,*x;
  997.   char tmp[MAILTMPLEN];
  998.   unsigned long i,j,k,m;
  999.   off_t curpos = LOCAL->filesize;
  1000.   unsigned long nmsgs = stream->nmsgs;
  1001.   unsigned long recent = stream->recent;
  1002.   unsigned long lastuid = 0;
  1003.   short silent = stream->silent;
  1004.   fstat (LOCAL->fd,&sbuf); /* get status */
  1005.   if (sbuf.st_size < curpos) { /* sanity check */
  1006.     sprintf (tmp,"Mailbox shrank from %lu to %lu!",
  1007.      (unsigned long) curpos,(unsigned long) sbuf.st_size);
  1008.     mm_log (tmp,ERROR);
  1009.     mbx_close (stream,NIL);
  1010.     return NIL;
  1011.   }
  1012.   lseek (LOCAL->fd,0,L_SET); /* rewind file */
  1013. /* read internal header */
  1014.   read (LOCAL->fd,LOCAL->buf,HDRSIZE);
  1015.   LOCAL->buf[HDRSIZE] = ''; /* tie off header */
  1016.   c = LOCAL->buf[15]; /* save first character of last UID */
  1017.   LOCAL->buf[15] = '';
  1018. /* parse UID validity */
  1019.   stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
  1020.   LOCAL->buf[15] = c; /* restore first character of last UID */
  1021. /* parse last UID */
  1022.   i = strtoul (LOCAL->buf + 15,NIL,16);
  1023.   stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
  1024. /* parse user flags */
  1025.   for (i = 0, s = LOCAL->buf + 25;
  1026.        (i < NUSERFLAGS) && (t = strchr (s,'15')) && (t - s);
  1027.        i++, s = t + 2) {
  1028.     *t = ''; /* tie off flag */
  1029.     if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (s);
  1030.   }
  1031.   LOCAL->ffuserflag = (int) i; /* first free user flag */
  1032.   stream->silent = T; /* don't pass up mm_exists() events yet */
  1033.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1034. /* get to that position in the file */
  1035.     lseek (LOCAL->fd,curpos,L_SET);
  1036.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  1037.       sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
  1038.        (unsigned long) curpos,(unsigned long) sbuf.st_size,
  1039.        i ? strerror (errno) : "no data read");
  1040.       mm_log (tmp,ERROR);
  1041.       mbx_close (stream,NIL);
  1042.       return NIL;
  1043.     }
  1044.     LOCAL->buf[i] = ''; /* tie off buffer just in case */
  1045.     if (!((s = strchr (LOCAL->buf,'15')) && (s[1] == '12'))) {
  1046.       sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
  1047.        (unsigned long) curpos,i,LOCAL->buf);
  1048.       mm_log (tmp,ERROR);
  1049.       mbx_close (stream,NIL);
  1050.       return NIL;
  1051.     }
  1052.     *s = ''; /* tie off header line */
  1053.     i = (s + 2) - LOCAL->buf; /* note start of text offset */
  1054.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  1055.       sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
  1056.        (unsigned long) curpos,LOCAL->buf);
  1057.       mm_log (tmp,ERROR);
  1058.       mbx_close (stream,NIL);
  1059.       return NIL;
  1060.     }
  1061.     if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
  1062.   isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
  1063.   isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
  1064.   isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
  1065.       sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
  1066.        (unsigned long) curpos,LOCAL->buf);
  1067.       mm_log (tmp,ERROR);
  1068.       mbx_close (stream,NIL);
  1069.       return NIL;
  1070.     }
  1071.     if ((t[13] != '-') || t[22] ||
  1072. !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
  1073.   isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
  1074.   isxdigit (t[20]) && isxdigit (t[21]))) {
  1075.       sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
  1076.        (unsigned long) curpos,LOCAL->buf);
  1077.       mm_log (tmp,ERROR);
  1078.       mbx_close (stream,NIL);
  1079.       return NIL;
  1080.     }
  1081.     *s++ = ''; *t++ = ''; /* break up fields */
  1082. /* get message size */
  1083.     if (!(j = strtoul (s,&x,10)) && (!(x && *x))) {
  1084.       sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
  1085.        (unsigned long) curpos,LOCAL->buf,s,t);
  1086.       mm_log (tmp,ERROR);
  1087.       mbx_close (stream,NIL);
  1088.       return NIL;
  1089.     }
  1090. /* make sure didn't run off end of file */
  1091.     if (((off_t) (curpos + i + j)) > sbuf.st_size) {
  1092.       sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
  1093.        (unsigned long) curpos,(unsigned long) (curpos + i + j),
  1094.        (unsigned long) sbuf.st_size);
  1095.       mm_log (tmp,ERROR);
  1096.       mbx_close (stream,NIL);
  1097.       return NIL;
  1098.     }
  1099. /* parse UID */
  1100.     if ((m = strtoul (t+13,NIL,16)) &&
  1101. ((m <= lastuid) || (m > stream->uid_last))) {
  1102.       sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
  1103.  m,nmsgs+1);
  1104.       mm_log (tmp,WARN);
  1105.       m = 0; /* lose this UID */
  1106. /* restart UID validity and last UID */
  1107.       stream->uid_validity = time (0);
  1108.       stream->uid_last = lastuid;
  1109.     }
  1110.     t[12] = ''; /* parse system flags */
  1111.     if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED)
  1112. /* expunged message, update last UID */
  1113.       lastuid = m ? m : ++stream->uid_last;
  1114.     else { /* not expunged, swell the cache */
  1115.       mail_exists (stream,++nmsgs);
  1116. /* instantiate an elt for this message */
  1117.       (elt = mail_elt (stream,nmsgs))->valid = T;
  1118. /* parse the date */
  1119.       if (!mail_parse_date (elt,LOCAL->buf)) {
  1120. sprintf (tmp,"Unable to parse message date at %lu: %.80s",
  1121.  (unsigned long) curpos,LOCAL->buf);
  1122. mm_log (tmp,ERROR);
  1123. mbx_close (stream,NIL);
  1124. return NIL;
  1125.       }
  1126. /* note file offset of header */
  1127.       elt->private.special.offset = curpos;
  1128. /* and internal header size */
  1129.       elt->private.special.text.size = i;
  1130. /* header size not known yet */
  1131.       elt->private.msg.header.text.size = 0;
  1132.       elt->rfc822_size = j; /* note message size */
  1133. /* calculate system flags */
  1134.       if (k & fSEEN) elt->seen = T;
  1135.       if (k & fDELETED) elt->deleted = T;
  1136.       if (k & fFLAGGED) elt->flagged = T;
  1137.       if (k & fANSWERED) elt->answered = T;
  1138.       if (k & fDRAFT) elt->draft = T;
  1139.       t[8] = ''; /* get user flags value */
  1140.       elt->user_flags = strtoul (t,NIL,16);
  1141. /* UID already assigned? */
  1142.       if (!(elt->private.uid = m)) {
  1143. elt->recent = T; /* no, mark as recent */
  1144. ++recent; /* count up a new recent message */
  1145. /* assign new UID */
  1146. elt->private.uid = ++stream->uid_last;
  1147. mbx_update_status (stream,elt->msgno,NIL);
  1148.       }
  1149. /* update last parsed UID */
  1150.       lastuid = elt->private.uid;
  1151.     }
  1152.     curpos += i + j; /* update position */
  1153.   }
  1154. /* update header */
  1155.   if (!stream->rdonly) mbx_update_header (stream);
  1156.   fsync (LOCAL->fd); /* make sure all the UID updates take */
  1157. /* update parsed file size and time */
  1158.   LOCAL->filesize = sbuf.st_size;
  1159.   fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
  1160.   LOCAL->filetime = sbuf.st_mtime;
  1161.   stream->silent = silent; /* can pass up events now */
  1162.   mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
  1163.   mail_recent (stream,recent); /* and of change in recent messages */
  1164.   return LONGT; /* return the winnage */
  1165. }
  1166. /* MBX get cache element with status updating from file
  1167.  * Accepts: MAIL stream
  1168.  *     message number
  1169.  *     expunge OK flag
  1170.  * Returns: cache element
  1171.  */
  1172. MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
  1173. {
  1174.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1175.   struct { /* old flags */
  1176.     unsigned int seen : 1;
  1177.     unsigned int deleted : 1;
  1178.     unsigned int flagged : 1;
  1179.     unsigned int answered : 1;
  1180.     unsigned int draft : 1;
  1181.     unsigned long user_flags;
  1182.   } old;
  1183.   old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
  1184.   old.answered = elt->answered; old.draft = elt->draft;
  1185.   old.user_flags = elt->user_flags;
  1186. /* get new flags */
  1187.   if (mbx_read_flags (stream,elt) && expok) {
  1188.     mail_expunged (stream,elt->msgno);
  1189.     return NIL; /* return this message was expunged */
  1190.   }
  1191.   if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
  1192.       (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
  1193.       (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
  1194.     mm_flags (stream,msgno); /* let top level know */
  1195.   return elt;
  1196. }
  1197. /* MBX read flags from file
  1198.  * Accepts: MAIL stream
  1199.  *     cache element
  1200.  * Returns: non-NIL if message expunged
  1201.  */
  1202. unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
  1203. {
  1204.   unsigned long i;
  1205. /* noop if readonly and have valid flags */
  1206.   if (stream->rdonly && elt->valid) return NIL;
  1207. /* set the seek pointer */
  1208.   lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1209.  elt->private.special.text.size - 23,L_SET);
  1210. /* read the new flags */
  1211.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1212.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1213.     fatal (LOCAL->buf);
  1214.   }
  1215.   LOCAL->buf[12] = ''; /* tie off buffer */
  1216. /* calculate system flags */
  1217.   i = strtoul (LOCAL->buf+8,NIL,16);
  1218.   elt->seen = i & fSEEN ? T : NIL;
  1219.   elt->deleted = i & fDELETED ? T : NIL;
  1220.   elt->flagged = i & fFLAGGED ? T : NIL;
  1221.   elt->answered = i & fANSWERED ? T : NIL;
  1222.   elt->draft = i & fDRAFT ? T : NIL;
  1223.   LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
  1224.   LOCAL->buf[8] = ''; /* tie off flags */
  1225. /* get user flags value */
  1226.   elt->user_flags = strtoul (LOCAL->buf,NIL,16);
  1227.   elt->valid = T; /* have valid flags now */
  1228.   return i & fEXPUNGED;
  1229. }
  1230. /* MBX update header
  1231.  * Accepts: MAIL stream
  1232.  */
  1233. void mbx_update_header (MAILSTREAM *stream)
  1234. {
  1235.   int i;
  1236.   char *s = LOCAL->buf;
  1237.   memset (s,'',HDRSIZE); /* initialize header */
  1238.   sprintf (s,"*mbx*1512%08lx%08lx1512",
  1239.    stream->uid_validity,stream->uid_last);
  1240.   for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
  1241.     sprintf (s += strlen (s),"%s1512",stream->user_flags[i]);
  1242.   LOCAL->ffuserflag = i; /* first free user flag */
  1243. /* can we create more user flags? */
  1244.   stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
  1245. /* write reserved lines */
  1246.   while (i++ < NUSERFLAGS) strcat (s,"1512");
  1247.   while (T) {
  1248.     lseek (LOCAL->fd,0,L_SET); /* rewind file */
  1249. /* write new header */
  1250.     if (write (LOCAL->fd,LOCAL->buf,HDRSIZE) > 0) break;
  1251.     mm_notify (stream,strerror (errno),WARN);
  1252.     mm_diskerror (stream,errno,T);
  1253.   }
  1254. }
  1255. /* MBX update status string
  1256.  * Accepts: MAIL stream
  1257.  *     message number
  1258.  *     flags
  1259.  */
  1260. void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
  1261. {
  1262.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1263.   struct stat sbuf;
  1264.   int expflag;
  1265. /* readonly */
  1266.   if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
  1267.   else { /* readwrite */
  1268. /* want to expunge message? */
  1269.     if (elt->deleted && (flags & mus_EXPUNGE)) expflag = fEXPUNGED;
  1270.     else { /* seek to system flags */
  1271.       lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1272.      elt->private.special.text.size - 15,L_SET);
  1273. /* read the current system flags */
  1274.       if (read (LOCAL->fd,LOCAL->buf,4) < 0) {
  1275. sprintf (LOCAL->buf,"Unable to read system flags: %s",
  1276.  strerror (errno));
  1277. fatal (LOCAL->buf);
  1278.       }
  1279.       LOCAL->buf[4] = ''; /* tie off buffer */
  1280. /* note current set of expunged flag */
  1281.       expflag = (strtoul (LOCAL->buf,NIL,16)) & fEXPUNGED;
  1282.     }
  1283. /* print new flag string */
  1284.     sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
  1285.      (expflag + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1286.       (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
  1287.       (fDRAFT * elt->draft)),elt->private.uid);
  1288.     while (T) { /* get to that place in the file */
  1289.       lseek (LOCAL->fd,(off_t) elt->private.special.offset +
  1290.      elt->private.special.text.size - 23,L_SET);
  1291. /* write new flags and UID */
  1292.       if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
  1293.       mm_notify (stream,strerror (errno),WARN);
  1294.       mm_diskerror (stream,errno,T);
  1295.     }
  1296.     if (flags & mus_SYNC) { /* sync if requested */
  1297.       fsync (LOCAL->fd);
  1298.       fstat (LOCAL->fd,&sbuf); /* get new write time */
  1299.       LOCAL->filetime = sbuf.st_mtime;
  1300.     }
  1301.   }
  1302. }
  1303. /* MBX locate header for a message
  1304.  * Accepts: MAIL stream
  1305.  *     message number
  1306.  *     pointer to returned header size
  1307.  * Returns: position of header in file
  1308.  */
  1309. unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
  1310.   unsigned long *size)
  1311. {
  1312.   unsigned long siz;
  1313.   long i = 0;
  1314.   int q = 0;
  1315.   char *s,tmp[MAILTMPLEN];
  1316.   MESSAGECACHE *elt = mbx_elt (stream,msgno,NIL);
  1317.   unsigned long ret = elt->private.special.offset +
  1318.     elt->private.special.text.size;
  1319. /* is header size known? */
  1320.   if (!(*size = elt->private.msg.header.text.size)) {
  1321.     lseek (LOCAL->fd,ret,L_SET);/* get to header position */
  1322. /* search message for CRLF CRLF */
  1323.     for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
  1324. /* read another buffer as necessary */
  1325.       if (--i <= 0) /* buffer empty? */
  1326. if (read (LOCAL->fd,s = tmp,
  1327.   i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  1328.   return ret; /* I/O error? */
  1329.       switch (q) { /* sniff at buffer */
  1330.       case 0: /* first character */
  1331. q = (*s++ == '15') ? 1 : 0;
  1332. break;
  1333.       case 1: /* second character */
  1334. q = (*s++ == '12') ? 2 : 0;
  1335. break;
  1336.       case 2: /* third character */
  1337. q = (*s++ == '15') ? 3 : 0;
  1338. break;
  1339.       case 3: /* fourth character */
  1340. if (*s++ == '12') { /* have the sequence? */
  1341. /* yes, note for later */
  1342.   elt->private.msg.header.text.size = *size = siz;
  1343.   return ret;
  1344. }
  1345. q = 0; /* lost... */
  1346. break;
  1347.       }
  1348.     }
  1349. /* header consumes entire message */
  1350.     elt->private.msg.header.text.size = *size = elt->rfc822_size;
  1351.   }
  1352.   return ret;
  1353. }