imapext-2007

annotate src/imapd/imapd.c @ 4:d741b3ecc917

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

UW-IMAP'd extensions by yuuji