home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / osdep / amiga / mbx.c < prev    next >
C/C++ Source or Header  |  1999-01-06  |  48KB  |  1,425 lines

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