imapext-2007

diff src/imapd/imapd.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children 28a55bc1110c 5cecc027b845
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/imapd/imapd.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,4608 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2008 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	IMAP4rev1 server
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		UW Technology
    1.22 + *		University of Washington
    1.23 + *		Seattle, WA  98195
    1.24 + *		Internet: MRC@Washington.EDU
    1.25 + *
    1.26 + * Date:	5 November 1990
    1.27 + * Last Edited:	3 March 2008
    1.28 + */
    1.29 +
    1.30 +/* Parameter files */
    1.31 +
    1.32 +#include <stdio.h>
    1.33 +#include <ctype.h>
    1.34 +#include <errno.h>
    1.35 +extern int errno;		/* just in case */
    1.36 +#include <signal.h>
    1.37 +#include <setjmp.h>
    1.38 +#include <time.h>
    1.39 +#include "c-client.h"
    1.40 +#include "newsrc.h"
    1.41 +#include <sys/stat.h>
    1.42 +
    1.43 +
    1.44 +#define CRLF PSOUT ("\015\012")	/* primary output terpri */
    1.45 +
    1.46 +
    1.47 +/* Timeouts and timers */
    1.48 +
    1.49 +#define MINUTES *60
    1.50 +
    1.51 +#define LOGINTIMEOUT 3 MINUTES	/* not logged in autologout timer */
    1.52 +#define TIMEOUT 30 MINUTES	/* RFC 3501 minimum autologout timer */
    1.53 +#define INPUTTIMEOUT 5 MINUTES	/* timer for additional command input */
    1.54 +#define ALERTTIMER 1 MINUTES	/* alert check timer */
    1.55 +#define SHUTDOWNTIMER 1 MINUTES	/* shutdown dally timer */
    1.56 +#define IDLETIMER 1 MINUTES	/* IDLE command poll timer */
    1.57 +#define CHECKTIMER 15 MINUTES	/* IDLE command last checkpoint timer */
    1.58 +
    1.59 +
    1.60 +#define LITSTKLEN 20		/* length of literal stack */
    1.61 +#define MAXCLIENTLIT 10000	/* maximum non-APPEND client literal size
    1.62 +				 * must be smaller than 4294967295
    1.63 +				 */
    1.64 +#define MAXAPPENDTXT 0x40000000	/* maximum APPEND literal size
    1.65 +				 * must be smaller than 4294967295
    1.66 +				 */
    1.67 +#define CMDLEN 65536		/* size of command buffer */
    1.68 +
    1.69 +
    1.70 +/* Server states */
    1.71 +
    1.72 +#define LOGIN 0
    1.73 +#define SELECT 1
    1.74 +#define OPEN 2
    1.75 +#define LOGOUT 3
    1.76 +
    1.77 +/* Body text fetching */
    1.78 +
    1.79 +typedef struct text_args {
    1.80 +  char *section;		/* body section */
    1.81 +  STRINGLIST *lines;		/* header lines */
    1.82 +  unsigned long first;		/* first octet to fetch */
    1.83 +  unsigned long last;		/* number of octets to fetch */
    1.84 +  long flags;			/* fetch flags */
    1.85 +  long binary;			/* binary flags */
    1.86 +} TEXTARGS;
    1.87 +
    1.88 +#define FTB_BINARY 0x1		/* fetch as binary */
    1.89 +#define FTB_SIZE 0x2		/* fetch size only */
    1.90 +
    1.91 +
    1.92 +/* Append data */
    1.93 +
    1.94 +typedef struct append_data {
    1.95 +  unsigned char *arg;		/* append argument pointer */
    1.96 +  char *flags;			/* message flags */
    1.97 +  char *date;			/* message date */
    1.98 +  char *msg;			/* message text */
    1.99 +  STRING *message;		/* message stringstruct */
   1.100 +} APPENDDATA;
   1.101 +
   1.102 +
   1.103 +/* Message pointer */
   1.104 +
   1.105 +typedef struct msg_data {
   1.106 +  MAILSTREAM *stream;		/* stream */
   1.107 +  unsigned long msgno;		/* message number */
   1.108 +  char *flags;			/* current flags */
   1.109 +  char *date;			/* current date */
   1.110 +  STRING *message;		/* stringstruct of message */
   1.111 +} MSGDATA;
   1.112 +
   1.113 +/* Function prototypes */
   1.114 +
   1.115 +int main (int argc,char *argv[]);
   1.116 +void ping_mailbox (unsigned long uid);
   1.117 +time_t palert (char *file,time_t oldtime);
   1.118 +void msg_string_init (STRING *s,void *data,unsigned long size);
   1.119 +char msg_string_next (STRING *s);
   1.120 +void msg_string_setpos (STRING *s,unsigned long i);
   1.121 +void new_flags (MAILSTREAM *stream);
   1.122 +void settimeout (unsigned int i);
   1.123 +void clkint (void);
   1.124 +void kodint (void);
   1.125 +void hupint (void);
   1.126 +void trmint (void);
   1.127 +void staint (void);
   1.128 +char *sout (char *s,char *t);
   1.129 +char *nout (char *s,unsigned long n,unsigned long base);
   1.130 +void slurp (char *s,int n,unsigned long timeout);
   1.131 +void inliteral (char *s,unsigned long n);
   1.132 +unsigned char *flush (void);
   1.133 +void ioerror (FILE *f,char *reason);
   1.134 +unsigned char *parse_astring (unsigned char **arg,unsigned long *i,
   1.135 +			      unsigned char *del);
   1.136 +unsigned char *snarf (unsigned char **arg);
   1.137 +unsigned char *snarf_base64 (unsigned char **arg);
   1.138 +unsigned char *snarf_list (unsigned char **arg);
   1.139 +STRINGLIST *parse_stringlist (unsigned char **s,int *list);
   1.140 +unsigned long uidmax (MAILSTREAM *stream);
   1.141 +long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
   1.142 +		     unsigned long maxuid,unsigned long depth);
   1.143 +long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long msgmsg,
   1.144 +		      unsigned long maxuid,unsigned long depth);
   1.145 +long crit_date (unsigned short *date,unsigned char **arg);
   1.146 +long crit_date_work (unsigned short *date,unsigned char **arg);
   1.147 +long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima);
   1.148 +long crit_number (unsigned long *number,unsigned char **arg);
   1.149 +long crit_string (STRINGLIST **string,unsigned char **arg);
   1.150 +
   1.151 +void fetch (char *t,unsigned long uid);
   1.152 +typedef void (*fetchfn_t) (unsigned long i,void *args);
   1.153 +void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
   1.154 +void fetch_bodystructure (unsigned long i,void *args);
   1.155 +void fetch_body (unsigned long i,void *args);
   1.156 +void fetch_body_part_mime (unsigned long i,void *args);
   1.157 +void fetch_body_part_contents (unsigned long i,void *args);
   1.158 +void fetch_body_part_binary (unsigned long i,void *args);
   1.159 +void fetch_body_part_header (unsigned long i,void *args);
   1.160 +void fetch_body_part_text (unsigned long i,void *args);
   1.161 +void remember (unsigned long uid,char *id,SIZEDTEXT *st);
   1.162 +void fetch_envelope (unsigned long i,void *args);
   1.163 +void fetch_encoding (unsigned long i,void *args);
   1.164 +void changed_flags (unsigned long i,int f);
   1.165 +void fetch_flags (unsigned long i,void *args);
   1.166 +void put_flag (int *c,char *s);
   1.167 +void fetch_internaldate (unsigned long i,void *args);
   1.168 +void fetch_uid (unsigned long i,void *args);
   1.169 +void fetch_rfc822 (unsigned long i,void *args);
   1.170 +void fetch_rfc822_header (unsigned long i,void *args);
   1.171 +void fetch_rfc822_size (unsigned long i,void *args);
   1.172 +void fetch_rfc822_text (unsigned long i,void *args);
   1.173 +void penv (ENVELOPE *env);
   1.174 +void pbodystructure (BODY *body);
   1.175 +void pbody (BODY *body);
   1.176 +void pparam (PARAMETER *param);
   1.177 +void paddr (ADDRESS *a);
   1.178 +void pset (SEARCHSET **set);
   1.179 +void pnum (unsigned long i);
   1.180 +void pstring (char *s);
   1.181 +void pnstring (char *s);
   1.182 +void pastring (char *s);
   1.183 +void psizedquoted (SIZEDTEXT *s);
   1.184 +void psizedliteral (SIZEDTEXT *s,STRING *st);
   1.185 +void psizedstring (SIZEDTEXT *s,STRING *st);
   1.186 +void psizedastring (SIZEDTEXT *s);
   1.187 +void pastringlist (STRINGLIST *s);
   1.188 +void pnstringorlist (STRINGLIST *s);
   1.189 +void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
   1.190 +		      TEXTARGS *ta);
   1.191 +void ptext (SIZEDTEXT *s,STRING *st);
   1.192 +void pthread (THREADNODE *thr);
   1.193 +void pcapability (long flag);
   1.194 +long nameok (char *ref,char *name);
   1.195 +char *bboardname (char *cmd,char *name);
   1.196 +long isnewsproxy (char *name);
   1.197 +long newsproxypattern (char *ref,char *pat,char *pattern,long flag);
   1.198 +char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
   1.199 +long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
   1.200 +long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
   1.201 +		   STRING **message);
   1.202 +long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
   1.203 +		 STRING **message);
   1.204 +void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
   1.205 +	      SEARCHSET *sourceset,SEARCHSET *destset);
   1.206 +void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set);
   1.207 +char *referral (MAILSTREAM *stream,char *url,long code);
   1.208 +void mm_list_work (char *what,int delimiter,char *name,long attributes);
   1.209 +char *lasterror (void);
   1.210 +
   1.211 +/* Global storage */
   1.212 +
   1.213 +char *version = "404";		/* edit number of this server */
   1.214 +char *logout = "Logout";	/* syslogreason for logout */
   1.215 +char *goodbye = NIL;		/* bye reason */
   1.216 +time_t alerttime = 0;		/* time of last alert */
   1.217 +time_t sysalerttime = 0;	/* time of last system alert */
   1.218 +time_t useralerttime = 0;	/* time of last user alert */
   1.219 +time_t lastcheck = 0;		/* time of last checkpoint */
   1.220 +time_t shutdowntime = 0;	/* time of last shutdown */
   1.221 +int state = LOGIN;		/* server state */
   1.222 +int cancelled = NIL;		/* authenticate cancelled */
   1.223 +int trycreate = 0;		/* saw a trycreate */
   1.224 +int finding = NIL;		/* doing old FIND command */
   1.225 +int anonymous = 0;		/* non-zero if anonymous */
   1.226 +int critical = NIL;		/* non-zero if in critical code */
   1.227 +int quell_events = NIL;		/* non-zero if in FETCH response */
   1.228 +int existsquelled = NIL;	/* non-zero if an EXISTS was quelled */
   1.229 +int proxylist = NIL;		/* doing a proxy LIST */
   1.230 +MAILSTREAM *stream = NIL;	/* mailbox stream */
   1.231 +DRIVER *curdriver = NIL;	/* note current driver */
   1.232 +MAILSTREAM *tstream = NIL;	/* temporary mailbox stream */
   1.233 +unsigned int nflags = 0;	/* current number of keywords */
   1.234 +unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
   1.235 +unsigned long recent = 0xffffffff;
   1.236 +char *nntpproxy = NIL;		/* NNTP proxy name */
   1.237 +unsigned char *user = NIL;	/* user name */
   1.238 +unsigned char *pass = NIL;	/* password */
   1.239 +unsigned char *initial = NIL;	/* initial response */
   1.240 +unsigned char cmdbuf[CMDLEN];	/* command buffer */
   1.241 +char *status = "starting up";	/* server status */
   1.242 +char *tag;			/* tag portion of command */
   1.243 +unsigned char *cmd;		/* command portion of command */
   1.244 +unsigned char *arg;		/* pointer to current argument of command */
   1.245 +char *lstwrn = NIL;		/* last warning message from c-client */
   1.246 +char *lsterr = NIL;		/* last error message from c-client */
   1.247 +char *lstref = NIL;		/* last referral from c-client */
   1.248 +char *response = NIL;		/* command response */
   1.249 +struct {
   1.250 +  unsigned long size;		/* size of current LITERAL+ */
   1.251 +  unsigned int ok : 1;		/* LITERAL+ in effect */
   1.252 +} litplus;
   1.253 +int litsp = 0;			/* literal stack pointer */
   1.254 +char *litstk[LITSTKLEN];	/* stack to hold literals */
   1.255 +unsigned long uidvalidity = 0;	/* last reported UID validity */
   1.256 +unsigned long lastuid = 0;	/* last fetched uid */
   1.257 +char *lastid = NIL;		/* last fetched body id for this message */
   1.258 +char *lastsel = NIL;		/* last selected mailbox name */
   1.259 +SIZEDTEXT lastst = {NIL,0};	/* last sizedtext */
   1.260 +unsigned long cauidvalidity = 0;/* UIDVALIDITY for COPYUID/APPENDUID */
   1.261 +SEARCHSET *csset = NIL;		/* COPYUID source set */
   1.262 +SEARCHSET *caset = NIL;		/* COPYUID/APPENDUID destination set */
   1.263 +jmp_buf jmpenv;			/* stack context for setjmp */
   1.264 +
   1.265 +
   1.266 +/* Response texts which appear in multiple places */
   1.267 +
   1.268 +char *win = "%.80s OK ";
   1.269 +char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012";
   1.270 +char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012";
   1.271 +char *lose = "%.80s NO ";
   1.272 +char *logwin = "%.80s OK [";
   1.273 +char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s\015\012";
   1.274 +char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.900s\015\012";
   1.275 +char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012";
   1.276 +char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012";
   1.277 +char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012";
   1.278 +char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012";
   1.279 +char *badatt = "%.80s BAD Bogus attribute list in %.80s\015\012";
   1.280 +char *badbin = "%.80s BAD Syntax error in binary specifier\015\012";
   1.281 +
   1.282 +/* Message string driver for message stringstructs */
   1.283 +
   1.284 +STRINGDRIVER msg_string = {
   1.285 +  msg_string_init,		/* initialize string structure */
   1.286 +  msg_string_next,		/* get next byte in string structure */
   1.287 +  msg_string_setpos		/* set position in string structure */
   1.288 +};
   1.289 +
   1.290 +/* Main program */
   1.291 +
   1.292 +int main (int argc,char *argv[])
   1.293 +{
   1.294 +  unsigned long i,uid;
   1.295 +  long f;
   1.296 +  unsigned char *s,*t,*u,*v,tmp[MAILTMPLEN];
   1.297 +  struct stat sbuf;
   1.298 +  logouthook_t lgoh;
   1.299 +  int ret = 0;
   1.300 +  time_t autologouttime = 0;
   1.301 +  char *pgmname;
   1.302 +				/* if case we get borked immediately */
   1.303 +  if (setjmp (jmpenv)) _exit (1);
   1.304 +  pgmname = (argc && argv[0]) ?
   1.305 +    (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
   1.306 +     (char *) s+1 : argv[0]) : "imapd";
   1.307 +				/* set service name before linkage */
   1.308 +  mail_parameters (NIL,SET_SERVICENAME,(void *) "imap");
   1.309 +#include "linkage.c"
   1.310 +  rfc822_date (tmp);		/* get date/time at startup */
   1.311 +				/* initialize server */
   1.312 +  server_init (pgmname,"imap","imaps",clkint,kodint,hupint,trmint,staint);
   1.313 +				/* forbid automatic untagged expunge */
   1.314 +  mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
   1.315 +				/* arm proxy copy callback */
   1.316 +  mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
   1.317 +				/* arm referral callback */
   1.318 +  mail_parameters (NIL,SET_IMAPREFERRAL,(void *) referral);
   1.319 +				/* arm COPYUID callback */
   1.320 +  mail_parameters (NIL,SET_COPYUID,(void *) copyuid);
   1.321 +				/* arm APPENDUID callback */
   1.322 +  mail_parameters (NIL,SET_APPENDUID,(void *) appenduid);
   1.323 +
   1.324 +  if (stat (SHUTDOWNFILE,&sbuf)) {
   1.325 +    char proxy[MAILTMPLEN];
   1.326 +    FILE *nntp = fopen (NNTPFILE,"r");
   1.327 +    if (nntp) {			/* desire NNTP proxy? */
   1.328 +      if (fgets (proxy,MAILTMPLEN,nntp)) {
   1.329 +				/* remove newline and set NNTP proxy */
   1.330 +	if (s = strchr (proxy,'\n')) *s = '\0';
   1.331 +	nntpproxy = cpystr (proxy);
   1.332 +				/* disable the news driver */
   1.333 +	mail_parameters (NIL,DISABLE_DRIVER,"news");
   1.334 +      }
   1.335 +      fclose (nntp);		/* done reading proxy name */
   1.336 +    }
   1.337 +    s = myusername_full (&i);	/* get user name and flags */
   1.338 +    switch (i) {
   1.339 +    case MU_NOTLOGGEDIN:
   1.340 +      PSOUT ("* OK [");		/* not logged in, ordinary startup */
   1.341 +      pcapability (-1);
   1.342 +      break;
   1.343 +    case MU_ANONYMOUS:
   1.344 +      anonymous = T;		/* anonymous user, fall into default */
   1.345 +      s = "ANONYMOUS";
   1.346 +    case MU_LOGGEDIN:
   1.347 +      PSOUT ("* PREAUTH [");	/* already logged in, pre-authorized */
   1.348 +      pcapability (1);
   1.349 +      user = cpystr (s);	/* copy user name */
   1.350 +      pass = cpystr ("*");	/* set fake password */
   1.351 +      state = SELECT;		/* enter select state */
   1.352 +      break;
   1.353 +    default:
   1.354 +      fatal ("Unknown state from myusername_full()");
   1.355 +    }
   1.356 +    PSOUT ("] ");
   1.357 +    if (user) {			/* preauthenticated as someone? */
   1.358 +      PSOUT ("Pre-authenticated user ");
   1.359 +      PSOUT (user);
   1.360 +      PBOUT (' ');
   1.361 +    }
   1.362 +  }
   1.363 +  else {			/* login disabled */
   1.364 +    PSOUT ("* BYE Service not available ");
   1.365 +    state = LOGOUT;
   1.366 +  }
   1.367 +  PSOUT (tcp_serverhost ());
   1.368 +  PSOUT (" IMAP4rev1 ");
   1.369 +  PSOUT (CCLIENTVERSION);
   1.370 +  PBOUT ('.');
   1.371 +  PSOUT (version);
   1.372 +  PSOUT (" at ");
   1.373 +  PSOUT (tmp);
   1.374 +  CRLF;
   1.375 +  PFLUSH ();			/* dump output buffer */
   1.376 +  switch (state) {		/* do this after the banner */
   1.377 +  case LOGIN:
   1.378 +    autologouttime = time (0) + LOGINTIMEOUT;
   1.379 +    break;
   1.380 +  case SELECT:
   1.381 +    syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
   1.382 +	    user,tcp_clienthost ());
   1.383 +    break;
   1.384 +  }
   1.385 +
   1.386 +  if (setjmp (jmpenv)) {	/* die if a signal handler say so */
   1.387 +				/* in case we get borked now */
   1.388 +    if (setjmp (jmpenv)) _exit (1);
   1.389 +				/* need to close stream gracefully? */
   1.390 +    if (stream && !stream->lock && (stream->dtb->flags & DR_XPOINT))
   1.391 +      stream = mail_close (stream);
   1.392 +    ret = 1;			/* set exit status */
   1.393 +  }
   1.394 +  else while (state != LOGOUT) {/* command processing loop */
   1.395 +    slurp (cmdbuf,CMDLEN,TIMEOUT);
   1.396 +				/* no more last error or literal */
   1.397 +    if (lstwrn) fs_give ((void **) &lstwrn);
   1.398 +    if (lsterr) fs_give ((void **) &lsterr);
   1.399 +    if (lstref) fs_give ((void **) &lstref);
   1.400 +    while (litsp) fs_give ((void **) &litstk[--litsp]);
   1.401 +				/* find end of line */
   1.402 +    if (!strchr (cmdbuf,'\012')) {
   1.403 +      if (t = strchr (cmdbuf,' ')) *t = '\0';
   1.404 +      if ((t - cmdbuf) > 100) t = NIL;
   1.405 +      flush ();			/* flush excess */
   1.406 +      if (state == LOGIN)	/* error if NLI */
   1.407 +	syslog (LOG_INFO,"Line too long before authentication host=%.80s",
   1.408 +		tcp_clienthost ());
   1.409 +      sprintf (tmp,response,t ? (char *) cmdbuf : "*");
   1.410 +      PSOUT (tmp);
   1.411 +    }
   1.412 +    else if (!(tag = strtok (cmdbuf," \015\012"))) {
   1.413 +      if (state == LOGIN)	/* error if NLI */
   1.414 +	syslog (LOG_INFO,"Null command before authentication host=%.80s",
   1.415 +		tcp_clienthost ());
   1.416 +      PSOUT ("* BAD Null command\015\012");
   1.417 +    }
   1.418 +    else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag\015\012");
   1.419 +    else if (!(s = strtok (NIL," \015\012"))) {
   1.420 +      if (state == LOGIN)	/* error if NLI */
   1.421 +	syslog (LOG_INFO,"Missing command before authentication host=%.80s",
   1.422 +		tcp_clienthost ());
   1.423 +      PSOUT (tag);
   1.424 +      PSOUT (" BAD Missing command\015\012");
   1.425 +    }
   1.426 +    else {			/* parse command */
   1.427 +      response = win;		/* set default response */
   1.428 +      finding = NIL;		/* no longer FINDing */
   1.429 +      ucase (s);		/* canonicalize command case */
   1.430 +				/* UID command? */
   1.431 +      if (!strcmp (s,"UID") && strtok (NIL," \015\012")) {
   1.432 +	uid = T;		/* a UID command */
   1.433 +	s[3] = ' ';		/* restore the space delimiter */
   1.434 +	ucase (s);		/* make sure command all uppercase */
   1.435 +      }
   1.436 +      else uid = NIL;		/* not a UID command */
   1.437 +				/* flush previous saved command */
   1.438 +      if (cmd) fs_give ((void **) &cmd);
   1.439 +      cmd = cpystr (s);		/* save current command */
   1.440 +				/* snarf argument, see if possible litplus */
   1.441 +      if ((arg = strtok (NIL,"\015\012")) && ((i = strlen (arg)) > 3) &&
   1.442 +	  (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) {
   1.443 +				/* back over possible count */
   1.444 +	for (i -= 4; i && isdigit (arg[i]); i--);
   1.445 +	if (arg[i] == '{') {	/* found a literal? */
   1.446 +	  litplus.ok = T;	/* yes, note LITERAL+ in effect, set size */
   1.447 +	  litplus.size = strtoul (arg + i + 1,NIL,10);
   1.448 +	}
   1.449 +      }
   1.450 +
   1.451 +				/* these commands always valid */
   1.452 +      if (!strcmp (cmd,"NOOP")) {
   1.453 +	if (arg) response = badarg;
   1.454 +	else if (stream)	/* allow untagged EXPUNGE */
   1.455 +	  mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
   1.456 +      }
   1.457 +      else if (!strcmp (cmd,"LOGOUT")) {
   1.458 +	if (arg) response = badarg;
   1.459 +	else {			/* time to say farewell */
   1.460 +	  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
   1.461 +	  if (lastsel) fs_give ((void **) &lastsel);
   1.462 +	  if (state == OPEN) stream = mail_close (stream);
   1.463 +	  state = LOGOUT;
   1.464 +	  PSOUT ("* BYE ");
   1.465 +	  PSOUT (mylocalhost ());
   1.466 +	  PSOUT (" IMAP4rev1 server terminating connection\015\012");
   1.467 +	}
   1.468 +      }
   1.469 +      else if (!strcmp (cmd,"CAPABILITY")) {
   1.470 +	if (arg) response = badarg;
   1.471 +	else {
   1.472 +	  PSOUT ("* ");
   1.473 +	  pcapability (0);	/* print capabilities */
   1.474 +	  CRLF;
   1.475 +	}
   1.476 +	if (stream)		/* allow untagged EXPUNGE */
   1.477 +	  mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
   1.478 +      }
   1.479 +#ifdef NETSCAPE_BRAIN_DAMAGE
   1.480 +      else if (!strcmp (cmd,"NETSCAPE")) {
   1.481 +	PSOUT ("* OK [NETSCAPE]\015\012* VERSION 1.0 UNIX\015\012* ACCOUNT-URL \"");
   1.482 +	PSOUT (NETSCAPE_BRAIN_DAMAGE);
   1.483 +	PBOUT ('"');
   1.484 +	CRLF;
   1.485 +      }
   1.486 +#endif
   1.487 +
   1.488 +      else switch (state) {	/* dispatch depending upon state */
   1.489 +      case LOGIN:		/* waiting to get logged in */
   1.490 +				/* new style authentication */
   1.491 +	if (!strcmp (cmd,"AUTHENTICATE")) {
   1.492 +	  if (user) fs_give ((void **) &user);
   1.493 +	  if (pass) fs_give ((void **) &pass);
   1.494 +	  initial = NIL;	/* no initial argument */
   1.495 +	  cancelled = NIL;	/* not cancelled */
   1.496 +				/* mandatory first argument */
   1.497 +	  if (!(s = snarf (&arg))) response = misarg;
   1.498 +	  else if (arg && !(initial = snarf_base64 (&arg)))
   1.499 +	    response = misarg;	/* optional second argument */
   1.500 +	  else if (arg) response = badarg;
   1.501 +	  else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
   1.502 +	    if (!(s = imap_responder ("",0,NIL)))
   1.503 +	      response ="%.80s BAD AUTHENTICATE ANONYMOUS cancelled\015\012";
   1.504 +	    else if (anonymous_login (argc,argv)) {
   1.505 +	      anonymous = T;	/* note we are anonymous */
   1.506 +	      user = cpystr ("ANONYMOUS");
   1.507 +	      pass = cpystr ("*");
   1.508 +	      state = SELECT;	/* make select */
   1.509 +	      alerttime = 0;	/* force alert */
   1.510 +	      response = logwin;/* return logged-in capabilities */
   1.511 +	      syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
   1.512 +		      tcp_clienthost ());
   1.513 +	      fs_give ((void **) &s);
   1.514 +	    }
   1.515 +	    else response ="%.80s NO AUTHENTICATE ANONYMOUS failed\015\012";
   1.516 +	  }
   1.517 +	  else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
   1.518 +	    pass = cpystr ("*");
   1.519 +	    state = SELECT;	/* make select */
   1.520 +	    alerttime = 0;	/* force alert */
   1.521 +	    response = logwin;	/* return logged-in capabilities */
   1.522 +	    syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s mech=%.80s",
   1.523 +		    user,tcp_clienthost (),s);
   1.524 +	  }
   1.525 +
   1.526 +	  else {
   1.527 +	    AUTHENTICATOR *auth = mail_lookup_auth (1);
   1.528 +	    char *msg = (char *) fs_get (strlen (cmd) + strlen (s) + 2);
   1.529 +	    sprintf (msg,"%s %s",cmd,s);
   1.530 +	    fs_give ((void **) &cmd);
   1.531 +	    cmd = msg;
   1.532 +	    for (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
   1.533 +		 auth && compare_cstring (s,auth->name); auth = auth->next);
   1.534 +	    /* Failed authentication when hidden looks like invalid command.
   1.535 +	     * This is intentional but confused me when I was debugging.
   1.536 +	     */
   1.537 +	    if (auth && auth->server && !(auth->flags & AU_DISABLE) &&
   1.538 +		!(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
   1.539 +	      response = lose;
   1.540 +	      if (cancelled) {
   1.541 +		if (lsterr) fs_give ((void **) &lsterr);
   1.542 +		lsterr = cpystr ("cancelled by user");
   1.543 +	      }
   1.544 +	      if (!lsterr)	/* catch-all */
   1.545 +		lsterr = cpystr ("Invalid authentication credentials");
   1.546 +	      syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
   1.547 +		      tcp_clienthost ());
   1.548 +	    }
   1.549 +	    else {
   1.550 +	      response = badcmd;
   1.551 +	      syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s,
   1.552 +		      tcp_clienthost ());
   1.553 +	    }
   1.554 +	  }
   1.555 +	}
   1.556 +
   1.557 +				/* plaintext login with password */
   1.558 +	else if (!strcmp (cmd,"LOGIN")) {
   1.559 +	  if (user) fs_give ((void **) &user);
   1.560 +	  if (pass) fs_give ((void **) &pass);
   1.561 +				/* two arguments */
   1.562 +	  if (!((user = cpystr (snarf (&arg))) &&
   1.563 +		(pass = cpystr (snarf (&arg))))) response = misarg;
   1.564 +	  else if (arg) response = badarg;
   1.565 +				/* see if we allow anonymous */
   1.566 +	  else if (!compare_cstring (user,"ANONYMOUS") &&
   1.567 +		   !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
   1.568 +	    anonymous = T;	/* note we are anonymous */
   1.569 +	    ucase (user);	/* make all uppercase for consistency */
   1.570 +	    state = SELECT;	/* make select */
   1.571 +	    alerttime = 0;	/* force alert */
   1.572 +	    response = logwin;	/* return logged-in capabilities */
   1.573 +	    syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
   1.574 +		    tcp_clienthost ());
   1.575 +	  }
   1.576 +	  else {		/* delimit user from possible admin */
   1.577 +	    if (s = strchr (user,'*')) *s++ ='\0';
   1.578 +				/* see if username and password are OK */
   1.579 +	    if (server_login (user,pass,s,argc,argv)) {
   1.580 +	      state = SELECT;	/* make select */
   1.581 +	      alerttime = 0;	/* force alert */
   1.582 +	      response = logwin;/* return logged-in capabilities */
   1.583 +	      syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
   1.584 +		      tcp_clienthost ());
   1.585 +	    }
   1.586 +	    else {
   1.587 +	      response = lose;
   1.588 +	      if (!lsterr) lsterr = cpystr ("Invalid login credentials");
   1.589 +	    }
   1.590 +	  }
   1.591 +	}
   1.592 +				/* start TLS security */
   1.593 +	else if (!strcmp (cmd,"STARTTLS")) {
   1.594 +	  if (arg) response = badarg;
   1.595 +	  else if (lsterr = ssl_start_tls (pgmname)) response = lose;
   1.596 +	}
   1.597 +	else response = badcmd;
   1.598 +	break;
   1.599 +
   1.600 +      case OPEN:		/* valid only when mailbox open */
   1.601 +				/* fetch mailbox attributes */
   1.602 +	if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
   1.603 +	  if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
   1.604 +	    response = misarg;
   1.605 +	  else if (uid ? mail_uid_sequence (stream,s) :
   1.606 +		   mail_sequence (stream,s)) fetch (t,uid);
   1.607 +	  else response = badseq;
   1.608 +	}
   1.609 +				/* store mailbox attributes */
   1.610 +	else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
   1.611 +				/* must have three arguments */
   1.612 +	  if (!(arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) &&
   1.613 +		(t = strtok (NIL,"\015\012")))) response = misarg;
   1.614 +	  else if (!(uid ? mail_uid_sequence (stream,s) :
   1.615 +		     mail_sequence (stream,s))) response = badseq;
   1.616 +	  else {
   1.617 +	    f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
   1.618 +	    if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
   1.619 +	      strcpy (tmp,"\\Answered \\Flagged \\Deleted \\Draft \\Seen");
   1.620 +	      for (i = 0, u = tmp;
   1.621 +		   (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
   1.622 +	        if (strlen (v) <
   1.623 +		    ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
   1.624 +		  *u++ = ' ';	/* write next flag */
   1.625 +		  strcpy (u,v);
   1.626 +		}
   1.627 +	      mail_flag (stream,s,tmp,f & ~ST_SET);
   1.628 +	    }
   1.629 +	    else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
   1.630 +	      f &= ~ST_SET;	/* clear flags */
   1.631 +	    else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
   1.632 +	      response = badatt;
   1.633 +	      break;
   1.634 +	    }
   1.635 +				/* find last keyword */
   1.636 +	    for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
   1.637 +	    mail_flag (stream,s,t,f);
   1.638 +				/* any new keywords appeared? */
   1.639 +	    if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
   1.640 +				/* return flags if silence not wanted */
   1.641 +	    if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))
   1.642 +	      for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
   1.643 +		mail_elt (stream,i)->spare2 = (f & ST_SILENT) ? NIL : T;
   1.644 +	  }
   1.645 +	}
   1.646 +
   1.647 +				/* check for new mail */
   1.648 +	else if (!strcmp (cmd,"CHECK")) {
   1.649 +				/* no arguments */
   1.650 +	  if (arg) response = badarg;
   1.651 +	  else if (!anonymous) {
   1.652 +	    mail_check (stream);
   1.653 +				/* remember last check time */
   1.654 +	    lastcheck = time (0);
   1.655 +	  }
   1.656 +	}
   1.657 +				/* expunge deleted messages */
   1.658 +	else if (!(anonymous || (strcmp (cmd,"EXPUNGE") &&
   1.659 +				 strcmp (cmd,"UID EXPUNGE")))) {
   1.660 +	  if (uid && !arg) response = misarg;
   1.661 +	  else if (!uid && arg) response = badarg;
   1.662 +	  else {		/* expunge deleted or specified UIDs */
   1.663 +	    mail_expunge_full (stream,arg,arg ? EX_UID : NIL);
   1.664 +				/* remember last checkpoint */
   1.665 +	    lastcheck = time (0);
   1.666 +	  }
   1.667 +	}
   1.668 +				/* close mailbox */
   1.669 +	else if (!strcmp (cmd,"CLOSE") || !strcmp (cmd,"UNSELECT")) {
   1.670 +				/* no arguments */
   1.671 +	  if (arg) response = badarg;
   1.672 +	  else {
   1.673 +				/* no last uid */
   1.674 +	    uidvalidity = lastuid = 0;
   1.675 +	    if (lastsel) fs_give ((void **) &lastsel);
   1.676 +	    if (lastid) fs_give ((void **) &lastid);
   1.677 +	    if (lastst.data) fs_give ((void **) &lastst.data);
   1.678 +	    stream = mail_close_full (stream,((*cmd == 'C') && !anonymous) ?
   1.679 +				      CL_EXPUNGE : NIL);
   1.680 +	    state = SELECT;	/* no longer opened */
   1.681 +	    lastcheck = 0;	/* no last checkpoint */
   1.682 +	  }
   1.683 +	}
   1.684 +	else if (!anonymous &&	/* copy message(s) */
   1.685 +		 (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
   1.686 +	  trycreate = NIL;	/* no trycreate status */
   1.687 +	  if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"\015\012"))
   1.688 +		&& (t = snarf (&arg)))) response = misarg;
   1.689 +	  else if (arg) response = badarg;
   1.690 +	  else if (!nmsgs) {
   1.691 +	    response = lose;
   1.692 +	    if (!lsterr) lsterr = cpystr ("Mailbox is empty");
   1.693 +	  }
   1.694 +	  else if (!(uid ? mail_uid_sequence (stream,s) :
   1.695 +		     mail_sequence (stream,s))) response = badseq;
   1.696 +				/* try copy */
   1.697 +	  else if (!mail_copy_full (stream,s,t,uid ? CP_UID : NIL)) {
   1.698 +	    response = trycreate ? losetry : lose;
   1.699 +	    if (!lsterr) lsterr = cpystr ("No such destination mailbox");
   1.700 +	  }
   1.701 +	}
   1.702 +
   1.703 +				/* sort mailbox */
   1.704 +	else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
   1.705 +				/* must have four arguments */
   1.706 +	  if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
   1.707 +		(t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
   1.708 +	  else {		/* read criteria */
   1.709 +	    SEARCHPGM *spg = NIL;
   1.710 +	    char *cs = NIL;
   1.711 +	    SORTPGM *pgm = NIL,*pg = NIL;
   1.712 +	    unsigned long *slst,*sl;
   1.713 +	    *t = NIL;		/* tie off criteria list */
   1.714 +	    if (!(s = strtok (ucase (s)," "))) response = badatt;
   1.715 +	    else {
   1.716 +	      do {		/* parse sort attributes */
   1.717 +		if (pg) pg = pg->next = mail_newsortpgm ();
   1.718 +		else pgm = pg = mail_newsortpgm ();
   1.719 +		if (!strcmp (s,"REVERSE")) {
   1.720 +		  pg->reverse = T;
   1.721 +		  if (!(s = strtok (NIL," "))) {
   1.722 +		    s = "";	/* end of attributes */
   1.723 +		    break;
   1.724 +		  }
   1.725 +		}
   1.726 +		if (!strcmp (s,"DATE")) pg->function = SORTDATE;
   1.727 +		else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
   1.728 +		else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
   1.729 +		else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
   1.730 +		else if (!strcmp (s,"TO")) pg->function = SORTTO;
   1.731 +		else if (!strcmp (s,"CC")) pg->function = SORTCC;
   1.732 +		else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
   1.733 +		else break;
   1.734 +	      } while (s = strtok (NIL," "));
   1.735 +				/* bad SORT attribute */
   1.736 +	      if (s) response = badatt;
   1.737 +				/* get charset and search criteria */
   1.738 +	      else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg &&
   1.739 +			 *arg)) response = misarg;
   1.740 +				/* parse search criteria  */
   1.741 +	      else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
   1.742 +					uidmax (stream),0)) response = badatt;
   1.743 +	      else if (arg && *arg) response = badarg;
   1.744 +	      else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID:NIL)) {
   1.745 +		PSOUT ("* SORT");
   1.746 +		for (sl = slst; *sl; sl++) {
   1.747 +		  PBOUT (' ');
   1.748 +		  pnum (*sl);
   1.749 +		}
   1.750 +		CRLF;
   1.751 +		fs_give ((void **) &slst);
   1.752 +	      }
   1.753 +	    }
   1.754 +	    if (pgm) mail_free_sortpgm (&pgm);
   1.755 +	    if (spg) mail_free_searchpgm (&spg);
   1.756 +	    if (cs) fs_give ((void **) &cs);
   1.757 +	  }
   1.758 +	}
   1.759 +
   1.760 +				/* thread mailbox */
   1.761 +	else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
   1.762 +	  THREADNODE *thr;
   1.763 +	  SEARCHPGM *spg = NIL;
   1.764 +	  char *cs = NIL;
   1.765 +				/* must have four arguments */
   1.766 +	  if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) &&
   1.767 +		(cs = cpystr (cs)) && (arg = strtok (NIL,"\015\012"))))
   1.768 +	    response = misarg;
   1.769 +	  else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
   1.770 +				    uidmax (stream),0)) response = badatt;
   1.771 +	  else if (arg && *arg) response = badarg;
   1.772 +	  else {
   1.773 +	    if (thr = mail_thread (stream,s,cs,spg,uid ? SE_UID : NIL)) {
   1.774 +	      PSOUT ("* THREAD ");
   1.775 +	      pthread (thr);
   1.776 +	      mail_free_threadnode (&thr);
   1.777 +	    }
   1.778 +	    else PSOUT ("* THREAD");
   1.779 +	    CRLF;
   1.780 +	  }
   1.781 +	  if (spg) mail_free_searchpgm (&spg);
   1.782 +	  if (cs) fs_give ((void **) &cs);
   1.783 +	}
   1.784 +
   1.785 +				/* search mailbox */
   1.786 +        else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
   1.787 +	  int retval = NIL;
   1.788 +	  char *charset = NIL;
   1.789 +	  SEARCHPGM *pgm;
   1.790 +	  response = misarg;	/* assume failure */
   1.791 +	  if (!arg) break;	/* one or more arguments required */
   1.792 +	  if (((arg[0] == 'R') || (arg[0] == 'r')) &&
   1.793 +	      ((arg[1] == 'E') || (arg[1] == 'e')) &&
   1.794 +	      ((arg[2] == 'T') || (arg[2] == 't')) &&
   1.795 +	      ((arg[3] == 'U') || (arg[3] == 'u')) &&
   1.796 +	      ((arg[4] == 'R') || (arg[4] == 'r')) &&
   1.797 +	      ((arg[5] == 'N') || (arg[5] == 'n')) &&
   1.798 +	      (arg[6] == ' ') && (arg[7] == '(')) {
   1.799 +	    retval = 0x4000;	/* return is specified */
   1.800 +	    for (arg += 8; *arg && (*arg != ')'); ) {
   1.801 +	      if (((arg[0] == 'M') || (arg[0] == 'm')) &&
   1.802 +		  ((arg[1] == 'I') || (arg[1] == 'i')) &&
   1.803 +		  ((arg[2] == 'N') || (arg[2] == 'n')) &&
   1.804 +		  ((arg[3] == ' ') || (arg[3] == ')'))) {
   1.805 +		retval |= 0x1;
   1.806 +		arg += 3;
   1.807 +	      }
   1.808 +	      else if (((arg[0] == 'M') || (arg[0] == 'm')) &&
   1.809 +		       ((arg[1] == 'A') || (arg[1] == 'a')) &&
   1.810 +		       ((arg[2] == 'X') || (arg[2] == 'x')) &&
   1.811 +		       ((arg[3] == ' ') || (arg[3] == ')'))) {
   1.812 +		retval |= 0x2;
   1.813 +		arg += 3;
   1.814 +	      }
   1.815 +	      else if (((arg[0] == 'A') || (arg[0] == 'a')) &&
   1.816 +		       ((arg[1] == 'L') || (arg[1] == 'l')) &&
   1.817 +		       ((arg[2] == 'L') || (arg[2] == 'l')) &&
   1.818 +		       ((arg[3] == ' ') || (arg[3] == ')'))) {
   1.819 +		retval |= 0x4;
   1.820 +		arg += 3;
   1.821 +	      }
   1.822 +	      else if (((arg[0] == 'C') || (arg[0] == 'c')) &&
   1.823 +		       ((arg[1] == 'O') || (arg[1] == 'o')) &&
   1.824 +		       ((arg[2] == 'U') || (arg[2] == 'u')) &&
   1.825 +		       ((arg[3] == 'N') || (arg[3] == 'n')) &&
   1.826 +		       ((arg[4] == 'T') || (arg[4] == 't')) &&
   1.827 +		       ((arg[5] == ' ') || (arg[5] == ')'))) {
   1.828 +		retval |= 0x10;
   1.829 +		arg += 5;
   1.830 +	      }
   1.831 +	      else break;	/* unknown return value */
   1.832 +				/* more return values to come */
   1.833 +	      if ((*arg == ' ') && (arg[1] != ')')) ++arg;
   1.834 +	    }
   1.835 +				/* RETURN list must be properly terminated */
   1.836 +	    if ((*arg++ != ')') || (*arg++ != ' ')) break;
   1.837 +				/* default return value is ALL */
   1.838 +	    if (!(retval &= 0x3fff)) retval = 0x4;
   1.839 +	  }
   1.840 +
   1.841 +				/* character set specified? */
   1.842 +	  if (((arg[0] == 'C') || (arg[0] == 'c')) &&
   1.843 +	      ((arg[1] == 'H') || (arg[1] == 'h')) &&
   1.844 +	      ((arg[2] == 'A') || (arg[2] == 'a')) &&
   1.845 +	      ((arg[3] == 'R') || (arg[3] == 'r')) &&
   1.846 +	      ((arg[4] == 'S') || (arg[4] == 's')) &&
   1.847 +	      ((arg[5] == 'E') || (arg[5] == 'e')) &&
   1.848 +	      ((arg[6] == 'T') || (arg[6] == 't')) &&
   1.849 +	      (arg[7] == ' ')) {
   1.850 +	    arg += 8;		/* yes, skip over CHARSET token */
   1.851 +	    if (s = snarf (&arg)) charset = cpystr (s);
   1.852 +	    else break;		/* missing character set */
   1.853 +	  }
   1.854 +				/* must have arguments here */
   1.855 +	  if (!(arg && *arg)) break;
   1.856 +	  if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
   1.857 +			      uidmax (stream),0) && !*arg) {
   1.858 +	    response = win;	/* looks good, try the search */
   1.859 +	    mail_search_full (stream,charset,pgm,SE_FREE);
   1.860 +				/* output search results if success */
   1.861 +	    if (response == win) {
   1.862 +	      if (retval) {	/* ESEARCH desired */
   1.863 +		PSOUT ("* ESEARCH (TAG ");
   1.864 +		pstring (tag);
   1.865 +		PBOUT (')');
   1.866 +		if (uid) PSOUT (" UID");
   1.867 +				/* wants MIN */
   1.868 +		if (retval & 0x1) {
   1.869 +		  for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
   1.870 +		       ++i);
   1.871 +		  if (i <= nmsgs) {
   1.872 +		    PSOUT (" MIN ");
   1.873 +		    pnum (uid ? mail_uid (stream,i) : i);
   1.874 +		  }
   1.875 +		}
   1.876 +				/* wants MAX */
   1.877 +		if (retval & 0x2) {
   1.878 +		  for (i = nmsgs; i && !mail_elt (stream,i)->searched; --i);
   1.879 +		  if (i) {
   1.880 +		    PSOUT (" MAX ");
   1.881 +		    pnum (uid ? mail_uid (stream,i) : i);
   1.882 +		  }
   1.883 +		}
   1.884 +
   1.885 +				/* wants ALL */
   1.886 +		if (retval & 0x4) {
   1.887 +		  unsigned long j;
   1.888 +				/* find first match */
   1.889 +		  for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
   1.890 +		       ++i);
   1.891 +		  if (i <= nmsgs) {
   1.892 +		    PSOUT (" ALL ");
   1.893 +		    pnum (uid ? mail_uid (stream,i) : i);
   1.894 +		    j = i;	/* last message output */
   1.895 +		  }
   1.896 +		  while (++i <= nmsgs) {
   1.897 +		    if (mail_elt (stream,i)->searched) {
   1.898 +		      while ((++i <= nmsgs) && mail_elt (stream,i)->searched);
   1.899 +				/* previous message is end of range */
   1.900 +		      if (j != --i) {
   1.901 +			PBOUT (':');
   1.902 +			pnum (uid ? mail_uid (stream,i) : i);
   1.903 +		      }
   1.904 +		    }
   1.905 +				/* search for next match */
   1.906 +		    while ((++i <= nmsgs) && !mail_elt (stream,i)->searched);
   1.907 +		    if (i <= nmsgs) {
   1.908 +		      PBOUT (',');
   1.909 +		      pnum (uid ? mail_uid (stream,i) : i);
   1.910 +		      j = i;	/* last message output */
   1.911 +		    }
   1.912 +		  }
   1.913 +		}
   1.914 +				/* wants COUNT */
   1.915 +		if (retval & 0x10) {
   1.916 +		  unsigned long j;
   1.917 +		  for (i = 1, j = 0; i <= nmsgs; ++i)
   1.918 +		    if (mail_elt (stream,i)->searched) ++j;
   1.919 +		  PSOUT (" COUNT ");
   1.920 +		  pnum (j);
   1.921 +		}
   1.922 +	      }
   1.923 +	      else {		/* standard search */
   1.924 +		PSOUT ("* SEARCH");
   1.925 +		for (i = 1; i <= nmsgs; ++i)
   1.926 +		  if (mail_elt (stream,i)->searched) {
   1.927 +		    PBOUT (' ');
   1.928 +		    pnum (uid ? mail_uid (stream,i) : i);
   1.929 +		  }
   1.930 +	      }
   1.931 +	      CRLF;
   1.932 +	    }
   1.933 +	  }
   1.934 +	  else mail_free_searchpgm (&pgm);
   1.935 +	  if (charset) fs_give ((void **) &charset);
   1.936 +	}
   1.937 +
   1.938 +	else			/* fall into select case */
   1.939 +      case SELECT:		/* valid whenever logged in */
   1.940 +				/* select new mailbox */
   1.941 +	  if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
   1.942 +		strcmp (cmd,"BBOARD"))) {
   1.943 +				/* single argument */
   1.944 +	  if (!(s = snarf (&arg))) response = misarg;
   1.945 +	  else if (arg) response = badarg;
   1.946 +	  else if (nameok (NIL,s = bboardname (cmd,s))) {
   1.947 +	    DRIVER *factory = mail_valid (NIL,s,NIL);
   1.948 +	    f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) |
   1.949 +	      ((*cmd == 'S') ? NIL : OP_READONLY);
   1.950 +	    curdriver = NIL;	/* no drivers known */
   1.951 +				/* no last uid */
   1.952 +	    uidvalidity = lastuid = 0;
   1.953 +	    if (lastid) fs_give ((void **) &lastid);
   1.954 +	    if (lastst.data) fs_give ((void **) &lastst.data);
   1.955 +	    nflags = 0;		/* force update */
   1.956 +	    nmsgs = recent = 0xffffffff;
   1.957 +	    if (factory && !strcmp (factory->name,"phile") &&
   1.958 +		(stream = mail_open (stream,s,f | OP_SILENT)) &&
   1.959 +		(response == win)) {
   1.960 +	      BODY *b;
   1.961 +				/* see if proxy open */
   1.962 +	      if ((mail_elt (stream,1)->rfc822_size < 400) &&
   1.963 +		  mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
   1.964 +		  (t = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
   1.965 +		  (i < MAILTMPLEN) && (t[0] == '{')) {
   1.966 +				/* copy and tie off */
   1.967 +		strncpy (tmp,t,i)[i] = '\0';
   1.968 +				/* nuke any trailing newline */
   1.969 +		if (t = strpbrk (tmp,"\r\n")) *t = '\0';
   1.970 +				/* try to open proxy */
   1.971 +		if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
   1.972 +		    (response == win) && tstream->nmsgs) {
   1.973 +		  s = tmp;	/* got it, close the link */
   1.974 +		  mail_close (stream);
   1.975 +		  stream = tstream;
   1.976 +		  tstream = NIL;
   1.977 +		}
   1.978 +	      }
   1.979 +				/* now give the exists event */
   1.980 +	      stream->silent = NIL;
   1.981 +	      mm_exists (stream,stream->nmsgs);
   1.982 +	    }
   1.983 +	    else if (!factory && isnewsproxy (s)) {
   1.984 +	      sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
   1.985 +	      stream = mail_open (stream,tmp,f);
   1.986 +	    }
   1.987 +				/* open stream normally then */
   1.988 +	    else stream = mail_open (stream,s,f);
   1.989 +
   1.990 +	    if (stream && (response == win)) {
   1.991 +	      state = OPEN;	/* note state open */
   1.992 +	      if (lastsel) fs_give ((void **) &lastsel);
   1.993 +				/* canonicalize INBOX */
   1.994 +	      if (!compare_cstring (s,"#MHINBOX"))
   1.995 +		lastsel = cpystr ("#MHINBOX");
   1.996 +	      else lastsel = cpystr (compare_cstring (s,"INBOX") ?
   1.997 +				     (char *) s : "INBOX");
   1.998 +				/* note readonly/readwrite */
   1.999 +	      response = stream->rdonly ? rowin : rwwin;
  1.1000 +	      if (anonymous)
  1.1001 +		syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
  1.1002 +			stream->mailbox,tcp_clienthost ());
  1.1003 +	      lastcheck = 0;	/* no last check */
  1.1004 +	    }
  1.1005 +	    else {		/* failed, nuke old selection */
  1.1006 +	      if (stream) stream = mail_close (stream);
  1.1007 +	      state = SELECT;	/* no mailbox open now */
  1.1008 +	      if (lastsel) fs_give ((void **) &lastsel);
  1.1009 +	      response = lose;	/* open failed */
  1.1010 +	    }
  1.1011 +	  }
  1.1012 +	}
  1.1013 +
  1.1014 +				/* APPEND message to mailbox */
  1.1015 +	else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  1.1016 +				/* parse mailbox name */
  1.1017 +	  if ((s = snarf (&arg)) && arg) {
  1.1018 +	    STRING st;		/* message stringstruct */
  1.1019 +	    APPENDDATA ad;
  1.1020 +	    ad.arg = arg;	/* command arguments */
  1.1021 +				/* no message yet */
  1.1022 +	    ad.flags = ad.date = ad.msg = NIL;
  1.1023 +	    ad.message = &st;	/* pointer to stringstruct to use */
  1.1024 +	    trycreate = NIL;	/* no trycreate status */
  1.1025 +	    if (!mail_append_multiple (NIL,s,append_msg,(void *) &ad)) {
  1.1026 +	      if (response == win) response = trycreate ? losetry : lose;
  1.1027 +				/* this can happen with #driver. hack */
  1.1028 +	      if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  1.1029 +	    }
  1.1030 +				/* clean up any message text left behind */
  1.1031 +	    if (ad.flags) fs_give ((void **) &ad.flags);
  1.1032 +	    if (ad.date) fs_give ((void **) &ad.date);
  1.1033 +	    if (ad.msg) fs_give ((void **) &ad.msg);
  1.1034 +	  }
  1.1035 +	  else response = misarg;
  1.1036 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1037 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1038 +	}
  1.1039 +				/* list mailboxes */
  1.1040 +	else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
  1.1041 +				/* get reference and mailbox argument */
  1.1042 +	  if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
  1.1043 +	    response = misarg;
  1.1044 +	  else if (arg) response = badarg;
  1.1045 +				/* make sure anonymous can't do bad things */
  1.1046 +	  else if (nameok (s,t)) {
  1.1047 +	    if (newsproxypattern (s,t,tmp,LONGT)) {
  1.1048 +	      proxylist = T;
  1.1049 +	      mail_list (NIL,"",tmp);
  1.1050 +	      proxylist = NIL;
  1.1051 +	    }
  1.1052 +	    else mail_list (NIL,s,t);
  1.1053 +	  }
  1.1054 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1055 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1056 +	}
  1.1057 +				/* scan mailboxes */
  1.1058 +	else if (!strcmp (cmd,"SCAN")) {
  1.1059 +				/* get arguments */
  1.1060 +	  if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
  1.1061 +		(u = snarf (&arg)))) response = misarg;
  1.1062 +	  else if (arg) response = badarg;
  1.1063 +				/* make sure anonymous can't do bad things */
  1.1064 +	  else if (nameok (s,t)) {
  1.1065 +	    if (newsproxypattern (s,t,tmp,NIL))
  1.1066 +	      mm_log ("SCAN not permitted for news",ERROR);
  1.1067 +	    else mail_scan (NIL,s,t,u);
  1.1068 +	  }
  1.1069 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1070 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1071 +	}
  1.1072 +				/* list subscribed mailboxes */
  1.1073 +	else if (!strcmp (cmd,"LSUB") || !strcmp (cmd,"RLSUB")) {
  1.1074 +				/* get reference and mailbox argument */
  1.1075 +	  if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
  1.1076 +	    response = misarg;
  1.1077 +	  else if (arg) response = badarg;
  1.1078 +				/* make sure anonymous can't do bad things */
  1.1079 +	  else if (nameok (s,t)) {
  1.1080 +	    if (newsproxypattern (s,t,tmp,NIL)) newsrc_lsub (NIL,tmp);
  1.1081 +	    else mail_lsub (NIL,s,t);
  1.1082 +	  }
  1.1083 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1084 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1085 +	}
  1.1086 +
  1.1087 +				/* find mailboxes */
  1.1088 +	else if (!strcmp (cmd,"FIND")) {
  1.1089 +				/* get subcommand and true argument */
  1.1090 +	  if (!(arg && (s = strtok (arg," \015\012")) && (s == cmd + 5) &&
  1.1091 +		(cmd[4] = ' ') && ucase (s) &&
  1.1092 +		(arg = strtok (NIL,"\015\012")) && (s = snarf_list (&arg))))
  1.1093 +	    response = misarg;	/* missing required argument */
  1.1094 +	  else if (arg) response = badarg;
  1.1095 +				/* punt on single-char wildcards */
  1.1096 +	  else if (strpbrk (s,"%?")) response =
  1.1097 +	    "%.80s NO IMAP2 ? and %% wildcards not supported: %.80s\015\012";
  1.1098 +	  else if (nameok (NIL,s)) {
  1.1099 +	    finding = T;	/* note that we are FINDing */
  1.1100 +				/* dispatch based on type */
  1.1101 +	    if (!strcmp (cmd,"FIND MAILBOXES") && !anonymous)
  1.1102 +	      mail_lsub (NIL,NIL,s);
  1.1103 +	    else if (!strcmp (cmd,"FIND ALL.MAILBOXES")) {
  1.1104 +				/* convert * to % for compatible behavior */
  1.1105 +	      for (t = s; *t; t++) if (*t == '*') *t = '%';
  1.1106 +	      mail_list (NIL,NIL,s);
  1.1107 +	    }
  1.1108 +	    else response = badcmd;
  1.1109 +	  }
  1.1110 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1111 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1112 +	}
  1.1113 +
  1.1114 +				/* status of mailbox */
  1.1115 +	else if (!strcmp (cmd,"STATUS")) {
  1.1116 +	  if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
  1.1117 +		(t = strchr (arg,')')) && (t - arg) && !t[1]))
  1.1118 +	    response = misarg;
  1.1119 +	  else {
  1.1120 +	    f = NIL;		/* initially no flags */
  1.1121 +	    *t = '\0';		/* tie off flag string */
  1.1122 +				/* read flags */
  1.1123 +	    t = strtok (ucase (arg)," ");
  1.1124 +	    do {		/* parse each one; unknown generate warning */
  1.1125 +	      if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
  1.1126 +	      else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
  1.1127 +	      else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
  1.1128 +	      else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
  1.1129 +	      else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
  1.1130 +	      else {
  1.1131 +		PSOUT ("* NO Unknown status flag ");
  1.1132 +		PSOUT (t);
  1.1133 +		CRLF;
  1.1134 +	      }
  1.1135 +	    } while (t = strtok (NIL," "));
  1.1136 +	    ping_mailbox (uid);	/* in case the fool did STATUS on open mbx */
  1.1137 +	    PFLUSH ();		/* make sure stdout is dumped in case slave */
  1.1138 +	    if (!compare_cstring (s,"INBOX")) s = "INBOX";
  1.1139 +	    else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
  1.1140 +	    if (state == LOGOUT) response = lose;
  1.1141 +				/* get mailbox status */
  1.1142 +	    else if (lastsel && (!strcmp (s,lastsel) ||
  1.1143 +				 (stream && !strcmp (s,stream->mailbox)))) {
  1.1144 +	      unsigned long unseen;
  1.1145 +				/* snarl at cretins which do this */
  1.1146 +	      PSOUT ("* NO CLIENT BUG DETECTED: STATUS on selected mailbox: ");
  1.1147 +	      PSOUT (s);
  1.1148 +	      CRLF;
  1.1149 +	      tmp[0] = ' '; tmp[1] = '\0';
  1.1150 +	      if (f & SA_MESSAGES)
  1.1151 +		sprintf (tmp + strlen (tmp)," MESSAGES %lu",stream->nmsgs);
  1.1152 +	      if (f & SA_RECENT)
  1.1153 +		sprintf (tmp + strlen (tmp)," RECENT %lu",stream->recent);
  1.1154 +	      if (f & SA_UNSEEN) {
  1.1155 +		for (i = 1,unseen = 0; i <= stream->nmsgs; i++)
  1.1156 +		  if (!mail_elt (stream,i)->seen) unseen++;
  1.1157 +		sprintf (tmp + strlen (tmp)," UNSEEN %lu",unseen);
  1.1158 +	      }
  1.1159 +	      if (f & SA_UIDNEXT)
  1.1160 +		sprintf (tmp + strlen (tmp)," UIDNEXT %lu",stream->uid_last+1);
  1.1161 +	      if (f & SA_UIDVALIDITY)
  1.1162 +		sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",
  1.1163 +			 stream->uid_validity);
  1.1164 +	      tmp[1] = '(';
  1.1165 +	      strcat (tmp,")\015\012");
  1.1166 +	      PSOUT ("* STATUS ");
  1.1167 +	      pastring (s);
  1.1168 +	      PSOUT (tmp);
  1.1169 +	    }
  1.1170 +	    else if (isnewsproxy (s)) {
  1.1171 +	      sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
  1.1172 +	      if (!mail_status (NIL,tmp,f)) response = lose;
  1.1173 +	    }
  1.1174 +	    else if (!mail_status (NIL,s,f)) response = lose;
  1.1175 +	  }
  1.1176 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1177 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1178 +	}
  1.1179 +
  1.1180 +				/* subscribe to mailbox */
  1.1181 +	else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  1.1182 +				/* get <mailbox> or MAILBOX <mailbox> */
  1.1183 +	  if (!(s = snarf (&arg))) response = misarg;
  1.1184 +	  else if (arg) {	/* IMAP2bis form */
  1.1185 +	    if (compare_cstring (s,"MAILBOX")) response = badarg;
  1.1186 +	    else if (!(s = snarf (&arg))) response = misarg;
  1.1187 +	    else if (arg) response = badarg;
  1.1188 +	    else mail_subscribe (NIL,s);
  1.1189 +	  }
  1.1190 +	  else if (isnewsproxy (s)) newsrc_update (NIL,s+6,':');
  1.1191 +	  else mail_subscribe (NIL,s);
  1.1192 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1193 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1194 +	}
  1.1195 +				/* unsubscribe to mailbox */
  1.1196 +	else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  1.1197 +				/* get <mailbox> or MAILBOX <mailbox> */
  1.1198 +	  if (!(s = snarf (&arg))) response = misarg;
  1.1199 +	  else if (arg) {	/* IMAP2bis form */
  1.1200 +	    if (compare_cstring (s,"MAILBOX")) response = badarg;
  1.1201 +	    else if (!(s = snarf (&arg))) response = misarg;
  1.1202 +	    else if (arg) response = badarg;
  1.1203 +	    else if (isnewsproxy (s)) newsrc_update (NIL,s+6,'!');
  1.1204 +	    else mail_unsubscribe (NIL,s);
  1.1205 +	  }
  1.1206 +	  else mail_unsubscribe (NIL,s);
  1.1207 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1208 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1209 +	}
  1.1210 +
  1.1211 +	else if (!strcmp (cmd,"NAMESPACE")) {
  1.1212 +	  if (arg) response = badarg;
  1.1213 +	  else {
  1.1214 +	    NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
  1.1215 +							     NIL);
  1.1216 +	    NAMESPACE *n;
  1.1217 +	    PARAMETER *p;
  1.1218 +	    PSOUT ("* NAMESPACE");
  1.1219 +	    if (ns) for (i = 0; i < 3; i++) {
  1.1220 +	      if (n = ns[i]) {
  1.1221 +		PSOUT (" (");
  1.1222 +		do {
  1.1223 +		  PBOUT ('(');
  1.1224 +		  pstring (n->name);
  1.1225 +		  switch (n->delimiter) {
  1.1226 +		  case '\\':	/* quoted delimiter */
  1.1227 +		  case '"':
  1.1228 +		    PSOUT (" \"\\\\\"");
  1.1229 +		    break;
  1.1230 +		  case '\0':	/* no delimiter */
  1.1231 +		    PSOUT (" NIL");
  1.1232 +		    break;
  1.1233 +		  default:	/* unquoted delimiter */
  1.1234 +		    PSOUT (" \"");
  1.1235 +		    PBOUT (n->delimiter);
  1.1236 +		    PBOUT ('"');
  1.1237 +		    break;
  1.1238 +		  }
  1.1239 +				/* NAMESPACE extensions are hairy */
  1.1240 +		  if (p = n->param) do {
  1.1241 +		    PBOUT (' ');
  1.1242 +		    pstring (p->attribute);
  1.1243 +		    PSOUT (" (");
  1.1244 +		    do pstring (p->value);
  1.1245 +		    while (p->next && !p->next->attribute && (p = p->next));
  1.1246 +		    PBOUT (')');
  1.1247 +		  } while (p = p->next);
  1.1248 +		  PBOUT (')');
  1.1249 +		} while (n = n->next);
  1.1250 +		PBOUT (')');
  1.1251 +	      }
  1.1252 +	      else PSOUT (" NIL");
  1.1253 +	    }
  1.1254 +	    else PSOUT (" NIL NIL NIL");
  1.1255 +	    CRLF;
  1.1256 +	  }
  1.1257 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1258 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1259 +	}
  1.1260 +
  1.1261 +				/* create mailbox */
  1.1262 +	else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  1.1263 +	  if (!(s = snarf (&arg))) response = misarg;
  1.1264 +	  else if (arg) response = badarg;
  1.1265 +	  else mail_create (NIL,s);
  1.1266 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1267 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1268 +	}
  1.1269 +				/* delete mailbox */
  1.1270 +	else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  1.1271 +	  if (!(s = snarf (&arg))) response = misarg;
  1.1272 +	  else if (arg) response = badarg;
  1.1273 +	  else {		/* make sure not selected */
  1.1274 +	    if (lastsel && (!strcmp (s,lastsel) ||
  1.1275 +			    (stream && !strcmp (s,stream->mailbox))))
  1.1276 +	      mm_log ("Can not DELETE the selected mailbox",ERROR);
  1.1277 +	    else mail_delete (NIL,s);
  1.1278 +	  }
  1.1279 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1280 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1281 +	}
  1.1282 +				/* rename mailbox */
  1.1283 +	else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  1.1284 +	  if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  1.1285 +	  else if (arg) response = badarg;
  1.1286 +	  else {		/* make sure not selected */
  1.1287 +	    if (!compare_cstring (s,"INBOX")) s = "INBOX";
  1.1288 +	    else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
  1.1289 +	    if (lastsel && (!strcmp (s,lastsel) ||
  1.1290 +			    (stream && !strcmp (s,stream->mailbox))))
  1.1291 +	      mm_log ("Can not RENAME the selected mailbox",ERROR);
  1.1292 +	    else mail_rename (NIL,s,t);
  1.1293 +	  }
  1.1294 +	  if (stream)		/* allow untagged EXPUNGE */
  1.1295 +	    mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
  1.1296 +	}
  1.1297 +
  1.1298 +				/* idle mode */
  1.1299 +	else if (!strcmp (cmd,"IDLE")) {
  1.1300 +				/* no arguments */
  1.1301 +	  if (arg) response = badarg;
  1.1302 +	  else {		/* tell client ready for argument */
  1.1303 +	    unsigned long donefake = 0;
  1.1304 +	    PSOUT ("+ Waiting for DONE\015\012");
  1.1305 +	    PFLUSH ();		/* dump output buffer */
  1.1306 +				/* inactivity countdown */
  1.1307 +	    i = ((TIMEOUT) / (IDLETIMER)) + 1;
  1.1308 +	    do {		/* main idle loop */
  1.1309 +	      if (!donefake) {	/* don't ping mailbox if faking */
  1.1310 +		mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,
  1.1311 +				 (void *) stream);
  1.1312 +		ping_mailbox (uid);
  1.1313 +				/* maybe do a checkpoint if not anonymous */
  1.1314 +		if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) {
  1.1315 +		  mail_check (stream);
  1.1316 +				/* cancel likely altwin from mail_check() */
  1.1317 +		  if (lsterr) fs_give ((void **) &lsterr);
  1.1318 +		  if (lstwrn) fs_give ((void **) &lstwrn);
  1.1319 +				/* remember last checkpoint */
  1.1320 +		  lastcheck = time (0);
  1.1321 +		}
  1.1322 +	      }
  1.1323 +	      if (lstwrn) {	/* have a warning? */
  1.1324 +		PSOUT ("* NO ");
  1.1325 +		PSOUT (lstwrn);
  1.1326 +		CRLF;
  1.1327 +		fs_give ((void **) &lstwrn);
  1.1328 +	      }
  1.1329 +	      if (!(i % 2)) {	/* prevent NAT timeouts */
  1.1330 +		sprintf (tmp,"* OK Timeout in %lu minutes\015\012",
  1.1331 +			 (i * IDLETIMER) / 60);
  1.1332 +		PSOUT (tmp);
  1.1333 +	      }
  1.1334 +				/* two minutes before the end... */
  1.1335 +	      if ((state == OPEN) && (i <= 2)) {
  1.1336 +		sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
  1.1337 +			 donefake = nmsgs + 1,recent + 1);
  1.1338 +		PSOUT (tmp);	/* prod client to wake up */
  1.1339 +	      }
  1.1340 +	      PFLUSH ();	/* dump output buffer */
  1.1341 +	    } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
  1.1342 +
  1.1343 +				/* time to exit idle loop */
  1.1344 +	    if (state != LOGOUT) {
  1.1345 +	      if (i) {		/* still have time left? */
  1.1346 +				/* yes, read expected DONE */
  1.1347 +		slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
  1.1348 +		if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
  1.1349 +		    ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
  1.1350 +		    ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
  1.1351 +		    ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
  1.1352 +		    (((tmp[4] != '\015') || (tmp[5] != '\012')) &&
  1.1353 +		     (tmp[4] != '\012')))
  1.1354 +		  response = "%.80s BAD Bogus IDLE continuation\015\012";
  1.1355 +		if (donefake) {	/* if faking at the end */
  1.1356 +				/* send EXPUNGE (should be just 1) */
  1.1357 +		  while (donefake > nmsgs) {
  1.1358 +		    sprintf (tmp,"* %lu EXPUNGE\015\012",donefake--);
  1.1359 +		    PSOUT (tmp);
  1.1360 +		  }
  1.1361 +		  sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
  1.1362 +			   nmsgs,recent);
  1.1363 +		  PSOUT (tmp);
  1.1364 +		}
  1.1365 +	      }
  1.1366 +	      else clkint ();	/* otherwise do autologout action */
  1.1367 +	    }
  1.1368 +	  }
  1.1369 +	}
  1.1370 +	else response = badcmd;
  1.1371 +	break;
  1.1372 +      default:
  1.1373 +        response = "%.80s BAD Unknown state for %.80s command\015\012";
  1.1374 +	break;
  1.1375 +      }
  1.1376 +
  1.1377 +      while (litplus.ok) {	/* any unread LITERAL+? */
  1.1378 +	litplus.ok = NIL;	/* yes, cancel it now */
  1.1379 +	clearerr (stdin);	/* clear stdin errors */
  1.1380 +	status = "discarding unread literal";
  1.1381 +				/* read literal and discard it */
  1.1382 +	while (i = (litplus.size > MAILTMPLEN) ? MAILTMPLEN : litplus.size) {
  1.1383 +	  if (state == LOGOUT) litplus.size = 0;
  1.1384 +	  else {
  1.1385 +	    settimeout (INPUTTIMEOUT);
  1.1386 +	    if (PSINR (tmp,i)) litplus.size -= i;
  1.1387 +	    else {
  1.1388 +	      ioerror (stdin,status);
  1.1389 +	      litplus.size = 0;	/* in case it continues */
  1.1390 +	    }
  1.1391 +	  }
  1.1392 +	}
  1.1393 +	settimeout (0);		/* stop timeout */
  1.1394 +				/* get new command tail */
  1.1395 +	slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
  1.1396 +				/* locate end of line */
  1.1397 +	if (t = strchr (tmp,'\012')) {
  1.1398 +				/* back over CR */
  1.1399 +	  if ((t > tmp) && (t[-1] == '\015')) --t;
  1.1400 +	  *t = NIL;		/* tie off CRLF */
  1.1401 +				/* possible LITERAL+? */
  1.1402 +	  if (((i = strlen (tmp)) > 3) && (tmp[i - 1] == '}') &&
  1.1403 +	      (tmp[i - 2] == '+') && isdigit (tmp[i - 3])) {
  1.1404 +				/* back over possible count */
  1.1405 +	    for (i -= 4; i && isdigit (tmp[i]); i--);
  1.1406 +	    if (tmp[i] == '{') {	/* found a literal? */
  1.1407 +	      litplus.ok = T;	/* yes, note LITERAL+ in effect, set size */
  1.1408 +	      litplus.size = strtoul (tmp + i + 1,NIL,10);
  1.1409 +	    }
  1.1410 +	  }
  1.1411 +	}
  1.1412 +	else flush ();		/* overlong line after LITERAL+, punt */
  1.1413 +      }
  1.1414 +      ping_mailbox (uid);	/* update mailbox status before response */
  1.1415 +      if (lstwrn && lsterr) {	/* output most recent warning */
  1.1416 +	PSOUT ("* NO ");
  1.1417 +	PSOUT (lstwrn);
  1.1418 +	CRLF;
  1.1419 +	fs_give ((void **) &lstwrn);
  1.1420 +      }
  1.1421 +
  1.1422 +      if (response == logwin) {	/* authentication win message */
  1.1423 +	sprintf (tmp,response,lstref ? "*" : tag);
  1.1424 +	PSOUT (tmp);		/* start response */
  1.1425 +	pcapability (1);	/* print logged-in capabilities */
  1.1426 +	PSOUT ("] User ");
  1.1427 +	PSOUT (user);
  1.1428 +	PSOUT (" authenticated\015\012");
  1.1429 +	if (lstref) {
  1.1430 +	  sprintf (tmp,response,tag);
  1.1431 +	  PSOUT (tmp);		/* start response */
  1.1432 +	  PSOUT ("[REFERRAL ");
  1.1433 +	  PSOUT (lstref);
  1.1434 +	  PSOUT ("] ");
  1.1435 +	  PSOUT (lasterror ());
  1.1436 +	  CRLF;
  1.1437 +	}
  1.1438 +      }
  1.1439 +      else if ((response == win) || (response == lose)) {
  1.1440 +	sprintf (tmp,response,tag);
  1.1441 +	PSOUT (tmp);
  1.1442 +	if (cauidvalidity) {	/* COPYUID/APPENDUID response? */
  1.1443 +	  sprintf (tmp,"[%.80sUID %lu ",(char *)
  1.1444 +		   ((s = strchr (cmd,' ')) ? s+1 : cmd),cauidvalidity);
  1.1445 +	  PSOUT (tmp);
  1.1446 +	  cauidvalidity = 0;	/* cancel response for future */
  1.1447 +	  if (csset) {
  1.1448 +	    pset (&csset);
  1.1449 +	    PBOUT (' ');
  1.1450 +	  }
  1.1451 +	  pset (&caset);
  1.1452 +	  PSOUT ("] ");
  1.1453 +	}
  1.1454 +	else if (lstref) {	/* have a referral? */
  1.1455 +	  PSOUT ("[REFERRAL ");
  1.1456 +	  PSOUT (lstref);
  1.1457 +	  PSOUT ("] ");
  1.1458 +	}
  1.1459 +	if (lsterr || lstwrn) PSOUT (lasterror ());
  1.1460 +	else {
  1.1461 +	  PSOUT (cmd);
  1.1462 +	  PSOUT ((response == win) ? " completed" : "failed");
  1.1463 +	}
  1.1464 +	CRLF;
  1.1465 +      }
  1.1466 +      else {			/* normal response */
  1.1467 +	if ((response == rowin) || (response == rwwin)) {
  1.1468 +	  if (lstwrn) {		/* output most recent warning */
  1.1469 +	    PSOUT ("* NO ");
  1.1470 +	    PSOUT (lstwrn);
  1.1471 +	    CRLF;
  1.1472 +	    fs_give ((void **) &lstwrn);
  1.1473 +	  }
  1.1474 +	}
  1.1475 +	sprintf (tmp,response,tag,cmd,lasterror ());
  1.1476 +	PSOUT (tmp);		/* output response */
  1.1477 +      }
  1.1478 +    }
  1.1479 +    PFLUSH ();			/* make sure output blatted */
  1.1480 +
  1.1481 +    if (autologouttime) {	/* have an autologout in effect? */
  1.1482 +				/* cancel if no longer waiting for login */
  1.1483 +      if (state != LOGIN) autologouttime = 0;
  1.1484 +				/* took too long to login */
  1.1485 +      else if (autologouttime < time (0)) {
  1.1486 +	logout = goodbye = "Autologout";
  1.1487 +	stream = NIL;
  1.1488 +	state = LOGOUT;		/* sayonara */
  1.1489 +      }
  1.1490 +    }
  1.1491 +  }
  1.1492 +  if (goodbye && !quell_events){/* have a goodbye message? */
  1.1493 +    PSOUT ("* BYE ");		/* utter it */
  1.1494 +    PSOUT (goodbye);
  1.1495 +    CRLF;
  1.1496 +    PFLUSH ();			/* make sure blatted */
  1.1497 +  }
  1.1498 +  syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
  1.1499 +	  user ? (char *) user : "???",tcp_clienthost ());
  1.1500 +				/* do logout hook if needed */
  1.1501 +  if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL))
  1.1502 +    (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
  1.1503 +  _exit (ret);			/* all done */
  1.1504 +  return ret;			/* stupid compilers */
  1.1505 +}
  1.1506 +
  1.1507 +/* Ping mailbox during each cycle.  Also check alerts
  1.1508 + * Accepts: last command was UID flag
  1.1509 + */
  1.1510 +
  1.1511 +void ping_mailbox (unsigned long uid)
  1.1512 +{
  1.1513 +  unsigned long i;
  1.1514 +  char tmp[MAILTMPLEN];
  1.1515 +  if (state == OPEN) {
  1.1516 +    if (!mail_ping (stream)) {	/* make sure stream still alive */
  1.1517 +      PSOUT ("* BYE ");
  1.1518 +      PSOUT (mylocalhost ());
  1.1519 +      PSOUT (" Fatal mailbox error: ");
  1.1520 +      PSOUT (lasterror ());
  1.1521 +      CRLF;
  1.1522 +      stream = NIL;		/* don't try to clean up stream */
  1.1523 +      state = LOGOUT;		/* go away */
  1.1524 +      syslog (LOG_INFO,
  1.1525 +	      "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1.1526 +	      user ? (char *) user : "???",tcp_clienthost (),
  1.1527 +	      (stream && stream->mailbox) ? stream->mailbox : "???",
  1.1528 +	      lasterror ());
  1.1529 +      return;
  1.1530 +    }
  1.1531 +				/* change in number of messages? */
  1.1532 +    if (existsquelled || (nmsgs != stream->nmsgs)) {
  1.1533 +      PSOUT ("* ");
  1.1534 +      pnum (nmsgs = stream->nmsgs);
  1.1535 +      PSOUT (" EXISTS\015\012");
  1.1536 +    }
  1.1537 +				/* change in recent messages? */
  1.1538 +    if (existsquelled || (recent != stream->recent)) {
  1.1539 +      PSOUT ("* ");
  1.1540 +      pnum (recent = stream->recent);
  1.1541 +      PSOUT (" RECENT\015\012");
  1.1542 +    }
  1.1543 +    existsquelled = NIL;	/* don't do this until asked again */
  1.1544 +    if (stream->uid_validity && (stream->uid_validity != uidvalidity)) {
  1.1545 +      PSOUT ("* OK [UIDVALIDITY ");
  1.1546 +      pnum (stream->uid_validity);
  1.1547 +      PSOUT ("] UID validity status\015\012* OK [UIDNEXT ");
  1.1548 +      pnum (stream->uid_last + 1);
  1.1549 +      PSOUT ("] Predicted next UID\015\012");
  1.1550 +      if (stream->uid_nosticky) {
  1.1551 +	PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
  1.1552 +	PSOUT (stream->mailbox);
  1.1553 +	CRLF;
  1.1554 +      }
  1.1555 +      uidvalidity = stream->uid_validity;
  1.1556 +    }
  1.1557 +
  1.1558 +				/* don't bother if driver changed */
  1.1559 +    if (curdriver == stream->dtb) {
  1.1560 +				/* first report any new flags */
  1.1561 +      if ((nflags < NUSERFLAGS) && stream->user_flags[nflags])
  1.1562 +	new_flags (stream);
  1.1563 +      for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
  1.1564 +	PSOUT ("* ");
  1.1565 +	pnum (i);
  1.1566 +	PSOUT (" FETCH (");
  1.1567 +	fetch_flags (i,NIL);	/* output changed flags */
  1.1568 +	if (uid) {		/* need to include UIDs in response? */
  1.1569 +	  PBOUT (' ');
  1.1570 +	  fetch_uid (i,NIL);
  1.1571 +	}
  1.1572 +	PSOUT (")\015\012");
  1.1573 +      }
  1.1574 +    }
  1.1575 +    else {			/* driver changed */
  1.1576 +      new_flags (stream);	/* send mailbox flags */
  1.1577 +      if (curdriver) {		/* note readonly/write if possible change */
  1.1578 +	PSOUT ("* OK [READ-");
  1.1579 +	PSOUT (stream->rdonly ? "ONLY" : "WRITE");
  1.1580 +	PSOUT ("] Mailbox status\015\012");
  1.1581 +      }
  1.1582 +      curdriver = stream->dtb;
  1.1583 +      if (nmsgs) {		/* get flags for all messages */
  1.1584 +	sprintf (tmp,"1:%lu",nmsgs);
  1.1585 +	mail_fetch_flags (stream,tmp,NIL);
  1.1586 +				/* don't do this if newsrc already did */
  1.1587 +	if (!(curdriver->flags & DR_NEWS)) {
  1.1588 +				/* find first unseen message */
  1.1589 +	  for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
  1.1590 +	  if (i <= nmsgs) {
  1.1591 +	    PSOUT ("* OK [UNSEEN ");
  1.1592 +	    pnum (i);
  1.1593 +	    PSOUT ("] first unseen message in ");
  1.1594 +	    PSOUT (stream->mailbox);
  1.1595 +	    CRLF;
  1.1596 +	  }
  1.1597 +	}
  1.1598 +      }
  1.1599 +    }
  1.1600 +  }
  1.1601 +  if (shutdowntime && (time (0) > shutdowntime + SHUTDOWNTIMER)) {
  1.1602 +    PSOUT ("* BYE Server shutting down\015\012");
  1.1603 +    state = LOGOUT;
  1.1604 +  }
  1.1605 +				/* don't do these stat()s every cycle */
  1.1606 +  else if (time (0) > alerttime + ALERTTIMER) { 
  1.1607 +    struct stat sbuf;
  1.1608 +				/* have a shutdown file? */
  1.1609 +    if (!stat (SHUTDOWNFILE,&sbuf)) {
  1.1610 +      PSOUT ("* OK [ALERT] Server shutting down shortly\015\012");
  1.1611 +      shutdowntime = time (0);
  1.1612 +    }
  1.1613 +    alerttime = time (0);	/* output any new alerts */
  1.1614 +    sysalerttime = palert (ALERTFILE,sysalerttime);
  1.1615 +    if (state != LOGIN)		/* do user alert if logged in */
  1.1616 +      useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
  1.1617 +  }
  1.1618 +}
  1.1619 +
  1.1620 +/* Print an alert file
  1.1621 + * Accepts: path of alert file
  1.1622 + *	    time of last printed alert file
  1.1623 + * Returns: updated time of last printed alert file
  1.1624 + */
  1.1625 +
  1.1626 +time_t palert (char *file,time_t oldtime)
  1.1627 +{
  1.1628 +  FILE *alf;
  1.1629 +  struct stat sbuf;
  1.1630 +  int c,lc = '\012';
  1.1631 +				/* have a new alert file? */
  1.1632 +  if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
  1.1633 +      !(alf = fopen (file,"r"))) return oldtime;
  1.1634 +				/* yes, display it */
  1.1635 +  while ((c = getc (alf)) != EOF) {
  1.1636 +    if (lc == '\012') PSOUT ("* OK [ALERT] ");
  1.1637 +    switch (c) {		/* output character */
  1.1638 +    case '\012':		/* newline means do CRLF */
  1.1639 +      CRLF;
  1.1640 +    case '\015':		/* flush CRs */
  1.1641 +    case '\0':			/* flush nulls */
  1.1642 +      break;
  1.1643 +    default:
  1.1644 +      PBOUT (c);		/* output all other characters */
  1.1645 +      break;
  1.1646 +    }
  1.1647 +    lc = c;			/* note previous character */
  1.1648 +  }
  1.1649 +  fclose (alf);
  1.1650 +  if (lc != '\012') CRLF;	/* final terminating CRLF */
  1.1651 +  return sbuf.st_mtime;		/* return updated last alert time */
  1.1652 +}
  1.1653 +
  1.1654 +/* Initialize file string structure for file stringstruct
  1.1655 + * Accepts: string structure
  1.1656 + *	    pointer to message data structure
  1.1657 + *	    size of string
  1.1658 + */
  1.1659 +
  1.1660 +void msg_string_init (STRING *s,void *data,unsigned long size)
  1.1661 +{
  1.1662 +  MSGDATA *md = (MSGDATA *) data;
  1.1663 +  s->data = data;		/* note stream/msgno and header length */
  1.1664 +#if 0
  1.1665 +  s->size = size;		/* message size */
  1.1666 +  s->curpos = s->chunk =	/* load header */
  1.1667 +    mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
  1.1668 +			   FT_PREFETCHTEXT | FT_PEEK);
  1.1669 +#else	/* This kludge is necessary because of broken mail stores */
  1.1670 +  mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
  1.1671 +  s->curpos = s->chunk =	/* load header */
  1.1672 +    mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PEEK);
  1.1673 +  s->size += s->data1;		/* header + body size */
  1.1674 +#endif
  1.1675 +  s->cursize = s->chunksize = s->data1;
  1.1676 +  s->offset = 0;		/* offset is start of message */
  1.1677 +}
  1.1678 +
  1.1679 +
  1.1680 +/* Get next character from file stringstruct
  1.1681 + * Accepts: string structure
  1.1682 + * Returns: character, string structure chunk refreshed
  1.1683 + */
  1.1684 +
  1.1685 +char msg_string_next (STRING *s)
  1.1686 +{
  1.1687 +  char c = *s->curpos++;	/* get next byte */
  1.1688 +  SETPOS (s,GETPOS (s));	/* move to next chunk */
  1.1689 +  return c;			/* return the byte */
  1.1690 +}
  1.1691 +
  1.1692 +
  1.1693 +/* Set string pointer position for file stringstruct
  1.1694 + * Accepts: string structure
  1.1695 + *	    new position
  1.1696 + */
  1.1697 +
  1.1698 +void msg_string_setpos (STRING *s,unsigned long i)
  1.1699 +{
  1.1700 +  MSGDATA *md = (MSGDATA *) s->data;
  1.1701 +  if (i < s->data1) {		/* want header? */
  1.1702 +    s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
  1.1703 +    s->chunksize = s->data1;	/* header length */
  1.1704 +    s->offset = 0;		/* offset is start of message */
  1.1705 +  }
  1.1706 +  else if (i < s->size) {	/* want body */
  1.1707 +    s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
  1.1708 +    s->chunksize = s->size - s->data1;
  1.1709 +    s->offset = s->data1;	/* offset is end of header */
  1.1710 +  }
  1.1711 +  else {			/* off end of message */
  1.1712 +    s->chunk = NIL;		/* make sure that we crack on this then */
  1.1713 +    s->chunksize = 1;		/* make sure SNX cracks the right way... */
  1.1714 +    s->offset = i;
  1.1715 +  }
  1.1716 +				/* initial position and size */
  1.1717 +  s->curpos = s->chunk + (i -= s->offset);
  1.1718 +  s->cursize = s->chunksize - i;
  1.1719 +}
  1.1720 +
  1.1721 +/* Send flags for stream
  1.1722 + * Accepts: MAIL stream
  1.1723 + *	    scratch buffer
  1.1724 + */
  1.1725 +
  1.1726 +void new_flags (MAILSTREAM *stream)
  1.1727 +{
  1.1728 +  int i,c;
  1.1729 +  PSOUT ("* FLAGS (");
  1.1730 +  for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
  1.1731 +    PSOUT (stream->user_flags[i]);
  1.1732 +    PBOUT (' ');
  1.1733 +    nflags = i + 1;
  1.1734 +  }
  1.1735 +  PSOUT ("\\Answered \\Flagged \\Deleted \\Draft \\Seen)\015\012* OK [PERMANENTFLAGS (");
  1.1736 +  for (i = c = 0; i < NUSERFLAGS; i++)
  1.1737 +    if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
  1.1738 +      put_flag (&c,stream->user_flags[i]);
  1.1739 +  if (stream->kwd_create) put_flag (&c,"\\*");
  1.1740 +  if (stream->perm_answered) put_flag (&c,"\\Answered");
  1.1741 +  if (stream->perm_flagged) put_flag (&c,"\\Flagged");
  1.1742 +  if (stream->perm_deleted) put_flag (&c,"\\Deleted");
  1.1743 +  if (stream->perm_draft) put_flag (&c,"\\Draft");
  1.1744 +  if (stream->perm_seen) put_flag (&c,"\\Seen");
  1.1745 +  PSOUT (")] Permanent flags\015\012");
  1.1746 +}
  1.1747 +
  1.1748 +/* Set timeout
  1.1749 + * Accepts: desired interval
  1.1750 + */
  1.1751 +
  1.1752 +void settimeout (unsigned int i)
  1.1753 +{
  1.1754 +				/* limit if not logged in */
  1.1755 +  if (i) alarm ((state == LOGIN) ? LOGINTIMEOUT : i);
  1.1756 +  else alarm (0);
  1.1757 +}
  1.1758 +
  1.1759 +
  1.1760 +/* Clock interrupt
  1.1761 + * Returns only if critical code in progress
  1.1762 + */
  1.1763 +
  1.1764 +void clkint (void)
  1.1765 +{
  1.1766 +  settimeout (0);		/* disable all interrupts */
  1.1767 +  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1.1768 +  logout = "Autologout";
  1.1769 +  goodbye = "Autologout (idle for too long)";
  1.1770 +  if (critical) {		/* must defer if in critical code(?) */
  1.1771 +    close (0);			/* kill stdin */
  1.1772 +    state = LOGOUT;		/* die as soon as we can */
  1.1773 +  }
  1.1774 +  else longjmp (jmpenv,1);	/* die now */
  1.1775 +}
  1.1776 +
  1.1777 +
  1.1778 +/* Kiss Of Death interrupt
  1.1779 + * Returns only if critical code in progress
  1.1780 + */
  1.1781 +
  1.1782 +void kodint (void)
  1.1783 +{
  1.1784 +  settimeout (0);		/* disable all interrupts */
  1.1785 +  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1.1786 +  logout = goodbye = "Killed (lost mailbox lock)";
  1.1787 +  if (critical) {		/* must defer if in critical code */
  1.1788 +    close (0);			/* kill stdin */
  1.1789 +    state = LOGOUT;		/* die as soon as we can */
  1.1790 +  }
  1.1791 +  else longjmp (jmpenv,1);	/* die now */
  1.1792 +}
  1.1793 +
  1.1794 +/* Hangup interrupt
  1.1795 + * Returns only if critical code in progress
  1.1796 + */
  1.1797 +
  1.1798 +void hupint (void)
  1.1799 +{
  1.1800 +  settimeout (0);		/* disable all interrupts */
  1.1801 +  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1.1802 +  logout = "Hangup";
  1.1803 +  goodbye = NIL;		/* other end is already gone */
  1.1804 +  if (critical) {		/* must defer if in critical code */
  1.1805 +    close (0);			/* kill stdin */
  1.1806 +    close (1);			/* and stdout */
  1.1807 +    state = LOGOUT;		/* die as soon as we can */
  1.1808 +  }
  1.1809 +  else longjmp (jmpenv,1);	/* die now */
  1.1810 +}
  1.1811 +
  1.1812 +
  1.1813 +/* Termination interrupt
  1.1814 + * Returns only if critical code in progress
  1.1815 + */
  1.1816 +
  1.1817 +void trmint (void)
  1.1818 +{
  1.1819 +  settimeout (0);		/* disable all interrupts */
  1.1820 +  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1.1821 +  logout = goodbye = "Killed (terminated)";
  1.1822 +  /* Make no attempt at graceful closure since a shutdown may be in
  1.1823 +   * progress, and we won't have any time to do mail_close() actions
  1.1824 +   */
  1.1825 +  stream = NIL;
  1.1826 +  if (critical) {		/* must defer if in critical code */
  1.1827 +    close (0);			/* kill stdin */
  1.1828 +    close (1);			/* and stdout */
  1.1829 +    state = LOGOUT;		/* die as soon as we can */
  1.1830 +  }
  1.1831 +  else longjmp (jmpenv,1);	/* die now */
  1.1832 +}
  1.1833 +
  1.1834 +/* The routines on this and the next page eschew the use of non-syscall libc
  1.1835 + * routines (especially stdio) for a reason.  Also, these hideous #if
  1.1836 + * condtionals need to be replaced.
  1.1837 + */
  1.1838 +
  1.1839 +#ifndef unix
  1.1840 +#define unix 0
  1.1841 +#endif
  1.1842 +
  1.1843 +
  1.1844 +/* Status request interrupt
  1.1845 + * Always returns
  1.1846 + */
  1.1847 +
  1.1848 +void staint (void)
  1.1849 +{
  1.1850 +#if unix
  1.1851 +  int fd;
  1.1852 +  char *s,buf[8*MAILTMPLEN];
  1.1853 +  unsigned long pid = getpid ();
  1.1854 +				/* build file name */
  1.1855 +  s = nout (sout (buf,"/tmp/imapd-status."),pid,10);
  1.1856 +  if (user) s = sout (sout (s,"."),user);
  1.1857 +  *s = '\0';			/* tie off file name */
  1.1858 +  if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) {
  1.1859 +    fchmod (fd,0666);
  1.1860 +    s = nout (sout (buf,"PID="),pid,10);
  1.1861 +    if (user) s = sout (sout (s,", user="),user);
  1.1862 +    switch (state) {
  1.1863 +    case LOGIN:
  1.1864 +      s = sout (s,", not logged in");
  1.1865 +      break;
  1.1866 +    case SELECT:
  1.1867 +      s = sout (s,", logged in");
  1.1868 +      break;
  1.1869 +    case OPEN:
  1.1870 +      s = sout (s,", mailbox open");
  1.1871 +      break;
  1.1872 +    case LOGOUT:
  1.1873 +      s = sout (s,", logging out");
  1.1874 +      break;
  1.1875 +    }
  1.1876 +    if (stream && stream->mailbox)
  1.1877 +      s = sout (sout (s,"\nmailbox="),stream->mailbox);
  1.1878 +    *s++ = '\n';
  1.1879 +    if (status) {
  1.1880 +      s = sout (s,status);
  1.1881 +      if (cmd) s = sout (sout (s,", last command="),cmd);
  1.1882 +    }
  1.1883 +    else s = sout (sout (s,cmd)," in progress");
  1.1884 +    *s++ = '\n';
  1.1885 +    write (fd,buf,s-buf);
  1.1886 +    close (fd);
  1.1887 +  }
  1.1888 +#endif
  1.1889 +}
  1.1890 +
  1.1891 +/* Write string
  1.1892 + * Accepts: destination string pointer
  1.1893 + *	    string
  1.1894 + * Returns: updated string pointer
  1.1895 + */
  1.1896 +
  1.1897 +char *sout (char *s,char *t)
  1.1898 +{
  1.1899 +  while (*t) *s++ = *t++;
  1.1900 +  return s;
  1.1901 +}
  1.1902 +
  1.1903 +
  1.1904 +/* Write number
  1.1905 + * Accepts: destination string pointer
  1.1906 + *	    number
  1.1907 + *	    base
  1.1908 + * Returns: updated string pointer
  1.1909 + */
  1.1910 +
  1.1911 +char *nout (char *s,unsigned long n,unsigned long base)
  1.1912 +{
  1.1913 +  char stack[256];
  1.1914 +  char *t = stack;
  1.1915 +				/* push PID digits on stack */
  1.1916 +  do *t++ = (char) (n % base) + '0';
  1.1917 +  while (n /= base);
  1.1918 +				/* pop digits from stack */
  1.1919 +  while (t > stack) *s++ = *--t;
  1.1920 +  return s;
  1.1921 +}
  1.1922 +
  1.1923 +/* Slurp a command line
  1.1924 + * Accepts: buffer pointer
  1.1925 + *	    buffer size
  1.1926 + *	    input timeout
  1.1927 + */
  1.1928 +
  1.1929 +void slurp (char *s,int n,unsigned long timeout)
  1.1930 +{
  1.1931 +  memset (s,'\0',n);		/* zap buffer */
  1.1932 +  if (state != LOGOUT) {	/* get a command under timeout */
  1.1933 +    settimeout (timeout);
  1.1934 +    clearerr (stdin);		/* clear stdin errors */
  1.1935 +    status = "reading line";
  1.1936 +    if (!PSIN (s,n-1)) ioerror (stdin,status);
  1.1937 +    settimeout (0);		/* make sure timeout disabled */
  1.1938 +    status = NIL;
  1.1939 +  }
  1.1940 +}
  1.1941 +
  1.1942 +
  1.1943 +/* Read a literal
  1.1944 + * Accepts: destination buffer (must be size+1 for trailing NUL)
  1.1945 + *	    size of buffer (must be less than 4294967295)
  1.1946 + */
  1.1947 +
  1.1948 +void inliteral (char *s,unsigned long n)
  1.1949 +{
  1.1950 +  unsigned long i;
  1.1951 +  if (litplus.ok) {		/* no more LITERAL+ to worry about */
  1.1952 +    litplus.ok = NIL;
  1.1953 +    litplus.size = 0;
  1.1954 +  }
  1.1955 +  else {			/* otherwise tell client ready for argument */
  1.1956 +    PSOUT ("+ Ready for argument\015\012");
  1.1957 +    PFLUSH ();			/* dump output buffer */
  1.1958 +  }
  1.1959 +  clearerr (stdin);		/* clear stdin errors */
  1.1960 +  memset (s,'\0',n+1);		/* zap buffer */
  1.1961 +  status = "reading literal";
  1.1962 +  while (n) {			/* get data under timeout */
  1.1963 +    if (state == LOGOUT) n = 0;
  1.1964 +    else {
  1.1965 +      settimeout (INPUTTIMEOUT);
  1.1966 +      i = min (n,8192);		/* must read at least 8K within timeout */
  1.1967 +      if (PSINR (s,i)) {
  1.1968 +	s += i;
  1.1969 +	n -= i;
  1.1970 +      }
  1.1971 +      else {
  1.1972 +	ioerror (stdin,status);
  1.1973 +	n = 0;			/* in case it continues */
  1.1974 +      }
  1.1975 +      settimeout (0);		/* stop timeout */
  1.1976 +    }
  1.1977 +  }
  1.1978 +}
  1.1979 +
  1.1980 +/* Flush until newline seen
  1.1981 + * Returns: NIL, always
  1.1982 + */
  1.1983 +
  1.1984 +unsigned char *flush (void)
  1.1985 +{
  1.1986 +  int c;
  1.1987 +  if (state != LOGOUT) {
  1.1988 +    settimeout (INPUTTIMEOUT);
  1.1989 +    clearerr (stdin);		/* clear stdin errors */
  1.1990 +    status = "flushing line";
  1.1991 +    while ((c = PBIN ()) != '\012') if (c == EOF) ioerror (stdin,status);
  1.1992 +    settimeout (0);		/* make sure timeout disabled */
  1.1993 +  }
  1.1994 +  response = "%.80s BAD Command line too long\015\012";
  1.1995 +  status = NIL;
  1.1996 +  return NIL;
  1.1997 +}
  1.1998 +
  1.1999 +
  1.2000 +/* Report command stream error and die
  1.2001 + * Accepts: stdin or stdout (whichever got the error)
  1.2002 + *	    reason (what caller was doing)
  1.2003 + */
  1.2004 +
  1.2005 +void ioerror (FILE *f,char *reason)
  1.2006 +{
  1.2007 +  static char msg[MAILTMPLEN];
  1.2008 +  char *s,*t;
  1.2009 +  if (logout) {			/* say nothing if already dying */
  1.2010 +    settimeout (0);		/* disable all interrupts */
  1.2011 +    server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  1.2012 +				/* write error string */
  1.2013 +    for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect",
  1.2014 +	   t = logout = msg; *s; *t++ = *s++);
  1.2015 +    for (s = ", while "; *s; *t++ = *s++);
  1.2016 +    for (s = reason; *s; *t++ = *s++);
  1.2017 +    if (critical) {		/* must defer if in critical code */
  1.2018 +      close (0);		/* kill stdin */
  1.2019 +      close (1);		/* and stdout */
  1.2020 +      state = LOGOUT;		/* die as soon as we can */
  1.2021 +    }
  1.2022 +    else longjmp (jmpenv,1);	/* die now */
  1.2023 +  }
  1.2024 +}
  1.2025 +
  1.2026 +/* Parse an IMAP astring
  1.2027 + * Accepts: pointer to argument text pointer
  1.2028 + *	    pointer to returned size
  1.2029 + *	    pointer to returned delimiter
  1.2030 + * Returns: argument
  1.2031 + */
  1.2032 +
  1.2033 +unsigned char *parse_astring (unsigned char **arg,unsigned long *size,
  1.2034 +			      unsigned char *del)
  1.2035 +{
  1.2036 +  unsigned long i;
  1.2037 +  unsigned char c,*s,*t,*v;
  1.2038 +  if (!*arg) return NIL;	/* better be an argument */
  1.2039 +  switch (**arg) {		/* see what the argument is */
  1.2040 +  default:			/* atom */
  1.2041 +    for (s = t = *arg, i = 0;
  1.2042 +	 (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
  1.2043 +	 (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
  1.2044 +	 (*t != '\\'); ++t,++i);
  1.2045 +    if (*size = i) break;	/* got atom if non-empty */
  1.2046 +  case ')': case '%': case '*': case '\\': case '\0': case ' ':
  1.2047 +   return NIL;			/* empty atom is a bogon */
  1.2048 +  case '"':			/* hunt for trailing quote */
  1.2049 +    for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
  1.2050 +				/* quote next character */
  1.2051 +      if (c == '\\') switch (c = *t++) {
  1.2052 +      case '"': case '\\': break;
  1.2053 +      default: return NIL;	/* invalid quote-next */
  1.2054 +      }
  1.2055 +				/* else must be a CHAR */
  1.2056 +      if (!c || (c & 0x80)) return NIL;
  1.2057 +    }
  1.2058 +    *v = '\0';			/* tie off string */
  1.2059 +    *size = v - s;		/* return size */
  1.2060 +    break;
  1.2061 +
  1.2062 +  case '{':			/* literal string */
  1.2063 +    s = *arg + 1;		/* get size */
  1.2064 +    if (!isdigit (*s)) return NIL;
  1.2065 +    if ((*size = i = strtoul (s,(char **) &t,10)) > MAXCLIENTLIT) {
  1.2066 +      mm_notify (NIL,"Absurdly long client literal",ERROR);
  1.2067 +      syslog (LOG_INFO,"Overlong (%lu) client literal user=%.80s host=%.80s",
  1.2068 +	      i,user ? (char *) user : "???",tcp_clienthost ());
  1.2069 +      return NIL;
  1.2070 +    }
  1.2071 +    switch (*t) {		/* validate end of literal */
  1.2072 +    case '+':			/* non-blocking literal */
  1.2073 +      if (*++t != '}') return NIL;
  1.2074 +    case '}':
  1.2075 +      if (!t[1]) break;		/* OK if end of line */
  1.2076 +    default:
  1.2077 +      return NIL;		/* bad literal */
  1.2078 +    }
  1.2079 +    if (litsp >= LITSTKLEN) {	/* make sure don't overflow stack */
  1.2080 +      mm_notify (NIL,"Too many literals in command",ERROR);
  1.2081 +      return NIL;
  1.2082 +    }
  1.2083 +				/* get a literal buffer */
  1.2084 +    inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i);
  1.2085 +    				/* get new command tail */
  1.2086 +    slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT);
  1.2087 +    if (!strchr (t,'\012')) return flush ();
  1.2088 +				/* reset strtok mechanism, tie off if done */
  1.2089 +    if (!strtok (t,"\015\012")) *t = '\0';
  1.2090 +				/* possible LITERAL+? */
  1.2091 +    if (((i = strlen (t)) > 3) && (t[i - 1] == '}') &&
  1.2092 +	(t[i - 2] == '+') && isdigit (t[i - 3])) {
  1.2093 +				/* back over possible count */
  1.2094 +      for (i -= 4; i && isdigit (t[i]); i--);
  1.2095 +      if (t[i] == '{') {	/* found a literal? */
  1.2096 +	litplus.ok = T;		/* yes, note LITERAL+ in effect, set size */
  1.2097 +	litplus.size = strtoul (t + i + 1,NIL,10);
  1.2098 +      }
  1.2099 +    }
  1.2100 +    break;
  1.2101 +  }
  1.2102 +  if (*del = *t) {		/* have a delimiter? */
  1.2103 +    *t++ = '\0';		/* yes, stomp on it */
  1.2104 +    *arg = t;			/* update argument pointer */
  1.2105 +  }
  1.2106 +  else *arg = NIL;		/* no more arguments */
  1.2107 +  return s;
  1.2108 +}
  1.2109 +
  1.2110 +/* Snarf a command argument (simple jacket into parse_astring())
  1.2111 + * Accepts: pointer to argument text pointer
  1.2112 + * Returns: argument
  1.2113 + */
  1.2114 +
  1.2115 +unsigned char *snarf (unsigned char **arg)
  1.2116 +{
  1.2117 +  unsigned long i;
  1.2118 +  unsigned char c;
  1.2119 +  unsigned char *s = parse_astring (arg,&i,&c);
  1.2120 +  return ((c == ' ') || !c) ? s : NIL;
  1.2121 +}
  1.2122 +
  1.2123 +
  1.2124 +/* Snarf a BASE64 argument for SASL-IR
  1.2125 + * Accepts: pointer to argument text pointer
  1.2126 + * Returns: argument
  1.2127 + */
  1.2128 +
  1.2129 +unsigned char *snarf_base64 (unsigned char **arg)
  1.2130 +{
  1.2131 +  unsigned char *ret = *arg;
  1.2132 +  unsigned char *s = ret + 1;
  1.2133 +  static char base64mask[256] = {
  1.2134 +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1.2135 +   0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
  1.2136 +   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
  1.2137 +   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
  1.2138 +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1.2139 +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1.2140 +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1.2141 +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  1.2142 +  };
  1.2143 +  if (*(ret = *arg) == '=');	/* easy case if zero-length argument */
  1.2144 +				/* must be at least one BASE64 char */
  1.2145 +  else if (!base64mask[*ret]) return NIL;
  1.2146 +  else {			/* quick and dirty */
  1.2147 +    while (base64mask[*s++]);	/* scan until end of BASE64 */
  1.2148 +    if (*s == '=') ++s;		/* allow up to two padding chars */
  1.2149 +    if (*s == '=') ++s;
  1.2150 +  }
  1.2151 +  switch (*s) {			/* anything following the argument? */
  1.2152 +  case ' ':			/* another argument */
  1.2153 +    *s++ = '\0';		/* tie off previous argument */
  1.2154 +    *arg = s;			/* and update argument pointer */
  1.2155 +    break;
  1.2156 +  case '\0':			/* end of command */
  1.2157 +    *arg = NIL;
  1.2158 +    break;
  1.2159 +  default:			/* syntax error */
  1.2160 +    return NIL;
  1.2161 +  }
  1.2162 +  return ret;			/* return BASE64 string */
  1.2163 +}
  1.2164 +
  1.2165 +/* Snarf a list command argument (simple jacket into parse_astring())
  1.2166 + * Accepts: pointer to argument text pointer
  1.2167 + * Returns: argument
  1.2168 + */
  1.2169 +
  1.2170 +unsigned char *snarf_list (unsigned char **arg)
  1.2171 +{
  1.2172 +  unsigned long i;
  1.2173 +  unsigned char c,*s,*t;
  1.2174 +  if (!*arg) return NIL;	/* better be an argument */
  1.2175 +  switch (**arg) {
  1.2176 +  default:			/* atom and/or wildcard chars */
  1.2177 +    for (s = t = *arg, i = 0;
  1.2178 +	 (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
  1.2179 +	 (*t != '"') && (*t != '\\'); ++t,++i);
  1.2180 +    if (c = *t) {		/* have a delimiter? */
  1.2181 +      *t++ = '\0';		/* stomp on it */
  1.2182 +      *arg = t;			/* update argument pointer */
  1.2183 +    }
  1.2184 +    else *arg = NIL;
  1.2185 +    break;
  1.2186 +  case ')': case '\\': case '\0': case ' ':
  1.2187 +    return NIL;			/* empty name is bogus */
  1.2188 +  case '"':			/* quoted string? */
  1.2189 +  case '{':			/* or literal? */
  1.2190 +    s = parse_astring (arg,&i,&c);
  1.2191 +    break;
  1.2192 +  }
  1.2193 +  return ((c == ' ') || !c) ? s : NIL;
  1.2194 +}
  1.2195 +
  1.2196 +/* Get a list of header lines
  1.2197 + * Accepts: pointer to string pointer
  1.2198 + *	    pointer to list flag
  1.2199 + * Returns: string list
  1.2200 + */
  1.2201 +
  1.2202 +STRINGLIST *parse_stringlist (unsigned char **s,int *list)
  1.2203 +{
  1.2204 +  char c = ' ',*t;
  1.2205 +  unsigned long i;
  1.2206 +  STRINGLIST *ret = NIL,*cur = NIL;
  1.2207 +  if (*s && **s == '(') {	/* proper list? */
  1.2208 +    ++*s;			/* for each item in list */
  1.2209 +    while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
  1.2210 +				/* get new block */
  1.2211 +      if (cur) cur = cur->next = mail_newstringlist ();
  1.2212 +      else cur = ret = mail_newstringlist ();
  1.2213 +				/* note text */
  1.2214 +      cur->text.data = (unsigned char *) fs_get (i + 1);
  1.2215 +      memcpy (cur->text.data,t,i);
  1.2216 +      cur->text.size = i;		/* and size */
  1.2217 +    }
  1.2218 +				/* must be end of list */
  1.2219 +    if (c != ')') mail_free_stringlist (&ret);
  1.2220 +  }
  1.2221 +  if (t = *s) {			/* need to reload strtok() state? */
  1.2222 +				/* end of a list? */
  1.2223 +    if (*list && (*t == ')') && !t[1]) *list = NIL;
  1.2224 +    else {
  1.2225 +      *--t = ' ';		/* patch a space back in */
  1.2226 +      *--t = 'x';		/* and a hokey character before that */
  1.2227 +      t = strtok (t," ");	/* reset to *s */
  1.2228 +    }
  1.2229 +  }
  1.2230 +  return ret;
  1.2231 +}
  1.2232 +
  1.2233 +/* Get value of UID * for criteria parsing
  1.2234 + * Accepts: stream
  1.2235 + * Returns: maximum UID
  1.2236 + */
  1.2237 +
  1.2238 +unsigned long uidmax (MAILSTREAM *stream)
  1.2239 +{
  1.2240 +  return stream->nmsgs ? mail_uid (stream,stream->nmsgs) : 0xffffffff;
  1.2241 +}
  1.2242 +
  1.2243 +
  1.2244 +/* Parse search criteria
  1.2245 + * Accepts: search program to write criteria into
  1.2246 + *	    pointer to argument text pointer
  1.2247 + *	    maximum message number
  1.2248 + *	    maximum UID
  1.2249 + *	    logical nesting depth
  1.2250 + * Returns: T if success, NIL if error
  1.2251 + */
  1.2252 +
  1.2253 +long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
  1.2254 +		     unsigned long maxuid,unsigned long depth)
  1.2255 +{
  1.2256 +  if (arg && *arg) {		/* must be an argument */
  1.2257 +				/* parse criteria */
  1.2258 +    do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
  1.2259 +				/* as long as a space delimiter */
  1.2260 +    while (**arg == ' ' && (*arg)++);
  1.2261 +				/* failed if not end of criteria */
  1.2262 +    if (**arg && **arg != ')') return NIL;
  1.2263 +  }
  1.2264 +  return T;			/* success */
  1.2265 +}
  1.2266 +
  1.2267 +/* Parse a search criterion
  1.2268 + * Accepts: search program to write criterion into
  1.2269 + *	    pointer to argument text pointer
  1.2270 + *	    maximum message number
  1.2271 + *	    maximum UID
  1.2272 + *	    logical nesting depth
  1.2273 + * Returns: T if success, NIL if error
  1.2274 + */
  1.2275 +
  1.2276 +long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
  1.2277 +		      unsigned long maxuid,unsigned long depth)
  1.2278 +{
  1.2279 +  unsigned long i;
  1.2280 +  unsigned char c = NIL,*s,*t,*v,*tail,*del;
  1.2281 +  SEARCHSET **set;
  1.2282 +  SEARCHPGMLIST **not;
  1.2283 +  SEARCHOR **or;
  1.2284 +  SEARCHHEADER **hdr;
  1.2285 +  long ret = NIL;
  1.2286 +				/* better be an argument */
  1.2287 +  if ((depth > 500) || !(arg && *arg));
  1.2288 +  else if (**arg == '(') {	/* list of criteria? */
  1.2289 +    (*arg)++;			/* yes, parse the criteria */
  1.2290 +    if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
  1.2291 +      (*arg)++;			/* skip closing paren */
  1.2292 +      ret = T;			/* successful parse of list */
  1.2293 +    }
  1.2294 +  }
  1.2295 +  else {			/* find end of criterion */
  1.2296 +    if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
  1.2297 +    c = *(del = tail);		/* remember the delimiter */
  1.2298 +    *del = '\0';		/* tie off criterion */
  1.2299 +    switch (*ucase (s)) {	/* dispatch based on character */
  1.2300 +    case '*':			/* sequence */
  1.2301 +    case '0': case '1': case '2': case '3': case '4':
  1.2302 +    case '5': case '6': case '7': case '8': case '9':
  1.2303 +      if (*(set = &pgm->msgno)){/* already a sequence? */
  1.2304 +				/* silly, but not as silly as the client! */
  1.2305 +	for (not = &pgm->not; *not; not = &(*not)->next);
  1.2306 +	*not = mail_newsearchpgmlist ();
  1.2307 +	set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
  1.2308 +      }
  1.2309 +      ret = crit_set (set,&s,maxmsg) && (tail == s);
  1.2310 +      break;
  1.2311 +    case 'A':			/* possible ALL, ANSWERED */
  1.2312 +      if (!strcmp (s+1,"LL")) ret = T;
  1.2313 +      else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
  1.2314 +      break;
  1.2315 +
  1.2316 +    case 'B':			/* possible BCC, BEFORE, BODY */
  1.2317 +      if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
  1.2318 +	ret = crit_string (&pgm->bcc,&tail);
  1.2319 +      else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
  1.2320 +	ret = crit_date (&pgm->before,&tail);
  1.2321 +      else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
  1.2322 +	ret = crit_string (&pgm->body,&tail);
  1.2323 +      break;
  1.2324 +    case 'C':			/* possible CC */
  1.2325 +      if (!strcmp (s+1,"C") && c == ' ' && *++tail)
  1.2326 +	ret = crit_string (&pgm->cc,&tail);
  1.2327 +      break;
  1.2328 +    case 'D':			/* possible DELETED */
  1.2329 +      if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
  1.2330 +      if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
  1.2331 +      break;
  1.2332 +    case 'F':			/* possible FLAGGED, FROM */
  1.2333 +      if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
  1.2334 +      else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
  1.2335 +	ret = crit_string (&pgm->from,&tail);
  1.2336 +      break;
  1.2337 +    case 'H':			/* possible HEADER */
  1.2338 +      if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
  1.2339 +	  (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
  1.2340 +	  (t = parse_astring (&v,&i,&c))) {
  1.2341 +	for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
  1.2342 +	*hdr = mail_newsearchheader (s,t);
  1.2343 +				/* update tail, restore delimiter */
  1.2344 +	*(tail = v ? v - 1 : t + i) = c;
  1.2345 +	ret = T;		/* success */
  1.2346 +      }
  1.2347 +      break;
  1.2348 +    case 'K':			/* possible KEYWORD */
  1.2349 +      if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
  1.2350 +	ret = crit_string (&pgm->keyword,&tail);
  1.2351 +      break;
  1.2352 +    case 'L':
  1.2353 +      if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
  1.2354 +	ret = crit_number (&pgm->larger,&tail);
  1.2355 +      break;
  1.2356 +    case 'N':			/* possible NEW, NOT */
  1.2357 +      if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
  1.2358 +      else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
  1.2359 +	for (not = &pgm->not; *not; not = &(*not)->next);
  1.2360 +	*not = mail_newsearchpgmlist ();
  1.2361 +	ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
  1.2362 +      }
  1.2363 +      break;
  1.2364 +
  1.2365 +    case 'O':			/* possible OLD, ON */
  1.2366 +      if (!strcmp (s+1,"LD")) ret = pgm->old = T;
  1.2367 +      else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
  1.2368 +	ret = crit_date (&pgm->on,&tail);
  1.2369 +      else if (!strcmp (s+1,"R") && c == ' ') {
  1.2370 +	for (or = &pgm->or; *or; or = &(*or)->next);
  1.2371 +	*or = mail_newsearchor ();
  1.2372 +	ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
  1.2373 +					 depth+1) &&
  1.2374 +	  (*tail == ' ') && *++tail &&
  1.2375 +	  parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
  1.2376 +      }
  1.2377 +      else if (!strcmp (s+1,"LDER") && c == ' ' && *++tail)
  1.2378 +	ret = crit_number (&pgm->older,&tail);
  1.2379 +      break;
  1.2380 +    case 'R':			/* possible RECENT */
  1.2381 +      if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
  1.2382 +      break;
  1.2383 +    case 'S':			/* possible SEEN, SINCE, SUBJECT */
  1.2384 +      if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
  1.2385 +      else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
  1.2386 +	ret = crit_date (&pgm->sentbefore,&tail);
  1.2387 +      else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
  1.2388 +	ret = crit_date (&pgm->senton,&tail);
  1.2389 +      else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
  1.2390 +	ret = crit_date (&pgm->sentsince,&tail);
  1.2391 +      else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
  1.2392 +	ret = crit_date (&pgm->since,&tail);
  1.2393 +      else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
  1.2394 +	ret = crit_number (&pgm->smaller,&tail);
  1.2395 +      else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
  1.2396 +	ret = crit_string (&pgm->subject,&tail);
  1.2397 +      break;
  1.2398 +    case 'T':			/* possible TEXT, TO */
  1.2399 +      if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
  1.2400 +	ret = crit_string (&pgm->text,&tail);
  1.2401 +      else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
  1.2402 +	ret = crit_string (&pgm->to,&tail);
  1.2403 +      break;
  1.2404 +
  1.2405 +    case 'U':			/* possible UID, UN* */
  1.2406 +      if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
  1.2407 +	if (*(set = &pgm->uid)){/* already a sequence? */
  1.2408 +				/* silly, but not as silly as the client! */
  1.2409 +	  for (not = &pgm->not; *not; not = &(*not)->next);
  1.2410 +	  *not = mail_newsearchpgmlist ();
  1.2411 +	  set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
  1.2412 +	}
  1.2413 +	ret = crit_set (set,&tail,maxuid);
  1.2414 +      }
  1.2415 +      else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
  1.2416 +      else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
  1.2417 +      else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
  1.2418 +      else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
  1.2419 +      else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
  1.2420 +	ret = crit_string (&pgm->unkeyword,&tail);
  1.2421 +      else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
  1.2422 +      break;
  1.2423 +    case 'Y':			/* possible YOUNGER */
  1.2424 +      if (!strcmp (s+1,"OUNGER") && c == ' ' && *++tail)
  1.2425 +	ret = crit_number (&pgm->younger,&tail);
  1.2426 +      break;
  1.2427 +    default:			/* oh dear */
  1.2428 +      break;
  1.2429 +    }
  1.2430 +    if (ret) {			/* only bother if success */
  1.2431 +      *del = c;			/* restore delimiter */
  1.2432 +      *arg = tail;		/* update argument pointer */
  1.2433 +    }
  1.2434 +  }
  1.2435 +  return ret;			/* return more to come */
  1.2436 +}
  1.2437 +
  1.2438 +/* Parse a search date criterion
  1.2439 + * Accepts: date to write into
  1.2440 + *	    pointer to argument text pointer
  1.2441 + * Returns: T if success, NIL if error
  1.2442 + */
  1.2443 +
  1.2444 +long crit_date (unsigned short *date,unsigned char **arg)
  1.2445 +{
  1.2446 +  if (*date) return NIL;	/* can't double this value */
  1.2447 +				/* handle quoted form */
  1.2448 +  if (**arg != '"') return crit_date_work (date,arg);
  1.2449 +  (*arg)++;			/* skip past opening quote */
  1.2450 +  if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
  1.2451 +  (*arg)++;			/* skip closing quote */
  1.2452 +  return T;
  1.2453 +}
  1.2454 +
  1.2455 +/* Worker routine to parse a search date criterion
  1.2456 + * Accepts: date to write into
  1.2457 + *	    pointer to argument text pointer
  1.2458 + * Returns: T if success, NIL if error
  1.2459 + */
  1.2460 +
  1.2461 +long crit_date_work (unsigned short *date,unsigned char **arg)
  1.2462 +{
  1.2463 +  int d,m,y;
  1.2464 +				/* day */
  1.2465 +  if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
  1.2466 +    if (d == ' ') d = 0;	/* leading space */
  1.2467 +    else d -= '0';		/* first digit */
  1.2468 +    if (isdigit (**arg)) {	/* if a second digit */
  1.2469 +      d *= 10;			/* slide over first digit */
  1.2470 +      d += *(*arg)++ - '0';	/* second digit */
  1.2471 +    }
  1.2472 +    if ((**arg == '-') && (y = *++(*arg))) {
  1.2473 +      m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
  1.2474 +      if ((y = *++(*arg))) {
  1.2475 +	m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
  1.2476 +	if ((y = *++(*arg))) {
  1.2477 +	  m += (y >= 'a' ? y - 'a' : y - 'A');
  1.2478 +	  switch (m) {		/* determine the month */
  1.2479 +	  case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1.2480 +	  case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1.2481 +	  case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1.2482 +	  case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1.2483 +	  case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1.2484 +	  case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1.2485 +	  case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1.2486 +	  case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1.2487 +	  case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1.2488 +	  case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
  1.2489 +	  case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
  1.2490 +	  case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
  1.2491 +	  default: return NIL;
  1.2492 +	  }
  1.2493 +	  if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
  1.2494 +	    y = 0;		/* init year */
  1.2495 +	    do {
  1.2496 +	      y *= 10;		/* add this number */
  1.2497 +	      y += *(*arg)++ - '0';
  1.2498 +	    }
  1.2499 +	    while (isdigit (**arg));
  1.2500 +				/* minimal validity check of date */
  1.2501 +	    if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL; 
  1.2502 +				/* time began on UNIX in 1970 */
  1.2503 +	    if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1.2504 +				/* return value */
  1.2505 +	    *date = mail_shortdate (y - BASEYEAR,m,d);
  1.2506 +	    return T;		/* success */
  1.2507 +	  }
  1.2508 +	}
  1.2509 +      }
  1.2510 +    }
  1.2511 +  }
  1.2512 +  return NIL;			/* else error */
  1.2513 +}
  1.2514 +
  1.2515 +/* Parse a search set criterion
  1.2516 + * Accepts: set to write into
  1.2517 + *	    pointer to argument text pointer
  1.2518 + *	    maximum value permitted
  1.2519 + * Returns: T if success, NIL if error
  1.2520 + */
  1.2521 +
  1.2522 +long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima)
  1.2523 +{
  1.2524 +  unsigned long i = 0;
  1.2525 +  if (*set) return NIL;		/* can't double this value */
  1.2526 +  *set = mail_newsearchset ();	/* instantiate a new search set */
  1.2527 +  if (**arg == '*') {		/* maxnum? */
  1.2528 +    (*arg)++;			/* skip past that number */
  1.2529 +    (*set)->first = maxima;
  1.2530 +  }
  1.2531 +  else if (crit_number (&i,arg) && i) (*set)->first = i;
  1.2532 +  else return NIL;		/* bogon */
  1.2533 +  switch (**arg) {		/* decide based on delimiter */
  1.2534 +  case ':':			/* sequence range */
  1.2535 +    i = 0;			/* reset for crit_number() */
  1.2536 +    if (*++(*arg) == '*') {	/* maxnum? */
  1.2537 +      (*arg)++;			/* skip past that number */
  1.2538 +      (*set)->last = maxima;
  1.2539 +    }
  1.2540 +    else if (crit_number (&i,arg) && i) {
  1.2541 +      if (i < (*set)->first) {	/* backwards range */
  1.2542 +	(*set)->last = (*set)->first;
  1.2543 +	(*set)->first = i;
  1.2544 +      }
  1.2545 +      else (*set)->last = i;	/* set last number */
  1.2546 +    }
  1.2547 +    else return NIL;		/* bogon */
  1.2548 +    if (**arg != ',') break;	/* drop into comma case if comma seen */
  1.2549 +  case ',':
  1.2550 +    (*arg)++;			/* skip past delimiter */
  1.2551 +    return crit_set (&(*set)->next,arg,maxima);
  1.2552 +  default:
  1.2553 +    break;
  1.2554 +  }
  1.2555 +  return T;			/* return success */
  1.2556 +}
  1.2557 +
  1.2558 +/* Parse a search number criterion
  1.2559 + * Accepts: number to write into
  1.2560 + *	    pointer to argument text pointer
  1.2561 + * Returns: T if success, NIL if error
  1.2562 + */
  1.2563 +
  1.2564 +long crit_number (unsigned long *number,unsigned char **arg)
  1.2565 +{
  1.2566 +				/* can't double this value */
  1.2567 +  if (*number || !isdigit (**arg)) return NIL;
  1.2568 +  *number = 0;
  1.2569 +  while (isdigit (**arg)) {	/* found a digit? */
  1.2570 +    *number *= 10;		/* add a decade */
  1.2571 +    *number += *(*arg)++ - '0';	/* add number */
  1.2572 +  }
  1.2573 +  return T;
  1.2574 +}
  1.2575 +
  1.2576 +
  1.2577 +/* Parse a search string criterion
  1.2578 + * Accepts: date to write into
  1.2579 + *	    pointer to argument text pointer
  1.2580 + * Returns: T if success, NIL if error
  1.2581 + */
  1.2582 +
  1.2583 +long crit_string (STRINGLIST **string,unsigned char **arg)
  1.2584 +{
  1.2585 +  unsigned long i;
  1.2586 +  char c;
  1.2587 +  char *s = parse_astring (arg,&i,&c);
  1.2588 +  if (!s) return NIL;
  1.2589 +				/* find tail of list */
  1.2590 +  while (*string) string = &(*string)->next;
  1.2591 +  *string = mail_newstringlist ();
  1.2592 +  (*string)->text.data = (unsigned char *) fs_get (i + 1);
  1.2593 +  memcpy ((*string)->text.data,s,i);
  1.2594 +  (*string)->text.data[i] = '\0';
  1.2595 +  (*string)->text.size = i;
  1.2596 +				/* if end of arguments, wrap it up here */
  1.2597 +  if (!*arg) *arg = (char *) (*string)->text.data + i;
  1.2598 +  else (*--(*arg) = c);		/* back up pointer, restore delimiter */
  1.2599 +  return T;
  1.2600 +}
  1.2601 +
  1.2602 +/* Fetch message data
  1.2603 + * Accepts: string of data items to be fetched (must be writeable)
  1.2604 + *	    UID fetch flag
  1.2605 + */
  1.2606 +
  1.2607 +#define MAXFETCH 100
  1.2608 +
  1.2609 +void fetch (char *t,unsigned long uid)
  1.2610 +{
  1.2611 +  fetchfn_t f[MAXFETCH +2];
  1.2612 +  void *fa[MAXFETCH + 2];
  1.2613 +  int k;
  1.2614 +  memset ((void *) f,NIL,sizeof (f));
  1.2615 +  memset ((void *) fa,NIL,sizeof (fa));
  1.2616 +  fetch_work (t,uid,f,fa);	/* do the work */
  1.2617 +				/* clean up arguments */
  1.2618 +  for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
  1.2619 +}
  1.2620 +
  1.2621 +
  1.2622 +/* Fetch message data worker routine
  1.2623 + * Accepts: string of data items to be fetched (must be writeable)
  1.2624 + *	    UID fetch flag
  1.2625 + *	    function dispatch vector
  1.2626 + *	    function argument vector
  1.2627 + */
  1.2628 +
  1.2629 +void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
  1.2630 +{
  1.2631 +  unsigned char *s,*v;
  1.2632 +  unsigned long i;
  1.2633 +  unsigned long k = 0;
  1.2634 +  BODY *b;
  1.2635 +  int list = NIL;
  1.2636 +  int parse_envs = NIL;
  1.2637 +  int parse_bodies = NIL;
  1.2638 +  if (uid) {			/* need to fetch UIDs? */
  1.2639 +    fa[k] = NIL;		/* no argument */
  1.2640 +    f[k++] = fetch_uid;		/* push a UID fetch on the stack */
  1.2641 +  }
  1.2642 +
  1.2643 +				/* process macros */
  1.2644 +  if (!strcmp (ucase (t),"ALL"))
  1.2645 +    strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  1.2646 +  else if (!strcmp (t,"FULL"))
  1.2647 +    strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  1.2648 +  else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  1.2649 +  if (list = (*t == '(')) t++;	/* skip open paren */
  1.2650 +  if (s = strtok (t," ")) do {	/* parse attribute list */
  1.2651 +    if (list && (i = strlen (s)) && (s[i-1] == ')')) {
  1.2652 +      list = NIL;		/* done with list */
  1.2653 +      s[i-1] = '\0';		/* tie off last item */
  1.2654 +    }
  1.2655 +    fa[k] = NIL;		/* default to no argument */
  1.2656 +    if (!strcmp (s,"UID")) {	/* no-op if implicit */
  1.2657 +      if (!uid) f[k++] = fetch_uid;
  1.2658 +    }
  1.2659 +    else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  1.2660 +    else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  1.2661 +    else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  1.2662 +    else if (!strcmp (s,"ENVELOPE")) {
  1.2663 +      parse_envs = T;		/* we will need to parse envelopes */
  1.2664 +      f[k++] = fetch_envelope;
  1.2665 +    }
  1.2666 +    else if (!strcmp (s,"BODY")) {
  1.2667 +      parse_envs = parse_bodies = T;
  1.2668 +      f[k++] = fetch_body;
  1.2669 +    }
  1.2670 +    else if (!strcmp (s,"BODYSTRUCTURE")) {
  1.2671 +      parse_envs = parse_bodies = T;
  1.2672 +      f[k++] = fetch_bodystructure;
  1.2673 +    }
  1.2674 +    else if (!strcmp (s,"RFC822")) {
  1.2675 +      fa[k] = s[6] ? (void *) FT_PEEK : NIL;
  1.2676 +      f[k++] = fetch_rfc822;
  1.2677 +    }
  1.2678 +    else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  1.2679 +    else if (!strcmp (s,"RFC822.TEXT")) {
  1.2680 +      fa[k] = s[11] ? (void *) FT_PEEK : NIL;
  1.2681 +      f[k++] = fetch_rfc822_text;
  1.2682 +    }
  1.2683 +
  1.2684 +    else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10) ||
  1.2685 +	     !strncmp (s,"BINARY[",7) || !strncmp (s,"BINARY.PEEK[",12) ||
  1.2686 +	     !strncmp (s,"BINARY.SIZE[",12)) {
  1.2687 +      TEXTARGS *ta = (TEXTARGS *)
  1.2688 +	memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
  1.2689 +      if (s[1] == 'I') {	/* body or binary? */
  1.2690 +	ta->binary = FTB_BINARY;/* binary */
  1.2691 +	f[k] = fetch_body_part_binary;
  1.2692 +	if (s[6] == '.') {	/* wanted peek or size? */
  1.2693 +	  if (s[7] == 'P') ta->flags = FT_PEEK;
  1.2694 +	  else ta->binary |= FTB_SIZE;
  1.2695 +	  s += 12;		/* skip to section specifier */
  1.2696 +	}
  1.2697 +	else s += 7;		/* skip to section specifier */
  1.2698 +	if (!isdigit (*s)) {	/* make sure top-level digit */
  1.2699 +	  fs_give ((void **) &ta);
  1.2700 +	  response = badbin;
  1.2701 +	  return;
  1.2702 +	}
  1.2703 +      }
  1.2704 +      else {			/* body */
  1.2705 +	f[k] = fetch_body_part_contents;
  1.2706 +	if (s[4] == '.') {	/* wanted peek? */
  1.2707 +	  ta->flags = FT_PEEK;
  1.2708 +	  s += 10;		/* skip to section specifier */
  1.2709 +	}
  1.2710 +	else s += 5;		/* skip to section specifier */
  1.2711 +      }
  1.2712 +      if (*(v = s) != ']') {	/* non-empty section specifier? */
  1.2713 +	if (isdigit (*v)) {	/* have section specifier? */
  1.2714 +				/* need envelopes and bodies */
  1.2715 +	  parse_envs = parse_bodies = T;
  1.2716 +	  while (isdigit (*v))	/* scan to end of section specifier */
  1.2717 +	    if ((*++v == '.') && isdigit (v[1])) v++;
  1.2718 +				/* any IMAP4rev1 stuff following? */
  1.2719 +	  if ((*v == '.') && isalpha (v[1])) {
  1.2720 +	    if (ta->binary) {	/* not if binary you don't */
  1.2721 +	      fs_give ((void **) &ta);
  1.2722 +	      response = badbin;
  1.2723 +	      return;
  1.2724 +	    }
  1.2725 +	    *v++ = '\0';	/* yes, tie off section specifier */
  1.2726 +	    if (!strncmp (v,"MIME",4)) {
  1.2727 +	      v += 4;		/* found <section>.MIME */
  1.2728 +	      f[k] = fetch_body_part_mime;
  1.2729 +	    }
  1.2730 +	  }
  1.2731 +	  else if (*v != ']') {	/* better be the end if no IMAP4rev1 stuff */
  1.2732 +	    fs_give ((void **) &ta);/* clean up */
  1.2733 +	    response = "%.80s BAD Syntax error in section specifier\015\012";
  1.2734 +	    return;
  1.2735 +	  }
  1.2736 +	}
  1.2737 +
  1.2738 +	if (*v != ']') {	/* IMAP4rev1 stuff here? */
  1.2739 +	  if (!strncmp (v,"HEADER",6)) {
  1.2740 +	    *v = '\0';		/* tie off in case top level */
  1.2741 +	    v += 6;		/* found [<section>.]HEADER */
  1.2742 +	    f[k] = fetch_body_part_header;
  1.2743 +				/* partial headers wanted? */
  1.2744 +	    if (!strncmp (v,".FIELDS",7)) {
  1.2745 +	      v += 7;		/* yes */
  1.2746 +	      if (!strncmp (v,".NOT",4)) {
  1.2747 +		v += 4;		/* want to exclude named headers */
  1.2748 +		ta->flags |= FT_NOT;
  1.2749 +	      }
  1.2750 +	      if (*v || !(v = strtok (NIL,"\015\012")) ||
  1.2751 +		  !(ta->lines = parse_stringlist (&v,&list))) {
  1.2752 +		fs_give ((void **) &ta);/* clean up */
  1.2753 +		response = "%.80s BAD Syntax error in header fields\015\012";
  1.2754 +		return;
  1.2755 +	      }
  1.2756 +	    }
  1.2757 +	  }
  1.2758 +	  else if (!strncmp (v,"TEXT",4)) {
  1.2759 +	    *v = '\0';		/* tie off in case top level */
  1.2760 +	    v += 4;		/* found [<section>.]TEXT */
  1.2761 +	    f[k] = fetch_body_part_text;
  1.2762 +	  }
  1.2763 +	  else {
  1.2764 +	    fs_give ((void **) &ta);/* clean up */
  1.2765 +	    response = "%.80s BAD Unknown section text specifier\015\012";
  1.2766 +	    return;
  1.2767 +	  }
  1.2768 +	}
  1.2769 +      }
  1.2770 +				/* tie off section */
  1.2771 +      if (*v == ']') *v++ = '\0';
  1.2772 +      else {			/* bogon */
  1.2773 +	if (ta->lines) mail_free_stringlist (&ta->lines);
  1.2774 +	fs_give ((void **) &ta);/* clean up */
  1.2775 +	response = "%.80s BAD Section specifier not terminated\015\012";
  1.2776 +	return;
  1.2777 +      }
  1.2778 +
  1.2779 +      if ((*v == '<') &&	/* partial specifier? */
  1.2780 +	  ((ta->binary & FTB_SIZE) ||
  1.2781 +	   !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) ||
  1.2782 +				v) &&
  1.2783 +	     (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) &&
  1.2784 +	     (*v++ == '>')))) {
  1.2785 +	if (ta->lines) mail_free_stringlist (&ta->lines);
  1.2786 +	fs_give ((void **) &ta);
  1.2787 +	response ="%.80s BAD Syntax error in partial text specifier\015\012";
  1.2788 +	return;
  1.2789 +      }
  1.2790 +      switch (*v) {		/* what's there now? */
  1.2791 +      case ' ':			/* more follows */
  1.2792 +	*--v = ' ';		/* patch a space back in */
  1.2793 +	*--v = 'x';		/* and a hokey character before that */
  1.2794 +	strtok (v," ");		/* reset strtok mechanism */
  1.2795 +	break;
  1.2796 +      case '\0':		/* none */
  1.2797 +	break;
  1.2798 +      case ')':			/* end of list */
  1.2799 +	if (list && !v[1]) {	/* make sure of that */
  1.2800 +	  list = NIL;
  1.2801 +	  strtok (v," ");	/* reset strtok mechanism */
  1.2802 +	  break;		/* all done */
  1.2803 +	}
  1.2804 +				/* otherwise it's a bogon, drop in */
  1.2805 +      default:			/* bogon */
  1.2806 +	if (ta->lines) mail_free_stringlist (&ta->lines);
  1.2807 +	fs_give ((void **) &ta);
  1.2808 +	response = "%.80s BAD Syntax error after section specifier\015\012";
  1.2809 +	return;
  1.2810 +      }
  1.2811 +				/* make copy of section specifier */
  1.2812 +      if (s && *s) ta->section = cpystr (s);
  1.2813 +      fa[k++] = (void *) ta;	/* set argument */
  1.2814 +    }
  1.2815 +    else {			/* unknown attribute */
  1.2816 +      response = badatt;
  1.2817 +      return;
  1.2818 +    }
  1.2819 +  } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list);
  1.2820 +  else {
  1.2821 +    response = misarg;		/* missing attribute list */
  1.2822 +    return;
  1.2823 +  }
  1.2824 +
  1.2825 +  if (s) {			/* too many attributes? */
  1.2826 +    response = "%.80s BAD Excessively complex FETCH attribute list\015\012";
  1.2827 +    return;
  1.2828 +  }
  1.2829 +  if (list) {			/* too many attributes? */
  1.2830 +    response = "%.80s BAD Unterminated FETCH attribute list\015\012";
  1.2831 +    return;
  1.2832 +  }
  1.2833 +  f[k] = NIL;			/* tie off attribute list */
  1.2834 +				/* c-client clobbers sequence, use spare */
  1.2835 +  for (i = 1; i <= nmsgs; i++)
  1.2836 +    mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
  1.2837 +				/* for each requested message */
  1.2838 +  for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) {
  1.2839 +				/* kill if dying */
  1.2840 +    if (state == LOGOUT) longjmp (jmpenv,1);
  1.2841 +    if (mail_elt (stream,i)->spare) {
  1.2842 +				/* parse envelope, set body, do warnings */
  1.2843 +      if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  1.2844 +      quell_events = T;		/* can't do any events now */
  1.2845 +      PSOUT ("* ");		/* leader */
  1.2846 +      pnum (i);
  1.2847 +      PSOUT (" FETCH (");
  1.2848 +      (*f[0]) (i,fa[0]);	/* do first attribute */
  1.2849 +				/* for each subsequent attribute */
  1.2850 +      for (k = 1; f[k] && (response != loseunknowncte); k++) {
  1.2851 +	PBOUT (' ');		/* delimit with space */
  1.2852 +	(*f[k]) (i,fa[k]);	/* do that attribute */
  1.2853 +      }
  1.2854 +      PSOUT (")\015\012");	/* trailer */
  1.2855 +      quell_events = NIL;	/* events alright now */
  1.2856 +    }
  1.2857 +  }
  1.2858 +}
  1.2859 +
  1.2860 +/* Fetch message body structure (extensible)
  1.2861 + * Accepts: message number
  1.2862 + *	    extra argument
  1.2863 + */
  1.2864 +
  1.2865 +void fetch_bodystructure (unsigned long i,void *args)
  1.2866 +{
  1.2867 +  BODY *body;
  1.2868 +  mail_fetchstructure (stream,i,&body);
  1.2869 +  PSOUT ("BODYSTRUCTURE ");
  1.2870 +  pbodystructure (body);	/* output body */
  1.2871 +}
  1.2872 +
  1.2873 +
  1.2874 +/* Fetch message body structure (non-extensible)
  1.2875 + * Accepts: message number
  1.2876 + *	    extra argument
  1.2877 + */
  1.2878 +
  1.2879 +
  1.2880 +void fetch_body (unsigned long i,void *args)
  1.2881 +{
  1.2882 +  BODY *body;
  1.2883 +  mail_fetchstructure (stream,i,&body);
  1.2884 +  PSOUT ("BODY ");		/* output attribute */
  1.2885 +  pbody (body);			/* output body */
  1.2886 +}
  1.2887 +
  1.2888 +/* Fetch body part MIME header
  1.2889 + * Accepts: message number
  1.2890 + *	    extra argument
  1.2891 + */
  1.2892 +
  1.2893 +void fetch_body_part_mime (unsigned long i,void *args)
  1.2894 +{
  1.2895 +  TEXTARGS *ta = (TEXTARGS *) args;
  1.2896 +  if (i) {			/* do work? */
  1.2897 +    SIZEDTEXT st;
  1.2898 +    unsigned long uid = mail_uid (stream,i);
  1.2899 +    char *tmp = (char *) fs_get (100 + strlen (ta->section));
  1.2900 +    sprintf (tmp,"BODY[%s.MIME]",ta->section);
  1.2901 +				/* try to use remembered text */
  1.2902 +    if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  1.2903 +    else {			/* get data */
  1.2904 +      st.data = (unsigned char *)
  1.2905 +	mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
  1.2906 +      if (ta->first || ta->last) remember (uid,tmp,&st);
  1.2907 +    }
  1.2908 +    pbodypartstring (i,tmp,&st,NIL,ta);
  1.2909 +    fs_give ((void **) &tmp);
  1.2910 +  }
  1.2911 +  else {			/* clean up the arguments */
  1.2912 +    fs_give ((void **) &ta->section);
  1.2913 +    fs_give ((void **) &args);
  1.2914 +  }
  1.2915 +}
  1.2916 +
  1.2917 +
  1.2918 +/* Fetch body part contents
  1.2919 + * Accepts: message number
  1.2920 + *	    extra argument
  1.2921 + */
  1.2922 +
  1.2923 +void fetch_body_part_contents (unsigned long i,void *args)
  1.2924 +{
  1.2925 +  TEXTARGS *ta = (TEXTARGS *) args;
  1.2926 +  if (i) {			/* do work? */
  1.2927 +    SIZEDTEXT st;
  1.2928 +    char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
  1.2929 +    unsigned long uid = mail_uid (stream,i);
  1.2930 +    sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
  1.2931 +				/* try to use remembered text */
  1.2932 +    if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  1.2933 +				/* get data */
  1.2934 +    else if ((st.data = (unsigned char *)
  1.2935 +	      mail_fetch_body (stream,i,ta->section,&st.size,
  1.2936 +			       ta->flags | FT_RETURNSTRINGSTRUCT)) &&
  1.2937 +	     (ta->first || ta->last)) remember (uid,tmp,&st);
  1.2938 +    pbodypartstring (i,tmp,&st,&stream->private.string,ta);
  1.2939 +    fs_give ((void **) &tmp);
  1.2940 +  }
  1.2941 +  else {			/* clean up the arguments */
  1.2942 +    if (ta->section) fs_give ((void **) &ta->section);
  1.2943 +    fs_give ((void **) &args);
  1.2944 +  }
  1.2945 +}
  1.2946 +
  1.2947 +/* Fetch body part binary
  1.2948 + * Accepts: message number
  1.2949 + *	    extra argument
  1.2950 + * Someday fix this to use stringstruct instead of memory
  1.2951 + */
  1.2952 +
  1.2953 +void fetch_body_part_binary (unsigned long i,void *args)
  1.2954 +{
  1.2955 +  TEXTARGS *ta = (TEXTARGS *) args;
  1.2956 +  if (i) {			/* do work? */
  1.2957 +    SIZEDTEXT st,cst;
  1.2958 +    BODY *body = mail_body (stream,i,ta->section);
  1.2959 +    char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
  1.2960 +    unsigned long uid = mail_uid (stream,i);
  1.2961 +				/* try to use remembered text */
  1.2962 +    if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  1.2963 +    else {			/* get data */
  1.2964 +      st.data = (unsigned char *)
  1.2965 +	mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
  1.2966 +      if (ta->first || ta->last) remember (uid,tmp,&st);
  1.2967 +    }
  1.2968 +				/* what encoding was used? */
  1.2969 +    if (body) switch (body->encoding) {
  1.2970 +    case ENCBASE64:
  1.2971 +      if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break;
  1.2972 +      fetch_uid (i,NIL);	/* wrote a space, so must do something */
  1.2973 +      if (lsterr) fs_give ((void **) &lsterr);
  1.2974 +      lsterr = cpystr ("Undecodable BASE64 contents");
  1.2975 +      response = loseunknowncte;
  1.2976 +      fs_give ((void **) &tmp);
  1.2977 +      return;
  1.2978 +    case ENCQUOTEDPRINTABLE:
  1.2979 +      if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break;
  1.2980 +      fetch_uid (i,NIL);	/* wrote a space, so must do something */
  1.2981 +      if (lsterr) fs_give ((void **) &lsterr);
  1.2982 +      lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents");
  1.2983 +      response = loseunknowncte;
  1.2984 +      fs_give ((void **) &tmp);
  1.2985 +      return;
  1.2986 +    case ENC7BIT:		/* no need to convert any of these */
  1.2987 +    case ENC8BIT:
  1.2988 +    case ENCBINARY:
  1.2989 +      cst.data = NIL;		/* no converted data to free */
  1.2990 +      break;
  1.2991 +    default:			/* unknown encoding, oops */
  1.2992 +      fetch_uid (i,NIL);	/* wrote a space, so must do something */
  1.2993 +      if (lsterr) fs_give ((void **) &lsterr);
  1.2994 +      lsterr = cpystr ("Unknown Content-Transfer-Encoding");
  1.2995 +      response = loseunknowncte;
  1.2996 +      fs_give ((void **) &tmp);
  1.2997 +      return;
  1.2998 +    }
  1.2999 +    else {
  1.3000 +      if (lsterr) fs_give ((void **) &lsterr);
  1.3001 +      lsterr = cpystr ("Invalid body part");
  1.3002 +      response = loseunknowncte;
  1.3003 +      fs_give ((void **) &tmp);
  1.3004 +      return;
  1.3005 +    }
  1.3006 +
  1.3007 +				/* use decoded version if exists */
  1.3008 +    if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT));
  1.3009 +    if (ta->binary & FTB_SIZE) {/* just want size? */
  1.3010 +      sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "",
  1.3011 +	       st.size);
  1.3012 +      PSOUT (tmp);
  1.3013 +    }
  1.3014 +    else {			/* no, blat binary data */
  1.3015 +      int f = mail_elt (stream,i)->seen;
  1.3016 +      if (st.data) {		/* only if have useful data */
  1.3017 +				/* partial specifier */
  1.3018 +	if (ta->first || ta->last)
  1.3019 +	  sprintf (tmp,"BINARY[%s]<%lu> ",
  1.3020 +		   ta->section ? ta->section : "",ta->first);
  1.3021 +	else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : "");
  1.3022 +  				/* in case first byte beyond end of text */
  1.3023 +	if (st.size <= ta->first) st.size = ta->first = 0;
  1.3024 +	else {			/* offset and truncate */
  1.3025 +	  st.data += ta->first;	/* move to desired position */
  1.3026 +	  st.size -= ta->first;	/* reduced size */
  1.3027 +	  if (ta->last && (st.size > ta->last)) st.size = ta->last;
  1.3028 +	}
  1.3029 +	if (st.size) sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size);
  1.3030 +	else strcat (tmp,"\"\"");
  1.3031 +	PSOUT (tmp);		/* write binary output */
  1.3032 +	if (st.size && (PSOUTR (&st) == EOF)) ioerror(stdout,"writing binary");
  1.3033 +      }
  1.3034 +      else {
  1.3035 +	sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : "");
  1.3036 +	PSOUT (tmp);
  1.3037 +      }
  1.3038 +      changed_flags (i,f);	/* write changed flags */
  1.3039 +    }
  1.3040 +				/* free converted data */
  1.3041 +    if (cst.data) fs_give ((void **) &cst.data);
  1.3042 +    fs_give ((void **) &tmp);	/* and temporary string */
  1.3043 +  }
  1.3044 +  else {			/* clean up the arguments */
  1.3045 +    if (ta->section) fs_give ((void **) &ta->section);
  1.3046 +    fs_give ((void **) &args);
  1.3047 +  }
  1.3048 +}
  1.3049 +
  1.3050 +/* Fetch MESSAGE/RFC822 body part header
  1.3051 + * Accepts: message number
  1.3052 + *	    extra argument
  1.3053 + */
  1.3054 +
  1.3055 +void fetch_body_part_header (unsigned long i,void *args)
  1.3056 +{
  1.3057 +  TEXTARGS *ta = (TEXTARGS *) args;
  1.3058 +  unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
  1.3059 +  STRINGLIST *s;
  1.3060 +  for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
  1.3061 +  if (i) {			/* do work? */
  1.3062 +    SIZEDTEXT st;
  1.3063 +    char *tmp = (char *) fs_get (len);
  1.3064 +    PSOUT ("BODY[");
  1.3065 +				/* output attribute */
  1.3066 +    if (ta->section && *ta->section) {
  1.3067 +      PSOUT (ta->section);
  1.3068 +      PBOUT ('.');
  1.3069 +    }
  1.3070 +    PSOUT ("HEADER");
  1.3071 +    if (ta->lines) {
  1.3072 +      PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
  1.3073 +      pastringlist (ta->lines);
  1.3074 +    }
  1.3075 +    strcpy (tmp,"]");		/* close section specifier */
  1.3076 +    st.data = (unsigned char *)	/* get data (no hope in using remember here) */
  1.3077 +      mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
  1.3078 +    pbodypartstring (i,tmp,&st,NIL,ta);
  1.3079 +    fs_give ((void **) &tmp);
  1.3080 +  }
  1.3081 +  else {			/* clean up the arguments */
  1.3082 +    if (ta->lines) mail_free_stringlist (&ta->lines);
  1.3083 +    if (ta->section) fs_give ((void **) &ta->section);
  1.3084 +    fs_give ((void **) &args);
  1.3085 +  }
  1.3086 +}
  1.3087 +
  1.3088 +/* Fetch MESSAGE/RFC822 body part text
  1.3089 + * Accepts: message number
  1.3090 + *	    extra argument
  1.3091 + */
  1.3092 +
  1.3093 +void fetch_body_part_text (unsigned long i,void *args)
  1.3094 +{
  1.3095 +  TEXTARGS *ta = (TEXTARGS *) args;
  1.3096 +  if (i) {			/* do work? */
  1.3097 +    SIZEDTEXT st;
  1.3098 +    char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
  1.3099 +    unsigned long uid = mail_uid (stream,i);
  1.3100 +				/* output attribute */
  1.3101 +    if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
  1.3102 +    else strcpy (tmp,"BODY[TEXT]");
  1.3103 +				/* try to use remembered text */
  1.3104 +    if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
  1.3105 +				/* get data */
  1.3106 +    else if ((st.data = (unsigned char *)
  1.3107 +	      mail_fetch_text (stream,i,ta->section,&st.size,
  1.3108 +			       ta->flags | FT_RETURNSTRINGSTRUCT)) &&
  1.3109 +	     (ta->first || ta->last)) remember (uid,tmp,&st);
  1.3110 +    pbodypartstring (i,tmp,&st,&stream->private.string,ta);
  1.3111 +    fs_give ((void **) &tmp);
  1.3112 +  }
  1.3113 +  else {			/* clean up the arguments */
  1.3114 +    if (ta->section) fs_give ((void **) &ta->section);
  1.3115 +    fs_give ((void **) &args);
  1.3116 +  }
  1.3117 +}
  1.3118 +
  1.3119 +
  1.3120 +/* Remember body part text for subsequent partial fetching
  1.3121 + * Accepts: message UID
  1.3122 + *	    body part id
  1.3123 + *	    text
  1.3124 + *	    string
  1.3125 + */
  1.3126 +
  1.3127 +void remember (unsigned long uid,char *id,SIZEDTEXT *st)
  1.3128 +{
  1.3129 +  lastuid = uid;		/* remember UID */
  1.3130 +  if (lastid) fs_give ((void **) &lastid);
  1.3131 +  lastid = cpystr (id);		/* remember body part id */
  1.3132 +  if (lastst.data) fs_give ((void **) &lastst.data);
  1.3133 +				/* remember text */
  1.3134 +  lastst.data = (unsigned char *)
  1.3135 +    memcpy (fs_get (st->size + 1),st->data,st->size);
  1.3136 +  lastst.size = st->size;
  1.3137 +}
  1.3138 +
  1.3139 +
  1.3140 +/* Fetch envelope
  1.3141 + * Accepts: message number
  1.3142 + *	    extra argument
  1.3143 + */
  1.3144 +
  1.3145 +void fetch_envelope (unsigned long i,void *args)
  1.3146 +{
  1.3147 +  ENVELOPE *env = mail_fetchenvelope (stream,i);
  1.3148 +  PSOUT ("ENVELOPE ");		/* output attribute */
  1.3149 +  penv (env);			/* output envelope */
  1.3150 +}
  1.3151 +
  1.3152 +/* Fetch flags
  1.3153 + * Accepts: message number
  1.3154 + *	    extra argument
  1.3155 + */
  1.3156 +
  1.3157 +void fetch_flags (unsigned long i,void *args)
  1.3158 +{
  1.3159 +  unsigned long u;
  1.3160 +  char *t,tmp[MAILTMPLEN];
  1.3161 +  int c = NIL;
  1.3162 +  MESSAGECACHE *elt = mail_elt (stream,i);
  1.3163 +  if (!elt->valid) {		/* have valid flags yet? */
  1.3164 +    sprintf (tmp,"%lu",i);
  1.3165 +    mail_fetch_flags (stream,tmp,NIL);
  1.3166 +  }
  1.3167 +  PSOUT ("FLAGS (");		/* output attribute */
  1.3168 +				/* output system flags */
  1.3169 +  if (elt->recent) put_flag (&c,"\\Recent");
  1.3170 +  if (elt->seen) put_flag (&c,"\\Seen");
  1.3171 +  if (elt->deleted) put_flag (&c,"\\Deleted");
  1.3172 +  if (elt->flagged) put_flag (&c,"\\Flagged");
  1.3173 +  if (elt->answered) put_flag (&c,"\\Answered");
  1.3174 +  if (elt->draft) put_flag (&c,"\\Draft");
  1.3175 +  if (u = elt->user_flags) do	/* any user flags? */
  1.3176 +    if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
  1.3177 +  while (u);			/* until no more user flags */
  1.3178 +  PBOUT (')');			/* end of flags */
  1.3179 +  elt->spare2 = NIL;		/* we've sent the update */
  1.3180 +}
  1.3181 +
  1.3182 +
  1.3183 +/* Output a flag
  1.3184 + * Accepts: pointer to current delimiter character
  1.3185 + *	    flag to output
  1.3186 + * Changes delimiter character to space
  1.3187 + */
  1.3188 +
  1.3189 +void put_flag (int *c,char *s)
  1.3190 +{
  1.3191 +  if (*c) PBOUT (*c);		/* put delimiter */
  1.3192 +  PSOUT (s);			/* dump flag */
  1.3193 +  *c = ' ';			/* change delimiter if necessary */
  1.3194 +}
  1.3195 +
  1.3196 +
  1.3197 +/* Output flags if was unseen
  1.3198 + * Accepts: message number
  1.3199 + *	    prior value of Seen flag
  1.3200 + */
  1.3201 +
  1.3202 +void changed_flags (unsigned long i,int f)
  1.3203 +{
  1.3204 +				/* was unseen, now seen? */
  1.3205 +  if (!f && mail_elt (stream,i)->seen) {
  1.3206 +    PBOUT (' ');		/* yes, delimit with space */
  1.3207 +    fetch_flags (i,NIL);	/* output flags */
  1.3208 +  }
  1.3209 +}
  1.3210 +
  1.3211 +/* Fetch message internal date
  1.3212 + * Accepts: message number
  1.3213 + *	    extra argument
  1.3214 + */
  1.3215 +
  1.3216 +void fetch_internaldate (unsigned long i,void *args)
  1.3217 +{
  1.3218 +  char tmp[MAILTMPLEN];
  1.3219 +  MESSAGECACHE *elt = mail_elt (stream,i);
  1.3220 +  if (!elt->day) {		/* have internal date yet? */
  1.3221 +    sprintf (tmp,"%lu",i);
  1.3222 +    mail_fetch_fast (stream,tmp,NIL);
  1.3223 +  }
  1.3224 +  PSOUT ("INTERNALDATE \"");
  1.3225 +  PSOUT (mail_date (tmp,elt));
  1.3226 +  PBOUT ('"');
  1.3227 +}
  1.3228 +
  1.3229 +
  1.3230 +/* Fetch unique identifier
  1.3231 + * Accepts: message number
  1.3232 + *	    extra argument
  1.3233 + */
  1.3234 +
  1.3235 +void fetch_uid (unsigned long i,void *args)
  1.3236 +{
  1.3237 +  PSOUT ("UID ");
  1.3238 +  pnum (mail_uid (stream,i));
  1.3239 +}
  1.3240 +
  1.3241 +/* Fetch complete RFC-822 format message
  1.3242 + * Accepts: message number
  1.3243 + *	    extra argument
  1.3244 + */
  1.3245 +
  1.3246 +void fetch_rfc822 (unsigned long i,void *args)
  1.3247 +{
  1.3248 +  if (i) {			/* do work? */
  1.3249 +    int f = mail_elt (stream,i)->seen;
  1.3250 +#if 0
  1.3251 +    SIZEDTEXT st;
  1.3252 +    st.data = (unsigned char *)
  1.3253 +      mail_fetch_message (stream,i,&st.size,(long) args);
  1.3254 +    pbodypartstring (i,"RFC822",&st,NIL,NIL);
  1.3255 +#else
  1.3256 +    /* Yes, this version is bletcherous, but mail_fetch_message() requires
  1.3257 +       too much memory */
  1.3258 +    SIZEDTEXT txt,hdr;
  1.3259 +    char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
  1.3260 +    hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
  1.3261 +    txt.data = (unsigned char *)
  1.3262 +      mail_fetch_text (stream,i,NIL,&txt.size,
  1.3263 +		       ((long) args) | FT_RETURNSTRINGSTRUCT);
  1.3264 +    PSOUT ("RFC822 {");
  1.3265 +    pnum (hdr.size + txt.size);
  1.3266 +    PSOUT ("}\015\012");
  1.3267 +    ptext (&hdr,NIL);
  1.3268 +    ptext (&txt,&stream->private.string);
  1.3269 +    fs_give ((void **) &hdr.data);
  1.3270 +#endif
  1.3271 +    changed_flags (i,f);	/* output changed flags */
  1.3272 +  }
  1.3273 +}
  1.3274 +
  1.3275 +
  1.3276 +/* Fetch RFC-822 header
  1.3277 + * Accepts: message number
  1.3278 + *	    extra argument
  1.3279 + */
  1.3280 +
  1.3281 +void fetch_rfc822_header (unsigned long i,void *args)
  1.3282 +{
  1.3283 +  SIZEDTEXT st;
  1.3284 +  st.data = (unsigned char *)
  1.3285 +    mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
  1.3286 +  pbodypartstring (i,"RFC822.HEADER",&st,NIL,NIL);
  1.3287 +}
  1.3288 +
  1.3289 +
  1.3290 +/* Fetch RFC-822 message length
  1.3291 + * Accepts: message number
  1.3292 + *	    extra argument
  1.3293 + */
  1.3294 +
  1.3295 +void fetch_rfc822_size (unsigned long i,void *args)
  1.3296 +{
  1.3297 +  char tmp[MAILTMPLEN];
  1.3298 +  MESSAGECACHE *elt = mail_elt (stream,i);
  1.3299 +  if (!elt->rfc822_size) {	/* have message size yet? */
  1.3300 +    sprintf (tmp,"%lu",i);
  1.3301 +    mail_fetch_fast (stream,tmp,NIL);
  1.3302 +  }
  1.3303 +  PSOUT ("RFC822.SIZE ");
  1.3304 +  pnum (elt->rfc822_size);
  1.3305 +}
  1.3306 +
  1.3307 +/* Fetch RFC-822 text only
  1.3308 + * Accepts: message number
  1.3309 + *	    extra argument
  1.3310 + */
  1.3311 +
  1.3312 +void fetch_rfc822_text (unsigned long i,void *args)
  1.3313 +{
  1.3314 +  if (i) {			/* do work? */
  1.3315 +    int f = mail_elt (stream,i)->seen;
  1.3316 +    SIZEDTEXT st;
  1.3317 +    st.data = (unsigned char *)
  1.3318 +      mail_fetch_text (stream,i,NIL,&st.size,
  1.3319 +		       ((long) args) | FT_RETURNSTRINGSTRUCT);
  1.3320 +    pbodypartstring (i,"RFC822.TEXT",&st,&stream->private.string,NIL);
  1.3321 +  }
  1.3322 +}
  1.3323 +
  1.3324 +/* Print envelope
  1.3325 + * Accepts: body
  1.3326 + */
  1.3327 +
  1.3328 +void penv (ENVELOPE *env)
  1.3329 +{
  1.3330 +  PBOUT ('(');			/* delimiter */
  1.3331 +  if (env) {			/* only if there is an envelope */
  1.3332 +    pnstring (env->date);	/* output envelope fields */
  1.3333 +    PBOUT (' ');
  1.3334 +    pnstring (env->subject);
  1.3335 +    PBOUT (' ');
  1.3336 +    paddr (env->from);
  1.3337 +    PBOUT (' ');
  1.3338 +    paddr (env->sender);
  1.3339 +    PBOUT (' ');
  1.3340 +    paddr (env->reply_to);
  1.3341 +    PBOUT (' ');
  1.3342 +    paddr (env->to);
  1.3343 +    PBOUT (' ');
  1.3344 +    paddr (env->cc);
  1.3345 +    PBOUT (' ');
  1.3346 +    paddr (env->bcc);
  1.3347 +    PBOUT (' ');
  1.3348 +    pnstring (env->in_reply_to);
  1.3349 +    PBOUT (' ');
  1.3350 +    pnstring (env->message_id);
  1.3351 +  }
  1.3352 +				/* no envelope */
  1.3353 +  else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
  1.3354 +  PBOUT (')');			/* end of envelope */
  1.3355 +}
  1.3356 +
  1.3357 +/* Print body structure (extensible)
  1.3358 + * Accepts: body
  1.3359 + */
  1.3360 +
  1.3361 +void pbodystructure (BODY *body)
  1.3362 +{
  1.3363 +  PBOUT ('(');			/* delimiter */
  1.3364 +  if (body) {			/* only if there is a body */
  1.3365 +    PART *part;
  1.3366 +				/* multipart type? */
  1.3367 +    if (body->type == TYPEMULTIPART) {
  1.3368 +				/* print each part */
  1.3369 +      if (part = body->nested.part)
  1.3370 +	for (; part; part = part->next) pbodystructure (&(part->body));
  1.3371 +      else pbodystructure (NIL);
  1.3372 +      PBOUT (' ');		/* space delimiter */
  1.3373 +      pstring (body->subtype);	/* subtype */
  1.3374 +      PBOUT (' ');
  1.3375 +      pparam (body->parameter);	/* multipart body extension data */
  1.3376 +      PBOUT (' ');
  1.3377 +      if (body->disposition.type) {
  1.3378 +	PBOUT ('(');
  1.3379 +	pstring (body->disposition.type);
  1.3380 +	PBOUT (' ');
  1.3381 +	pparam (body->disposition.parameter);
  1.3382 +	PBOUT (')');
  1.3383 +      }
  1.3384 +      else PSOUT ("NIL");
  1.3385 +      PBOUT (' ');
  1.3386 +      pnstringorlist (body->language);
  1.3387 +      PBOUT (' ');
  1.3388 +      pnstring (body->location);
  1.3389 +    }
  1.3390 +
  1.3391 +    else {			/* non-multipart body type */
  1.3392 +      pstring ((char *) body_types[body->type]);
  1.3393 +      PBOUT (' ');
  1.3394 +      pstring (body->subtype);
  1.3395 +      PBOUT (' ');
  1.3396 +      pparam (body->parameter);
  1.3397 +      PBOUT (' ');
  1.3398 +      pnstring (body->id);
  1.3399 +      PBOUT (' ');
  1.3400 +      pnstring (body->description);
  1.3401 +      PBOUT (' ');
  1.3402 +      pstring ((char *) body_encodings[body->encoding]);
  1.3403 +      PBOUT (' ');
  1.3404 +      pnum (body->size.bytes);
  1.3405 +      switch (body->type) {	/* extra stuff depends upon body type */
  1.3406 +      case TYPEMESSAGE:
  1.3407 +				/* can't do this if not RFC822 */
  1.3408 +	if (strcmp (body->subtype,"RFC822")) break;
  1.3409 +	PBOUT (' ');
  1.3410 +	penv (body->nested.msg->env);
  1.3411 +	PBOUT (' ');
  1.3412 +	pbodystructure (body->nested.msg->body);
  1.3413 +      case TYPETEXT:
  1.3414 +	PBOUT (' ');
  1.3415 +	pnum (body->size.lines);
  1.3416 +	break;
  1.3417 +      default:
  1.3418 +	break;
  1.3419 +      }
  1.3420 +      PBOUT (' ');
  1.3421 +      pnstring (body->md5);
  1.3422 +      PBOUT (' ');
  1.3423 +      if (body->disposition.type) {
  1.3424 +	PBOUT ('(');
  1.3425 +	pstring (body->disposition.type);
  1.3426 +	PBOUT (' ');
  1.3427 +	pparam (body->disposition.parameter);
  1.3428 +	PBOUT (')');
  1.3429 +      }
  1.3430 +      else PSOUT ("NIL");
  1.3431 +      PBOUT (' ');
  1.3432 +      pnstringorlist (body->language);
  1.3433 +      PBOUT (' ');
  1.3434 +      pnstring (body->location);
  1.3435 +    }
  1.3436 +  }
  1.3437 +				/* no body */
  1.3438 +  else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL");
  1.3439 +  PBOUT (')');			/* end of body */
  1.3440 +}
  1.3441 +
  1.3442 +/* Print body (non-extensible)
  1.3443 + * Accepts: body
  1.3444 + */
  1.3445 +
  1.3446 +void pbody (BODY *body)
  1.3447 +{
  1.3448 +  PBOUT ('(');			/* delimiter */
  1.3449 +  if (body) {			/* only if there is a body */
  1.3450 +    PART *part;
  1.3451 +				/* multipart type? */
  1.3452 +    if (body->type == TYPEMULTIPART) {
  1.3453 +				/* print each part */
  1.3454 +      if (part = body->nested.part)
  1.3455 +	for (; part; part = part->next) pbody (&(part->body));
  1.3456 +      else pbody (NIL);
  1.3457 +      PBOUT (' ');		/* space delimiter */
  1.3458 +      pstring (body->subtype);	/* and finally the subtype */
  1.3459 +    }
  1.3460 +    else {			/* non-multipart body type */
  1.3461 +      pstring ((char *) body_types[body->type]);
  1.3462 +      PBOUT (' ');
  1.3463 +      pstring (body->subtype);
  1.3464 +      PBOUT (' ');
  1.3465 +      pparam (body->parameter);
  1.3466 +      PBOUT (' ');
  1.3467 +      pnstring (body->id);
  1.3468 +      PBOUT (' ');
  1.3469 +      pnstring (body->description);
  1.3470 +      PBOUT (' ');
  1.3471 +      pstring ((char *) body_encodings[body->encoding]);
  1.3472 +      PBOUT (' ');
  1.3473 +      pnum (body->size.bytes);
  1.3474 +      switch (body->type) {	/* extra stuff depends upon body type */
  1.3475 +      case TYPEMESSAGE:
  1.3476 +				/* can't do this if not RFC822 */
  1.3477 +	if (strcmp (body->subtype,"RFC822")) break;
  1.3478 +	PBOUT (' ');
  1.3479 +	penv (body->nested.msg ? body->nested.msg->env : NIL);
  1.3480 +	PBOUT (' ');
  1.3481 +	pbody (body->nested.msg ? body->nested.msg->body : NIL);
  1.3482 +      case TYPETEXT:
  1.3483 +	PBOUT (' ');
  1.3484 +	pnum (body->size.lines);
  1.3485 +	break;
  1.3486 +      default:
  1.3487 +	break;
  1.3488 +      }
  1.3489 +    }
  1.3490 +  }
  1.3491 +				/* no body */
  1.3492 +  else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0");
  1.3493 +  PBOUT (')');			/* end of body */
  1.3494 +}
  1.3495 +
  1.3496 +/* Print parameter list
  1.3497 + * Accepts: paramter
  1.3498 + */
  1.3499 +
  1.3500 +void pparam (PARAMETER *param)
  1.3501 +{
  1.3502 +  if (param) {			/* one specified? */
  1.3503 +    PBOUT ('(');
  1.3504 +    do {
  1.3505 +      pstring (param->attribute);
  1.3506 +      PBOUT (' ');
  1.3507 +      pstring (param->value);
  1.3508 +      if (param = param->next) PBOUT (' ');
  1.3509 +    } while (param);
  1.3510 +    PBOUT (')');		/* end of parameters */
  1.3511 +  }
  1.3512 +  else PSOUT ("NIL");
  1.3513 +}
  1.3514 +
  1.3515 +
  1.3516 +/* Print address list
  1.3517 + * Accepts: address list
  1.3518 + */
  1.3519 +
  1.3520 +void paddr (ADDRESS *a)
  1.3521 +{
  1.3522 +  if (a) {			/* have anything in address? */
  1.3523 +    PBOUT ('(');		/* open the address list */
  1.3524 +    do {			/* for each address */
  1.3525 +      PBOUT ('(');		/* open the address */
  1.3526 +      pnstring (a->personal);	/* personal name */
  1.3527 +      PBOUT (' ');
  1.3528 +      pnstring (a->adl);	/* at-domain-list */
  1.3529 +      PBOUT (' ');
  1.3530 +      pnstring (a->mailbox);	/* mailbox */
  1.3531 +      PBOUT (' ');
  1.3532 +      pnstring (a->host);	/* domain name of mailbox's host */
  1.3533 +      PBOUT (')');		/* terminate address */
  1.3534 +    } while (a = a->next);	/* until end of address */
  1.3535 +    PBOUT (')');		/* close address list */
  1.3536 +  }
  1.3537 +  else PSOUT ("NIL");		/* empty address */
  1.3538 +}
  1.3539 +
  1.3540 +/* Print set
  1.3541 + * Accepts: set
  1.3542 + */
  1.3543 +
  1.3544 +void pset (SEARCHSET **set)
  1.3545 +{
  1.3546 +  SEARCHSET *cur = *set;
  1.3547 +  while (cur) {			/* while there's a set to do */
  1.3548 +    pnum (cur->first);		/* output first value */
  1.3549 +    if (cur->last) {		/* if range, output second value of range */
  1.3550 +      PBOUT (':');
  1.3551 +      pnum (cur->last);
  1.3552 +    }
  1.3553 +    if (cur = cur->next) PBOUT (',');
  1.3554 +  }
  1.3555 +  mail_free_searchset (set);	/* flush set */
  1.3556 +}
  1.3557 +
  1.3558 +
  1.3559 +/* Print number
  1.3560 + * Accepts: number
  1.3561 + */
  1.3562 +
  1.3563 +void pnum (unsigned long i)
  1.3564 +{
  1.3565 +  char tmp[MAILTMPLEN];
  1.3566 +  sprintf (tmp,"%lu",i);
  1.3567 +  PSOUT (tmp);
  1.3568 +}
  1.3569 +
  1.3570 +
  1.3571 +/* Print string
  1.3572 + * Accepts: string
  1.3573 + */
  1.3574 +
  1.3575 +void pstring (char *s)
  1.3576 +{
  1.3577 +  SIZEDTEXT st;
  1.3578 +  st.data = (unsigned char *) s;/* set up sized text */
  1.3579 +  st.size = strlen (s);
  1.3580 +  psizedstring (&st,NIL);	/* print string */
  1.3581 +}
  1.3582 +
  1.3583 +
  1.3584 +/* Print nstring
  1.3585 + * Accepts: string or NIL
  1.3586 + */
  1.3587 +
  1.3588 +void pnstring (char *s)
  1.3589 +{
  1.3590 +  if (s) pstring (s);		/* print string */
  1.3591 +  else PSOUT ("NIL");
  1.3592 +}
  1.3593 +
  1.3594 +
  1.3595 +/* Print atom or string
  1.3596 + * Accepts: astring
  1.3597 + */
  1.3598 +
  1.3599 +void pastring (char *s)
  1.3600 +{
  1.3601 +  char *t;
  1.3602 +  if (!*s) PSOUT ("\"\"");	/* empty string */
  1.3603 +  else {			/* see if atom */
  1.3604 +    for (t = s; (*t > ' ') && !(*t & 0x80) &&
  1.3605 +	 (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') &&
  1.3606 +	 (*t != '{') && (*t != '%') && (*t != '*'); t++);
  1.3607 +    if (*t) pstring (s);	/* not an atom */
  1.3608 +    else PSOUT (s);		/* else plop down as atomic */
  1.3609 +  }
  1.3610 +}
  1.3611 +
  1.3612 +/* Print sized text as quoted
  1.3613 + * Accepts: sized text
  1.3614 + */
  1.3615 +
  1.3616 +void psizedquoted (SIZEDTEXT *s)
  1.3617 +{
  1.3618 +  PBOUT ('"');			/* use quoted string */
  1.3619 +  ptext (s,NIL);
  1.3620 +  PBOUT ('"');
  1.3621 +}
  1.3622 +
  1.3623 +
  1.3624 +/* Print sized text as literal
  1.3625 + * Accepts: sized text
  1.3626 + */
  1.3627 +
  1.3628 +void psizedliteral (SIZEDTEXT *s,STRING *st)
  1.3629 +{
  1.3630 +  PBOUT ('{');			/* print literal size */
  1.3631 +  pnum (s->size);
  1.3632 +  PSOUT ("}\015\012");
  1.3633 +  ptext (s,st);
  1.3634 +}
  1.3635 +
  1.3636 +/* Print sized text as literal or quoted string
  1.3637 + * Accepts: sized text
  1.3638 + *	    alternative stringstruct of text
  1.3639 + */
  1.3640 +
  1.3641 +void psizedstring (SIZEDTEXT *s,STRING *st)
  1.3642 +{
  1.3643 +  unsigned char c;
  1.3644 +  unsigned long i;
  1.3645 +		
  1.3646 +  if (s->data) {		/* if text, check if must use literal */
  1.3647 +    for (i = 0; ((i < s->size) && ((c = s->data[i]) & 0xe0) &&
  1.3648 +		 !(c & 0x80) && (c != '"') && (c != '\\')); ++i);
  1.3649 +				/* must use literal if not all QUOTED-CHAR */
  1.3650 +    if (i < s->size) psizedliteral (s,st);
  1.3651 +    else psizedquoted (s);
  1.3652 +  }
  1.3653 +  else psizedliteral (s,st);
  1.3654 +}
  1.3655 +
  1.3656 +
  1.3657 +/* Print sized text as literal or quoted string
  1.3658 + * Accepts: sized text
  1.3659 + */
  1.3660 +
  1.3661 +void psizedastring (SIZEDTEXT *s)
  1.3662 +{
  1.3663 +  unsigned long i;
  1.3664 +  unsigned int atomp = s->size ? T : NIL;
  1.3665 +  for (i = 0; i < s->size; i++){/* check if must use literal */
  1.3666 +    if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
  1.3667 +	(s->data[i] == '"') || (s->data[i] == '\\')) {
  1.3668 +      psizedliteral (s,NIL);
  1.3669 +      return;
  1.3670 +    }
  1.3671 +    else switch (s->data[i]) {	/* else see if any atom-specials */
  1.3672 +    case '(': case ')': case '{': case ' ':
  1.3673 +    case '%': case '*':		/* list-wildcards */
  1.3674 +    case ']':			/* resp-specials */
  1.3675 +				/* CTL and quoted-specials in literal check */
  1.3676 +      atomp = NIL;		/* not an atom */
  1.3677 +    }
  1.3678 +  }
  1.3679 +  if (atomp) ptext (s,NIL);	/* print as atom */
  1.3680 +  else psizedquoted (s);	/* print as quoted string */
  1.3681 +}
  1.3682 +
  1.3683 +/* Print string list
  1.3684 + * Accepts: string list
  1.3685 + */
  1.3686 +
  1.3687 +void pastringlist (STRINGLIST *s)
  1.3688 +{
  1.3689 +  PBOUT ('(');			/* start list */
  1.3690 +  do {
  1.3691 +    psizedastring (&s->text);	/* output list member */
  1.3692 +    if (s->next) PBOUT (' ');
  1.3693 +  } while (s = s->next);
  1.3694 +  PBOUT (')');			/* terminate list */
  1.3695 +}
  1.3696 +
  1.3697 +
  1.3698 +/* Print nstring or list of strings
  1.3699 + * Accepts: string / string list
  1.3700 + */
  1.3701 +
  1.3702 +void pnstringorlist (STRINGLIST *s)
  1.3703 +{
  1.3704 +  if (!s) PSOUT ("NIL");	/* no argument given */
  1.3705 +  else if (s->next) {		/* output list as list of strings*/
  1.3706 +    PBOUT ('(');		/* start list */
  1.3707 +    do {			/* output list member */
  1.3708 +      psizedstring (&s->text,NIL);
  1.3709 +      if (s->next) PBOUT (' ');
  1.3710 +    } while (s = s->next);
  1.3711 +    PBOUT (')');		/* terminate list */
  1.3712 +  } 
  1.3713 +				/* and single-element list as string */
  1.3714 +  else psizedstring (&s->text,NIL);
  1.3715 +}
  1.3716 +
  1.3717 +/* Print body part string
  1.3718 + * Accepts: message number
  1.3719 + *	    body part id (note: must have space at end to append stuff)
  1.3720 + *	    sized text of string
  1.3721 + *	    alternative stringstruct of string
  1.3722 + *	    text printing arguments
  1.3723 + */
  1.3724 +
  1.3725 +void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
  1.3726 +		      TEXTARGS *ta)
  1.3727 +{
  1.3728 +  int f = mail_elt (stream,msgno)->seen;
  1.3729 +				/* ignore stringstruct if non-initialized */
  1.3730 +  if (bs && !bs->curpos) bs = NIL;
  1.3731 +  if (ta && st->size) {		/* only if have useful data */
  1.3732 +				/* partial specifier */
  1.3733 +    if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
  1.3734 +  				/* in case first byte beyond end of text */
  1.3735 +    if (st->size <= ta->first) st->size = ta->first = 0;
  1.3736 +    else {
  1.3737 +      if (st->data) {		/* offset and truncate */
  1.3738 +	st->data += ta->first;	/* move to desired position */
  1.3739 +	st->size -= ta->first;	/* reduced size */
  1.3740 +      }
  1.3741 +      else if (bs && (SIZE (bs) >= ta->first))
  1.3742 +	SETPOS (bs,ta->first + GETPOS (bs));
  1.3743 +      else st->size = 0;	/* shouldn't happen */
  1.3744 +      if (ta->last && (st->size > ta->last)) st->size = ta->last;
  1.3745 +    }
  1.3746 +  }
  1.3747 +  PSOUT (id);
  1.3748 +  PBOUT (' ');
  1.3749 +  psizedstring (st,bs);		/* output string */
  1.3750 +  changed_flags (msgno,f);	/* and changed flags */
  1.3751 +}
  1.3752 +
  1.3753 +/*  RFC 3501 technically forbids NULs in literals.  Normally, the delivering
  1.3754 + * MTA would take care of MIME converting the message text so that it is
  1.3755 + * NUL-free.  If it doesn't, then we have the choice of either violating
  1.3756 + * IMAP by sending NULs, corrupting the data, or going to lots of work to do
  1.3757 + * MIME conversion in the IMAP server.
  1.3758 + */
  1.3759 +
  1.3760 +/* Print raw sized text
  1.3761 + * Accepts: sizedtext
  1.3762 + */
  1.3763 +
  1.3764 +void ptext (SIZEDTEXT *txt,STRING *st)
  1.3765 +{
  1.3766 +  unsigned char c,*s;
  1.3767 +  unsigned long i = txt->size;
  1.3768 +  if (s = txt->data) while (i && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))) --i;
  1.3769 +  else if (st) while (i && (PBOUT ((c = SNX (st)) ? c : 0x80) != EOF)) --i;
  1.3770 +				/* failed to complete? */
  1.3771 +  if (i) ioerror (stdout,"writing text");
  1.3772 +}
  1.3773 +
  1.3774 +/* Print thread
  1.3775 + * Accepts: thread
  1.3776 + */
  1.3777 +
  1.3778 +void pthread (THREADNODE *thr)
  1.3779 +{
  1.3780 +  THREADNODE *t;
  1.3781 +  while (thr) {			/* for each branch */
  1.3782 +    PBOUT ('(');		/* open branch */
  1.3783 +    if (thr->num) {		/* first node message number */
  1.3784 +      pnum (thr->num);
  1.3785 +      if (t = thr->next) {	/* any subsequent nodes? */
  1.3786 +	PBOUT (' ');
  1.3787 +	while (t) {		/* for each subsequent node */
  1.3788 +	  if (t->branch) {	/* branches? */
  1.3789 +	    pthread (t);	/* yes, recurse to do branch */
  1.3790 +	    t = NIL;		/* done */
  1.3791 +	  }
  1.3792 +	  else {		/* just output this number */
  1.3793 +	    pnum (t->num);
  1.3794 +	    t = t->next;	/* and do next message */
  1.3795 +	  }
  1.3796 +	  if (t) PBOUT (' ');	/* delimit if more to come */
  1.3797 +	}
  1.3798 +      }
  1.3799 +    }
  1.3800 +    else pthread (thr->next);	/* nest for dummy */
  1.3801 +    PBOUT (')');		/* done with this branch */
  1.3802 +    thr = thr->branch;		/* do next branch */
  1.3803 +  }
  1.3804 +}
  1.3805 +
  1.3806 +/* Print capabilities
  1.3807 + * Accepts: option flag
  1.3808 + */
  1.3809 +
  1.3810 +void pcapability (long flag)
  1.3811 +{
  1.3812 +  unsigned long i;
  1.3813 +  char *s;
  1.3814 +  struct stat sbuf;
  1.3815 +  AUTHENTICATOR *auth;
  1.3816 +  THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
  1.3817 +				/* always output protocol level */
  1.3818 +  PSOUT ("CAPABILITY IMAP4REV1 I18NLEVEL=1 LITERAL+");
  1.3819 +#ifdef NETSCAPE_BRAIN_DAMAGE
  1.3820 +  PSOUT (" X-NETSCAPE");
  1.3821 +#endif
  1.3822 +  if (flag >= 0) {		/* want post-authentication capabilities? */
  1.3823 +    PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT ESEARCH WITHIN SCAN SORT");
  1.3824 +    while (thr) {		/* threaders */
  1.3825 +      PSOUT (" THREAD=");
  1.3826 +      PSOUT (thr->name);
  1.3827 +      thr = thr->next;
  1.3828 +    }
  1.3829 +    if (!anonymous) PSOUT (" MULTIAPPEND");
  1.3830 +  }
  1.3831 +  if (flag <= 0) {		/* want pre-authentication capabilities? */
  1.3832 +    PSOUT (" SASL-IR LOGIN-REFERRALS");
  1.3833 +    if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
  1.3834 +    else PSOUT (" STARTTLS");
  1.3835 +				/* disable plaintext */
  1.3836 +    if (!(i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)))
  1.3837 +      PSOUT (" LOGINDISABLED");
  1.3838 +    for (auth = mail_lookup_auth (1); auth; auth = auth->next)
  1.3839 +      if (auth->server && !(auth->flags & AU_DISABLE) &&
  1.3840 +	  !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
  1.3841 +	PSOUT (" AUTH=");
  1.3842 +	PSOUT (auth->name);
  1.3843 +      }
  1.3844 +    if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
  1.3845 +  }
  1.3846 +}
  1.3847 +
  1.3848 +/* Anonymous users may only use these mailboxes in these namespaces */
  1.3849 +
  1.3850 +char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
  1.3851 +
  1.3852 +
  1.3853 +/* Check if mailbox name is OK
  1.3854 + * Accepts: reference name
  1.3855 + *	    mailbox name
  1.3856 + */
  1.3857 +
  1.3858 +long nameok (char *ref,char *name)
  1.3859 +{
  1.3860 +  int i;
  1.3861 +  unsigned char *s,*t;
  1.3862 +  if (!name) return NIL;	/* failure if missing name */
  1.3863 +  if (!anonymous) return T;	/* otherwise OK if not anonymous */
  1.3864 +				/* validate reference */
  1.3865 +  if (ref && ((*ref == '#') || (*ref == '{')))
  1.3866 +    for (i = 0; oktab[i]; i++) {
  1.3867 +      for (s = ref, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
  1.3868 +      if (!*t) {		/* reference OK */
  1.3869 +	if (*name == '#') break;/* check name if override */
  1.3870 +	else return T;		/* otherwise done */
  1.3871 +      }
  1.3872 +    }
  1.3873 +				/* ordinary names are OK */
  1.3874 +  if ((*name != '#') && (*name != '{')) return T;
  1.3875 +  for (i = 0; oktab[i]; i++) {	/* validate mailbox */
  1.3876 +    for (s = name, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
  1.3877 +    if (!*t) return T;		/* name is OK */
  1.3878 +  }
  1.3879 +  response = "%.80s NO Anonymous may not %.80s this name\015\012";
  1.3880 +  return NIL;
  1.3881 +}
  1.3882 +
  1.3883 +
  1.3884 +/* Convert possible BBoard name to actual name
  1.3885 + * Accepts: command
  1.3886 + *	    mailbox name
  1.3887 + * Returns: maibox name
  1.3888 + */
  1.3889 +
  1.3890 +char *bboardname (char *cmd,char *name)
  1.3891 +{
  1.3892 +  if (cmd[0] == 'B') {		/* want bboard? */
  1.3893 +    char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
  1.3894 +    sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
  1.3895 +    name = s;
  1.3896 +  }
  1.3897 +  return name;
  1.3898 +}
  1.3899 +
  1.3900 +/* Test if name is news proxy
  1.3901 + * Accepts: name
  1.3902 + * Returns: T if news proxy, NIL otherwise
  1.3903 + */
  1.3904 +
  1.3905 +long isnewsproxy (char *name)
  1.3906 +{
  1.3907 +  return (nntpproxy && (name[0] == '#') &&
  1.3908 +	  ((name[1] == 'N') || (name[1] == 'n')) &&
  1.3909 +	  ((name[2] == 'E') || (name[2] == 'e')) &&
  1.3910 +	  ((name[3] == 'W') || (name[3] == 'w')) &&
  1.3911 +	  ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ?
  1.3912 +    LONGT : NIL;
  1.3913 +}
  1.3914 +
  1.3915 +
  1.3916 +/* News proxy generate canonical pattern
  1.3917 + * Accepts: reference
  1.3918 + *	    pattern
  1.3919 + *	    buffer to return canonical pattern
  1.3920 + * Returns: T on success with pattern in buffer, NIL on failure
  1.3921 + */
  1.3922 +
  1.3923 +long newsproxypattern (char *ref,char *pat,char *pattern,long flag)
  1.3924 +{
  1.3925 +  if (!nntpproxy) return NIL;
  1.3926 +  if (strlen (ref) > NETMAXMBX) {
  1.3927 +    sprintf (pattern,"Invalid reference specification: %.80s",ref);
  1.3928 +    mm_log (pattern,ERROR);
  1.3929 +    return NIL;
  1.3930 +  }
  1.3931 +  if (strlen (pat) > NETMAXMBX) {
  1.3932 +    sprintf (pattern,"Invalid pattern specification: %.80s",pat);
  1.3933 +    mm_log (pattern,ERROR);
  1.3934 +    return NIL;
  1.3935 +  }
  1.3936 +  if (flag) {			/* prepend proxy specifier */
  1.3937 +    sprintf (pattern,"{%.300s/nntp}",nntpproxy);
  1.3938 +    pattern += strlen (pattern);
  1.3939 +  }
  1.3940 +  if (*ref) {			/* have a reference */
  1.3941 +    strcpy (pattern,ref);	/* copy reference to pattern */
  1.3942 +				/* # overrides mailbox field in reference */
  1.3943 +    if (*pat == '#') strcpy (pattern,pat);
  1.3944 +				/* pattern starts, reference ends, with . */
  1.3945 +    else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
  1.3946 +      strcat (pattern,pat + 1);	/* append, omitting one of the period */
  1.3947 +    else strcat (pattern,pat);	/* anything else is just appended */
  1.3948 +  }
  1.3949 +  else strcpy (pattern,pat);	/* just have basic name */
  1.3950 +  return isnewsproxy (pattern);
  1.3951 +}
  1.3952 +
  1.3953 +/* IMAP4rev1 Authentication responder
  1.3954 + * Accepts: challenge
  1.3955 + *	    length of challenge
  1.3956 + *	    pointer to response length return location if non-NIL
  1.3957 + * Returns: response
  1.3958 + */
  1.3959 +
  1.3960 +#define RESPBUFLEN 8*MAILTMPLEN
  1.3961 +
  1.3962 +char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
  1.3963 +{
  1.3964 +  unsigned long i,j;
  1.3965 +  unsigned char *t,resp[RESPBUFLEN];
  1.3966 +  if (initial) {		/* initial response given? */
  1.3967 +    if (clen) return NIL;	/* not permitted */
  1.3968 +				/* set up response */
  1.3969 +    i = strlen ((char *) (t = initial));
  1.3970 +    initial = NIL;		/* no more initial response */
  1.3971 +    if ((*t == '=') && !t[1]) {	/* SASL-IR does this for 0-length response */
  1.3972 +      if (rlen) *rlen = 0;	/* set length zero if empty */
  1.3973 +      return cpystr ("");	/* and return empty string as response */
  1.3974 +    }
  1.3975 +  }
  1.3976 +  else {			/* issue challenge, get response */
  1.3977 +    PSOUT ("+ ");
  1.3978 +    for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
  1.3979 +      if (t[j] > ' ') PBOUT (t[j]);
  1.3980 +    fs_give ((void **) &t);
  1.3981 +    CRLF;
  1.3982 +    PFLUSH ();			/* dump output buffer */
  1.3983 +				/* slurp response buffer */
  1.3984 +    slurp ((char *) resp,RESPBUFLEN,INPUTTIMEOUT);
  1.3985 +    if (!(t = (unsigned char *) strchr ((char *) resp,'\012')))
  1.3986 +      return (char *) flush ();
  1.3987 +    if (t[-1] == '\015') --t;	/* remove CR */
  1.3988 +    *t = '\0';			/* tie off buffer */
  1.3989 +    if (resp[0] == '*') {
  1.3990 +      cancelled = T;
  1.3991 +      return NIL;
  1.3992 +    }
  1.3993 +    i = t - resp;		/* length of response */
  1.3994 +    t = resp;			/* set up for return call */
  1.3995 +  }
  1.3996 +  return (i % 4) ? NIL :	/* return if valid BASE64 */
  1.3997 +    (char *) rfc822_base64 (t,i,rlen ? rlen : &i);
  1.3998 +}
  1.3999 +
  1.4000 +/* Proxy copy across mailbox formats
  1.4001 + * Accepts: mail stream
  1.4002 + *	    sequence to copy on this stream
  1.4003 + *	    destination mailbox
  1.4004 + *	    option flags
  1.4005 + * Returns: T if success, else NIL
  1.4006 + */
  1.4007 +
  1.4008 +long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  1.4009 +{
  1.4010 +  MAILSTREAM *ts;
  1.4011 +  STRING st;
  1.4012 +  MSGDATA md;
  1.4013 +  SEARCHSET *set;
  1.4014 +  char tmp[MAILTMPLEN];
  1.4015 +  unsigned long i,j;
  1.4016 +  md.stream = stream;
  1.4017 +  md.msgno = 0;
  1.4018 +  md.flags = md.date = NIL;
  1.4019 +  md.message = &st;
  1.4020 +  /* Currently ignores CP_MOVE and CP_DEBUG */
  1.4021 +  if (!((options & CP_UID) ?	/* validate sequence */
  1.4022 +	mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
  1.4023 +    return NIL;
  1.4024 +  response = win;		/* cancel previous errors */
  1.4025 +  if (lsterr) fs_give ((void **) &lsterr);
  1.4026 +				/* c-client clobbers sequence, use spare */
  1.4027 +  for (i = 1,j = 0,set = mail_newsearchset (); i <= nmsgs; i++)
  1.4028 +    if (mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) {
  1.4029 +      mail_append_set (set,mail_uid (stream,i));
  1.4030 +      if (!j) md.msgno = (j = i) - 1;
  1.4031 +    }
  1.4032 +				/* only if at least one message to copy */
  1.4033 +  if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) {
  1.4034 +    response = trycreate ? losetry : lose;
  1.4035 +    if (set) mail_free_searchset (&set);
  1.4036 +    return NIL;
  1.4037 +  }
  1.4038 +  if (caset) csset = set;	/* set for return value now */
  1.4039 +  else if (set) mail_free_searchset (&set);
  1.4040 +  response = win;		/* stomp any previous babble */
  1.4041 +  if (md.msgno) {		/* get new driver name if was dummy */
  1.4042 +    sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
  1.4043 +	     stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
  1.4044 +	     ts->dtb->name : "unknown");
  1.4045 +    mm_log (tmp,NIL);
  1.4046 +  }
  1.4047 +  return LONGT;
  1.4048 +}
  1.4049 +
  1.4050 +/* Proxy append message callback
  1.4051 + * Accepts: MAIL stream
  1.4052 + *	    append data package
  1.4053 + *	    pointer to return initial flags
  1.4054 + *	    pointer to return message internal date
  1.4055 + *	    pointer to return stringstruct of message or NIL to stop
  1.4056 + * Returns: T if success (have message or stop), NIL if error
  1.4057 + */
  1.4058 +
  1.4059 +long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
  1.4060 +		   STRING **message)
  1.4061 +{
  1.4062 +  MESSAGECACHE *elt;
  1.4063 +  unsigned long i;
  1.4064 +  char *s,*t,tmp[MAILTMPLEN];
  1.4065 +  MSGDATA *md = (MSGDATA *) data;
  1.4066 +  if (md->flags) fs_give ((void **) &md->flags);
  1.4067 +  if (md->date) fs_give ((void **) &md->date);
  1.4068 +  *message = NIL;		/* assume all done */
  1.4069 +  *flags = *date = NIL;
  1.4070 +  while (++md->msgno <= nmsgs)
  1.4071 +    if ((elt = mail_elt (md->stream,md->msgno))->spare) {
  1.4072 +      if (!(elt->valid && elt->day)) {
  1.4073 +	sprintf (tmp,"%lu",md->msgno);
  1.4074 +	mail_fetch_fast (md->stream,tmp,NIL);
  1.4075 +      }
  1.4076 +      memset (s = tmp,0,MAILTMPLEN);
  1.4077 +				/* copy flags */
  1.4078 +      if (elt->seen) strcat (s," \\Seen");
  1.4079 +      if (elt->deleted) strcat (s," \\Deleted");
  1.4080 +      if (elt->flagged) strcat (s," \\Flagged");
  1.4081 +      if (elt->answered) strcat (s," \\Answered");
  1.4082 +      if (elt->draft) strcat (s," \\Draft");
  1.4083 +      if (i = elt->user_flags) do 
  1.4084 +	if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t &&
  1.4085 +	    (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
  1.4086 +	*s++ = ' ';		/* space delimiter */
  1.4087 +	strcpy (s,t);
  1.4088 +      } while (i);		/* until no more user flags */
  1.4089 +      *message = md->message;	/* set up return values */
  1.4090 +      *flags = md->flags = cpystr (tmp + 1);
  1.4091 +      *date = md->date = cpystr (mail_date (tmp,elt));
  1.4092 +      INIT (md->message,msg_string,(void *) md,elt->rfc822_size);
  1.4093 +      break;			/* process this message */
  1.4094 +    }
  1.4095 +  return LONGT;
  1.4096 +}
  1.4097 +
  1.4098 +/* Append message callback
  1.4099 + * Accepts: MAIL stream
  1.4100 + *	    append data package
  1.4101 + *	    pointer to return initial flags
  1.4102 + *	    pointer to return message internal date
  1.4103 + *	    pointer to return stringstruct of message or NIL to stop
  1.4104 + * Returns: T if success (have message or stop), NIL if error
  1.4105 + */
  1.4106 +
  1.4107 +long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
  1.4108 +		 STRING **message)
  1.4109 +{
  1.4110 +  unsigned long i,j;
  1.4111 +  char *t;
  1.4112 +  APPENDDATA *ad = (APPENDDATA *) data;
  1.4113 +  unsigned char *arg = ad->arg;
  1.4114 +				/* flush text of previous message */
  1.4115 +  if (t = ad->flags) fs_give ((void **) &ad->flags);
  1.4116 +  if (t = ad->date) fs_give ((void **) &ad->date);
  1.4117 +  if (t = ad->msg) fs_give ((void **) &ad->msg);
  1.4118 +  *flags = *date = NIL;		/* assume no flags or date */
  1.4119 +  if (t) {			/* have previous message? */
  1.4120 +    if (!*arg) {		/* if least one message, and no more coming */
  1.4121 +      *message = NIL;		/* set stop */
  1.4122 +      return LONGT;		/* return success */
  1.4123 +    }
  1.4124 +    else if (*arg++ != ' ') {	/* must have a delimiter to next argument */
  1.4125 +      response = misarg;	/* oops */
  1.4126 +      return NIL;
  1.4127 +    }
  1.4128 +  }
  1.4129 +  *message = ad->message;	/* return pointer to message stringstruct */
  1.4130 +  if (*arg == '(') {		/* parse optional flag list */
  1.4131 +    t = ++arg;			/* pointer to flag list contents */
  1.4132 +    while (*arg && (*arg != ')')) arg++;
  1.4133 +    if (*arg) *arg++ = '\0';
  1.4134 +    if (*arg == ' ') arg++;
  1.4135 +    *flags = ad->flags = cpystr (t);
  1.4136 +  }
  1.4137 +				/* parse optional date */
  1.4138 +  if (*arg == '"') *date = ad->date = cpystr (snarf (&arg));
  1.4139 +  if (!arg || (*arg != '{'))	/* parse message */
  1.4140 +    response = "%.80s BAD Missing literal in %.80s\015\012";
  1.4141 +  else if (!isdigit (arg[1]))
  1.4142 +    response = "%.80s BAD Missing message to %.80s\015\012";
  1.4143 +  else if (!(i = strtoul (arg+1,&t,10)))
  1.4144 +    response = "%.80s NO Empty message to %.80s\015\012";
  1.4145 +  else if (i > MAXAPPENDTXT)	/* maybe relax this a little */
  1.4146 +    response = "%.80s NO Excessively large message to %.80s\015\012";
  1.4147 +  else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) {
  1.4148 +				/* get a literal buffer */
  1.4149 +    inliteral (ad->msg = (char *) fs_get (i+1),i);
  1.4150 +    				/* get new command tail */
  1.4151 +    slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT);
  1.4152 +    if (strchr (ad->arg,'\012')) {
  1.4153 +				/* reset strtok mechanism, tie off if done */
  1.4154 +      if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0';
  1.4155 +				/* possible LITERAL+? */
  1.4156 +      if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') &&
  1.4157 +	  (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) {
  1.4158 +				/* back over possible count */
  1.4159 +	for (j -= 4; j && isdigit (ad->arg[j]); j--);
  1.4160 +	if (ad->arg[j] == '{') {/* found a literal? */
  1.4161 +	  litplus.ok = T;	/* yes, note LITERAL+ in effect, set size */
  1.4162 +	  litplus.size = strtoul (ad->arg + j + 1,NIL,10);
  1.4163 +	}
  1.4164 +      }
  1.4165 +				/* initialize stringstruct */
  1.4166 +      INIT (ad->message,mail_string,(void *) ad->msg,i);
  1.4167 +      return LONGT;		/* ready to go */
  1.4168 +    }
  1.4169 +    flush ();			/* didn't find end of line? */
  1.4170 +    fs_give ((void **) &ad->msg);
  1.4171 +  }
  1.4172 +  else response = badarg;	/* not a literal */
  1.4173 +  return NIL;			/* error */
  1.4174 +}
  1.4175 +
  1.4176 +/* Got COPY UID data
  1.4177 + * Accepts: MAIL stream
  1.4178 + *	    mailbox name
  1.4179 + *	    UID validity
  1.4180 + *	    source set of UIDs
  1.4181 + *	    destination set of UIDs
  1.4182 + */
  1.4183 +
  1.4184 +void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
  1.4185 +	      SEARCHSET *sourceset,SEARCHSET *destset)
  1.4186 +{
  1.4187 +  if (cauidvalidity) fatal ("duplicate COPYUID/APPENDUID data");
  1.4188 +  cauidvalidity = uidvalidity;
  1.4189 +  csset = sourceset;
  1.4190 +  caset = destset;
  1.4191 +}
  1.4192 +
  1.4193 +
  1.4194 +/* Got APPEND UID data
  1.4195 + * Accepts: mailbox name
  1.4196 + *	    UID validity
  1.4197 + *	    destination set of UIDs
  1.4198 + */
  1.4199 +
  1.4200 +void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set)
  1.4201 +{
  1.4202 +  copyuid (NIL,mailbox,uidvalidity,NIL,set);
  1.4203 +}
  1.4204 +
  1.4205 +
  1.4206 +/* Got a referral
  1.4207 + * Accepts: MAIL stream
  1.4208 + *	    URL
  1.4209 + *	    referral type code
  1.4210 + */
  1.4211 +
  1.4212 +char *referral (MAILSTREAM *stream,char *url,long code)
  1.4213 +{
  1.4214 +  if (lstref) fs_give ((void **) &lstref);
  1.4215 +  lstref = cpystr (url);	/* set referral */
  1.4216 +				/* set error if not a logged in referral */
  1.4217 +  if (code != REFAUTH) response = lose;
  1.4218 +  if (!lsterr) lsterr = cpystr ("Try referral URL");
  1.4219 +  return NIL;			/* don't chase referrals for now */
  1.4220 +}
  1.4221 +
  1.4222 +/* Co-routines from MAIL library */
  1.4223 +
  1.4224 +
  1.4225 +/* Message matches a search
  1.4226 + * Accepts: MAIL stream
  1.4227 + *	    message number
  1.4228 + */
  1.4229 +
  1.4230 +void mm_searched (MAILSTREAM *s,unsigned long msgno)
  1.4231 +{
  1.4232 +				/* nothing to do here */
  1.4233 +}
  1.4234 +
  1.4235 +
  1.4236 +/* Message exists (i.e. there are that many messages in the mailbox)
  1.4237 + * Accepts: MAIL stream
  1.4238 + *	    message number
  1.4239 + */
  1.4240 +
  1.4241 +void mm_exists (MAILSTREAM *s,unsigned long number)
  1.4242 +{
  1.4243 +				/* note change in number of messages */
  1.4244 +  if ((s != tstream) && (nmsgs != number)) {
  1.4245 +    nmsgs = number;		/* always update number of messages */
  1.4246 +    if (quell_events) existsquelled = T;
  1.4247 +    else {
  1.4248 +      PSOUT ("* ");
  1.4249 +      pnum (nmsgs);
  1.4250 +      PSOUT (" EXISTS\015\012");
  1.4251 +    }
  1.4252 +    recent = 0xffffffff;	/* make sure update recent too */
  1.4253 +  }
  1.4254 +}
  1.4255 +
  1.4256 +
  1.4257 +/* Message expunged
  1.4258 + * Accepts: MAIL stream
  1.4259 + *	    message number
  1.4260 + */
  1.4261 +
  1.4262 +void mm_expunged (MAILSTREAM *s,unsigned long number)
  1.4263 +{
  1.4264 +  if (quell_events) fatal ("Impossible EXPUNGE event");
  1.4265 +  if (s != tstream) {
  1.4266 +    PSOUT ("* ");
  1.4267 +    pnum (number);
  1.4268 +    PSOUT (" EXPUNGE\015\012");
  1.4269 +  }
  1.4270 +  nmsgs--;
  1.4271 +  existsquelled = T;		/* do EXISTS when command done */
  1.4272 +}
  1.4273 +
  1.4274 +
  1.4275 +/* Message status changed
  1.4276 + * Accepts: MAIL stream
  1.4277 + *	    message number
  1.4278 + */
  1.4279 +
  1.4280 +void mm_flags (MAILSTREAM *s,unsigned long number)
  1.4281 +{
  1.4282 +  if (s != tstream) mail_elt (s,number)->spare2 = T;
  1.4283 +}
  1.4284 +
  1.4285 +/* Mailbox found
  1.4286 + * Accepts: hierarchy delimiter
  1.4287 + *	    mailbox name
  1.4288 + *	    attributes
  1.4289 + */
  1.4290 +
  1.4291 +void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  1.4292 +{
  1.4293 +  mm_list_work ("LIST",delimiter,name,attributes);
  1.4294 +}
  1.4295 +
  1.4296 +
  1.4297 +/* Subscribed mailbox found
  1.4298 + * Accepts: hierarchy delimiter
  1.4299 + *	    mailbox name
  1.4300 + *	    attributes
  1.4301 + */
  1.4302 +
  1.4303 +void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  1.4304 +{
  1.4305 +  mm_list_work ("LSUB",delimiter,name,attributes);
  1.4306 +}
  1.4307 +
  1.4308 +
  1.4309 +/* Mailbox status
  1.4310 + * Accepts: MAIL stream
  1.4311 + *	    mailbox name
  1.4312 + *	    mailbox status
  1.4313 + */
  1.4314 +
  1.4315 +void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
  1.4316 +{
  1.4317 +  if (!quell_events) {
  1.4318 +    char tmp[MAILTMPLEN];
  1.4319 +    tmp[0] = tmp[1] = '\0';
  1.4320 +    if (status->flags & SA_MESSAGES)
  1.4321 +      sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
  1.4322 +    if (status->flags & SA_RECENT)
  1.4323 +      sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
  1.4324 +    if (status->flags & SA_UNSEEN)
  1.4325 +      sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
  1.4326 +    if (status->flags & SA_UIDNEXT)
  1.4327 +      sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
  1.4328 +    if (status->flags & SA_UIDVALIDITY)
  1.4329 +      sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
  1.4330 +    PSOUT ("* STATUS ");
  1.4331 +    pastring (mailbox);
  1.4332 +    PSOUT (" (");
  1.4333 +    PSOUT (tmp+1);
  1.4334 +    PBOUT (')');
  1.4335 +    CRLF;
  1.4336 +  }
  1.4337 +}
  1.4338 +
  1.4339 +/* Worker routine for LIST and LSUB
  1.4340 + * Accepts: name of response
  1.4341 + *	    hierarchy delimiter
  1.4342 + *	    mailbox name
  1.4343 + *	    attributes
  1.4344 + */
  1.4345 +
  1.4346 +void mm_list_work (char *what,int delimiter,char *name,long attributes)
  1.4347 +{
  1.4348 +  char *s;
  1.4349 +  if (!quell_events) {
  1.4350 +    char tmp[MAILTMPLEN];
  1.4351 +    if (finding) {
  1.4352 +      PSOUT ("* MAILBOX ");
  1.4353 +      PSOUT (name);
  1.4354 +    }
  1.4355 +				/* new form */
  1.4356 +    else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
  1.4357 +      PSOUT ("* ");
  1.4358 +      PSOUT (what);
  1.4359 +      PSOUT (" (");
  1.4360 +      tmp[0] = tmp[1] = '\0';
  1.4361 +      if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors");
  1.4362 +      if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect");
  1.4363 +      if (attributes & LATT_MARKED) strcat (tmp," \\Marked");
  1.4364 +      if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked");
  1.4365 +      if (attributes & LATT_HASCHILDREN) strcat (tmp," \\HasChildren");
  1.4366 +      if (attributes & LATT_HASNOCHILDREN) strcat (tmp," \\HasNoChildren");
  1.4367 +      PSOUT (tmp+1);
  1.4368 +      switch (delimiter) {
  1.4369 +      case '\\':		/* quoted delimiter */
  1.4370 +      case '"':
  1.4371 +	PSOUT (") \"\\");
  1.4372 +	PBOUT (delimiter);
  1.4373 +	PBOUT ('"');
  1.4374 +	break;
  1.4375 +      case '\0':		/* no delimiter */
  1.4376 +	PSOUT (") NIL");
  1.4377 +	break;
  1.4378 +      default:			/* unquoted delimiter */
  1.4379 +	PSOUT (") \"");
  1.4380 +	PBOUT (delimiter);
  1.4381 +	PBOUT ('"');
  1.4382 +	break;
  1.4383 +      }
  1.4384 +      PBOUT (' ');
  1.4385 +				/* output mailbox name */
  1.4386 +      if (proxylist && (s = strchr (name,'}'))) pastring (s+1);
  1.4387 +      else pastring (name);
  1.4388 +    }
  1.4389 +    CRLF;
  1.4390 +  }
  1.4391 +}
  1.4392 +
  1.4393 +/* Notification event
  1.4394 + * Accepts: MAIL stream
  1.4395 + *	    string to log
  1.4396 + *	    error flag
  1.4397 + */
  1.4398 +
  1.4399 +void mm_notify (MAILSTREAM *stream,char *string,long errflg)
  1.4400 +{
  1.4401 +  SIZEDTEXT msg;
  1.4402 +  char *s,*code;
  1.4403 +  if (!quell_events && (!tstream || (stream != tstream))) {
  1.4404 +    switch (errflg) {
  1.4405 +    case NIL:			/* information message, set as OK response */
  1.4406 +      if ((string[0] == '[') &&
  1.4407 +	  ((string[1] == 'T') || (string[1] == 't')) &&
  1.4408 +	  ((string[2] == 'R') || (string[2] == 'r')) &&
  1.4409 +	  ((string[3] == 'Y') || (string[3] == 'y')) &&
  1.4410 +	  ((string[4] == 'C') || (string[4] == 'c')) &&
  1.4411 +	  ((string[5] == 'R') || (string[5] == 'r')) &&
  1.4412 +	  ((string[6] == 'E') || (string[6] == 'e')) &&
  1.4413 +	  ((string[7] == 'A') || (string[7] == 'a')) &&
  1.4414 +	  ((string[8] == 'T') || (string[8] == 't')) &&
  1.4415 +	  ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
  1.4416 +	trycreate = T;
  1.4417 +    case BYE:			/* some other server signing off */
  1.4418 +    case PARSE:			/* parse glitch, output unsolicited OK */
  1.4419 +      code = "* OK ";
  1.4420 +      break;
  1.4421 +    case WARN:			/* warning, output unsolicited NO (kludge!) */
  1.4422 +      code = "* NO ";
  1.4423 +      break;
  1.4424 +    case ERROR:			/* error that broke command */
  1.4425 +    default:			/* default should never happen */
  1.4426 +      code = "* BAD ";
  1.4427 +      break;
  1.4428 +    }
  1.4429 +    PSOUT (code);
  1.4430 +    msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),
  1.4431 +			     "\015\012")) ?
  1.4432 +      (s - string) : strlen (string);
  1.4433 +    PSOUTR (&msg);
  1.4434 +    CRLF;
  1.4435 +    PFLUSH ();			/* let client see it immediately */
  1.4436 +  }
  1.4437 +}
  1.4438 +
  1.4439 +/* Log an event for the user to see
  1.4440 + * Accepts: string to log
  1.4441 + *	    error flag
  1.4442 + */
  1.4443 +
  1.4444 +void mm_log (char *string,long errflg)
  1.4445 +{
  1.4446 +  SIZEDTEXT msg;
  1.4447 +  char *s;
  1.4448 +  msg.size = 
  1.4449 +    (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
  1.4450 +      (s - string) : strlen (string);
  1.4451 +  switch (errflg) {
  1.4452 +  case NIL:			/* information message, set as OK response */
  1.4453 +    if (response == win) {	/* only if no other response yet */
  1.4454 +      if (lsterr) {		/* if there was a previous message */
  1.4455 +	if (!quell_events) {
  1.4456 +	  PSOUT ("* OK ");	/* blat it out */
  1.4457 +	  PSOUT (lsterr);
  1.4458 +	  CRLF;
  1.4459 +	  PFLUSH ();		/* let client see it immediately */
  1.4460 +	}
  1.4461 +	fs_give ((void **) &lsterr);
  1.4462 +      }
  1.4463 +      lsterr = cpystr (string); /* copy string for later use */
  1.4464 +      if (s) lsterr[s - string] = NIL;
  1.4465 +    }
  1.4466 +    break;
  1.4467 +  case PARSE:			/* parse glitch, output unsolicited OK */
  1.4468 +    if (!quell_events) {
  1.4469 +      PSOUT ("* OK [PARSE] ");
  1.4470 +      PSOUTR (&msg);
  1.4471 +      CRLF;
  1.4472 +      PFLUSH ();		/* let client see it immediately */
  1.4473 +    }
  1.4474 +    break;
  1.4475 +  case WARN:			/* warning, output unsolicited NO */
  1.4476 +				/* ignore "Mailbox is empty" (KLUDGE!) */
  1.4477 +    if (strcmp (string,"Mailbox is empty")) {
  1.4478 +      if (lstwrn) {		/* have previous warning? */
  1.4479 +	if (!quell_events) {
  1.4480 +	  PSOUT ("* NO ");
  1.4481 +	  PSOUT (lstwrn);
  1.4482 +	  CRLF;
  1.4483 +	  PFLUSH ();		/* make sure client sees it immediately */
  1.4484 +	}
  1.4485 +	fs_give ((void **) &lstwrn);
  1.4486 +      }
  1.4487 +      lstwrn = cpystr (string); /* note last warning */
  1.4488 +      if (s) lstwrn[s - string] = NIL;
  1.4489 +    }
  1.4490 +    break;
  1.4491 +  case ERROR:			/* error that broke command */
  1.4492 +  default:			/* default should never happen */
  1.4493 +    response = trycreate ? losetry : lose;
  1.4494 +    if (lsterr) fs_give ((void **) &lsterr);
  1.4495 +    lsterr = cpystr (string);	/* note last error */
  1.4496 +    if (s) lsterr[s - string] = NIL;
  1.4497 +    break;
  1.4498 +  }
  1.4499 +}
  1.4500 +
  1.4501 +/* Return last error
  1.4502 + */
  1.4503 +
  1.4504 +char *lasterror (void)
  1.4505 +{
  1.4506 +  if (lsterr) return lsterr;
  1.4507 +  if (lstwrn) return lstwrn;
  1.4508 +  return "<unknown>";
  1.4509 +}
  1.4510 +
  1.4511 +
  1.4512 +/* Log an event to debugging telemetry
  1.4513 + * Accepts: string to log
  1.4514 + */
  1.4515 +
  1.4516 +void mm_dlog (char *string)
  1.4517 +{
  1.4518 +  mm_log (string,WARN);		/* shouldn't happen normally */
  1.4519 +}
  1.4520 +
  1.4521 +/* Get user name and password for this host
  1.4522 + * Accepts: parse of network user name
  1.4523 + *	    where to return user name
  1.4524 + *	    where to return password
  1.4525 + *	    trial count
  1.4526 + */
  1.4527 +
  1.4528 +void mm_login (NETMBX *mb,char *username,char *password,long trial)
  1.4529 +{
  1.4530 +				/* set user name */
  1.4531 +  strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER);
  1.4532 +  strncpy (password,pass,256);	/* and password */
  1.4533 +}
  1.4534 +
  1.4535 +
  1.4536 +/* About to enter critical code
  1.4537 + * Accepts: stream
  1.4538 + */
  1.4539 +
  1.4540 +void mm_critical (MAILSTREAM *s)
  1.4541 +{
  1.4542 +  ++critical;
  1.4543 +}
  1.4544 +
  1.4545 +
  1.4546 +/* About to exit critical code
  1.4547 + * Accepts: stream
  1.4548 + */
  1.4549 +
  1.4550 +void mm_nocritical (MAILSTREAM *s)
  1.4551 +{
  1.4552 +				/* go non-critical, pending death? */
  1.4553 +  if (!--critical && (state == LOGOUT)) {
  1.4554 +				/* clean up iff needed */
  1.4555 +    if (s && (stream != s) && !s->lock && (s->dtb->flags & DR_XPOINT))
  1.4556 +      s = mail_close (s);
  1.4557 +    longjmp (jmpenv,1);		/* die now */
  1.4558 +  }
  1.4559 +}
  1.4560 +
  1.4561 +/* Disk error found
  1.4562 + * Accepts: stream
  1.4563 + *	    system error code
  1.4564 + *	    flag indicating that mailbox may be clobbered
  1.4565 + * Returns: abort flag
  1.4566 + */
  1.4567 +
  1.4568 +long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  1.4569 +{
  1.4570 +  if (serious) {		/* try your damnest if clobberage likely */
  1.4571 +    mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
  1.4572 +    PFLUSH ();			/* dump output buffer */
  1.4573 +    syslog (LOG_ALERT,
  1.4574 +	    "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1.4575 +	    user ? (char *) user : "???",tcp_clienthost (),
  1.4576 +	    (stream && stream->mailbox) ? stream->mailbox : "???",
  1.4577 +	    strerror (errcode));
  1.4578 +    settimeout (0);		/* make damn sure timeout disabled */
  1.4579 +    sleep (60);			/* give it some time to clear up */
  1.4580 +    return NIL;
  1.4581 +  }
  1.4582 +  if (!quell_events) {		/* otherwise die before more damage is done */
  1.4583 +    PSOUT ("* NO Disk error: ");
  1.4584 +    PSOUT (strerror (errcode));
  1.4585 +    CRLF;
  1.4586 +  }
  1.4587 +  return T;
  1.4588 +}
  1.4589 +
  1.4590 +
  1.4591 +/* Log a fatal error event
  1.4592 + * Accepts: string to log
  1.4593 + */
  1.4594 +
  1.4595 +void mm_fatal (char *string)
  1.4596 +{
  1.4597 +  SIZEDTEXT msg;
  1.4598 +  char *s;
  1.4599 +  msg.size = 
  1.4600 +    (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
  1.4601 +      (s - string) : strlen (string);
  1.4602 +  if (!quell_events) {
  1.4603 +    PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
  1.4604 +    PSOUTR (&msg);
  1.4605 +    CRLF;
  1.4606 +    PFLUSH ();
  1.4607 +  }
  1.4608 +  syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1.4609 +	  user ? (char *) user : "???",tcp_clienthost (),
  1.4610 +	  (stream && stream->mailbox) ? stream->mailbox : "???",string);
  1.4611 +}

UW-IMAP'd extensions by yuuji