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 / mx.c < prev    next >
C/C++ Source or Header  |  1998-09-25  |  32KB  |  1,035 lines

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