1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
4 */
5
6 /*
7 * Program: IMAP4rev1 server
8 *
9 * Author: Mark Crispin
10 *
11 * Date: 5 November 1990
12 * Last Edited: 30 July 2011
13 *
14 * Previous versions of this file were
15 *
16 * Copyright 1988-2008 University of Washington
17 *
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 *
24 */
25
26 /* Parameter files */
27
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <errno.h>
31 extern int errno; /* just in case */
32 #include <signal.h>
33 #include <setjmp.h>
34 #include <time.h>
35 #include <limits.h>
36 #include "c-client.h"
37 #include "newsrc.h"
38 #include <sys/stat.h>
39
40
41 #define CRLF PSOUT ("\015\012") /* primary output terpri */
42
43
44 /* Timeouts and timers */
45
46 #define MINUTES *60
47
48 #define LOGINTIMEOUT 3 MINUTES /* not logged in autologout timer */
49 #define TIMEOUT 30 MINUTES /* RFC 3501 minimum autologout timer */
50 #define INPUTTIMEOUT 5 MINUTES /* timer for additional command input */
51 #define ALERTTIMER 1 MINUTES /* alert check timer */
52 #define SHUTDOWNTIMER 1 MINUTES /* shutdown dally timer */
53 #define IDLETIMER 1 MINUTES /* IDLE command poll timer */
54 #define CHECKTIMER 5 MINUTES /* IDLE command last checkpoint timer */
55 #define SIGNALTIMER 10 MINUTES /* allocated time to die at signal */
56
57
58 #define MAXNLIBADCOMMAND 3 /* limit on number of NLI bad commands */
59 #define MAXTAG 50 /* maximum tag length */
60 #define LITSTKLEN 20 /* length of literal stack */
61 #define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size
62 * must be smaller than 4294967295
63 */
64 #define MAXAPPENDTXT 0x40000000 /* maximum APPEND literal size
65 * must be smaller than 4294967295
66 */
67 #define CMDLEN 65536 /* size of command buffer */
68
69
70 /* Server states */
71
72 typedef enum {
73 LOGIN = 0,
74 SELECT,
75 OPEN,
76 LOGOUT
77 } IMAPSTATE;
78
79 /* Body text fetching */
80
81 typedef struct text_args {
82 char *section; /* body section */
83 STRINGLIST *lines; /* header lines */
84 unsigned long first; /* first octet to fetch */
85 unsigned long last; /* number of octets to fetch */
86 long flags; /* fetch flags */
87 long binary; /* binary flags */
88 } TEXTARGS;
89
90 #define FTB_BINARY 0x1 /* fetch as binary */
91 #define FTB_SIZE 0x2 /* fetch size only */
92
93
94 /* Append data */
95
96 typedef struct append_data {
97 unsigned char *arg; /* append argument pointer */
98 char *flags; /* message flags */
99 char *date; /* message date */
100 char *msg; /* message text */
101 STRING *message; /* message stringstruct */
102 } APPENDDATA;
103
104
105 /* Message pointer */
106
107 typedef struct msg_data {
108 MAILSTREAM *stream; /* stream */
109 unsigned long msgno; /* message number */
110 char *flags; /* current flags */
111 char *date; /* current date */
112 STRING *message; /* stringstruct of message */
113 } MSGDATA;
114
115 /* Function prototypes */
116
117 int main (int argc,char *argv[]);
118 void ping_mailbox (unsigned long uid);
119 time_t palert (char *file,time_t oldtime);
120 void msg_string_init (STRING *s,void *data,unsigned long size);
121 char msg_string_next (STRING *s);
122 void msg_string_setpos (STRING *s,unsigned long i);
123 void new_flags (MAILSTREAM *stream);
124 void settimeout (unsigned int i);
125 void clkint (void);
126 void dieint (void);
127 void kodint (void);
128 void hupint (void);
129 void trmint (void);
130 void staint (void);
131 char *sout (char *s,char *t);
132 char *nout (char *s,unsigned long n,unsigned long base);
133 void slurp (char *s,int n,unsigned long timeout);
134 void inliteral (char *s,unsigned long n);
135 unsigned char *flush (void);
136 void ioerror (FILE *f,char *reason);
137 unsigned char *parse_astring (unsigned char **arg,unsigned long *i,
138 unsigned char *del);
139 unsigned char *parse_tag (unsigned char *cmd,unsigned char **ret);
140 unsigned char *snarf (unsigned char **arg);
141 unsigned char *snarf_base64 (unsigned char **arg);
142 unsigned char *snarf_list (unsigned char **arg);
143 STRINGLIST *parse_stringlist (unsigned char **s,int *list);
144 unsigned long uidmax (MAILSTREAM *stream);
145 long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
146 unsigned long maxuid,unsigned long depth);
147 long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long msgmsg,
148 unsigned long maxuid,unsigned long depth);
149 long crit_date (unsigned short *date,unsigned char **arg);
150 long crit_date_work (unsigned short *date,unsigned char **arg);
151 long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima);
152 long crit_number (unsigned long *number,unsigned char **arg);
153 long crit_string (STRINGLIST **string,unsigned char **arg);
154
155 void fetch (char *t,unsigned long uid);
156 typedef void (*fetchfn_t) (unsigned long i,void *args);
157 void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
158 void fetch_bodystructure (unsigned long i,void *args);
159 void fetch_body (unsigned long i,void *args);
160 void fetch_body_part_mime (unsigned long i,void *args);
161 void fetch_body_part_contents (unsigned long i,void *args);
162 void fetch_body_part_binary (unsigned long i,void *args);
163 void fetch_body_part_header (unsigned long i,void *args);
164 void fetch_body_part_text (unsigned long i,void *args);
165 void remember (unsigned long uid,char *id,SIZEDTEXT *st);
166 void fetch_envelope (unsigned long i,void *args);
167 void fetch_encoding (unsigned long i,void *args);
168 void changed_flags (unsigned long i,int f);
169 void fetch_flags (unsigned long i,void *args);
170 void put_flag (int *c,char *s);
171 void fetch_internaldate (unsigned long i,void *args);
172 void fetch_uid (unsigned long i,void *args);
173 void fetch_rfc822 (unsigned long i,void *args);
174 void fetch_rfc822_header (unsigned long i,void *args);
175 void fetch_rfc822_size (unsigned long i,void *args);
176 void fetch_rfc822_text (unsigned long i,void *args);
177 void penv (ENVELOPE *env);
178 void pbodystructure (BODY *body);
179 void pbody (BODY *body);
180 void pparam (PARAMETER *param);
181 void paddr (ADDRESS *a);
182 void pset (SEARCHSET **set);
183 void pnum (unsigned long i);
184 void pstring (char *s);
185 void pnstring (char *s);
186 void pastring (char *s);
187 void psizedquoted (SIZEDTEXT *s);
188 void psizedliteral (SIZEDTEXT *s,STRING *st);
189 void psizedstring (SIZEDTEXT *s,STRING *st);
190 void psizedastring (SIZEDTEXT *s);
191 void pastringlist (STRINGLIST *s);
192 void pnstringorlist (STRINGLIST *s);
193 void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
194 TEXTARGS *ta);
195 void ptext (SIZEDTEXT *s,STRING *st);
196 void pthread (THREADNODE *thr);
197 void pcapability (long flag);
198 long nameok (char *ref,char *name);
199 char *bboardname (char *cmd,char *name);
200 long isnewsproxy (char *name);
201 long newsproxypattern (char *ref,char *pat,char *pattern,long flag);
202 char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
203 long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
204 long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
205 STRING **message);
206 long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
207 STRING **message);
208 void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
209 SEARCHSET *sourceset,SEARCHSET *destset);
210 void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set);
211 char *referral (MAILSTREAM *stream,char *url,long code);
212 void mm_list_work (char *what,int delimiter,char *name,long attributes);
213 char *lasterror (void);
214
215 /* Global storage */
216
217 char *version = "417"; /* edit number of this server */
218 char *logout = "Logout"; /* syslogreason for logout */
219 char *goodbye = NIL; /* bye reason */
220 time_t alerttime = 0; /* time of last alert */
221 time_t sysalerttime = 0; /* time of last system alert */
222 time_t useralerttime = 0; /* time of last user alert */
223 time_t lastcheck = 0; /* time of last checkpoint */
224 time_t shutdowntime = 0; /* time of last shutdown */
225 int blackberry = 0; /* 1 if BlackBerry, -1 if not, 0 if unknown */
226 int nlibcm = MAXNLIBADCOMMAND; /* limit number of bad commands */
227 IMAPSTATE state = LOGIN; /* server state */
228 int cancelled = NIL; /* authenticate cancelled */
229 int trycreate = 0; /* saw a trycreate */
230 int finding = NIL; /* doing old FIND command */
231 int anonymous = 0; /* non-zero if anonymous */
232 int critical = NIL; /* non-zero if in critical code */
233 int quell_events = NIL; /* non-zero if in FETCH response */
234 int existsquelled = NIL; /* non-zero if an EXISTS was quelled */
235 int proxylist = NIL; /* doing a proxy LIST */
236 MAILSTREAM *stream = NIL; /* mailbox stream */
237 DRIVER *curdriver = NIL; /* note current driver */
238 MAILSTREAM *tstream = NIL; /* temporary mailbox stream */
239 unsigned int nflags = 0; /* current number of keywords */
240 unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
241 unsigned long recent = 0xffffffff;
242 char *nntpproxy = NIL; /* NNTP proxy name */
243 unsigned char *user = NIL; /* user name */
244 unsigned char *pass = NIL; /* password */
245 unsigned char *initial = NIL; /* initial response */
246 unsigned char cmdbuf[CMDLEN]; /* command buffer */
247 char *status = "starting up"; /* server status */
248 char *tag; /* tag portion of command */
249 unsigned char *cmd; /* command portion of command */
250 unsigned char *arg; /* pointer to current argument of command */
251 char *lstwrn = NIL; /* last warning message from c-client */
252 char *lsterr = NIL; /* last error message from c-client */
253 char *lstref = NIL; /* last referral from c-client */
254 char *response = NIL; /* command response */
255 char *sstate = NIL; /* strtok state */
256 struct {
257 unsigned long size; /* size of current LITERAL+ */
258 unsigned int ok : 1; /* LITERAL+ in effect */
259 } litplus;
260 int litsp = 0; /* literal stack pointer */
261 char *litstk[LITSTKLEN]; /* stack to hold literals */
262 unsigned long uidvalidity = 0; /* last reported UID validity */
263 unsigned long lastuid = 0; /* last fetched uid */
264 char *lastid = NIL; /* last fetched body id for this message */
265 char *lastsel = NIL; /* last selected mailbox name */
266 SIZEDTEXT lastst = {NIL,0}; /* last sizedtext */
267 unsigned long cauidvalidity = 0;/* UIDVALIDITY for COPYUID/APPENDUID */
268 SEARCHSET *csset = NIL; /* COPYUID source set */
269 SEARCHSET *caset = NIL; /* COPYUID/APPENDUID destination set */
270 jmp_buf jmpenv; /* stack context for setjmp */
271
272
273 /* Response texts which appear in multiple places */
274
275 char *win = "%.80s OK ";
276 char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012";
277 char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012";
278 char *lose = "%.80s NO ";
279 char *logwin = "%.80s OK [";
280 char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.800s\015\012";
281 char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.800s\015\012";
282 char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012";
283 char *badcml = "%.80s BAD Command unrecognized\015\012";
284 char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012";
285 char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012";
286 char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012";
287 char *badatt = "%.80s BAD Bogus attribute list in %.80s\015\012";
288 char *badbin = "%.80s BAD Syntax error in binary specifier\015\012";
289
290 /* Message string driver for message stringstructs */
291
292 STRINGDRIVER msg_string = {
293 msg_string_init, /* initialize string structure */
294 msg_string_next, /* get next byte in string structure */
295 msg_string_setpos /* set position in string structure */
296 };
297
298 /* Main program */
299
main(int argc,char * argv[])300 int main (int argc,char *argv[])
301 {
302 unsigned long i,uid;
303 long f;
304 unsigned char *s,*t,*u,*v,tmp[MAILTMPLEN];
305 struct stat sbuf;
306 logouthook_t lgoh;
307 int ret = 0;
308 time_t autologouttime = 0;
309 char *pgmname;
310 /* if case we get borked immediately */
311 if (setjmp (jmpenv)) _exit (1);
312 pgmname = (argc && argv[0]) ?
313 (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
314 (char *) s+1 : argv[0]) : "imapd";
315 /* set service name before linkage */
316 mail_parameters (NIL,SET_SERVICENAME,(void *) "imap");
317 #include "linkage.c"
318 rfc822_date (tmp); /* get date/time at startup */
319 /* initialize server */
320 server_init (pgmname,"imap","imaps",clkint,kodint,hupint,trmint,staint);
321 /* forbid automatic untagged expunge */
322 mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
323 /* arm proxy copy callback */
324 mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
325 /* arm referral callback */
326 mail_parameters (NIL,SET_IMAPREFERRAL,(void *) referral);
327 /* arm COPYUID callback */
328 mail_parameters (NIL,SET_COPYUID,(void *) copyuid);
329 /* arm APPENDUID callback */
330 mail_parameters (NIL,SET_APPENDUID,(void *) appenduid);
331
332 if (stat (SHUTDOWNFILE,&sbuf)) {
333 char proxy[MAILTMPLEN];
334 FILE *nntp = fopen (NNTPFILE,"r");
335 if (nntp) { /* desire NNTP proxy? */
336 if (fgets (proxy,MAILTMPLEN,nntp)) {
337 /* remove newline and set NNTP proxy */
338 if (s = strchr (proxy,'\n')) *s = '\0';
339 nntpproxy = cpystr (proxy);
340 /* disable the news driver */
341 mail_parameters (NIL,DISABLE_DRIVER,"news");
342 }
343 fclose (nntp); /* done reading proxy name */
344 }
345 s = myusername_full (&i); /* get user name and flags */
346 switch (i) {
347 case MU_NOTLOGGEDIN:
348 PSOUT ("* OK ["); /* not logged in, ordinary startup */
349 pcapability (-1);
350 break;
351 case MU_ANONYMOUS:
352 anonymous = T; /* anonymous user, fall into default */
353 s = "ANONYMOUS";
354 case MU_LOGGEDIN:
355 PSOUT ("* PREAUTH ["); /* already logged in, pre-authorized */
356 pcapability (1);
357 user = cpystr (s); /* copy user name */
358 pass = cpystr ("*"); /* set fake password */
359 state = SELECT; /* enter select state */
360 break;
361 default:
362 fatal ("Unknown state from myusername_full()");
363 }
364 PSOUT ("] ");
365 if (user) { /* preauthenticated as someone? */
366 PSOUT ("Pre-authenticated user ");
367 PSOUT (user);
368 PBOUT (' ');
369 }
370 }
371 else { /* login disabled */
372 PSOUT ("* BYE Service not available ");
373 state = LOGOUT;
374 }
375 PSOUT (tcp_serverhost ());
376 PSOUT (" Panda IMAP ");
377 PSOUT (CCLIENTVERSION);
378 PBOUT ('.');
379 PSOUT (version);
380 PSOUT (" at ");
381 PSOUT (tmp);
382 CRLF;
383 PFLUSH (); /* dump output buffer */
384 switch (state) { /* do this after the banner */
385 case LOGIN:
386 autologouttime = time (0) + LOGINTIMEOUT;
387 break;
388 case SELECT:
389 syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
390 user,tcp_clienthost ());
391 break;
392 }
393
394 if (setjmp (jmpenv)) { /* die if a signal handler say so */
395 /* in case we get borked now */
396 if (setjmp (jmpenv)) _exit (1);
397 /* need to close stream gracefully? */
398 if (stream && !stream->lock && (stream->dtb->flags & DR_XPOINT))
399 stream = mail_close (stream);
400 ret = 1; /* set exit status */
401 }
402 else while (state != LOGOUT) {/* command processing loop */
403 slurp (cmdbuf,CMDLEN,TIMEOUT);
404 /* no more last error or literal */
405 if (lstwrn) fs_give ((void **) &lstwrn);
406 if (lsterr) fs_give ((void **) &lsterr);
407 if (lstref) fs_give ((void **) &lstref);
408 while (litsp) fs_give ((void **) &litstk[--litsp]);
409 /* find end of line */
410 if (t = strchr (cmdbuf,'\012')) {
411 /* tie off command termination */
412 if ((t > cmdbuf) && (t[-1] == '\015')) --t;
413 *t = '\0'; /* tie off LF or CRLF */
414 }
415 if (!t) { /* probably line too long if not terminated */
416 if (t = strchr (cmdbuf,' ')) {
417 if ((t - cmdbuf) > MAXTAG) t = NIL;
418 else *t = '\0';
419 }
420 flush (); /* flush excess, set response */
421 if (state == LOGIN) { /* error if NLI */
422 syslog (LOG_INFO,"Line too long before authentication host=%.80s",
423 tcp_clienthost ());
424 state = LOGOUT;
425 }
426 sprintf (tmp,response,t ? (char *) cmdbuf : "*");
427 PSOUT (tmp);
428 }
429 else if (!(tag = parse_tag (cmdbuf,&s))) {
430 if (state == LOGIN) { /* error if NLI */
431 syslog (LOG_INFO,"Null command before authentication host=%.80s",
432 tcp_clienthost ());
433 state = LOGOUT;
434 }
435 PSOUT ("* BAD Missing command tag\015\012");
436 }
437 else if (strlen (tag) > MAXTAG)
438 PSOUT ("* BAD Excessively long tag\015\012");
439 else if (!*s) { /* make sure have command */
440 if (state == LOGIN) { /* error if NLI */
441 syslog (LOG_INFO,"Missing command before authentication host=%.80s",
442 tcp_clienthost ());
443 state = LOGOUT;
444 }
445 PSOUT (tag);
446 PSOUT (" BAD Missing command\015\012");
447 }
448 else { /* parse command */
449 response = win; /* set default response */
450 finding = NIL; /* no longer FINDing */
451 /* UID command? */
452 if (((s[0] == 'U') || (s[0] == 'u')) &&
453 ((s[1] == 'I') || (s[1] == 'i')) &&
454 ((s[2] == 'D') || (s[2] == 'd')) && (s[3] == ' ')) {
455 uid = T; /* a UID command */
456 arg = strchr (s+4,' '); /* find argument */
457 }
458 else {
459 uid = NIL; /* not a UID command */
460 arg = strchr (s,' '); /* find argument */
461 }
462 if (arg) *arg++ = '\0'; /* tie off command argument */
463 ucase (s); /* canonicalize command case */
464 /* flush previous saved command */
465 if (cmd) fs_give ((void **) &cmd);
466 cmd = cpystr (s); /* save current command */
467 /* snarf argument, see if possible litplus */
468 if (arg && ((i = strlen (arg)) > 3) &&
469 (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) {
470 /* back over possible count */
471 for (i -= 4; i && isdigit (arg[i]); i--);
472 if (arg[i] == '{') { /* found a literal? */
473 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
474 litplus.size = strtoul (arg + i + 1,NIL,10);
475 }
476 }
477
478 if (!blackberry) { /* do we know if it's a BlackBerry yet? */
479 char *bb[] = {".blackberry.com", ".blackberry.net", NIL};
480 size_t bbs;
481 unsigned char *cl = cpystr (tcp_clienthost ());
482 size_t cls;
483 /* get just host name, calculate length */
484 if (t = strchr (cl,' ')) {
485 *t = '\0';
486 cls = t - cl;
487 }
488 else cls = strlen (cl);
489 for (i = 0; bb[i]; ++i) {
490 if (((bbs = strlen (bb[i])) < cls) &&
491 !compare_cstring(bb[i], cl + cls - bbs))
492 blackberry = 1;
493 }
494 fs_give ((void **) & cl);
495 /* not a blackberry if no match on list */
496 if(!blackberry) blackberry = -1;
497 }
498 /* these commands always valid */
499 if (!strcmp (cmd,"NOOP")) {
500 if (arg) response = badarg;
501 else if (stream) /* allow untagged EXPUNGE */
502 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
503 }
504 else if (!strcmp (cmd,"LOGOUT")) {
505 if (arg) response = badarg;
506 else { /* time to say farewell */
507 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
508 if (lastsel) fs_give ((void **) &lastsel);
509 if (state == OPEN) stream = mail_close (stream);
510 state = LOGOUT;
511 PSOUT ("* BYE ");
512 PSOUT (mylocalhost ());
513 PSOUT (" IMAP4rev1 server terminating connection\015\012");
514 }
515 }
516 else if (!strcmp (cmd,"CAPABILITY")) {
517 if (arg) response = badarg;
518 else {
519 PSOUT ("* ");
520 pcapability (0); /* print capabilities */
521 CRLF;
522 }
523 if (stream) /* allow untagged EXPUNGE */
524 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
525 }
526 #ifdef NETSCAPE_BRAIN_DAMAGE
527 else if (!strcmp (cmd,"NETSCAPE")) {
528 PSOUT ("* OK [NETSCAPE]\015\012* VERSION 1.0 UNIX\015\012* ACCOUNT-URL \"");
529 PSOUT (NETSCAPE_BRAIN_DAMAGE);
530 PBOUT ('"');
531 CRLF;
532 }
533 #endif
534
535 else switch (state) { /* dispatch depending upon state */
536 case LOGIN: /* waiting to get logged in */
537 /* new style authentication */
538 if (!strcmp (cmd,"AUTHENTICATE")) {
539 if (user) fs_give ((void **) &user);
540 if (pass) fs_give ((void **) &pass);
541 initial = NIL; /* no initial argument */
542 cancelled = NIL; /* not cancelled */
543 /* mandatory first argument */
544 if (!(s = snarf (&arg))) response = misarg;
545 else if (arg && !(initial = snarf_base64 (&arg)))
546 response = misarg; /* optional second argument */
547 else if (arg) response = badarg;
548 else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
549 if (!(s = imap_responder ("",0,NIL)))
550 response ="%.80s BAD AUTHENTICATE ANONYMOUS cancelled\015\012";
551 else if (anonymous_login (argc,argv)) {
552 anonymous = T; /* note we are anonymous */
553 user = cpystr ("ANONYMOUS");
554 pass = cpystr ("*");
555 state = SELECT; /* make select */
556 alerttime = 0; /* force alert */
557 response = logwin;/* return logged-in capabilities */
558 syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
559 tcp_clienthost ());
560 fs_give ((void **) &s);
561 }
562 else response ="%.80s NO AUTHENTICATE ANONYMOUS failed\015\012";
563 }
564 else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
565 pass = cpystr ("*");
566 state = SELECT; /* make select */
567 alerttime = 0; /* force alert */
568 response = logwin; /* return logged-in capabilities */
569 syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s mech=%.80s",
570 user,tcp_clienthost (),s);
571 }
572
573 else {
574 AUTHENTICATOR *auth = mail_lookup_auth (1);
575 char *msg = (char *) fs_get (strlen (cmd) + strlen (s) + 2);
576 sprintf (msg,"%s %s",cmd,s);
577 fs_give ((void **) &cmd);
578 cmd = msg;
579 for (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
580 auth && compare_cstring (s,auth->name); auth = auth->next);
581 /* Failed authentication when hidden looks like invalid command.
582 * This is intentional but confused me when I was debugging.
583 */
584 if (auth && auth->server && !(auth->flags & AU_DISABLE) &&
585 !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
586 response = lose;
587 if (cancelled) {
588 if (lsterr) fs_give ((void **) &lsterr);
589 lsterr = cpystr ("cancelled by user");
590 }
591 if (!lsterr) /* catch-all */
592 lsterr = cpystr ("Invalid authentication credentials");
593 syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
594 tcp_clienthost ());
595 }
596 else {
597 response = badcml;
598 syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s,
599 tcp_clienthost ());
600 }
601 }
602 }
603
604 /* plaintext login with password */
605 else if (!strcmp (cmd,"LOGIN")) {
606 if (user) fs_give ((void **) &user);
607 if (pass) fs_give ((void **) &pass);
608 /* two arguments */
609 if (!((user = cpystr (snarf (&arg))) &&
610 (pass = cpystr (snarf (&arg))))) response = misarg;
611 else if (arg) response = badarg;
612 /* see if we allow anonymous */
613 else if (!compare_cstring (user,"ANONYMOUS") &&
614 !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
615 anonymous = T; /* note we are anonymous */
616 ucase (user); /* make all uppercase for consistency */
617 state = SELECT; /* make select */
618 alerttime = 0; /* force alert */
619 response = logwin; /* return logged-in capabilities */
620 syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
621 tcp_clienthost ());
622 }
623 else { /* delimit user from possible admin */
624 if (s = strchr (user,'*')) *s++ ='\0';
625 /* see if username and password are OK */
626 if (server_login (user,pass,s,argc,argv)) {
627 state = SELECT; /* make select */
628 alerttime = 0; /* force alert */
629 response = logwin;/* return logged-in capabilities */
630 syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
631 tcp_clienthost ());
632 }
633 else {
634 response = lose;
635 if (!lsterr) lsterr = cpystr ("Invalid login credentials");
636 }
637 }
638 }
639 /* start TLS security */
640 else if (!strcmp (cmd,"STARTTLS")) {
641 if (arg) response = badarg;
642 else if (lsterr = ssl_start_tls (pgmname)) response = lose;
643 }
644 else {
645 response = badcml;
646 /* limit the number of bad commands */
647 if (--nlibcm <= 0) state = LOGOUT;
648 }
649 break;
650
651 case OPEN: /* valid only when mailbox open */
652 /* fetch mailbox attributes */
653 if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
654 if (!(arg && (s = strtok_r (arg," ",&sstate)) &&
655 (t = strtok_r (NIL,"\015\012",&sstate))))
656 response = misarg;
657 else if (uid ? mail_uid_sequence (stream,s) :
658 mail_sequence (stream,s)) fetch (t,uid);
659 else response = badseq;
660 }
661 /* store mailbox attributes */
662 else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
663 /* must have three arguments */
664 if (!(arg && (s = strtok_r (arg," ",&sstate)) &&
665 (v = strtok_r (NIL," ",&sstate)) &&
666 (t = strtok_r (NIL,"\015\012",&sstate)))) response = misarg;
667 else if (!(uid ? mail_uid_sequence (stream,s) :
668 mail_sequence (stream,s))) response = badseq;
669 else {
670 f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
671 if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
672 strcpy (tmp,"\\Answered \\Flagged \\Deleted \\Draft \\Seen");
673 for (i = 0, u = tmp;
674 (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
675 if (strlen (v) <
676 ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
677 *u++ = ' '; /* write next flag */
678 strcpy (u,v);
679 }
680 mail_flag (stream,s,tmp,f & ~ST_SET);
681 }
682 else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
683 f &= ~ST_SET; /* clear flags */
684 else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
685 response = badatt;
686 break;
687 }
688 /* find last keyword */
689 for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
690 mail_flag (stream,s,t,f);
691 /* any new keywords appeared? */
692 if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
693 /* return flags if silence not wanted */
694 if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))
695 for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
696 mail_elt (stream,i)->spare2 = (f & ST_SILENT) ? NIL : T;
697 }
698 }
699
700 /* check for new mail */
701 else if (!strcmp (cmd,"CHECK")) {
702 /* no arguments */
703 if (arg) response = badarg;
704 else if (!anonymous) {
705 mail_check (stream);
706 /* remember last check time */
707 lastcheck = time (0);
708 }
709 }
710 /* expunge deleted messages */
711 else if (!(anonymous || (strcmp (cmd,"EXPUNGE") &&
712 strcmp (cmd,"UID EXPUNGE")))) {
713 if (uid && !arg) response = misarg;
714 else if (!uid && arg) response = badarg;
715 else { /* expunge deleted or specified UIDs */
716 mail_expunge_full (stream,arg,arg ? EX_UID : NIL);
717 /* remember last checkpoint */
718 lastcheck = time (0);
719 }
720 }
721 /* close mailbox */
722 else if (!strcmp (cmd,"CLOSE") || !strcmp (cmd,"UNSELECT")) {
723 /* no arguments */
724 if (arg) response = badarg;
725 else {
726 /* no last uid */
727 uidvalidity = lastuid = 0;
728 if (lastsel) fs_give ((void **) &lastsel);
729 if (lastid) fs_give ((void **) &lastid);
730 if (lastst.data) fs_give ((void **) &lastst.data);
731 stream = mail_close_full (stream,((*cmd == 'C') && !anonymous) ?
732 CL_EXPUNGE : NIL);
733 state = SELECT; /* no longer opened */
734 lastcheck = 0; /* no last checkpoint */
735 }
736 }
737 else if (!anonymous && /* copy message(s) */
738 (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
739 trycreate = NIL; /* no trycreate status */
740 if (!(arg && (s = strtok_r (arg," ",&sstate)) &&
741 (arg = strtok_r (NIL,"\015\012",&sstate))
742 && (t = snarf (&arg)))) response = misarg;
743 else if (arg) response = badarg;
744 else if (!nmsgs) {
745 response = lose;
746 if (!lsterr) lsterr = cpystr ("Mailbox is empty");
747 }
748 else if (!(uid ? mail_uid_sequence (stream,s) :
749 mail_sequence (stream,s))) response = badseq;
750 /* try copy */
751 else if (!mail_copy_full (stream,s,t,uid ? CP_UID : NIL)) {
752 response = trycreate ? losetry : lose;
753 if (!lsterr) lsterr = cpystr ("No such destination mailbox");
754 }
755 }
756
757 /* sort mailbox */
758 else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
759 /* must have four arguments */
760 if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
761 (t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
762 else { /* read criteria */
763 SEARCHPGM *spg = NIL;
764 char *cs = NIL;
765 SORTPGM *pgm = NIL,*pg = NIL;
766 unsigned long *slst,*sl;
767 *t = NIL; /* tie off criteria list */
768 if (!(s = strtok_r (ucase (s)," ",&sstate))) response = badatt;
769 else {
770 do { /* parse sort attributes */
771 if (pg) pg = pg->next = mail_newsortpgm ();
772 else pgm = pg = mail_newsortpgm ();
773 if (!strcmp (s,"REVERSE")) {
774 pg->reverse = T;
775 if (!(s = strtok_r (NIL," ",&sstate))) {
776 s = ""; /* end of attributes */
777 break;
778 }
779 }
780 if (!strcmp (s,"DATE")) pg->function = SORTDATE;
781 else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
782 else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
783 else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
784 else if (!strcmp (s,"TO")) pg->function = SORTTO;
785 else if (!strcmp (s,"CC")) pg->function = SORTCC;
786 else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
787 else break;
788 } while (s = strtok_r (NIL," ",&sstate));
789 /* bad SORT attribute */
790 if (s) response = badatt;
791 /* get charset and search criteria */
792 else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg &&
793 *arg)) response = misarg;
794 /* parse search criteria */
795 else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
796 uidmax (stream),0)) response = badatt;
797 else if (arg && *arg) response = badarg;
798 else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID:NIL)) {
799 PSOUT ("* SORT");
800 for (sl = slst; *sl; sl++) {
801 PBOUT (' ');
802 pnum (*sl);
803 }
804 CRLF;
805 fs_give ((void **) &slst);
806 }
807 }
808 if (pgm) mail_free_sortpgm (&pgm);
809 if (spg) mail_free_searchpgm (&spg);
810 if (cs) fs_give ((void **) &cs);
811 }
812 }
813
814 /* thread mailbox */
815 else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
816 THREADNODE *thr;
817 SEARCHPGM *spg = NIL;
818 char *cs = NIL;
819 /* must have four arguments */
820 if (!(arg && (s = strtok_r (arg," ",&sstate)) &&
821 (cs = strtok_r (NIL," ",&sstate)) && (cs = cpystr (cs)) &&
822 (arg = strtok_r (NIL,"\015\012",&sstate))))
823 response = misarg;
824 else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
825 uidmax (stream),0)) response = badatt;
826 else if (arg && *arg) response = badarg;
827 else {
828 if (thr = mail_thread (stream,s,cs,spg,uid ? SE_UID : NIL)) {
829 PSOUT ("* THREAD ");
830 pthread (thr);
831 mail_free_threadnode (&thr);
832 }
833 else PSOUT ("* THREAD");
834 CRLF;
835 }
836 if (spg) mail_free_searchpgm (&spg);
837 if (cs) fs_give ((void **) &cs);
838 }
839
840 /* search mailbox */
841 else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
842 int retval = NIL;
843 char *charset = NIL;
844 SEARCHPGM *pgm;
845 response = misarg; /* assume failure */
846 if (!arg) break; /* one or more arguments required */
847 if (((arg[0] == 'R') || (arg[0] == 'r')) &&
848 ((arg[1] == 'E') || (arg[1] == 'e')) &&
849 ((arg[2] == 'T') || (arg[2] == 't')) &&
850 ((arg[3] == 'U') || (arg[3] == 'u')) &&
851 ((arg[4] == 'R') || (arg[4] == 'r')) &&
852 ((arg[5] == 'N') || (arg[5] == 'n')) &&
853 (arg[6] == ' ') && (arg[7] == '(')) {
854 retval = 0x4000; /* return is specified */
855 for (arg += 8; *arg && (*arg != ')'); ) {
856 if (((arg[0] == 'M') || (arg[0] == 'm')) &&
857 ((arg[1] == 'I') || (arg[1] == 'i')) &&
858 ((arg[2] == 'N') || (arg[2] == 'n')) &&
859 ((arg[3] == ' ') || (arg[3] == ')'))) {
860 retval |= 0x1;
861 arg += 3;
862 }
863 else if (((arg[0] == 'M') || (arg[0] == 'm')) &&
864 ((arg[1] == 'A') || (arg[1] == 'a')) &&
865 ((arg[2] == 'X') || (arg[2] == 'x')) &&
866 ((arg[3] == ' ') || (arg[3] == ')'))) {
867 retval |= 0x2;
868 arg += 3;
869 }
870 else if (((arg[0] == 'A') || (arg[0] == 'a')) &&
871 ((arg[1] == 'L') || (arg[1] == 'l')) &&
872 ((arg[2] == 'L') || (arg[2] == 'l')) &&
873 ((arg[3] == ' ') || (arg[3] == ')'))) {
874 retval |= 0x4;
875 arg += 3;
876 }
877 else if (((arg[0] == 'C') || (arg[0] == 'c')) &&
878 ((arg[1] == 'O') || (arg[1] == 'o')) &&
879 ((arg[2] == 'U') || (arg[2] == 'u')) &&
880 ((arg[3] == 'N') || (arg[3] == 'n')) &&
881 ((arg[4] == 'T') || (arg[4] == 't')) &&
882 ((arg[5] == ' ') || (arg[5] == ')'))) {
883 retval |= 0x10;
884 arg += 5;
885 }
886 else break; /* unknown return value */
887 /* more return values to come */
888 if ((*arg == ' ') && (arg[1] != ')')) ++arg;
889 }
890 /* RETURN list must be properly terminated */
891 if ((*arg++ != ')') || (*arg++ != ' ')) break;
892 /* default return value is ALL */
893 if (!(retval &= 0x3fff)) retval = 0x4;
894 }
895
896 /* character set specified? */
897 if (((arg[0] == 'C') || (arg[0] == 'c')) &&
898 ((arg[1] == 'H') || (arg[1] == 'h')) &&
899 ((arg[2] == 'A') || (arg[2] == 'a')) &&
900 ((arg[3] == 'R') || (arg[3] == 'r')) &&
901 ((arg[4] == 'S') || (arg[4] == 's')) &&
902 ((arg[5] == 'E') || (arg[5] == 'e')) &&
903 ((arg[6] == 'T') || (arg[6] == 't')) &&
904 (arg[7] == ' ')) {
905 arg += 8; /* yes, skip over CHARSET token */
906 if (s = snarf (&arg)) charset = cpystr (s);
907 else break; /* missing character set */
908 }
909 /* must have arguments here */
910 if (!(arg && *arg)) break;
911 if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
912 uidmax (stream),0) && !*arg) {
913 response = win; /* looks good, try the search */
914 mail_search_full (stream,charset,pgm,SE_FREE);
915 /* output search results if success */
916 if (response == win) {
917 if (retval) { /* ESEARCH desired */
918 PSOUT ("* ESEARCH (TAG ");
919 pstring (tag);
920 PBOUT (')');
921 if (uid) PSOUT (" UID");
922 /* wants MIN */
923 if (retval & 0x1) {
924 for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
925 ++i);
926 if (i <= nmsgs) {
927 PSOUT (" MIN ");
928 pnum (uid ? mail_uid (stream,i) : i);
929 }
930 }
931 /* wants MAX */
932 if (retval & 0x2) {
933 for (i = nmsgs; i && !mail_elt (stream,i)->searched; --i);
934 if (i) {
935 PSOUT (" MAX ");
936 pnum (uid ? mail_uid (stream,i) : i);
937 }
938 }
939
940 /* wants ALL */
941 if (retval & 0x4) {
942 unsigned long j,juid,iuid;
943 /* find first match */
944 for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
945 ++i);
946 if (i <= nmsgs) {
947 juid= (uid ? mail_uid (stream,i) : i);
948 PSOUT (" ALL ");
949 pnum (juid);
950 j = i; /* last message index processed */
951 }
952 if (uid) { /* doing UIDs, no MRC optimization */
953 while (++i <= nmsgs) {
954 if (mail_elt (stream,i)->searched) { /* this message matches */
955 iuid= mail_uid (stream,i);
956 if (juid == (iuid - (i-j))) { /* in sequence of UIDs */
957 if ( i < nmsgs) continue; /* may be more, check next */
958 PBOUT (':'); pnum(iuid); /* done with all, tie it off */
959 j = i; juid= iuid;
960 } else { /* break in sequence of UIDs */
961 if ((i-1) > j) { /* prev sequence, tie it off */
962 j= i-1; juid= mail_uid (stream,j);
963 PBOUT (':'); pnum(juid);
964 }
965 PBOUT (','); pnum(iuid);
966 j = i; juid= iuid; /* record last done message/UID */
967 }
968 } else { /* this message doesn't match, have a pending sequence? */
969 /* if pending sequence, tie it off & reset flags */
970 if ((i-1) > j) { /* prev sequence, tie it off */
971 j= i-1; juid= mail_uid (stream,j);
972 PBOUT (':'); pnum(juid);
973 }
974 j= nmsgs+1; juid= ULONG_MAX; /* flag that we're not in a sequence of matches */
975 }
976 }
977 } else { /* doing message-IDs, use MRC optimization */
978 while (++i <= nmsgs) {
979 if (mail_elt (stream,i)->searched) {
980 while ((++i <= nmsgs) && mail_elt (stream,i)->searched);
981 /* previous message is end of range */
982 if (j != --i) {
983 PBOUT (':');
984 pnum (i);
985 }
986 }
987 /* search for next match */
988 while ((++i <= nmsgs) && !mail_elt (stream,i)->searched);
989 if (i <= nmsgs) {
990 PBOUT (',');
991 pnum (i);
992 j = i; /* last message output */
993 }
994 }
995 }
996 }
997 /* wants COUNT */
998 if (retval & 0x10) {
999 unsigned long j;
1000 for (i = 1, j = 0; i <= nmsgs; ++i)
1001 if (mail_elt (stream,i)->searched) ++j;
1002 PSOUT (" COUNT ");
1003 pnum (j);
1004 }
1005 }
1006 else { /* standard search */
1007 PSOUT ("* SEARCH");
1008 for (i = 1; i <= nmsgs; ++i)
1009 if (mail_elt (stream,i)->searched) {
1010 PBOUT (' ');
1011 pnum (uid ? mail_uid (stream,i) : i);
1012 }
1013 }
1014 CRLF;
1015 }
1016 }
1017 else mail_free_searchpgm (&pgm);
1018 if (charset) fs_give ((void **) &charset);
1019 }
1020
1021 else /* fall into select case */
1022 case SELECT: /* valid whenever logged in */
1023 /* select new mailbox */
1024 if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
1025 strcmp (cmd,"BBOARD"))) {
1026 /* single argument */
1027 if (!(s = snarf (&arg))) response = misarg;
1028 else if (arg) response = badarg;
1029 else if (nameok (NIL,s = bboardname (cmd,s))) {
1030 DRIVER *factory = mail_valid (NIL,s,NIL);
1031 f = anonymous ? OP_ANONYMOUS | OP_READONLY :
1032 (((blackberry > 0) || (*cmd == 'S')) ? NIL : OP_READONLY);
1033 curdriver = NIL; /* no drivers known */
1034 /* no last uid */
1035 uidvalidity = lastuid = 0;
1036 if (lastid) fs_give ((void **) &lastid);
1037 if (lastst.data) fs_give ((void **) &lastst.data);
1038 nflags = 0; /* force update */
1039 nmsgs = recent = 0xffffffff;
1040 if (factory && !strcmp (factory->name,"phile") &&
1041 (stream = mail_open (stream,s,f | OP_SILENT)) &&
1042 (response == win)) {
1043 BODY *b;
1044 /* see if proxy open */
1045 if ((mail_elt (stream,1)->rfc822_size < 400) &&
1046 mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
1047 (t = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
1048 (i < MAILTMPLEN) && (t[0] == '{')) {
1049 /* copy and tie off */
1050 strncpy (tmp,t,i)[i] = '\0';
1051 /* nuke any trailing newline */
1052 if (t = strpbrk (tmp,"\r\n")) *t = '\0';
1053 /* try to open proxy */
1054 if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
1055 (response == win) && tstream->nmsgs) {
1056 s = tmp; /* got it, close the link */
1057 mail_close (stream);
1058 stream = tstream;
1059 tstream = NIL;
1060 }
1061 }
1062 /* now give the exists event */
1063 stream->silent = NIL;
1064 mm_exists (stream,stream->nmsgs);
1065 }
1066 else if (!factory && isnewsproxy (s)) {
1067 sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
1068 stream = mail_open (stream,tmp,f);
1069 }
1070 /* open stream normally then */
1071 else stream = mail_open (stream,s,f);
1072
1073 if (stream && (response == win)) {
1074 state = OPEN; /* note state open */
1075 if (lastsel) fs_give ((void **) &lastsel);
1076 /* canonicalize INBOX */
1077 if (!compare_cstring (s,"#MHINBOX"))
1078 lastsel = cpystr ("#MHINBOX");
1079 else lastsel = cpystr (compare_cstring (s,"INBOX") ?
1080 (char *) s : "INBOX");
1081 /* note readonly/readwrite */
1082 response = stream->rdonly ? rowin : rwwin;
1083 if (anonymous)
1084 syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
1085 stream->mailbox,tcp_clienthost ());
1086 lastcheck = 0; /* no last check */
1087 }
1088 else { /* failed, nuke old selection */
1089 if (stream) stream = mail_close (stream);
1090 state = SELECT; /* no mailbox open now */
1091 if (lastsel) fs_give ((void **) &lastsel);
1092 response = lose; /* open failed */
1093 }
1094 }
1095 }
1096
1097 /* APPEND message to mailbox */
1098 else if (!(anonymous || strcmp (cmd,"APPEND"))) {
1099 /* parse mailbox name */
1100 if ((s = snarf (&arg)) && arg) {
1101 STRING st; /* message stringstruct */
1102 APPENDDATA ad;
1103 ad.arg = arg; /* command arguments */
1104 /* no message yet */
1105 ad.flags = ad.date = ad.msg = NIL;
1106 ad.message = &st; /* pointer to stringstruct to use */
1107 trycreate = NIL; /* no trycreate status */
1108 if (!mail_append_multiple (NIL,s,append_msg,(void *) &ad)) {
1109 if (response == win) response = trycreate ? losetry : lose;
1110 /* this can happen with #driver. hack */
1111 if (!lsterr) lsterr = cpystr ("No such destination mailbox");
1112 }
1113 /* clean up any message text left behind */
1114 if (ad.flags) fs_give ((void **) &ad.flags);
1115 if (ad.date) fs_give ((void **) &ad.date);
1116 if (ad.msg) fs_give ((void **) &ad.msg);
1117 }
1118 else response = misarg;
1119 if (stream) /* allow untagged EXPUNGE */
1120 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1121 }
1122 /* list mailboxes */
1123 else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
1124 /* get reference and mailbox argument */
1125 if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
1126 response = misarg;
1127 else if (arg) response = badarg;
1128 /* make sure anonymous can't do bad things */
1129 else if (nameok (s,t)) {
1130 if (newsproxypattern (s,t,tmp,LONGT)) {
1131 proxylist = T;
1132 mail_list (NIL,"",tmp);
1133 proxylist = NIL;
1134 }
1135 else mail_list (NIL,s,t);
1136 }
1137 if (stream) /* allow untagged EXPUNGE */
1138 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1139 }
1140 /* scan mailboxes */
1141 else if (!strcmp (cmd,"SCAN")) {
1142 /* get arguments */
1143 if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
1144 (u = snarf (&arg)))) response = misarg;
1145 else if (arg) response = badarg;
1146 /* make sure anonymous can't do bad things */
1147 else if (nameok (s,t)) {
1148 if (newsproxypattern (s,t,tmp,NIL))
1149 mm_log ("SCAN not permitted for news",ERROR);
1150 else mail_scan (NIL,s,t,u);
1151 }
1152 if (stream) /* allow untagged EXPUNGE */
1153 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1154 }
1155 /* list subscribed mailboxes */
1156 else if (!strcmp (cmd,"LSUB") || !strcmp (cmd,"RLSUB")) {
1157 /* get reference and mailbox argument */
1158 if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
1159 response = misarg;
1160 else if (arg) response = badarg;
1161 /* make sure anonymous can't do bad things */
1162 else if (nameok (s,t)) {
1163 if (newsproxypattern (s,t,tmp,NIL)) newsrc_lsub (NIL,tmp);
1164 else mail_lsub (NIL,s,t);
1165 }
1166 if (stream) /* allow untagged EXPUNGE */
1167 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1168 }
1169
1170 /* find mailboxes */
1171 else if (!strcmp (cmd,"FIND")) {
1172 /* get subcommand and true argument */
1173 if (!(arg && (s = strtok_r (arg," \015\012",&sstate)) &&
1174 (s == cmd + 5) && (cmd[4] = ' ') && ucase (s) &&
1175 (arg = strtok_r (NIL,"\015\012",&sstate)) &&
1176 (s = snarf_list (&arg))))
1177 response = misarg; /* missing required argument */
1178 else if (arg) response = badarg;
1179 /* punt on single-char wildcards */
1180 else if (strpbrk (s,"%?")) response =
1181 "%.80s NO IMAP2 ? and %% wildcards not supported: %.80s\015\012";
1182 else if (nameok (NIL,s)) {
1183 finding = T; /* note that we are FINDing */
1184 /* dispatch based on type */
1185 if (!strcmp (cmd,"FIND MAILBOXES") && !anonymous)
1186 mail_lsub (NIL,NIL,s);
1187 else if (!strcmp (cmd,"FIND ALL.MAILBOXES")) {
1188 /* convert * to % for compatible behavior */
1189 for (t = s; *t; t++) if (*t == '*') *t = '%';
1190 mail_list (NIL,NIL,s);
1191 }
1192 else response = badcmd;
1193 }
1194 if (stream) /* allow untagged EXPUNGE */
1195 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1196 }
1197
1198 /* status of mailbox */
1199 else if (!strcmp (cmd,"STATUS")) {
1200 if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
1201 (t = strchr (arg,')')) && (t - arg) && !t[1]))
1202 response = misarg;
1203 else {
1204 f = NIL; /* initially no flags */
1205 *t = '\0'; /* tie off flag string */
1206 /* read flags */
1207 t = strtok_r (ucase (arg)," ",&sstate);
1208 do { /* parse each one; unknown generate warning */
1209 if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
1210 else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
1211 else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
1212 else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
1213 else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
1214 else {
1215 PSOUT ("* NO Unknown status flag ");
1216 PSOUT (t);
1217 CRLF;
1218 }
1219 } while (t = strtok_r (NIL," ",&sstate));
1220 ping_mailbox (uid); /* in case the fool did STATUS on open mbx */
1221 PFLUSH (); /* make sure stdout is dumped in case slave */
1222 if (!compare_cstring (s,"INBOX")) s = "INBOX";
1223 else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
1224 if (state == LOGOUT) response = lose;
1225 /* get mailbox status */
1226 else if (lastsel && (!strcmp (s,lastsel) ||
1227 (stream && !strcmp (s,stream->mailbox)))) {
1228 unsigned long unseen;
1229 /* snarl at cretins which do this */
1230 PSOUT ("* NO CLIENT BUG DETECTED: STATUS on selected mailbox: ");
1231 PSOUT (s);
1232 CRLF;
1233 tmp[0] = ' '; tmp[1] = '\0';
1234 if (f & SA_MESSAGES)
1235 sprintf (tmp + strlen (tmp)," MESSAGES %lu",stream->nmsgs);
1236 if (f & SA_RECENT)
1237 sprintf (tmp + strlen (tmp)," RECENT %lu",stream->recent);
1238 if (f & SA_UNSEEN) {
1239 for (i = 1,unseen = 0; i <= stream->nmsgs; i++)
1240 if (!mail_elt (stream,i)->seen) unseen++;
1241 sprintf (tmp + strlen (tmp)," UNSEEN %lu",unseen);
1242 }
1243 if (f & SA_UIDNEXT)
1244 sprintf (tmp + strlen (tmp)," UIDNEXT %lu",stream->uid_last+1);
1245 if (f & SA_UIDVALIDITY)
1246 sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",
1247 stream->uid_validity);
1248 tmp[1] = '(';
1249 strcat (tmp,")\015\012");
1250 PSOUT ("* STATUS ");
1251 pastring (s);
1252 PSOUT (tmp);
1253 }
1254 else if (isnewsproxy (s)) {
1255 sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
1256 if (!mail_status (NIL,tmp,f)) response = lose;
1257 }
1258 else if (!mail_status (NIL,s,f)) response = lose;
1259 }
1260 if (stream) /* allow untagged EXPUNGE */
1261 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1262 }
1263
1264 /* subscribe to mailbox */
1265 else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
1266 /* get <mailbox> or MAILBOX <mailbox> */
1267 if (!(s = snarf (&arg))) response = misarg;
1268 else if (arg) { /* IMAP2bis form */
1269 if (compare_cstring (s,"MAILBOX")) response = badarg;
1270 else if (!(s = snarf (&arg))) response = misarg;
1271 else if (arg) response = badarg;
1272 else mail_subscribe (NIL,s);
1273 }
1274 else if (isnewsproxy (s)) newsrc_update (NIL,s+6,':');
1275 else mail_subscribe (NIL,s);
1276 if (stream) /* allow untagged EXPUNGE */
1277 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1278 }
1279 /* unsubscribe to mailbox */
1280 else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
1281 /* get <mailbox> or MAILBOX <mailbox> */
1282 if (!(s = snarf (&arg))) response = misarg;
1283 else if (arg) { /* IMAP2bis form */
1284 if (compare_cstring (s,"MAILBOX")) response = badarg;
1285 else if (!(s = snarf (&arg))) response = misarg;
1286 else if (arg) response = badarg;
1287 else if (isnewsproxy (s)) newsrc_update (NIL,s+6,'!');
1288 else mail_unsubscribe (NIL,s);
1289 }
1290 else mail_unsubscribe (NIL,s);
1291 if (stream) /* allow untagged EXPUNGE */
1292 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1293 }
1294
1295 else if (!strcmp (cmd,"NAMESPACE")) {
1296 if (arg) response = badarg;
1297 else {
1298 NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
1299 NIL);
1300 NAMESPACE *n;
1301 PARAMETER *p;
1302 PSOUT ("* NAMESPACE");
1303 if (ns) for (i = 0; i < 3; i++) {
1304 if (n = ns[i]) {
1305 PSOUT (" (");
1306 do {
1307 PBOUT ('(');
1308 pstring (n->name);
1309 switch (n->delimiter) {
1310 case '\\': /* quoted delimiter */
1311 case '"':
1312 PSOUT (" \"\\\\\"");
1313 break;
1314 case '\0': /* no delimiter */
1315 PSOUT (" NIL");
1316 break;
1317 default: /* unquoted delimiter */
1318 PSOUT (" \"");
1319 PBOUT (n->delimiter);
1320 PBOUT ('"');
1321 break;
1322 }
1323 /* NAMESPACE extensions are hairy */
1324 if (p = n->param) do {
1325 PBOUT (' ');
1326 pstring (p->attribute);
1327 PSOUT (" (");
1328 do pstring (p->value);
1329 while (p->next && !p->next->attribute && (p = p->next));
1330 PBOUT (')');
1331 } while (p = p->next);
1332 PBOUT (')');
1333 } while (n = n->next);
1334 PBOUT (')');
1335 }
1336 else PSOUT (" NIL");
1337 }
1338 else PSOUT (" NIL NIL NIL");
1339 CRLF;
1340 }
1341 if (stream) /* allow untagged EXPUNGE */
1342 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1343 }
1344
1345 /* create mailbox */
1346 else if (!(anonymous || strcmp (cmd,"CREATE"))) {
1347 if (!(s = snarf (&arg))) response = misarg;
1348 else if (arg) response = badarg;
1349 else mail_create (NIL,s);
1350 if (stream) /* allow untagged EXPUNGE */
1351 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1352 }
1353 /* delete mailbox */
1354 else if (!(anonymous || strcmp (cmd,"DELETE"))) {
1355 if (!(s = snarf (&arg))) response = misarg;
1356 else if (arg) response = badarg;
1357 else { /* make sure not selected */
1358 if (lastsel && (!strcmp (s,lastsel) ||
1359 (stream && !strcmp (s,stream->mailbox))))
1360 mm_log ("Can not DELETE the selected mailbox",ERROR);
1361 else mail_delete (NIL,s);
1362 }
1363 if (stream) /* allow untagged EXPUNGE */
1364 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1365 }
1366 /* rename mailbox */
1367 else if (!(anonymous || strcmp (cmd,"RENAME"))) {
1368 if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
1369 else if (arg) response = badarg;
1370 else { /* make sure not selected */
1371 if (!compare_cstring (s,"INBOX")) s = "INBOX";
1372 else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
1373 if (lastsel && (!strcmp (s,lastsel) ||
1374 (stream && !strcmp (s,stream->mailbox))))
1375 mm_log ("Can not RENAME the selected mailbox",ERROR);
1376 else mail_rename (NIL,s,t);
1377 }
1378 if (stream) /* allow untagged EXPUNGE */
1379 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
1380 }
1381
1382 /* idle mode */
1383 else if (!strcmp (cmd,"IDLE")) {
1384 /* no arguments */
1385 if (arg) response = badarg;
1386 else { /* tell client ready for argument */
1387 unsigned long donefake = 0;
1388 PSOUT ("+ Waiting for DONE\015\012");
1389 PFLUSH (); /* dump output buffer */
1390 /* inactivity countdown */
1391 i = ((TIMEOUT) / (IDLETIMER)) + 1;
1392 do { /* main idle loop */
1393 if (!donefake) { /* don't ping mailbox if faking */
1394 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,
1395 (void *) stream);
1396 ping_mailbox (uid);
1397 /* maybe do a checkpoint if not anonymous */
1398 if (!anonymous && stream &&
1399 (time (0) > (lastcheck + CHECKTIMER))) {
1400 mail_check (stream);
1401 /* cancel likely altwin from mail_check() */
1402 if (lsterr) fs_give ((void **) &lsterr);
1403 if (lstwrn) fs_give ((void **) &lstwrn);
1404 /* remember last checkpoint */
1405 lastcheck = time (0);
1406 }
1407 }
1408 if (lstwrn) { /* have a warning? */
1409 PSOUT ("* NO ");
1410 PSOUT (lstwrn);
1411 CRLF;
1412 fs_give ((void **) &lstwrn);
1413 }
1414 if (!(i % 2)) { /* prevent NAT timeouts */
1415 sprintf (tmp,"* OK Timeout in %lu minutes\015\012",
1416 (i * IDLETIMER) / 60);
1417 PSOUT (tmp);
1418 }
1419 /* two minutes before the end... */
1420 if ((state == OPEN) && (i <= 2)) {
1421 sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
1422 donefake = nmsgs + 1,recent + 1);
1423 PSOUT (tmp); /* prod client to wake up */
1424 }
1425 PFLUSH (); /* dump output buffer */
1426 } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
1427
1428 /* time to exit idle loop */
1429 if (state != LOGOUT) {
1430 if (i) { /* still have time left? */
1431 /* yes, read expected DONE */
1432 slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
1433 if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
1434 ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
1435 ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
1436 ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
1437 (((tmp[4] != '\015') || (tmp[5] != '\012')) &&
1438 (tmp[4] != '\012')))
1439 response = "%.80s BAD Bogus IDLE continuation\015\012";
1440 if (donefake) { /* if faking at the end */
1441 /* send EXPUNGE (should be just 1) */
1442 while (donefake > nmsgs) {
1443 sprintf (tmp,"* %lu EXPUNGE\015\012",donefake--);
1444 PSOUT (tmp);
1445 }
1446 sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
1447 nmsgs,recent);
1448 PSOUT (tmp);
1449 }
1450 }
1451 else clkint (); /* otherwise do autologout action */
1452 }
1453 }
1454 }
1455 else response = badcmd;
1456 break;
1457 default:
1458 response = "%.80s BAD Unknown state for %.80s command\015\012";
1459 break;
1460 }
1461
1462 while (litplus.ok) { /* any unread LITERAL+? */
1463 litplus.ok = NIL; /* yes, cancel it now */
1464 clearerr (stdin); /* clear stdin errors */
1465 status = "discarding unread literal";
1466 /* read literal and discard it */
1467 while (i = (litplus.size > MAILTMPLEN) ? MAILTMPLEN : litplus.size) {
1468 if (state == LOGOUT) litplus.size = 0;
1469 else {
1470 settimeout (INPUTTIMEOUT);
1471 if (PSINR (tmp,i)) litplus.size -= i;
1472 else {
1473 ioerror (stdin,status);
1474 litplus.size = 0; /* in case it continues */
1475 }
1476 }
1477 }
1478 settimeout (0); /* stop timeout */
1479 /* get new command tail */
1480 slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
1481 /* locate end of line */
1482 if (t = strchr (tmp,'\012')) {
1483 /* back over CR */
1484 if ((t > tmp) && (t[-1] == '\015')) --t;
1485 *t = NIL; /* tie off CRLF */
1486 /* possible LITERAL+? */
1487 if (((i = strlen (tmp)) > 3) && (tmp[i - 1] == '}') &&
1488 (tmp[i - 2] == '+') && isdigit (tmp[i - 3])) {
1489 /* back over possible count */
1490 for (i -= 4; i && isdigit (tmp[i]); i--);
1491 if (tmp[i] == '{') { /* found a literal? */
1492 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
1493 litplus.size = strtoul (tmp + i + 1,NIL,10);
1494 }
1495 }
1496 }
1497 else flush (); /* overlong line after LITERAL+, punt */
1498 }
1499 ping_mailbox (uid); /* update mailbox status before response */
1500 if (lstwrn && lsterr) { /* output most recent warning */
1501 PSOUT ("* NO ");
1502 PSOUT (lstwrn);
1503 CRLF;
1504 fs_give ((void **) &lstwrn);
1505 }
1506
1507 if (response == logwin) { /* authentication win message */
1508 sprintf (tmp,response,lstref ? "*" : tag);
1509 PSOUT (tmp); /* start response */
1510 pcapability (1); /* print logged-in capabilities */
1511 PSOUT ("] User ");
1512 PSOUT (user);
1513 PSOUT (" authenticated\015\012");
1514 if (lstref) {
1515 sprintf (tmp,response,tag);
1516 PSOUT (tmp); /* start response */
1517 PSOUT ("[REFERRAL ");
1518 PSOUT (lstref);
1519 PSOUT ("] ");
1520 PSOUT (lasterror ());
1521 CRLF;
1522 }
1523 }
1524 else if ((response == win) || (response == lose)) {
1525 sprintf (tmp,response,tag);
1526 PSOUT (tmp);
1527 if (cauidvalidity) { /* COPYUID/APPENDUID response? */
1528 sprintf (tmp,"[%.80sUID %lu ",(char *)
1529 ((s = strchr (cmd,' ')) ? s+1 : cmd),cauidvalidity);
1530 PSOUT (tmp);
1531 cauidvalidity = 0; /* cancel response for future */
1532 if (csset) {
1533 pset (&csset);
1534 PBOUT (' ');
1535 }
1536 pset (&caset);
1537 PSOUT ("] ");
1538 }
1539 else if (lstref) { /* have a referral? */
1540 PSOUT ("[REFERRAL ");
1541 PSOUT (lstref);
1542 PSOUT ("] ");
1543 }
1544 if (lsterr || lstwrn) PSOUT (lasterror ());
1545 else {
1546 PSOUT (cmd);
1547 PSOUT ((response == win) ? " completed" : "failed");
1548 }
1549 CRLF;
1550 }
1551 else { /* normal response */
1552 if ((response == rowin) || (response == rwwin)) {
1553 if (lstwrn) { /* output most recent warning */
1554 PSOUT ("* NO ");
1555 PSOUT (lstwrn);
1556 CRLF;
1557 fs_give ((void **) &lstwrn);
1558 }
1559 }
1560 sprintf (tmp,response,tag,cmd,lasterror ());
1561 PSOUT (tmp); /* output response */
1562 }
1563 }
1564 PFLUSH (); /* make sure output blatted */
1565
1566 if (autologouttime) { /* have an autologout in effect? */
1567 /* cancel if no longer waiting for login */
1568 if (state != LOGIN) autologouttime = 0;
1569 /* took too long to login */
1570 else if (autologouttime < time (0)) {
1571 logout = goodbye = "Autologout";
1572 stream = NIL;
1573 state = LOGOUT; /* sayonara */
1574 }
1575 }
1576 }
1577 if (goodbye && !quell_events){/* have a goodbye message? */
1578 PSOUT ("* BYE "); /* utter it */
1579 PSOUT (goodbye);
1580 CRLF;
1581 PFLUSH (); /* make sure blatted */
1582 }
1583 syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
1584 user ? (char *) user : "???",tcp_clienthost ());
1585 /* do logout hook if needed */
1586 if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL))
1587 (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
1588 _exit (ret); /* all done */
1589 return ret; /* stupid compilers */
1590 }
1591
1592 /* Ping mailbox during each cycle. Also check alerts
1593 * Accepts: last command was UID flag
1594 */
1595
ping_mailbox(unsigned long uid)1596 void ping_mailbox (unsigned long uid)
1597 {
1598 unsigned long i;
1599 char tmp[MAILTMPLEN];
1600 if (state == OPEN) {
1601 if (!mail_ping (stream)) { /* make sure stream still alive */
1602 PSOUT ("* BYE ");
1603 PSOUT (mylocalhost ());
1604 PSOUT (" Fatal mailbox error: ");
1605 PSOUT (lasterror ());
1606 CRLF;
1607 stream = NIL; /* don't try to clean up stream */
1608 state = LOGOUT; /* go away */
1609 syslog (LOG_INFO,
1610 "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
1611 user ? (char *) user : "???",tcp_clienthost (),
1612 (stream && stream->mailbox) ? stream->mailbox : "???",
1613 lasterror ());
1614 return;
1615 }
1616 /* change in number of messages? */
1617 if (existsquelled || (nmsgs != stream->nmsgs)) {
1618 PSOUT ("* ");
1619 pnum (nmsgs = stream->nmsgs);
1620 PSOUT (" EXISTS\015\012");
1621 }
1622 /* change in recent messages? */
1623 if (existsquelled || (recent != stream->recent)) {
1624 PSOUT ("* ");
1625 pnum (recent = stream->recent);
1626 PSOUT (" RECENT\015\012");
1627 }
1628 existsquelled = NIL; /* don't do this until asked again */
1629 if (stream->uid_validity && (stream->uid_validity != uidvalidity)) {
1630 PSOUT ("* OK [UIDVALIDITY ");
1631 pnum (stream->uid_validity);
1632 PSOUT ("] UID validity status\015\012* OK [UIDNEXT ");
1633 pnum (stream->uid_last + 1);
1634 PSOUT ("] Predicted next UID\015\012");
1635 if (stream->uid_nosticky) {
1636 PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
1637 PSOUT (stream->mailbox);
1638 CRLF;
1639 }
1640 uidvalidity = stream->uid_validity;
1641 }
1642
1643 /* don't bother if driver changed */
1644 if (curdriver == stream->dtb) {
1645 /* first report any new flags */
1646 if ((nflags < NUSERFLAGS) && stream->user_flags[nflags])
1647 new_flags (stream);
1648 for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
1649 PSOUT ("* ");
1650 pnum (i);
1651 PSOUT (" FETCH (");
1652 fetch_flags (i,NIL); /* output changed flags */
1653 if (uid) { /* need to include UIDs in response? */
1654 PBOUT (' ');
1655 fetch_uid (i,NIL);
1656 }
1657 PSOUT (")\015\012");
1658 }
1659 }
1660 else { /* driver changed */
1661 new_flags (stream); /* send mailbox flags */
1662 if (curdriver) { /* note readonly/write if possible change */
1663 PSOUT ("* OK [READ-");
1664 PSOUT (stream->rdonly ? "ONLY" : "WRITE");
1665 PSOUT ("] Mailbox status\015\012");
1666 }
1667 curdriver = stream->dtb;
1668 if (nmsgs) { /* get flags for all messages */
1669 sprintf (tmp,"1:%lu",nmsgs);
1670 mail_fetch_flags (stream,tmp,NIL);
1671 /* don't do this if newsrc already did */
1672 if (!(curdriver->flags & DR_NEWS)) {
1673 /* find first unseen message */
1674 for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
1675 if (i <= nmsgs) {
1676 PSOUT ("* OK [UNSEEN ");
1677 pnum (i);
1678 PSOUT ("] first unseen message in ");
1679 PSOUT (stream->mailbox);
1680 CRLF;
1681 }
1682 }
1683 }
1684 }
1685 }
1686 if (shutdowntime && (time (0) > shutdowntime + SHUTDOWNTIMER)) {
1687 PSOUT ("* BYE Server shutting down\015\012");
1688 state = LOGOUT;
1689 }
1690 /* don't do these stat()s every cycle */
1691 else if (time (0) > alerttime + ALERTTIMER) {
1692 struct stat sbuf;
1693 /* have a shutdown file? */
1694 if (!stat (SHUTDOWNFILE,&sbuf)) {
1695 PSOUT ("* OK [ALERT] Server shutting down shortly\015\012");
1696 shutdowntime = time (0);
1697 }
1698 alerttime = time (0); /* output any new alerts */
1699 sysalerttime = palert (ALERTFILE,sysalerttime);
1700 if (state != LOGIN) /* do user alert if logged in */
1701 useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
1702 }
1703 }
1704
1705 /* Print an alert file
1706 * Accepts: path of alert file
1707 * time of last printed alert file
1708 * Returns: updated time of last printed alert file
1709 */
1710
palert(char * file,time_t oldtime)1711 time_t palert (char *file,time_t oldtime)
1712 {
1713 FILE *alf;
1714 struct stat sbuf;
1715 int c,lc = '\012';
1716 /* have a new alert file? */
1717 if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
1718 !(alf = fopen (file,"r"))) return oldtime;
1719 /* yes, display it */
1720 while ((c = getc (alf)) != EOF) {
1721 if (lc == '\012') PSOUT ("* OK [ALERT] ");
1722 switch (c) { /* output character */
1723 case '\012': /* newline means do CRLF */
1724 CRLF;
1725 case '\015': /* flush CRs */
1726 case '\0': /* flush nulls */
1727 break;
1728 default:
1729 PBOUT (c); /* output all other characters */
1730 break;
1731 }
1732 lc = c; /* note previous character */
1733 }
1734 fclose (alf);
1735 if (lc != '\012') CRLF; /* final terminating CRLF */
1736 return sbuf.st_mtime; /* return updated last alert time */
1737 }
1738
1739 /* Initialize file string structure for file stringstruct
1740 * Accepts: string structure
1741 * pointer to message data structure
1742 * size of string
1743 */
1744
msg_string_init(STRING * s,void * data,unsigned long size)1745 void msg_string_init (STRING *s,void *data,unsigned long size)
1746 {
1747 MSGDATA *md = (MSGDATA *) data;
1748 s->data = data; /* note stream/msgno and header length */
1749 #if 0
1750 s->size = size; /* message size */
1751 s->curpos = s->chunk = /* load header */
1752 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
1753 FT_PREFETCHTEXT | FT_PEEK);
1754 #else /* This kludge is necessary because of broken mail stores */
1755 mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
1756 s->curpos = s->chunk = /* load header */
1757 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PEEK);
1758 s->size += s->data1; /* header + body size */
1759 #endif
1760 s->cursize = s->chunksize = s->data1;
1761 s->offset = 0; /* offset is start of message */
1762 }
1763
1764
1765 /* Get next character from file stringstruct
1766 * Accepts: string structure
1767 * Returns: character, string structure chunk refreshed
1768 */
1769
msg_string_next(STRING * s)1770 char msg_string_next (STRING *s)
1771 {
1772 char c = *s->curpos++; /* get next byte */
1773 SETPOS (s,GETPOS (s)); /* move to next chunk */
1774 return c; /* return the byte */
1775 }
1776
1777
1778 /* Set string pointer position for file stringstruct
1779 * Accepts: string structure
1780 * new position
1781 */
1782
msg_string_setpos(STRING * s,unsigned long i)1783 void msg_string_setpos (STRING *s,unsigned long i)
1784 {
1785 MSGDATA *md = (MSGDATA *) s->data;
1786 if (i < s->data1) { /* want header? */
1787 s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
1788 s->chunksize = s->data1; /* header length */
1789 s->offset = 0; /* offset is start of message */
1790 }
1791 else if (i < s->size) { /* want body */
1792 s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
1793 s->chunksize = s->size - s->data1;
1794 s->offset = s->data1; /* offset is end of header */
1795 }
1796 else { /* off end of message */
1797 s->chunk = NIL; /* make sure that we crack on this then */
1798 s->chunksize = 1; /* make sure SNX cracks the right way... */
1799 s->offset = i;
1800 }
1801 /* initial position and size */
1802 s->curpos = s->chunk + (i -= s->offset);
1803 s->cursize = s->chunksize - i;
1804 }
1805
1806 /* Send flags for stream
1807 * Accepts: MAIL stream
1808 * scratch buffer
1809 */
1810
new_flags(MAILSTREAM * stream)1811 void new_flags (MAILSTREAM *stream)
1812 {
1813 int i,c;
1814 PSOUT ("* FLAGS (");
1815 for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
1816 PSOUT (stream->user_flags[i]);
1817 PBOUT (' ');
1818 nflags = i + 1;
1819 }
1820 PSOUT ("\\Answered \\Flagged \\Deleted \\Draft \\Seen)\015\012* OK [PERMANENTFLAGS (");
1821 for (i = c = 0; i < NUSERFLAGS; i++)
1822 if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
1823 put_flag (&c,stream->user_flags[i]);
1824 if (stream->kwd_create) put_flag (&c,"\\*");
1825 if (stream->perm_answered) put_flag (&c,"\\Answered");
1826 if (stream->perm_flagged) put_flag (&c,"\\Flagged");
1827 if (stream->perm_deleted) put_flag (&c,"\\Deleted");
1828 if (stream->perm_draft) put_flag (&c,"\\Draft");
1829 if (stream->perm_seen) put_flag (&c,"\\Seen");
1830 PSOUT (")] Permanent flags\015\012");
1831 }
1832
1833 /* Set timeout
1834 * Accepts: desired interval
1835 */
1836
settimeout(unsigned int i)1837 void settimeout (unsigned int i)
1838 {
1839 /* limit if not logged in */
1840 if (i) alarm ((state == LOGIN) ? LOGINTIMEOUT : i);
1841 else alarm (0);
1842 }
1843
1844
1845 /* Clock interrupt
1846 * Returns only if critical code in progress
1847 */
1848
clkint(void)1849 void clkint (void)
1850 {
1851 settimeout (SIGNALTIMER); /* disable most interrupts */
1852 server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
1853 logout = "Autologout";
1854 goodbye = "Autologout (idle for too long)";
1855 if (critical) state = LOGOUT; /* must defer if in critical code */
1856 else longjmp (jmpenv,1); /* die now */
1857 }
1858
1859 /* Clock interrupt after signal
1860 * Never returns
1861 */
1862
dieint(void)1863 void dieint (void)
1864 {
1865 _exit(1); /* slay the beast. Now. */
1866 }
1867
1868
1869 /* Kiss Of Death interrupt
1870 * Returns only if critical code in progress
1871 */
1872
kodint(void)1873 void kodint (void)
1874 {
1875 settimeout (SIGNALTIMER); /* disable most interrupts */
1876 server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
1877 logout = goodbye = "Killed (lost mailbox lock)";
1878 if (critical) state = LOGOUT; /* must defer if in critical code */
1879 else longjmp (jmpenv,1); /* die now */
1880 }
1881
1882 /* Hangup interrupt
1883 * Returns only if critical code in progress
1884 */
1885
hupint(void)1886 void hupint (void)
1887 {
1888 settimeout (SIGNALTIMER); /* disable most interrupts */
1889 server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
1890 logout = "Hangup";
1891 goodbye = NIL; /* other end is already gone */
1892 if (critical) state = LOGOUT; /* must defer if in critical code */
1893 else longjmp (jmpenv,1); /* die now */
1894 }
1895
1896
1897 /* Termination interrupt
1898 * Returns only if critical code in progress
1899 */
1900
trmint(void)1901 void trmint (void)
1902 {
1903 settimeout (SIGNALTIMER); /* disable most interrupts */
1904 server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
1905 logout = goodbye = "Killed (terminated)";
1906 /* Make no attempt at graceful closure since a shutdown may be in
1907 * progress, and we won't have any time to do mail_close() actions
1908 */
1909 stream = NIL;
1910 if (critical) state = LOGOUT; /* must defer if in critical code */
1911 else longjmp (jmpenv,1); /* die now */
1912 }
1913
1914 /* The routines on this and the next page eschew the use of non-syscall libc
1915 * routines (especially stdio) for a reason. Also, these hideous #if
1916 * condtionals need to be replaced.
1917 */
1918
1919 #ifndef unix
1920 #define unix 0
1921 #endif
1922
1923
1924 /* Status request interrupt
1925 * Always returns
1926 */
1927
staint(void)1928 void staint (void)
1929 {
1930 #if unix
1931 int fd;
1932 char *s,buf[8*MAILTMPLEN];
1933 unsigned long pid = getpid ();
1934 /* build file name */
1935 s = nout (sout (buf,"/tmp/imapd-status."),pid,10);
1936 if (user) s = sout (sout (s,"."),user);
1937 *s = '\0'; /* tie off file name */
1938 if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) {
1939 fchmod (fd,0666);
1940 s = nout (sout (buf,"PID="),pid,10);
1941 switch (state) {
1942 case LOGIN:
1943 s = sout (s,", not logged in");
1944 break;
1945 case SELECT:
1946 s = sout (s,", logged in");
1947 break;
1948 case OPEN:
1949 s = sout (s,", mailbox open");
1950 break;
1951 case LOGOUT:
1952 s = sout (s,", logging out");
1953 break;
1954 }
1955 if (user) {
1956 s = sout (sout (s,"\nuser="),user);
1957 }
1958 if (blackberry) {
1959 /* Don't report client host until Blackberryness known. This is
1960 because tcp_clienthost() may not have been called yet and so the
1961 string might not be cached. As this is a signal handler, we want to
1962 minimize any system calls.
1963 */
1964 s = sout (sout (s,"\nhost="),tcp_clienthost ());
1965 if (blackberry > 0) s = sout (s, " (blackberry)");
1966 }
1967 if (stream && stream->mailbox) {
1968 s = sout (sout (sout (s,"\nmailbox="),stream->mailbox),
1969 stream->rdonly ? " (read-only)" : " (read-write)");
1970 }
1971 *s++ = '\n';
1972 if (status) {
1973 s = sout (s,status);
1974 if (cmd) s = sout (sout (s,", last command="),cmd);
1975 }
1976 else if (cmd) s = sout (sout (s,cmd)," in progress");
1977 else s = sout (s,"UNKNOWN STATE");
1978 *s++ = '\n';
1979 write (fd,buf,s-buf);
1980 close (fd);
1981 }
1982 #endif
1983 }
1984
1985 /* Write string
1986 * Accepts: destination string pointer
1987 * string
1988 * Returns: updated string pointer
1989 */
1990
sout(char * s,char * t)1991 char *sout (char *s,char *t)
1992 {
1993 while (*t) *s++ = *t++;
1994 return s;
1995 }
1996
1997
1998 /* Write number
1999 * Accepts: destination string pointer
2000 * number
2001 * base
2002 * Returns: updated string pointer
2003 */
2004
nout(char * s,unsigned long n,unsigned long base)2005 char *nout (char *s,unsigned long n,unsigned long base)
2006 {
2007 char stack[256];
2008 char *t = stack;
2009 /* push PID digits on stack */
2010 do *t++ = (char) (n % base) + '0';
2011 while (n /= base);
2012 /* pop digits from stack */
2013 while (t > stack) *s++ = *--t;
2014 return s;
2015 }
2016
2017 /* Slurp a command line
2018 * Accepts: buffer pointer
2019 * buffer size
2020 * input timeout
2021 */
2022
slurp(char * s,int n,unsigned long timeout)2023 void slurp (char *s,int n,unsigned long timeout)
2024 {
2025 memset (s,'\0',n); /* zap buffer */
2026 if (state != LOGOUT) { /* get a command under timeout */
2027 settimeout (timeout);
2028 clearerr (stdin); /* clear stdin errors */
2029 status = "reading line";
2030 if (!PSIN (s,n-1)) ioerror (stdin,status);
2031 settimeout (0); /* make sure timeout disabled */
2032 status = NIL;
2033 }
2034 }
2035
2036
2037 /* Read a literal
2038 * Accepts: destination buffer (must be size+1 for trailing NUL)
2039 * size of buffer (must be less than 4294967295)
2040 */
2041
inliteral(char * s,unsigned long n)2042 void inliteral (char *s,unsigned long n)
2043 {
2044 unsigned long i;
2045 if (litplus.ok) { /* no more LITERAL+ to worry about */
2046 litplus.ok = NIL;
2047 litplus.size = 0;
2048 }
2049 else { /* otherwise tell client ready for argument */
2050 PSOUT ("+ Ready for argument\015\012");
2051 PFLUSH (); /* dump output buffer */
2052 }
2053 clearerr (stdin); /* clear stdin errors */
2054 memset (s,'\0',n+1); /* zap buffer */
2055 status = "reading literal";
2056 while (n) { /* get data under timeout */
2057 if (state == LOGOUT) n = 0;
2058 else {
2059 settimeout (INPUTTIMEOUT);
2060 i = min (n,8192); /* must read at least 8K within timeout */
2061 if (PSINR (s,i)) {
2062 s += i;
2063 n -= i;
2064 }
2065 else {
2066 ioerror (stdin,status);
2067 n = 0; /* in case it continues */
2068 }
2069 settimeout (0); /* stop timeout */
2070 }
2071 }
2072 }
2073
2074 /* Flush until newline seen
2075 * Returns: NIL and sets response, always
2076 */
2077
flush(void)2078 unsigned char *flush (void)
2079 {
2080 int c;
2081 if (state != LOGOUT) {
2082 settimeout (INPUTTIMEOUT);
2083 clearerr (stdin); /* clear stdin errors */
2084 status = "flushing line";
2085 while ((c = PBIN ()) != '\012') if (c == EOF) ioerror (stdin,status);
2086 settimeout (0); /* make sure timeout disabled */
2087 }
2088 response = "%.80s BAD Command line too long\015\012";
2089 status = NIL;
2090 return NIL;
2091 }
2092
2093
2094 /* Report command stream error and die
2095 * Accepts: stdin or stdout (whichever got the error)
2096 * reason (what caller was doing)
2097 */
2098
ioerror(FILE * f,char * reason)2099 void ioerror (FILE *f,char *reason)
2100 {
2101 static char msg[MAILTMPLEN];
2102 char *s,*t;
2103 if (logout) { /* say nothing if already dying */
2104 settimeout (SIGNALTIMER); /* disable most interrupts */
2105 server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
2106 /* write error string */
2107 for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect",
2108 t = logout = msg; *s; *t++ = *s++);
2109 for (s = ", while "; *s; *t++ = *s++);
2110 for (s = reason; *s; *t++ = *s++);
2111 if (critical) { /* must defer if in critical code */
2112 state = LOGOUT; /* die as soon as we can */
2113 }
2114 else longjmp (jmpenv,1); /* die now */
2115 }
2116 }
2117
2118 /* Parse an IMAP astring
2119 * Accepts: pointer to argument text pointer
2120 * pointer to returned size
2121 * pointer to returned delimiter
2122 * Returns: argument
2123 */
2124
parse_astring(unsigned char ** arg,unsigned long * size,unsigned char * del)2125 unsigned char *parse_astring (unsigned char **arg,unsigned long *size,
2126 unsigned char *del)
2127 {
2128 unsigned long i;
2129 unsigned char c,*s,*t,*v;
2130 if (!*arg) return NIL; /* better be an argument */
2131 switch (**arg) { /* see what the argument is */
2132 default: /* atom */
2133 for (s = t = *arg, i = 0;
2134 (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
2135 (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
2136 (*t != '\\'); ++t,++i);
2137 if (*size = i) break; /* got atom if non-empty */
2138 case ')': case '%': case '*': case '\\': case '\0': case ' ':
2139 return NIL; /* empty atom is a bogon */
2140 case '"': /* hunt for trailing quote */
2141 for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
2142 /* quote next character */
2143 if (c == '\\') switch (c = *t++) {
2144 case '"': case '\\': break;
2145 default: return NIL; /* invalid quote-next */
2146 }
2147 /* else must be a CHAR */
2148 if (!c || (c & 0x80)) return NIL;
2149 }
2150 *v = '\0'; /* tie off string */
2151 *size = v - s; /* return size */
2152 break;
2153
2154 case '{': /* literal string */
2155 s = *arg + 1; /* get size */
2156 if (!isdigit (*s)) return NIL;
2157 if ((*size = i = strtoul (s,(char **) &t,10)) > MAXCLIENTLIT) {
2158 mm_notify (NIL,"Absurdly long client literal",ERROR);
2159 syslog (LOG_INFO,"Overlong (%lu) client literal user=%.80s host=%.80s",
2160 i,user ? (char *) user : "???",tcp_clienthost ());
2161 return NIL;
2162 }
2163 switch (*t) { /* validate end of literal */
2164 case '+': /* non-blocking literal */
2165 if (*++t != '}') return NIL;
2166 case '}':
2167 if (!t[1]) break; /* OK if end of line */
2168 default:
2169 return NIL; /* bad literal */
2170 }
2171 if (litsp >= LITSTKLEN) { /* make sure don't overflow stack */
2172 mm_notify (NIL,"Too many literals in command",ERROR);
2173 return NIL;
2174 }
2175 /* get a literal buffer */
2176 inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i);
2177 /* get new command tail */
2178 slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT);
2179 /* if too long, flush and set response */
2180 if (!strchr (t,'\012')) return flush ();
2181 /* reset strtok mechanism, tie off if done */
2182 if (!strtok_r (t,"\015\012",&sstate)) *t = '\0';
2183 /* possible LITERAL+? */
2184 if (((i = strlen (t)) > 3) && (t[i - 1] == '}') &&
2185 (t[i - 2] == '+') && isdigit (t[i - 3])) {
2186 /* back over possible count */
2187 for (i -= 4; i && isdigit (t[i]); i--);
2188 if (t[i] == '{') { /* found a literal? */
2189 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
2190 litplus.size = strtoul (t + i + 1,NIL,10);
2191 }
2192 }
2193 break;
2194 }
2195 if (*del = *t) { /* have a delimiter? */
2196 *t++ = '\0'; /* yes, stomp on it */
2197 *arg = t; /* update argument pointer */
2198 }
2199 else *arg = NIL; /* no more arguments */
2200 return s;
2201 }
2202
2203 /* Parse tag
2204 * Accepts: command tag
2205 * Returns: tag if valid, NIL otherwise
2206 */
2207
parse_tag(unsigned char * cmd,unsigned char ** ret)2208 unsigned char *parse_tag (unsigned char *cmd,unsigned char **ret)
2209 {
2210 unsigned char *s;
2211 *ret = cmd; /* make sure this stays defined */
2212 if (!*cmd) return NIL; /* empty command line */
2213 /* find end of tag */
2214 for (s = cmd; *s && (*s > ' ') && (*s < 0x7f) &&
2215 (*s != '(') && (*s != ')') && (*s != '{') &&
2216 (*s != '%') && (*s != '*') &&
2217 (*s != '"') && (*s != '\\'); ++s);
2218 if (*s != ' ') return NIL; /* tag must be delimited by space */
2219 *s++ = '\0'; /* tie off tag */
2220 *ret = s; /* set pointer to remainder of command */
2221 return cmd; /* return tag */
2222 }
2223
2224 /* Snarf a command argument (simple jacket into parse_astring())
2225 * Accepts: pointer to argument text pointer
2226 * Returns: argument
2227 */
2228
snarf(unsigned char ** arg)2229 unsigned char *snarf (unsigned char **arg)
2230 {
2231 unsigned long i;
2232 unsigned char c;
2233 unsigned char *s = parse_astring (arg,&i,&c);
2234 return ((c == ' ') || !c) ? s : NIL;
2235 }
2236
2237
2238 /* Snarf a BASE64 argument for SASL-IR
2239 * Accepts: pointer to argument text pointer
2240 * Returns: argument
2241 */
2242
snarf_base64(unsigned char ** arg)2243 unsigned char *snarf_base64 (unsigned char **arg)
2244 {
2245 unsigned char *ret = *arg;
2246 unsigned char *s = ret + 1;
2247 static char base64mask[256] = {
2248 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,
2249 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,
2250 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,
2251 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,
2252 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,
2253 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,
2254 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,
2255 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
2256 };
2257 if (*(ret = *arg) == '='); /* easy case if zero-length argument */
2258 /* must be at least one BASE64 char */
2259 else if (!base64mask[*ret]) return NIL;
2260 else { /* quick and dirty */
2261 while (base64mask[*s]) ++s; /* scan until end of BASE64 */
2262 if (*s == '=') ++s; /* allow up to two padding chars */
2263 if (*s == '=') ++s;
2264 }
2265 switch (*s) { /* anything following the argument? */
2266 case ' ': /* another argument */
2267 *s++ = '\0'; /* tie off previous argument */
2268 *arg = s; /* and update argument pointer */
2269 break;
2270 case '\0': /* end of command */
2271 *arg = NIL;
2272 break;
2273 default: /* syntax error */
2274 return NIL;
2275 }
2276 return ret; /* return BASE64 string */
2277 }
2278
2279 /* Snarf a list command argument (simple jacket into parse_astring())
2280 * Accepts: pointer to argument text pointer
2281 * Returns: argument
2282 */
2283
snarf_list(unsigned char ** arg)2284 unsigned char *snarf_list (unsigned char **arg)
2285 {
2286 unsigned long i;
2287 unsigned char c,*s,*t;
2288 if (!*arg) return NIL; /* better be an argument */
2289 switch (**arg) {
2290 default: /* atom and/or wildcard chars */
2291 for (s = t = *arg, i = 0;
2292 (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
2293 (*t != '"') && (*t != '\\'); ++t,++i);
2294 if (c = *t) { /* have a delimiter? */
2295 *t++ = '\0'; /* stomp on it */
2296 *arg = t; /* update argument pointer */
2297 }
2298 else *arg = NIL;
2299 break;
2300 case ')': case '\\': case '\0': case ' ':
2301 return NIL; /* empty name is bogus */
2302 case '"': /* quoted string? */
2303 case '{': /* or literal? */
2304 s = parse_astring (arg,&i,&c);
2305 break;
2306 }
2307 return ((c == ' ') || !c) ? s : NIL;
2308 }
2309
2310 /* Get a list of header lines
2311 * Accepts: pointer to string pointer
2312 * pointer to list flag
2313 * Returns: string list
2314 */
2315
parse_stringlist(unsigned char ** s,int * list)2316 STRINGLIST *parse_stringlist (unsigned char **s,int *list)
2317 {
2318 char *t;
2319 unsigned long i;
2320 char c = ' ';
2321 STRINGLIST *ret = NIL,*cur = NIL;
2322 if (*s && **s == '(') { /* proper list? */
2323 ++*s; /* for each item in list */
2324 while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
2325 /* get new block */
2326 if (cur) cur = cur->next = mail_newstringlist ();
2327 else cur = ret = mail_newstringlist ();
2328 /* note text */
2329 cur->text.data = (unsigned char *) fs_get (i + 1);
2330 memcpy (cur->text.data,t,i);
2331 cur->text.size = i; /* and size */
2332 }
2333 /* must be end of list */
2334 if (c != ')') mail_free_stringlist (&ret);
2335 }
2336 if (t = *s) { /* need to reload strtok state? */
2337 /* end of a list? */
2338 if (*list && (*t == ')') && !t[1]) *list = NIL;
2339 else sstate = t; /* otherwise reset strtok state to s */
2340 }
2341 return ret;
2342 }
2343
2344 /* Get value of UID * for criteria parsing
2345 * Accepts: stream
2346 * Returns: maximum UID
2347 */
2348
uidmax(MAILSTREAM * stream)2349 unsigned long uidmax (MAILSTREAM *stream)
2350 {
2351 return stream->nmsgs ? mail_uid (stream,stream->nmsgs) : 0xffffffff;
2352 }
2353
2354
2355 /* Parse search criteria
2356 * Accepts: search program to write criteria into
2357 * pointer to argument text pointer
2358 * maximum message number
2359 * maximum UID
2360 * logical nesting depth
2361 * Returns: T if success, NIL if error
2362 */
2363
parse_criteria(SEARCHPGM * pgm,unsigned char ** arg,unsigned long maxmsg,unsigned long maxuid,unsigned long depth)2364 long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
2365 unsigned long maxuid,unsigned long depth)
2366 {
2367 if (arg && *arg) { /* must be an argument */
2368 /* parse criteria */
2369 do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
2370 /* as long as a space delimiter */
2371 while (**arg == ' ' && (*arg)++);
2372 /* failed if not end of criteria */
2373 if (**arg && **arg != ')') return NIL;
2374 }
2375 return T; /* success */
2376 }
2377
2378 /* Parse a search criterion
2379 * Accepts: search program to write criterion into
2380 * pointer to argument text pointer
2381 * maximum message number
2382 * maximum UID
2383 * logical nesting depth
2384 * Returns: T if success, NIL if error
2385 */
2386
parse_criterion(SEARCHPGM * pgm,unsigned char ** arg,unsigned long maxmsg,unsigned long maxuid,unsigned long depth)2387 long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
2388 unsigned long maxuid,unsigned long depth)
2389 {
2390 unsigned long i;
2391 unsigned char c = NIL,*s,*t,*v,*tail,*del;
2392 SEARCHSET **set;
2393 SEARCHPGMLIST **not;
2394 SEARCHOR **or;
2395 SEARCHHEADER **hdr;
2396 long ret = NIL;
2397 /* better be an argument */
2398 if ((depth > 500) || !(arg && *arg));
2399 else if (**arg == '(') { /* list of criteria? */
2400 (*arg)++; /* yes, parse the criteria */
2401 if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
2402 (*arg)++; /* skip closing paren */
2403 ret = T; /* successful parse of list */
2404 }
2405 }
2406 else { /* find end of criterion */
2407 if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
2408 c = *(del = tail); /* remember the delimiter */
2409 *del = '\0'; /* tie off criterion */
2410 switch (*ucase (s)) { /* dispatch based on character */
2411 case '*': /* sequence */
2412 case '0': case '1': case '2': case '3': case '4':
2413 case '5': case '6': case '7': case '8': case '9':
2414 if (*(set = &pgm->msgno)){/* already a sequence? */
2415 /* silly, but not as silly as the client! */
2416 for (not = &pgm->not; *not; not = &(*not)->next);
2417 *not = mail_newsearchpgmlist ();
2418 set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
2419 }
2420 ret = crit_set (set,&s,maxmsg) && (tail == s);
2421 break;
2422 case 'A': /* possible ALL, ANSWERED */
2423 if (!strcmp (s+1,"LL")) ret = T;
2424 else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
2425 break;
2426
2427 case 'B': /* possible BCC, BEFORE, BODY */
2428 if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
2429 ret = crit_string (&pgm->bcc,&tail);
2430 else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
2431 ret = crit_date (&pgm->before,&tail);
2432 else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
2433 ret = crit_string (&pgm->body,&tail);
2434 break;
2435 case 'C': /* possible CC */
2436 if (!strcmp (s+1,"C") && c == ' ' && *++tail)
2437 ret = crit_string (&pgm->cc,&tail);
2438 break;
2439 case 'D': /* possible DELETED */
2440 if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
2441 if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
2442 break;
2443 case 'F': /* possible FLAGGED, FROM */
2444 if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
2445 else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
2446 ret = crit_string (&pgm->from,&tail);
2447 break;
2448 case 'H': /* possible HEADER */
2449 if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
2450 (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
2451 (t = parse_astring (&v,&i,&c))) {
2452 for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
2453 *hdr = mail_newsearchheader (s,t);
2454 /* update tail, restore delimiter */
2455 *(tail = v ? v - 1 : t + i) = c;
2456 ret = T; /* success */
2457 }
2458 break;
2459 case 'K': /* possible KEYWORD */
2460 if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
2461 ret = crit_string (&pgm->keyword,&tail);
2462 break;
2463 case 'L':
2464 if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
2465 ret = crit_number (&pgm->larger,&tail);
2466 break;
2467 case 'N': /* possible NEW, NOT */
2468 if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
2469 else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
2470 for (not = &pgm->not; *not; not = &(*not)->next);
2471 *not = mail_newsearchpgmlist ();
2472 ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
2473 }
2474 break;
2475
2476 case 'O': /* possible OLD, ON */
2477 if (!strcmp (s+1,"LD")) ret = pgm->old = T;
2478 else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
2479 ret = crit_date (&pgm->on,&tail);
2480 else if (!strcmp (s+1,"R") && c == ' ') {
2481 for (or = &pgm->or; *or; or = &(*or)->next);
2482 *or = mail_newsearchor ();
2483 ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
2484 depth+1) &&
2485 (*tail == ' ') && *++tail &&
2486 parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
2487 }
2488 else if (!strcmp (s+1,"LDER") && c == ' ' && *++tail)
2489 ret = crit_number (&pgm->older,&tail);
2490 break;
2491 case 'R': /* possible RECENT */
2492 if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
2493 break;
2494 case 'S': /* possible SEEN, SINCE, SUBJECT */
2495 if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
2496 else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
2497 ret = crit_date (&pgm->sentbefore,&tail);
2498 else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
2499 ret = crit_date (&pgm->senton,&tail);
2500 else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
2501 ret = crit_date (&pgm->sentsince,&tail);
2502 else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
2503 ret = crit_date (&pgm->since,&tail);
2504 else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
2505 ret = crit_number (&pgm->smaller,&tail);
2506 else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
2507 ret = crit_string (&pgm->subject,&tail);
2508 break;
2509 case 'T': /* possible TEXT, TO */
2510 if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
2511 ret = crit_string (&pgm->text,&tail);
2512 else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
2513 ret = crit_string (&pgm->to,&tail);
2514 break;
2515
2516 case 'U': /* possible UID, UN* */
2517 if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
2518 if (*(set = &pgm->uid)){/* already a sequence? */
2519 /* silly, but not as silly as the client! */
2520 for (not = &pgm->not; *not; not = &(*not)->next);
2521 *not = mail_newsearchpgmlist ();
2522 set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
2523 }
2524 ret = crit_set (set,&tail,maxuid);
2525 }
2526 else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
2527 else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
2528 else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
2529 else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
2530 else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
2531 ret = crit_string (&pgm->unkeyword,&tail);
2532 else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
2533 break;
2534 case 'Y': /* possible YOUNGER */
2535 if (!strcmp (s+1,"OUNGER") && c == ' ' && *++tail)
2536 ret = crit_number (&pgm->younger,&tail);
2537 break;
2538 default: /* oh dear */
2539 break;
2540 }
2541 if (ret) { /* only bother if success */
2542 *del = c; /* restore delimiter */
2543 *arg = tail; /* update argument pointer */
2544 }
2545 }
2546 return ret; /* return more to come */
2547 }
2548
2549 /* Parse a search date criterion
2550 * Accepts: date to write into
2551 * pointer to argument text pointer
2552 * Returns: T if success, NIL if error
2553 */
2554
crit_date(unsigned short * date,unsigned char ** arg)2555 long crit_date (unsigned short *date,unsigned char **arg)
2556 {
2557 if (*date) return NIL; /* can't double this value */
2558 /* handle quoted form */
2559 if (**arg != '"') return crit_date_work (date,arg);
2560 (*arg)++; /* skip past opening quote */
2561 if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
2562 (*arg)++; /* skip closing quote */
2563 return T;
2564 }
2565
2566 /* Worker routine to parse a search date criterion
2567 * Accepts: date to write into
2568 * pointer to argument text pointer
2569 * Returns: T if success, NIL if error
2570 */
2571
crit_date_work(unsigned short * date,unsigned char ** arg)2572 long crit_date_work (unsigned short *date,unsigned char **arg)
2573 {
2574 int d,m,y;
2575 /* day */
2576 if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
2577 if (d == ' ') d = 0; /* leading space */
2578 else d -= '0'; /* first digit */
2579 if (isdigit (**arg)) { /* if a second digit */
2580 d *= 10; /* slide over first digit */
2581 d += *(*arg)++ - '0'; /* second digit */
2582 }
2583 if ((**arg == '-') && (y = *++(*arg))) {
2584 m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
2585 if ((y = *++(*arg))) {
2586 m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
2587 if ((y = *++(*arg))) {
2588 m += (y >= 'a' ? y - 'a' : y - 'A');
2589 switch (m) { /* determine the month */
2590 case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
2591 case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
2592 case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
2593 case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
2594 case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
2595 case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
2596 case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
2597 case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
2598 case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
2599 case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
2600 case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
2601 case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
2602 default: return NIL;
2603 }
2604 if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
2605 y = 0; /* init year */
2606 do {
2607 y *= 10; /* add this number */
2608 y += *(*arg)++ - '0';
2609 }
2610 while (isdigit (**arg));
2611 /* minimal validity check of date */
2612 if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
2613 /* time began on UNIX in 1970 */
2614 if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
2615 /* return value */
2616 *date = mail_shortdate (y - BASEYEAR,m,d);
2617 return T; /* success */
2618 }
2619 }
2620 }
2621 }
2622 }
2623 return NIL; /* else error */
2624 }
2625
2626 /* Parse a search set criterion
2627 * Accepts: set to write into
2628 * pointer to argument text pointer
2629 * maximum value permitted
2630 * Returns: T if success, NIL if error
2631 */
2632
crit_set(SEARCHSET ** set,unsigned char ** arg,unsigned long maxima)2633 long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima)
2634 {
2635 unsigned long i = 0;
2636 if (*set) return NIL; /* can't double this value */
2637 *set = mail_newsearchset (); /* instantiate a new search set */
2638 if (**arg == '*') { /* maxnum? */
2639 (*arg)++; /* skip past that number */
2640 (*set)->first = maxima;
2641 }
2642 else if (crit_number (&i,arg) && i) (*set)->first = i;
2643 else return NIL; /* bogon */
2644 switch (**arg) { /* decide based on delimiter */
2645 case ':': /* sequence range */
2646 i = 0; /* reset for crit_number() */
2647 if (*++(*arg) == '*') { /* maxnum? */
2648 (*arg)++; /* skip past that number */
2649 (*set)->last = maxima;
2650 }
2651 else if (crit_number (&i,arg) && i) {
2652 if (i < (*set)->first) { /* backwards range */
2653 (*set)->last = (*set)->first;
2654 (*set)->first = i;
2655 }
2656 else (*set)->last = i; /* set last number */
2657 }
2658 else return NIL; /* bogon */
2659 if (**arg != ',') break; /* drop into comma case if comma seen */
2660 case ',':
2661 (*arg)++; /* skip past delimiter */
2662 return crit_set (&(*set)->next,arg,maxima);
2663 default:
2664 break;
2665 }
2666 return T; /* return success */
2667 }
2668
2669 /* Parse a search number criterion
2670 * Accepts: number to write into
2671 * pointer to argument text pointer
2672 * Returns: T if success, NIL if error
2673 */
2674
crit_number(unsigned long * number,unsigned char ** arg)2675 long crit_number (unsigned long *number,unsigned char **arg)
2676 {
2677 /* can't double this value */
2678 if (*number || !isdigit (**arg)) return NIL;
2679 *number = 0;
2680 while (isdigit (**arg)) { /* found a digit? */
2681 *number *= 10; /* add a decade */
2682 *number += *(*arg)++ - '0'; /* add number */
2683 }
2684 return T;
2685 }
2686
2687
2688 /* Parse a search string criterion
2689 * Accepts: date to write into
2690 * pointer to argument text pointer
2691 * Returns: T if success, NIL if error
2692 */
2693
crit_string(STRINGLIST ** string,unsigned char ** arg)2694 long crit_string (STRINGLIST **string,unsigned char **arg)
2695 {
2696 unsigned long i;
2697 char c;
2698 char *s = parse_astring (arg,&i,&c);
2699 if (!s) return NIL;
2700 /* find tail of list */
2701 while (*string) string = &(*string)->next;
2702 *string = mail_newstringlist ();
2703 (*string)->text.data = (unsigned char *) fs_get (i + 1);
2704 memcpy ((*string)->text.data,s,i);
2705 (*string)->text.data[i] = '\0';
2706 (*string)->text.size = i;
2707 /* if end of arguments, wrap it up here */
2708 if (!*arg) *arg = (char *) (*string)->text.data + i;
2709 else (*--(*arg) = c); /* back up pointer, restore delimiter */
2710 return T;
2711 }
2712
2713 /* Fetch message data
2714 * Accepts: string of data items to be fetched (must be writeable)
2715 * UID fetch flag
2716 */
2717
2718 #define MAXFETCH 100
2719
fetch(char * t,unsigned long uid)2720 void fetch (char *t,unsigned long uid)
2721 {
2722 fetchfn_t f[MAXFETCH +2];
2723 void *fa[MAXFETCH + 2];
2724 int k;
2725 memset ((void *) f,NIL,sizeof (f));
2726 memset ((void *) fa,NIL,sizeof (fa));
2727 fetch_work (t,uid,f,fa); /* do the work */
2728 /* clean up arguments */
2729 for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
2730 }
2731
2732
2733 /* Fetch message data worker routine
2734 * Accepts: string of data items to be fetched (must be writeable)
2735 * UID fetch flag
2736 * function dispatch vector
2737 * function argument vector
2738 */
2739
fetch_work(char * t,unsigned long uid,fetchfn_t f[],void * fa[])2740 void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
2741 {
2742 unsigned char *s,*v;
2743 unsigned long i;
2744 unsigned long k = 0;
2745 BODY *b;
2746 int list = NIL;
2747 int parse_envs = NIL;
2748 int parse_bodies = NIL;
2749 if (uid) { /* need to fetch UIDs? */
2750 fa[k] = NIL; /* no argument */
2751 f[k++] = fetch_uid; /* push a UID fetch on the stack */
2752 }
2753
2754 /* process macros */
2755 if (!strcmp (ucase (t),"ALL"))
2756 strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
2757 else if (!strcmp (t,"FULL"))
2758 strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
2759 else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
2760 if (list = (*t == '(')) t++; /* skip open paren */
2761 /* parse attribute list */
2762 if (s = strtok_r (t," ",&sstate)) do {
2763 if (list && (i = strlen (s)) && (s[i-1] == ')')) {
2764 list = NIL; /* done with list */
2765 s[i-1] = '\0'; /* tie off last item */
2766 }
2767 fa[k] = NIL; /* default to no argument */
2768 if (!strcmp (s,"UID")) { /* no-op if implicit */
2769 if (!uid) f[k++] = fetch_uid;
2770 }
2771 else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
2772 else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
2773 else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
2774 else if (!strcmp (s,"ENVELOPE")) {
2775 parse_envs = T; /* we will need to parse envelopes */
2776 f[k++] = fetch_envelope;
2777 }
2778 else if (!strcmp (s,"BODY")) {
2779 parse_envs = parse_bodies = T;
2780 f[k++] = fetch_body;
2781 }
2782 else if (!strcmp (s,"BODYSTRUCTURE")) {
2783 parse_envs = parse_bodies = T;
2784 f[k++] = fetch_bodystructure;
2785 }
2786 else if (!strcmp (s,"RFC822")) {
2787 fa[k] = s[6] ? (void *) FT_PEEK : NIL;
2788 f[k++] = fetch_rfc822;
2789 }
2790 else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
2791 else if (!strcmp (s,"RFC822.TEXT")) {
2792 fa[k] = s[11] ? (void *) FT_PEEK : NIL;
2793 f[k++] = fetch_rfc822_text;
2794 }
2795
2796 else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10) ||
2797 !strncmp (s,"BINARY[",7) || !strncmp (s,"BINARY.PEEK[",12) ||
2798 !strncmp (s,"BINARY.SIZE[",12)) {
2799 TEXTARGS *ta = (TEXTARGS *)
2800 memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
2801 if (s[1] == 'I') { /* body or binary? */
2802 ta->binary = FTB_BINARY;/* binary */
2803 f[k] = fetch_body_part_binary;
2804 if (s[6] == '.') { /* wanted peek or size? */
2805 if (s[7] == 'P') ta->flags = FT_PEEK;
2806 else ta->binary |= FTB_SIZE;
2807 s += 12; /* skip to section specifier */
2808 }
2809 else s += 7; /* skip to section specifier */
2810 if (!isdigit (*s)) { /* make sure top-level digit */
2811 fs_give ((void **) &ta);
2812 response = badbin;
2813 return;
2814 }
2815 }
2816 else { /* body */
2817 f[k] = fetch_body_part_contents;
2818 if (s[4] == '.') { /* wanted peek? */
2819 ta->flags = FT_PEEK;
2820 s += 10; /* skip to section specifier */
2821 }
2822 else s += 5; /* skip to section specifier */
2823 }
2824 if (*(v = s) != ']') { /* non-empty section specifier? */
2825 if (isdigit (*v)) { /* have section specifier? */
2826 /* need envelopes and bodies */
2827 parse_envs = parse_bodies = T;
2828 while (isdigit (*v)) /* scan to end of section specifier */
2829 if ((*++v == '.') && isdigit (v[1])) v++;
2830 /* any IMAP4rev1 stuff following? */
2831 if ((*v == '.') && isalpha (v[1])) {
2832 if (ta->binary) { /* not if binary you don't */
2833 fs_give ((void **) &ta);
2834 response = badbin;
2835 return;
2836 }
2837 *v++ = '\0'; /* yes, tie off section specifier */
2838 if (!strncmp (v,"MIME",4)) {
2839 v += 4; /* found <section>.MIME */
2840 f[k] = fetch_body_part_mime;
2841 }
2842 }
2843 else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */
2844 fs_give ((void **) &ta);/* clean up */
2845 response = "%.80s BAD Syntax error in section specifier\015\012";
2846 return;
2847 }
2848 }
2849
2850 if (*v != ']') { /* IMAP4rev1 stuff here? */
2851 if (!strncmp (v,"HEADER",6)) {
2852 *v = '\0'; /* tie off in case top level */
2853 v += 6; /* found [<section>.]HEADER */
2854 f[k] = fetch_body_part_header;
2855 /* partial headers wanted? */
2856 if (!strncmp (v,".FIELDS",7)) {
2857 v += 7; /* yes */
2858 if (!strncmp (v,".NOT",4)) {
2859 v += 4; /* want to exclude named headers */
2860 ta->flags |= FT_NOT;
2861 }
2862 if (*v || !(v = strtok_r (NIL,"\015\012",&sstate)) ||
2863 !(ta->lines = parse_stringlist (&v,&list))) {
2864 fs_give ((void **) &ta);/* clean up */
2865 response = "%.80s BAD Syntax error in header fields\015\012";
2866 return;
2867 }
2868 ucase (v); /* make sure followup is ucase */
2869 }
2870 }
2871 else if (!strncmp (v,"TEXT",4)) {
2872 *v = '\0'; /* tie off in case top level */
2873 v += 4; /* found [<section>.]TEXT */
2874 f[k] = fetch_body_part_text;
2875 }
2876 else {
2877 fs_give ((void **) &ta);/* clean up */
2878 response = "%.80s BAD Unknown section text specifier\015\012";
2879 return;
2880 }
2881 }
2882 }
2883 /* tie off section */
2884 if (*v == ']') *v++ = '\0';
2885 else { /* bogon */
2886 if (ta->lines) mail_free_stringlist (&ta->lines);
2887 fs_give ((void **) &ta);/* clean up */
2888 response = "%.80s BAD Section specifier not terminated\015\012";
2889 return;
2890 }
2891
2892 if ((*v == '<') && /* partial specifier? */
2893 ((ta->binary & FTB_SIZE) ||
2894 !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) ||
2895 v) &&
2896 (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) &&
2897 (*v++ == '>')))) {
2898 if (ta->lines) mail_free_stringlist (&ta->lines);
2899 fs_give ((void **) &ta);
2900 response ="%.80s BAD Syntax error in partial text specifier\015\012";
2901 return;
2902 }
2903 switch (*v) { /* what's there now? */
2904 case ' ': /* more follows */
2905 sstate = v + 1; /* reset strtok mechanism */
2906 break;
2907 case '\0': /* none */
2908 break;
2909 case ')': /* end of list */
2910 if (list && !v[1]) { /* make sure of that */
2911 list = NIL;
2912 /* reset strtok mechanism */
2913 strtok_r (v," ",&sstate);
2914 break; /* all done */
2915 }
2916 /* otherwise it's a bogon, drop in */
2917 default: /* bogon */
2918 if (ta->lines) mail_free_stringlist (&ta->lines);
2919 fs_give ((void **) &ta);
2920 response = "%.80s BAD Syntax error after section specifier\015\012";
2921 return;
2922 }
2923 /* make copy of section specifier */
2924 if (s && *s) ta->section = cpystr (s);
2925 fa[k++] = (void *) ta; /* set argument */
2926 }
2927 else { /* unknown attribute */
2928 response = badatt;
2929 return;
2930 }
2931 } while ((s = strtok_r (NIL," ",&sstate)) && (k < MAXFETCH) && list);
2932 else {
2933 response = misarg; /* missing attribute list */
2934 return;
2935 }
2936
2937 if (s) { /* too many attributes? */
2938 response = "%.80s BAD Excessively complex FETCH attribute list\015\012";
2939 return;
2940 }
2941 if (list) { /* too many attributes? */
2942 response = "%.80s BAD Unterminated FETCH attribute list\015\012";
2943 return;
2944 }
2945 f[k] = NIL; /* tie off attribute list */
2946 /* c-client clobbers sequence, use spare */
2947 for (i = 1; i <= nmsgs; i++)
2948 mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
2949 /* for each requested message */
2950 for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) {
2951 /* kill if dying */
2952 if (state == LOGOUT) longjmp (jmpenv,1);
2953 if (mail_elt (stream,i)->spare) {
2954 /* parse envelope, set body, do warnings */
2955 if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
2956 quell_events = T; /* can't do any events now */
2957 PSOUT ("* "); /* leader */
2958 pnum (i);
2959 PSOUT (" FETCH (");
2960 (*f[0]) (i,fa[0]); /* do first attribute */
2961 /* for each subsequent attribute */
2962 for (k = 1; f[k] && (response != loseunknowncte); k++) {
2963 PBOUT (' '); /* delimit with space */
2964 (*f[k]) (i,fa[k]); /* do that attribute */
2965 }
2966 PSOUT (")\015\012"); /* trailer */
2967 quell_events = NIL; /* events alright now */
2968 }
2969 }
2970 }
2971
2972 /* Fetch message body structure (extensible)
2973 * Accepts: message number
2974 * extra argument
2975 */
2976
fetch_bodystructure(unsigned long i,void * args)2977 void fetch_bodystructure (unsigned long i,void *args)
2978 {
2979 BODY *body;
2980 mail_fetchstructure (stream,i,&body);
2981 PSOUT ("BODYSTRUCTURE ");
2982 pbodystructure (body); /* output body */
2983 }
2984
2985
2986 /* Fetch message body structure (non-extensible)
2987 * Accepts: message number
2988 * extra argument
2989 */
2990
2991
fetch_body(unsigned long i,void * args)2992 void fetch_body (unsigned long i,void *args)
2993 {
2994 BODY *body;
2995 mail_fetchstructure (stream,i,&body);
2996 PSOUT ("BODY "); /* output attribute */
2997 pbody (body); /* output body */
2998 }
2999
3000 /* Fetch body part MIME header
3001 * Accepts: message number
3002 * extra argument
3003 */
3004
fetch_body_part_mime(unsigned long i,void * args)3005 void fetch_body_part_mime (unsigned long i,void *args)
3006 {
3007 TEXTARGS *ta = (TEXTARGS *) args;
3008 if (i) { /* do work? */
3009 SIZEDTEXT st;
3010 unsigned long uid = mail_uid (stream,i);
3011 char *tmp = (char *) fs_get (100 + strlen (ta->section));
3012 sprintf (tmp,"BODY[%s.MIME]",ta->section);
3013 /* try to use remembered text */
3014 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
3015 else { /* get data */
3016 st.data = (unsigned char *)
3017 mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
3018 if (ta->first || ta->last) remember (uid,tmp,&st);
3019 }
3020 pbodypartstring (i,tmp,&st,NIL,ta);
3021 fs_give ((void **) &tmp);
3022 }
3023 else { /* clean up the arguments */
3024 fs_give ((void **) &ta->section);
3025 fs_give ((void **) &args);
3026 }
3027 }
3028
3029
3030 /* Fetch body part contents
3031 * Accepts: message number
3032 * extra argument
3033 */
3034
fetch_body_part_contents(unsigned long i,void * args)3035 void fetch_body_part_contents (unsigned long i,void *args)
3036 {
3037 TEXTARGS *ta = (TEXTARGS *) args;
3038 if (i) { /* do work? */
3039 SIZEDTEXT st;
3040 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
3041 unsigned long uid = mail_uid (stream,i);
3042 sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
3043 /* try to use remembered text */
3044 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
3045 /* get data */
3046 else if ((st.data = (unsigned char *)
3047 mail_fetch_body (stream,i,ta->section,&st.size,
3048 ta->flags | FT_RETURNSTRINGSTRUCT)) &&
3049 (ta->first || ta->last)) remember (uid,tmp,&st);
3050 pbodypartstring (i,tmp,&st,&stream->private.string,ta);
3051 fs_give ((void **) &tmp);
3052 }
3053 else { /* clean up the arguments */
3054 if (ta->section) fs_give ((void **) &ta->section);
3055 fs_give ((void **) &args);
3056 }
3057 }
3058
3059 /* Fetch body part binary
3060 * Accepts: message number
3061 * extra argument
3062 * Someday fix this to use stringstruct instead of memory
3063 */
3064
fetch_body_part_binary(unsigned long i,void * args)3065 void fetch_body_part_binary (unsigned long i,void *args)
3066 {
3067 TEXTARGS *ta = (TEXTARGS *) args;
3068 if (i) { /* do work? */
3069 SIZEDTEXT st,cst;
3070 BODY *body = mail_body (stream,i,ta->section);
3071 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
3072 unsigned long uid = mail_uid (stream,i);
3073 /* try to use remembered text */
3074 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
3075 else { /* get data */
3076 st.data = (unsigned char *)
3077 mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
3078 if (ta->first || ta->last) remember (uid,tmp,&st);
3079 }
3080 /* what encoding was used? */
3081 if (body) switch (body->encoding) {
3082 case ENCBASE64:
3083 if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break;
3084 fetch_uid (i,NIL); /* wrote a space, so must do something */
3085 if (lsterr) fs_give ((void **) &lsterr);
3086 lsterr = cpystr ("Undecodable BASE64 contents");
3087 response = loseunknowncte;
3088 fs_give ((void **) &tmp);
3089 return;
3090 case ENCQUOTEDPRINTABLE:
3091 if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break;
3092 fetch_uid (i,NIL); /* wrote a space, so must do something */
3093 if (lsterr) fs_give ((void **) &lsterr);
3094 lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents");
3095 response = loseunknowncte;
3096 fs_give ((void **) &tmp);
3097 return;
3098 case ENC7BIT: /* no need to convert any of these */
3099 case ENC8BIT:
3100 case ENCBINARY:
3101 cst.data = NIL; /* no converted data to free */
3102 break;
3103 default: /* unknown encoding, oops */
3104 fetch_uid (i,NIL); /* wrote a space, so must do something */
3105 if (lsterr) fs_give ((void **) &lsterr);
3106 lsterr = cpystr ("Unknown Content-Transfer-Encoding");
3107 response = loseunknowncte;
3108 fs_give ((void **) &tmp);
3109 return;
3110 }
3111 else {
3112 if (lsterr) fs_give ((void **) &lsterr);
3113 lsterr = cpystr ("Invalid body part");
3114 response = loseunknowncte;
3115 fs_give ((void **) &tmp);
3116 return;
3117 }
3118
3119 /* use decoded version if exists */
3120 if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT));
3121 if (ta->binary & FTB_SIZE) {/* just want size? */
3122 sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "",
3123 st.size);
3124 PSOUT (tmp);
3125 }
3126 else { /* no, blat binary data */
3127 int f = mail_elt (stream,i)->seen;
3128 if (st.data) { /* only if have useful data */
3129 /* partial specifier */
3130 if (ta->first || ta->last)
3131 sprintf (tmp,"BINARY[%s]<%lu> ",
3132 ta->section ? ta->section : "",ta->first);
3133 else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : "");
3134 /* in case first byte beyond end of text */
3135 if (st.size <= ta->first) st.size = ta->first = 0;
3136 else { /* offset and truncate */
3137 st.data += ta->first; /* move to desired position */
3138 st.size -= ta->first; /* reduced size */
3139 if (ta->last && (st.size > ta->last)) st.size = ta->last;
3140 }
3141 if (st.size) sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size);
3142 else strcat (tmp,"\"\"");
3143 PSOUT (tmp); /* write binary output */
3144 if (st.size && (PSOUTR (&st) == EOF)) ioerror(stdout,"writing binary");
3145 }
3146 else {
3147 sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : "");
3148 PSOUT (tmp);
3149 }
3150 changed_flags (i,f); /* write changed flags */
3151 }
3152 /* free converted data */
3153 if (cst.data) fs_give ((void **) &cst.data);
3154 fs_give ((void **) &tmp); /* and temporary string */
3155 }
3156 else { /* clean up the arguments */
3157 if (ta->section) fs_give ((void **) &ta->section);
3158 fs_give ((void **) &args);
3159 }
3160 }
3161
3162 /* Fetch MESSAGE/RFC822 body part header
3163 * Accepts: message number
3164 * extra argument
3165 */
3166
fetch_body_part_header(unsigned long i,void * args)3167 void fetch_body_part_header (unsigned long i,void *args)
3168 {
3169 TEXTARGS *ta = (TEXTARGS *) args;
3170 unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
3171 STRINGLIST *s;
3172 for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
3173 if (i) { /* do work? */
3174 SIZEDTEXT st;
3175 char *tmp = (char *) fs_get (len);
3176 PSOUT ("BODY[");
3177 /* output attribute */
3178 if (ta->section && *ta->section) {
3179 PSOUT (ta->section);
3180 PBOUT ('.');
3181 }
3182 PSOUT ("HEADER");
3183 if (ta->lines) {
3184 PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
3185 pastringlist (ta->lines);
3186 }
3187 strcpy (tmp,"]"); /* close section specifier */
3188 st.data = (unsigned char *) /* get data (no hope in using remember here) */
3189 mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
3190 pbodypartstring (i,tmp,&st,NIL,ta);
3191 fs_give ((void **) &tmp);
3192 }
3193 else { /* clean up the arguments */
3194 if (ta->lines) mail_free_stringlist (&ta->lines);
3195 if (ta->section) fs_give ((void **) &ta->section);
3196 fs_give ((void **) &args);
3197 }
3198 }
3199
3200 /* Fetch MESSAGE/RFC822 body part text
3201 * Accepts: message number
3202 * extra argument
3203 */
3204
fetch_body_part_text(unsigned long i,void * args)3205 void fetch_body_part_text (unsigned long i,void *args)
3206 {
3207 TEXTARGS *ta = (TEXTARGS *) args;
3208 if (i) { /* do work? */
3209 SIZEDTEXT st;
3210 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
3211 unsigned long uid = mail_uid (stream,i);
3212 /* output attribute */
3213 if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
3214 else strcpy (tmp,"BODY[TEXT]");
3215 /* try to use remembered text */
3216 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
3217 /* get data */
3218 else if ((st.data = (unsigned char *)
3219 mail_fetch_text (stream,i,ta->section,&st.size,
3220 ta->flags | FT_RETURNSTRINGSTRUCT)) &&
3221 (ta->first || ta->last)) remember (uid,tmp,&st);
3222 pbodypartstring (i,tmp,&st,&stream->private.string,ta);
3223 fs_give ((void **) &tmp);
3224 }
3225 else { /* clean up the arguments */
3226 if (ta->section) fs_give ((void **) &ta->section);
3227 fs_give ((void **) &args);
3228 }
3229 }
3230
3231
3232 /* Remember body part text for subsequent partial fetching
3233 * Accepts: message UID
3234 * body part id
3235 * text
3236 * string
3237 */
3238
remember(unsigned long uid,char * id,SIZEDTEXT * st)3239 void remember (unsigned long uid,char *id,SIZEDTEXT *st)
3240 {
3241 lastuid = uid; /* remember UID */
3242 if (lastid) fs_give ((void **) &lastid);
3243 lastid = cpystr (id); /* remember body part id */
3244 if (lastst.data) fs_give ((void **) &lastst.data);
3245 /* remember text */
3246 lastst.data = (unsigned char *)
3247 memcpy (fs_get (st->size + 1),st->data,st->size);
3248 lastst.size = st->size;
3249 }
3250
3251
3252 /* Fetch envelope
3253 * Accepts: message number
3254 * extra argument
3255 */
3256
fetch_envelope(unsigned long i,void * args)3257 void fetch_envelope (unsigned long i,void *args)
3258 {
3259 ENVELOPE *env = mail_fetchenvelope (stream,i);
3260 PSOUT ("ENVELOPE "); /* output attribute */
3261 penv (env); /* output envelope */
3262 }
3263
3264 /* Fetch flags
3265 * Accepts: message number
3266 * extra argument
3267 */
3268
fetch_flags(unsigned long i,void * args)3269 void fetch_flags (unsigned long i,void *args)
3270 {
3271 unsigned long u;
3272 char *t,tmp[MAILTMPLEN];
3273 int c = NIL;
3274 MESSAGECACHE *elt = mail_elt (stream,i);
3275 if (!elt->valid) { /* have valid flags yet? */
3276 sprintf (tmp,"%lu",i);
3277 mail_fetch_flags (stream,tmp,NIL);
3278 }
3279 PSOUT ("FLAGS ("); /* output attribute */
3280 /* output system flags */
3281 if (elt->recent) put_flag (&c,"\\Recent");
3282 if (elt->seen) put_flag (&c,"\\Seen");
3283 if (elt->deleted) put_flag (&c,"\\Deleted");
3284 if (elt->flagged) put_flag (&c,"\\Flagged");
3285 if (elt->answered) put_flag (&c,"\\Answered");
3286 if (elt->draft) put_flag (&c,"\\Draft");
3287 if (u = elt->user_flags) do /* any user flags? */
3288 if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
3289 while (u); /* until no more user flags */
3290 PBOUT (')'); /* end of flags */
3291 elt->spare2 = NIL; /* we've sent the update */
3292 }
3293
3294
3295 /* Output a flag
3296 * Accepts: pointer to current delimiter character
3297 * flag to output
3298 * Changes delimiter character to space
3299 */
3300
put_flag(int * c,char * s)3301 void put_flag (int *c,char *s)
3302 {
3303 if (*c) PBOUT (*c); /* put delimiter */
3304 PSOUT (s); /* dump flag */
3305 *c = ' '; /* change delimiter if necessary */
3306 }
3307
3308
3309 /* Output flags if was unseen
3310 * Accepts: message number
3311 * prior value of Seen flag
3312 */
3313
changed_flags(unsigned long i,int f)3314 void changed_flags (unsigned long i,int f)
3315 {
3316 /* was unseen, now seen? */
3317 if (!f && mail_elt (stream,i)->seen) {
3318 PBOUT (' '); /* yes, delimit with space */
3319 fetch_flags (i,NIL); /* output flags */
3320 }
3321 }
3322
3323 /* Fetch message internal date
3324 * Accepts: message number
3325 * extra argument
3326 */
3327
fetch_internaldate(unsigned long i,void * args)3328 void fetch_internaldate (unsigned long i,void *args)
3329 {
3330 char tmp[MAILTMPLEN];
3331 MESSAGECACHE *elt = mail_elt (stream,i);
3332 if (!elt->day) { /* have internal date yet? */
3333 sprintf (tmp,"%lu",i);
3334 mail_fetch_fast (stream,tmp,NIL);
3335 }
3336 PSOUT ("INTERNALDATE \"");
3337 PSOUT (mail_date (tmp,elt));
3338 PBOUT ('"');
3339 }
3340
3341
3342 /* Fetch unique identifier
3343 * Accepts: message number
3344 * extra argument
3345 */
3346
fetch_uid(unsigned long i,void * args)3347 void fetch_uid (unsigned long i,void *args)
3348 {
3349 PSOUT ("UID ");
3350 pnum (mail_uid (stream,i));
3351 }
3352
3353 /* Fetch complete RFC-822 format message
3354 * Accepts: message number
3355 * extra argument
3356 */
3357
fetch_rfc822(unsigned long i,void * args)3358 void fetch_rfc822 (unsigned long i,void *args)
3359 {
3360 if (i) { /* do work? */
3361 int f = mail_elt (stream,i)->seen;
3362 #if 0
3363 SIZEDTEXT st;
3364 st.data = (unsigned char *)
3365 mail_fetch_message (stream,i,&st.size,(long) args);
3366 pbodypartstring (i,"RFC822",&st,NIL,NIL);
3367 #else
3368 /* Yes, this version is bletcherous, but mail_fetch_message() requires
3369 too much memory */
3370 SIZEDTEXT txt,hdr;
3371 char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
3372 hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
3373 txt.data = (unsigned char *)
3374 mail_fetch_text (stream,i,NIL,&txt.size,
3375 ((long) args) | FT_RETURNSTRINGSTRUCT);
3376 PSOUT ("RFC822 {");
3377 pnum (hdr.size + txt.size);
3378 PSOUT ("}\015\012");
3379 ptext (&hdr,NIL);
3380 ptext (&txt,&stream->private.string);
3381 fs_give ((void **) &hdr.data);
3382 #endif
3383 changed_flags (i,f); /* output changed flags */
3384 }
3385 }
3386
3387
3388 /* Fetch RFC-822 header
3389 * Accepts: message number
3390 * extra argument
3391 */
3392
fetch_rfc822_header(unsigned long i,void * args)3393 void fetch_rfc822_header (unsigned long i,void *args)
3394 {
3395 SIZEDTEXT st;
3396 st.data = (unsigned char *)
3397 mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
3398 pbodypartstring (i,"RFC822.HEADER",&st,NIL,NIL);
3399 }
3400
3401
3402 /* Fetch RFC-822 message length
3403 * Accepts: message number
3404 * extra argument
3405 */
3406
fetch_rfc822_size(unsigned long i,void * args)3407 void fetch_rfc822_size (unsigned long i,void *args)
3408 {
3409 char tmp[MAILTMPLEN];
3410 MESSAGECACHE *elt = mail_elt (stream,i);
3411 if (!elt->rfc822_size) { /* have message size yet? */
3412 sprintf (tmp,"%lu",i);
3413 mail_fetch_fast (stream,tmp,NIL);
3414 }
3415 PSOUT ("RFC822.SIZE ");
3416 pnum (elt->rfc822_size);
3417 }
3418
3419 /* Fetch RFC-822 text only
3420 * Accepts: message number
3421 * extra argument
3422 */
3423
fetch_rfc822_text(unsigned long i,void * args)3424 void fetch_rfc822_text (unsigned long i,void *args)
3425 {
3426 if (i) { /* do work? */
3427 int f = mail_elt (stream,i)->seen;
3428 SIZEDTEXT st;
3429 st.data = (unsigned char *)
3430 mail_fetch_text (stream,i,NIL,&st.size,
3431 ((long) args) | FT_RETURNSTRINGSTRUCT);
3432 pbodypartstring (i,"RFC822.TEXT",&st,&stream->private.string,NIL);
3433 }
3434 }
3435
3436 /* Print envelope
3437 * Accepts: body
3438 */
3439
penv(ENVELOPE * env)3440 void penv (ENVELOPE *env)
3441 {
3442 PBOUT ('('); /* delimiter */
3443 if (env) { /* only if there is an envelope */
3444 pnstring (env->date); /* output envelope fields */
3445 PBOUT (' ');
3446 pnstring (env->subject);
3447 PBOUT (' ');
3448 paddr (env->from);
3449 PBOUT (' ');
3450 paddr (env->sender);
3451 PBOUT (' ');
3452 paddr (env->reply_to);
3453 PBOUT (' ');
3454 paddr (env->to);
3455 PBOUT (' ');
3456 paddr (env->cc);
3457 PBOUT (' ');
3458 paddr (env->bcc);
3459 PBOUT (' ');
3460 pnstring (env->in_reply_to);
3461 PBOUT (' ');
3462 pnstring (env->message_id);
3463 }
3464 /* no envelope */
3465 else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
3466 PBOUT (')'); /* end of envelope */
3467 }
3468
3469 /* Print body structure (extensible)
3470 * Accepts: body
3471 */
3472
pbodystructure(BODY * body)3473 void pbodystructure (BODY *body)
3474 {
3475 PBOUT ('('); /* delimiter */
3476 if (body) { /* only if there is a body */
3477 PART *part;
3478 /* multipart type? */
3479 if (body->type == TYPEMULTIPART) {
3480 /* print each part */
3481 if (part = body->nested.part)
3482 for (; part; part = part->next) pbodystructure (&(part->body));
3483 else pbodystructure (NIL);
3484 PBOUT (' '); /* space delimiter */
3485 pstring (body->subtype); /* subtype */
3486 PBOUT (' ');
3487 pparam (body->parameter); /* multipart body extension data */
3488 PBOUT (' ');
3489 if (body->disposition.type) {
3490 PBOUT ('(');
3491 pstring (body->disposition.type);
3492 PBOUT (' ');
3493 pparam (body->disposition.parameter);
3494 PBOUT (')');
3495 }
3496 else PSOUT ("NIL");
3497 PBOUT (' ');
3498 pnstringorlist (body->language);
3499 PBOUT (' ');
3500 pnstring (body->location);
3501 }
3502
3503 else { /* non-multipart body type */
3504 pstring ((char *) body_types[body->type]);
3505 PBOUT (' ');
3506 pstring (body->subtype);
3507 PBOUT (' ');
3508 pparam (body->parameter);
3509 PBOUT (' ');
3510 pnstring (body->id);
3511 PBOUT (' ');
3512 pnstring (body->description);
3513 PBOUT (' ');
3514 pstring ((char *) body_encodings[body->encoding]);
3515 PBOUT (' ');
3516 pnum (body->size.bytes);
3517 switch (body->type) { /* extra stuff depends upon body type */
3518 case TYPEMESSAGE:
3519 /* can't do this if not RFC822 */
3520 if (strcmp (body->subtype,"RFC822")) break;
3521 PBOUT (' ');
3522 penv (body->nested.msg->env);
3523 PBOUT (' ');
3524 pbodystructure (body->nested.msg->body);
3525 case TYPETEXT:
3526 PBOUT (' ');
3527 pnum (body->size.lines);
3528 break;
3529 default:
3530 break;
3531 }
3532 PBOUT (' ');
3533 pnstring (body->md5);
3534 PBOUT (' ');
3535 if (body->disposition.type) {
3536 PBOUT ('(');
3537 pstring (body->disposition.type);
3538 PBOUT (' ');
3539 pparam (body->disposition.parameter);
3540 PBOUT (')');
3541 }
3542 else PSOUT ("NIL");
3543 PBOUT (' ');
3544 pnstringorlist (body->language);
3545 PBOUT (' ');
3546 pnstring (body->location);
3547 }
3548 }
3549 /* no body */
3550 else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL");
3551 PBOUT (')'); /* end of body */
3552 }
3553
3554 /* Print body (non-extensible)
3555 * Accepts: body
3556 */
3557
pbody(BODY * body)3558 void pbody (BODY *body)
3559 {
3560 PBOUT ('('); /* delimiter */
3561 if (body) { /* only if there is a body */
3562 PART *part;
3563 /* multipart type? */
3564 if (body->type == TYPEMULTIPART) {
3565 /* print each part */
3566 if (part = body->nested.part)
3567 for (; part; part = part->next) pbody (&(part->body));
3568 else pbody (NIL);
3569 PBOUT (' '); /* space delimiter */
3570 pstring (body->subtype); /* and finally the subtype */
3571 }
3572 else { /* non-multipart body type */
3573 pstring ((char *) body_types[body->type]);
3574 PBOUT (' ');
3575 pstring (body->subtype);
3576 PBOUT (' ');
3577 pparam (body->parameter);
3578 PBOUT (' ');
3579 pnstring (body->id);
3580 PBOUT (' ');
3581 pnstring (body->description);
3582 PBOUT (' ');
3583 pstring ((char *) body_encodings[body->encoding]);
3584 PBOUT (' ');
3585 pnum (body->size.bytes);
3586 switch (body->type) { /* extra stuff depends upon body type */
3587 case TYPEMESSAGE:
3588 /* can't do this if not RFC822 */
3589 if (strcmp (body->subtype,"RFC822")) break;
3590 PBOUT (' ');
3591 penv (body->nested.msg ? body->nested.msg->env : NIL);
3592 PBOUT (' ');
3593 pbody (body->nested.msg ? body->nested.msg->body : NIL);
3594 case TYPETEXT:
3595 PBOUT (' ');
3596 pnum (body->size.lines);
3597 break;
3598 default:
3599 break;
3600 }
3601 }
3602 }
3603 /* no body */
3604 else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0");
3605 PBOUT (')'); /* end of body */
3606 }
3607
3608 /* Print parameter list
3609 * Accepts: paramter
3610 */
3611
pparam(PARAMETER * param)3612 void pparam (PARAMETER *param)
3613 {
3614 if (param) { /* one specified? */
3615 PBOUT ('(');
3616 do {
3617 pstring (param->attribute);
3618 PBOUT (' ');
3619 pstring (param->value);
3620 if (param = param->next) PBOUT (' ');
3621 } while (param);
3622 PBOUT (')'); /* end of parameters */
3623 }
3624 else PSOUT ("NIL");
3625 }
3626
3627
3628 /* Print address list
3629 * Accepts: address list
3630 */
3631
paddr(ADDRESS * a)3632 void paddr (ADDRESS *a)
3633 {
3634 if (a) { /* have anything in address? */
3635 PBOUT ('('); /* open the address list */
3636 do { /* for each address */
3637 PBOUT ('('); /* open the address */
3638 pnstring (a->personal); /* personal name */
3639 PBOUT (' ');
3640 pnstring (a->adl); /* at-domain-list */
3641 PBOUT (' ');
3642 pnstring (a->mailbox); /* mailbox */
3643 PBOUT (' ');
3644 pnstring (a->host); /* domain name of mailbox's host */
3645 PBOUT (')'); /* terminate address */
3646 } while (a = a->next); /* until end of address */
3647 PBOUT (')'); /* close address list */
3648 }
3649 else PSOUT ("NIL"); /* empty address */
3650 }
3651
3652 /* Print set
3653 * Accepts: set
3654 */
3655
pset(SEARCHSET ** set)3656 void pset (SEARCHSET **set)
3657 {
3658 SEARCHSET *cur = *set;
3659 while (cur) { /* while there's a set to do */
3660 pnum (cur->first); /* output first value */
3661 if (cur->last) { /* if range, output second value of range */
3662 PBOUT (':');
3663 pnum (cur->last);
3664 }
3665 if (cur = cur->next) PBOUT (',');
3666 }
3667 mail_free_searchset (set); /* flush set */
3668 }
3669
3670
3671 /* Print number
3672 * Accepts: number
3673 */
3674
pnum(unsigned long i)3675 void pnum (unsigned long i)
3676 {
3677 char tmp[MAILTMPLEN];
3678 sprintf (tmp,"%lu",i);
3679 PSOUT (tmp);
3680 }
3681
3682
3683 /* Print string
3684 * Accepts: string
3685 */
3686
pstring(char * s)3687 void pstring (char *s)
3688 {
3689 SIZEDTEXT st;
3690 st.data = (unsigned char *) s;/* set up sized text */
3691 st.size = strlen (s);
3692 psizedstring (&st,NIL); /* print string */
3693 }
3694
3695
3696 /* Print nstring
3697 * Accepts: string or NIL
3698 */
3699
pnstring(char * s)3700 void pnstring (char *s)
3701 {
3702 if (s) pstring (s); /* print string */
3703 else PSOUT ("NIL");
3704 }
3705
3706
3707 /* Print atom or string
3708 * Accepts: astring
3709 */
3710
pastring(char * s)3711 void pastring (char *s)
3712 {
3713 char *t;
3714 if (!*s) PSOUT ("\"\""); /* empty string */
3715 else { /* see if atom */
3716 for (t = s; (*t > ' ') && !(*t & 0x80) &&
3717 (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') &&
3718 (*t != '{') && (*t != '%') && (*t != '*'); t++);
3719 if (*t) pstring (s); /* not an atom */
3720 else PSOUT (s); /* else plop down as atomic */
3721 }
3722 }
3723
3724 /* Print sized text as quoted
3725 * Accepts: sized text
3726 */
3727
psizedquoted(SIZEDTEXT * s)3728 void psizedquoted (SIZEDTEXT *s)
3729 {
3730 PBOUT ('"'); /* use quoted string */
3731 ptext (s,NIL);
3732 PBOUT ('"');
3733 }
3734
3735
3736 /* Print sized text as literal
3737 * Accepts: sized text
3738 */
3739
psizedliteral(SIZEDTEXT * s,STRING * st)3740 void psizedliteral (SIZEDTEXT *s,STRING *st)
3741 {
3742 PBOUT ('{'); /* print literal size */
3743 pnum (s->size);
3744 PSOUT ("}\015\012");
3745 ptext (s,st);
3746 }
3747
3748 /* Print sized text as literal or quoted string
3749 * Accepts: sized text
3750 * alternative stringstruct of text
3751 */
3752
psizedstring(SIZEDTEXT * s,STRING * st)3753 void psizedstring (SIZEDTEXT *s,STRING *st)
3754 {
3755 unsigned char c;
3756 unsigned long i;
3757
3758 if (s->data) { /* if text, check if must use literal */
3759 for (i = 0; ((i < s->size) && ((c = s->data[i]) & 0xe0) &&
3760 !(c & 0x80) && (c != '"') && (c != '\\')); ++i);
3761 /* must use literal if not all QUOTED-CHAR */
3762 if (i < s->size) psizedliteral (s,st);
3763 else psizedquoted (s);
3764 }
3765 else psizedliteral (s,st);
3766 }
3767
3768
3769 /* Print sized text as literal or quoted string
3770 * Accepts: sized text
3771 */
3772
psizedastring(SIZEDTEXT * s)3773 void psizedastring (SIZEDTEXT *s)
3774 {
3775 unsigned long i;
3776 unsigned int atomp = s->size ? T : NIL;
3777 for (i = 0; i < s->size; i++){/* check if must use literal */
3778 if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
3779 (s->data[i] == '"') || (s->data[i] == '\\')) {
3780 psizedliteral (s,NIL);
3781 return;
3782 }
3783 else switch (s->data[i]) { /* else see if any atom-specials */
3784 case '(': case ')': case '{': case ' ':
3785 case '%': case '*': /* list-wildcards */
3786 case ']': /* resp-specials */
3787 /* CTL and quoted-specials in literal check */
3788 atomp = NIL; /* not an atom */
3789 }
3790 }
3791 if (atomp) ptext (s,NIL); /* print as atom */
3792 else psizedquoted (s); /* print as quoted string */
3793 }
3794
3795 /* Print string list
3796 * Accepts: string list
3797 */
3798
pastringlist(STRINGLIST * s)3799 void pastringlist (STRINGLIST *s)
3800 {
3801 PBOUT ('('); /* start list */
3802 do {
3803 psizedastring (&s->text); /* output list member */
3804 if (s->next) PBOUT (' ');
3805 } while (s = s->next);
3806 PBOUT (')'); /* terminate list */
3807 }
3808
3809
3810 /* Print nstring or list of strings
3811 * Accepts: string / string list
3812 */
3813
pnstringorlist(STRINGLIST * s)3814 void pnstringorlist (STRINGLIST *s)
3815 {
3816 if (!s) PSOUT ("NIL"); /* no argument given */
3817 else if (s->next) { /* output list as list of strings*/
3818 PBOUT ('('); /* start list */
3819 do { /* output list member */
3820 psizedstring (&s->text,NIL);
3821 if (s->next) PBOUT (' ');
3822 } while (s = s->next);
3823 PBOUT (')'); /* terminate list */
3824 }
3825 /* and single-element list as string */
3826 else psizedstring (&s->text,NIL);
3827 }
3828
3829 /* Print body part string
3830 * Accepts: message number
3831 * body part id (note: must have space at end to append stuff)
3832 * sized text of string
3833 * alternative stringstruct of string
3834 * text printing arguments
3835 */
3836
pbodypartstring(unsigned long msgno,char * id,SIZEDTEXT * st,STRING * bs,TEXTARGS * ta)3837 void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
3838 TEXTARGS *ta)
3839 {
3840 int f = mail_elt (stream,msgno)->seen;
3841 /* ignore stringstruct if non-initialized */
3842 if (bs && !bs->curpos) bs = NIL;
3843 if (ta && st->size) { /* only if have useful data */
3844 /* partial specifier */
3845 if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
3846 /* in case first byte beyond end of text */
3847 if (st->size <= ta->first) st->size = ta->first = 0;
3848 else {
3849 if (st->data) { /* offset and truncate */
3850 st->data += ta->first; /* move to desired position */
3851 st->size -= ta->first; /* reduced size */
3852 }
3853 else if (bs && (SIZE (bs) >= ta->first))
3854 SETPOS (bs,ta->first + GETPOS (bs));
3855 else st->size = 0; /* shouldn't happen */
3856 if (ta->last && (st->size > ta->last)) st->size = ta->last;
3857 }
3858 }
3859 PSOUT (id);
3860 PBOUT (' ');
3861 psizedstring (st,bs); /* output string */
3862 changed_flags (msgno,f); /* and changed flags */
3863 }
3864
3865 /* RFC 3501 technically forbids NULs in literals. Normally, the delivering
3866 * MTA would take care of MIME converting the message text so that it is
3867 * NUL-free. If it doesn't, then we have the choice of either violating
3868 * IMAP by sending NULs, corrupting the data, or going to lots of work to do
3869 * MIME conversion in the IMAP server.
3870 */
3871
3872 /* Print raw sized text
3873 * Accepts: sizedtext
3874 */
3875
ptext(SIZEDTEXT * txt,STRING * st)3876 void ptext (SIZEDTEXT *txt,STRING *st)
3877 {
3878 unsigned char c,*s;
3879 unsigned long i = txt->size;
3880 if (s = txt->data) while (i && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))) --i;
3881 else if (st) while (i && (PBOUT ((c = SNX (st)) ? c : 0x80) != EOF)) --i;
3882 /* failed to complete? */
3883 if (i) ioerror (stdout,"writing text");
3884 }
3885
3886 /* Print thread
3887 * Accepts: thread
3888 */
3889
pthread(THREADNODE * thr)3890 void pthread (THREADNODE *thr)
3891 {
3892 THREADNODE *t;
3893 while (thr) { /* for each branch */
3894 PBOUT ('('); /* open branch */
3895 if (thr->num) { /* first node message number */
3896 pnum (thr->num);
3897 if (t = thr->next) { /* any subsequent nodes? */
3898 PBOUT (' ');
3899 while (t) { /* for each subsequent node */
3900 if (t->branch) { /* branches? */
3901 pthread (t); /* yes, recurse to do branch */
3902 t = NIL; /* done */
3903 }
3904 else { /* just output this number */
3905 pnum (t->num);
3906 t = t->next; /* and do next message */
3907 }
3908 if (t) PBOUT (' '); /* delimit if more to come */
3909 }
3910 }
3911 }
3912 else pthread (thr->next); /* nest for dummy */
3913 PBOUT (')'); /* done with this branch */
3914 thr = thr->branch; /* do next branch */
3915 }
3916 }
3917
3918 /* Print capabilities
3919 * Accepts: option flag
3920 */
3921
pcapability(long flag)3922 void pcapability (long flag)
3923 {
3924 unsigned long i;
3925 char *s;
3926 struct stat sbuf;
3927 AUTHENTICATOR *auth;
3928 THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
3929 /* always output protocol level */
3930 PSOUT ("CAPABILITY IMAP4REV1 I18NLEVEL=1 LITERAL+");
3931 #ifdef NETSCAPE_BRAIN_DAMAGE
3932 PSOUT (" X-NETSCAPE");
3933 #endif
3934 if (flag >= 0) { /* want post-authentication capabilities? */
3935 PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT");
3936 #ifdef ESEARCH
3937 PSOUT (" ESEARCH");
3938 #endif
3939 PSOUT (" WITHIN SORT");
3940 while (thr) { /* threaders */
3941 PSOUT (" THREAD=");
3942 PSOUT (thr->name);
3943 thr = thr->next;
3944 }
3945 if (!anonymous) PSOUT (" MULTIAPPEND");
3946 PSOUT (" SCAN"); /* private extension */
3947 }
3948 if (flag <= 0) { /* want pre-authentication capabilities? */
3949 PSOUT (" SASL-IR LOGIN-REFERRALS");
3950 if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
3951 else PSOUT (" STARTTLS");
3952 /* disable plaintext */
3953 if (!(i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)))
3954 PSOUT (" LOGINDISABLED");
3955 for (auth = mail_lookup_auth (1); auth; auth = auth->next)
3956 if (auth->server && !(auth->flags & AU_DISABLE) &&
3957 !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
3958 PSOUT (" AUTH=");
3959 PSOUT (auth->name);
3960 }
3961 if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
3962 }
3963 }
3964
3965 /* Anonymous users may only use these mailboxes in these namespaces */
3966
3967 char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
3968
3969
3970 /* Check if mailbox name is OK
3971 * Accepts: reference name
3972 * mailbox name
3973 */
3974
nameok(char * ref,char * name)3975 long nameok (char *ref,char *name)
3976 {
3977 int i;
3978 unsigned char *s,*t;
3979 if (!name) return NIL; /* failure if missing name */
3980 if (!anonymous) return T; /* otherwise OK if not anonymous */
3981 /* validate reference */
3982 if (ref && ((*ref == '#') || (*ref == '{')))
3983 for (i = 0; oktab[i]; i++) {
3984 for (s = ref, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
3985 if (!*t) { /* reference OK */
3986 if (*name == '#') break;/* check name if override */
3987 else return T; /* otherwise done */
3988 }
3989 }
3990 /* ordinary names are OK */
3991 if ((*name != '#') && (*name != '{')) return T;
3992 for (i = 0; oktab[i]; i++) { /* validate mailbox */
3993 for (s = name, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
3994 if (!*t) return T; /* name is OK */
3995 }
3996 response = "%.80s NO Anonymous may not %.80s this name\015\012";
3997 return NIL;
3998 }
3999
4000
4001 /* Convert possible BBoard name to actual name
4002 * Accepts: command
4003 * mailbox name
4004 * Returns: maibox name
4005 */
4006
bboardname(char * cmd,char * name)4007 char *bboardname (char *cmd,char *name)
4008 {
4009 if (cmd[0] == 'B') { /* want bboard? */
4010 char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
4011 sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
4012 name = s;
4013 }
4014 return name;
4015 }
4016
4017 /* Test if name is news proxy
4018 * Accepts: name
4019 * Returns: T if news proxy, NIL otherwise
4020 */
4021
isnewsproxy(char * name)4022 long isnewsproxy (char *name)
4023 {
4024 return (nntpproxy && (name[0] == '#') &&
4025 ((name[1] == 'N') || (name[1] == 'n')) &&
4026 ((name[2] == 'E') || (name[2] == 'e')) &&
4027 ((name[3] == 'W') || (name[3] == 'w')) &&
4028 ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ?
4029 LONGT : NIL;
4030 }
4031
4032
4033 /* News proxy generate canonical pattern
4034 * Accepts: reference
4035 * pattern
4036 * buffer to return canonical pattern
4037 * Returns: T on success with pattern in buffer, NIL on failure
4038 */
4039
newsproxypattern(char * ref,char * pat,char * pattern,long flag)4040 long newsproxypattern (char *ref,char *pat,char *pattern,long flag)
4041 {
4042 if (!nntpproxy) return NIL;
4043 if (strlen (ref) > NETMAXMBX) {
4044 sprintf (pattern,"Invalid reference specification: %.80s",ref);
4045 mm_log (pattern,ERROR);
4046 return NIL;
4047 }
4048 if (strlen (pat) > NETMAXMBX) {
4049 sprintf (pattern,"Invalid pattern specification: %.80s",pat);
4050 mm_log (pattern,ERROR);
4051 return NIL;
4052 }
4053 if (flag) { /* prepend proxy specifier */
4054 sprintf (pattern,"{%.300s/nntp}",nntpproxy);
4055 pattern += strlen (pattern);
4056 }
4057 if (*ref) { /* have a reference */
4058 strcpy (pattern,ref); /* copy reference to pattern */
4059 /* # overrides mailbox field in reference */
4060 if (*pat == '#') strcpy (pattern,pat);
4061 /* pattern starts, reference ends, with . */
4062 else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
4063 strcat (pattern,pat + 1); /* append, omitting one of the period */
4064 else strcat (pattern,pat); /* anything else is just appended */
4065 }
4066 else strcpy (pattern,pat); /* just have basic name */
4067 return isnewsproxy (pattern);
4068 }
4069
4070 /* IMAP4rev1 Authentication responder
4071 * Accepts: challenge
4072 * length of challenge
4073 * pointer to response length return location if non-NIL
4074 * Returns: response
4075 */
4076
4077 #define RESPBUFLEN 8*MAILTMPLEN
4078
imap_responder(void * challenge,unsigned long clen,unsigned long * rlen)4079 char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
4080 {
4081 unsigned long i,j;
4082 unsigned char *t,resp[RESPBUFLEN];
4083 if (initial) { /* initial response given? */
4084 if (clen) return NIL; /* not permitted */
4085 /* set up response */
4086 i = strlen ((char *) (t = initial));
4087 initial = NIL; /* no more initial response */
4088 if ((*t == '=') && !t[1]) { /* SASL-IR does this for 0-length response */
4089 if (rlen) *rlen = 0; /* set length zero if empty */
4090 return cpystr (""); /* and return empty string as response */
4091 }
4092 }
4093 else { /* issue challenge, get response */
4094 PSOUT ("+ ");
4095 for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
4096 if (t[j] > ' ') PBOUT (t[j]);
4097 fs_give ((void **) &t);
4098 CRLF;
4099 PFLUSH (); /* dump output buffer */
4100 /* slurp response buffer */
4101 slurp ((char *) resp,RESPBUFLEN,INPUTTIMEOUT);
4102 if (!(t = (unsigned char *) strchr ((char *) resp,'\012')))
4103 return (char *) flush ();
4104 if (t[-1] == '\015') --t; /* remove CR */
4105 *t = '\0'; /* tie off buffer */
4106 if (resp[0] == '*') {
4107 cancelled = T;
4108 return NIL;
4109 }
4110 i = t - resp; /* length of response */
4111 t = resp; /* set up for return call */
4112 }
4113 return (i % 4) ? NIL : /* return if valid BASE64 */
4114 (char *) rfc822_base64 (t,i,rlen ? rlen : &i);
4115 }
4116
4117 /* Proxy copy across mailbox formats
4118 * Accepts: mail stream
4119 * sequence to copy on this stream
4120 * destination mailbox
4121 * option flags
4122 * Returns: T if success, else NIL
4123 */
4124
proxycopy(MAILSTREAM * stream,char * sequence,char * mailbox,long options)4125 long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
4126 {
4127 MAILSTREAM *ts;
4128 STRING st;
4129 MSGDATA md;
4130 SEARCHSET *set;
4131 char tmp[MAILTMPLEN];
4132 unsigned long i,j;
4133 md.stream = stream;
4134 md.msgno = 0;
4135 md.flags = md.date = NIL;
4136 md.message = &st;
4137 /* Currently ignores CP_MOVE and CP_DEBUG */
4138 if (!((options & CP_UID) ? /* validate sequence */
4139 mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
4140 return NIL;
4141 response = win; /* cancel previous errors */
4142 if (lsterr) fs_give ((void **) &lsterr);
4143 /* c-client clobbers sequence, use spare */
4144 for (i = 1,j = 0,set = mail_newsearchset (); i <= nmsgs; i++)
4145 if (mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) {
4146 mail_append_set (set,mail_uid (stream,i));
4147 if (!j) md.msgno = (j = i) - 1;
4148 }
4149 /* only if at least one message to copy */
4150 if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) {
4151 response = trycreate ? losetry : lose;
4152 if (set) mail_free_searchset (&set);
4153 return NIL;
4154 }
4155 if (caset) csset = set; /* set for return value now */
4156 else if (set) mail_free_searchset (&set);
4157 response = win; /* stomp any previous babble */
4158 if (md.msgno) { /* get new driver name if was dummy */
4159 sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
4160 stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
4161 ts->dtb->name : "unknown");
4162 mm_log (tmp,NIL);
4163 }
4164 return LONGT;
4165 }
4166
4167 /* Proxy append message callback
4168 * Accepts: MAIL stream
4169 * append data package
4170 * pointer to return initial flags
4171 * pointer to return message internal date
4172 * pointer to return stringstruct of message or NIL to stop
4173 * Returns: T if success (have message or stop), NIL if error
4174 */
4175
proxy_append(MAILSTREAM * stream,void * data,char ** flags,char ** date,STRING ** message)4176 long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
4177 STRING **message)
4178 {
4179 MESSAGECACHE *elt;
4180 unsigned long i;
4181 char *s,*t,tmp[MAILTMPLEN];
4182 MSGDATA *md = (MSGDATA *) data;
4183 if (md->flags) fs_give ((void **) &md->flags);
4184 if (md->date) fs_give ((void **) &md->date);
4185 *message = NIL; /* assume all done */
4186 *flags = *date = NIL;
4187 while (++md->msgno <= nmsgs)
4188 if ((elt = mail_elt (md->stream,md->msgno))->spare) {
4189 if (!(elt->valid && elt->day)) {
4190 sprintf (tmp,"%lu",md->msgno);
4191 mail_fetch_fast (md->stream,tmp,NIL);
4192 }
4193 memset (s = tmp,0,MAILTMPLEN);
4194 /* copy flags */
4195 if (elt->seen) strcat (s," \\Seen");
4196 if (elt->deleted) strcat (s," \\Deleted");
4197 if (elt->flagged) strcat (s," \\Flagged");
4198 if (elt->answered) strcat (s," \\Answered");
4199 if (elt->draft) strcat (s," \\Draft");
4200 if (i = elt->user_flags) do
4201 if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t &&
4202 (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
4203 *s++ = ' '; /* space delimiter */
4204 strcpy (s,t);
4205 } while (i); /* until no more user flags */
4206 *message = md->message; /* set up return values */
4207 *flags = md->flags = cpystr (tmp + 1);
4208 *date = md->date = cpystr (mail_date (tmp,elt));
4209 INIT (md->message,msg_string,(void *) md,elt->rfc822_size);
4210 break; /* process this message */
4211 }
4212 return LONGT;
4213 }
4214
4215 /* Append message callback
4216 * Accepts: MAIL stream
4217 * append data package
4218 * pointer to return initial flags
4219 * pointer to return message internal date
4220 * pointer to return stringstruct of message or NIL to stop
4221 * Returns: T if success (have message or stop), NIL if error
4222 */
4223
append_msg(MAILSTREAM * stream,void * data,char ** flags,char ** date,STRING ** message)4224 long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
4225 STRING **message)
4226 {
4227 unsigned long i,j;
4228 char *t;
4229 APPENDDATA *ad = (APPENDDATA *) data;
4230 unsigned char *arg = ad->arg;
4231 /* flush text of previous message */
4232 if (t = ad->flags) fs_give ((void **) &ad->flags);
4233 if (t = ad->date) fs_give ((void **) &ad->date);
4234 if (t = ad->msg) fs_give ((void **) &ad->msg);
4235 *flags = *date = NIL; /* assume no flags or date */
4236 if (t) { /* have previous message? */
4237 if (!*arg) { /* if least one message, and no more coming */
4238 *message = NIL; /* set stop */
4239 return LONGT; /* return success */
4240 }
4241 else if (*arg++ != ' ') { /* must have a delimiter to next argument */
4242 response = misarg; /* oops */
4243 return NIL;
4244 }
4245 }
4246 *message = ad->message; /* return pointer to message stringstruct */
4247 if (*arg == '(') { /* parse optional flag list */
4248 t = ++arg; /* pointer to flag list contents */
4249 while (*arg && (*arg != ')')) arg++;
4250 if (*arg) *arg++ = '\0';
4251 if (*arg == ' ') arg++;
4252 *flags = ad->flags = cpystr (t);
4253 }
4254 /* parse optional date */
4255 if (*arg == '"') *date = ad->date = cpystr (snarf (&arg));
4256 if (!arg || (*arg != '{')) /* parse message */
4257 response = "%.80s BAD Missing literal in %.80s\015\012";
4258 else if (!isdigit (arg[1]))
4259 response = "%.80s BAD Missing message to %.80s\015\012";
4260 else if (!(i = strtoul (arg+1,&t,10)))
4261 response = "%.80s NO Empty message to %.80s\015\012";
4262 else if (i > MAXAPPENDTXT) /* maybe relax this a little */
4263 response = "%.80s NO Excessively large message to %.80s\015\012";
4264 else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) {
4265 /* get a literal buffer */
4266 inliteral (ad->msg = (char *) fs_get (i+1),i);
4267 /* get new command tail */
4268 slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT);
4269 if (strchr (ad->arg,'\012')) {
4270 /* reset strtok mechanism, tie off if done */
4271 if (!strtok_r (ad->arg,"\015\012",&sstate)) *ad->arg = '\0';
4272 /* possible LITERAL+? */
4273 if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') &&
4274 (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) {
4275 /* back over possible count */
4276 for (j -= 4; j && isdigit (ad->arg[j]); j--);
4277 if (ad->arg[j] == '{') {/* found a literal? */
4278 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
4279 litplus.size = strtoul (ad->arg + j + 1,NIL,10);
4280 }
4281 }
4282 /* initialize stringstruct */
4283 INIT (ad->message,mail_string,(void *) ad->msg,i);
4284 return LONGT; /* ready to go */
4285 }
4286 flush (); /* didn't find end of line? */
4287 fs_give ((void **) &ad->msg);
4288 }
4289 else response = badarg; /* not a literal */
4290 return NIL; /* error */
4291 }
4292
4293 /* Got COPY UID data
4294 * Accepts: MAIL stream
4295 * mailbox name
4296 * UID validity
4297 * source set of UIDs
4298 * destination set of UIDs
4299 */
4300
copyuid(MAILSTREAM * stream,char * mailbox,unsigned long uidvalidity,SEARCHSET * sourceset,SEARCHSET * destset)4301 void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
4302 SEARCHSET *sourceset,SEARCHSET *destset)
4303 {
4304 if (cauidvalidity) fatal ("duplicate COPYUID/APPENDUID data");
4305 cauidvalidity = uidvalidity;
4306 csset = sourceset;
4307 caset = destset;
4308 }
4309
4310
4311 /* Got APPEND UID data
4312 * Accepts: mailbox name
4313 * UID validity
4314 * destination set of UIDs
4315 */
4316
appenduid(char * mailbox,unsigned long uidvalidity,SEARCHSET * set)4317 void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set)
4318 {
4319 copyuid (NIL,mailbox,uidvalidity,NIL,set);
4320 }
4321
4322
4323 /* Got a referral
4324 * Accepts: MAIL stream
4325 * URL
4326 * referral type code
4327 */
4328
referral(MAILSTREAM * stream,char * url,long code)4329 char *referral (MAILSTREAM *stream,char *url,long code)
4330 {
4331 if (lstref) fs_give ((void **) &lstref);
4332 lstref = cpystr (url); /* set referral */
4333 /* set error if not a logged in referral */
4334 if (code != REFAUTH) response = lose;
4335 if (!lsterr) lsterr = cpystr ("Try referral URL");
4336 return NIL; /* don't chase referrals for now */
4337 }
4338
4339 /* Co-routines from MAIL library */
4340
4341
4342 /* Message matches a search
4343 * Accepts: MAIL stream
4344 * message number
4345 */
4346
mm_searched(MAILSTREAM * s,unsigned long msgno)4347 void mm_searched (MAILSTREAM *s,unsigned long msgno)
4348 {
4349 /* nothing to do here */
4350 }
4351
4352
4353 /* Message exists (i.e. there are that many messages in the mailbox)
4354 * Accepts: MAIL stream
4355 * message number
4356 */
4357
mm_exists(MAILSTREAM * s,unsigned long number)4358 void mm_exists (MAILSTREAM *s,unsigned long number)
4359 {
4360 /* note change in number of messages */
4361 if ((s != tstream) && (nmsgs != number)) {
4362 nmsgs = number; /* always update number of messages */
4363 if (quell_events) existsquelled = T;
4364 else {
4365 PSOUT ("* ");
4366 pnum (nmsgs);
4367 PSOUT (" EXISTS\015\012");
4368 }
4369 recent = 0xffffffff; /* make sure update recent too */
4370 }
4371 }
4372
4373
4374 /* Message expunged
4375 * Accepts: MAIL stream
4376 * message number
4377 */
4378
mm_expunged(MAILSTREAM * s,unsigned long number)4379 void mm_expunged (MAILSTREAM *s,unsigned long number)
4380 {
4381 if (quell_events) fatal ("Impossible EXPUNGE event");
4382 if (s != tstream) {
4383 PSOUT ("* ");
4384 pnum (number);
4385 PSOUT (" EXPUNGE\015\012");
4386 }
4387 nmsgs--;
4388 existsquelled = T; /* do EXISTS when command done */
4389 }
4390
4391
4392 /* Message status changed
4393 * Accepts: MAIL stream
4394 * message number
4395 */
4396
mm_flags(MAILSTREAM * s,unsigned long number)4397 void mm_flags (MAILSTREAM *s,unsigned long number)
4398 {
4399 if (s != tstream) mail_elt (s,number)->spare2 = T;
4400 }
4401
4402 /* Mailbox found
4403 * Accepts: hierarchy delimiter
4404 * mailbox name
4405 * attributes
4406 */
4407
mm_list(MAILSTREAM * stream,int delimiter,char * name,long attributes)4408 void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
4409 {
4410 mm_list_work ("LIST",delimiter,name,attributes);
4411 }
4412
4413
4414 /* Subscribed mailbox found
4415 * Accepts: hierarchy delimiter
4416 * mailbox name
4417 * attributes
4418 */
4419
mm_lsub(MAILSTREAM * stream,int delimiter,char * name,long attributes)4420 void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
4421 {
4422 mm_list_work ("LSUB",delimiter,name,attributes);
4423 }
4424
4425
4426 /* Mailbox status
4427 * Accepts: MAIL stream
4428 * mailbox name
4429 * mailbox status
4430 */
4431
mm_status(MAILSTREAM * stream,char * mailbox,MAILSTATUS * status)4432 void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
4433 {
4434 if (!quell_events) {
4435 char tmp[MAILTMPLEN];
4436 tmp[0] = tmp[1] = '\0';
4437 if (status->flags & SA_MESSAGES)
4438 sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
4439 if (status->flags & SA_RECENT)
4440 sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
4441 if (status->flags & SA_UNSEEN)
4442 sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
4443 if (status->flags & SA_UIDNEXT)
4444 sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
4445 if (status->flags & SA_UIDVALIDITY)
4446 sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
4447 PSOUT ("* STATUS ");
4448 pastring (mailbox);
4449 PSOUT (" (");
4450 PSOUT (tmp+1);
4451 PBOUT (')');
4452 CRLF;
4453 }
4454 }
4455
4456 /* Worker routine for LIST and LSUB
4457 * Accepts: name of response
4458 * hierarchy delimiter
4459 * mailbox name
4460 * attributes
4461 */
4462
mm_list_work(char * what,int delimiter,char * name,long attributes)4463 void mm_list_work (char *what,int delimiter,char *name,long attributes)
4464 {
4465 char *s;
4466 if (!quell_events) {
4467 char tmp[MAILTMPLEN];
4468 if (finding) {
4469 PSOUT ("* MAILBOX ");
4470 PSOUT (name);
4471 }
4472 /* new form */
4473 else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
4474 PSOUT ("* ");
4475 PSOUT (what);
4476 PSOUT (" (");
4477 tmp[0] = tmp[1] = '\0';
4478 if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors");
4479 if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect");
4480 if (attributes & LATT_MARKED) strcat (tmp," \\Marked");
4481 if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked");
4482 if (attributes & LATT_HASCHILDREN) strcat (tmp," \\HasChildren");
4483 if (attributes & LATT_HASNOCHILDREN) strcat (tmp," \\HasNoChildren");
4484 PSOUT (tmp+1);
4485 switch (delimiter) {
4486 case '\\': /* quoted delimiter */
4487 case '"':
4488 PSOUT (") \"\\");
4489 PBOUT (delimiter);
4490 PBOUT ('"');
4491 break;
4492 case '\0': /* no delimiter */
4493 PSOUT (") NIL");
4494 break;
4495 default: /* unquoted delimiter */
4496 PSOUT (") \"");
4497 PBOUT (delimiter);
4498 PBOUT ('"');
4499 break;
4500 }
4501 PBOUT (' ');
4502 /* output mailbox name */
4503 if (proxylist && (s = strchr (name,'}'))) pastring (s+1);
4504 else pastring (name);
4505 }
4506 CRLF;
4507 }
4508 }
4509
4510 /* Notification event
4511 * Accepts: MAIL stream
4512 * string to log
4513 * error flag
4514 */
4515
mm_notify(MAILSTREAM * stream,char * string,long errflg)4516 void mm_notify (MAILSTREAM *stream,char *string,long errflg)
4517 {
4518 SIZEDTEXT msg;
4519 char *s,*code;
4520 if (!quell_events && (!tstream || (stream != tstream))) {
4521 switch (errflg) {
4522 case NIL: /* information message, set as OK response */
4523 if ((string[0] == '[') &&
4524 ((string[1] == 'T') || (string[1] == 't')) &&
4525 ((string[2] == 'R') || (string[2] == 'r')) &&
4526 ((string[3] == 'Y') || (string[3] == 'y')) &&
4527 ((string[4] == 'C') || (string[4] == 'c')) &&
4528 ((string[5] == 'R') || (string[5] == 'r')) &&
4529 ((string[6] == 'E') || (string[6] == 'e')) &&
4530 ((string[7] == 'A') || (string[7] == 'a')) &&
4531 ((string[8] == 'T') || (string[8] == 't')) &&
4532 ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
4533 trycreate = T;
4534 case BYE: /* some other server signing off */
4535 case PARSE: /* parse glitch, output unsolicited OK */
4536 code = "* OK ";
4537 break;
4538 case WARN: /* warning, output unsolicited NO (kludge!) */
4539 code = "* NO ";
4540 break;
4541 case ERROR: /* error that broke command */
4542 default: /* default should never happen */
4543 code = "* BAD ";
4544 break;
4545 }
4546 PSOUT (code);
4547 msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),
4548 "\015\012")) ?
4549 (s - string) : strlen (string);
4550 PSOUTR (&msg);
4551 CRLF;
4552 PFLUSH (); /* let client see it immediately */
4553 }
4554 }
4555
4556 /* Log an event for the user to see
4557 * Accepts: string to log
4558 * error flag
4559 */
4560
mm_log(char * string,long errflg)4561 void mm_log (char *string,long errflg)
4562 {
4563 SIZEDTEXT msg;
4564 char *s;
4565 msg.size =
4566 (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
4567 (s - string) : strlen (string);
4568 switch (errflg) {
4569 case NIL: /* information message, set as OK response */
4570 if (response == win) { /* only if no other response yet */
4571 if (lsterr) { /* if there was a previous message */
4572 if (!quell_events) {
4573 PSOUT ("* OK "); /* blat it out */
4574 PSOUT (lsterr);
4575 CRLF;
4576 PFLUSH (); /* let client see it immediately */
4577 }
4578 fs_give ((void **) &lsterr);
4579 }
4580 lsterr = cpystr (string); /* copy string for later use */
4581 if (s) lsterr[s - string] = NIL;
4582 }
4583 break;
4584 case PARSE: /* parse glitch, output unsolicited OK */
4585 if (!quell_events) {
4586 PSOUT ("* OK [PARSE] ");
4587 PSOUTR (&msg);
4588 CRLF;
4589 PFLUSH (); /* let client see it immediately */
4590 }
4591 break;
4592 case WARN: /* warning, output unsolicited NO */
4593 /* ignore "Mailbox is empty" (KLUDGE!) */
4594 if (strcmp (string,"Mailbox is empty")) {
4595 if (lstwrn) { /* have previous warning? */
4596 if (!quell_events) {
4597 PSOUT ("* NO ");
4598 PSOUT (lstwrn);
4599 CRLF;
4600 PFLUSH (); /* make sure client sees it immediately */
4601 }
4602 fs_give ((void **) &lstwrn);
4603 }
4604 lstwrn = cpystr (string); /* note last warning */
4605 if (s) lstwrn[s - string] = NIL;
4606 }
4607 break;
4608 case ERROR: /* error that broke command */
4609 default: /* default should never happen */
4610 response = trycreate ? losetry : lose;
4611 if (lsterr) fs_give ((void **) &lsterr);
4612 lsterr = cpystr (string); /* note last error */
4613 if (s) lsterr[s - string] = NIL;
4614 break;
4615 }
4616 }
4617
4618 /* Return last error
4619 */
4620
lasterror(void)4621 char *lasterror (void)
4622 {
4623 if (lsterr) return lsterr;
4624 if (lstwrn) return lstwrn;
4625 return "<unknown>";
4626 }
4627
4628
4629 /* Log an event to debugging telemetry
4630 * Accepts: string to log
4631 */
4632
mm_dlog(char * string)4633 void mm_dlog (char *string)
4634 {
4635 mm_log (string,WARN); /* shouldn't happen normally */
4636 }
4637
4638 /* Get user name and password for this host
4639 * Accepts: parse of network user name
4640 * where to return user name
4641 * where to return password
4642 * trial count
4643 */
4644
mm_login(NETMBX * mb,char * username,char * password,long trial)4645 void mm_login (NETMBX *mb,char *username,char *password,long trial)
4646 {
4647 /* set user name */
4648 strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER);
4649 strncpy (password,pass,256); /* and password */
4650 }
4651
4652
4653 /* About to enter critical code
4654 * Accepts: stream
4655 */
4656
mm_critical(MAILSTREAM * s)4657 void mm_critical (MAILSTREAM *s)
4658 {
4659 ++critical;
4660 }
4661
4662
4663 /* About to exit critical code
4664 * Accepts: stream
4665 */
4666
mm_nocritical(MAILSTREAM * s)4667 void mm_nocritical (MAILSTREAM *s)
4668 {
4669 /* go non-critical, pending death? */
4670 if (!--critical && (state == LOGOUT)) {
4671 /* clean up iff needed */
4672 if (s && (stream != s) && !s->lock && (s->dtb->flags & DR_XPOINT))
4673 s = mail_close (s);
4674 longjmp (jmpenv,1); /* die now */
4675 }
4676 }
4677
4678 /* Disk error found
4679 * Accepts: stream
4680 * system error code
4681 * flag indicating that mailbox may be clobbered
4682 * Returns: abort flag
4683 */
4684
mm_diskerror(MAILSTREAM * s,long errcode,long serious)4685 long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
4686 {
4687 if (serious) { /* try your damnest if clobberage likely */
4688 mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
4689 PFLUSH (); /* dump output buffer */
4690 syslog (LOG_ALERT,
4691 "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
4692 user ? (char *) user : "???",tcp_clienthost (),
4693 (stream && stream->mailbox) ? stream->mailbox : "???",
4694 strerror (errcode));
4695 settimeout (0); /* make damn sure timeout disabled */
4696 sleep (60); /* give it some time to clear up */
4697 return NIL;
4698 }
4699 if (!quell_events) { /* otherwise die before more damage is done */
4700 PSOUT ("* NO Disk error: ");
4701 PSOUT (strerror (errcode));
4702 CRLF;
4703 }
4704 return T;
4705 }
4706
4707
4708 /* Log a fatal error event
4709 * Accepts: string to log
4710 */
4711
mm_fatal(char * string)4712 void mm_fatal (char *string)
4713 {
4714 SIZEDTEXT msg;
4715 char *s;
4716 msg.size =
4717 (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
4718 (s - string) : strlen (string);
4719 if (!quell_events) {
4720 PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
4721 PSOUTR (&msg);
4722 CRLF;
4723 PFLUSH ();
4724 }
4725 syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
4726 user ? (char *) user : "???",tcp_clienthost (),
4727 (stream && stream->mailbox) ? stream->mailbox : "???",string);
4728 }
4729