home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / c-client / tenex2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-14  |  55.8 KB  |  1,804 lines

  1. /*
  2.  * Program:    Tenex 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:    22 May 1990
  13.  * Last Edited:    15 July 1993
  14.  *
  15.  * Copyright 1993 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 <netdb.h>
  40. #include <errno.h>
  41. extern int errno;        /* just in case */
  42. #include "mail.h"
  43. #include "osdep.h"
  44. #include <pwd.h>
  45. #include <sys/file.h>
  46. #include <sys/stat.h>
  47. #include <sys/time.h>
  48. #include "tenex2.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51.  
  52. /* Tenex mail routines */
  53.  
  54.  
  55. /* Driver dispatch used by MAIL */
  56.  
  57. DRIVER tenexdriver = {
  58.   "tenex",            /* driver name */
  59.   (DRIVER *) NIL,        /* next driver */
  60.   tenex_valid,            /* mailbox is valid for us */
  61.   tenex_parameters,        /* manipulate parameters */
  62.   tenex_find,            /* find mailboxes */
  63.   tenex_find_bboards,        /* find bboards */
  64.   tenex_find_all,        /* find all mailboxes */
  65.   tenex_find_all_bboards,    /* find all bboards */
  66.   tenex_subscribe,        /* subscribe to mailbox */
  67.   tenex_unsubscribe,        /* unsubscribe from mailbox */
  68.   tenex_subscribe_bboard,    /* subscribe to bboard */
  69.   tenex_unsubscribe_bboard,    /* unsubscribe from bboard */
  70.   tenex_create,            /* create mailbox */
  71.   tenex_delete,            /* delete mailbox */
  72.   tenex_rename,            /* rename mailbox */
  73.   tenex_open,            /* open mailbox */
  74.   tenex_close,            /* close mailbox */
  75.   tenex_fetchfast,        /* fetch message "fast" attributes */
  76.   tenex_fetchflags,        /* fetch message flags */
  77.   tenex_fetchstructure,        /* fetch message envelopes */
  78.   tenex_fetchheader,        /* fetch message header only */
  79.   tenex_fetchtext,        /* fetch message body only */
  80.   tenex_fetchbody,        /* fetch message body section */
  81.   tenex_setflag,        /* set message flag */
  82.   tenex_clearflag,        /* clear message flag */
  83.   tenex_search,            /* search for message based on criteria */
  84.   tenex_ping,            /* ping mailbox to see if still alive */
  85.   tenex_check,            /* check for new messages */
  86.   tenex_expunge,        /* expunge deleted messages */
  87.   tenex_copy,            /* copy messages to another mailbox */
  88.   tenex_move,            /* move messages to another mailbox */
  89.   tenex_append,            /* append string message to mailbox */
  90.   tenex_gc            /* garbage collect stream */
  91. };
  92.  
  93.                 /* prototype stream */
  94. MAILSTREAM tenexproto = {&tenexdriver};
  95.  
  96. /* Tenex mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *tenex_valid (char *name)
  102. {
  103.   char tmp[MAILTMPLEN];
  104.   return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
  105. }
  106.  
  107.  
  108. /* Tenex mail test for valid mailbox
  109.  * Accepts: mailbox name
  110.  * Returns: T if valid, NIL otherwise
  111.  */
  112.  
  113. long tenex_isvalid (char *name,char *tmp)
  114. {
  115.   int i,fd;
  116.   char *s;
  117.   struct stat sbuf;
  118.   struct hostent *host_name;
  119.   if (!lhostn) {        /* have local host yet? */
  120.     gethostname(tmp,MAILTMPLEN);/* get local host name */
  121.     lhostn = cpystr ((host_name = gethostbyname (tmp)) ?
  122.              host_name->h_name : tmp);
  123.   }
  124.                 /* if file, get its status */
  125.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  126.       (stat (tenex_file (tmp,name),&sbuf) == 0)) {
  127.     if (sbuf.st_size != 0) {    /* if non-empty file */
  128.       if ((fd = open (tmp,O_RDONLY,NIL)) >= 0 && read (fd,tmp,64) >= 0) {
  129.     close (fd);        /* close the file */
  130.     if (s = strchr (tmp,'\n')) {
  131.       *s = '\0';        /* tie off header */
  132.                 /* must begin with dd-mmm-yy" */
  133.       if (((tmp[2] == '-' && tmp[6] == '-') ||
  134.            (tmp[1] == '-' && tmp[5] == '-')) &&
  135.           (s = strchr (tmp+20,',')) && strchr (s+2,';')) return LONGT;
  136.     }
  137.       }
  138.     }
  139.                 /* allow empty if a ".txt" file */
  140.     else if ((i = strlen (tmp)) > 4 && !strcmp (tmp + i - 4 ,".txt"))
  141.       return LONGT;
  142.   }
  143.   return NIL;            /* failed miserably */
  144. }
  145.  
  146.  
  147. /* Tenex manipulate driver parameters
  148.  * Accepts: function code
  149.  *        function-dependent value
  150.  * Returns: function-dependent return value
  151.  */
  152.  
  153. void *tenex_parameters (long function,void *value)
  154. {
  155.   fatal ("Invalid tenex_parameters function");
  156.   return NIL;
  157. }
  158.  
  159. /* Tenex mail find list of mailboxes
  160.  * Accepts: mail stream
  161.  *        pattern to search
  162.  */
  163.  
  164. void tenex_find (MAILSTREAM *stream,char *pat)
  165. {
  166.   void *s = NIL;
  167.   char *t,tmp[MAILTMPLEN];
  168.   while (t = sm_read (&s))    /* read subscription database */
  169.     if ((*t != '{') && (*t != '*') &&
  170.     strcmp (t,"INBOX") && pmatch (t,pat) &&    tenex_isvalid (t,tmp))
  171.       mm_mailbox (t);
  172. }
  173.  
  174.  
  175. /* Tenex mail find list of bboards
  176.  * Accepts: mail stream
  177.  *        pattern to search
  178.  */
  179.  
  180. void tenex_find_bboards (MAILSTREAM *stream,char *pat)
  181. {
  182.   void *s = NIL;
  183.   char *t,tmp[MAILTMPLEN];
  184.   while (t = sm_read (&s))    /* read subscription database */
  185.     if ((*t == '*') && (t[1] != '{') &&
  186.     pmatch (t+1,pat) && tenex_isvalid (t+1,tmp))
  187.       mm_bboard (t+1);
  188. }
  189.  
  190. /* Tenex mail find list of all mailboxes
  191.  * Accepts: mail stream
  192.  *        pattern to search
  193.  */
  194.  
  195. void tenex_find_all (MAILSTREAM *stream,char *pat)
  196. {
  197.   DIR *dirp;
  198.   struct direct *d;
  199.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  200.   int i = 0;
  201.   char *s,*t;
  202.   if (s = strrchr (pat,'/')) {    /* directory specified in pattern? */
  203.     strncpy (file,pat,i = (++s) - pat);
  204.     file[i] = '\0';        /* tie off prefix */
  205.     t = tenex_file (tmp,pat);    /* make fully-qualified file name */
  206.                 /* tie off directory name */
  207.     if (s = strrchr (t,'/')) *s = '\0';
  208.   }
  209.   else t = myhomedir ();    /* use home directory to search */
  210.   if (dirp = opendir (t)) {    /* now open that directory */
  211.     while (d = readdir (dirp)) {/* for each directory entry */
  212.       strcpy (file + i,d->d_name);
  213.       if (pmatch (file,pat) && (tenex_isvalid (file,tmp))) mm_mailbox (file);
  214.     }
  215.     closedir (dirp);        /* flush directory */
  216.   }
  217. }
  218.  
  219. /* Tenex mail find list of all bboards
  220.  * Accepts: mail stream
  221.  *        pattern to search
  222.  */
  223.  
  224. void tenex_find_all_bboards (MAILSTREAM *stream,char *pat)
  225. {
  226.   DIR *dirp;
  227.   struct direct *d;
  228.   struct passwd *pw;
  229.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  230.   int i = 1;
  231.   char *s;
  232.   if (!((pw = getpwnam ("ftp")) && pw->pw_dir)) return;
  233.   file[0] = '*';        /* bboard designator */
  234.                 /* directory specified in pattern? */
  235.   if (s = strrchr (pat,'/')) strncpy (file + 1,pat,i += (++s) - pat);
  236.   file[i] = '\0';        /* tie off prefix */
  237.   sprintf (tmp,"%s/%s",pw->pw_dir,(file[1] == '/') ? file + 2 : file + 1);
  238.   if (dirp = opendir (tmp)) {    /* now open that directory */
  239.     while (d = readdir (dirp)) {/* for each directory entry */
  240.       strcpy (file + i,d->d_name);
  241.       if (pmatch (file + 1,pat) && (tenex_isvalid (file,tmp)))
  242.     mm_bboard (file + 1);
  243.     }
  244.     closedir (dirp);        /* flush directory */
  245.   }
  246. }
  247.  
  248. /* Tenex mail subscribe to mailbox
  249.  * Accepts: mail stream
  250.  *        mailbox to add to subscription list
  251.  * Returns: T on success, NIL on failure
  252.  */
  253.  
  254. long tenex_subscribe (MAILSTREAM *stream,char *mailbox)
  255. {
  256.   char tmp[MAILTMPLEN];
  257.   return sm_subscribe (tenex_file (tmp,mailbox));
  258. }
  259.  
  260.  
  261. /* Tenex mail unsubscribe to mailbox
  262.  * Accepts: mail stream
  263.  *        mailbox to delete from subscription list
  264.  * Returns: T on success, NIL on failure
  265.  */
  266.  
  267. long tenex_unsubscribe (MAILSTREAM *stream,char *mailbox)
  268. {
  269.   char tmp[MAILTMPLEN];
  270.   return sm_unsubscribe (tenex_file (tmp,mailbox));
  271. }
  272.  
  273.  
  274. /* Tenex mail subscribe to bboard
  275.  * Accepts: mail stream
  276.  *        bboard to add to subscription list
  277.  * Returns: T on success, NIL on failure
  278.  */
  279.  
  280. long tenex_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  281. {
  282.   return NIL;            /* never valid for Tenex */
  283. }
  284.  
  285.  
  286. /* Tenex mail unsubscribe to bboard
  287.  * Accepts: mail stream
  288.  *        bboard to delete from subscription list
  289.  * Returns: T on success, NIL on failure
  290.  */
  291.  
  292. long tenex_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  293. {
  294.   return NIL;            /* never valid for Tenex */
  295. }
  296.  
  297. /* Tenex mail create mailbox
  298.  * Accepts: MAIL stream
  299.  *        mailbox name to create
  300.  * Returns: T on success, NIL on failure
  301.  */
  302.  
  303. long tenex_create (MAILSTREAM *stream,char *mailbox)
  304. {
  305.   char tmp[MAILTMPLEN];
  306.   int i,fd;
  307.                 /* must be a ".txt" file */
  308.   if ((i = strlen (mailbox)) < 4 || strcmp (mailbox + i - 4 ,".txt")) {
  309.     mm_log ("Can't create mailbox: name must end with .txt",ERROR);
  310.     return NIL;
  311.   }
  312.   if ((fd = open (tenex_file (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL,0600)) < 0){
  313.     sprintf (tmp,"Can't create mailbox %s: %s",mailbox,strerror (errno));
  314.     mm_log (tmp,ERROR);
  315.     return NIL;
  316.   }
  317.   close (fd);            /* close the file */
  318.   return LONGT;            /* return success */
  319. }
  320.  
  321.  
  322. /* Tenex mail delete mailbox
  323.  * Accepts: MAIL stream
  324.  *        mailbox name to delete
  325.  * Returns: T on success, NIL on failure
  326.  */
  327.  
  328. long tenex_delete (MAILSTREAM *stream,char *mailbox)
  329. {
  330.   return tenex_rename (stream,mailbox,NIL);
  331. }
  332.  
  333. /* Tenex mail rename mailbox
  334.  * Accepts: MAIL stream
  335.  *        old mailbox name
  336.  *        new mailbox name (or NIL for delete)
  337.  * Returns: T on success, NIL on failure
  338.  */
  339.  
  340. long tenex_rename (MAILSTREAM *stream,char *old,char *new)
  341. {
  342.   long ret = T;
  343.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  344.   int ld;
  345.   int fd = open (tenex_file (file,old),O_RDONLY,NIL);
  346.                 /* lock out non c-client applications */
  347.   if (fd < 0) {            /* open mailbox */
  348.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  349.     mm_log (tmp,ERROR);
  350.     return NIL;
  351.   }
  352.                 /* get exclusive parse/append permission */
  353.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  354.     mm_log ("Unable to lock rename mailbox",ERROR);
  355.     return NIL;
  356.   }
  357.                 /* lock out other users */
  358.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  359.     close (fd);            /* couldn't lock, give up on it then */
  360.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  361.     mm_log (tmp,ERROR);
  362.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  363.     return NIL;
  364.   }
  365.                 /* do the rename or delete operation */
  366.   if (new ? rename (file,tenex_file (tmp,new)) : unlink (file)) {
  367.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  368.          strerror (errno));
  369.     mm_log (tmp,ERROR);
  370.     ret = NIL;            /* set failure */
  371.   }
  372.   flock (fd,LOCK_UN);        /* release lock on the file */
  373.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  374.   close (fd);            /* close the file */
  375.   return ret;            /* return success */
  376. }
  377.  
  378. /* Tenex mail open
  379.  * Accepts: stream to open
  380.  * Returns: stream on success, NIL on failure
  381.  */
  382.  
  383. MAILSTREAM *tenex_open (MAILSTREAM *stream)
  384. {
  385.   long i;
  386.   int fd,ld;
  387.   char *s,*t,*k;
  388.   char tmp[MAILTMPLEN];
  389.   struct stat sbuf;
  390.                 /* return prototype for OP_PROTOTYPE call */
  391.   if (!stream) return &tenexproto;
  392.   if (LOCAL) {            /* close old file if stream being recycled */
  393.     tenex_close (stream);    /* dump and save the changes */
  394.     stream->dtb = &tenexdriver;    /* reattach this driver */
  395.     mail_free_cache (stream);    /* clean up cache */
  396.   }
  397.   else {            /* flush old flagstring and flags if any */
  398.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  399.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  400.                 /* open .imapinit or .mminit file */
  401.     if ((fd = open (strcat (strcpy (tmp,myhomedir ()),"/.imapinit"),
  402.             O_RDONLY,NIL)) < 0)
  403.       fd = open (strcat (strcpy (tmp,myhomedir ()),"/.mminit"),O_RDONLY,NIL);
  404.     if (fd >= 0) {        /* got an init file? */
  405.       fstat (fd,&sbuf);        /* yes, get size */
  406.       read (fd,(s = stream->flagstring = (char *) fs_get (sbuf.st_size + 1)),
  407.         sbuf.st_size);    /* make readin area and read the file */
  408.       close (fd);        /* don't need the file any more */
  409.       s[sbuf.st_size] = '\0';    /* tie it off */
  410.                 /* parse init file */
  411.       while (*s && (t = strchr (s,'\n'))) {
  412.     *t = '\0';        /* tie off line, find second space */
  413.     if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
  414.       *k = '\0';        /* tie off two words, is it "set keywords"? */
  415.       if (!strcmp (lcase (s),"set keywords")) {
  416.                 /* yes, get first keyword */
  417.         k = strtok (++k,", ");
  418.                 /* until parsed all keywords or empty */
  419.         for (i = 0; k && i < NUSERFLAGS; ++i) {
  420.                 /* set keyword, get next one */
  421.           stream->user_flags[i] = k;
  422.           k = strtok (NIL,", ");
  423.         }
  424.         break;        /* don't need to look at rest of file */
  425.       }
  426.     }
  427.     s = ++t;        /* try next line */
  428.       }
  429.     }
  430.   }
  431.  
  432.                 /* force readonly if bboard */
  433.   if (*stream->mailbox == '*') stream->readonly = T;
  434.   if (stream->readonly ||
  435.       (fd = open (tenex_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  436.     if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  437.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  438.       mm_log (tmp,ERROR);
  439.       return NIL;
  440.     }
  441.     else if (!stream->readonly) {/* got it, but readonly */
  442.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  443.       stream->readonly = T;
  444.     }
  445.   }
  446.   stream->local = fs_get (sizeof (TENEXLOCAL));
  447.                 /* note if an INBOX or not */
  448.   LOCAL->inbox = !strcmp (ucase (stream->mailbox),"INBOX");
  449.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  450.     fs_give ((void **) &stream->mailbox);
  451.     stream->mailbox = cpystr (tmp);
  452.   }
  453.                 /* get shared parse permission */
  454.   if ((ld = tenex_lock (fd,tmp,LOCK_SH)) < 0) {
  455.     mm_log ("Unable to lock open mailbox",ERROR);
  456.     return NIL;
  457.   }
  458.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  459.   tenex_unlock (ld,tmp);    /* release shared parse permission */
  460.   LOCAL->filesize = 0;        /* initialize parsed file size */
  461.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  462.   LOCAL->buflen = MAXMESSAGESIZE;
  463.   stream->sequence++;        /* bump sequence number */
  464.                 /* parse mailbox */
  465.   stream->nmsgs = stream->recent = 0;
  466.   if (tenex_ping (stream) && !stream->nmsgs)
  467.     mm_log ("Mailbox is empty",(long) NIL);
  468.   return stream;        /* return stream to caller */
  469. }
  470.  
  471. /* Tenex mail close
  472.  * Accepts: MAIL stream
  473.  */
  474.  
  475. void tenex_close (MAILSTREAM *stream)
  476. {
  477.   if (stream && LOCAL) {    /* only if a file is open */
  478.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  479.     close (LOCAL->fd);        /* close the local file */
  480.                 /* free local text buffer */
  481.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  482.                 /* nuke the local data */
  483.     fs_give ((void **) &stream->local);
  484.     stream->dtb = NIL;        /* log out the DTB */
  485.   }
  486. }
  487.  
  488.  
  489. /* Tenex mail fetch fast information
  490.  * Accepts: MAIL stream
  491.  *        sequence
  492.  */
  493.  
  494. void tenex_fetchfast (MAILSTREAM *stream,char *sequence)
  495. {
  496.   return;            /* no-op for local mail */
  497. }
  498.  
  499.  
  500. /* Tenex mail fetch flags
  501.  * Accepts: MAIL stream
  502.  *        sequence
  503.  */
  504.  
  505. void tenex_fetchflags (MAILSTREAM *stream,char *sequence)
  506. {
  507.   return;            /* no-op for local mail */
  508. }
  509.  
  510. /* Tenex mail fetch structure
  511.  * Accepts: MAIL stream
  512.  *        message # to fetch
  513.  *        pointer to return body
  514.  * Returns: envelope of this message, body returned in body value
  515.  *
  516.  * Fetches the "fast" information as well
  517.  */
  518.  
  519. ENVELOPE *tenex_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  520. {
  521.   LONGCACHE *lelt;
  522.   ENVELOPE **env;
  523.   BODY **b;
  524.   STRING bs;
  525.   char *hdr,*text;
  526.   unsigned long hdrsize;
  527.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  528.   unsigned long textsize = body ? tenex_size (stream,msgno) - hdrsize : 0;
  529.   unsigned long i = max (hdrsize,textsize);
  530.   if (stream->scache) {        /* short cache */
  531.     if (msgno != stream->msgno){/* flush old poop if a different message */
  532.       mail_free_envelope (&stream->env);
  533.       mail_free_body (&stream->body);
  534.     }
  535.     stream->msgno = msgno;
  536.     env = &stream->env;        /* get pointers to envelope and body */
  537.     b = &stream->body;
  538.   }
  539.   else {            /* long cache */
  540.     lelt = mail_lelt (stream,msgno);
  541.     env = &lelt->env;        /* get pointers to envelope and body */
  542.     b = &lelt->body;
  543.   }
  544.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  545.     mail_free_envelope (env);    /* flush old envelope and body */
  546.     mail_free_body (b);
  547.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  548.       fs_give ((void **) &LOCAL->buf);
  549.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  550.     }
  551.                 /* get to header position */
  552.     lseek (LOCAL->fd,hdrpos,L_SET);
  553.                 /* read the header and text */
  554.     read (LOCAL->fd,hdr = (char *) fs_get (hdrsize + 1),hdrsize);
  555.     read (LOCAL->fd,text = (char *) fs_get (textsize + 1),textsize);
  556.     hdr[hdrsize] = '\0';    /* make sure tied off */
  557.     text[textsize] = '\0';    /* make sure tied off */
  558.     INIT (&bs,mail_string,(void *) text,textsize);
  559.                 /* parse envelope and body */
  560.     rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,lhostn,LOCAL->buf);
  561.     fs_give ((void **) &text);
  562.     fs_give ((void **) &hdr);
  563.   }
  564.   if (body) *body = *b;        /* return the body */
  565.   return *env;            /* return the envelope */
  566. }
  567.  
  568. /* Tenex mail fetch message header
  569.  * Accepts: MAIL stream
  570.  *        message # to fetch
  571.  * Returns: message header in RFC822 format
  572.  */
  573.  
  574. char *tenex_fetchheader (MAILSTREAM *stream,long msgno)
  575. {
  576.   unsigned long hdrsize;
  577.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  578.   char *s = (char *) fs_get (1 + hdrsize);
  579.   s[hdrsize] = '\0';        /* tie off string */
  580.                 /* get to header position */
  581.   lseek (LOCAL->fd,hdrpos,L_SET);
  582.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  583.                 /* copy the string */
  584.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,hdrsize);
  585.   fs_give ((void **) &s);    /* flush readin buffer */
  586.   return LOCAL->buf;
  587. }
  588.  
  589.  
  590. /* Tenex mail fetch message text (body only)
  591.  * Accepts: MAIL stream
  592.  *        message # to fetch
  593.  * Returns: message text in RFC822 format
  594.  */
  595.  
  596. char *tenex_fetchtext (MAILSTREAM *stream,long msgno)
  597. {
  598.   unsigned long hdrsize;
  599.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  600.   unsigned long textsize = tenex_size (stream,msgno) - hdrsize;
  601.   char *s = (char *) fs_get (1 + textsize);
  602.   s[textsize] = '\0';        /* tie off string */
  603.                 /* mark message as seen */
  604.   mail_elt (stream,msgno)->seen = T;
  605.                 /* recalculate status */
  606.   tenex_update_status (stream,msgno,T);
  607.                 /* get to text position */
  608.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  609.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  610.                 /* copy the string */
  611.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,textsize);
  612.   fs_give ((void **) &s);    /* flush readin buffer */
  613.   return LOCAL->buf;
  614. }
  615.  
  616. /* Tenex fetch message body as a structure
  617.  * Accepts: Mail stream
  618.  *        message # to fetch
  619.  *        section specifier
  620.  *        pointer to length
  621.  * Returns: pointer to section of message body
  622.  */
  623.  
  624. char *tenex_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  625. {
  626.   BODY *b;
  627.   PART *pt;
  628.   char *t;
  629.   unsigned long i;
  630.   unsigned long base;
  631.   unsigned long offset = 0;
  632.   unsigned long hdrpos = tenex_header (stream,m,&base);
  633.   MESSAGECACHE *elt = mail_elt (stream,m);
  634.                 /* make sure have a body */
  635.   if (!(tenex_fetchstructure (stream,m,&b) && b && s && *s &&
  636.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  637.   do {                /* until find desired body part */
  638.                 /* multipart content? */
  639.     if (b->type == TYPEMULTIPART) {
  640.       pt = b->contents.part;    /* yes, find desired part */
  641.       while (--i && (pt = pt->next));
  642.       if (!pt) return NIL;    /* bad specifier */
  643.                 /* note new body, check valid nesting */
  644.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  645.       offset = pt->offset;    /* get new offset */
  646.     }
  647.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  648.                 /* need to go down further? */
  649.     if (i = *s) switch (b->type) {
  650.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  651.       offset = b->contents.msg.offset;
  652.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  653.     case TYPEMULTIPART:        /* multipart, get next section */
  654.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  655.     default:            /* bogus subpart specification */
  656.       return NIL;
  657.     }
  658.   } while (i);
  659.                 /* lose if body bogus */
  660.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  661.   elt->seen = T;        /* mark message as seen */
  662.                 /* recalculate status */
  663.   tenex_update_status (stream,m,T);
  664.                 /* move to that place in the data */
  665.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  666.                 /* slurp the data */
  667.   t = (char *) fs_get (1 + b->size.ibytes);
  668.   read (LOCAL->fd,t,b->size.ibytes);
  669.   rfc822_contents(&LOCAL->buf,&LOCAL->buflen,len,t,b->size.ibytes,b->encoding);
  670.   fs_give ((void **) &t);    /* flush readin buffer */
  671.   return LOCAL->buf;
  672. }
  673.  
  674. /* Tenex locate header for a message
  675.  * Accepts: MAIL stream
  676.  *        message number
  677.  *        pointer to returned header size
  678.  * Returns: position of header in file
  679.  */
  680.  
  681. unsigned long tenex_header (MAILSTREAM *stream,long msgno,unsigned long *size)
  682. {
  683.   long siz;
  684.   long i = 0;
  685.   char c = '\0';
  686.   char *s = NIL;
  687.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  688.   long pos = elt->data1 + (elt->data2 >> 24);
  689.   long msiz = tenex_size (stream,msgno);
  690.                 /* is size known? */
  691.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  692.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  693.                 /* search message for LF LF */
  694.     for (siz = 0; siz < msiz; siz++) {
  695.       if (--i <= 0)        /* read another buffer as necessary */
  696.     read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
  697.                 /* two newline sequence? */
  698.       if ((c == '\012') && (*s == '\012')) {
  699.                 /* yes, note for later */
  700.     elt->data2 |= (*size = siz + 1);
  701.     return pos;        /* return to caller */
  702.       }
  703.       else c = *s++;        /* next character */
  704.     }
  705.   }
  706.   return pos;            /* have position */
  707. }
  708.  
  709. /* Tenex mail set flag
  710.  * Accepts: MAIL stream
  711.  *        sequence
  712.  *        flag(s)
  713.  */
  714.  
  715. void tenex_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  716. {
  717.   MESSAGECACHE *elt;
  718.   long i;
  719.   long uf;
  720.   short f;
  721.                 /* no-op if no flags to modify */
  722.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  723.                 /* get sequence and loop on it */
  724.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  725.     if ((elt = tenex_elt (stream,i))->sequence) {
  726.                 /* set all requested flags */
  727.       if (f&fSEEN) elt->seen = T;
  728.       if (f&fDELETED) elt->deleted = T;
  729.       if (f&fFLAGGED) elt->flagged = T;
  730.       if (f&fANSWERED) elt->answered = T;
  731.       elt->user_flags |= uf;
  732.                 /* recalculate status */
  733.       tenex_update_status (stream,i,NIL);
  734.     }
  735.                 /* make sure the update takes */
  736.   if (!stream->readonly) fsync (LOCAL->fd);
  737. }
  738.  
  739. /* Tenex mail clear flag
  740.  * Accepts: MAIL stream
  741.  *        sequence
  742.  *        flag(s)
  743.  */
  744.  
  745. void tenex_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  746. {
  747.   MESSAGECACHE *elt;
  748.   long i;
  749.   long uf;
  750.   short f;
  751.                 /* no-op if no flags to modify */
  752.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  753.                 /* get sequence and loop on it */
  754.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  755.     if ((elt = tenex_elt (stream,i))->sequence) {
  756.                 /* clear all requested flags */
  757.       if (f&fSEEN) elt->seen = NIL;
  758.       if (f&fDELETED) elt->deleted = NIL;
  759.       if (f&fFLAGGED) elt->flagged = NIL;
  760.       if (f&fANSWERED) elt->answered = NIL;
  761.       elt->user_flags &= ~uf;
  762.                 /* recalculate status */
  763.       tenex_update_status (stream,i,NIL);
  764.     }
  765.                 /* make sure the update takes */
  766.   if (!stream->readonly) fsync (LOCAL->fd);
  767. }
  768.  
  769. /* Tenex mail search for messages
  770.  * Accepts: MAIL stream
  771.  *        search criteria
  772.  */
  773.  
  774. void tenex_search (MAILSTREAM *stream,char *criteria)
  775. {
  776.   long i,n;
  777.   char *d;
  778.   search_t f;
  779.                 /* initially all searched */
  780.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  781.                 /* get first criterion */
  782.   if (criteria && (criteria = strtok (criteria," "))) {
  783.                 /* for each criterion */
  784.     for (; criteria; (criteria = strtok (NIL," "))) {
  785.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  786.       switch (*ucase (criteria)) {
  787.       case 'A':            /* possible ALL, ANSWERED */
  788.     if (!strcmp (criteria+1,"LL")) f = tenex_search_all;
  789.     else if (!strcmp (criteria+1,"NSWERED")) f = tenex_search_answered;
  790.     break;
  791.       case 'B':            /* possible BCC, BEFORE, BODY */
  792.     if (!strcmp (criteria+1,"CC"))
  793.       f = tenex_search_string (tenex_search_bcc,&d,&n);
  794.     else if (!strcmp (criteria+1,"EFORE"))
  795.       f = tenex_search_date (tenex_search_before,&n);
  796.     else if (!strcmp (criteria+1,"ODY"))
  797.       f = tenex_search_string (tenex_search_body,&d,&n);
  798.     break;
  799.       case 'C':            /* possible CC */
  800.     if (!strcmp (criteria+1,"C")) 
  801.       f = tenex_search_string (tenex_search_cc,&d,&n);
  802.     break;
  803.       case 'D':            /* possible DELETED */
  804.     if (!strcmp (criteria+1,"ELETED")) f = tenex_search_deleted;
  805.     break;
  806.       case 'F':            /* possible FLAGGED, FROM */
  807.     if (!strcmp (criteria+1,"LAGGED")) f = tenex_search_flagged;
  808.     else if (!strcmp (criteria+1,"ROM"))
  809.       f = tenex_search_string (tenex_search_from,&d,&n);
  810.     break;
  811.       case 'K':            /* possible KEYWORD */
  812.     if (!strcmp (criteria+1,"EYWORD"))
  813.       f = tenex_search_flag (tenex_search_keyword,&n,stream);
  814.     break;
  815.       case 'N':            /* possible NEW */
  816.     if (!strcmp (criteria+1,"EW")) f = tenex_search_new;
  817.     break;
  818.  
  819.       case 'O':            /* possible OLD, ON */
  820.     if (!strcmp (criteria+1,"LD")) f = tenex_search_old;
  821.     else if (!strcmp (criteria+1,"N"))
  822.       f = tenex_search_date (tenex_search_on,&n);
  823.     break;
  824.       case 'R':            /* possible RECENT */
  825.     if (!strcmp (criteria+1,"ECENT")) f = tenex_search_recent;
  826.     break;
  827.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  828.     if (!strcmp (criteria+1,"EEN")) f = tenex_search_seen;
  829.     else if (!strcmp (criteria+1,"INCE"))
  830.       f = tenex_search_date (tenex_search_since,&n);
  831.     else if (!strcmp (criteria+1,"UBJECT"))
  832.       f = tenex_search_string (tenex_search_subject,&d,&n);
  833.     break;
  834.       case 'T':            /* possible TEXT, TO */
  835.     if (!strcmp (criteria+1,"EXT"))
  836.       f = tenex_search_string (tenex_search_text,&d,&n);
  837.     else if (!strcmp (criteria+1,"O"))
  838.       f = tenex_search_string (tenex_search_to,&d,&n);
  839.     break;
  840.       case 'U':            /* possible UN* */
  841.     if (criteria[1] == 'N') {
  842.       if (!strcmp (criteria+2,"ANSWERED")) f = tenex_search_unanswered;
  843.       else if (!strcmp (criteria+2,"DELETED")) f = tenex_search_undeleted;
  844.       else if (!strcmp (criteria+2,"FLAGGED")) f = tenex_search_unflagged;
  845.       else if (!strcmp (criteria+2,"KEYWORD"))
  846.         f = tenex_search_flag (tenex_search_unkeyword,&n,stream);
  847.       else if (!strcmp (criteria+2,"SEEN")) f = tenex_search_unseen;
  848.     }
  849.     break;
  850.       default:            /* we will barf below */
  851.     break;
  852.       }
  853.       if (!f) {            /* if can't determine any criteria */
  854.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  855.     mm_log (LOCAL->buf,ERROR);
  856.     return;
  857.       }
  858.                 /* run the search criterion */
  859.       for (i = 1; i <= stream->nmsgs; ++i)
  860.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  861.       mail_elt (stream,i)->searched = NIL;
  862.     }
  863.                 /* report search results to main program */
  864.     for (i = 1; i <= stream->nmsgs; ++i)
  865.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  866.   }
  867. }
  868.  
  869. /* Tenex mail ping mailbox
  870.  * Accepts: MAIL stream
  871.  * Returns: T if stream still alive, NIL if not
  872.  */
  873.  
  874. long tenex_ping (MAILSTREAM *stream)
  875. {
  876.   long r = NIL;
  877.   int ld;
  878.   char lock[MAILTMPLEN];
  879.   if (stream && LOCAL) {    /* only if stream already open */
  880.                 /* get shared parse/append permission */
  881.     if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  882.                 /* parse resulting mailbox */
  883.       r = (tenex_parse (stream)) ? T : NIL;
  884.       tenex_unlock (ld,lock);    /* release shared parse/append permission */
  885.     }
  886.                 /* snarf if this is a read-write inbox */
  887.     if (stream && LOCAL && LOCAL->inbox && !stream->readonly) {
  888.       tenex_snarf (stream);
  889.                 /* get shared parse/append permission */
  890.       if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  891.                 /* parse resulting mailbox */
  892.     r = (tenex_parse (stream)) ? T : NIL;
  893.     tenex_unlock (ld,lock);    /* release shared parse/append permission */
  894.       }
  895.     }
  896.   }
  897.   return r;            /* return result of the parse */
  898. }
  899.  
  900.  
  901. /* Tenex mail check mailbox (reparses status too)
  902.  * Accepts: MAIL stream
  903.  */
  904.  
  905. void tenex_check (MAILSTREAM *stream)
  906. {
  907.   long i = 1;
  908.   if (tenex_ping (stream)) {    /* ping mailbox */
  909.                 /* get new message status */
  910.     while (i <= stream->nmsgs) tenex_elt (stream,i++);
  911.     mm_log ("Check completed",(long) NIL);
  912.   }
  913. }
  914.  
  915. /* Tenex mail snarf messages from bezerk inbox
  916.  * Accepts: MAIL stream
  917.  */
  918.  
  919. void tenex_snarf (MAILSTREAM *stream)
  920. {
  921.   long i = 0;
  922.   long r,j;
  923.   struct stat sbuf;
  924.   struct iovec iov[2];
  925.   char lock[MAILTMPLEN];
  926.   MAILSTREAM *bezerk = NIL;
  927.   int ld = tenex_lock (LOCAL->fd,lock,LOCK_EX);
  928.   if (ld < 0) return;        /* give up if can't get exclusive permission */
  929.   mm_critical (stream);        /* go critical */
  930.                 /* calculate name of bezerk file */
  931.   sprintf (LOCAL->buf,MAILFILE,myusername ());
  932.   stat (LOCAL->buf,&sbuf);    /* see if anything there */
  933.   if (sbuf.st_size) {        /* non-empty? */
  934.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  935.     if ((sbuf.st_size == LOCAL->filesize) && 
  936.     (bezerk = mail_open (bezerk,LOCAL->buf,OP_SILENT)) &&
  937.     (r = bezerk->nmsgs)) {    /* sizes match and can get bezerk mailbox? */
  938.                 /* yes, go to end of file in our mailbox */
  939.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  940.                 /* for each message in bezerk mailbox */
  941.       while (r && (++i <= bezerk->nmsgs)) {
  942.                 /* snarf message from Berkeley mailbox */
  943.     iov[1].iov_base = bezerk_snarf (bezerk,i,&j);
  944.                 /* calculate header line */
  945.     mail_date ((iov[0].iov_base = LOCAL->buf),mail_elt (bezerk,i));
  946.     sprintf (LOCAL->buf + strlen (LOCAL->buf),",%d;000000000000\n",
  947.          iov[1].iov_len = j);
  948.     iov[0].iov_len = strlen (iov[0].iov_base);
  949.                 /* copy message to new mailbox */
  950.     if (writev (LOCAL->fd,iov,2) < 0) {
  951.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  952.       mm_log (LOCAL->buf,ERROR);
  953.       ftruncate (LOCAL->fd,sbuf.st_size);
  954.       r = 0;        /* flag that we have lost big */
  955.     }
  956.       }
  957.       if (r) {            /* delete all the messages we copied */
  958.     for (i = 1; i <= r; i++) mail_elt (bezerk,i)->deleted = T;
  959.     mail_expunge (bezerk);/* now expunge all those messages */
  960.       }
  961.     }
  962.     if (bezerk) mail_close (bezerk);
  963.   }
  964.   mm_nocritical (stream);    /* release critical */
  965.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  966. }
  967.  
  968. /* Tenex mail expunge mailbox
  969.  * Accepts: MAIL stream
  970.  */
  971.  
  972. void tenex_expunge (MAILSTREAM *stream)
  973. {
  974.   int ld;
  975.   unsigned long i = 1;
  976.   unsigned long j,k,m,recent;
  977.   unsigned long n = 0;
  978.   unsigned long delta = 0;
  979.   char lock[MAILTMPLEN];
  980.   MESSAGECACHE *elt;
  981.                 /* do nothing if stream dead */
  982.   if (!tenex_ping (stream)) return;
  983.   if (stream->readonly) {    /* won't do on readonly files! */
  984.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  985.     return;
  986.   }
  987.   /* The cretins who designed flock() created a window of vulnerability in
  988.    * upgrading locks from shared to exclusive or downgrading from exclusive
  989.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  990.    * flock() actually *releases* the former lock.  Obviously they never talked
  991.    * to any database guys.  Fortunately, we have the parse/append permission
  992.    * lock.  If we require this lock before going exclusive on the mailbox,
  993.    * another process can not sneak in and steal the exclusive mailbox lock on
  994.    * us, because it will block on trying to get parse/append permission first.
  995.    */
  996.                 /* get exclusive parse/append permission */
  997.   if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  998.     mm_log ("Unable to lock expunge mailbox",ERROR);
  999.     return;
  1000.   }
  1001.                 /* get exclusive access */
  1002.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  1003.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  1004.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  1005.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1006.     return;
  1007.   }
  1008.  
  1009.   mm_critical (stream);        /* go critical */
  1010.   recent = stream->recent;    /* get recent now that pinged and locked */
  1011.   while (i <= stream->nmsgs) {    /* for each message */
  1012.                 /* number of bytes to smash or preserve */
  1013.     k = ((elt = tenex_elt (stream,i))->data2 >> 24) + tenex_size (stream,i);
  1014.     if (elt->deleted) {        /* if deleted */
  1015.       if (elt->recent) --recent;/* if recent, note one less recent message */
  1016.       delta += k;        /* number of bytes to delete */
  1017.       mail_expunged (stream,i);    /* notify upper levels */
  1018.       n++;            /* count up one more expunged message */
  1019.     }
  1020.     else if (i++ && delta) {    /* preserved message */
  1021.       j = elt->data1;        /* first byte to preserve */
  1022.       do {            /* read from source position */
  1023.     m = min (k,LOCAL->buflen);
  1024.     lseek (LOCAL->fd,j,L_SET);
  1025.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1026.                 /* write to destination position */
  1027.     lseek (LOCAL->fd,j - delta,L_SET);
  1028.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1029.     j += m;            /* next chunk, perhaps */
  1030.       } while (k -= m);        /* until done */
  1031.       elt->data1 -= delta;    /* note the new address of this text */
  1032.     }
  1033.   }
  1034.   if (n) {            /* truncate file after last message */
  1035.     ftruncate (LOCAL->fd,LOCAL->filesize -= delta);
  1036.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1037.                 /* output the news */
  1038.     mm_log (LOCAL->buf,(long) NIL);
  1039.   }
  1040.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1041.   mm_nocritical (stream);    /* release critical */
  1042.                 /* notify upper level of new mailbox size */
  1043.   mail_exists (stream,stream->nmsgs);
  1044.   mail_recent (stream,recent);
  1045.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1046.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1047. }
  1048.  
  1049. /* Tenex mail copy message(s)
  1050.  * Accepts: MAIL stream
  1051.  *        sequence
  1052.  *        destination mailbox
  1053.  * Returns: T if success, NIL if failed
  1054.  */
  1055.  
  1056. long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  1057. {
  1058.                 /* copy the messages */
  1059.   return (mail_sequence (stream,sequence)) ?
  1060.     tenex_copy_messages (stream,mailbox) : NIL;
  1061. }
  1062.  
  1063.  
  1064. /* Tenex mail move message(s)
  1065.  * Accepts: MAIL stream
  1066.  *        sequence
  1067.  *        destination mailbox
  1068.  * Returns: T if success, NIL if failed
  1069.  */
  1070.  
  1071. long tenex_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1072. {
  1073.   long i;
  1074.   MESSAGECACHE *elt;
  1075.   if (!(mail_sequence (stream,sequence) &&
  1076.     tenex_copy_messages (stream,mailbox))) return NIL;
  1077.                 /* delete all requested messages */
  1078.   for (i = 1; i <= stream->nmsgs; i++)
  1079.     if ((elt = tenex_elt (stream,i))->sequence) {
  1080.       elt->deleted = T;        /* mark message deleted */
  1081.                 /* recalculate status */
  1082.       tenex_update_status (stream,i,NIL);
  1083.     }
  1084.                 /* make sure the update takes */
  1085.   if (!stream->readonly) fsync (LOCAL->fd);
  1086.   return LONGT;
  1087. }
  1088.  
  1089. /* Tenex mail append message from stringstruct
  1090.  * Accepts: MAIL stream
  1091.  *        destination mailbox
  1092.  *        stringstruct of messages to append
  1093.  * Returns: T if append successful, else NIL
  1094.  */
  1095.  
  1096. long tenex_append (MAILSTREAM *stream,char *mailbox,STRING *message)
  1097. {
  1098.   struct stat sbuf;
  1099.   char c,*s,*t,tmp[MAILTMPLEN],lock[MAILTMPLEN];
  1100.   int fd,ld;
  1101.   long i = SIZE (message);
  1102.   long size = 0;
  1103.   long ret = LONGT;
  1104.                 /* N.B.: can't use LOCAL->buf for tmp */
  1105.                 /* make sure valid mailbox */
  1106.   if (!tenex_isvalid (mailbox,tmp)) {
  1107.     sprintf (tmp,"Not a Tenex-format mailbox: %s",mailbox);
  1108.     mm_log (tmp,ERROR);
  1109.     return NIL;
  1110.   }
  1111.   if ((fd = open (tenex_file (tmp,mailbox),O_RDWR|O_CREAT,
  1112.           S_IREAD|S_IWRITE)) < 0) {
  1113.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1114.     mm_log (tmp,ERROR);
  1115.     return NIL;
  1116.   }
  1117.                 /* get exclusive parse/append permission */
  1118.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1119.     mm_log ("Unable to lock append mailbox",ERROR);
  1120.     return NIL;
  1121.   }
  1122.   s = fs_get (i + 1);        /* get space for the data */
  1123.                 /* copy the data w/o CR's */
  1124.   while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
  1125.  
  1126.   mm_critical (stream);        /* go critical */
  1127.   fstat (fd,&sbuf);        /* get current file size */
  1128.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1129.   rfc822_date (tmp);        /* get the date in RFC822 format */
  1130.   tmp[4] = '0';            /* in case 1-digit day */
  1131.   t = tmp[7] == ' ' ? tmp+5 : tmp+4;
  1132.   t[2] = t[6] = '-';        /* convert date string to Tenex format */
  1133.                 /* add remainder of header */
  1134.   sprintf (t+26,",%ld;000000000000\n",size);
  1135.                 /* write header */
  1136.   if ((write (fd,t,strlen (t)) < 0) || ((write (fd,s,size)) < 0)) {
  1137.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1138.     mm_log (tmp,ERROR);
  1139.     ftruncate (fd,sbuf.st_size);
  1140.     ret = NIL;
  1141.   }
  1142.   fsync (fd);            /* force out the update */
  1143.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1144.   close (fd);            /* close the file */
  1145.   mm_nocritical (stream);    /* release critical */
  1146.   fs_give ((void **) &s);    /* flush the buffer */
  1147.   return ret;
  1148. }
  1149.  
  1150. /* Tenex garbage collect stream
  1151.  * Accepts: Mail stream
  1152.  *        garbage collection flags
  1153.  */
  1154.  
  1155. void tenex_gc (MAILSTREAM *stream,long gcflags)
  1156. {
  1157.   /* nothing here for now */
  1158. }
  1159.  
  1160. /* Internal routines */
  1161.  
  1162.  
  1163. /* Tenex mail lock file for parse/append permission
  1164.  * Accepts: file descriptor
  1165.  *        lock file name buffer
  1166.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1167.  * Returns: file descriptor of lock or -1 if failure
  1168.  */
  1169.  
  1170. int tenex_lock (int fd,char *lock,int op)
  1171. {
  1172.   int ld;
  1173.   struct stat sbuf;
  1174.                 /* get data for this file */
  1175.   if (fstat (fd,&sbuf)) return -1;
  1176.                 /* make temporary file name */
  1177.   sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1178.   if ((ld = open (lock,O_RDWR|O_CREAT,0666)) < 0) return NIL;
  1179.   flock (ld,op);        /* get this lock */
  1180.   return ld;            /* return locking file descriptor */
  1181. }
  1182.  
  1183.  
  1184. /* Tenex mail unlock file for parse/append permission
  1185.  * Accepts: file descriptor
  1186.  *        lock file name from tenex_lock()
  1187.  */
  1188.  
  1189. void tenex_unlock (int fd,char *lock)
  1190. {
  1191.   unlink (lock);        /* delete the file */
  1192.   flock (fd,LOCK_UN);        /* unlock it */
  1193.   close (fd);            /* close it */
  1194. }
  1195.  
  1196. /* Tenex mail return internal message size in bytes
  1197.  * Accepts: MAIL stream
  1198.  *        message #
  1199.  * Returns: internal size of message
  1200.  */
  1201.  
  1202. unsigned long tenex_size (MAILSTREAM *stream,long m)
  1203. {
  1204.   MESSAGECACHE *elt = mail_elt (stream,m);
  1205.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1206.     - (elt->data1 + (elt->data2 >> 24));
  1207. }
  1208.  
  1209.  
  1210. /* Tenex mail generate file string
  1211.  * Accepts: temporary buffer to write into
  1212.  *        mailbox name string
  1213.  * Returns: local file string
  1214.  */
  1215.  
  1216. char *tenex_file (char *dst,char *name)
  1217. {
  1218.   struct passwd *pw;
  1219.   char *s,*t,tmp[MAILTMPLEN];
  1220.   switch (*name) {
  1221.   case '*':            /* bboard? */
  1222.     sprintf (tmp,"~ftp/%s",(name[1] == '/') ? name+2 : name+1);
  1223.     dst = tenex_file (dst,tmp);/* recurse to get result */
  1224.     break;
  1225.   case '/':            /* absolute file path */
  1226.     strcpy (dst,name);        /* copy the mailbox name */
  1227.     break;
  1228.   case '~':            /* home directory */
  1229.     if (name[1] == '/') t = myhomedir ();
  1230.     else {
  1231.       strcpy (tmp,name + 1);    /* copy user name */
  1232.       if (s = strchr (tmp,'/')) *s = '\0';
  1233.       t = ((pw = getpwnam (tmp)) && pw->pw_dir) ? pw->pw_dir : "/NOSUCHUSER";
  1234.     }
  1235.     sprintf (dst,"%s%s",t,(s = strchr (name,'/')) ? s : "");
  1236.     break;
  1237.   default:            /* other name - INBOX becomes mail.txt */
  1238.     if (!strcmp (ucase (strcpy (dst,name)),"INBOX")) name = "mail.txt";
  1239.     sprintf (dst,"%s/%s",myhomedir (),name);
  1240.   }
  1241.   return dst;
  1242. }
  1243.  
  1244. /* Parse flag list
  1245.  * Accepts: MAIL stream
  1246.  *        flag list as a character string
  1247.  *        pointer to user flags to return
  1248.  * Returns: system flags
  1249.  */
  1250.  
  1251. long tenex_getflags (MAILSTREAM *stream,char *flag,long *uf)
  1252. {
  1253.   char key[MAILTMPLEN];
  1254.   char *t,*s;
  1255.   short f = 0;
  1256.   long i;
  1257.   short j;
  1258.   *uf = 0;            /* initially no user flags */
  1259.   if (flag && *flag) {        /* no-op if no flag string */
  1260.                 /* check if a list and make sure valid */
  1261.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1262.       mm_log ("Bad flag list",ERROR);
  1263.       return NIL;
  1264.     }
  1265.                 /* copy the flag string w/o list construct */
  1266.     strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
  1267.     LOCAL->buf[j] = '\0';    /* tie off tail */
  1268.                 /* make uppercase, find first, parse */
  1269.     if (t = strtok (ucase (LOCAL->buf)," ")) do {
  1270.       i = 0;            /* no flag yet */
  1271.                 /* system flag, dispatch on first character */
  1272.       if (*t == '\\') switch (*++t) {
  1273.       case 'S':            /* possible \Seen flag */
  1274.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1275.       f |= i = fSEEN;
  1276.     break;
  1277.       case 'D':            /* possible \Deleted flag */
  1278.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1279.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1280.     break;
  1281.       case 'F':            /* possible \Flagged flag */
  1282.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1283.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1284.     break;
  1285.       case 'A':            /* possible \Answered flag */
  1286.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1287.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1288.       f |= i = fANSWERED;
  1289.     break;
  1290.       default:            /* unknown */
  1291.     break;
  1292.       }
  1293.  
  1294.                 /* user flag, search through table */
  1295.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1296.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1297.       if (!i) {            /* didn't find a matching flag? */
  1298.     sprintf (key,"Unknown flag: %.80s",t);
  1299.     mm_log (key,ERROR);
  1300.     *uf = NIL;        /* be sure no user flags returned */
  1301.     return NIL;        /* return no system flags */
  1302.       }
  1303.                 /* parse next flag */
  1304.     } while (t = strtok (NIL," "));
  1305.   }
  1306.   return f;
  1307. }
  1308.  
  1309. /* Tenex mail parse mailbox
  1310.  * Accepts: MAIL stream
  1311.  * Returns: T if parse OK
  1312.  *        NIL if failure, stream aborted
  1313.  */
  1314.  
  1315. long tenex_parse (MAILSTREAM *stream)
  1316. {
  1317.   struct stat sbuf;
  1318.   MESSAGECACHE *elt = NIL;
  1319.   char *s,*t;
  1320.   char tmp[MAILTMPLEN];
  1321.   long i,j,msiz;
  1322.   long curpos = LOCAL->filesize;
  1323.   long nmsgs = stream->nmsgs;
  1324.   long recent = stream->recent;
  1325.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1326.   if (sbuf.st_size < curpos) {    /* sanity check */
  1327.     mm_log ("Mailbox shrank!",ERROR);
  1328.     tenex_close (stream);
  1329.     return NIL;
  1330.   }
  1331.                 /* while there is stuff to parse */
  1332.   while (i = sbuf.st_size - curpos) {
  1333.                 /* get to that position in the file */
  1334.     lseek (LOCAL->fd,curpos,L_SET);
  1335.     if (!((read (LOCAL->fd,tmp,64) > 0) && (s = strchr (tmp,'\012')))) {
  1336.       sprintf (tmp,"Unable to read internal header line at %ld",curpos);
  1337.       mm_log (tmp,ERROR);
  1338.       tenex_close (stream);
  1339.       return NIL;
  1340.     }
  1341.     *s = '\0';            /* tie off header line */
  1342.     i = (s + 1) - tmp;        /* note start of text offset */
  1343.     if (!((s = strchr (tmp,',')) && (t = strchr (s+1,';')))) {
  1344.       sprintf (tmp,"Unable to parse internal header line at %ld",curpos);
  1345.       mm_log (tmp,ERROR);
  1346.       tenex_close (stream);
  1347.       return NIL;
  1348.     }
  1349.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1350.                 /* intantiate an elt for this message */
  1351.     elt = mail_elt (stream,++nmsgs);
  1352.     elt->data1 = curpos;    /* note file offset of header */
  1353.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1354.                 /* parse the header components */
  1355.     if (!(mail_parse_date (elt,tmp) &&
  1356.       (elt->rfc822_size = msiz = strtol (s,&s,10)) && (!(s && *s)) &&
  1357.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1358.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1359.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1360.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1361.       sprintf (tmp,"Unable to parse internal header line components at %ld",
  1362.            curpos);
  1363.       mm_log (tmp,ERROR);
  1364.       tenex_close (stream);
  1365.       return NIL;
  1366.     }
  1367.  
  1368.                 /* calculate system flags */
  1369.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1370.     if (j & fDELETED) elt->deleted = T;
  1371.     if (j & fFLAGGED) elt->flagged = T;
  1372.     if (j & fANSWERED) elt->answered = T;
  1373.     if (!(j & fOLD)) {        /* newly arrived message? */
  1374.       elt->recent = T;
  1375.       recent++;            /* count up a new recent message */
  1376.                 /* mark it as old */
  1377.       tenex_update_status (stream,nmsgs,NIL);
  1378.     }
  1379.                 /* start at first message byte */
  1380.     lseek (LOCAL->fd,curpos += i,L_SET);
  1381.     for (i = msiz; i;) {    /* for all bytes of the message */
  1382.                 /* read a buffer's worth */
  1383.       read (LOCAL->fd,tmp,j = min (i,(long) MAILTMPLEN));
  1384.       i -= j;            /* account for having read that much */
  1385.                 /* now count the CRLFs */
  1386.       while (j) if (tmp[--j] == '\n') elt->rfc822_size++;
  1387.     }
  1388.                 /* make sure didn't run off end of file */
  1389.     if ((curpos += msiz) > sbuf.st_size) {
  1390.       mm_log ("Last message runs past end of file",ERROR);
  1391.       tenex_close (stream);
  1392.       return NIL;
  1393.     }
  1394.   }
  1395.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1396.                 /* update parsed file size */
  1397.   LOCAL->filesize = sbuf.st_size;
  1398.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1399.   mail_recent (stream,recent);    /* and of change in recent messages */
  1400.   return LONGT;            /* return the winnage */
  1401. }
  1402.  
  1403. /* Tenex copy messages
  1404.  * Accepts: MAIL stream
  1405.  *        mailbox copy vector
  1406.  *        mailbox name
  1407.  * Returns: T if success, NIL if failed
  1408.  */
  1409.  
  1410. long tenex_copy_messages (MAILSTREAM *stream,char *mailbox)
  1411. {
  1412.   struct stat sbuf;
  1413.   MESSAGECACHE *elt;
  1414.   unsigned long i,j,k;
  1415.   int fd,ld;
  1416.   char lock[MAILTMPLEN];
  1417.                 /* make sure valid mailbox */
  1418.   if (!tenex_isvalid (mailbox,LOCAL->buf)) {
  1419.     sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %s",mailbox);
  1420.     mm_log (LOCAL->buf,ERROR);
  1421.     return NIL;
  1422.   }
  1423.                 /* got file? */  
  1424.   if ((fd = open (tenex_file (LOCAL->buf,mailbox),O_RDWR|O_CREAT,
  1425.           S_IREAD|S_IWRITE)) < 0) {
  1426.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1427.     mm_log (LOCAL->buf,ERROR);
  1428.     return NIL;
  1429.   }
  1430.   mm_critical (stream);        /* go critical */
  1431.                 /* get exclusive parse/append permission */
  1432.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1433.     mm_log ("Unable to lock copy mailbox",ERROR);
  1434.     return NIL;
  1435.   }
  1436.   fstat (fd,&sbuf);        /* get current file size */
  1437.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1438.  
  1439.                 /* for each requested message */
  1440.   for (i = 1; i <= stream->nmsgs; i++)
  1441.     if ((elt = mail_elt (stream,i))->sequence) {
  1442.       lseek (LOCAL->fd,tenex_header (stream,i,&k),L_SET);
  1443.       j = 0;            /* mark first time through */
  1444.       do {            /* read from source position */
  1445.     if (j) {        /* get another message chunk */
  1446.       k = min (j,LOCAL->buflen);
  1447.       read (LOCAL->fd,LOCAL->buf,(unsigned int) k);
  1448.     }
  1449.     else {            /* first time through */
  1450.                 /* make a header */
  1451.       mail_date (LOCAL->buf,elt);
  1452.       sprintf (LOCAL->buf + strlen (LOCAL->buf),",%d;000000000000\012",
  1453.            j = tenex_size (stream,i));
  1454.                 /* add size of header string */
  1455.       j += (k = strlen (LOCAL->buf));
  1456.     }
  1457.     if (write (fd,LOCAL->buf,(unsigned int) k) < 0) {
  1458.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1459.       mm_log (LOCAL->buf,ERROR);
  1460.       ftruncate (fd,sbuf.st_size);
  1461.                 /* release exclusive parse/append permission */
  1462.       tenex_unlock (ld,lock);
  1463.       close (fd);        /* punt */
  1464.       mm_nocritical (stream);
  1465.       return NIL;
  1466.     }
  1467.       } while (j -= k);        /* until done */
  1468.     }
  1469.   fsync (fd);            /* force out the update */
  1470.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1471.   close (fd);            /* close the file */
  1472.   mm_nocritical (stream);    /* release critical */
  1473.   return LONGT;
  1474. }
  1475.  
  1476. /* Tenex get cache element with status updating from file
  1477.  * Accepts: MAIL stream
  1478.  *        message number
  1479.  * Returns: cache element
  1480.  */
  1481.  
  1482. MESSAGECACHE *tenex_elt (MAILSTREAM *stream,long msgno)
  1483. {
  1484.   unsigned long i,j;
  1485.   char c;
  1486.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1487.                 /* set the seek pointer */
  1488.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1489.                 /* read the new flags */
  1490.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1491.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1492.     fatal (LOCAL->buf);
  1493.   }
  1494.                 /* calculate system flags */
  1495.   elt->seen = (i = ((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') & fSEEN ?
  1496.     T : NIL;
  1497.   elt->deleted = i & fDELETED ? T : NIL;
  1498.   elt->flagged = i & fFLAGGED ? T : NIL;
  1499.   elt->answered = i & fANSWERED ? T : NIL;
  1500.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1501.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1502.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1503.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1504.                 /* set up all valid user flags (reversed!) */
  1505.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1506.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1507.   return elt;
  1508. }
  1509.  
  1510. /* Tenex update status string
  1511.  * Accepts: MAIL stream
  1512.  *        message number
  1513.  *        flag saying whether or not to sync
  1514.  */
  1515.  
  1516. void tenex_update_status (MAILSTREAM *stream,long msgno,long syncflag)
  1517. {
  1518.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1519.   unsigned long j,k = 0;
  1520.   if (!stream->readonly) {    /* not if readonly you don't */
  1521.     j = elt->user_flags;    /* get user flags */
  1522.                 /* reverse bits (dontcha wish we had CIRC?) */
  1523.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1524.                 /* print new flag string */
  1525.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1526.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1527.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1528.                 /* get to that place in the file */
  1529.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1530.                 /* write new flags */
  1531.     write (LOCAL->fd,LOCAL->buf,12);
  1532.                 /* sync if requested */
  1533.     if (syncflag) fsync (LOCAL->fd);
  1534.   }
  1535. }
  1536.  
  1537. /* Search support routines
  1538.  * Accepts: MAIL stream
  1539.  *        message number
  1540.  *        pointer to additional data
  1541.  * Returns: T if search matches, else NIL
  1542.  */
  1543.  
  1544.  
  1545. char tenex_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1546. {
  1547.   return T;            /* ALL always succeeds */
  1548. }
  1549.  
  1550.  
  1551. char tenex_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1552. {
  1553.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1554. }
  1555.  
  1556.  
  1557. char tenex_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1558. {
  1559.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1560. }
  1561.  
  1562.  
  1563. char tenex_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1564. {
  1565.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1566. }
  1567.  
  1568.  
  1569. char tenex_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1570. {
  1571.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1572. }
  1573.  
  1574.  
  1575. char tenex_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1576. {
  1577.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1578.   return (elt->recent && !elt->seen) ? T : NIL;
  1579. }
  1580.  
  1581. char tenex_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1582. {
  1583.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1584. }
  1585.  
  1586.  
  1587. char tenex_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1588. {
  1589.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1590. }
  1591.  
  1592.  
  1593. char tenex_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1594. {
  1595.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1596. }
  1597.  
  1598.  
  1599. char tenex_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1600. {
  1601.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1602. }
  1603.  
  1604.  
  1605. char tenex_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1606. {
  1607.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1608. }
  1609.  
  1610.  
  1611. char tenex_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1612. {
  1613.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1614. }
  1615.  
  1616.  
  1617. char tenex_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1618. {
  1619.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1620. }
  1621.  
  1622.  
  1623. char tenex_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1624. {
  1625.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1626. }
  1627.  
  1628. char tenex_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1629. {
  1630.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1631.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1632. }
  1633.  
  1634.  
  1635. char tenex_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1636. {
  1637.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1638.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1639. }
  1640.  
  1641.  
  1642. char tenex_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1643. {
  1644.                 /* everybody interprets "since" as .GE. */
  1645.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1646.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1647. }
  1648.  
  1649.  
  1650. char tenex_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1651. {
  1652.   char ret;
  1653.   unsigned long hdrsize;
  1654.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  1655.   unsigned long textsize = tenex_size (stream,msgno) - hdrsize;
  1656.   char *s = (char *) fs_get (1 + textsize);
  1657.   s[textsize] = '\0';        /* tie off string */
  1658.                 /* recalculate status */
  1659.   tenex_update_status (stream,msgno,T);
  1660.                 /* get to text position */
  1661.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  1662.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  1663.                 /* copy the string */
  1664.   ret = search (s,textsize,d,n);/* do the search */
  1665.   fs_give ((void **) &s);    /* flush readin buffer */
  1666.   return ret;            /* return search value */
  1667. }
  1668.  
  1669.  
  1670. char tenex_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1671. {
  1672.   char *s = tenex_fetchstructure (stream,msgno,NIL)->subject;
  1673.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1674. }
  1675.  
  1676.  
  1677. char tenex_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1678. {
  1679.   char ret;
  1680.   unsigned long hdrsize;
  1681.   unsigned long hdrpos = tenex_header (stream,msgno,&hdrsize);
  1682.   char *s = (char *) fs_get (1 + hdrsize);
  1683.   s[hdrsize] = '\0';        /* tie off string */
  1684.                 /* get to header position */
  1685.   lseek (LOCAL->fd,hdrpos,L_SET);
  1686.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  1687.   ret = search (s,hdrsize,d,n) || tenex_search_body (stream,msgno,d,n);
  1688.   fs_give ((void **) &s);    /* flush readin buffer */
  1689.   return ret;            /* return search value */
  1690. }
  1691.  
  1692. char tenex_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1693. {
  1694.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1695.                 /* get text for address */
  1696.   rfc822_write_address (LOCAL->buf,
  1697.             tenex_fetchstructure (stream,msgno,NIL)->bcc);
  1698.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1699. }
  1700.  
  1701.  
  1702. char tenex_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1703. {
  1704.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1705.                 /* get text for address */
  1706.   rfc822_write_address (LOCAL->buf,
  1707.             tenex_fetchstructure (stream,msgno,NIL)->cc);
  1708.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1709. }
  1710.  
  1711.  
  1712. char tenex_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1713. {
  1714.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1715.                 /* get text for address */
  1716.   rfc822_write_address (LOCAL->buf,
  1717.             tenex_fetchstructure (stream,msgno,NIL)->from);
  1718.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1719. }
  1720.  
  1721.  
  1722. char tenex_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1723. {
  1724.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1725.                 /* get text for address */
  1726.   rfc822_write_address (LOCAL->buf,
  1727.             tenex_fetchstructure (stream,msgno,NIL)->to);
  1728.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1729. }
  1730.  
  1731. /* Search parsers */
  1732.  
  1733.  
  1734. /* Parse a date
  1735.  * Accepts: function to return
  1736.  *        pointer to date integer to return
  1737.  * Returns: function to return
  1738.  */
  1739.  
  1740. search_t tenex_search_date (search_t f,long *n)
  1741. {
  1742.   long i;
  1743.   char *s;
  1744.   MESSAGECACHE elt;
  1745.                 /* parse the date and return fn if OK */
  1746.   return (tenex_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1747.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1748. }
  1749.  
  1750. /* Parse a flag
  1751.  * Accepts: function to return
  1752.  *        pointer to keyword integer to return
  1753.  *        MAIL stream
  1754.  * Returns: function to return
  1755.  */
  1756.  
  1757. search_t tenex_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1758. {
  1759.   short i;
  1760.   char *s,*t;
  1761.   if (t = strtok (NIL," ")) {    /* get a keyword */
  1762.     ucase (t);            /* get uppercase form of flag */
  1763.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  1764.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  1765.   }
  1766.   return NIL;            /* couldn't find keyword */
  1767. }
  1768.  
  1769. /* Parse a string
  1770.  * Accepts: function to return
  1771.  *        pointer to string to return
  1772.  *        pointer to string length to return
  1773.  * Returns: function to return
  1774.  */
  1775.  
  1776.  
  1777. search_t tenex_search_string (search_t f,char **d,long *n)
  1778. {
  1779.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1780.   if (c) {            /* better be an argument */
  1781.     switch (*c) {        /* see what the argument is */
  1782.     case '\0':            /* catch bogons */
  1783.     case ' ':
  1784.       return NIL;
  1785.     case '"':            /* quoted string */
  1786.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  1787.     return NIL;
  1788.       break;
  1789.     case '{':            /* literal string */
  1790.       *n = strtol (c+1,&c,10);    /* get its length */
  1791.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  1792.       *n > strlen (*d = c)) return NIL;
  1793.       c[*n] = '\255';        /* write new delimiter */
  1794.       strtok (c,"\255");    /* reset the strtok mechanism */
  1795.       break;
  1796.     default:            /* atomic string */
  1797.       *n = strlen (*d = strtok (c," "));
  1798.       break;
  1799.     }
  1800.     return f;
  1801.   }
  1802.   else return NIL;
  1803. }
  1804.