diff options
Diffstat (limited to 'imap/src/imapd')
-rw-r--r-- | imap/src/imapd/Makefile | 28 | ||||
-rw-r--r-- | imap/src/imapd/imapd.c | 376 |
2 files changed, 264 insertions, 140 deletions
diff --git a/imap/src/imapd/Makefile b/imap/src/imapd/Makefile index c3078f42..ae54bfed 100644 --- a/imap/src/imapd/Makefile +++ b/imap/src/imapd/Makefile @@ -1,16 +1,7 @@ # ======================================================================== -# Copyright 1988-2006 University of Washington -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# +# Copyright 2008-2010 Mark Crispin # ======================================================================== - # Program: IMAPD Makefile # # Author: Mark Crispin @@ -23,6 +14,16 @@ # # Date: 5 November 1990 # Last Edited: 30 August 2006 +# +# Previous versions of this file were +# +# Copyright 1988-2006 University of Washington +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 ALERT=/etc/imapd.alert @@ -37,15 +38,18 @@ SHELL= /bin/sh # causes the "Manage Mail" menu item to open the given URL, e.g. to point to # an alternative IMAP client (e.g. Pine) or perhaps to a homebrew mail # account management page. -#NSBD= -DNETSCAPE_BRAIN_DAMAGE=\"http://www.washington.edu/pine\" +#NSBD= -DNETSCAPE_BRAIN_DAMAGE=\"http://sourceforge.net/projects/re-alpine\" +# Un-comment to enable the ESEARCH command. Unfortunately, the iOS4 Mail +# client misinterprets the ESEARCH results due to an ambiguity in RFC 4731. +#ESEARCH= -DESEARCH=1 # Get local definitions from c-client directory C = ../c-client CCLIENTLIB = $C/c-client.a CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` $(NSBD) $(ENBD) -DANOFILE=\"$(ANO)\" \ +CFLAGS = -I$C `cat $C/CFLAGS` $(NSBD) $(ESEARCH) -DANOFILE=\"$(ANO)\" \ -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" \ -DUSERALERTFILE=\"$(USERALERT)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` diff --git a/imap/src/imapd/imapd.c b/imap/src/imapd/imapd.c index d3d15660..5f72ac70 100644 --- a/imap/src/imapd/imapd.c +++ b/imap/src/imapd/imapd.c @@ -1,13 +1,5 @@ /* ======================================================================== - * Copyright 1988-2008 University of Washington - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * + * Copyright 2008-2011 Mark Crispin * ======================================================================== */ @@ -15,13 +7,20 @@ * Program: IMAP4rev1 server * * Author: Mark Crispin - * UW Technology - * University of Washington - * Seattle, WA 98195 - * Internet: MRC@Washington.EDU * * Date: 5 November 1990 - * Last Edited: 3 March 2008 + * Last Edited: 30 July 2011 + * + * Previous versions of this file were + * + * Copyright 1988-2008 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * */ /* Parameter files */ @@ -33,6 +32,7 @@ extern int errno; /* just in case */ #include <signal.h> #include <setjmp.h> #include <time.h> +#include <limits.h> #include "c-client.h" #include "newsrc.h" #include <sys/stat.h> @@ -51,9 +51,12 @@ extern int errno; /* just in case */ #define ALERTTIMER 1 MINUTES /* alert check timer */ #define SHUTDOWNTIMER 1 MINUTES /* shutdown dally timer */ #define IDLETIMER 1 MINUTES /* IDLE command poll timer */ -#define CHECKTIMER 15 MINUTES /* IDLE command last checkpoint timer */ +#define CHECKTIMER 5 MINUTES /* IDLE command last checkpoint timer */ +#define SIGNALTIMER 10 MINUTES /* allocated time to die at signal */ +#define MAXNLIBADCOMMAND 3 /* limit on number of NLI bad commands */ +#define MAXTAG 50 /* maximum tag length */ #define LITSTKLEN 20 /* length of literal stack */ #define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size * must be smaller than 4294967295 @@ -66,10 +69,12 @@ extern int errno; /* just in case */ /* Server states */ -#define LOGIN 0 -#define SELECT 1 -#define OPEN 2 -#define LOGOUT 3 +typedef enum { + LOGIN = 0, + SELECT, + OPEN, + LOGOUT +} IMAPSTATE; /* Body text fetching */ @@ -118,6 +123,7 @@ void msg_string_setpos (STRING *s,unsigned long i); void new_flags (MAILSTREAM *stream); void settimeout (unsigned int i); void clkint (void); +void dieint (void); void kodint (void); void hupint (void); void trmint (void); @@ -130,6 +136,7 @@ unsigned char *flush (void); void ioerror (FILE *f,char *reason); unsigned char *parse_astring (unsigned char **arg,unsigned long *i, unsigned char *del); +unsigned char *parse_tag (unsigned char *cmd,unsigned char **ret); unsigned char *snarf (unsigned char **arg); unsigned char *snarf_base64 (unsigned char **arg); unsigned char *snarf_list (unsigned char **arg); @@ -207,7 +214,7 @@ char *lasterror (void); /* Global storage */ -char *version = "404"; /* edit number of this server */ +char *version = "417"; /* edit number of this server */ char *logout = "Logout"; /* syslogreason for logout */ char *goodbye = NIL; /* bye reason */ time_t alerttime = 0; /* time of last alert */ @@ -215,7 +222,9 @@ time_t sysalerttime = 0; /* time of last system alert */ time_t useralerttime = 0; /* time of last user alert */ time_t lastcheck = 0; /* time of last checkpoint */ time_t shutdowntime = 0; /* time of last shutdown */ -int state = LOGIN; /* server state */ +int blackberry = 0; /* 1 if BlackBerry, -1 if not, 0 if unknown */ +int nlibcm = MAXNLIBADCOMMAND; /* limit number of bad commands */ +IMAPSTATE state = LOGIN; /* server state */ int cancelled = NIL; /* authenticate cancelled */ int trycreate = 0; /* saw a trycreate */ int finding = NIL; /* doing old FIND command */ @@ -243,6 +252,7 @@ char *lstwrn = NIL; /* last warning message from c-client */ char *lsterr = NIL; /* last error message from c-client */ char *lstref = NIL; /* last referral from c-client */ char *response = NIL; /* command response */ +char *sstate = NIL; /* strtok state */ struct { unsigned long size; /* size of current LITERAL+ */ unsigned int ok : 1; /* LITERAL+ in effect */ @@ -267,9 +277,10 @@ char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012"; char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012"; char *lose = "%.80s NO "; char *logwin = "%.80s OK ["; -char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s\015\012"; -char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.900s\015\012"; +char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.800s\015\012"; +char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.800s\015\012"; char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012"; +char *badcml = "%.80s BAD Command unrecognized\015\012"; char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012"; char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012"; char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012"; @@ -362,7 +373,7 @@ int main (int argc,char *argv[]) state = LOGOUT; } PSOUT (tcp_serverhost ()); - PSOUT (" IMAP4rev1 "); + PSOUT (" Panda IMAP "); PSOUT (CCLIENTVERSION); PBOUT ('.'); PSOUT (version); @@ -396,46 +407,65 @@ int main (int argc,char *argv[]) if (lstref) fs_give ((void **) &lstref); while (litsp) fs_give ((void **) &litstk[--litsp]); /* find end of line */ - if (!strchr (cmdbuf,'\012')) { - if (t = strchr (cmdbuf,' ')) *t = '\0'; - if ((t - cmdbuf) > 100) t = NIL; - flush (); /* flush excess */ - if (state == LOGIN) /* error if NLI */ + if (t = strchr (cmdbuf,'\012')) { + /* tie off command termination */ + if ((t > cmdbuf) && (t[-1] == '\015')) --t; + *t = '\0'; /* tie off LF or CRLF */ + } + if (!t) { /* probably line too long if not terminated */ + if (t = strchr (cmdbuf,' ')) { + if ((t - cmdbuf) > MAXTAG) t = NIL; + else *t = '\0'; + } + flush (); /* flush excess, set response */ + if (state == LOGIN) { /* error if NLI */ syslog (LOG_INFO,"Line too long before authentication host=%.80s", tcp_clienthost ()); + state = LOGOUT; + } sprintf (tmp,response,t ? (char *) cmdbuf : "*"); PSOUT (tmp); } - else if (!(tag = strtok (cmdbuf," \015\012"))) { - if (state == LOGIN) /* error if NLI */ + else if (!(tag = parse_tag (cmdbuf,&s))) { + if (state == LOGIN) { /* error if NLI */ syslog (LOG_INFO,"Null command before authentication host=%.80s", tcp_clienthost ()); - PSOUT ("* BAD Null command\015\012"); + state = LOGOUT; + } + PSOUT ("* BAD Missing command tag\015\012"); } - else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag\015\012"); - else if (!(s = strtok (NIL," \015\012"))) { - if (state == LOGIN) /* error if NLI */ + else if (strlen (tag) > MAXTAG) + PSOUT ("* BAD Excessively long tag\015\012"); + else if (!*s) { /* make sure have command */ + if (state == LOGIN) { /* error if NLI */ syslog (LOG_INFO,"Missing command before authentication host=%.80s", tcp_clienthost ()); + state = LOGOUT; + } PSOUT (tag); PSOUT (" BAD Missing command\015\012"); } else { /* parse command */ response = win; /* set default response */ finding = NIL; /* no longer FINDing */ - ucase (s); /* canonicalize command case */ /* UID command? */ - if (!strcmp (s,"UID") && strtok (NIL," \015\012")) { + if (((s[0] == 'U') || (s[0] == 'u')) && + ((s[1] == 'I') || (s[1] == 'i')) && + ((s[2] == 'D') || (s[2] == 'd')) && (s[3] == ' ')) { uid = T; /* a UID command */ - s[3] = ' '; /* restore the space delimiter */ - ucase (s); /* make sure command all uppercase */ + arg = strchr (s+4,' '); /* find argument */ + } + else { + uid = NIL; /* not a UID command */ + arg = strchr (s,' '); /* find argument */ } - else uid = NIL; /* not a UID command */ + if (arg) *arg++ = '\0'; /* tie off command argument */ + ucase (s); /* canonicalize command case */ /* flush previous saved command */ if (cmd) fs_give ((void **) &cmd); cmd = cpystr (s); /* save current command */ /* snarf argument, see if possible litplus */ - if ((arg = strtok (NIL,"\015\012")) && ((i = strlen (arg)) > 3) && + if (arg && ((i = strlen (arg)) > 3) && (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) { /* back over possible count */ for (i -= 4; i && isdigit (arg[i]); i--); @@ -445,6 +475,26 @@ int main (int argc,char *argv[]) } } + if (!blackberry) { /* do we know if it's a BlackBerry yet? */ + char *bb[] = {".blackberry.com", ".blackberry.net", NIL}; + size_t bbs; + unsigned char *cl = cpystr (tcp_clienthost ()); + size_t cls; + /* get just host name, calculate length */ + if (t = strchr (cl,' ')) { + *t = '\0'; + cls = t - cl; + } + else cls = strlen (cl); + for (i = 0; bb[i]; ++i) { + if (((bbs = strlen (bb[i])) < cls) && + !compare_cstring(bb[i], cl + cls - bbs)) + blackberry = 1; + } + fs_give ((void **) & cl); + /* not a blackberry if no match on list */ + if(!blackberry) blackberry = -1; + } /* these commands always valid */ if (!strcmp (cmd,"NOOP")) { if (arg) response = badarg; @@ -544,7 +594,7 @@ int main (int argc,char *argv[]) tcp_clienthost ()); } else { - response = badcmd; + response = badcml; syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s, tcp_clienthost ()); } @@ -591,13 +641,18 @@ int main (int argc,char *argv[]) if (arg) response = badarg; else if (lsterr = ssl_start_tls (pgmname)) response = lose; } - else response = badcmd; + else { + response = badcml; + /* limit the number of bad commands */ + if (--nlibcm <= 0) state = LOGOUT; + } break; case OPEN: /* valid only when mailbox open */ /* fetch mailbox attributes */ if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) { - if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012")))) + if (!(arg && (s = strtok_r (arg," ",&sstate)) && + (t = strtok_r (NIL,"\015\012",&sstate)))) response = misarg; else if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s)) fetch (t,uid); @@ -606,8 +661,9 @@ int main (int argc,char *argv[]) /* store mailbox attributes */ else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) { /* must have three arguments */ - if (!(arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) && - (t = strtok (NIL,"\015\012")))) response = misarg; + if (!(arg && (s = strtok_r (arg," ",&sstate)) && + (v = strtok_r (NIL," ",&sstate)) && + (t = strtok_r (NIL,"\015\012",&sstate)))) response = misarg; else if (!(uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))) response = badseq; else { @@ -681,7 +737,8 @@ int main (int argc,char *argv[]) else if (!anonymous && /* copy message(s) */ (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) { trycreate = NIL; /* no trycreate status */ - if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"\015\012")) + if (!(arg && (s = strtok_r (arg," ",&sstate)) && + (arg = strtok_r (NIL,"\015\012",&sstate)) && (t = snarf (&arg)))) response = misarg; else if (arg) response = badarg; else if (!nmsgs) { @@ -708,14 +765,14 @@ int main (int argc,char *argv[]) SORTPGM *pgm = NIL,*pg = NIL; unsigned long *slst,*sl; *t = NIL; /* tie off criteria list */ - if (!(s = strtok (ucase (s)," "))) response = badatt; + if (!(s = strtok_r (ucase (s)," ",&sstate))) response = badatt; else { do { /* parse sort attributes */ if (pg) pg = pg->next = mail_newsortpgm (); else pgm = pg = mail_newsortpgm (); if (!strcmp (s,"REVERSE")) { pg->reverse = T; - if (!(s = strtok (NIL," "))) { + if (!(s = strtok_r (NIL," ",&sstate))) { s = ""; /* end of attributes */ break; } @@ -728,7 +785,7 @@ int main (int argc,char *argv[]) else if (!strcmp (s,"CC")) pg->function = SORTCC; else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE; else break; - } while (s = strtok (NIL," ")); + } while (s = strtok_r (NIL," ",&sstate)); /* bad SORT attribute */ if (s) response = badatt; /* get charset and search criteria */ @@ -760,8 +817,9 @@ int main (int argc,char *argv[]) SEARCHPGM *spg = NIL; char *cs = NIL; /* must have four arguments */ - if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) && - (cs = cpystr (cs)) && (arg = strtok (NIL,"\015\012")))) + if (!(arg && (s = strtok_r (arg," ",&sstate)) && + (cs = strtok_r (NIL," ",&sstate)) && (cs = cpystr (cs)) && + (arg = strtok_r (NIL,"\015\012",&sstate)))) response = misarg; else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs, uidmax (stream),0)) response = badatt; @@ -881,30 +939,58 @@ int main (int argc,char *argv[]) /* wants ALL */ if (retval & 0x4) { - unsigned long j; + unsigned long j,juid,iuid; /* find first match */ for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched; ++i); if (i <= nmsgs) { + juid= (uid ? mail_uid (stream,i) : i); PSOUT (" ALL "); - pnum (uid ? mail_uid (stream,i) : i); - j = i; /* last message output */ + pnum (juid); + j = i; /* last message index processed */ } - while (++i <= nmsgs) { - if (mail_elt (stream,i)->searched) { - while ((++i <= nmsgs) && mail_elt (stream,i)->searched); - /* previous message is end of range */ - if (j != --i) { - PBOUT (':'); - pnum (uid ? mail_uid (stream,i) : i); + if (uid) { /* doing UIDs, no MRC optimization */ + while (++i <= nmsgs) { + if (mail_elt (stream,i)->searched) { /* this message matches */ + iuid= mail_uid (stream,i); + if (juid == (iuid - (i-j))) { /* in sequence of UIDs */ + if ( i < nmsgs) continue; /* may be more, check next */ + PBOUT (':'); pnum(iuid); /* done with all, tie it off */ + j = i; juid= iuid; + } else { /* break in sequence of UIDs */ + if ((i-1) > j) { /* prev sequence, tie it off */ + j= i-1; juid= mail_uid (stream,j); + PBOUT (':'); pnum(juid); + } + PBOUT (','); pnum(iuid); + j = i; juid= iuid; /* record last done message/UID */ + } + } else { /* this message doesn't match, have a pending sequence? */ + /* if pending sequence, tie it off & reset flags */ + if ((i-1) > j) { /* prev sequence, tie it off */ + j= i-1; juid= mail_uid (stream,j); + PBOUT (':'); pnum(juid); + } + j= nmsgs+1; juid= ULONG_MAX; /* flag that we're not in a sequence of matches */ } } + } else { /* doing message-IDs, use MRC optimization */ + while (++i <= nmsgs) { + if (mail_elt (stream,i)->searched) { + while ((++i <= nmsgs) && mail_elt (stream,i)->searched); + /* previous message is end of range */ + if (j != --i) { + PBOUT (':'); + pnum (i); + } + } /* search for next match */ - while ((++i <= nmsgs) && !mail_elt (stream,i)->searched); - if (i <= nmsgs) { - PBOUT (','); - pnum (uid ? mail_uid (stream,i) : i); - j = i; /* last message output */ + while ((++i <= nmsgs) && !mail_elt (stream,i)->searched); + if (i <= nmsgs) { + PBOUT (','); + pnum (i); + j = i; /* last message output */ + } } } } @@ -942,8 +1028,8 @@ int main (int argc,char *argv[]) else if (arg) response = badarg; else if (nameok (NIL,s = bboardname (cmd,s))) { DRIVER *factory = mail_valid (NIL,s,NIL); - f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) | - ((*cmd == 'S') ? NIL : OP_READONLY); + f = anonymous ? OP_ANONYMOUS | OP_READONLY : + (((blackberry > 0) || (*cmd == 'S')) ? NIL : OP_READONLY); curdriver = NIL; /* no drivers known */ /* no last uid */ uidvalidity = lastuid = 0; @@ -1084,9 +1170,10 @@ int main (int argc,char *argv[]) /* find mailboxes */ else if (!strcmp (cmd,"FIND")) { /* get subcommand and true argument */ - if (!(arg && (s = strtok (arg," \015\012")) && (s == cmd + 5) && - (cmd[4] = ' ') && ucase (s) && - (arg = strtok (NIL,"\015\012")) && (s = snarf_list (&arg)))) + if (!(arg && (s = strtok_r (arg," \015\012",&sstate)) && + (s == cmd + 5) && (cmd[4] = ' ') && ucase (s) && + (arg = strtok_r (NIL,"\015\012",&sstate)) && + (s = snarf_list (&arg)))) response = misarg; /* missing required argument */ else if (arg) response = badarg; /* punt on single-char wildcards */ @@ -1117,7 +1204,7 @@ int main (int argc,char *argv[]) f = NIL; /* initially no flags */ *t = '\0'; /* tie off flag string */ /* read flags */ - t = strtok (ucase (arg)," "); + t = strtok_r (ucase (arg)," ",&sstate); do { /* parse each one; unknown generate warning */ if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES; else if (!strcmp (t,"RECENT")) f |= SA_RECENT; @@ -1129,7 +1216,7 @@ int main (int argc,char *argv[]) PSOUT (t); CRLF; } - } while (t = strtok (NIL," ")); + } while (t = strtok_r (NIL," ",&sstate)); ping_mailbox (uid); /* in case the fool did STATUS on open mbx */ PFLUSH (); /* make sure stdout is dumped in case slave */ if (!compare_cstring (s,"INBOX")) s = "INBOX"; @@ -1308,7 +1395,8 @@ int main (int argc,char *argv[]) (void *) stream); ping_mailbox (uid); /* maybe do a checkpoint if not anonymous */ - if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) { + if (!anonymous && stream && + (time (0) > (lastcheck + CHECKTIMER))) { mail_check (stream); /* cancel likely altwin from mail_check() */ if (lsterr) fs_give ((void **) &lsterr); @@ -1760,17 +1848,23 @@ void settimeout (unsigned int i) void clkint (void) { - settimeout (0); /* disable all interrupts */ - server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); + settimeout (SIGNALTIMER); /* disable most interrupts */ + server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); logout = "Autologout"; goodbye = "Autologout (idle for too long)"; - if (critical) { /* must defer if in critical code(?) */ - close (0); /* kill stdin */ - state = LOGOUT; /* die as soon as we can */ - } + if (critical) state = LOGOUT; /* must defer if in critical code */ else longjmp (jmpenv,1); /* die now */ } +/* Clock interrupt after signal + * Never returns + */ + +void dieint (void) +{ + _exit(1); /* slay the beast. Now. */ +} + /* Kiss Of Death interrupt * Returns only if critical code in progress @@ -1778,13 +1872,10 @@ void clkint (void) void kodint (void) { - settimeout (0); /* disable all interrupts */ - server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); + settimeout (SIGNALTIMER); /* disable most interrupts */ + server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); logout = goodbye = "Killed (lost mailbox lock)"; - if (critical) { /* must defer if in critical code */ - close (0); /* kill stdin */ - state = LOGOUT; /* die as soon as we can */ - } + if (critical) state = LOGOUT; /* must defer if in critical code */ else longjmp (jmpenv,1); /* die now */ } @@ -1794,15 +1885,11 @@ void kodint (void) void hupint (void) { - settimeout (0); /* disable all interrupts */ - server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); + settimeout (SIGNALTIMER); /* disable most interrupts */ + server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); logout = "Hangup"; goodbye = NIL; /* other end is already gone */ - if (critical) { /* must defer if in critical code */ - close (0); /* kill stdin */ - close (1); /* and stdout */ - state = LOGOUT; /* die as soon as we can */ - } + if (critical) state = LOGOUT; /* must defer if in critical code */ else longjmp (jmpenv,1); /* die now */ } @@ -1813,18 +1900,14 @@ void hupint (void) void trmint (void) { - settimeout (0); /* disable all interrupts */ - server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); + settimeout (SIGNALTIMER); /* disable most interrupts */ + server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); logout = goodbye = "Killed (terminated)"; /* Make no attempt at graceful closure since a shutdown may be in * progress, and we won't have any time to do mail_close() actions */ stream = NIL; - if (critical) { /* must defer if in critical code */ - close (0); /* kill stdin */ - close (1); /* and stdout */ - state = LOGOUT; /* die as soon as we can */ - } + if (critical) state = LOGOUT; /* must defer if in critical code */ else longjmp (jmpenv,1); /* die now */ } @@ -1855,7 +1938,6 @@ void staint (void) if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) { fchmod (fd,0666); s = nout (sout (buf,"PID="),pid,10); - if (user) s = sout (sout (s,", user="),user); switch (state) { case LOGIN: s = sout (s,", not logged in"); @@ -1870,14 +1952,29 @@ void staint (void) s = sout (s,", logging out"); break; } - if (stream && stream->mailbox) - s = sout (sout (s,"\nmailbox="),stream->mailbox); + if (user) { + s = sout (sout (s,"\nuser="),user); + } + if (blackberry) { + /* Don't report client host until Blackberryness known. This is + because tcp_clienthost() may not have been called yet and so the + string might not be cached. As this is a signal handler, we want to + minimize any system calls. + */ + s = sout (sout (s,"\nhost="),tcp_clienthost ()); + if (blackberry > 0) s = sout (s, " (blackberry)"); + } + if (stream && stream->mailbox) { + s = sout (sout (sout (s,"\nmailbox="),stream->mailbox), + stream->rdonly ? " (read-only)" : " (read-write)"); + } *s++ = '\n'; if (status) { s = sout (s,status); if (cmd) s = sout (sout (s,", last command="),cmd); } - else s = sout (sout (s,cmd)," in progress"); + else if (cmd) s = sout (sout (s,cmd)," in progress"); + else s = sout (s,"UNKNOWN STATE"); *s++ = '\n'; write (fd,buf,s-buf); close (fd); @@ -1975,7 +2072,7 @@ void inliteral (char *s,unsigned long n) } /* Flush until newline seen - * Returns: NIL, always + * Returns: NIL and sets response, always */ unsigned char *flush (void) @@ -2004,16 +2101,14 @@ void ioerror (FILE *f,char *reason) static char msg[MAILTMPLEN]; char *s,*t; if (logout) { /* say nothing if already dying */ - settimeout (0); /* disable all interrupts */ - server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); + settimeout (SIGNALTIMER); /* disable most interrupts */ + server_init (NIL,NIL,NIL,dieint,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN); /* write error string */ for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect", t = logout = msg; *s; *t++ = *s++); for (s = ", while "; *s; *t++ = *s++); for (s = reason; *s; *t++ = *s++); if (critical) { /* must defer if in critical code */ - close (0); /* kill stdin */ - close (1); /* and stdout */ state = LOGOUT; /* die as soon as we can */ } else longjmp (jmpenv,1); /* die now */ @@ -2081,9 +2176,10 @@ unsigned char *parse_astring (unsigned char **arg,unsigned long *size, inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i); /* get new command tail */ slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT); + /* if too long, flush and set response */ if (!strchr (t,'\012')) return flush (); /* reset strtok mechanism, tie off if done */ - if (!strtok (t,"\015\012")) *t = '\0'; + if (!strtok_r (t,"\015\012",&sstate)) *t = '\0'; /* possible LITERAL+? */ if (((i = strlen (t)) > 3) && (t[i - 1] == '}') && (t[i - 2] == '+') && isdigit (t[i - 3])) { @@ -2104,6 +2200,27 @@ unsigned char *parse_astring (unsigned char **arg,unsigned long *size, return s; } +/* Parse tag + * Accepts: command tag + * Returns: tag if valid, NIL otherwise + */ + +unsigned char *parse_tag (unsigned char *cmd,unsigned char **ret) +{ + unsigned char *s; + *ret = cmd; /* make sure this stays defined */ + if (!*cmd) return NIL; /* empty command line */ + /* find end of tag */ + for (s = cmd; *s && (*s > ' ') && (*s < 0x7f) && + (*s != '(') && (*s != ')') && (*s != '{') && + (*s != '%') && (*s != '*') && + (*s != '"') && (*s != '\\'); ++s); + if (*s != ' ') return NIL; /* tag must be delimited by space */ + *s++ = '\0'; /* tie off tag */ + *ret = s; /* set pointer to remainder of command */ + return cmd; /* return tag */ +} + /* Snarf a command argument (simple jacket into parse_astring()) * Accepts: pointer to argument text pointer * Returns: argument @@ -2141,7 +2258,7 @@ unsigned char *snarf_base64 (unsigned char **arg) /* must be at least one BASE64 char */ else if (!base64mask[*ret]) return NIL; else { /* quick and dirty */ - while (base64mask[*s++]); /* scan until end of BASE64 */ + while (base64mask[*s]) ++s; /* scan until end of BASE64 */ if (*s == '=') ++s; /* allow up to two padding chars */ if (*s == '=') ++s; } @@ -2198,8 +2315,9 @@ unsigned char *snarf_list (unsigned char **arg) STRINGLIST *parse_stringlist (unsigned char **s,int *list) { - char c = ' ',*t; + char *t; unsigned long i; + char c = ' '; STRINGLIST *ret = NIL,*cur = NIL; if (*s && **s == '(') { /* proper list? */ ++*s; /* for each item in list */ @@ -2215,14 +2333,10 @@ STRINGLIST *parse_stringlist (unsigned char **s,int *list) /* must be end of list */ if (c != ')') mail_free_stringlist (&ret); } - if (t = *s) { /* need to reload strtok() state? */ + if (t = *s) { /* need to reload strtok state? */ /* end of a list? */ if (*list && (*t == ')') && !t[1]) *list = NIL; - else { - *--t = ' '; /* patch a space back in */ - *--t = 'x'; /* and a hokey character before that */ - t = strtok (t," "); /* reset to *s */ - } + else sstate = t; /* otherwise reset strtok state to s */ } return ret; } @@ -2644,7 +2758,8 @@ void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)"); else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)"); if (list = (*t == '(')) t++; /* skip open paren */ - if (s = strtok (t," ")) do { /* parse attribute list */ + /* parse attribute list */ + if (s = strtok_r (t," ",&sstate)) do { if (list && (i = strlen (s)) && (s[i-1] == ')')) { list = NIL; /* done with list */ s[i-1] = '\0'; /* tie off last item */ @@ -2744,12 +2859,13 @@ void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]) v += 4; /* want to exclude named headers */ ta->flags |= FT_NOT; } - if (*v || !(v = strtok (NIL,"\015\012")) || + if (*v || !(v = strtok_r (NIL,"\015\012",&sstate)) || !(ta->lines = parse_stringlist (&v,&list))) { fs_give ((void **) &ta);/* clean up */ response = "%.80s BAD Syntax error in header fields\015\012"; return; } + ucase (v); /* make sure followup is ucase */ } } else if (!strncmp (v,"TEXT",4)) { @@ -2786,16 +2902,15 @@ void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]) } switch (*v) { /* what's there now? */ case ' ': /* more follows */ - *--v = ' '; /* patch a space back in */ - *--v = 'x'; /* and a hokey character before that */ - strtok (v," "); /* reset strtok mechanism */ + sstate = v + 1; /* reset strtok mechanism */ break; case '\0': /* none */ break; case ')': /* end of list */ if (list && !v[1]) { /* make sure of that */ list = NIL; - strtok (v," "); /* reset strtok mechanism */ + /* reset strtok mechanism */ + strtok_r (v," ",&sstate); break; /* all done */ } /* otherwise it's a bogon, drop in */ @@ -2813,7 +2928,7 @@ void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]) response = badatt; return; } - } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list); + } while ((s = strtok_r (NIL," ",&sstate)) && (k < MAXFETCH) && list); else { response = misarg; /* missing attribute list */ return; @@ -3817,13 +3932,18 @@ void pcapability (long flag) PSOUT (" X-NETSCAPE"); #endif if (flag >= 0) { /* want post-authentication capabilities? */ - PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT ESEARCH WITHIN SCAN SORT"); + PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT"); +#ifdef ESEARCH + PSOUT (" ESEARCH"); +#endif + PSOUT (" WITHIN SORT"); while (thr) { /* threaders */ PSOUT (" THREAD="); PSOUT (thr->name); thr = thr->next; } if (!anonymous) PSOUT (" MULTIAPPEND"); + PSOUT (" SCAN"); /* private extension */ } if (flag <= 0) { /* want pre-authentication capabilities? */ PSOUT (" SASL-IR LOGIN-REFERRALS"); @@ -4148,7 +4268,7 @@ long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date, slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT); if (strchr (ad->arg,'\012')) { /* reset strtok mechanism, tie off if done */ - if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0'; + if (!strtok_r (ad->arg,"\015\012",&sstate)) *ad->arg = '\0'; /* possible LITERAL+? */ if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') && (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) { |