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 +}