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

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: IMAP4rev1 server
  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: 5 November 1990
  13.  * Last Edited: 16 November 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. /* Primary I/O calls */
  36. #define PBIN getchar /* primary byte input */
  37. /* primary string input */
  38. #define PSIN(s,n) fgets (s,n,stdin)
  39. #define PBOUT(c) putchar (c) /* primary byte output */
  40. /* primary string output */
  41. #define PSOUT(s) fputs (s,stdout)
  42. #define PFLUSH fflush (stdout) /* flush primary output */
  43. #define CRLF PSOUT ("1512") /* primary output terpri */
  44. /* server input wait */
  45. #define INWAIT(t) server_input_wait (t)
  46. /* Parameter files */
  47. #include "mail.h"
  48. #include "osdep.h"
  49. #include "rfc822.h"
  50. #include <stdio.h>
  51. #include <ctype.h>
  52. #include <errno.h>
  53. extern int errno; /* just in case */
  54. #include <sys/stat.h>
  55. #include <signal.h>
  56. #include "misc.h"
  57. /* Timeouts and timers */
  58. #define MINUTES *60
  59. #define LOGINTIMEOUT 3 MINUTES /* not logged in autologout timer */
  60. #define TIMEOUT 30 MINUTES /* RFC 2060 minimum autologout timer */
  61. #define ALERTTIMER 1 MINUTES /* alert check timer */
  62. #define IDLETIMER 1 MINUTES /* IDLE command poll timer */
  63. #define CHECKTIMER 15 MINUTES /* IDLE command last checkpoint timer */
  64. #define LITSTKLEN 20 /* length of literal stack */
  65. #define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size */
  66. #define TMPLEN 8192 /* size of temporary buffers */
  67. /* Server states */
  68. #define LOGIN 0
  69. #define SELECT 1
  70. #define OPEN 2
  71. #define LOGOUT 3
  72. /* Body text fetching */
  73. typedef struct text_args {
  74.   char *section; /* body section */
  75.   STRINGLIST *lines; /* header lines */
  76.   unsigned long first; /* first octet to fetch */
  77.   unsigned long last; /* number of octets to fetch */
  78.   long flags; /* flags */
  79. } TEXTARGS;
  80. /* Message pointer */
  81. typedef struct msg_data {
  82.   MAILSTREAM *stream; /* stream */
  83.   unsigned long msgno; /* message number */
  84. } MSGDATA;
  85. /* Function prototypes */
  86. int main (int argc,char *argv[]);
  87. void ping_mailbox (unsigned long uid);
  88. time_t palert (char *file,time_t oldtime);
  89. void msg_string_init (STRING *s,void *data,unsigned long size);
  90. char msg_string_next (STRING *s);
  91. void msg_string_setpos (STRING *s,unsigned long i);
  92. void new_flags (MAILSTREAM *stream);
  93. void clkint (void);
  94. void kodint (void);
  95. void hupint (void);
  96. void trmint (void);
  97. void slurp (char *s,int n);
  98. char inchar (void);
  99. char *flush (void);
  100. char *parse_astring (char **arg,unsigned long *i,char *del);
  101. char *snarf (char **arg);
  102. char *snarf_list (char **arg);
  103. STRINGLIST *parse_stringlist (char **s,int *list);
  104. long parse_criteria (SEARCHPGM *pgm,char **arg,unsigned long maxmsg,
  105.      unsigned long maxuid,unsigned long depth);
  106. long parse_criterion (SEARCHPGM *pgm,char **arg,unsigned long msgmsg,
  107.       unsigned long maxuid,unsigned long depth);
  108. long crit_date (unsigned short *date,char **arg);
  109. long crit_date_work (unsigned short *date,char **arg);
  110. long crit_set (SEARCHSET **set,char **arg,unsigned long maxima);
  111. long crit_number (unsigned long *number,char **arg);
  112. long crit_string (STRINGLIST **string,char **arg);
  113. void fetch (char *t,unsigned long uid);
  114. typedef void (*fetchfn_t) (unsigned long i,void *args);
  115. void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
  116. void fetch_bodystructure (unsigned long i,void *args);
  117. void fetch_body (unsigned long i,void *args);
  118. void fetch_body_part_mime (unsigned long i,void *args);
  119. void fetch_body_part_contents (unsigned long i,void *args);
  120. void fetch_body_part_header (unsigned long i,void *args);
  121. void fetch_body_part_text (unsigned long i,void *args);
  122. void remember (unsigned long uid,char *id,SIZEDTEXT *st);
  123. void fetch_envelope (unsigned long i,void *args);
  124. void fetch_encoding (unsigned long i,void *args);
  125. void changed_flags (unsigned long i,int f);
  126. void fetch_rfc822_header_lines (unsigned long i,void *args);
  127. void fetch_rfc822_header_lines_not (unsigned long i,void *args);
  128. void fetch_flags (unsigned long i,void *args);
  129. void put_flag (int *c,char *s);
  130. void fetch_internaldate (unsigned long i,void *args);
  131. void fetch_uid (unsigned long i,void *args);
  132. void fetch_rfc822 (unsigned long i,void *args);
  133. void fetch_rfc822_header (unsigned long i,void *args);
  134. void fetch_rfc822_size (unsigned long i,void *args);
  135. void fetch_rfc822_text (unsigned long i,void *args);
  136. void penv (ENVELOPE *env);
  137. void pbodystructure (BODY *body);
  138. void pbody (BODY *body);
  139. void pparam (PARAMETER *param);
  140. void paddr (ADDRESS *a);
  141. void pnum (unsigned long i);
  142. void pstring (char *s);
  143. void pnstring (char *label,SIZEDTEXT *st);
  144. void pastring (char *s);
  145. void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,TEXTARGS *ta);
  146. void pstringorlist (STRINGLIST *s);
  147. void pstringlist (STRINGLIST *s);
  148. void psizedtext (SIZEDTEXT *s);
  149. void ptext (SIZEDTEXT *s);
  150. void pthread (THREADNODE *thr);
  151. long nameok (char *ref,char *name);
  152. char *bboardname (char *cmd,char *name);
  153. char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
  154. long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
  155. void mm_list_work (char *what,int delimiter,char *name,long attributes);
  156. char *lasterror (void);
  157. /* Global storage */
  158. char *version = "12.264"; /* version number of this server */
  159. time_t alerttime = 0; /* time of last alert */
  160. time_t sysalerttime = 0; /* time of last system alert */
  161. time_t useralerttime = 0; /* time of last user alert */
  162. time_t lastcheck = 0; /* time of last checkpoint */
  163. int state = LOGIN; /* server state */
  164. int trycreate = 0; /* saw a trycreate */
  165. int finding = NIL; /* doing old FIND command */
  166. int anonymous = 0; /* non-zero if anonymous */
  167. int critical = NIL; /* non-zero if in critical code */
  168. int quell_events = NIL; /* non-zero if in FETCH response */
  169. int existsquelled = NIL; /* non-zero if an EXISTS was quelled */
  170. MAILSTREAM *stream = NIL; /* mailbox stream */
  171. DRIVER *curdriver = NIL; /* note current driver */
  172. MAILSTREAM *tstream = NIL; /* temporary mailbox stream */
  173. unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
  174. unsigned long recent = 0xffffffff;
  175. char *user = NIL; /* user name */
  176. char *pass = NIL; /* password */
  177. char cmdbuf[TMPLEN]; /* command buffer */
  178. char *tag; /* tag portion of command */
  179. char *cmd; /* command portion of command */
  180. char *arg; /* pointer to current argument of command */
  181. char *lstwrn = NIL; /* last warning message from c-client */
  182. char *lsterr = NIL; /* last error message from c-client */
  183. char *response = NIL; /* command response */
  184. int litsp = 0; /* literal stack pointer */
  185. char *litstk[LITSTKLEN]; /* stack to hold literals */
  186. unsigned long lastuid = 0; /* last fetched uid */
  187. char *lastid = NIL; /* last fetched body id for this message */
  188. SIZEDTEXT lastst = {NIL,0}; /* last sizedtext */
  189. /* Response texts which appear in multiple places */
  190. char *win = "%.80s OK %.80s completed1512";
  191. char *altwin = "%.80s OK %.900s1512";
  192. char *lose = "%.80s NO %.80s failed: %.900s1512";
  193. char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s1512";
  194. char *misarg = "%.80s BAD Missing required argument to %.80s1512";
  195. char *badarg = "%.80s BAD Argument given to %.80s when none expected1512";
  196. char *badseq = "%.80s BAD Bogus sequence in %.80s1512";
  197. char *badatt = "%.80s BAD Bogus attribute list in %.80s1512";
  198. char *argrdy = "+ Ready for argument1512";
  199. /* Message string driver for message stringstructs */
  200. STRINGDRIVER msg_string = {
  201.   msg_string_init, /* initialize string structure */
  202.   msg_string_next, /* get next byte in string structure */
  203.   msg_string_setpos /* set position in string structure */
  204. };
  205. /* Main program */
  206. int main (int argc,char *argv[])
  207. {
  208.   unsigned long i,j,k,m,uid;
  209.   long f;
  210.   char *s,*t,*u,*v,tmp[MAILTMPLEN];
  211.   struct stat sbuf;
  212.   time_t autologouttime = 0;
  213. #include "linkage.c"
  214. /* initialize server */
  215.   server_init (argv[0],"imap","imaps","imap",clkint,kodint,hupint,trmint);
  216. /* forbid automatic untagged expunge */
  217.   mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
  218. /* arm proxy copy callback */
  219.   mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
  220.   s = myusername_full (&i); /* get user name and flags */
  221.   switch (i) {
  222.   case MU_NOTLOGGEDIN:
  223.     t = "OK"; /* not logged in, ordinary startup */
  224.     break;
  225.   case MU_ANONYMOUS:
  226.     anonymous = T; /* anonymous user, fall into default */
  227.     s = "ANONYMOUS";
  228.   case MU_LOGGEDIN:
  229.     t = "PREAUTH"; /* already logged in, pre-authorized */
  230.     user = cpystr (s); /* copy user name */
  231.     pass = cpystr ("*"); /* set fake password */
  232.     state = SELECT; /* enter select state */
  233.     break;
  234.   default:
  235.     fatal ("Unknown state from myusername_full()");
  236.   }
  237.   PSOUT ("* ");
  238.   PSOUT (t);
  239.   PBOUT (' ');
  240.   PSOUT (tcp_serverhost ());
  241.   PSOUT (" IMAP4rev1 v");
  242.   PSOUT (version);
  243.   PSOUT (" server ready1512");
  244.   PFLUSH; /* dump output buffer */
  245.   switch (state) { /* do this after the banner */
  246.   case LOGIN:
  247.     autologouttime = time (0) + LOGINTIMEOUT;
  248.     break;
  249.   case SELECT:
  250.     syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
  251.     user,tcp_clienthost ());
  252.     break;
  253.   }
  254.   do { /* command processing loop */
  255.     slurp (cmdbuf,TMPLEN); /* slurp command */
  256. /* no more last error or literal */
  257.     if (lstwrn) fs_give ((void **) &lstwrn);
  258.     if (lsterr) fs_give ((void **) &lsterr);
  259.     while (litsp) fs_give ((void **) &litstk[--litsp]);
  260. /* find end of line */
  261.     if (!strchr (cmdbuf,'12')) {
  262.       if (t = strchr (cmdbuf,' ')) *t = '';
  263.       if ((t - cmdbuf) > 100) t = NIL;
  264.       flush (); /* flush excess */
  265.       if (state == LOGIN) /* error if NLI */
  266. syslog (LOG_INFO,"Line too long before authentication host=%.80s",
  267. tcp_clienthost ());
  268.       sprintf (tmp,response,t ? cmdbuf : "*");
  269.       PSOUT (tmp);
  270.     }
  271.     else if (!(tag = strtok (cmdbuf," 1512"))) {
  272.       if (state == LOGIN) /* error if NLI */
  273. syslog (LOG_INFO,"Null command before authentication host=%.80s",
  274. tcp_clienthost ());
  275.       PSOUT ("* BAD Null command1512");
  276.     }
  277.     else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag1512");
  278.     else if (!(cmd = strtok (NIL," 1512"))) {
  279.       if (state == LOGIN) /* error if NLI */
  280. syslog (LOG_INFO,"Missing command before authentication host=%.80s",
  281. tcp_clienthost ());
  282.       PSOUT (tag);
  283.       PSOUT (" BAD Missing command1512");
  284.     }
  285.     else { /* parse command */
  286.       response = win; /* set default response */
  287.       finding = NIL; /* no longer FINDing */
  288.       ucase (cmd); /* canonicalize command case */
  289. /* UID command? */
  290.       if (!strcmp (cmd,"UID") && strtok (NIL," 1512")) {
  291. uid = T; /* a UID command */
  292. cmd[3] = ' '; /* restore the space delimiter */
  293. ucase (cmd); /* make sure command all uppercase */
  294.       }
  295.       else uid = NIL; /* not a UID command */
  296. /* snarf argument */
  297.       arg = strtok (NIL,"1512");
  298. /* these commands always valid */
  299.       if (!strcmp (cmd,"NOOP")) {
  300. if (arg) response = badarg;
  301. else if (stream) /* allow untagged EXPUNGE */
  302.   mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  303.       }
  304.       else if (!strcmp (cmd,"LOGOUT")) {
  305. if (arg) response = badarg;
  306. else { /* time to say farewell */
  307.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  308.   if (state == OPEN) mail_close (stream);
  309.   state = LOGOUT;
  310.   stream = NIL;
  311.   PSOUT ("* BYE ");
  312.   PSOUT (mylocalhost ());
  313.   PSOUT (" IMAP4rev1 server terminating connection1512");
  314. }
  315.       }
  316.       else if (!strcmp (cmd,"CAPABILITY")) {
  317. if (arg) response = badarg;
  318. else {
  319.   AUTHENTICATOR *auth = mail_lookup_auth (1);
  320.   THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
  321.   PSOUT ("* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS");
  322. #ifdef NETSCAPE_BRAIN_DAMAGE
  323.   PSOUT (" X-NETSCAPE");
  324. #endif
  325. #ifdef PLAINTEXT_DISABLED
  326.   PSOUT (" LOGINDISABLED");
  327. #endif
  328.   while (auth) {
  329. #ifdef PLAINTEXT_DISABLED
  330. /* disable insecure authenticators */
  331.     if (!auth->secflag) auth->server = NIL;
  332. #endif
  333.     if (auth->server) {
  334.       PSOUT (" AUTH=");
  335.       PSOUT (auth->name);
  336.     }
  337.     auth = auth->next;
  338.   }
  339.   if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
  340.   while (thr) {
  341.     PSOUT (" THREAD=");
  342.     PSOUT (thr->name);
  343.     thr = thr->next;
  344.   }
  345.   CRLF;
  346. }
  347. if (stream) /* allow untagged EXPUNGE */
  348.   mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  349.       }
  350. #ifdef NETSCAPE_BRAIN_DAMAGE
  351.       else if (!strcmp (cmd,"NETSCAPE")) {
  352. PSOUT ("* OK [NETSCAPE]1512* VERSION 1.0 UNIX1512* ACCOUNT-URL "");
  353. PSOUT (NETSCAPE_BRAIN_DAMAGE);
  354. PBOUT ('"');
  355. CRLF;
  356.       }
  357. #endif
  358.       else switch (state) { /* dispatch depending upon state */
  359.       case LOGIN: /* waiting to get logged in */
  360. /* new style authentication */
  361. if (!strcmp (cmd,"AUTHENTICATE")) {
  362.   if (user) fs_give ((void **) &user);
  363.   if (pass) fs_give ((void **) &pass);
  364. /* single argument */
  365.   if (!(s = snarf (&arg))) response = misarg;
  366.   else if (arg) response = badarg;
  367.   else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
  368.     if ((s = imap_responder ("",0,NIL)) &&
  369. anonymous_login (argc,argv)) {
  370.       anonymous = T; /* note we are anonymous */
  371.       user = cpystr ("ANONYMOUS");
  372.       state = SELECT; /* make select */
  373.       syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
  374.       tcp_clienthost ());
  375.       fs_give ((void **) &s);
  376.     }
  377.     else response ="%.80s NO AUTHENTICATE ANONYMOUS cancelled1512";
  378.   }
  379.   else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
  380.     state = SELECT;
  381.     syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s",
  382.     user,tcp_clienthost ());
  383.   }
  384.   else {
  385.     lsterr = cpystr (s);
  386.     response = "%.80s NO %.80s %.80s failed1512";
  387.     syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
  388.     tcp_clienthost ());
  389.   }
  390. }
  391. /* plaintext login with password */
  392. else if (!strcmp (cmd,"LOGIN")) {
  393.   if (user) fs_give ((void **) &user);
  394.   if (pass) fs_give ((void **) &pass);
  395. /* two arguments */
  396.   if (!((user = cpystr (snarf (&arg))) &&
  397. (pass = cpystr (snarf (&arg))))) response = misarg;
  398.   else if (arg) response = badarg;
  399. /* see if we allow anonymous */
  400.   else if (((user[0] == 'a') || (user[0] == 'A')) &&
  401.    ((user[1] == 'n') || (user[1] == 'N')) &&
  402.    ((user[2] == 'o') || (user[2] == 'O')) &&
  403.    ((user[3] == 'n') || (user[3] == 'N')) &&
  404.    ((user[4] == 'y') || (user[4] == 'Y')) &&
  405.    ((user[5] == 'm') || (user[5] == 'M')) &&
  406.    ((user[6] == 'o') || (user[6] == 'O')) &&
  407.    ((user[7] == 'u') || (user[7] == 'U')) &&
  408.    ((user[8] == 's') || (user[8] == 'S')) && !user[9] &&
  409.    !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
  410.     anonymous = T; /* note we are anonymous */
  411.     ucase (user); /* make all uppercase for consistency */
  412.     state = SELECT; /* make select */
  413.     syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
  414.     tcp_clienthost ());
  415.   }
  416. /* see if username and password are OK */
  417.   else if (server_login (user,pass,argc,argv)) {
  418.     state = SELECT;
  419.     syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
  420.     tcp_clienthost ());
  421.   }
  422.   else response = "%.80s NO %.80s failed1512";
  423. }
  424. else response =
  425.   "%.80s BAD Command unrecognized/login please: %.80s1512";
  426. break;
  427.       case OPEN: /* valid only when mailbox open */
  428. /* fetch mailbox attributes */
  429. if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
  430.   if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"1512"))))
  431.     response = misarg;
  432.   else if (uid ? mail_uid_sequence (stream,s) :
  433.    mail_sequence (stream,s)) fetch (t,uid);
  434.   else response = badseq;
  435. }
  436. /* store mailbox attributes */
  437. else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
  438. /* must have three arguments */
  439.   if (arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) &&
  440.       (t = strtok (NIL,"1512"))) {
  441.     f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
  442.     if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
  443.       strcpy (tmp,"\Answered \Flagged \Deleted \Draft \Seen");
  444.       for (i = 0, u = tmp;
  445.    (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
  446.         if (strlen (v) <
  447.     ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
  448.   *u++ = ' '; /* write next flag */
  449.   strcpy (u,v);
  450. }
  451.       mail_flag (stream,s,tmp,f & ~ST_SET);
  452.     }
  453.     else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
  454.       f &= ~ST_SET; /* clear flags */
  455.     else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
  456.       response = badatt;
  457.       break;
  458.     }
  459. /* find last keyword */
  460.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
  461.     mail_flag (stream,s,t,f);
  462. /* any new keywords appeared? */
  463.     if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
  464. /* return flags if silence not wanted */
  465.     if (!(f & ST_SILENT) &&
  466. (uid ? mail_uid_sequence (stream,s) : mail_sequence(stream,s)))
  467.       for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
  468. mail_elt (stream,i)->spare2 = T;
  469.   }
  470.   else response = misarg;
  471. }
  472. /* fetch partial mailbox attributes */
  473. else if (!strcmp (cmd,"PARTIAL")) {
  474.   SIZEDTEXT st;
  475.   if (!(arg && (m = strtoul (arg,&s,10)) && (t = strtok (s," ")) &&
  476. (s = strtok (NIL,"1512")) && (j = strtoul (s,&s,10)) &&
  477. (k = strtoul (s,&s,10)))) response = misarg;
  478.   else if (s && *s) response = badarg;
  479.   else if (m > nmsgs) response = badseq;
  480.   else { /* looks good */
  481.     int sf = mail_elt (stream,m)->seen;
  482.     if (!strcmp (ucase (t),"RFC822"))
  483.       st.data = (unsigned char *)
  484. mail_fetch_message (stream,m,&st.size,NIL);
  485.     else if (!strcmp (t,"RFC822.PEEK"))
  486.       st.data = (unsigned char *)
  487. mail_fetch_message (stream,m,&st.size,FT_PEEK);
  488.     else if (!strcmp (t,"RFC822.HEADER"))
  489.       st.data = (unsigned char *)
  490. mail_fetch_header (stream,m,NIL,NIL,&st.size,FT_PEEK);
  491.     else if (!strcmp (t,"RFC822.TEXT"))
  492.       st.data = (unsigned char *)
  493. mail_fetch_text (stream,m,NIL,&st.size,NIL);
  494.     else if (!strcmp (t,"RFC822.TEXT.PEEK"))
  495.       st.data = (unsigned char *)
  496. mail_fetch_text (stream,m,NIL,&st.size,FT_PEEK);
  497.     else if (!strncmp (t,"BODY[",5) && (v = strchr(t+5,']')) && !v[1]){
  498.       strncpy (tmp,t+5,i = v - (t+5));
  499.       tmp[i] = ''; /* tie off body part */
  500.       st.data = (unsigned char *)
  501. mail_fetch_body (stream,m,tmp,&st.size,NIL);
  502.     }
  503.     else if (!strncmp (t,"BODY.PEEK[",10) &&
  504.      (v = strchr (t+10,']')) && !v[1]) {
  505.       strncpy (tmp,t+10,i = v - (t+10));
  506.       tmp[i] = ''; /* tie off body part */
  507.       st.data = (unsigned char *)
  508. mail_fetch_body (stream,m,tmp,&st.size,FT_PEEK);
  509.     }
  510.     else response = badatt, st.data = NIL;
  511.     if (st.data) { /* got a string? */
  512.       PSOUT ("* ");
  513.       pnum (m);
  514.       PSOUT (" FETCH (");
  515.       PSOUT (t);
  516.       PBOUT (' ');
  517. /* start position larger than text size */
  518.       if (st.size <= --j) PSOUT ("NIL");
  519.       else { /* output as sized text */
  520. st.data += j;
  521. if ((st.size -= j) > k) st.size = k;
  522. psizedtext (&st);
  523.       }
  524.       changed_flags (m,sf);
  525.       PSOUT (")1512");
  526.     }
  527.   }
  528. }
  529. /* check for new mail */
  530. else if (!strcmp (cmd,"CHECK")) {
  531. /* no arguments */
  532.   if (arg) response = badarg;
  533.   else if (!anonymous) {
  534.     mail_check (stream);
  535. /* remember last check time */
  536.     lastcheck = time (0);
  537.   }
  538. }
  539. /* expunge deleted messages */
  540. else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  541. /* no arguments */
  542.   if (arg) response = badarg;
  543.   else {
  544.     mail_expunge (stream);
  545. /* remember last checkpoint */
  546.     lastcheck = time (0);
  547.   }
  548. }
  549. /* close mailbox */
  550. else if (!strcmp (cmd,"CLOSE")) {
  551. /* no arguments */
  552.   if (arg) response = badarg;
  553.   else {
  554.     lastuid = 0; /* no last uid */
  555.     if (lastid) fs_give ((void **) &lastid);
  556.     if (lastst.data) fs_give ((void **) &lastst.data);
  557.     stream = mail_close_full (stream,anonymous ? NIL : CL_EXPUNGE);
  558.     state = SELECT; /* no longer opened */
  559.     lastcheck = 0; /* no last checkpoint */
  560.   }
  561. }
  562. else if (!anonymous && /* copy message(s) */
  563.  (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
  564.   trycreate = NIL; /* no trycreate status */
  565.   if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"1512"))
  566. && (t = snarf (&arg)))) response = misarg;
  567.   else if (arg) response = badarg;
  568.   else if (!nmsgs) response = "%.80s NO Mailbox is empty1512";
  569. /* try copy */
  570.   if (!(stream->dtb->copy) (stream,s,t,uid ? CP_UID : NIL)) {
  571.     response = trycreate ? losetry : lose;
  572.     if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  573.   }
  574. }
  575. /* sort mailbox */
  576. else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
  577. /* must have four arguments */
  578.   if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
  579. (t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
  580.   else { /* read criteria */
  581.     SEARCHPGM *spg = NIL;
  582.     char *cs = NIL;
  583.     SORTPGM *pgm = NIL,*pg = NIL;
  584.     unsigned long *slst,*sl;
  585.     *t = NIL; /* tie off criteria list */
  586.     s = strtok (ucase (s)," ");
  587.     do { /* parse sort attributes */
  588.       if (pg) pg = pg->next = mail_newsortpgm ();
  589.       else pgm = pg = mail_newsortpgm ();
  590.       if (!strcmp (s,"REVERSE")) {
  591. pg->reverse = T;
  592. if (!(s = strtok (NIL," "))) {
  593.   s = ""; /* end of attributes */
  594.   break;
  595. }
  596.       }
  597.       if (!strcmp (s,"DATE")) pg->function = SORTDATE;
  598.       else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
  599.       else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
  600.       else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
  601.       else if (!strcmp (s,"TO")) pg->function = SORTTO;
  602.       else if (!strcmp (s,"CC")) pg->function = SORTCC;
  603.       else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
  604.       else break;
  605.     } while (s = strtok (NIL," "));
  606. /* bad SORT attribute */
  607.     if (s) response = badatt;
  608.     else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg && *arg))
  609.       response = misarg;/* missing search attributes */
  610.     else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
  611.       nmsgs ? mail_uid (stream,nmsgs) : 0,0))
  612.       response = badatt;/* bad search attribute */
  613.     else if (arg && *arg) response = badarg;
  614.     else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID : NIL)) {
  615.       PSOUT ("* SORT");
  616.       for (sl = slst; *sl; sl++) {
  617. PBOUT (' ');
  618. pnum (*sl);
  619.       }
  620.       CRLF;
  621.       fs_give ((void **) &slst);
  622.     }
  623.     if (pgm) mail_free_sortpgm (&pgm);
  624.     if (spg) mail_free_searchpgm (&spg);
  625.     if (cs) fs_give ((void **) &cs);
  626.   }
  627. }
  628. /* thread mailbox */
  629. else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
  630.   THREADNODE *thr;
  631.   SEARCHPGM *spg;
  632.   char *cs = NIL;
  633. /* must have four arguments */
  634.   if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) &&
  635. (cs = cpystr (cs)) && (arg = strtok (NIL,"1512"))))
  636.     response = misarg;
  637.   else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
  638.     nmsgs ? mail_uid (stream,nmsgs) : 0,0))
  639.       response = badatt;/* bad thread attribute */
  640.   else if (arg && *arg) response = badarg;
  641.   else if (thr = mail_thread (stream,ucase (s),cs,spg,
  642.       uid ? SE_UID : NIL)) {
  643.     PSOUT ("* THREAD ");
  644.     pthread (thr);
  645.     CRLF;
  646.     mail_free_threadnode (&thr);
  647.   }
  648.   if (spg) mail_free_searchpgm (&spg);
  649.   if (cs) fs_give ((void **) &cs);
  650. }
  651. /* search mailbox */
  652.         else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
  653.   char *charset = NIL;
  654.   SEARCHPGM *pgm;
  655. /* one or more arguments required */
  656.   if (!arg) response = misarg;
  657. /* character set specified? */
  658.   else if ((arg[0] == 'C' || arg[0] == 'c') &&
  659.    (arg[1] == 'H' || arg[1] == 'h') &&
  660.    (arg[2] == 'A' || arg[2] == 'a') &&
  661.    (arg[3] == 'R' || arg[3] == 'r') &&
  662.    (arg[4] == 'S' || arg[4] == 's') &&
  663.    (arg[5] == 'E' || arg[5] == 'e') &&
  664.    (arg[6] == 'T' || arg[6] == 't') &&
  665.    (arg[7] == ' ' || arg[7] == ' ')) {
  666.     arg += 8; /* yes, skip over CHARSET token */
  667.     if (s = snarf (&arg)) charset = cpystr (s);
  668.     else { /* missing character set */
  669.       response = misarg;
  670.       break;
  671.     }
  672.   }
  673. /* must have arguments here */
  674.   if (!(arg && *arg)) response = misarg;
  675.   else if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
  676.    nmsgs ? mail_uid (stream,nmsgs) : 0,0) &&
  677.    !*arg) {
  678.     mail_search_full (stream,charset,pgm,SE_FREE);
  679.     if (response == win || response == altwin) {
  680. /* output search results */
  681.       PSOUT ("* SEARCH");
  682.       for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->searched) {
  683. PBOUT (' ');
  684. pnum (uid ? mail_uid (stream,i) : i);
  685.       }
  686.       CRLF;
  687.     }
  688.   }
  689.   else {
  690.     response = "%.80s BAD Bogus criteria list in %.80s1512";
  691.     mail_free_searchpgm (&pgm);
  692.   }
  693.   if (charset) fs_give ((void **) &charset);
  694. }
  695. else /* fall into select case */
  696.       case SELECT: /* valid whenever logged in */
  697. /* select new mailbox */
  698.   if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
  699. strcmp (cmd,"BBOARD"))) {
  700. /* single argument */
  701.   if (!(s = snarf (&arg))) response = misarg;
  702.   else if (arg) response = badarg;
  703.   else if (nameok (NIL,s = bboardname (cmd,s))) {
  704.     DRIVER *factory = mail_valid (NIL,s,NIL);
  705.     f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) |
  706.       ((*cmd == 'S') ? NIL : OP_READONLY);
  707.     curdriver = NIL; /* no drivers known */
  708.     lastuid = 0; /* no last uid */
  709.     if (lastid) fs_give ((void **) &lastid);
  710.     if (lastst.data) fs_give ((void **) &lastst.data);
  711. /* force update */
  712.     nmsgs = recent = 0xffffffff;
  713.     if (factory && !strcmp (factory->name,"phile") &&
  714. (stream = mail_open (stream,s,f | OP_SILENT)) &&
  715. ((response == win) || (response == altwin))) {
  716.       BODY *b;
  717. /* see if proxy open */
  718.       if ((mail_elt (stream,1)->rfc822_size < 400) &&
  719.   mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
  720.   (s = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
  721.   (i < MAILTMPLEN) && (s[0] == '{')) {
  722. /* copy and tie off */
  723. strncpy (tmp,s,i)[i] = '';
  724. /* nuke any trailing newline */
  725. if (s = strpbrk (tmp,"rn")) *s = '';
  726. /* try to open proxy */
  727. if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
  728.     ((response == win) || (response == altwin)) &&
  729.     tstream->nmsgs) {
  730. /* got it, close the link */
  731.   mail_close (stream);
  732.   stream = tstream;
  733.   tstream = NIL;
  734. }
  735.       }
  736. /* now give the exists event */
  737.       stream->silent = NIL;
  738.       mm_exists (stream,stream->nmsgs);
  739.     }
  740. /* open stream normally then */
  741.     else stream = mail_open (stream,s,f);
  742.     if (stream && ((response == win) || (response == altwin))) {
  743.       state = OPEN; /* note state open */
  744. /* note readonly/readwrite */
  745.       response = stream->rdonly ?
  746. "%.80s OK [READ-ONLY] %.80s completed1512" :
  747.   "%.80s OK [READ-WRITE] %.80s completed1512";
  748.       if (anonymous)
  749. syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
  750. stream->mailbox,tcp_clienthost ());
  751.       lastcheck = 0; /* no last check */
  752.     }
  753.     else { /* failed */
  754.       state = SELECT; /* no mailbox open now */
  755.       response = lose; /* open failed */
  756.     }
  757.   }
  758. }
  759. /* APPEND message to mailbox */
  760. else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  761.   u = v = NIL; /* init flags/date */
  762. /* parse mailbox name */
  763.   if ((s = snarf (&arg)) && arg) {
  764.     if (*arg == '(') { /* parse optional flag list */
  765.       u = ++arg; /* pointer to flag list contents */
  766.       while (*arg && (*arg != ')')) arg++;
  767.       if (*arg) *arg++ = '';
  768.       if (*arg == ' ') arg++;
  769.     }
  770. /* parse optional date */
  771.     if (*arg == '"') v = snarf (&arg);
  772. /* parse message */
  773.     if (!arg || (*arg != '{'))
  774.       response = "%.80s BAD Missing literal in %.80s1512";
  775.     else if (!isdigit (arg[1]))
  776.       response = "%.80s BAD Missing message to %.80s1512";
  777.     else if (!(i = strtoul (arg+1,&t,10)))
  778.       response = "%.80s No Empty message to %.80s1512";
  779.     else if ((*t != '}') || t[1]) response = badarg;
  780.     else { /* append the data */
  781.       STRING st;
  782.       PSOUT (argrdy); /* tell client ready for argument */
  783.       PFLUSH; /* dump output buffer */
  784. /* get a literal buffer */
  785.       t = (char *) fs_get (i+1);
  786.       alarm (TIMEOUT); /* get data under timeout */
  787.       for (j = 0; j < i; j++) t[j] = inchar ();
  788.       alarm (0); /* stop timeout */
  789.       t[i] = ''; /* make sure tied off */
  790.      /* get new command tail */
  791.       if ((j = inchar ()) == '15') j = inchar ();
  792.       if (j == '12') {/* must be end of command */
  793. INIT (&st,mail_string,(void *) t,i);
  794. trycreate = NIL; /* no trycreate status */
  795. if (!mail_append_full (NIL,s,u,v,&st)) {
  796.   response = trycreate ? losetry : lose;
  797.   if (!lsterr) lsterr = cpystr ("Unexpected APPEND failure");
  798. }
  799.       }
  800.       else { /* junk after literal */
  801. while (inchar () != '12');
  802. response = badarg;
  803.       }
  804.       fs_give ((void **) &t);
  805.     }
  806.   }
  807.   else response = misarg;
  808.   if (stream) /* allow untagged EXPUNGE */
  809.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  810. }
  811. /* list mailboxes */
  812. else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
  813. /* get reference and mailbox argument */
  814.   if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
  815.     response = misarg;
  816.   else if (arg) response = badarg;
  817. /* make sure anonymous can't do bad things */
  818.   else if (nameok (s,t)) mail_list (NIL,s,t);
  819.   if (stream) /* allow untagged EXPUNGE */
  820.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  821. }
  822. /* scan mailboxes */
  823. else if (!strcmp (cmd,"SCAN")) {
  824. /* get arguments */
  825.   if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
  826. (u = snarf (&arg)))) response = misarg;
  827.   else if (arg) response = badarg;
  828. /* make sure anonymous can't do bad things */
  829.   else if (nameok (s,t)) mail_scan (NIL,s,t,u);
  830.   if (stream) /* allow untagged EXPUNGE */
  831.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  832. }
  833. /* list subscribed mailboxes */
  834. else if (!(anonymous || (strcmp(cmd,"LSUB") && strcmp(cmd,"RLSUB")))) {
  835. /* get reference and mailbox argument */
  836.   if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
  837.     response = misarg;
  838.   else if (arg) response = badarg;
  839. /* make sure anonymous can't do bad things */
  840.   else if (nameok (s,t)) mail_lsub (NIL,s,t);
  841.   if (stream) /* allow untagged EXPUNGE */
  842.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  843. }
  844. /* find mailboxes */
  845. else if (!strcmp (cmd,"FIND")) {
  846.   response = "%.80s OK FIND %.80s completed1512";
  847. /* get subcommand and true argument */
  848.   if (!(arg && (s = strtok (arg," 1512")) && (cmd = ucase (s)) &&
  849. (arg = strtok (NIL,"1512")) && (s = snarf_list (&arg))))
  850.     response = misarg; /* missing required argument */
  851.   else if (arg) response = badarg;
  852. /* punt on single-char wildcards */
  853.   else if (strpbrk (s,"%?")) response =
  854.     "%.80s NO FIND %.80s ? or %% wildcard not supported1512";
  855.   else if (nameok (NIL,s)) {
  856.     finding = T; /* note that we are FINDing */
  857. /* dispatch based on type */
  858.     if (!strcmp (cmd,"MAILBOXES") && !anonymous) mail_lsub (NIL,NIL,s);
  859.     else if (!strcmp (cmd,"ALL.MAILBOXES")) {
  860. /* convert * to % for compatible behavior */
  861.       for (t = s; *t; t++) if (*t == '*') *t = '%';
  862.       mail_list (NIL,NIL,s);
  863.     }
  864.     else response="%.80s BAD Command unrecognized: FIND %.80s1512";
  865.   }
  866.   if (stream) /* allow untagged EXPUNGE */
  867.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  868. }
  869. /* status of mailbox */
  870. else if (!strcmp (cmd,"STATUS")) {
  871.   if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
  872. (t = strchr (arg,')')) && (t - arg) && !t[1]))
  873.     response = misarg;
  874.   else {
  875.     f = NIL; /* initially no flags */
  876.     *t = ''; /* tie off flag string */
  877. /* read flags */
  878.     t = strtok (ucase (arg)," ");
  879.     do { /* parse each one; unknown generate warning */
  880.       if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
  881.       else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
  882.       else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
  883.       else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
  884.       else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
  885.       else {
  886. PSOUT ("* NO Unknown status flag ");
  887. PSOUT (t);
  888. CRLF;
  889.       }
  890.     } while (t = strtok (NIL," "));
  891.     ping_mailbox (uid); /* in case the fool did STATUS on open mbx */
  892. /* get mailbox status */
  893.     if ((state == LOGOUT) || !mail_status (NIL,s,f)) response = lose;
  894.   }
  895.   if (stream) /* allow untagged EXPUNGE */
  896.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  897. }
  898. /* subscribe to mailbox */
  899. else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  900. /* get <mailbox> or MAILBOX <mailbox> */
  901.   if (!(s = snarf (&arg))) response = misarg;
  902.   else if (arg) {
  903.     sprintf (tmp,"%.30s",s);
  904.     if (strcmp (ucase (tmp),"MAILBOX")) response = badarg;
  905.     else if (!(s = snarf (&arg))) response = misarg;
  906.     else if (arg) response = badarg;
  907.     else mail_subscribe (NIL,s);
  908.   }
  909.   else mail_subscribe (NIL,s);
  910.   if (stream) /* allow untagged EXPUNGE */
  911.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  912. }
  913. /* unsubscribe to mailbox */
  914. else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  915. /* get <mailbox> or MAILBOX <mailbox> */
  916.   if (!(s = snarf (&arg))) response = misarg;
  917.   else if (arg) {
  918.     sprintf (tmp,"%.30s",s);
  919.     if (strcmp (ucase (tmp),"MAILBOX")) response = badarg;
  920.     else if (!(s = snarf (&arg))) response = misarg;
  921.     else if (arg) response = badarg;
  922.     else mail_unsubscribe (NIL,s);
  923.   }
  924.   else mail_unsubscribe (NIL,s);
  925.   if (stream) /* allow untagged EXPUNGE */
  926.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  927. }
  928. else if (!strcmp (cmd,"NAMESPACE")) {
  929.   if (arg) response = badarg;
  930.   else {
  931.     NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
  932.      NIL);
  933.     NAMESPACE *n;
  934.     PARAMETER *p;
  935.     PSOUT ("* NAMESPACE");
  936.     if (ns) for (i = 0; i < 3; i++) {
  937.       if (n = ns[i]) {
  938. PSOUT (" (");
  939. do {
  940.   PBOUT ('(');
  941.   pstring (n->name);
  942.   switch (n->delimiter) {
  943.   case '\': /* quoted delimiter */
  944.   case '"':
  945.     PSOUT (" "\\"");
  946.     break;
  947.   case '': /* no delimiter */
  948.     PSOUT (" NIL");
  949.     break;
  950.   default: /* unquoted delimiter */
  951.     PSOUT (" "");
  952.     PBOUT (n->delimiter);
  953.     PBOUT ('"');
  954.     break;
  955.   }
  956.   if (p = n->param) do {
  957.     PBOUT (' ');
  958.     pstring (p->attribute);
  959.     PBOUT (' ');
  960.     pstring (p->value);
  961.   } while (p = p->next);
  962.   PBOUT (')');
  963. } while (n = n->next);
  964. PBOUT (')');
  965.       }
  966.       else PSOUT (" NIL");
  967.     }
  968.     else PSOUT (" NIL NIL NIL");
  969.     CRLF;
  970.   }
  971.   if (stream) /* allow untagged EXPUNGE */
  972.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  973. }
  974. /* create mailbox */
  975. else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  976.   if (!(s = snarf (&arg))) response = misarg;
  977.   else if (arg) response = badarg;
  978.   else mail_create (NIL,s);
  979.   if (stream) /* allow untagged EXPUNGE */
  980.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  981. }
  982. /* delete mailbox */
  983. else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  984.   if (!(s = snarf (&arg))) response = misarg;
  985.   else if (arg) response = badarg;
  986.   else mail_delete (NIL,s);
  987.   if (stream) /* allow untagged EXPUNGE */
  988.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  989. }
  990. /* rename mailbox */
  991. else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  992.   if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  993.   else if (arg) response = badarg;
  994.   else mail_rename (NIL,s,t);
  995.   if (stream) /* allow untagged EXPUNGE */
  996.     mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  997. }
  998. /* idle mode */
  999. else if (!strcmp (cmd,"IDLE")) {
  1000. /* no arguments */
  1001.   if (arg) response = badarg;
  1002.   else { /* tell client ready for argument */
  1003.     PSOUT (argrdy);
  1004.     PFLUSH; /* dump output buffer */
  1005. /* maybe do a checkpoint if not anonymous */
  1006.     if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) {
  1007.       mail_check (stream);
  1008. /* cancel likely altwin from mail_check() */
  1009.       if (response == altwin) response = win;
  1010. /* remember last checkpoint */
  1011.       lastcheck = time (0);
  1012.     }
  1013. /* inactivity countdown */
  1014.     i = ((TIMEOUT) / (IDLETIMER)) + 1;
  1015.     do { /* main idle loop */
  1016.       mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *)stream);
  1017.       ping_mailbox (uid);
  1018.       if (lstwrn) { /* have a warning? */
  1019. PSOUT ("* NO ");
  1020. PSOUT (lstwrn);
  1021. CRLF;
  1022. fs_give ((void **) &lstwrn);
  1023.       }
  1024.       PFLUSH; /* dump output buffer */
  1025.     } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
  1026. /* time to exit idle loop */
  1027.     if (state != LOGOUT) {
  1028.       if (i) { /* still have time left? */
  1029. /* yes, read expected DONE */
  1030. slurp (tmp,MAILTMPLEN);
  1031. if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
  1032.     ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
  1033.     ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
  1034.     ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
  1035.     (((tmp[4] != '15') || (tmp[5] != '12')) &&
  1036.      (tmp[4] != '12')))
  1037.   response = "%.80s BAD Bogus IDLE continuation1512";
  1038.       }
  1039.       else clkint (); /* otherwise do autologout action */
  1040.     }
  1041.   }
  1042. }
  1043. else response = "%.80s BAD Command unrecognized: %.80s1512";
  1044. break;
  1045.       default:
  1046.         response = "%.80s BAD Server in unknown state for %.80s command1512";
  1047. break;
  1048.       }
  1049.       if (lstwrn) { /* output most recent warning */
  1050. PSOUT ("* NO ");
  1051. PSOUT (lstwrn);
  1052. CRLF;
  1053.       }
  1054. /* get text for alternative win message now */
  1055.       if (response == altwin) cmd = lsterr;
  1056. /* build response */
  1057.       sprintf (tmp,response,tag,cmd,lasterror ());
  1058.       ping_mailbox (uid); /* update mailbox status before response */
  1059.       PSOUT (tmp); /* output response */
  1060.     }
  1061.     PFLUSH; /* make sure output blatted */
  1062.     if (autologouttime) { /* have an autologout in effect? */
  1063. /* cancel if no longer waiting for login */
  1064.       if (state != LOGIN) autologouttime = 0;
  1065. /* took too long to login */
  1066.       else if (autologouttime < time (0)) {
  1067. PSOUT ("* BYE Autologout1512");
  1068. syslog (LOG_INFO,"Autologout host=%.80s",tcp_clienthost ());
  1069. PFLUSH; /* make sure output blatted */
  1070. state = LOGOUT; /* sayonara */
  1071.       }
  1072.     }
  1073.   } while (state != LOGOUT); /* until logged out */
  1074.   syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???",
  1075.   tcp_clienthost ());
  1076.   return 0; /* all done */
  1077. }
  1078. /* Ping mailbox during each cycle.  Also check alerts
  1079.  * Accepts: last command was UID flag
  1080.  */
  1081. void ping_mailbox (unsigned long uid)
  1082. {
  1083.   unsigned long i;
  1084.   char tmp[MAILTMPLEN];
  1085.   if (state == OPEN) {
  1086.     if (!mail_ping (stream)) { /* make sure stream still alive */
  1087.       PSOUT ("* BYE ");
  1088.       PSOUT (mylocalhost ());
  1089.       PSOUT (" Fatal mailbox error: ");
  1090.       PSOUT (lasterror ());
  1091.       CRLF;
  1092.       state = LOGOUT; /* go away */
  1093.       syslog (LOG_INFO,
  1094.       "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1095.       user ? user : "???",tcp_clienthost (),
  1096.       (stream && stream->mailbox) ? stream->mailbox : "???",
  1097.       lasterror ());
  1098.       return;
  1099.     }
  1100. /* change in number of messages? */
  1101.     if (existsquelled || (nmsgs != stream->nmsgs)) {
  1102.       PSOUT ("* ");
  1103.       pnum (nmsgs = stream->nmsgs);
  1104.       PSOUT (" EXISTS1512");
  1105.     }
  1106. /* change in recent messages? */
  1107.     if (existsquelled || (recent != stream->recent)) {
  1108.       PSOUT ("* ");
  1109.       pnum (recent = stream->recent);
  1110.       PSOUT (" RECENT1512");
  1111.     }
  1112.     existsquelled = NIL; /* don't do this until asked again */
  1113. /* don't bother if driver changed */
  1114.     if (curdriver == stream->dtb) {
  1115.       for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
  1116. PSOUT ("* ");
  1117. pnum (i);
  1118. PSOUT (" FETCH (");
  1119. fetch_flags (i,NIL); /* output changed flags */
  1120. if (uid) { /* need to include UIDs in response? */
  1121.   PBOUT (' ');
  1122.   fetch_uid (i,NIL);
  1123. }
  1124. PSOUT (")1512");
  1125.       }
  1126.     }
  1127.     else { /* driver changed */
  1128.       PSOUT ("* OK [UIDVALIDITY ");
  1129.       pnum (stream->uid_validity);
  1130.       PSOUT ("] UID validity status1512* OK [UIDNEXT ");
  1131.       pnum (stream->uid_last + 1);
  1132.       PSOUT ("] Predicted next UID1512");
  1133.       if (stream->uid_nosticky) {
  1134. PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
  1135. PSOUT (stream->mailbox);
  1136. CRLF;
  1137.       }
  1138.       new_flags (stream); /* send mailbox flags */
  1139.       if (curdriver) { /* note readonly/write if possible change */
  1140. PSOUT ("* OK [READ-");
  1141. PSOUT (stream->rdonly ? "ONLY" : "WRITE");
  1142. PSOUT ("] Mailbox status1512");
  1143.       }
  1144.       curdriver = stream->dtb;
  1145.       if (nmsgs) { /* get flags for all messages */
  1146. sprintf (tmp,"1:%lu",nmsgs);
  1147. mail_fetch_flags (stream,tmp,NIL);
  1148. /* don't do this if newsrc already did */
  1149. if (!(curdriver->flags & DR_NEWS)) {
  1150. /* find first unseen message */
  1151.   for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
  1152.   if (i <= nmsgs) {
  1153.     PSOUT ("* OK [UNSEEN ");
  1154.     pnum (i);
  1155.     PSOUT ("] first unseen message in ");
  1156.     PSOUT (stream->mailbox);
  1157.     CRLF;
  1158.   }
  1159. }
  1160.       }
  1161.     }
  1162.   }
  1163. /* don't do these stat()s every cycle */
  1164.   if (time (0) > alerttime + ALERTTIMER) {
  1165.     alerttime = time (0); /* output any new alerts */
  1166.     sysalerttime = palert (ALERTFILE,sysalerttime);
  1167.     useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
  1168.   }
  1169. }
  1170. /* Print an alert file
  1171.  * Accepts: path of alert file
  1172.  *     time of last printed alert file
  1173.  * Returns: updated time of last printed alert file
  1174.  */
  1175. time_t palert (char *file,time_t oldtime)
  1176. {
  1177.   FILE *alf;
  1178.   struct stat sbuf;
  1179.   int c,lc = '12';
  1180. /* have a new alert file? */
  1181.   if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
  1182.       !(alf = fopen (file,"r"))) return oldtime;
  1183. /* yes, display it */
  1184.   while ((c = getc (alf)) != EOF) {
  1185.     if (lc == '12') PSOUT ("* OK [ALERT] ");
  1186.     switch (c) { /* output character */
  1187.     case '12': /* newline means do CRLF */
  1188.       CRLF;
  1189.     case '15': /* flush CRs */
  1190.     case '': /* flush nulls */
  1191.       break;
  1192.     default:
  1193.       PBOUT (c); /* output all other characters */
  1194.       break;
  1195.     }
  1196.     lc = c; /* note previous character */
  1197.   }
  1198.   fclose (alf);
  1199.   if (lc != '12') CRLF; /* final terminating CRLF */
  1200.   return sbuf.st_mtime; /* return updated last alert time */
  1201. }
  1202. /* Initialize file string structure for file stringstruct
  1203.  * Accepts: string structure
  1204.  *     pointer to message data structure
  1205.  *     size of string
  1206.  */
  1207. void msg_string_init (STRING *s,void *data,unsigned long size)
  1208. {
  1209.   MSGDATA *md = (MSGDATA *) data;
  1210.   s->data = data; /* note stream/msgno and header length */
  1211.   mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
  1212.  FT_PREFETCHTEXT | FT_PEEK);
  1213. #if 0
  1214.   s->size = size; /* message size */
  1215. #else /* This kludge is necessary because of broken IMAP servers (sigh!) */
  1216.   mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
  1217.   s->size += s->data1; /* header + body size */
  1218. #endif
  1219.   SETPOS (s,0);
  1220. }
  1221. /* Get next character from file stringstruct
  1222.  * Accepts: string structure
  1223.  * Returns: character, string structure chunk refreshed
  1224.  */
  1225. char msg_string_next (STRING *s)
  1226. {
  1227.   char c = *s->curpos++; /* get next byte */
  1228.   SETPOS (s,GETPOS (s)); /* move to next chunk */
  1229.   return c; /* return the byte */
  1230. }
  1231. /* Set string pointer position for file stringstruct
  1232.  * Accepts: string structure
  1233.  *     new position
  1234.  */
  1235. void msg_string_setpos (STRING *s,unsigned long i)
  1236. {
  1237.   MSGDATA *md = (MSGDATA *) s->data;
  1238.   if (i < s->data1) { /* want header? */
  1239.     s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
  1240.     s->chunksize = s->data1; /* header length */
  1241.     s->offset = 0; /* offset is start of message */
  1242.   }
  1243.   else if (i < s->size) { /* want body */
  1244.     s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
  1245.     s->chunksize = s->size - s->data1;
  1246.     s->offset = s->data1; /* offset is end of header */
  1247.   }
  1248.   else { /* off end of message */
  1249.     s->chunk = NIL; /* make sure that we crack on this then */
  1250.     s->chunksize = 1; /* make sure SNX cracks the right way... */
  1251.     s->offset = i;
  1252.   }
  1253. /* initial position and size */
  1254.   s->curpos = s->chunk + (i -= s->offset);
  1255.   s->cursize = s->chunksize - i;
  1256. }
  1257. /* Send flags for stream
  1258.  * Accepts: MAIL stream
  1259.  *     scratch buffer
  1260.  */
  1261. void new_flags (MAILSTREAM *stream)
  1262. {
  1263.   int i,c;
  1264.   PSOUT ("* FLAGS (");
  1265.   for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
  1266.     PSOUT (stream->user_flags[i]);
  1267.     PBOUT (' ');
  1268.   }
  1269.   PSOUT ("\Answered \Flagged \Deleted \Draft \Seen)1512* OK [PERMANENTFLAGS (");
  1270.   for (i = c = 0; i < NUSERFLAGS; i++)
  1271.     if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
  1272.       put_flag (&c,stream->user_flags[i]);
  1273.   if (stream->kwd_create) put_flag (&c,"\*");
  1274.   if (stream->perm_answered) put_flag (&c,"\Answered");
  1275.   if (stream->perm_flagged) put_flag (&c,"\Flagged");
  1276.   if (stream->perm_deleted) put_flag (&c,"\Deleted");
  1277.   if (stream->perm_draft) put_flag (&c,"\Draft");
  1278.   if (stream->perm_seen) put_flag (&c,"\Seen");
  1279.   PSOUT (")] Permanent flags1512");
  1280. }
  1281. /* Clock interrupt
  1282.  */
  1283. void clkint (void)
  1284. {
  1285.   alarm (0); /* disable all interrupts */
  1286.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1287.   if (!quell_events)
  1288.     PSOUT ("* BYE Autologout; idle for too long1512");
  1289.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  1290.   tcp_clienthost ());
  1291.   PFLUSH; /* make sure output blatted */
  1292.   if (critical) state = LOGOUT; /* badly hosed if in critical code */
  1293.   else { /* try to gracefully close the stream */
  1294.     if ((state == OPEN) && !stream->lock) mail_close (stream);
  1295.     state = LOGOUT;
  1296.     stream = NIL;
  1297.     _exit (1); /* die die die */
  1298.   }
  1299. }
  1300. /* Kiss Of Death interrupt
  1301.  */
  1302. void kodint (void)
  1303. {
  1304.   alarm (0); /* disable all interrupts */
  1305.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1306.   if (!quell_events) PSOUT ("* BYE Lost mailbox lock1512");
  1307.   PFLUSH; /* make sure output blatted */
  1308.   syslog (LOG_INFO,"Killed (lost mailbox lock) user=%.80s host=%.80s",
  1309.   user ? user : "???",tcp_clienthost ());
  1310.   if (critical) state = LOGOUT; /* must defer if in critical code */
  1311.   else { /* try to gracefully close the stream */
  1312.     if ((state == OPEN) && !stream->lock) mail_close (stream);
  1313.     state = LOGOUT;
  1314.     stream = NIL;
  1315.     _exit (1); /* die die die */
  1316.   }
  1317. }
  1318. /* Hangup interrupt
  1319.  */
  1320. void hupint (void)
  1321. {
  1322.   alarm (0); /* disable all interrupts */
  1323.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1324.   syslog (LOG_INFO,"Hangup user=%.80s host=%.80s",user ? user : "???",
  1325.   tcp_clienthost ());
  1326.   if (critical) state = LOGOUT; /* must defer if in critical code */
  1327.   else { /* try to gracefully close the stream */
  1328.     if ((state == OPEN) && !stream->lock) mail_close (stream);
  1329.     state = LOGOUT;
  1330.     stream = NIL;
  1331.     _exit (1); /* die die die */
  1332.   }
  1333. }
  1334. /* Termination interrupt
  1335.  */
  1336. void trmint (void)
  1337. {
  1338.   alarm (0); /* disable all interrupts */
  1339.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1340.   if (!quell_events) PSOUT ("* BYE Killed1512");
  1341.   syslog (LOG_INFO,"Killed user=%.80s host=%.80s",user ? user : "???",
  1342.   tcp_clienthost ());
  1343.   if (critical) state = LOGOUT; /* must defer if in critical code */
  1344.   else { /* try to gracefully close the stream */
  1345.     if ((state == OPEN) && !stream->lock) mail_close (stream);
  1346.     state = LOGOUT;
  1347.     stream = NIL;
  1348.     _exit (1); /* die die die */
  1349.   }
  1350. }
  1351. /* Slurp a command line
  1352.  * Accepts: buffer pointer
  1353.  *     buffer size
  1354.  */
  1355. void slurp (char *s,int n)
  1356. {
  1357.   s[--n] = ''; /* last buffer character is guaranteed NUL */
  1358. /* get a command under timeout */
  1359.   alarm ((state != LOGIN) ? TIMEOUT : LOGINTIMEOUT);
  1360.   clearerr (stdin); /* clear stdin errors */
  1361.   while (!PSIN (s,n)) { /* read buffer */
  1362. /* ignore if some interrupt */
  1363.     if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
  1364.     else {
  1365.       char *e = ferror (stdin) ?
  1366. strerror (errno) : "Command stream end of file";
  1367.       alarm (0); /* disable all interrupts */
  1368.       server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1369.       syslog (LOG_INFO,"%.80s, while reading line user=%.80s host=%.80s",
  1370.       e,user ? user : "???",tcp_clienthost ());
  1371. /* try to gracefully close the stream */
  1372.       if (state == OPEN) mail_close (stream);
  1373.       state = LOGOUT;
  1374.       stream = NIL;
  1375.       _exit (1);
  1376.     }
  1377.   }
  1378.   alarm (0); /* make sure timeout disabled */
  1379. }
  1380. /* Read a character
  1381.  * Returns: character
  1382.  */
  1383. char inchar (void)
  1384. {
  1385.   int c;
  1386.   clearerr (stdin); /* clear stdin errors */
  1387.   while ((c = PBIN ()) == EOF) {
  1388. /* ignore if some interrupt */
  1389.     if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
  1390.     else {
  1391.       char *e = ferror (stdin) ?
  1392. strerror (errno) : "Command stream end of file";
  1393.       alarm (0); /* disable all interrupts */
  1394.       server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1395.       syslog (LOG_INFO,"%.80s, while reading char user=%.80s host=%.80s",
  1396.       e,user ? user : "???",tcp_clienthost ());
  1397. /* try to gracefully close the stream */
  1398.       if (state == OPEN) mail_close (stream);
  1399.       state = LOGOUT;
  1400.       stream = NIL;
  1401.       _exit (1);
  1402.     }
  1403.   }
  1404.   return c;
  1405. }
  1406. /* Flush until newline seen
  1407.  * Returns: NIL, always
  1408.  */
  1409. char *flush (void)
  1410. {
  1411.   while (inchar () != '12'); /* slurp until we find newline */
  1412.   response = "%.80s BAD Command line too long1512";
  1413.   return NIL;
  1414. }
  1415. /* Parse an IMAP astring
  1416.  * Accepts: pointer to argument text pointer
  1417.  *     pointer to returned size
  1418.  *     pointer to returned delimiter
  1419.  * Returns: argument
  1420.  */
  1421. char *parse_astring (char **arg,unsigned long *size,char *del)
  1422. {
  1423.   unsigned long i;
  1424.   char c,*s,*t,*v;
  1425.   if (!*arg) return NIL; /* better be an argument */
  1426.   switch (**arg) { /* see what the argument is */
  1427.   default: /* atom */
  1428.     for (s = t = *arg, i = 0;
  1429.  (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
  1430.  (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
  1431.  (*t != '\'); ++t,++i);
  1432.     if (*size = i) break; /* got atom if non-empty */
  1433.   case ')': case '%': case '*': case '\': case '': case ' ':
  1434.    return NIL; /* empty atom is a bogon */
  1435.   case '"': /* hunt for trailing quote */
  1436.     for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
  1437.       if (c == '\') c = *t++; /* quote next character */
  1438. /* else must be a CHAR */
  1439.       if (!c || (c & 0x80)) return NIL;
  1440.     }
  1441.     *v = ''; /* tie off string */
  1442.     *size = v - s; /* return size */
  1443.     break;
  1444.   case '{': /* literal string */
  1445.     s = *arg + 1; /* get size */
  1446.     if (!isdigit (*s)) return NIL;
  1447.     if ((*size = i = strtoul (s,&t,10)) > MAXCLIENTLIT) {
  1448.       mm_notify (NIL,"Absurdly long client literal",ERROR);
  1449.       syslog (LOG_INFO,"Absurdly long client literal user=%.80s host=%.80s",
  1450.       user ? user : "???",tcp_clienthost ());
  1451.       return NIL;
  1452.     }
  1453. /* validate end of literal */
  1454.     if (!t || (*t != '}') || t[1]) return NIL;
  1455.     if (litsp >= LITSTKLEN) { /* make sure don't overflow stack */
  1456.       mm_notify (NIL,"Too many literals in command",ERROR);
  1457.       return NIL;
  1458.     }
  1459.     PSOUT (argrdy); /* tell client ready for argument */
  1460.     PFLUSH; /* dump output buffer */
  1461. /* get a literal buffer */
  1462.     s = v = litstk[litsp++] = (char *) fs_get (i+1);
  1463. /* get literal under timeout */
  1464.     alarm ((state != LOGIN) ? TIMEOUT : LOGINTIMEOUT);
  1465.     while (i--) *v++ = inchar ();
  1466.     alarm (0); /* stop timeout */
  1467.     *v++ = NIL; /* make sure string tied off */
  1468.      /* get new command tail */
  1469.     slurp ((*arg = v = t),TMPLEN - (t - cmdbuf));
  1470.     if (!strchr (t,'12')) return flush ();
  1471. /* reset strtok mechanism, tie off if done */
  1472.     if (!strtok (t,"1512")) *t = '';
  1473.     break;
  1474.   }
  1475.   if (*del = *t) { /* have a delimiter? */
  1476.     *t++ = ''; /* yes, stomp on it */
  1477.     *arg = t; /* update argument pointer */
  1478.   }
  1479.   else *arg = NIL; /* no more arguments */
  1480.   return s;
  1481. }
  1482. /* Snarf a command argument (simple jacket into parse_astring())
  1483.  * Accepts: pointer to argument text pointer
  1484.  * Returns: argument
  1485.  */
  1486. char *snarf (char **arg)
  1487. {
  1488.   unsigned long i;
  1489.   char c;
  1490.   char *s = parse_astring (arg,&i,&c);
  1491.   return ((c == ' ') || !c) ? s : NIL;
  1492. }
  1493. /* Snarf a list command argument (simple jacket into parse_astring())
  1494.  * Accepts: pointer to argument text pointer
  1495.  * Returns: argument
  1496.  */
  1497. char *snarf_list (char **arg)
  1498. {
  1499.   unsigned long i;
  1500.   char c;
  1501.   char *s,*t;
  1502.   if (!*arg) return NIL; /* better be an argument */
  1503.   switch (**arg) {
  1504.   default: /* atom and/or wildcard chars */
  1505.     for (s = t = *arg, i = 0;
  1506.  (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
  1507.  (*t != '"') && (*t != '\'); ++t,++i);
  1508.     if (c = *t) { /* have a delimiter? */
  1509.       *t++ = ''; /* stomp on it */
  1510.       *arg = t; /* update argument pointer */
  1511.     }
  1512.     else *arg = NIL;
  1513.     break;
  1514.   case ')': case '\': case '': case ' ':
  1515.     return NIL; /* empty name is bogus */
  1516.   case '"': /* quoted string? */
  1517.   case '{': /* or literal? */
  1518.     s = parse_astring (arg,&i,&c);
  1519.     break;
  1520.   }
  1521.   return ((c == ' ') || !c) ? s : NIL;
  1522. }
  1523. /* Get a list of header lines
  1524.  * Accepts: pointer to string pointer
  1525.  *     pointer to list flag
  1526.  * Returns: string list
  1527.  */
  1528. STRINGLIST *parse_stringlist (char **s,int *list)
  1529. {
  1530.   char c = ' ',*t;
  1531.   unsigned long i;
  1532.   STRINGLIST *ret = NIL,*cur = NIL;
  1533.   if (*s && **s == '(') { /* proper list? */
  1534.     ++*s; /* for each item in list */
  1535.     while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
  1536. /* get new block */
  1537.       if (cur) cur = cur->next = mail_newstringlist ();
  1538.       else cur = ret = mail_newstringlist ();
  1539. /* note text */
  1540.       cur->text.data = (unsigned char *) fs_get (i + 1);
  1541.       memcpy (cur->text.data,t,i);
  1542.       cur->text.size = i; /* and size */
  1543.     }
  1544. /* must be end of list */
  1545.     if (c != ')') mail_free_stringlist (&ret);
  1546.   }
  1547.   if (t = *s) { /* need to reload strtok() state? */
  1548. /* end of a list? */
  1549.     if (*list && (*t == ')') && !t[1]) *list = NIL;
  1550.     else {
  1551.       *--t = ' '; /* patch a space back in */
  1552.       *--t = 'x'; /* and a hokey character before that */
  1553.       t = strtok (t," "); /* reset to *s */
  1554.     }
  1555.   }
  1556.   return ret;
  1557. }
  1558. /* Parse search criteria
  1559.  * Accepts: search program to write criteria into
  1560.  *     pointer to argument text pointer
  1561.  *     maximum message number
  1562.  *     maximum UID
  1563.  *     logical nesting depth
  1564.  * Returns: T if success, NIL if error
  1565.  */
  1566. long parse_criteria (SEARCHPGM *pgm,char **arg,unsigned long maxmsg,
  1567.      unsigned long maxuid,unsigned long depth)
  1568. {
  1569.   if (arg && *arg) { /* must be an argument */
  1570. /* parse criteria */
  1571.     do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
  1572. /* as long as a space delimiter */
  1573.     while (**arg == ' ' && (*arg)++);
  1574. /* failed if not end of criteria */
  1575.     if (**arg && **arg != ')') return NIL;
  1576.   }
  1577.   return T; /* success */
  1578. }
  1579. /* Parse a search criterion
  1580.  * Accepts: search program to write criterion into
  1581.  *     pointer to argument text pointer
  1582.  *     maximum message number
  1583.  *     maximum UID
  1584.  *     logical nesting depth
  1585.  * Returns: T if success, NIL if error
  1586.  */
  1587. long parse_criterion (SEARCHPGM *pgm,char **arg,unsigned long maxmsg,
  1588.       unsigned long maxuid,unsigned long depth)
  1589. {
  1590.   unsigned long i;
  1591.   char c = NIL,*s,*t,*v,*tail,*del;
  1592.   SEARCHSET **set;
  1593.   SEARCHPGMLIST **not;
  1594.   SEARCHOR **or;
  1595.   SEARCHHEADER **hdr;
  1596.   long ret = NIL;
  1597. /* better be an argument */
  1598.   if ((depth > 50) || !(arg && *arg));
  1599.   else if (**arg == '(') { /* list of criteria? */
  1600.     (*arg)++; /* yes, parse the criteria */
  1601.     if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
  1602.       (*arg)++; /* skip closing paren */
  1603.       ret = T; /* successful parse of list */
  1604.     }
  1605.   }
  1606.   else { /* find end of criterion */
  1607.     if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
  1608.     c = *(del = tail); /* remember the delimiter */
  1609.     *del = ''; /* tie off criterion */
  1610.     switch (*ucase (s)) { /* dispatch based on character */
  1611.     case '*': /* sequence */
  1612.     case '0': case '1': case '2': case '3': case '4':
  1613.     case '5': case '6': case '7': case '8': case '9':
  1614.       if (*(set = &pgm->msgno)){/* already a sequence? */
  1615. /* silly, but not as silly as the client! */
  1616. for (not = &pgm->not; *not; not = &(*not)->next);
  1617. *not = mail_newsearchpgmlist ();
  1618. set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
  1619.       }
  1620.       ret = crit_set (set,&s,maxmsg) && (tail == s);
  1621.       break;
  1622.     case 'A': /* possible ALL, ANSWERED */
  1623.       if (!strcmp (s+1,"LL")) ret = T;
  1624.       else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
  1625.       break;
  1626.     case 'B': /* possible BCC, BEFORE, BODY */
  1627.       if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
  1628. ret = crit_string (&pgm->bcc,&tail);
  1629.       else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
  1630. ret = crit_date (&pgm->before,&tail);
  1631.       else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
  1632. ret = crit_string (&pgm->body,&tail);
  1633.       break;
  1634.     case 'C': /* possible CC */
  1635.       if (!strcmp (s+1,"C") && c == ' ' && *++tail)
  1636. ret = crit_string (&pgm->cc,&tail);
  1637.       break;
  1638.     case 'D': /* possible DELETED */
  1639.       if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
  1640.       if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
  1641.       break;
  1642.     case 'F': /* possible FLAGGED, FROM */
  1643.       if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
  1644.       else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
  1645. ret = crit_string (&pgm->from,&tail);
  1646.       break;
  1647.     case 'H': /* possible HEADER */
  1648.       if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
  1649.   (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
  1650.   (t = parse_astring (&v,&i,&c))) {
  1651. for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
  1652. *hdr = mail_newsearchheader (s,t);
  1653. /* update tail, restore delimiter */
  1654. *(tail = v ? v - 1 : t + i) = c;
  1655. ret = T; /* success */
  1656.       }
  1657.       break;
  1658.     case 'K': /* possible KEYWORD */
  1659.       if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
  1660. ret = crit_string (&pgm->keyword,&tail);
  1661.       break;
  1662.     case 'L':
  1663.       if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
  1664. ret = crit_number (&pgm->larger,&tail);
  1665.       break;
  1666.     case 'N': /* possible NEW, NOT */
  1667.       if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
  1668.       else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
  1669. for (not = &pgm->not; *not; not = &(*not)->next);
  1670. *not = mail_newsearchpgmlist ();
  1671. ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
  1672.       }
  1673.       break;
  1674.     case 'O': /* possible OLD, ON */
  1675.       if (!strcmp (s+1,"LD")) ret = pgm->old = T;
  1676.       else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
  1677. ret = crit_date (&pgm->on,&tail);
  1678.       else if (!strcmp (s+1,"R") && c == ' ') {
  1679. for (or = &pgm->or; *or; or = &(*or)->next);
  1680. *or = mail_newsearchor ();
  1681. ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
  1682.  depth+1) && *tail == ' ' && *++tail &&
  1683.     parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
  1684.       }
  1685.       break;
  1686.     case 'R': /* possible RECENT */
  1687.       if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
  1688.       break;
  1689.     case 'S': /* possible SEEN, SINCE, SUBJECT */
  1690.       if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
  1691.       else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
  1692. ret = crit_date (&pgm->sentbefore,&tail);
  1693.       else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
  1694. ret = crit_date (&pgm->senton,&tail);
  1695.       else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
  1696. ret = crit_date (&pgm->sentsince,&tail);
  1697.       else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
  1698. ret = crit_date (&pgm->since,&tail);
  1699.       else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
  1700. ret = crit_number (&pgm->smaller,&tail);
  1701.       else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
  1702. ret = crit_string (&pgm->subject,&tail);
  1703.       break;
  1704.     case 'T': /* possible TEXT, TO */
  1705.       if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
  1706. ret = crit_string (&pgm->text,&tail);
  1707.       else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
  1708. ret = crit_string (&pgm->to,&tail);
  1709.       break;
  1710.     case 'U': /* possible UID, UN* */
  1711.       if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
  1712. if (*(set = &pgm->uid)){/* already a sequence? */
  1713. /* silly, but not as silly as the client! */
  1714.   for (not = &pgm->not; *not; not = &(*not)->next);
  1715.   *not = mail_newsearchpgmlist ();
  1716.   set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
  1717. }
  1718. ret = crit_set (set,&tail,maxuid);
  1719.       }
  1720.       else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
  1721.       else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
  1722.       else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
  1723.       else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
  1724.       else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
  1725. ret = crit_string (&pgm->unkeyword,&tail);
  1726.       else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
  1727.       break;
  1728.     default: /* oh dear */
  1729.       break;
  1730.     }
  1731.     if (ret) { /* only bother if success */
  1732.       *del = c; /* restore delimiter */
  1733.       *arg = tail; /* update argument pointer */
  1734.     }
  1735.   }
  1736.   return ret; /* return more to come */
  1737. }
  1738. /* Parse a search date criterion
  1739.  * Accepts: date to write into
  1740.  *     pointer to argument text pointer
  1741.  * Returns: T if success, NIL if error
  1742.  */
  1743. long crit_date (unsigned short *date,char **arg)
  1744. {
  1745. /* handle quoted form */
  1746.   if (**arg != '"') return crit_date_work (date,arg);
  1747.   (*arg)++; /* skip past opening quote */
  1748.   if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
  1749.   (*arg)++; /* skip closing quote */
  1750.   return T;
  1751. }
  1752. /* Worker routine to parse a search date criterion
  1753.  * Accepts: date to write into
  1754.  *     pointer to argument text pointer
  1755.  * Returns: T if success, NIL if error
  1756.  */
  1757. long crit_date_work (unsigned short *date,char **arg)
  1758. {
  1759.   int d,m,y;
  1760. /* day */
  1761.   if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
  1762.     if (d == ' ') d = 0; /* leading space */
  1763.     else d -= '0'; /* first digit */
  1764.     if (isdigit (**arg)) { /* if a second digit */
  1765.       d *= 10; /* slide over first digit */
  1766.       d += *(*arg)++ - '0'; /* second digit */
  1767.     }
  1768.     if ((**arg == '-') && (y = *++(*arg))) {
  1769.       m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
  1770.       if ((y = *++(*arg))) {
  1771. m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
  1772. if ((y = *++(*arg))) {
  1773.   m += (y >= 'a' ? y - 'a' : y - 'A');
  1774.   switch (m) { /* determine the month */
  1775.   case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1776.   case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1777.   case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1778.   case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1779.   case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1780.   case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1781.   case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1782.   case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1783.   case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1784.   case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
  1785.   case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
  1786.   case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
  1787.   default: return NIL;
  1788.   }
  1789.   if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
  1790.     y = 0; /* init year */
  1791.     do {
  1792.       y *= 10; /* add this number */
  1793.       y += *(*arg)++ - '0';
  1794.     }
  1795.     while (isdigit (**arg));
  1796. /* minimal validity check of date */
  1797.     if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL; 
  1798. /* Tenex/ARPAnet began in 1969 */
  1799.     if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1800. /* return value */
  1801.     *date = ((y - BASEYEAR) << 9) + (m << 5) + d;
  1802.     return T; /* success */
  1803.   }
  1804. }
  1805.       }
  1806.     }
  1807.   }
  1808.   return NIL; /* else error */
  1809. }
  1810. /* Parse a search set criterion
  1811.  * Accepts: set to write into
  1812.  *     pointer to argument text pointer
  1813.  *     maximum value permitted
  1814.  * Returns: T if success, NIL if error
  1815.  */
  1816. long crit_set (SEARCHSET **set,char **arg,unsigned long maxima)
  1817. {
  1818.   unsigned long i;
  1819.   *set = mail_newsearchset (); /* instantiate a new search set */
  1820.   if (**arg == '*') { /* maxnum? */
  1821.     (*arg)++; /* skip past that number */
  1822.     (*set)->first = maxima;
  1823.   }
  1824.   else if (crit_number (&i,arg) && i) (*set)->first = i;
  1825.   else return NIL; /* bogon */
  1826.   switch (**arg) { /* decide based on delimiter */
  1827.   case ':': /* sequence range */
  1828.     if (*++(*arg) == '*') { /* maxnum? */
  1829.       (*arg)++; /* skip past that number */
  1830.       (*set)->last -= maxima;
  1831.     }
  1832.     else if (crit_number (&i,arg) && i) {
  1833.       if (i < (*set)->first) { /* backwards range */
  1834. (*set)->last = (*set)->first;
  1835. (*set)->first = i;
  1836.       }
  1837.       else (*set)->last = i; /* set last number */
  1838.     }
  1839.     else return NIL; /* bogon */
  1840.     if (**arg != ',') break; /* drop into comma case if comma seen */
  1841.   case ',':
  1842.     (*arg)++; /* skip past delimiter */
  1843.     return crit_set (&(*set)->next,arg,maxima);
  1844.   default:
  1845.     break;
  1846.   }
  1847.   return T; /* return success */
  1848. }
  1849. /* Parse a search number criterion
  1850.  * Accepts: number to write into
  1851.  *     pointer to argument text pointer
  1852.  * Returns: T if success, NIL if error
  1853.  */
  1854. long crit_number (unsigned long *number,char **arg)
  1855. {
  1856.   if (!isdigit (**arg)) return NIL;
  1857.   *number = 0;
  1858.   while (isdigit (**arg)) { /* found a digit? */
  1859.     *number *= 10; /* add a decade */
  1860.     *number += *(*arg)++ - '0'; /* add number */
  1861.   }
  1862.   return T;
  1863. }
  1864. /* Parse a search string criterion
  1865.  * Accepts: date to write into
  1866.  *     pointer to argument text pointer
  1867.  * Returns: T if success, NIL if error
  1868.  */
  1869. long crit_string (STRINGLIST **string,char **arg)
  1870. {
  1871.   unsigned long i;
  1872.   char c;
  1873.   char *s = parse_astring (arg,&i,&c);
  1874.   if (!s) return NIL;
  1875. /* find tail of list */
  1876.   while (*string) string = &(*string)->next;
  1877.   *string = mail_newstringlist ();
  1878.   (*string)->text.data = (unsigned char *) fs_get (i + 1);
  1879.   memcpy ((*string)->text.data,s,i);
  1880.   (*string)->text.data[i] = '';
  1881.   (*string)->text.size = i;
  1882. /* if end of arguments, wrap it up here */
  1883.   if (!*arg) *arg = (char *) (*string)->text.data + i;
  1884.   else (*--(*arg) = c); /* back up pointer, restore delimiter */
  1885.   return T;
  1886. }
  1887. /* Fetch message data
  1888.  * Accepts: string of data items to be fetched (must be writeable)
  1889.  *     UID fetch flag
  1890.  */
  1891. #define MAXFETCH 100
  1892. void fetch (char *t,unsigned long uid)
  1893. {
  1894.   fetchfn_t f[MAXFETCH +2];
  1895.   void *fa[MAXFETCH + 2];
  1896.   int k;
  1897.   memset ((void *) f,NIL,sizeof (f));
  1898.   memset ((void *) fa,NIL,sizeof (fa));
  1899.   fetch_work (t,uid,f,fa); /* do the work */
  1900. /* clean up arguments */
  1901.   for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
  1902. }
  1903. /* Fetch message data worker routine
  1904.  * Accepts: string of data items to be fetched (must be writeable)
  1905.  *     UID fetch flag
  1906.  *     function dispatch vector
  1907.  *     function argument vector
  1908.  */
  1909. void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
  1910. {
  1911.   char *s,*v;
  1912.   unsigned long i;
  1913.   unsigned long k = 0;
  1914.   BODY *b;
  1915.   int list = NIL;
  1916.   int parse_envs = NIL;
  1917.   int parse_bodies = NIL;
  1918.   if (uid) { /* need to fetch UIDs? */
  1919.     fa[k] = NIL; /* no argument */
  1920.     f[k++] = fetch_uid; /* push a UID fetch on the stack */
  1921.   }
  1922. /* process macros */
  1923.   if (!strcmp (ucase (t),"ALL"))
  1924.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  1925.   else if (!strcmp (t,"FULL"))
  1926.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  1927.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  1928.   if (list = (*t == '(')) t++; /* skip open paren */
  1929.   if (s = strtok (t," ")) do { /* parse attribute list */
  1930.     if (list && (i = strlen (s)) && (s[i-1] == ')')) {
  1931.       list = NIL; /* done with list */
  1932.       s[i-1] = ''; /* tie off last item */
  1933.     }
  1934.     fa[k] = NIL; /* default to no argument */
  1935.     if (!strcmp (s,"UID")) { /* no-op if implicit */
  1936.       if (!uid) f[k++] = fetch_uid;
  1937.     }
  1938.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  1939.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  1940.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  1941.     else if (!strcmp (s,"ENVELOPE")) {
  1942.       parse_envs = T; /* we will need to parse envelopes */
  1943.       f[k++] = fetch_envelope;
  1944.     }
  1945.     else if (!strcmp (s,"BODY")) {
  1946.       parse_envs = parse_bodies = T;
  1947.       f[k++] = fetch_body;
  1948.     }
  1949.     else if (!strcmp (s,"BODYSTRUCTURE")) {
  1950.       parse_envs = parse_bodies = T;
  1951.       f[k++] = fetch_bodystructure;
  1952.     }
  1953.     else if (!strcmp (s,"RFC822") || !strcmp (s,"RFC822.PEEK")) {
  1954.       fa[k] = s[6] ? (void *) FT_PEEK : NIL;
  1955.       f[k++] = fetch_rfc822;
  1956.     }
  1957.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  1958.     else if (!strcmp (s,"RFC822.TEXT") || !strcmp (s,"RFC822.TEXT.PEEK")) {
  1959.       fa[k] = s[11] ? (void *) FT_PEEK : NIL;
  1960.       f[k++] = fetch_rfc822_text;
  1961.     }
  1962.     else if (!strcmp (s,"RFC822.HEADER.LINES") &&
  1963.      (v = strtok (NIL,"1512")) &&
  1964.      (fa[k] = (void *) parse_stringlist (&v,&list)))
  1965.       f[k++] = fetch_rfc822_header_lines;
  1966.     else if (!strcmp (s,"RFC822.HEADER.LINES.NOT") &&
  1967.      (v = strtok (NIL,"1512")) &&
  1968.      (fa[k] = (void *) parse_stringlist (&v,&list)))
  1969.       f[k++] = fetch_rfc822_header_lines_not;
  1970.     else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10)) {
  1971.       TEXTARGS *ta = (TEXTARGS *)
  1972. memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
  1973.       if (s[4] == '.') { /* wanted peek? */
  1974. ta->flags = FT_PEEK;
  1975. s += 10; /* skip to section specifier */
  1976.       }
  1977.       else s += 5; /* skip to section specifier */
  1978.       f[k] = fetch_body_part_contents;
  1979.       if (*(v = s) != ']') { /* non-empty section specifier? */
  1980. if (isdigit (*v)) { /* have section specifier? */
  1981. /* need envelopes and bodies */
  1982.   parse_envs = parse_bodies = T;
  1983.   while (isdigit (*v)) /* scan to end of section specifier */
  1984.     if ((*++v == '.') && isdigit (v[1])) v++;
  1985. /* any IMAP4rev1 stuff following? */
  1986.   if ((*v == '.') && isalpha (v[1])) {
  1987.     *v++ = ''; /* yes, tie off section specifier */
  1988.     if (!strncmp (v,"MIME",4)) {
  1989.       v += 4; /* found <section>.MIME */
  1990.       f[k] = fetch_body_part_mime;
  1991.     }
  1992.   }
  1993.   else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */
  1994.     fs_give ((void **) &ta);/* clean up */
  1995.     response = "%.80s BAD Syntax error in section specifier1512";
  1996.     return;
  1997.   }
  1998. }
  1999. if (*v != ']') { /* IMAP4rev1 stuff here? */
  2000.   if (!strncmp (v,"HEADER",6)) {
  2001.     *v = ''; /* tie off in case top level */
  2002.     v += 6; /* found [<section>.]HEADER */
  2003.     f[k] = fetch_body_part_header;
  2004. /* partial headers wanted? */
  2005.     if (!strncmp (v,".FIELDS",7)) {
  2006.       v += 7; /* yes */
  2007.       if (!strncmp (v,".NOT",4)) {
  2008. v += 4; /* want to exclude named headers */
  2009. ta->flags |= FT_NOT;
  2010.       }
  2011.       if (*v || !(v = strtok (NIL,"1512")) ||
  2012.   !(ta->lines = parse_stringlist (&v,&list))) {
  2013. fs_give ((void **) &ta);/* clean up */
  2014. response = "%.80s BAD Syntax error in header fields1512";
  2015. return;
  2016.       }
  2017.     }
  2018.   }
  2019.   else if (!strncmp (v,"TEXT",4)) {
  2020.     *v = ''; /* tie off in case top level */
  2021.     v += 4; /* found [<section>.]TEXT */
  2022.     f[k] = fetch_body_part_text;
  2023.   }
  2024.   else {
  2025.     fs_give ((void **) &ta);/* clean up */
  2026.     response = "%.80s BAD Unknown section text specifier1512";
  2027.     return;
  2028.   }
  2029. }
  2030.       }
  2031. /* tie off section */
  2032.       if (*v == ']') *v++ = '';
  2033.       else { /* bogon */
  2034. if (ta->lines) mail_free_stringlist (&ta->lines);
  2035. fs_give ((void **) &ta);/* clean up */
  2036. response = "%.80s BAD Section specifier not terminated1512";
  2037. return;
  2038.       }
  2039.       if (*v == '<') { /* partial specifier? */
  2040. ta->first = strtoul (v+1,&v,10);
  2041. if ((*v++ != '.') || !(ta->last = strtoul (v,&v,10)) || (*v++ != '>')){
  2042.   if (ta->lines) mail_free_stringlist (&ta->lines);
  2043.   fs_give ((void **) &ta);
  2044.   response ="%.80s BAD Syntax error in partial text specifier1512";
  2045.   return;
  2046. }
  2047.       }
  2048.       switch (*v) { /* what's there now? */
  2049.       case ' ': /* more follows */
  2050. *--v = ' '; /* patch a space back in */
  2051. *--v = 'x'; /* and a hokey character before that */
  2052. strtok (v," "); /* reset strtok mechanism */
  2053. break;
  2054.       case '': /* none */
  2055. break;
  2056.       case ')': /* end of list */
  2057. if (list && !v[1]) { /* make sure of that */
  2058.   list = NIL;
  2059.   strtok (v," "); /* reset strtok mechanism */
  2060.   break; /* all done */
  2061. }
  2062. /* otherwise it's a bogon, drop in */
  2063.       default: /* bogon */
  2064. if (ta->lines) mail_free_stringlist (&ta->lines);
  2065. fs_give ((void **) &ta);
  2066. response = "%.80s BAD Syntax error after section specifier1512";
  2067. return;
  2068.       }
  2069. /* make copy of section specifier */
  2070.       if (s && *s) ta->section = cpystr (s);
  2071.       fa[k++] = (void *) ta; /* set argument */
  2072.     }
  2073.     else { /* unknown attribute */
  2074.       response = badatt;
  2075.       return;
  2076.     }
  2077.   } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list);
  2078.   else {
  2079.     response = misarg; /* missing attribute list */
  2080.     return;
  2081.   }
  2082.   if (s) { /* too many attributes? */
  2083.     response = "%.80s BAD Excessively complex FETCH attribute list1512";
  2084.     return;
  2085.   }
  2086.   if (list) { /* too many attributes? */
  2087.     response = "%.80s BAD Unterminated FETCH attribute list1512";
  2088.     return;
  2089.   }
  2090.   f[k] = NIL; /* tie off attribute list */
  2091. /* c-client clobbers sequence, use spare */
  2092.   for (i = 1; i <= nmsgs; i++)
  2093.     mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
  2094. /* for each requested message */
  2095.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare) {
  2096. /* parse envelope, set body, do warnings */
  2097.     if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  2098.     quell_events = T; /* can't do any events now */
  2099.     PSOUT ("* "); /* leader */
  2100.     pnum (i);
  2101.     PSOUT (" FETCH (");
  2102.     (*f[0]) (i,fa[0]); /* do first attribute */
  2103.     for (k = 1; f[k]; k++) { /* for each subsequent attribute */
  2104.       PBOUT (' '); /* delimit with space */
  2105.       (*f[k]) (i,fa[k]); /* do that attribute */
  2106.     }
  2107.     PSOUT (")1512"); /* trailer */
  2108.     quell_events = NIL; /* events alright now */
  2109.   }
  2110. }
  2111. /* Fetch message body structure (extensible)
  2112.  * Accepts: message number
  2113.  *     extra argument
  2114.  */
  2115. void fetch_bodystructure (unsigned long i,void *args)
  2116. {
  2117.   BODY *body;
  2118.   mail_fetchstructure (stream,i,&body);
  2119.   PSOUT ("BODYSTRUCTURE ");
  2120.   pbodystructure (body); /* output body */
  2121. }
  2122. /* Fetch message body structure (non-extensible)
  2123.  * Accepts: message number
  2124.  *     extra argument
  2125.  */
  2126. void fetch_body (unsigned long i,void *args)
  2127. {
  2128.   BODY *body;
  2129.   mail_fetchstructure (stream,i,&body);
  2130.   PSOUT ("BODY "); /* output attribute */
  2131.   pbody (body); /* output body */
  2132. }
  2133. /* Fetch body part MIME header
  2134.  * Accepts: message number
  2135.  *     extra argument
  2136.  */
  2137. void fetch_body_part_mime (unsigned long i,void *args)
  2138. {
  2139.   TEXTARGS *ta = (TEXTARGS *) args;
  2140.   if (i) { /* do work? */
  2141.     SIZEDTEXT st;
  2142.     unsigned long uid = mail_uid (stream,i);
  2143.     char *tmp = (char *) fs_get (100 + strlen (ta->section));
  2144.     sprintf (tmp,"BODY[%s.MIME]",ta->section);
  2145. /* try to use remembered text */
  2146.     if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  2147.     else { /* get data */
  2148.       st.data = (unsigned char *)
  2149. mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
  2150.       if (ta->first || ta->last) remember (uid,tmp,&st);
  2151.     }
  2152.     pbodypartstring (i,tmp,&st,ta);
  2153.     fs_give ((void **) &tmp);
  2154.   }
  2155.   else { /* clean up the arguments */
  2156.     fs_give ((void **) &ta->section);
  2157.     fs_give ((void **) &args);
  2158.   }
  2159. }
  2160. /* Fetch body part contents
  2161.  * Accepts: message number
  2162.  *     extra argument
  2163.  */
  2164. void fetch_body_part_contents (unsigned long i,void *args)
  2165. {
  2166.   TEXTARGS *ta = (TEXTARGS *) args;
  2167.   if (i) { /* do work? */
  2168.     SIZEDTEXT st;
  2169.     char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
  2170.     unsigned long uid = mail_uid (stream,i);
  2171.     sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
  2172. /* try to use remembered text */
  2173.     if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  2174.     else { /* get data */
  2175.       st.data = (unsigned char *)
  2176. mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
  2177.       if (ta->first || ta->last) remember (uid,tmp,&st);
  2178.     }
  2179.     pbodypartstring (i,tmp,&st,ta);
  2180.     fs_give ((void **) &tmp);
  2181.   }
  2182.   else { /* clean up the arguments */
  2183.     if (ta->section) fs_give ((void **) &ta->section);
  2184.     fs_give ((void **) &args);
  2185.   }
  2186. }
  2187. /* Fetch MESSAGE/RFC822 body part header
  2188.  * Accepts: message number
  2189.  *     extra argument
  2190.  */
  2191. void fetch_body_part_header (unsigned long i,void *args)
  2192. {
  2193.   TEXTARGS *ta = (TEXTARGS *) args;
  2194.   unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
  2195.   STRINGLIST *s;
  2196.   for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
  2197.   if (i) { /* do work? */
  2198.     SIZEDTEXT st;
  2199.     char *tmp = (char *) fs_get (len);
  2200.     PSOUT ("BODY[");
  2201. /* output attribute */
  2202.     if (ta->section && *ta->section) {
  2203.       PSOUT (ta->section);
  2204.       PBOUT ('.');
  2205.     }
  2206.     PSOUT ("HEADER");
  2207.     if (ta->lines) {
  2208.       PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
  2209.       pstringlist (ta->lines);
  2210.     }
  2211.     strcpy (tmp,"]"); /* close section specifier */
  2212.     st.data = (unsigned char *) /* get data (no hope in using remember here) */
  2213.       mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
  2214.     pbodypartstring (i,tmp,&st,ta);
  2215.     fs_give ((void **) &tmp);
  2216.   }
  2217.   else { /* clean up the arguments */
  2218.     if (ta->lines) mail_free_stringlist (&ta->lines);
  2219.     if (ta->section) fs_give ((void **) &ta->section);
  2220.     fs_give ((void **) &args);
  2221.   }
  2222. }
  2223. /* Fetch MESSAGE/RFC822 body part text
  2224.  * Accepts: message number
  2225.  *     extra argument
  2226.  */
  2227. void fetch_body_part_text (unsigned long i,void *args)
  2228. {
  2229.   TEXTARGS *ta = (TEXTARGS *) args;
  2230.   if (i) { /* do work? */
  2231.     SIZEDTEXT st;
  2232.     char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
  2233.     unsigned long uid = mail_uid (stream,i);
  2234. /* output attribute */
  2235.     if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
  2236.     else strcpy (tmp,"BODY[TEXT]");
  2237. /* try to use remembered text */
  2238.     if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  2239.     else { /* get data */
  2240.       st.data = (unsigned char *)
  2241. mail_fetch_text (stream,i,ta->section,&st.size,ta->flags);
  2242.       if (ta->first || ta->last) remember (uid,tmp,&st);
  2243.     }
  2244.     pbodypartstring (i,tmp,&st,ta);
  2245.     fs_give ((void **) &tmp);
  2246.   }
  2247.   else { /* clean up the arguments */
  2248.     if (ta->section) fs_give ((void **) &ta->section);
  2249.     fs_give ((void **) &args);
  2250.   }
  2251. }
  2252. /* Remember body part text for subsequent partial fetching
  2253.  * Accepts: message UID
  2254.  *     body part id
  2255.  *     text
  2256.  */
  2257. void remember (unsigned long uid,char *id,SIZEDTEXT *st)
  2258. {
  2259.   lastuid = uid; /* remember UID */
  2260.   if (lastid) fs_give ((void **) &lastid);
  2261.   lastid = cpystr (id); /* remember body part id */
  2262.   if (lastst.data) fs_give ((void **) &lastst.data);
  2263. /* remember text */
  2264.   lastst.data = (unsigned char *) cpystr ((char *) st->data);
  2265.   lastst.size = st->size;
  2266. }
  2267. /* Fetch envelope
  2268.  * Accepts: message number
  2269.  *     extra argument
  2270.  */
  2271. void fetch_envelope (unsigned long i,void *args)
  2272. {
  2273.   ENVELOPE *env = mail_fetchenvelope (stream,i);
  2274.   PSOUT ("ENVELOPE "); /* output attribute */
  2275.   penv (env); /* output envelope */
  2276. }
  2277. /* Fetch matching header lines
  2278.  * Accepts: message number
  2279.  *     extra argument
  2280.  */
  2281. void fetch_rfc822_header_lines (unsigned long i,void *args)
  2282. {
  2283.   STRINGLIST *sa = (STRINGLIST *) args;
  2284.   if (i) { /* do work? */
  2285.     SIZEDTEXT st;
  2286.     st.data = (unsigned char *)
  2287.       mail_fetch_header (stream,i,NIL,sa,&st.size,FT_PEEK);
  2288. /* output literal */
  2289.     pnstring ("RFC822.HEADER",&st);
  2290.   }
  2291.   else mail_free_stringlist (&sa);
  2292. }
  2293. /* Fetch not-matching header lines
  2294.  * Accepts: message number
  2295.  *     extra argument
  2296.  */
  2297. void fetch_rfc822_header_lines_not (unsigned long i,void *args)
  2298. {
  2299.   STRINGLIST *sa = (STRINGLIST *) args;
  2300.   if (i) { /* do work? */
  2301.     SIZEDTEXT st;
  2302.     st.data = (unsigned char *)
  2303.       mail_fetch_header (stream,i,NIL,sa,&st.size,FT_NOT | FT_PEEK);
  2304. /* output literal */
  2305.     pnstring ("RFC822.HEADER",&st);
  2306.   }
  2307.   else mail_free_stringlist (&sa);
  2308. }
  2309. /* Fetch flags
  2310.  * Accepts: message number
  2311.  *     extra argument
  2312.  */
  2313. void fetch_flags (unsigned long i,void *args)
  2314. {
  2315.   unsigned long u;
  2316.   char *t,tmp[MAILTMPLEN];
  2317.   int c = NIL;
  2318.   MESSAGECACHE *elt = mail_elt (stream,i);
  2319.   if (!elt->valid) { /* have valid flags yet? */
  2320.     sprintf (tmp,"%lu",i);
  2321.     mail_fetch_flags (stream,tmp,NIL);
  2322.   }
  2323.   PSOUT ("FLAGS ("); /* output attribute */
  2324. /* output system flags */
  2325.   if (elt->recent) put_flag (&c,"\Recent");
  2326.   if (elt->seen) put_flag (&c,"\Seen");
  2327.   if (elt->deleted) put_flag (&c,"\Deleted");
  2328.   if (elt->flagged) put_flag (&c,"\Flagged");
  2329.   if (elt->answered) put_flag (&c,"\Answered");
  2330.   if (elt->draft) put_flag (&c,"\Draft");
  2331.   if (u = elt->user_flags) do /* any user flags? */
  2332.     if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
  2333.   while (u); /* until no more user flags */
  2334.   PBOUT (')'); /* end of flags */
  2335.   elt->spare2 = NIL; /* we've sent the update */
  2336. }
  2337. /* Output a flag
  2338.  * Accepts: pointer to current delimiter character
  2339.  *     flag to output
  2340.  * Changes delimiter character to space
  2341.  */
  2342. void put_flag (int *c,char *s)
  2343. {
  2344.   if (*c) PBOUT (*c); /* put delimiter */
  2345.   PSOUT (s); /* dump flag */
  2346.   *c = ' '; /* change delimiter if necessary */
  2347. }
  2348. /* Output flags if was unseen
  2349.  * Accepts: message number
  2350.  *     prior value of Seen flag
  2351.  */
  2352. void changed_flags (unsigned long i,int f)
  2353. {
  2354. /* was unseen, now seen? */
  2355.   if (!f && mail_elt (stream,i)->seen) {
  2356.     PBOUT (' '); /* yes, delimit with space */
  2357.     fetch_flags (i,NIL); /* output flags */
  2358.   }
  2359. }
  2360. /* Fetch message internal date
  2361.  * Accepts: message number
  2362.  *     extra argument
  2363.  */
  2364. void fetch_internaldate (unsigned long i,void *args)
  2365. {
  2366.   char tmp[MAILTMPLEN];
  2367.   MESSAGECACHE *elt = mail_elt (stream,i);
  2368.   if (!elt->day) { /* have internal date yet? */
  2369.     sprintf (tmp,"%lu",i);
  2370.     mail_fetch_fast (stream,tmp,NIL);
  2371.   }
  2372.   PSOUT ("INTERNALDATE "");
  2373.   PSOUT (mail_date (tmp,elt));
  2374.   PBOUT ('"');
  2375. }
  2376. /* Fetch unique identifier
  2377.  * Accepts: message number
  2378.  *     extra argument
  2379.  */
  2380. void fetch_uid (unsigned long i,void *args)
  2381. {
  2382.   PSOUT ("UID ");
  2383.   pnum (mail_uid (stream,i));
  2384. }
  2385. /* Fetch complete RFC-822 format message
  2386.  * Accepts: message number
  2387.  *     extra argument
  2388.  */
  2389. void fetch_rfc822 (unsigned long i,void *args)
  2390. {
  2391.   if (i) { /* do work? */
  2392.     int f = mail_elt (stream,i)->seen;
  2393. #if 0
  2394.     SIZEDTEXT st;
  2395.     st.data = (unsigned char *)
  2396.       mail_fetch_message (stream,i,&st.size,(long) args);
  2397.     pnstring ("RFC822",st);
  2398. #else
  2399.     /* Yes, this version is bletcherous, but mail_fetch_message() requires
  2400.        too much memory */
  2401.     SIZEDTEXT txt,hdr;
  2402.     char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
  2403.     hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
  2404.     txt.data = (unsigned char *)
  2405.       mail_fetch_text (stream,i,NIL,&txt.size,(long) args);
  2406.     PSOUT ("RFC822 {");
  2407.     pnum (hdr.size + txt.size);
  2408.     PSOUT ("}1512");
  2409.     ptext (&hdr);
  2410.     ptext (&txt);
  2411.     fs_give ((void **) &hdr.data);
  2412. #endif
  2413.     changed_flags (i,f); /* output changed flags */
  2414.   }
  2415. }
  2416. /* Fetch RFC-822 header
  2417.  * Accepts: message number
  2418.  *     extra argument
  2419.  */
  2420. void fetch_rfc822_header (unsigned long i,void *args)
  2421. {
  2422.   SIZEDTEXT st;
  2423.   st.data = (unsigned char *)
  2424.     mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
  2425.   pnstring ("RFC822.HEADER",&st);
  2426. }
  2427. /* Fetch RFC-822 message length
  2428.  * Accepts: message number
  2429.  *     extra argument
  2430.  */
  2431. void fetch_rfc822_size (unsigned long i,void *args)
  2432. {
  2433.   char tmp[MAILTMPLEN];
  2434.   MESSAGECACHE *elt = mail_elt (stream,i);
  2435.   if (!elt->rfc822_size) { /* have message size yet? */
  2436.     sprintf (tmp,"%lu",i);
  2437.     mail_fetch_fast (stream,tmp,NIL);
  2438.   }
  2439.   PSOUT ("RFC822.SIZE ");
  2440.   pnum (elt->rfc822_size);
  2441. }
  2442. /* Fetch RFC-822 text only
  2443.  * Accepts: message number
  2444.  *     extra argument
  2445.  */
  2446. void fetch_rfc822_text (unsigned long i,void *args)
  2447. {
  2448.   if (i) { /* do work? */
  2449.     int f = mail_elt (stream,i)->seen;
  2450.     SIZEDTEXT st;
  2451.     st.data = (unsigned char *)
  2452.       mail_fetch_text (stream,i,NIL,&st.size,(long) args);
  2453.     pnstring ("RFC822.TEXT",&st);
  2454.     changed_flags (i,f); /* output changed flags */
  2455.   }
  2456. }
  2457. /* Print envelope
  2458.  * Accepts: body
  2459.  */
  2460. void penv (ENVELOPE *env)
  2461. {
  2462.   PBOUT ('('); /* delimiter */
  2463.   if (env) { /* only if there is an envelope */
  2464.     pstring (env->date); /* output envelope fields */
  2465.     PBOUT (' ');
  2466.     pstring (env->subject);
  2467.     PBOUT (' ');
  2468.     paddr (env->from);
  2469.     PBOUT (' ');
  2470.     paddr (env->sender);
  2471.     PBOUT (' ');
  2472.     paddr (env->reply_to);
  2473.     PBOUT (' ');
  2474.     paddr (env->to);
  2475.     PBOUT (' ');
  2476.     paddr (env->cc);
  2477.     PBOUT (' ');
  2478.     paddr (env->bcc);
  2479.     PBOUT (' ');
  2480.     pstring (env->in_reply_to);
  2481.     PBOUT (' ');
  2482.     pstring (env->message_id);
  2483.   }
  2484. /* no envelope */
  2485.   else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
  2486.   PBOUT (')'); /* end of envelope */
  2487. }
  2488. /* Print body structure (extensible)
  2489.  * Accepts: body
  2490.  */
  2491. void pbodystructure (BODY *body)
  2492. {
  2493.   PBOUT ('('); /* delimiter */
  2494.   if (body) { /* only if there is a body */
  2495.     PART *part;
  2496. /* multipart type? */
  2497.     if (body->type == TYPEMULTIPART) {
  2498. /* print each part */
  2499.       if (part = body->nested.part)
  2500. for (; part; part = part->next) pbodystructure (&(part->body));
  2501.       else pbodystructure (NIL);
  2502.       PBOUT (' '); /* space delimiter */
  2503.       pstring (body->subtype); /* subtype */
  2504.       PBOUT (' ');
  2505.       pparam (body->parameter); /* multipart body extension data */
  2506.       PBOUT (' ');
  2507.       if (body->disposition.type) {
  2508. PBOUT ('(');
  2509. pstring (body->disposition.type);
  2510. PBOUT (' ');
  2511. pparam (body->disposition.parameter);
  2512. PBOUT (')');
  2513.       }
  2514.       else PSOUT ("NIL");
  2515.       PBOUT (' ');
  2516.       pstringorlist (body->language);
  2517.     }
  2518.     else { /* non-multipart body type */
  2519.       pstring ((char *) body_types[body->type]);
  2520.       PBOUT (' ');
  2521.       pstring (body->subtype);
  2522.       PBOUT (' ');
  2523.       pparam (body->parameter);
  2524.       PBOUT (' ');
  2525.       pstring (body->id);
  2526.       PBOUT (' ');
  2527.       pstring (body->description);
  2528.       PBOUT (' ');
  2529.       pstring ((char *) body_encodings[body->encoding]);
  2530.       PBOUT (' ');
  2531.       pnum (body->size.bytes);
  2532.       switch (body->type) { /* extra stuff depends upon body type */
  2533.       case TYPEMESSAGE:
  2534. /* can't do this if not RFC822 */
  2535. if (strcmp (body->subtype,"RFC822")) break;
  2536. PBOUT (' ');
  2537. penv (body->nested.msg->env);
  2538. PBOUT (' ');
  2539. pbodystructure (body->nested.msg->body);
  2540.       case TYPETEXT:
  2541. PBOUT (' ');
  2542. pnum (body->size.lines);
  2543. break;
  2544.       default:
  2545. break;
  2546.       }
  2547.       PBOUT (' ');
  2548.       pstring (body->md5);
  2549.       PBOUT (' ');
  2550.       if (body->disposition.type) {
  2551. PBOUT ('(');
  2552. pstring (body->disposition.type);
  2553. PBOUT (' ');
  2554. pparam (body->disposition.parameter);
  2555. PBOUT (')');
  2556.       }
  2557.       else PSOUT ("NIL");
  2558.       PBOUT (' ');
  2559.       pstringorlist (body->language);
  2560.     }
  2561.   }
  2562. /* no body */
  2563.   else PSOUT (""TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 0 0 NIL NIL NIL");
  2564.   PBOUT (')'); /* end of body */
  2565. }
  2566. /* Print body (non-extensible)
  2567.  * Accepts: body
  2568.  */
  2569. void pbody (BODY *body)
  2570. {
  2571.   PBOUT ('('); /* delimiter */
  2572.   if (body) { /* only if there is a body */
  2573.     PART *part;
  2574. /* multipart type? */
  2575.     if (body->type == TYPEMULTIPART) {
  2576. /* print each part */
  2577.       if (part = body->nested.part)
  2578. for (; part; part = part->next) pbody (&(part->body));
  2579.       else pbody (NIL);
  2580.       PBOUT (' '); /* space delimiter */
  2581.       pstring (body->subtype); /* and finally the subtype */
  2582.     }
  2583.     else { /* non-multipart body type */
  2584.       pstring ((char *) body_types[body->type]);
  2585.       PBOUT (' ');
  2586.       pstring (body->subtype);
  2587.       PBOUT (' ');
  2588.       pparam (body->parameter);
  2589.       PBOUT (' ');
  2590.       pstring (body->id);
  2591.       PBOUT (' ');
  2592.       pstring (body->description);
  2593.       PBOUT (' ');
  2594.       pstring ((char *) body_encodings[body->encoding]);
  2595.       PBOUT (' ');
  2596.       pnum (body->size.bytes);
  2597.       switch (body->type) { /* extra stuff depends upon body type */
  2598.       case TYPEMESSAGE:
  2599. /* can't do this if not RFC822 */
  2600. if (strcmp (body->subtype,"RFC822")) break;
  2601. PBOUT (' ');
  2602. penv (body->nested.msg ? body->nested.msg->env : NIL);
  2603. PBOUT (' ');
  2604. pbody (body->nested.msg ? body->nested.msg->body : NIL);
  2605.       case TYPETEXT:
  2606. PBOUT (' ');
  2607. pnum (body->size.lines);
  2608. break;
  2609.       default:
  2610. break;
  2611.       }
  2612.     }
  2613.   }
  2614. /* no body */
  2615.   else PSOUT (""TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 0 0");
  2616.   PBOUT (')'); /* end of body */
  2617. }
  2618. /* Print parameter list
  2619.  * Accepts: paramter
  2620.  */
  2621. void pparam (PARAMETER *param)
  2622. {
  2623.   if (param) { /* one specified? */
  2624.     PBOUT ('(');
  2625.     do {
  2626.       pstring (param->attribute);
  2627.       PBOUT (' ');
  2628.       pstring (param->value);
  2629.       if (param = param->next) PBOUT (' ');
  2630.     } while (param);
  2631.     PBOUT (')'); /* end of parameters */
  2632.   }
  2633.   else PSOUT ("NIL");
  2634. }
  2635. /* Print address list
  2636.  * Accepts: address list
  2637.  */
  2638. void paddr (ADDRESS *a)
  2639. {
  2640.   if (a) { /* have anything in address? */
  2641.     PBOUT ('('); /* open the address list */
  2642.     do { /* for each address */
  2643.       PBOUT ('('); /* open the address */
  2644.       pstring (a->personal); /* personal name */
  2645.       PBOUT (' ');
  2646.       pstring (a->adl); /* at-domain-list */
  2647.       PBOUT (' ');
  2648.       pstring (a->mailbox); /* mailbox */
  2649.       PBOUT (' ');
  2650.       pstring (a->host); /* domain name of mailbox's host */
  2651.       PBOUT (')'); /* terminate address */
  2652.     } while (a = a->next); /* until end of address */
  2653.     PBOUT (')'); /* close address list */
  2654.   }
  2655.   else PSOUT ("NIL"); /* empty address */
  2656. }
  2657. /* Print number
  2658.  * Accepts: number
  2659.  */
  2660. void pnum (unsigned long i)
  2661. {
  2662.   char tmp[MAILTMPLEN];
  2663.   sprintf (tmp,"%lu",i);
  2664.   PSOUT (tmp);
  2665. }
  2666. /* Print null-terminated string
  2667.  * Accepts: string
  2668.  */
  2669. void pstring (char *s)
  2670. {
  2671.   SIZEDTEXT st;
  2672.   st.data = (unsigned char *) s;/* set up sized text */
  2673.   st.size = s ? strlen (s) : 0; /* NIL text is zero size */
  2674.   pnstring (NIL,&st); /* print nstring */
  2675. }
  2676. /* Print NIL or string
  2677.  * Accepts: label to be output before nstring
  2678.  *     pointer to sized text or NIL
  2679.  */
  2680. void pnstring (char *label,SIZEDTEXT *st)
  2681. {
  2682.   if (label) {
  2683.     PSOUT (label);
  2684.     PBOUT (' ');
  2685.   }
  2686.   if (st && st->data) psizedtext (st);
  2687.   else PSOUT ("NIL");
  2688. }
  2689. /* Print atom or string
  2690.  * Accepts: astring
  2691.  */
  2692. void pastring (char *s)
  2693. {
  2694.   char *t;
  2695.   if (!*s) PSOUT (""""); /* empty string */
  2696.   else { /* see if atom */
  2697.     for (t = s; (*t > ' ') && !(*t & 0x80) &&
  2698.  (*t != '"') && (*t != '\') && (*t != '(') && (*t != ')') &&
  2699.  (*t != '{') && (*t != '%') && (*t != '*'); t++);
  2700.     if (*t) pstring (s); /* not an atom */
  2701.     else PSOUT (s); /* else plop down as atomic */
  2702.   }
  2703. }
  2704. /* Print body part string
  2705.  * Accepts: message number
  2706.  *     body part id (note: must have space at end to append stuff)
  2707.  *     sized text of string
  2708.  *     text printing arguments
  2709.  */
  2710. void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,TEXTARGS *ta)
  2711. {
  2712.   int f = mail_elt (stream,msgno)->seen;
  2713.   if (st->data) { /* only if have useful data */
  2714. /* partial specifier */
  2715.     if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
  2716.    /* in case first byte beyond end of text */
  2717.     if (st->size <= ta->first) st->size = ta->first = 0;
  2718.     else { /* offset and truncate */
  2719.       st->data += ta->first; /* move to desired position */
  2720.       st->size -= ta->first; /* reduced size */
  2721.       if (ta->last && (st->size > ta->last)) st->size = ta->last;
  2722.     }
  2723.   }
  2724.   pnstring (id,st); /* output nstring */
  2725.   changed_flags (msgno,f); /* and changed flags */
  2726. }
  2727. /* Print string or string listlist
  2728.  * Accepts: string / string list list
  2729.  */
  2730. void pstringorlist (STRINGLIST *s)
  2731. {
  2732.   if (!s) PSOUT ("NIL"); /* no argument given */
  2733. /* output list as list */
  2734.   else if (s->next) pstringlist (s);
  2735.   else psizedtext (&s->text); /* and single-element list as atom */
  2736. }
  2737. /* Print string list
  2738.  * Accepts: string list
  2739.  */
  2740. void pstringlist (STRINGLIST *s)
  2741. {
  2742.   PBOUT ('('); /* start list */
  2743.   do {
  2744.     psizedtext (&s->text); /* output list member */
  2745.     if (s->next) PBOUT (' ');
  2746.   } while (s = s->next);
  2747.   PBOUT (')'); /* terminate list */
  2748. }
  2749. /* Print sized text as literal or quoted string
  2750.  * Accepts: sized text
  2751.  */
  2752. void psizedtext (SIZEDTEXT *s)
  2753. {
  2754.   unsigned long i;
  2755.   for (i = 0; i < s->size; i++) /* check if must use literal */
  2756.     if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
  2757. (s->data[i] == '"') || (s->data[i] == '\')) {
  2758.       PBOUT ('{');
  2759.       pnum (s->size);
  2760.       PSOUT ("}1512");
  2761.       ptext (s);
  2762.       return;
  2763.     }
  2764.   PBOUT ('"'); /* use quoted string */
  2765.   ptext (s);
  2766.   PBOUT ('"');
  2767. }
  2768. /* Print text
  2769.  * Accepts: pointer to text
  2770.  *     pointer to size of text
  2771.  */
  2772. void ptext (SIZEDTEXT *txt)
  2773. {
  2774.   unsigned long i;
  2775.   for (i = 0; ((i < txt->size) && ((PBOUT (txt->data[i])) != EOF)); i++);
  2776.   if (i < txt->size) { /* failed to complete? */
  2777.     alarm (0); /* disable all interrupts */
  2778.     server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  2779.     syslog (LOG_INFO,"%.80s, while writing text user=%.80s host=%.80s",
  2780.     strerror (errno),user ? user : "???",tcp_clienthost ());
  2781.     if (state == OPEN) mail_close (stream);
  2782.     state = LOGOUT;
  2783.     stream = NIL;
  2784.     _exit (1);
  2785.   }
  2786. }
  2787. /* Print thread
  2788.  * Accepts: thread
  2789.  */
  2790. void pthread (THREADNODE *thr)
  2791. {
  2792.   THREADNODE *t;
  2793.   while (thr) { /* for each branch */
  2794.     PBOUT ('('); /* open branch */
  2795.     pnum (thr->num); /* first first node message number */
  2796.     for (t = thr->next; t;) { /* any subsequent nodes? */
  2797.       PBOUT (' '); /* yes, delimit */
  2798.       if (t->branch) { /* branches? */
  2799. pthread (t); /* yes, recurse to do branch */
  2800. t = NIL; /* done */
  2801.       }
  2802.       else { /* just output this number */
  2803. pnum (t->num);
  2804. t = t->next; /* and do next message */
  2805.       }
  2806.     }
  2807.     PBOUT (')'); /* done with this branch */
  2808.     thr = thr->branch; /* do next branch */
  2809.   }
  2810. }
  2811. /* Anonymous users may only use these mailboxes in these namespaces */
  2812. char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
  2813. /* Check if mailbox name is OK
  2814.  * Accepts: reference name
  2815.  *     mailbox name
  2816.  */
  2817. long nameok (char *ref,char *name)
  2818. {
  2819.   int i;
  2820.   char *s,*t;
  2821.   if (!name) return NIL; /* failure if missing name */
  2822.   if (!anonymous) return T; /* otherwise OK if not anonymous */
  2823. /* validate reference */
  2824.   if (ref && ((*ref == '#') || (*ref == '{')))
  2825.     for (i = 0; oktab[i]; i++) {
  2826.       for (s = ref, t = oktab[i];
  2827.    *t && (*s + (isupper (*s) ? 'a'-'A' : 0)) == *t; s++, t++);
  2828.       if (!*t) { /* reference OK */
  2829. if (*name == '#') break;/* check name if override */
  2830. else return T; /* otherwise done */
  2831.       }
  2832.     }
  2833. /* ordinary names are OK */
  2834.   if ((*name != '#') && (*name != '{')) return T;
  2835.   for (i = 0; oktab[i]; i++) { /* validate mailbox */
  2836.     for (s = name, t = oktab[i];
  2837.  *t && (*s + (isupper (*s) ? 'a'-'A' : 0)) == *t; s++, t++);
  2838.     if (!*t) return T; /* name is OK */
  2839.   }
  2840.   response = "%.80s NO Anonymous may not %.80s this name1512";
  2841.   return NIL;
  2842. }
  2843. /* Convert possible BBoard name to actual name
  2844.  * Accepts: command
  2845.  *     mailbox name
  2846.  * Returns: maibox name
  2847.  */
  2848. char *bboardname (char *cmd,char *name)
  2849. {
  2850.   if (cmd[0] == 'B') { /* want bboard? */
  2851.     char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
  2852.     sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
  2853.     name = s;
  2854.   }
  2855.   return name;
  2856. }
  2857. /* IMAP4rev1 Authentication responder
  2858.  * Accepts: challenge
  2859.  *     length of challenge
  2860.  *     pointer to response length return location if non-NIL
  2861.  * Returns: response
  2862.  */
  2863. #define RESPBUFLEN 8*MAILTMPLEN
  2864. char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
  2865. {
  2866.   unsigned long i,j;
  2867.   unsigned char *t,resp[RESPBUFLEN];
  2868.   PSOUT ("+ ");
  2869.   for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
  2870.     if (t[j] > ' ') PBOUT (t[j]);
  2871.   fs_give ((void **) &t);
  2872.   CRLF;
  2873.   PFLUSH; /* dump output buffer */
  2874. /* slurp response buffer */
  2875.   slurp ((char *) resp,RESPBUFLEN);
  2876.   if (!(t = (unsigned char *) strchr ((char *) resp,'12'))) return flush ();
  2877.   if (t[-1] == '15') --t; /* remove CR */
  2878.   *t = ''; /* tie off buffer */
  2879.   return (resp[0] != '*') ?
  2880.     (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
  2881. }
  2882. /* Proxy copy across mailbox formats
  2883.  * Accepts: mail stream
  2884.  *     sequence to copy on this stream
  2885.  *     destination mailbox
  2886.  *     option flags
  2887.  * Returns: T if success, else NIL
  2888.  */
  2889. long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  2890. {
  2891.   MAILSTREAM *ts;
  2892.   MESSAGECACHE *elt;
  2893.   STRING st;
  2894.   MSGDATA md;
  2895.   unsigned long i,j;
  2896.   char *s,*t,tmp[MAILTMPLEN],date[MAILTMPLEN];
  2897.   md.stream = stream;
  2898.   if (!((options & CP_UID) ? /* validate sequence */
  2899. mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
  2900.     return NIL;
  2901.   response = win; /* cancel previous errors */
  2902.   if (lsterr) fs_give ((void **) &lsterr);
  2903.   for (i = 1; i <= nmsgs; i++) /* c-client clobbers sequence, use spare */
  2904.     mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
  2905.   for (j = 0,md.msgno = 1; md.msgno <= nmsgs; j++,md.msgno++)
  2906.     if ((elt = mail_elt (stream,md.msgno))->spare) {
  2907.       if (!(elt->valid && elt->day)) {
  2908. sprintf (tmp,"%lu",md.msgno);
  2909. mail_fetch_fast (stream,tmp,NIL);
  2910.       }
  2911.       memset (s = tmp,0,MAILTMPLEN);
  2912. /* copy flags */
  2913.       if (elt->seen) strcat (s," \Seen");
  2914.       if (elt->deleted) strcat (s," \Deleted");
  2915.       if (elt->flagged) strcat (s," \Flagged");
  2916.       if (elt->answered) strcat (s," \Answered");
  2917.       if (elt->draft) strcat (s," \Draft");
  2918.       if (i = elt->user_flags) do 
  2919. if ((t = stream->user_flags[find_rightmost_bit (&i)]) && *t &&
  2920.     (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
  2921. *s++ = ' '; /* space delimiter */
  2922. strcpy (s,t);
  2923.       } while (i); /* until no more user flags */
  2924.       INIT (&st,msg_string,(void *) &md,elt->rfc822_size);
  2925.       if (!mail_append_full (NIL,mailbox,tmp+1,mail_date (date,elt),&st)) {
  2926. s = lsterr ? lsterr : "Unexpected APPEND failure";
  2927. if (j) {
  2928.   sprintf (tmp,"%.80s after %lu messages",s,j);
  2929.   mm_log (tmp,ERROR);
  2930. }
  2931. else mm_log (s,ERROR);
  2932. return NIL;
  2933.       }
  2934.     }
  2935.   response = win; /* stomp any previous babble */
  2936. /* get new driver name if was dummy */
  2937.   sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
  2938.    stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
  2939.    ts->dtb->name : "unknown");
  2940.   mm_log (tmp,NIL);
  2941.   return T;
  2942. }
  2943. /* Co-routines from MAIL library */
  2944. /* Message matches a search
  2945.  * Accepts: MAIL stream
  2946.  *     message number
  2947.  */
  2948. void mm_searched (MAILSTREAM *s,unsigned long msgno)
  2949. {
  2950. /* nothing to do here */
  2951. }
  2952. /* Message exists (i.e. there are that many messages in the mailbox)
  2953.  * Accepts: MAIL stream
  2954.  *     message number
  2955.  */
  2956. void mm_exists (MAILSTREAM *s,unsigned long number)
  2957. {
  2958. /* note change in number of messages */
  2959.   if ((s != tstream) && (nmsgs != number)) {
  2960.     nmsgs = number; /* always update number of messages */
  2961.     if (quell_events) existsquelled = T;
  2962.     else {
  2963.       PSOUT ("* ");
  2964.       pnum (nmsgs);
  2965.       PSOUT (" EXISTS1512");
  2966.     }
  2967.     recent = 0xffffffff; /* make sure update recent too */
  2968.   }
  2969. }
  2970. /* Message expunged
  2971.  * Accepts: MAIL stream
  2972.  *     message number
  2973.  */
  2974. void mm_expunged (MAILSTREAM *s,unsigned long number)
  2975. {
  2976.   if (quell_events) fatal ("Impossible EXPUNGE event");
  2977.   if (s != tstream) {
  2978.     PSOUT ("* ");
  2979.     pnum (number);
  2980.     PSOUT (" EXPUNGE1512");
  2981.   }
  2982.   nmsgs--;
  2983.   existsquelled = T; /* do EXISTS when command done */
  2984. }
  2985. /* Message status changed
  2986.  * Accepts: MAIL stream
  2987.  *     message number
  2988.  */
  2989. void mm_flags (MAILSTREAM *s,unsigned long number)
  2990. {
  2991.   if (s != tstream) mail_elt (s,number)->spare2 = T;
  2992. }
  2993. /* Mailbox found
  2994.  * Accepts: hierarchy delimiter
  2995.  *     mailbox name
  2996.  *     attributes
  2997.  */
  2998. void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  2999. {
  3000.   mm_list_work ("LIST",delimiter,name,attributes);
  3001. }
  3002. /* Subscribed mailbox found
  3003.  * Accepts: hierarchy delimiter
  3004.  *     mailbox name
  3005.  *     attributes
  3006.  */
  3007. void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  3008. {
  3009.   mm_list_work ("LSUB",delimiter,name,attributes);
  3010. }
  3011. /* Mailbox status
  3012.  * Accepts: MAIL stream
  3013.  *     mailbox name
  3014.  *     mailbox status
  3015.  */
  3016. void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
  3017. {
  3018.   if (!quell_events) {
  3019.     char tmp[MAILTMPLEN];
  3020.     tmp[0] = tmp[1] = '';
  3021.     if (status->flags & SA_MESSAGES)
  3022.       sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
  3023.     if (status->flags & SA_RECENT)
  3024.       sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
  3025.     if (status->flags & SA_UNSEEN)
  3026.       sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
  3027.     if (status->flags & SA_UIDNEXT)
  3028.       sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
  3029.     if (status->flags & SA_UIDVALIDITY)
  3030.       sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
  3031.     PSOUT ("* STATUS ");
  3032.     pastring (mailbox);
  3033.     PSOUT (" (");
  3034.     PSOUT (tmp+1);
  3035.     PBOUT (')');
  3036.     CRLF;
  3037.   }
  3038. }
  3039. /* Worker routine for LIST and LSUB
  3040.  * Accepts: name of response
  3041.  *     hierarchy delimiter
  3042.  *     mailbox name
  3043.  *     attributes
  3044.  */
  3045. void mm_list_work (char *what,int delimiter,char *name,long attributes)
  3046. {
  3047.   if (!quell_events) {
  3048.     char tmp[MAILTMPLEN];
  3049.     if (finding) {
  3050.       PSOUT ("* MAILBOX ");
  3051.       PSOUT (name);
  3052.     }
  3053. /* new form */
  3054.     else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
  3055.       PSOUT ("* ");
  3056.       PSOUT (what);
  3057.       PSOUT (" (");
  3058.       tmp[0] = tmp[1] = '';
  3059.       if (attributes & LATT_NOINFERIORS) strcat (tmp," \NoInferiors");
  3060.       if (attributes & LATT_NOSELECT) strcat (tmp," \NoSelect");
  3061.       if (attributes & LATT_MARKED) strcat (tmp," \Marked");
  3062.       if (attributes & LATT_UNMARKED) strcat (tmp," \UnMarked");
  3063.       PSOUT (tmp+1);
  3064.       switch (delimiter) {
  3065.       case '\': /* quoted delimiter */
  3066.       case '"':
  3067. PSOUT (") "\");
  3068. PBOUT (delimiter);
  3069. PBOUT ('"');
  3070. break;
  3071.       case '': /* no delimiter */
  3072. PSOUT (") NIL");
  3073. break;
  3074.       default: /* unquoted delimiter */
  3075. PSOUT (") "");
  3076. PBOUT (delimiter);
  3077. PBOUT ('"');
  3078. break;
  3079.       }
  3080.       PBOUT (' ');
  3081.       pastring (name); /* output mailbox name */
  3082.     }
  3083.     CRLF;
  3084.   }
  3085. }
  3086. /* Notification event
  3087.  * Accepts: MAIL stream
  3088.  *     string to log
  3089.  *     error flag
  3090.  */
  3091. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  3092. {
  3093.   char *code;
  3094.   if (!quell_events && (!tstream || (s != tstream))) {
  3095.     switch (errflg) {
  3096.     case NIL: /* information message, set as OK response */
  3097.       if ((string[0] == '[') &&
  3098.   ((string[1] == 'T') || (string[1] == 't')) &&
  3099.   ((string[2] == 'R') || (string[2] == 'r')) &&
  3100.   ((string[3] == 'Y') || (string[3] == 'y')) &&
  3101.   ((string[4] == 'C') || (string[4] == 'c')) &&
  3102.   ((string[5] == 'R') || (string[5] == 'r')) &&
  3103.   ((string[6] == 'E') || (string[6] == 'e')) &&
  3104.   ((string[7] == 'A') || (string[7] == 'a')) &&
  3105.   ((string[8] == 'T') || (string[8] == 't')) &&
  3106.   ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
  3107. trycreate = T;
  3108.     case BYE: /* some other server signing off */
  3109.     case PARSE: /* parse glitch, output unsolicited OK */
  3110.       code = "* OK ";
  3111.       break;
  3112.     case WARN: /* warning, output unsolicited NO (kludge!) */
  3113.       code = "* NO ";
  3114.       break;
  3115.     case ERROR: /* error that broke command */
  3116.     default: /* default should never happen */
  3117.       code = "* BAD ";
  3118.       break;
  3119.     }
  3120.     PSOUT (code);
  3121.     PSOUT (string);
  3122.     CRLF;
  3123.   }
  3124. }
  3125. /* Log an event for the user to see
  3126.  * Accepts: string to log
  3127.  *     error flag
  3128.  */
  3129. void mm_log (char *string,long errflg)
  3130. {
  3131.   if (!quell_events) switch (errflg) {
  3132.   case NIL: /* information message, set as OK response */
  3133.     if (response == win) { /* only if no other response yet */
  3134.       response = altwin; /* switch to alternative win message */
  3135.       if (lsterr) fs_give ((void **) &lsterr);
  3136.       lsterr = cpystr (string); /* copy string for later use */
  3137.     }
  3138.     break;
  3139.   case PARSE: /* parse glitch, output unsolicited OK */
  3140.     PSOUT ("* OK [PARSE] ");
  3141.     PSOUT (string);
  3142.     CRLF;
  3143.     break;
  3144.   case WARN: /* warning, output unsolicited NO */
  3145. /* ignore "Mailbox is empty" (KLUDGE!) */
  3146.     if (strcmp (string,"Mailbox is empty")) {
  3147.       if (lstwrn) { /* have previous warning? */
  3148. PSOUT ("* NO ");
  3149. PSOUT (lstwrn);
  3150. CRLF;
  3151. fs_give ((void **) &lstwrn);
  3152.       }
  3153.       lstwrn = cpystr (string); /* note last warning */
  3154.     }
  3155.     break;
  3156.   case ERROR: /* error that broke command */
  3157.   default: /* default should never happen */
  3158.     response = lose; /* set fatality */
  3159.     if (lsterr) fs_give ((void **) &lsterr);
  3160.     lsterr = cpystr (string); /* note last error */
  3161.     break;
  3162.   }
  3163. }
  3164. /* Return last error
  3165.  */
  3166. char *lasterror (void)
  3167. {
  3168.   if (lsterr) return lsterr;
  3169.   if (lstwrn) return lstwrn;
  3170.   return "<unknown>";
  3171. }
  3172. /* Log an event to debugging telemetry
  3173.  * Accepts: string to log
  3174.  */
  3175. void mm_dlog (char *string)
  3176. {
  3177.   mm_log (string,WARN); /* shouldn't happen normally */
  3178. }
  3179. /* Get user name and password for this host
  3180.  * Accepts: parse of network user name
  3181.  *     where to return user name
  3182.  *     where to return password
  3183.  *     trial count
  3184.  */
  3185. void mm_login (NETMBX *mb,char *username,char *password,long trial)
  3186. {
  3187. /* set user name */
  3188.   strncpy (username,*mb->user ? mb->user : user,NETMAXUSER);
  3189.   strncpy (password,pass,256); /* and password */
  3190. }
  3191. /* About to enter critical code
  3192.  * Accepts: stream
  3193.  */
  3194. void mm_critical (MAILSTREAM *s)
  3195. {
  3196.   critical = T;
  3197. }
  3198. /* About to exit critical code
  3199.  * Accepts: stream
  3200.  */
  3201. void mm_nocritical (MAILSTREAM *s)
  3202. {
  3203.   critical = NIL;
  3204. }
  3205. /* Disk error found
  3206.  * Accepts: stream
  3207.  *     system error code
  3208.  *     flag indicating that mailbox may be clobbered
  3209.  * Returns: abort flag
  3210.  */
  3211. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  3212. {
  3213.   if (serious) { /* try your damnest if clobberage likely */
  3214.     mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
  3215.     PFLUSH; /* dump output buffer */
  3216.     syslog (LOG_ALERT,
  3217.     "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  3218.     user ? user : "???",tcp_clienthost (),
  3219.     (stream && stream->mailbox) ? stream->mailbox : "???",
  3220.     strerror (errcode));
  3221.     alarm (0); /* make damn sure timeout disabled */
  3222.     sleep (60); /* give it some time to clear up */
  3223.     return NIL;
  3224.   }
  3225.   if (!quell_events) { /* otherwise die before more damage is done */
  3226.     PSOUT ("* BYE Aborting due to disk error: ");
  3227.     PSOUT (strerror (errcode));
  3228.     CRLF;
  3229.   }
  3230.   syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  3231.   user ? user : "???",tcp_clienthost (),
  3232.   (stream && stream->mailbox) ? stream->mailbox : "???",
  3233.   strerror (errcode));
  3234.   return T;
  3235. }
  3236. /* Log a fatal error event
  3237.  * Accepts: string to log
  3238.  */
  3239. void mm_fatal (char *string)
  3240. {
  3241.   if (!quell_events) {
  3242.     PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
  3243.     PSOUT (string);
  3244.     CRLF;
  3245.   }
  3246.   syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
  3247.   user ? user : "???",tcp_clienthost (),
  3248.   (stream && stream->mailbox) ? stream->mailbox : "???",string);
  3249. }