imapext-2007
diff src/c-client/mail.c @ 0:ada5e610ab86
imap-2007e
author | yuuji@gentei.org |
---|---|
date | Mon, 14 Sep 2009 15:17:45 +0900 |
parents | |
children | 28a55bc1110c |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/c-client/mail.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,6331 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2008 University of Washington 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * 1.14 + * ======================================================================== 1.15 + */ 1.16 + 1.17 +/* 1.18 + * Program: Mailbox Access routines 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * UW Technology 1.22 + * University of Washington 1.23 + * Seattle, WA 98195 1.24 + * Internet: MRC@Washington.EDU 1.25 + * 1.26 + * Date: 22 November 1989 1.27 + * Last Edited: 15 April 2008 1.28 + */ 1.29 + 1.30 + 1.31 +#include <ctype.h> 1.32 +#include <stdio.h> 1.33 +#include <time.h> 1.34 +#include "c-client.h" 1.35 + 1.36 +char *UW_copyright = "Copyright 1988-2007 University of Washington\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n"; 1.37 + 1.38 +/* c-client global data */ 1.39 + 1.40 + /* version of this library */ 1.41 +static char *mailcclientversion = CCLIENTVERSION; 1.42 + /* list of mail drivers */ 1.43 +static DRIVER *maildrivers = NIL; 1.44 + /* list of authenticators */ 1.45 +static AUTHENTICATOR *mailauthenticators = NIL; 1.46 + /* SSL driver pointer */ 1.47 +static NETDRIVER *mailssldriver = NIL; 1.48 + /* pointer to alternate gets function */ 1.49 +static mailgets_t mailgets = NIL; 1.50 + /* pointer to read progress function */ 1.51 +static readprogress_t mailreadprogress = NIL; 1.52 + /* mail cache manipulation function */ 1.53 +static mailcache_t mailcache = mm_cache; 1.54 + /* RFC-822 output generator */ 1.55 +static rfc822out_t mail822out = NIL; 1.56 + /* RFC-822 output generator (new style) */ 1.57 +static rfc822outfull_t mail822outfull = NIL; 1.58 + /* SMTP verbose callback */ 1.59 +static smtpverbose_t mailsmtpverbose = mm_dlog; 1.60 + /* proxy copy routine */ 1.61 +static mailproxycopy_t mailproxycopy = NIL; 1.62 + /* RFC-822 external line parse */ 1.63 +static parseline_t mailparseline = NIL; 1.64 + /* RFC-822 external phrase parser */ 1.65 +static parsephrase_t mailparsephrase = NIL; 1.66 +static kinit_t mailkinit = NIL; /* application kinit callback */ 1.67 + /* note network sent command */ 1.68 +static sendcommand_t mailsendcommand = NIL; 1.69 + /* newsrc file name decision function */ 1.70 +static newsrcquery_t mailnewsrcquery = NIL; 1.71 + /* ACL results callback */ 1.72 +static getacl_t mailaclresults = NIL; 1.73 + /* list rights results callback */ 1.74 +static listrights_t maillistrightsresults = NIL; 1.75 + /* my rights results callback */ 1.76 +static myrights_t mailmyrightsresults = NIL; 1.77 + /* quota results callback */ 1.78 +static quota_t mailquotaresults = NIL; 1.79 + /* quota root results callback */ 1.80 +static quotaroot_t mailquotarootresults = NIL; 1.81 + /* sorted results callback */ 1.82 +static sortresults_t mailsortresults = NIL; 1.83 + /* threaded results callback */ 1.84 +static threadresults_t mailthreadresults = NIL; 1.85 + /* COPY UID results */ 1.86 +static copyuid_t mailcopyuid = NIL; 1.87 + /* APPEND UID results */ 1.88 +static appenduid_t mailappenduid = NIL; 1.89 + /* free elt extra stuff callback */ 1.90 +static freeeltsparep_t mailfreeeltsparep = NIL; 1.91 + /* free envelope extra stuff callback */ 1.92 +static freeenvelopesparep_t mailfreeenvelopesparep = NIL; 1.93 + /* free body extra stuff callback */ 1.94 +static freebodysparep_t mailfreebodysparep = NIL; 1.95 + /* free stream extra stuff callback */ 1.96 +static freestreamsparep_t mailfreestreamsparep = NIL; 1.97 + /* SSL start routine */ 1.98 +static sslstart_t mailsslstart = NIL; 1.99 + /* SSL certificate query */ 1.100 +static sslcertificatequery_t mailsslcertificatequery = NIL; 1.101 + /* SSL client certificate */ 1.102 +static sslclientcert_t mailsslclientcert = NIL; 1.103 + /* SSL client private key */ 1.104 +static sslclientkey_t mailsslclientkey = NIL; 1.105 + /* SSL failure notify */ 1.106 +static sslfailure_t mailsslfailure = NIL; 1.107 + /* snarf interval */ 1.108 +static long mailsnarfinterval = 60; 1.109 + /* snarf preservation */ 1.110 +static long mailsnarfpreserve = NIL; 1.111 + /* newsrc name uses canonical host */ 1.112 +static long mailnewsrccanon = LONGT; 1.113 + 1.114 + /* supported threaders */ 1.115 +static THREADER mailthreadordsub = { 1.116 + "ORDEREDSUBJECT",mail_thread_orderedsubject,NIL 1.117 +}; 1.118 +static THREADER mailthreadlist = { 1.119 + "REFERENCES",mail_thread_references,&mailthreadordsub 1.120 +}; 1.121 + 1.122 + /* server name */ 1.123 +static char *servicename = "unknown"; 1.124 + /* server externally-set authentication ID */ 1.125 +static char *externalauthid = NIL; 1.126 +static int expungeatping = T; /* mail_ping() may call mm_expunged() */ 1.127 +static int trysslfirst = NIL; /* always try SSL first */ 1.128 +static int notimezones = NIL; /* write timezones in "From " header */ 1.129 +static int trustdns = T; /* do DNS canonicalization */ 1.130 +static int saslusesptrname = T; /* SASL uses name from DNS PTR lookup */ 1.131 + /* trustdns also must be set */ 1.132 +static int debugsensitive = NIL;/* debug telemetry includes sensitive data */ 1.133 + 1.134 +/* Default mail cache handler 1.135 + * Accepts: pointer to cache handle 1.136 + * message number 1.137 + * caching function 1.138 + * Returns: cache data 1.139 + */ 1.140 + 1.141 +void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op) 1.142 +{ 1.143 + size_t n; 1.144 + void *ret = NIL; 1.145 + unsigned long i; 1.146 + switch ((int) op) { /* what function? */ 1.147 + case CH_INIT: /* initialize cache */ 1.148 + if (stream->cache) { /* flush old cache contents */ 1.149 + while (stream->cachesize) { 1.150 + mm_cache (stream,stream->cachesize,CH_FREE); 1.151 + mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE); 1.152 + } 1.153 + fs_give ((void **) &stream->cache); 1.154 + fs_give ((void **) &stream->sc); 1.155 + stream->nmsgs = 0; /* can't have any messages now */ 1.156 + } 1.157 + break; 1.158 + case CH_SIZE: /* (re-)size the cache */ 1.159 + if (!stream->cache) { /* have a cache already? */ 1.160 + /* no, create new cache */ 1.161 + n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *); 1.162 + stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n); 1.163 + stream->sc = (SORTCACHE **) memset (fs_get (n),0,n); 1.164 + } 1.165 + /* is existing cache size large neough */ 1.166 + else if (msgno > stream->cachesize) { 1.167 + i = stream->cachesize; /* remember old size */ 1.168 + n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *); 1.169 + fs_resize ((void **) &stream->cache,n); 1.170 + fs_resize ((void **) &stream->sc,n); 1.171 + while (i < stream->cachesize) { 1.172 + stream->cache[i] = NIL; 1.173 + stream->sc[i++] = NIL; 1.174 + } 1.175 + } 1.176 + break; 1.177 + 1.178 + case CH_MAKEELT: /* return elt, make if necessary */ 1.179 + if (!stream->cache[msgno - 1]) 1.180 + stream->cache[msgno - 1] = mail_new_cache_elt (msgno); 1.181 + /* falls through */ 1.182 + case CH_ELT: /* return elt */ 1.183 + ret = (void *) stream->cache[msgno - 1]; 1.184 + break; 1.185 + case CH_SORTCACHE: /* return sortcache entry, make if needed */ 1.186 + if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] = 1.187 + (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE)); 1.188 + ret = (void *) stream->sc[msgno - 1]; 1.189 + break; 1.190 + case CH_FREE: /* free elt */ 1.191 + mail_free_elt (&stream->cache[msgno - 1]); 1.192 + break; 1.193 + case CH_FREESORTCACHE: 1.194 + if (stream->sc[msgno - 1]) { 1.195 + if (stream->sc[msgno - 1]->from) 1.196 + fs_give ((void **) &stream->sc[msgno - 1]->from); 1.197 + if (stream->sc[msgno - 1]->to) 1.198 + fs_give ((void **) &stream->sc[msgno - 1]->to); 1.199 + if (stream->sc[msgno - 1]->cc) 1.200 + fs_give ((void **) &stream->sc[msgno - 1]->cc); 1.201 + if (stream->sc[msgno - 1]->subject) 1.202 + fs_give ((void **) &stream->sc[msgno - 1]->subject); 1.203 + if (stream->sc[msgno - 1]->unique && 1.204 + (stream->sc[msgno - 1]->unique != stream->sc[msgno - 1]->message_id)) 1.205 + fs_give ((void **) &stream->sc[msgno - 1]->unique); 1.206 + if (stream->sc[msgno - 1]->message_id) 1.207 + fs_give ((void **) &stream->sc[msgno - 1]->message_id); 1.208 + if (stream->sc[msgno - 1]->references) 1.209 + mail_free_stringlist (&stream->sc[msgno - 1]->references); 1.210 + fs_give ((void **) &stream->sc[msgno - 1]); 1.211 + } 1.212 + break; 1.213 + case CH_EXPUNGE: /* expunge cache slot */ 1.214 + for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) { 1.215 + if (stream->cache[i] = stream->cache[msgno]) 1.216 + stream->cache[i]->msgno = msgno; 1.217 + stream->sc[i] = stream->sc[msgno]; 1.218 + } 1.219 + stream->cache[i] = NIL; /* top of cache goes away */ 1.220 + stream->sc[i] = NIL; 1.221 + break; 1.222 + default: 1.223 + fatal ("Bad mm_cache op"); 1.224 + break; 1.225 + } 1.226 + return ret; 1.227 +} 1.228 + 1.229 +/* Dummy string driver for complete in-memory strings */ 1.230 + 1.231 +static void mail_string_init (STRING *s,void *data,unsigned long size); 1.232 +static char mail_string_next (STRING *s); 1.233 +static void mail_string_setpos (STRING *s,unsigned long i); 1.234 + 1.235 +STRINGDRIVER mail_string = { 1.236 + mail_string_init, /* initialize string structure */ 1.237 + mail_string_next, /* get next byte in string structure */ 1.238 + mail_string_setpos /* set position in string structure */ 1.239 +}; 1.240 + 1.241 + 1.242 +/* Initialize mail string structure for in-memory string 1.243 + * Accepts: string structure 1.244 + * pointer to string 1.245 + * size of string 1.246 + */ 1.247 + 1.248 +static void mail_string_init (STRING *s,void *data,unsigned long size) 1.249 +{ 1.250 + /* set initial string pointers */ 1.251 + s->chunk = s->curpos = (char *) (s->data = data); 1.252 + /* and sizes */ 1.253 + s->size = s->chunksize = s->cursize = size; 1.254 + s->data1 = s->offset = 0; /* never any offset */ 1.255 +} 1.256 + 1.257 + 1.258 +/* Get next character from string 1.259 + * Accepts: string structure 1.260 + * Returns: character, string structure chunk refreshed 1.261 + */ 1.262 + 1.263 +static char mail_string_next (STRING *s) 1.264 +{ 1.265 + return *s->curpos++; /* return the last byte */ 1.266 +} 1.267 + 1.268 + 1.269 +/* Set string pointer position 1.270 + * Accepts: string structure 1.271 + * new position 1.272 + */ 1.273 + 1.274 +static void mail_string_setpos (STRING *s,unsigned long i) 1.275 +{ 1.276 + s->curpos = s->chunk + i; /* set new position */ 1.277 + s->cursize = s->chunksize - i;/* and new size */ 1.278 +} 1.279 + 1.280 +/* Mail routines 1.281 + * 1.282 + * mail_xxx routines are the interface between this module and the outside 1.283 + * world. Only these routines should be referenced by external callers. 1.284 + * 1.285 + * Note that there is an important difference between a "sequence" and a 1.286 + * "message #" (msgno). A sequence is a string representing a sequence in 1.287 + * {"n", "n:m", or combination separated by commas} format, whereas a msgno 1.288 + * is a single integer. 1.289 + * 1.290 + */ 1.291 + 1.292 +/* Mail version check 1.293 + * Accepts: version 1.294 + */ 1.295 + 1.296 +void mail_versioncheck (char *version) 1.297 +{ 1.298 + /* attempt to protect again wrong .h */ 1.299 + if (strcmp (version,mailcclientversion)) { 1.300 + char tmp[MAILTMPLEN]; 1.301 + sprintf (tmp,"c-client library version skew, app=%.100s library=%.100s", 1.302 + version,mailcclientversion); 1.303 + fatal (tmp); 1.304 + } 1.305 +} 1.306 + 1.307 + 1.308 +/* Mail link driver 1.309 + * Accepts: driver to add to list 1.310 + */ 1.311 + 1.312 +void mail_link (DRIVER *driver) 1.313 +{ 1.314 + DRIVER **d = &maildrivers; 1.315 + while (*d) d = &(*d)->next; /* find end of list of drivers */ 1.316 + *d = driver; /* put driver at the end */ 1.317 + driver->next = NIL; /* this driver is the end of the list */ 1.318 +} 1.319 + 1.320 +/* Mail manipulate driver parameters 1.321 + * Accepts: mail stream 1.322 + * function code 1.323 + * function-dependent value 1.324 + * Returns: function-dependent return value 1.325 + */ 1.326 + 1.327 +void *mail_parameters (MAILSTREAM *stream,long function,void *value) 1.328 +{ 1.329 + void *r,*ret = NIL; 1.330 + DRIVER *d; 1.331 + AUTHENTICATOR *a; 1.332 + switch ((int) function) { 1.333 + case SET_INBOXPATH: 1.334 + fatal ("SET_INBOXPATH not permitted"); 1.335 + case GET_INBOXPATH: 1.336 + if ((stream || (stream = mail_open (NIL,"INBOX",OP_PROTOTYPE))) && 1.337 + stream->dtb) ret = (*stream->dtb->parameters) (function,value); 1.338 + break; 1.339 + case SET_THREADERS: 1.340 + fatal ("SET_THREADERS not permitted"); 1.341 + case GET_THREADERS: /* use stream dtb instead of global */ 1.342 + ret = (stream && stream->dtb) ? 1.343 + /* KLUDGE ALERT: note stream passed as value */ 1.344 + (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist; 1.345 + break; 1.346 + case SET_NAMESPACE: 1.347 + fatal ("SET_NAMESPACE not permitted"); 1.348 + break; 1.349 + case SET_NEWSRC: /* too late on open stream */ 1.350 + if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL)))) 1.351 + fatal ("SET_NEWSRC not permitted"); 1.352 + else ret = env_parameters (function,value); 1.353 + break; 1.354 + case GET_NAMESPACE: 1.355 + case GET_NEWSRC: /* use stream dtb instead of environment */ 1.356 + ret = (stream && stream->dtb) ? 1.357 + /* KLUDGE ALERT: note stream passed as value */ 1.358 + (*stream->dtb->parameters) (function,stream) : 1.359 + env_parameters (function,value); 1.360 + break; 1.361 + case ENABLE_DEBUG: 1.362 + fatal ("ENABLE_DEBUG not permitted"); 1.363 + case DISABLE_DEBUG: 1.364 + fatal ("DISABLE_DEBUG not permitted"); 1.365 + case SET_DIRFMTTEST: 1.366 + fatal ("SET_DIRFMTTEST not permitted"); 1.367 + case GET_DIRFMTTEST: 1.368 + if (!(stream && stream->dtb && 1.369 + (ret = (*stream->dtb->parameters) (function,NIL)))) 1.370 + fatal ("GET_DIRFMTTEST not permitted"); 1.371 + break; 1.372 + 1.373 + case SET_DRIVERS: 1.374 + fatal ("SET_DRIVERS not permitted"); 1.375 + case GET_DRIVERS: /* always return global */ 1.376 + ret = (void *) maildrivers; 1.377 + break; 1.378 + case SET_DRIVER: 1.379 + fatal ("SET_DRIVER not permitted"); 1.380 + case GET_DRIVER: 1.381 + for (d = maildrivers; d && compare_cstring (d->name,(char *) value); 1.382 + d = d->next); 1.383 + ret = (void *) d; 1.384 + break; 1.385 + case ENABLE_DRIVER: 1.386 + for (d = maildrivers; d && compare_cstring (d->name,(char *) value); 1.387 + d = d->next); 1.388 + if (ret = (void *) d) d->flags &= ~DR_DISABLE; 1.389 + break; 1.390 + case DISABLE_DRIVER: 1.391 + for (d = maildrivers; d && compare_cstring (d->name,(char *) value); 1.392 + d = d->next); 1.393 + if (ret = (void *) d) d->flags |= DR_DISABLE; 1.394 + break; 1.395 + case ENABLE_AUTHENTICATOR: 1.396 + for (a = mailauthenticators;/* scan authenticators */ 1.397 + a && compare_cstring (a->name,(char *) value); a = a->next); 1.398 + if (ret = (void *) a) a->flags &= ~AU_DISABLE; 1.399 + break; 1.400 + case DISABLE_AUTHENTICATOR: 1.401 + for (a = mailauthenticators;/* scan authenticators */ 1.402 + a && compare_cstring (a->name,(char *) value); a = a->next); 1.403 + if (ret = (void *) a) a->flags |= AU_DISABLE; 1.404 + break; 1.405 + case UNHIDE_AUTHENTICATOR: 1.406 + for (a = mailauthenticators;/* scan authenticators */ 1.407 + a && compare_cstring (a->name,(char *) value); a = a->next); 1.408 + if (ret = (void *) a) a->flags &= ~AU_HIDE; 1.409 + break; 1.410 + case HIDE_AUTHENTICATOR: 1.411 + for (a = mailauthenticators;/* scan authenticators */ 1.412 + a && compare_cstring (a->name,(char *) value); a = a->next); 1.413 + if (ret = (void *) a) a->flags |= AU_HIDE; 1.414 + break; 1.415 + case SET_EXTERNALAUTHID: 1.416 + if (value) { /* setting external authentication ID */ 1.417 + externalauthid = cpystr ((char *) value); 1.418 + mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"EXTERNAL"); 1.419 + } 1.420 + else { /* clearing external authentication ID */ 1.421 + if (externalauthid) fs_give ((void **) &externalauthid); 1.422 + mail_parameters (NIL,HIDE_AUTHENTICATOR,"EXTERNAL"); 1.423 + } 1.424 + case GET_EXTERNALAUTHID: 1.425 + ret = (void *) externalauthid; 1.426 + break; 1.427 + 1.428 + case SET_GETS: 1.429 + mailgets = (mailgets_t) value; 1.430 + case GET_GETS: 1.431 + ret = (void *) mailgets; 1.432 + break; 1.433 + case SET_READPROGRESS: 1.434 + mailreadprogress = (readprogress_t) value; 1.435 + case GET_READPROGRESS: 1.436 + ret = (void *) mailreadprogress; 1.437 + break; 1.438 + case SET_CACHE: 1.439 + mailcache = (mailcache_t) value; 1.440 + case GET_CACHE: 1.441 + ret = (void *) mailcache; 1.442 + break; 1.443 + case SET_RFC822OUTPUT: 1.444 + mail822out = (rfc822out_t) value; 1.445 + case GET_RFC822OUTPUT: 1.446 + ret = (void *) mail822out; 1.447 + break; 1.448 + case SET_RFC822OUTPUTFULL: 1.449 + mail822outfull = (rfc822outfull_t) value; 1.450 + case GET_RFC822OUTPUTFULL: 1.451 + ret = (void *) mail822outfull; 1.452 + break; 1.453 + case SET_SMTPVERBOSE: 1.454 + mailsmtpverbose = (smtpverbose_t) value; 1.455 + case GET_SMTPVERBOSE: 1.456 + ret = (void *) mailsmtpverbose; 1.457 + break; 1.458 + case SET_MAILPROXYCOPY: 1.459 + mailproxycopy = (mailproxycopy_t) value; 1.460 + case GET_MAILPROXYCOPY: 1.461 + ret = (void *) mailproxycopy; 1.462 + break; 1.463 + case SET_PARSELINE: 1.464 + mailparseline = (parseline_t) value; 1.465 + case GET_PARSELINE: 1.466 + ret = (void *) mailparseline; 1.467 + break; 1.468 + case SET_PARSEPHRASE: 1.469 + mailparsephrase = (parsephrase_t) value; 1.470 + case GET_PARSEPHRASE: 1.471 + ret = (void *) mailparsephrase; 1.472 + break; 1.473 + case SET_NEWSRCQUERY: 1.474 + mailnewsrcquery = (newsrcquery_t) value; 1.475 + case GET_NEWSRCQUERY: 1.476 + ret = (void *) mailnewsrcquery; 1.477 + break; 1.478 + case SET_NEWSRCCANONHOST: 1.479 + mailnewsrccanon = (long) value; 1.480 + case GET_NEWSRCCANONHOST: 1.481 + ret = (void *) mailnewsrccanon; 1.482 + break; 1.483 + 1.484 + case SET_COPYUID: 1.485 + mailcopyuid = (copyuid_t) value; 1.486 + case GET_COPYUID: 1.487 + ret = (void *) mailcopyuid; 1.488 + break; 1.489 + case SET_APPENDUID: 1.490 + mailappenduid = (appenduid_t) value; 1.491 + case GET_APPENDUID: 1.492 + ret = (void *) mailappenduid; 1.493 + break; 1.494 + case SET_FREEENVELOPESPAREP: 1.495 + mailfreeenvelopesparep = (freeenvelopesparep_t) value; 1.496 + case GET_FREEENVELOPESPAREP: 1.497 + ret = (void *) mailfreeenvelopesparep; 1.498 + break; 1.499 + case SET_FREEELTSPAREP: 1.500 + mailfreeeltsparep = (freeeltsparep_t) value; 1.501 + case GET_FREEELTSPAREP: 1.502 + ret = (void *) mailfreeeltsparep; 1.503 + break; 1.504 + case SET_FREESTREAMSPAREP: 1.505 + mailfreestreamsparep = (freestreamsparep_t) value; 1.506 + case GET_FREESTREAMSPAREP: 1.507 + ret = (void *) mailfreestreamsparep; 1.508 + break; 1.509 + case SET_FREEBODYSPAREP: 1.510 + mailfreebodysparep = (freebodysparep_t) value; 1.511 + case GET_FREEBODYSPAREP: 1.512 + ret = (void *) mailfreebodysparep; 1.513 + break; 1.514 + 1.515 + case SET_SSLSTART: 1.516 + mailsslstart = (sslstart_t) value; 1.517 + case GET_SSLSTART: 1.518 + ret = (void *) mailsslstart; 1.519 + break; 1.520 + case SET_SSLCERTIFICATEQUERY: 1.521 + mailsslcertificatequery = (sslcertificatequery_t) value; 1.522 + case GET_SSLCERTIFICATEQUERY: 1.523 + ret = (void *) mailsslcertificatequery; 1.524 + break; 1.525 + case SET_SSLCLIENTCERT: 1.526 + mailsslclientcert = (sslclientcert_t) value; 1.527 + case GET_SSLCLIENTCERT: 1.528 + ret = (void *) mailsslclientcert; 1.529 + break; 1.530 + case SET_SSLCLIENTKEY: 1.531 + mailsslclientkey = (sslclientkey_t) value; 1.532 + case GET_SSLCLIENTKEY: 1.533 + ret = (void *) mailsslclientkey; 1.534 + break; 1.535 + case SET_SSLFAILURE: 1.536 + mailsslfailure = (sslfailure_t) value; 1.537 + case GET_SSLFAILURE: 1.538 + ret = (void *) mailsslfailure; 1.539 + break; 1.540 + case SET_KINIT: 1.541 + mailkinit = (kinit_t) value; 1.542 + case GET_KINIT: 1.543 + ret = (void *) mailkinit; 1.544 + break; 1.545 + case SET_SENDCOMMAND: 1.546 + mailsendcommand = (sendcommand_t) value; 1.547 + case GET_SENDCOMMAND: 1.548 + ret = (void *) mailsendcommand; 1.549 + break; 1.550 + 1.551 + case SET_SERVICENAME: 1.552 + servicename = (char *) value; 1.553 + case GET_SERVICENAME: 1.554 + ret = (void *) servicename; 1.555 + break; 1.556 + case SET_EXPUNGEATPING: 1.557 + expungeatping = (value ? T : NIL); 1.558 + case GET_EXPUNGEATPING: 1.559 + ret = (void *) (expungeatping ? VOIDT : NIL); 1.560 + break; 1.561 + case SET_SORTRESULTS: 1.562 + mailsortresults = (sortresults_t) value; 1.563 + case GET_SORTRESULTS: 1.564 + ret = (void *) mailsortresults; 1.565 + break; 1.566 + case SET_THREADRESULTS: 1.567 + mailthreadresults = (threadresults_t) value; 1.568 + case GET_THREADRESULTS: 1.569 + ret = (void *) mailthreadresults; 1.570 + break; 1.571 + case SET_SSLDRIVER: 1.572 + mailssldriver = (NETDRIVER *) value; 1.573 + case GET_SSLDRIVER: 1.574 + ret = (void *) mailssldriver; 1.575 + break; 1.576 + case SET_TRYSSLFIRST: 1.577 + trysslfirst = (value ? T : NIL); 1.578 + case GET_TRYSSLFIRST: 1.579 + ret = (void *) (trysslfirst ? VOIDT : NIL); 1.580 + break; 1.581 + case SET_NOTIMEZONES: 1.582 + notimezones = (value ? T : NIL); 1.583 + case GET_NOTIMEZONES: 1.584 + ret = (void *) (notimezones ? VOIDT : NIL); 1.585 + break; 1.586 + case SET_TRUSTDNS: 1.587 + trustdns = (value ? T : NIL); 1.588 + case GET_TRUSTDNS: 1.589 + ret = (void *) (trustdns ? VOIDT : NIL); 1.590 + break; 1.591 + case SET_SASLUSESPTRNAME: 1.592 + saslusesptrname = (value ? T : NIL); 1.593 + case GET_SASLUSESPTRNAME: 1.594 + ret = (void *) (saslusesptrname ? VOIDT : NIL); 1.595 + break; 1.596 + case SET_DEBUGSENSITIVE: 1.597 + debugsensitive = (value ? T : NIL); 1.598 + case GET_DEBUGSENSITIVE: 1.599 + ret = (void *) (debugsensitive ? VOIDT : NIL); 1.600 + break; 1.601 + 1.602 + case SET_ACL: 1.603 + mailaclresults = (getacl_t) value; 1.604 + case GET_ACL: 1.605 + ret = (void *) mailaclresults; 1.606 + break; 1.607 + case SET_LISTRIGHTS: 1.608 + maillistrightsresults = (listrights_t) value; 1.609 + case GET_LISTRIGHTS: 1.610 + ret = (void *) maillistrightsresults; 1.611 + break; 1.612 + case SET_MYRIGHTS: 1.613 + mailmyrightsresults = (myrights_t) value; 1.614 + case GET_MYRIGHTS: 1.615 + ret = (void *) mailmyrightsresults; 1.616 + break; 1.617 + case SET_QUOTA: 1.618 + mailquotaresults = (quota_t) value; 1.619 + case GET_QUOTA: 1.620 + ret = (void *) mailquotaresults; 1.621 + break; 1.622 + case SET_QUOTAROOT: 1.623 + mailquotarootresults = (quotaroot_t) value; 1.624 + case GET_QUOTAROOT: 1.625 + ret = (void *) mailquotarootresults; 1.626 + break; 1.627 + case SET_SNARFINTERVAL: 1.628 + mailsnarfinterval = (long) value; 1.629 + case GET_SNARFINTERVAL: 1.630 + ret = (void *) mailsnarfinterval; 1.631 + break; 1.632 + case SET_SNARFPRESERVE: 1.633 + mailsnarfpreserve = (long) value; 1.634 + case GET_SNARFPRESERVE: 1.635 + ret = (void *) mailsnarfpreserve; 1.636 + break; 1.637 + case SET_SNARFMAILBOXNAME: 1.638 + if (stream) { /* have a stream? */ 1.639 + if (stream->snarf.name) fs_give ((void **) &stream->snarf.name); 1.640 + stream->snarf.name = cpystr ((char *) value); 1.641 + } 1.642 + else fatal ("SET_SNARFMAILBOXNAME with no stream"); 1.643 + case GET_SNARFMAILBOXNAME: 1.644 + if (stream) ret = (void *) stream->snarf.name; 1.645 + break; 1.646 + default: 1.647 + if (r = smtp_parameters (function,value)) ret = r; 1.648 + if (r = env_parameters (function,value)) ret = r; 1.649 + if (r = tcp_parameters (function,value)) ret = r; 1.650 + if (stream && stream->dtb) {/* if have stream, do for its driver only */ 1.651 + if (r = (*stream->dtb->parameters) (function,value)) ret = r; 1.652 + } 1.653 + /* else do all drivers */ 1.654 + else for (d = maildrivers; d; d = d->next) 1.655 + if (r = (d->parameters) (function,value)) ret = r; 1.656 + break; 1.657 + } 1.658 + return ret; 1.659 +} 1.660 + 1.661 +/* Mail validate mailbox name 1.662 + * Accepts: MAIL stream 1.663 + * mailbox name 1.664 + * purpose string for error message 1.665 + * Return: driver factory on success, NIL on failure 1.666 + */ 1.667 + 1.668 +DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose) 1.669 +{ 1.670 + char tmp[MAILTMPLEN]; 1.671 + DRIVER *factory = NIL; 1.672 + /* never allow names with newlines */ 1.673 + if (strpbrk (mailbox,"\015\012")) { 1.674 + if (purpose) { /* if want an error message */ 1.675 + sprintf (tmp,"Can't %s with such a name",purpose); 1.676 + MM_LOG (tmp,ERROR); 1.677 + } 1.678 + return NIL; 1.679 + } 1.680 + /* validate name, find driver factory */ 1.681 + if (strlen (mailbox) < (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) 1.682 + for (factory = maildrivers; factory && 1.683 + ((factory->flags & DR_DISABLE) || 1.684 + ((factory->flags & DR_LOCAL) && (*mailbox == '{')) || 1.685 + !(*factory->valid) (mailbox)); 1.686 + factory = factory->next); 1.687 + /* validate factory against non-dummy stream */ 1.688 + if (factory && stream && stream->dtb && (stream->dtb != factory) && 1.689 + strcmp (stream->dtb->name,"dummy")) 1.690 + /* factory invalid; if dummy, use stream */ 1.691 + factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb; 1.692 + if (!factory && purpose) { /* if want an error message */ 1.693 + sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ? 1.694 + "invalid remote specification" : "no such mailbox"); 1.695 + MM_LOG (tmp,ERROR); 1.696 + } 1.697 + return factory; /* return driver factory */ 1.698 +} 1.699 + 1.700 +/* Mail validate network mailbox name 1.701 + * Accepts: mailbox name 1.702 + * mailbox driver to validate against 1.703 + * pointer to where to return host name if non-NIL 1.704 + * pointer to where to return mailbox name if non-NIL 1.705 + * Returns: driver on success, NIL on failure 1.706 + */ 1.707 + 1.708 +DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox) 1.709 +{ 1.710 + NETMBX mb; 1.711 + if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name)) 1.712 + return NIL; 1.713 + if (host) strcpy (host,mb.host); 1.714 + if (mailbox) strcpy (mailbox,mb.mailbox); 1.715 + return drv; 1.716 +} 1.717 + 1.718 + 1.719 +/* Mail validate network mailbox name 1.720 + * Accepts: mailbox name 1.721 + * NETMBX structure to return values 1.722 + * Returns: T on success, NIL on failure 1.723 + */ 1.724 + 1.725 +long mail_valid_net_parse (char *name,NETMBX *mb) 1.726 +{ 1.727 + return mail_valid_net_parse_work (name,mb,"imap"); 1.728 +} 1.729 + 1.730 +/* Mail validate network mailbox name worker routine 1.731 + * Accepts: mailbox name 1.732 + * NETMBX structure to return values 1.733 + * default service 1.734 + * Returns: T on success, NIL on failure 1.735 + */ 1.736 + 1.737 +long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service) 1.738 +{ 1.739 + int i,j; 1.740 + char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN]; 1.741 + /* initialize structure */ 1.742 + memset (mb,'\0',sizeof (NETMBX)); 1.743 + /* must have host specification */ 1.744 + if (*name++ != '{') return NIL; 1.745 + if (*name == '[') { /* if domain literal, find its ending */ 1.746 + if (!((v = strpbrk (name,"]}")) && (*v++ == ']'))) return NIL; 1.747 + } 1.748 + /* find end of host name */ 1.749 + else if (!(v = strpbrk (name,"/:}"))) return NIL; 1.750 + /* validate length, find mailbox part */ 1.751 + if (!((i = v - name) && (i < NETMAXHOST) && (t = strchr (v,'}')) && 1.752 + ((j = t - v) < MAILTMPLEN) && (strlen (t+1) < (size_t) NETMAXMBX))) 1.753 + return NIL; /* invalid mailbox */ 1.754 + strncpy (mb->host,name,i); /* set host name */ 1.755 + strncpy (mb->orighost,name,i); 1.756 + mb->host[i] = mb->orighost[i] = '\0'; 1.757 + strcpy (mb->mailbox,t+1); /* set mailbox name */ 1.758 + if (t - v) { /* any switches or port specification? */ 1.759 + strncpy (t = tmp,v,j); /* copy it */ 1.760 + tmp[j] = '\0'; /* tie it off */ 1.761 + c = *t++; /* get first delimiter */ 1.762 + do switch (c) { /* act based upon the character */ 1.763 + case ':': /* port specification */ 1.764 + if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL; 1.765 + c = t ? *t++ : '\0'; /* get delimiter, advance pointer */ 1.766 + break; 1.767 + case '/': /* switch */ 1.768 + /* find delimiter */ 1.769 + if (t = strpbrk (s = t,"/:=")) { 1.770 + c = *t; /* remember delimiter for later */ 1.771 + *t++ = '\0'; /* tie off switch name */ 1.772 + } 1.773 + else c = '\0'; /* no delimiter */ 1.774 + if (c == '=') { /* parse switches which take arguments */ 1.775 + if (*t == '"') { /* quoted string? */ 1.776 + for (v = arg,i = 0,++t; (c = *t++) != '"';) { 1.777 + if (!c) return NIL; /* unterminated string */ 1.778 + /* quote next character */ 1.779 + if (c == '\\') c = *t++; 1.780 + if (!c) return NIL; /* can't quote NUL either */ 1.781 + arg[i++] = c; 1.782 + } 1.783 + c = *t++; /* remember delimiter for later */ 1.784 + arg[i] = '\0'; /* tie off argument */ 1.785 + } 1.786 + else { /* non-quoted argument */ 1.787 + if (t = strpbrk (v = t,"/:")) { 1.788 + c = *t; /* remember delimiter for later */ 1.789 + *t++ = '\0'; /* tie off switch name */ 1.790 + } 1.791 + else c = '\0'; /* no delimiter */ 1.792 + i = strlen (v); /* length of argument */ 1.793 + } 1.794 + if (!compare_cstring (s,"service") && (i < NETMAXSRV) && !*mb->service) 1.795 + lcase (strcpy (mb->service,v)); 1.796 + else if (!compare_cstring (s,"user") && (i < NETMAXUSER) && !*mb->user) 1.797 + strcpy (mb->user,v); 1.798 + else if (!compare_cstring (s,"authuser") && (i < NETMAXUSER) && 1.799 + !*mb->authuser) strcpy (mb->authuser,v); 1.800 + else return NIL; 1.801 + } 1.802 + 1.803 + else { /* non-argument switch */ 1.804 + if (!compare_cstring (s,"anonymous")) mb->anoflag = T; 1.805 + else if (!compare_cstring (s,"debug")) mb->dbgflag = T; 1.806 + else if (!compare_cstring (s,"readonly")) mb->readonlyflag = T; 1.807 + else if (!compare_cstring (s,"secure")) mb->secflag = T; 1.808 + else if (!compare_cstring (s,"norsh")) mb->norsh = T; 1.809 + else if (!compare_cstring (s,"loser")) mb->loser = T; 1.810 + else if (!compare_cstring (s,"tls") && !mb->notlsflag) 1.811 + mb->tlsflag = T; 1.812 + else if (!compare_cstring (s,"tls-sslv23") && !mb->notlsflag) 1.813 + mb->tlssslv23 = mb->tlsflag = T; 1.814 + else if (!compare_cstring (s,"notls") && !mb->tlsflag) 1.815 + mb->notlsflag = T; 1.816 + else if (!compare_cstring (s,"tryssl")) 1.817 + mb->trysslflag = mailssldriver? T : NIL; 1.818 + else if (mailssldriver && !compare_cstring (s,"ssl") && !mb->tlsflag) 1.819 + mb->sslflag = mb->notlsflag = T; 1.820 + else if (mailssldriver && !compare_cstring (s,"novalidate-cert")) 1.821 + mb->novalidate = T; 1.822 + /* hack for compatibility with the past */ 1.823 + else if (mailssldriver && !compare_cstring (s,"validate-cert")); 1.824 + /* service switches below here */ 1.825 + else if (*mb->service) return NIL; 1.826 + else if (!compare_cstring (s,"imap") || 1.827 + !compare_cstring (s,"nntp") || 1.828 + !compare_cstring (s,"pop3") || 1.829 + !compare_cstring (s,"smtp") || 1.830 + !compare_cstring (s,"submit")) 1.831 + lcase (strcpy (mb->service,s)); 1.832 + else if (!compare_cstring (s,"imap2") || 1.833 + !compare_cstring (s,"imap2bis") || 1.834 + !compare_cstring (s,"imap4") || 1.835 + !compare_cstring (s,"imap4rev1")) 1.836 + strcpy (mb->service,"imap"); 1.837 + else if (!compare_cstring (s,"pop")) 1.838 + strcpy (mb->service,"pop3"); 1.839 + else return NIL; /* invalid non-argument switch */ 1.840 + } 1.841 + break; 1.842 + default: /* anything else is bogus */ 1.843 + return NIL; 1.844 + } while (c); /* see if anything more to parse */ 1.845 + } 1.846 + /* default mailbox name */ 1.847 + if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX"); 1.848 + /* default service name */ 1.849 + if (!*mb->service) strcpy (mb->service,service); 1.850 + /* /norsh only valid if imap */ 1.851 + if (mb->norsh && strcmp (mb->service,"imap")) return NIL; 1.852 + return T; 1.853 +} 1.854 + 1.855 +/* Mail scan mailboxes for string 1.856 + * Accepts: mail stream 1.857 + * reference 1.858 + * pattern to search 1.859 + * contents to search 1.860 + */ 1.861 + 1.862 +void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) 1.863 +{ 1.864 + int remote = ((*pat == '{') || (ref && *ref == '{')); 1.865 + DRIVER *d; 1.866 + if (ref && (strlen (ref) > NETMAXMBX)) { 1.867 + char tmp[MAILTMPLEN]; 1.868 + sprintf (tmp,"Invalid LIST reference specification: %.80s",ref); 1.869 + MM_LOG (tmp,ERROR); 1.870 + return; 1.871 + } 1.872 + if (strlen (pat) > NETMAXMBX) { 1.873 + char tmp[MAILTMPLEN]; 1.874 + sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat); 1.875 + MM_LOG (tmp,ERROR); 1.876 + return; 1.877 + } 1.878 + if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */ 1.879 + if (stream) { /* if have a stream, do it for that stream */ 1.880 + if ((d = stream->dtb) && d->scan && 1.881 + !(((d->flags & DR_LOCAL) && remote))) 1.882 + (*d->scan) (stream,ref,pat,contents); 1.883 + } 1.884 + /* otherwise do for all DTB's */ 1.885 + else for (d = maildrivers; d; d = d->next) 1.886 + if (d->scan && !((d->flags & DR_DISABLE) || 1.887 + ((d->flags & DR_LOCAL) && remote))) 1.888 + (d->scan) (NIL,ref,pat,contents); 1.889 +} 1.890 + 1.891 +/* Mail list mailboxes 1.892 + * Accepts: mail stream 1.893 + * reference 1.894 + * pattern to search 1.895 + */ 1.896 + 1.897 +void mail_list (MAILSTREAM *stream,char *ref,char *pat) 1.898 +{ 1.899 + int remote = ((*pat == '{') || (ref && *ref == '{')); 1.900 + DRIVER *d = maildrivers; 1.901 + if (ref && (strlen (ref) > NETMAXMBX)) { 1.902 + char tmp[MAILTMPLEN]; 1.903 + sprintf (tmp,"Invalid LIST reference specification: %.80s",ref); 1.904 + MM_LOG (tmp,ERROR); 1.905 + return; 1.906 + } 1.907 + if (strlen (pat) > NETMAXMBX) { 1.908 + char tmp[MAILTMPLEN]; 1.909 + sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat); 1.910 + MM_LOG (tmp,ERROR); 1.911 + return; 1.912 + } 1.913 + if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */ 1.914 + if (stream && stream->dtb) { /* if have a stream, do it for that stream */ 1.915 + if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote)) 1.916 + (*d->list) (stream,ref,pat); 1.917 + } 1.918 + /* otherwise do for all DTB's */ 1.919 + else do if (!((d->flags & DR_DISABLE) || 1.920 + ((d->flags & DR_LOCAL) && remote))) 1.921 + (d->list) (NIL,ref,pat); 1.922 + while (d = d->next); /* until at the end */ 1.923 +} 1.924 + 1.925 +/* Mail list subscribed mailboxes 1.926 + * Accepts: mail stream 1.927 + * pattern to search 1.928 + */ 1.929 + 1.930 +void mail_lsub (MAILSTREAM *stream,char *ref,char *pat) 1.931 +{ 1.932 + int remote = ((*pat == '{') || (ref && *ref == '{')); 1.933 + DRIVER *d = maildrivers; 1.934 + if (ref && (strlen (ref) > NETMAXMBX)) { 1.935 + char tmp[MAILTMPLEN]; 1.936 + sprintf (tmp,"Invalid LSUB reference specification: %.80s",ref); 1.937 + MM_LOG (tmp,ERROR); 1.938 + return; 1.939 + } 1.940 + if (strlen (pat) > NETMAXMBX) { 1.941 + char tmp[MAILTMPLEN]; 1.942 + sprintf (tmp,"Invalid LSUB pattern specification: %.80s",pat); 1.943 + MM_LOG (tmp,ERROR); 1.944 + return; 1.945 + } 1.946 + if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */ 1.947 + if (stream && stream->dtb) { /* if have a stream, do it for that stream */ 1.948 + if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote)) 1.949 + (*d->lsub) (stream,ref,pat); 1.950 + } 1.951 + /* otherwise do for all DTB's */ 1.952 + else do if (!((d->flags & DR_DISABLE) || 1.953 + ((d->flags & DR_LOCAL) && remote))) 1.954 + (d->lsub) (NIL,ref,pat); 1.955 + while (d = d->next); /* until at the end */ 1.956 +} 1.957 + 1.958 +/* Mail subscribe to mailbox 1.959 + * Accepts: mail stream 1.960 + * mailbox to add to subscription list 1.961 + * Returns: T on success, NIL on failure 1.962 + */ 1.963 + 1.964 +long mail_subscribe (MAILSTREAM *stream,char *mailbox) 1.965 +{ 1.966 + DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox"); 1.967 + return factory ? 1.968 + (factory->subscribe ? 1.969 + (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL; 1.970 +} 1.971 + 1.972 + 1.973 +/* Mail unsubscribe to mailbox 1.974 + * Accepts: mail stream 1.975 + * mailbox to delete from subscription list 1.976 + * Returns: T on success, NIL on failure 1.977 + */ 1.978 + 1.979 +long mail_unsubscribe (MAILSTREAM *stream,char *mailbox) 1.980 +{ 1.981 + DRIVER *factory = mail_valid (stream,mailbox,NIL); 1.982 + return (factory && factory->unsubscribe) ? 1.983 + (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox); 1.984 +} 1.985 + 1.986 +/* Mail create mailbox 1.987 + * Accepts: mail stream 1.988 + * mailbox name to create 1.989 + * Returns: T on success, NIL on failure 1.990 + */ 1.991 + 1.992 +long mail_create (MAILSTREAM *stream,char *mailbox) 1.993 +{ 1.994 + MAILSTREAM *ts; 1.995 + char *s,*t,tmp[MAILTMPLEN]; 1.996 + size_t i; 1.997 + DRIVER *d; 1.998 + /* never allow names with newlines */ 1.999 + if (s = strpbrk (mailbox,"\015\012")) { 1.1000 + MM_LOG ("Can't create mailbox with such a name",ERROR); 1.1001 + return NIL; 1.1002 + } 1.1003 + if (strlen (mailbox) >= (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) { 1.1004 + sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ? 1.1005 + "invalid remote specification" : "no such mailbox"); 1.1006 + MM_LOG (tmp,ERROR); 1.1007 + return NIL; 1.1008 + } 1.1009 + /* create of INBOX invalid */ 1.1010 + if (!compare_cstring (mailbox,"INBOX")) { 1.1011 + MM_LOG ("Can't create INBOX",ERROR); 1.1012 + return NIL; 1.1013 + } 1.1014 + /* validate name */ 1.1015 + if (s = mail_utf7_valid (mailbox)) { 1.1016 + sprintf (tmp,"Can't create %s: %.80s",s,mailbox); 1.1017 + MM_LOG (tmp,ERROR); 1.1018 + return NIL; 1.1019 + } 1.1020 + 1.1021 + /* see if special driver hack */ 1.1022 + if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) && 1.1023 + ((mailbox[2] == 'r') || (mailbox[2] == 'R')) && 1.1024 + ((mailbox[3] == 'i') || (mailbox[3] == 'I')) && 1.1025 + ((mailbox[4] == 'v') || (mailbox[4] == 'V')) && 1.1026 + ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && 1.1027 + ((mailbox[6] == 'r') || (mailbox[6] == 'R')) && (mailbox[7] == '.')) { 1.1028 + /* copy driver until likely delimiter */ 1.1029 + if ((s = strpbrk (t = mailbox+8,"/\\:")) && (i = s - t)) { 1.1030 + strncpy (tmp,t,i); 1.1031 + tmp[i] = '\0'; 1.1032 + } 1.1033 + else { 1.1034 + sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox); 1.1035 + MM_LOG (tmp,ERROR); 1.1036 + return NIL; 1.1037 + } 1.1038 + for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next); 1.1039 + if (d) mailbox = ++s; /* skip past driver specification */ 1.1040 + else { 1.1041 + sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox); 1.1042 + MM_LOG (tmp,ERROR); 1.1043 + return NIL; 1.1044 + } 1.1045 + } 1.1046 + /* use stream if one given or deterministic */ 1.1047 + else if ((stream && stream->dtb) || 1.1048 + (((*mailbox == '{') || (*mailbox == '#')) && 1.1049 + (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) 1.1050 + d = stream->dtb; 1.1051 + else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; 1.1052 + else { /* failed utterly */ 1.1053 + sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); 1.1054 + MM_LOG (tmp,ERROR); 1.1055 + return NIL; 1.1056 + } 1.1057 + return (*d->create) (stream,mailbox); 1.1058 +} 1.1059 + 1.1060 +/* Mail delete mailbox 1.1061 + * Accepts: mail stream 1.1062 + * mailbox name to delete 1.1063 + * Returns: T on success, NIL on failure 1.1064 + */ 1.1065 + 1.1066 +long mail_delete (MAILSTREAM *stream,char *mailbox) 1.1067 +{ 1.1068 + DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox"); 1.1069 + if (!dtb) return NIL; 1.1070 + if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && 1.1071 + ((mailbox[1] == 'N') || (mailbox[1] == 'n')) && 1.1072 + ((mailbox[2] == 'B') || (mailbox[2] == 'b')) && 1.1073 + ((mailbox[3] == 'O') || (mailbox[3] == 'o')) && 1.1074 + ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) { 1.1075 + MM_LOG ("Can't delete INBOX",ERROR); 1.1076 + return NIL; 1.1077 + } 1.1078 + return SAFE_DELETE (dtb,stream,mailbox); 1.1079 +} 1.1080 + 1.1081 + 1.1082 +/* Mail rename mailbox 1.1083 + * Accepts: mail stream 1.1084 + * old mailbox name 1.1085 + * new mailbox name 1.1086 + * Returns: T on success, NIL on failure 1.1087 + */ 1.1088 + 1.1089 +long mail_rename (MAILSTREAM *stream,char *old,char *newname) 1.1090 +{ 1.1091 + char *s,tmp[MAILTMPLEN]; 1.1092 + DRIVER *dtb = mail_valid (stream,old,"rename mailbox"); 1.1093 + if (!dtb) return NIL; 1.1094 + /* validate name */ 1.1095 + if (s = mail_utf7_valid (newname)) { 1.1096 + sprintf (tmp,"Can't rename to %s: %.80s",s,newname); 1.1097 + MM_LOG (tmp,ERROR); 1.1098 + return NIL; 1.1099 + } 1.1100 + if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) { 1.1101 + sprintf (tmp,"Can't rename %.80s: mailbox %.80s already exists", 1.1102 + old,newname); 1.1103 + MM_LOG (tmp,ERROR); 1.1104 + return NIL; 1.1105 + } 1.1106 + return SAFE_RENAME (dtb,stream,old,newname); 1.1107 +} 1.1108 + 1.1109 +/* Validate mailbox as Modified UTF-7 1.1110 + * Accepts: candidate mailbox name 1.1111 + * Returns: error string if error, NIL if valid 1.1112 + */ 1.1113 + 1.1114 +char *mail_utf7_valid (char *mailbox) 1.1115 +{ 1.1116 + char *s; 1.1117 + for (s = mailbox; *s; s++) { /* make sure valid name */ 1.1118 + /* reserved for future use with UTF-8 */ 1.1119 + if (*s & 0x80) return "mailbox name with 8-bit octet"; 1.1120 + /* validate modified UTF-7 */ 1.1121 + else if (*s == '&') while (*++s != '-') switch (*s) { 1.1122 + case '\0': 1.1123 + return "unterminated modified UTF-7 name"; 1.1124 + case '+': /* valid modified BASE64 */ 1.1125 + case ',': 1.1126 + break; /* all OK so far */ 1.1127 + default: /* must be alphanumeric */ 1.1128 + if (!isalnum (*s)) return "invalid modified UTF-7 name"; 1.1129 + break; 1.1130 + } 1.1131 + } 1.1132 + return NIL; /* all OK */ 1.1133 +} 1.1134 + 1.1135 +/* Mail status of mailbox 1.1136 + * Accepts: mail stream if open on this mailbox 1.1137 + * mailbox name 1.1138 + * status flags 1.1139 + * Returns: T on success, NIL on failure 1.1140 + */ 1.1141 + 1.1142 +long mail_status (MAILSTREAM *stream,char *mbx,long flags) 1.1143 +{ 1.1144 + DRIVER *dtb = mail_valid (stream,mbx,"get status of mailbox"); 1.1145 + if (!dtb) return NIL; /* only if valid */ 1.1146 + if (stream && ((dtb != stream->dtb) || 1.1147 + ((dtb->flags & DR_LOCAL) && strcmp (mbx,stream->mailbox) && 1.1148 + strcmp (mbx,stream->original_mailbox)))) 1.1149 + stream = NIL; /* stream not suitable */ 1.1150 + return SAFE_STATUS (dtb,stream,mbx,flags); 1.1151 +} 1.1152 + 1.1153 + 1.1154 +/* Mail status of mailbox default handler 1.1155 + * Accepts: mail stream 1.1156 + * mailbox name 1.1157 + * status flags 1.1158 + * Returns: T on success, NIL on failure 1.1159 + */ 1.1160 + 1.1161 +long mail_status_default (MAILSTREAM *stream,char *mbx,long flags) 1.1162 +{ 1.1163 + MAILSTATUS status; 1.1164 + unsigned long i; 1.1165 + MAILSTREAM *tstream = NIL; 1.1166 + /* make temporary stream (unless this mbx) */ 1.1167 + if (!stream && !(stream = tstream = 1.1168 + mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; 1.1169 + status.flags = flags; /* return status values */ 1.1170 + status.messages = stream->nmsgs; 1.1171 + status.recent = stream->recent; 1.1172 + if (flags & SA_UNSEEN) /* must search to get unseen messages */ 1.1173 + for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) 1.1174 + if (!mail_elt (stream,i)->seen) status.unseen++; 1.1175 + status.uidnext = stream->uid_last + 1; 1.1176 + status.uidvalidity = stream->uid_validity; 1.1177 + MM_STATUS(stream,mbx,&status);/* pass status to main program */ 1.1178 + if (tstream) mail_close (tstream); 1.1179 + return T; /* success */ 1.1180 +} 1.1181 + 1.1182 +/* Mail open 1.1183 + * Accepts: candidate stream for recycling 1.1184 + * mailbox name 1.1185 + * open options 1.1186 + * Returns: stream to use on success, NIL on failure 1.1187 + */ 1.1188 + 1.1189 +MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options) 1.1190 +{ 1.1191 + int i; 1.1192 + char c,*s,tmp[MAILTMPLEN]; 1.1193 + NETMBX mb; 1.1194 + DRIVER *d; 1.1195 + switch (name[0]) { /* see if special handling */ 1.1196 + case '#': /* possible special hacks */ 1.1197 + if (((name[1] == 'M') || (name[1] == 'm')) && 1.1198 + ((name[2] == 'O') || (name[2] == 'o')) && 1.1199 + ((name[3] == 'V') || (name[3] == 'v')) && 1.1200 + ((name[4] == 'E') || (name[4] == 'e')) && (c = name[5]) && 1.1201 + (s = strchr (name+6,c)) && (i = s - (name + 6)) && (i < MAILTMPLEN)) { 1.1202 + if (stream = mail_open (stream,s+1,options)) { 1.1203 + strncpy (tmp,name+6,i); /* copy snarf mailbox name */ 1.1204 + tmp[i] = '\0'; /* tie off name */ 1.1205 + mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp); 1.1206 + stream->snarf.options = options; 1.1207 + mail_ping (stream); /* do initial snarf */ 1.1208 + /* punt if can't do initial snarf */ 1.1209 + if (!stream->snarf.time) stream = mail_close (stream); 1.1210 + } 1.1211 + return stream; 1.1212 + } 1.1213 + /* special POP hack */ 1.1214 + else if (((name[1] == 'P') || (name[1] == 'p')) && 1.1215 + ((name[2] == 'O') || (name[2] == 'o')) && 1.1216 + ((name[3] == 'P') || (name[3] == 'p')) && 1.1217 + mail_valid_net_parse_work (name+4,&mb,"pop3") && 1.1218 + !strcmp (mb.service,"pop3") && !mb.anoflag && !mb.readonlyflag) { 1.1219 + if (stream = mail_open (stream,mb.mailbox,options)) { 1.1220 + sprintf (tmp,"{%.255s",mb.host); 1.1221 + if (mb.port) sprintf (tmp + strlen (tmp),":%lu",mb.port); 1.1222 + if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=%.64s",mb.user); 1.1223 + if (mb.dbgflag) strcat (tmp,"/debug"); 1.1224 + if (mb.secflag) strcat (tmp,"/secure"); 1.1225 + if (mb.tlsflag) strcat (tmp,"/tls"); 1.1226 + if (mb.notlsflag) strcat (tmp,"/notls"); 1.1227 + if (mb.sslflag) strcat (tmp,"/ssl"); 1.1228 + if (mb.trysslflag) strcat (tmp,"/tryssl"); 1.1229 + if (mb.novalidate) strcat (tmp,"/novalidate-cert"); 1.1230 + strcat (tmp,"/pop3/loser}"); 1.1231 + mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp); 1.1232 + mail_ping (stream); /* do initial snarf */ 1.1233 + } 1.1234 + return stream; /* return local mailbox stream */ 1.1235 + } 1.1236 + 1.1237 + else if ((options & OP_PROTOTYPE) && 1.1238 + ((name[1] == 'D') || (name[1] == 'd')) && 1.1239 + ((name[2] == 'R') || (name[2] == 'r')) && 1.1240 + ((name[3] == 'I') || (name[3] == 'i')) && 1.1241 + ((name[4] == 'V') || (name[4] == 'v')) && 1.1242 + ((name[5] == 'E') || (name[5] == 'e')) && 1.1243 + ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) { 1.1244 + sprintf (tmp,"%.80s",name+8); 1.1245 + /* tie off name at likely delimiter */ 1.1246 + if (s = strpbrk (tmp,"/\\:")) *s++ = '\0'; 1.1247 + else { 1.1248 + sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name); 1.1249 + MM_LOG (tmp,ERROR); 1.1250 + return mail_close (stream); 1.1251 + } 1.1252 + for (d = maildrivers; d && compare_cstring (d->name,tmp); d = d->next); 1.1253 + if (d) return (*d->open) (NIL); 1.1254 + sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name); 1.1255 + MM_LOG (tmp,ERROR); 1.1256 + return mail_close (stream); 1.1257 + } 1.1258 + /* fall through to default case */ 1.1259 + default: /* not special hack (but could be # name */ 1.1260 + d = mail_valid (NIL,name,(options & OP_SILENT) ? 1.1261 + (char *) NIL : "open mailbox"); 1.1262 + } 1.1263 + return d ? mail_open_work (d,stream,name,options) : stream; 1.1264 +} 1.1265 + 1.1266 +/* Mail open worker routine 1.1267 + * Accepts: factory 1.1268 + * candidate stream for recycling 1.1269 + * mailbox name 1.1270 + * open options 1.1271 + * Returns: stream to use on success, NIL on failure 1.1272 + */ 1.1273 + 1.1274 +MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name, 1.1275 + long options) 1.1276 +{ 1.1277 + int i; 1.1278 + char tmp[MAILTMPLEN]; 1.1279 + NETMBX mb; 1.1280 + if (options & OP_PROTOTYPE) return (*d->open) (NIL); 1.1281 + /* name is copied here in case the caller does a re-open using 1.1282 + * stream->mailbox or stream->original_mailbox as the argument. 1.1283 + */ 1.1284 + name = cpystr (name); /* make copy of name */ 1.1285 + if (stream) { /* recycling requested? */ 1.1286 + if ((stream->dtb == d) && (d->flags & DR_RECYCLE) && 1.1287 + ((d->flags & DR_HALFOPEN) || !(options & OP_HALFOPEN)) && 1.1288 + mail_usable_network_stream (stream,name)) { 1.1289 + /* yes, checkpoint if needed */ 1.1290 + if (d->flags & DR_XPOINT) mail_check (stream); 1.1291 + mail_free_cache (stream); /* clean up stream */ 1.1292 + if (stream->mailbox) fs_give ((void **) &stream->mailbox); 1.1293 + if (stream->original_mailbox) 1.1294 + fs_give ((void **) &stream->original_mailbox); 1.1295 + /* flush user flags */ 1.1296 + for (i = 0; i < NUSERFLAGS; i++) 1.1297 + if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]); 1.1298 + } 1.1299 + else { /* stream not recycleable, babble if net */ 1.1300 + if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) && 1.1301 + mail_valid_net_parse (stream->mailbox,&mb)) { 1.1302 + sprintf (tmp,"Closing connection to %.80s",mb.host); 1.1303 + MM_LOG (tmp,(long) NIL); 1.1304 + } 1.1305 + /* flush the old stream */ 1.1306 + stream = mail_close (stream); 1.1307 + } 1.1308 + } 1.1309 + /* check if driver does not support halfopen */ 1.1310 + else if ((options & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)) { 1.1311 + fs_give ((void **) &name); 1.1312 + return NIL; 1.1313 + } 1.1314 + 1.1315 + /* instantiate new stream if not recycling */ 1.1316 + if (!stream) (*mailcache) (stream = (MAILSTREAM *) 1.1317 + memset (fs_get (sizeof (MAILSTREAM)),0, 1.1318 + sizeof (MAILSTREAM)),(long) 0,CH_INIT); 1.1319 + stream->dtb = d; /* set dispatch */ 1.1320 + /* set mailbox name */ 1.1321 + stream->mailbox = cpystr (stream->original_mailbox = name); 1.1322 + /* initialize stream flags */ 1.1323 + stream->inbox = stream->lock = NIL; 1.1324 + stream->debug = (options & OP_DEBUG) ? T : NIL; 1.1325 + stream->rdonly = (options & OP_READONLY) ? T : NIL; 1.1326 + stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL; 1.1327 + stream->scache = (options & OP_SHORTCACHE) ? T : NIL; 1.1328 + stream->silent = (options & OP_SILENT) ? T : NIL; 1.1329 + stream->halfopen = (options & OP_HALFOPEN) ? T : NIL; 1.1330 + stream->secure = (options & OP_SECURE) ? T : NIL; 1.1331 + stream->tryssl = (options & OP_TRYSSL) ? T : NIL; 1.1332 + stream->mulnewsrc = (options & OP_MULNEWSRC) ? T : NIL; 1.1333 + stream->nokod = (options & OP_NOKOD) ? T : NIL; 1.1334 + stream->sniff = (options & OP_SNIFF) ? T : NIL; 1.1335 + stream->perm_seen = stream->perm_deleted = stream->perm_flagged = 1.1336 + stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL; 1.1337 + stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL; 1.1338 + stream->uid_last = 0; /* default UID validity */ 1.1339 + stream->uid_validity = (unsigned long) time (0); 1.1340 + /* have driver open, flush if failed */ 1.1341 + return ((*d->open) (stream)) ? stream : mail_close (stream); 1.1342 +} 1.1343 + 1.1344 +/* Mail close 1.1345 + * Accepts: mail stream 1.1346 + * close options 1.1347 + * Returns: NIL, always 1.1348 + */ 1.1349 + 1.1350 +MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options) 1.1351 +{ 1.1352 + int i; 1.1353 + if (stream) { /* make sure argument given */ 1.1354 + /* do the driver's close action */ 1.1355 + if (stream->dtb) (*stream->dtb->close) (stream,options); 1.1356 + stream->dtb = NIL; /* resign driver */ 1.1357 + if (stream->mailbox) fs_give ((void **) &stream->mailbox); 1.1358 + if (stream->original_mailbox) 1.1359 + fs_give ((void **) &stream->original_mailbox); 1.1360 + if (stream->snarf.name) fs_give ((void **) &stream->snarf.name); 1.1361 + stream->sequence++; /* invalidate sequence */ 1.1362 + /* flush user flags */ 1.1363 + for (i = 0; i < NUSERFLAGS; i++) 1.1364 + if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]); 1.1365 + mail_free_cache (stream); /* finally free the stream's storage */ 1.1366 + if (mailfreestreamsparep && stream->sparep) 1.1367 + (*mailfreestreamsparep) (&stream->sparep); 1.1368 + if (!stream->use) fs_give ((void **) &stream); 1.1369 + } 1.1370 + return NIL; 1.1371 +} 1.1372 + 1.1373 +/* Mail make handle 1.1374 + * Accepts: mail stream 1.1375 + * Returns: handle 1.1376 + * 1.1377 + * Handles provide a way to have multiple pointers to a stream yet allow the 1.1378 + * stream's owner to nuke it or recycle it. 1.1379 + */ 1.1380 + 1.1381 +MAILHANDLE *mail_makehandle (MAILSTREAM *stream) 1.1382 +{ 1.1383 + MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE)); 1.1384 + handle->stream = stream; /* copy stream */ 1.1385 + /* and its sequence */ 1.1386 + handle->sequence = stream->sequence; 1.1387 + stream->use++; /* let stream know another handle exists */ 1.1388 + return handle; 1.1389 +} 1.1390 + 1.1391 + 1.1392 +/* Mail release handle 1.1393 + * Accepts: Mail handle 1.1394 + */ 1.1395 + 1.1396 +void mail_free_handle (MAILHANDLE **handle) 1.1397 +{ 1.1398 + MAILSTREAM *s; 1.1399 + if (*handle) { /* only free if exists */ 1.1400 + /* resign stream, flush unreferenced zombies */ 1.1401 + if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s); 1.1402 + fs_give ((void **) handle); /* now flush the handle */ 1.1403 + } 1.1404 +} 1.1405 + 1.1406 + 1.1407 +/* Mail get stream handle 1.1408 + * Accepts: Mail handle 1.1409 + * Returns: mail stream or NIL if stream gone 1.1410 + */ 1.1411 + 1.1412 +MAILSTREAM *mail_stream (MAILHANDLE *handle) 1.1413 +{ 1.1414 + MAILSTREAM *s = handle->stream; 1.1415 + return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL; 1.1416 +} 1.1417 + 1.1418 +/* Mail fetch cache element 1.1419 + * Accepts: mail stream 1.1420 + * message # to fetch 1.1421 + * Returns: cache element of this message 1.1422 + * Can also be used to create cache elements for new messages. 1.1423 + */ 1.1424 + 1.1425 +MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno) 1.1426 +{ 1.1427 + if (msgno < 1 || msgno > stream->nmsgs) { 1.1428 + char tmp[MAILTMPLEN]; 1.1429 + sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu, mbx=%.80s", 1.1430 + msgno,stream->nmsgs,stream->mailbox ? stream->mailbox : "???"); 1.1431 + fatal (tmp); 1.1432 + } 1.1433 + return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT); 1.1434 +} 1.1435 + 1.1436 + 1.1437 +/* Mail fetch fast information 1.1438 + * Accepts: mail stream 1.1439 + * sequence 1.1440 + * option flags 1.1441 + * 1.1442 + * Generally, mail_fetch_structure is preferred 1.1443 + */ 1.1444 + 1.1445 +void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags) 1.1446 +{ 1.1447 + /* do the driver's action */ 1.1448 + if (stream->dtb && stream->dtb->fast) 1.1449 + (*stream->dtb->fast) (stream,sequence,flags); 1.1450 +} 1.1451 + 1.1452 + 1.1453 +/* Mail fetch flags 1.1454 + * Accepts: mail stream 1.1455 + * sequence 1.1456 + * option flags 1.1457 + */ 1.1458 + 1.1459 +void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags) 1.1460 +{ 1.1461 + /* do the driver's action */ 1.1462 + if (stream->dtb && stream->dtb->msgflags) 1.1463 + (*stream->dtb->msgflags) (stream,sequence,flags); 1.1464 +} 1.1465 + 1.1466 +/* Mail fetch message overview 1.1467 + * Accepts: mail stream 1.1468 + * UID sequence to fetch 1.1469 + * pointer to overview return function 1.1470 + */ 1.1471 + 1.1472 +void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn) 1.1473 +{ 1.1474 + if (stream->dtb && mail_uid_sequence (stream,sequence) && 1.1475 + !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) && 1.1476 + mail_ping (stream)) 1.1477 + mail_fetch_overview_default (stream,ofn); 1.1478 +} 1.1479 + 1.1480 + 1.1481 +/* Mail fetch message overview using sequence numbers instead of UIDs 1.1482 + * Accepts: mail stream 1.1483 + * sequence to fetch 1.1484 + * pointer to overview return function 1.1485 + */ 1.1486 + 1.1487 +void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence, 1.1488 + overview_t ofn) 1.1489 +{ 1.1490 + if (stream->dtb && mail_sequence (stream,sequence) && 1.1491 + !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) && 1.1492 + mail_ping (stream)) 1.1493 + mail_fetch_overview_default (stream,ofn); 1.1494 +} 1.1495 + 1.1496 + 1.1497 +/* Mail fetch message overview default handler 1.1498 + * Accepts: mail stream with sequence bits lit 1.1499 + * pointer to overview return function 1.1500 + */ 1.1501 + 1.1502 +void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn) 1.1503 +{ 1.1504 + MESSAGECACHE *elt; 1.1505 + ENVELOPE *env; 1.1506 + OVERVIEW ov; 1.1507 + unsigned long i; 1.1508 + ov.optional.lines = 0; 1.1509 + ov.optional.xref = NIL; 1.1510 + for (i = 1; i <= stream->nmsgs; i++) 1.1511 + if (((elt = mail_elt (stream,i))->sequence) && 1.1512 + (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) { 1.1513 + ov.subject = env->subject; 1.1514 + ov.from = env->from; 1.1515 + ov.date = env->date; 1.1516 + ov.message_id = env->message_id; 1.1517 + ov.references = env->references; 1.1518 + ov.optional.octets = elt->rfc822_size; 1.1519 + (*ofn) (stream,mail_uid (stream,i),&ov,i); 1.1520 + } 1.1521 +} 1.1522 + 1.1523 +/* Mail fetch message structure 1.1524 + * Accepts: mail stream 1.1525 + * message # to fetch 1.1526 + * pointer to return body 1.1527 + * option flags 1.1528 + * Returns: envelope of this message, body returned in body value 1.1529 + * 1.1530 + * Fetches the "fast" information as well 1.1531 + */ 1.1532 + 1.1533 +ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno, 1.1534 + BODY **body,long flags) 1.1535 +{ 1.1536 + ENVELOPE **env; 1.1537 + BODY **b; 1.1538 + MESSAGECACHE *elt; 1.1539 + char c,*s,*hdr; 1.1540 + unsigned long hdrsize; 1.1541 + STRING bs; 1.1542 + /* do the driver's action if specified */ 1.1543 + if (stream->dtb && stream->dtb->structure) 1.1544 + return (*stream->dtb->structure) (stream,msgno,body,flags); 1.1545 + if (flags & FT_UID) { /* UID form of call */ 1.1546 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1547 + else return NIL; /* must get UID/msgno map first */ 1.1548 + } 1.1549 + elt = mail_elt (stream,msgno);/* get elt for real message number */ 1.1550 + if (stream->scache) { /* short caching */ 1.1551 + if (msgno != stream->msgno){/* garbage collect if not same message */ 1.1552 + mail_gc (stream,GC_ENV | GC_TEXTS); 1.1553 + stream->msgno = msgno; /* this is the current message now */ 1.1554 + } 1.1555 + env = &stream->env; /* get pointers to envelope and body */ 1.1556 + b = &stream->body; 1.1557 + } 1.1558 + else { /* get pointers to elt envelope and body */ 1.1559 + env = &elt->private.msg.env; 1.1560 + b = &elt->private.msg.body; 1.1561 + } 1.1562 + 1.1563 + if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) { 1.1564 + mail_free_envelope (env); /* flush old envelope and body */ 1.1565 + mail_free_body (b); 1.1566 + /* see if need to fetch the whole thing */ 1.1567 + if (body || !elt->rfc822_size) { 1.1568 + s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL); 1.1569 + /* make copy in case body fetch smashes it */ 1.1570 + hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize); 1.1571 + hdr[hdrsize] = '\0'; /* tie off header */ 1.1572 + (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK); 1.1573 + if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs); 1.1574 + if (body) /* only parse body if requested */ 1.1575 + rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags); 1.1576 + else 1.1577 + rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags); 1.1578 + fs_give ((void **) &hdr); /* flush header */ 1.1579 + } 1.1580 + else { /* can save memory doing it this way */ 1.1581 + hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL); 1.1582 + if (hdrsize) { /* in case null header */ 1.1583 + c = hdr[hdrsize]; /* preserve what's there */ 1.1584 + hdr[hdrsize] = '\0'; /* tie off header */ 1.1585 + rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags); 1.1586 + hdr[hdrsize] = c; /* restore in case cached data */ 1.1587 + } 1.1588 + else *env = mail_newenvelope (); 1.1589 + } 1.1590 + } 1.1591 + /* if need date, have date in envelope? */ 1.1592 + if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date); 1.1593 + /* sigh, fill in bogus default */ 1.1594 + if (!elt->day) elt->day = elt->month = 1; 1.1595 + if (body) *body = *b; /* return the body */ 1.1596 + return *env; /* return the envelope */ 1.1597 +} 1.1598 + 1.1599 +/* Mail mark single message (internal use only) 1.1600 + * Accepts: mail stream 1.1601 + * elt to mark 1.1602 + * fetch flags 1.1603 + */ 1.1604 + 1.1605 +static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags) 1.1606 +{ 1.1607 + unsigned long i; 1.1608 + char sequence[20]; 1.1609 + MESSAGECACHE *e; 1.1610 + /* non-peeking and needs to set \Seen? */ 1.1611 + if (!(flags & FT_PEEK) && !elt->seen) { 1.1612 + if (stream->dtb->flagmsg){ /* driver wants per-message call? */ 1.1613 + elt->valid = NIL; /* do pre-alteration driver call */ 1.1614 + (*stream->dtb->flagmsg) (stream,elt); 1.1615 + /* set seen, do post-alteration driver call */ 1.1616 + elt->seen = elt->valid = T; 1.1617 + (*stream->dtb->flagmsg) (stream,elt); 1.1618 + } 1.1619 + if (stream->dtb->flag) { /* driver wants one-time call? */ 1.1620 + /* better safe than sorry, save seq bits */ 1.1621 + for (i = 1; i <= stream->nmsgs; i++) { 1.1622 + e = mail_elt (stream,i); 1.1623 + e->private.sequence = e->sequence; 1.1624 + } 1.1625 + /* call driver to set the message */ 1.1626 + sprintf (sequence,"%lu",elt->msgno); 1.1627 + (*stream->dtb->flag) (stream,sequence,"\\Seen",ST_SET); 1.1628 + /* restore sequence bits */ 1.1629 + for (i = 1; i <= stream->nmsgs; i++) { 1.1630 + e = mail_elt (stream,i); 1.1631 + e->sequence = e->private.sequence; 1.1632 + } 1.1633 + } 1.1634 + /* notify mail program of flag change */ 1.1635 + MM_FLAGS (stream,elt->msgno); 1.1636 + } 1.1637 +} 1.1638 + 1.1639 +/* Mail fetch message 1.1640 + * Accepts: mail stream 1.1641 + * message # to fetch 1.1642 + * pointer to returned length 1.1643 + * flags 1.1644 + * Returns: message text 1.1645 + */ 1.1646 + 1.1647 +char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno, 1.1648 + unsigned long *len,long flags) 1.1649 +{ 1.1650 + GETS_DATA md; 1.1651 + SIZEDTEXT *t; 1.1652 + STRING bs; 1.1653 + MESSAGECACHE *elt; 1.1654 + char *s,*u; 1.1655 + unsigned long i,j; 1.1656 + if (len) *len = 0; /* default return size */ 1.1657 + if (flags & FT_UID) { /* UID form of call */ 1.1658 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1659 + else return ""; /* must get UID/msgno map first */ 1.1660 + } 1.1661 + /* initialize message data identifier */ 1.1662 + INIT_GETS (md,stream,msgno,"",0,0); 1.1663 + /* is data already cached? */ 1.1664 + if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) { 1.1665 + markseen (stream,elt,flags);/* mark message seen */ 1.1666 + return mail_fetch_text_return (&md,t,len); 1.1667 + } 1.1668 + if (!stream->dtb) return ""; /* not in cache, must have live driver */ 1.1669 + if (stream->dtb->msgdata) return 1.1670 + ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ? 1.1671 + mail_fetch_text_return (&md,t,len) : ""; 1.1672 + /* ugh, have to do this the crufty way */ 1.1673 + u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags); 1.1674 + /* copy in case text method stomps on it */ 1.1675 + s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i); 1.1676 + if ((*stream->dtb->text) (stream,msgno,&bs,flags)) { 1.1677 + t = &stream->text; /* build combined copy */ 1.1678 + if (t->data) fs_give ((void **) &t->data); 1.1679 + t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1); 1.1680 + if (!elt->rfc822_size) elt->rfc822_size = t->size; 1.1681 + else if (elt->rfc822_size != t->size) { 1.1682 + char tmp[MAILTMPLEN]; 1.1683 + sprintf (tmp,"Calculated RFC822.SIZE (%lu) != reported size (%lu)", 1.1684 + t->size,elt->rfc822_size); 1.1685 + mm_log (tmp,WARN); /* bug trap */ 1.1686 + } 1.1687 + memcpy (t->data,s,(size_t) i); 1.1688 + for (u = (char *) t->data + i, j = SIZE (&bs); j;) { 1.1689 + memcpy (u,bs.curpos,bs.cursize); 1.1690 + u += bs.cursize; /* update text */ 1.1691 + j -= bs.cursize; 1.1692 + bs.curpos += (bs.cursize -1); 1.1693 + bs.cursize = 0; 1.1694 + (*bs.dtb->next) (&bs); /* advance to next buffer's worth */ 1.1695 + } 1.1696 + *u = '\0'; /* tie off data */ 1.1697 + u = mail_fetch_text_return (&md,t,len); 1.1698 + } 1.1699 + else u = ""; 1.1700 + fs_give ((void **) &s); /* finished with copy of header */ 1.1701 + return u; 1.1702 +} 1.1703 + 1.1704 +/* Mail fetch message header 1.1705 + * Accepts: mail stream 1.1706 + * message # to fetch 1.1707 + * MIME section specifier (#.#.#...#) 1.1708 + * list of lines to fetch 1.1709 + * pointer to returned length 1.1710 + * flags 1.1711 + * Returns: message header in RFC822 format 1.1712 + * 1.1713 + * Note: never calls a mailgets routine 1.1714 + */ 1.1715 + 1.1716 +char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section, 1.1717 + STRINGLIST *lines,unsigned long *len,long flags) 1.1718 +{ 1.1719 + STRING bs; 1.1720 + BODY *b = NIL; 1.1721 + SIZEDTEXT *t = NIL,rt; 1.1722 + MESSAGE *m = NIL; 1.1723 + MESSAGECACHE *elt; 1.1724 + char tmp[MAILTMPLEN]; 1.1725 + if (len) *len = 0; /* default return size */ 1.1726 + if (section && (strlen (section) > (MAILTMPLEN - 20))) return ""; 1.1727 + if (flags & FT_UID) { /* UID form of call */ 1.1728 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1729 + else return ""; /* must get UID/msgno map first */ 1.1730 + } 1.1731 + elt = mail_elt (stream,msgno);/* get cache data */ 1.1732 + if (section && *section) { /* nested body header wanted? */ 1.1733 + if (!((b = mail_body (stream,msgno,section)) && 1.1734 + (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822"))) 1.1735 + return ""; /* lose if no body or not MESSAGE/RFC822 */ 1.1736 + m = b->nested.msg; /* point to nested message */ 1.1737 + } 1.1738 + /* else top-level message header wanted */ 1.1739 + else m = &elt->private.msg; 1.1740 + if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) { 1.1741 + if (lines) textcpy (t = &stream->text,&m->header.text); 1.1742 + else t = &m->header.text; /* in cache, and cache is valid */ 1.1743 + markseen (stream,elt,flags);/* mark message seen */ 1.1744 + } 1.1745 + 1.1746 + else if (stream->dtb) { /* not in cache, has live driver? */ 1.1747 + if (stream->dtb->msgdata) { /* has driver section fetch? */ 1.1748 + /* build driver section specifier */ 1.1749 + if (section && *section) sprintf (tmp,"%s.HEADER",section); 1.1750 + else strcpy (tmp,"HEADER"); 1.1751 + if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) { 1.1752 + t = &m->header.text; /* fetch data */ 1.1753 + /* don't need to postprocess lines */ 1.1754 + if (m->lines) lines = NIL; 1.1755 + else if (lines) textcpy (t = &stream->text,&m->header.text); 1.1756 + } 1.1757 + } 1.1758 + else if (b) { /* nested body wanted? */ 1.1759 + if (stream->private.search.text) { 1.1760 + rt.data = (unsigned char *) stream->private.search.text + 1.1761 + b->nested.msg->header.offset; 1.1762 + rt.size = b->nested.msg->header.text.size; 1.1763 + t = &rt; 1.1764 + } 1.1765 + else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) { 1.1766 + if ((bs.dtb->next == mail_string_next) && !lines) { 1.1767 + rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset; 1.1768 + rt.size = b->nested.msg->header.text.size; 1.1769 + if (stream->private.search.string) 1.1770 + stream->private.search.text = bs.curpos; 1.1771 + t = &rt; /* special hack to avoid extra copy */ 1.1772 + } 1.1773 + else textcpyoffstring (t = &stream->text,&bs, 1.1774 + b->nested.msg->header.offset, 1.1775 + b->nested.msg->header.text.size); 1.1776 + } 1.1777 + } 1.1778 + else { /* top-level header fetch */ 1.1779 + /* mark message seen */ 1.1780 + markseen (stream,elt,flags); 1.1781 + if (rt.data = (unsigned char *) 1.1782 + (*stream->dtb->header) (stream,msgno,&rt.size,flags)) { 1.1783 + /* make a safe copy if need to filter */ 1.1784 + if (lines) textcpy (t = &stream->text,&rt); 1.1785 + else t = &rt; /* top level header */ 1.1786 + } 1.1787 + } 1.1788 + } 1.1789 + if (!t || !t->data) return "";/* error if no string */ 1.1790 + /* filter headers if requested */ 1.1791 + if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags); 1.1792 + if (len) *len = t->size; /* return size if requested */ 1.1793 + return (char *) t->data; /* and text */ 1.1794 +} 1.1795 + 1.1796 +/* Mail fetch message text 1.1797 + * Accepts: mail stream 1.1798 + * message # to fetch 1.1799 + * MIME section specifier (#.#.#...#) 1.1800 + * pointer to returned length 1.1801 + * flags 1.1802 + * Returns: message text 1.1803 + */ 1.1804 + 1.1805 +char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section, 1.1806 + unsigned long *len,long flags) 1.1807 +{ 1.1808 + GETS_DATA md; 1.1809 + PARTTEXT *p; 1.1810 + STRING bs; 1.1811 + MESSAGECACHE *elt; 1.1812 + BODY *b = NIL; 1.1813 + char tmp[MAILTMPLEN]; 1.1814 + unsigned long i; 1.1815 + if (len) *len = 0; /* default return size */ 1.1816 + memset (&stream->private.string,NIL,sizeof (STRING)); 1.1817 + if (section && (strlen (section) > (MAILTMPLEN - 20))) return ""; 1.1818 + if (flags & FT_UID) { /* UID form of call */ 1.1819 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1820 + else return ""; /* must get UID/msgno map first */ 1.1821 + } 1.1822 + elt = mail_elt (stream,msgno);/* get cache data */ 1.1823 + if (section && *section) { /* nested body text wanted? */ 1.1824 + if (!((b = mail_body (stream,msgno,section)) && 1.1825 + (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822"))) 1.1826 + return ""; /* lose if no body or not MESSAGE/RFC822 */ 1.1827 + p = &b->nested.msg->text; /* point at nested message */ 1.1828 + /* build IMAP-format section specifier */ 1.1829 + sprintf (tmp,"%s.TEXT",section); 1.1830 + flags &= ~FT_INTERNAL; /* can't win with this set */ 1.1831 + } 1.1832 + else { /* top-level message text wanted */ 1.1833 + p = &elt->private.msg.text; 1.1834 + strcpy (tmp,"TEXT"); 1.1835 + } 1.1836 + /* initialize message data identifier */ 1.1837 + INIT_GETS (md,stream,msgno,section,0,0); 1.1838 + if (p->text.data) { /* is data already cached? */ 1.1839 + markseen (stream,elt,flags);/* mark message seen */ 1.1840 + return mail_fetch_text_return (&md,&p->text,len); 1.1841 + } 1.1842 + if (!stream->dtb) return ""; /* not in cache, must have live driver */ 1.1843 + if (stream->dtb->msgdata) return 1.1844 + ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)? 1.1845 + mail_fetch_text_return (&md,&p->text,len) : ""; 1.1846 + if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return ""; 1.1847 + if (section && *section) { /* nested is more complex */ 1.1848 + SETPOS (&bs,p->offset); 1.1849 + i = p->text.size; /* just want this much */ 1.1850 + } 1.1851 + else i = SIZE (&bs); /* want entire text */ 1.1852 + return mail_fetch_string_return (&md,&bs,i,len,flags); 1.1853 +} 1.1854 + 1.1855 +/* Mail fetch message body part MIME headers 1.1856 + * Accepts: mail stream 1.1857 + * message # to fetch 1.1858 + * MIME section specifier (#.#.#...#) 1.1859 + * pointer to returned length 1.1860 + * flags 1.1861 + * Returns: message text 1.1862 + */ 1.1863 + 1.1864 +char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section, 1.1865 + unsigned long *len,long flags) 1.1866 +{ 1.1867 + PARTTEXT *p; 1.1868 + STRING bs; 1.1869 + BODY *b; 1.1870 + char tmp[MAILTMPLEN]; 1.1871 + if (len) *len = 0; /* default return size */ 1.1872 + if (section && (strlen (section) > (MAILTMPLEN - 20))) return ""; 1.1873 + if (flags & FT_UID) { /* UID form of call */ 1.1874 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1875 + else return ""; /* must get UID/msgno map first */ 1.1876 + } 1.1877 + flags &= ~FT_INTERNAL; /* can't win with this set */ 1.1878 + if (!(section && *section && (b = mail_body (stream,msgno,section)))) 1.1879 + return ""; /* not valid section */ 1.1880 + /* in cache? */ 1.1881 + if ((p = &b->mime)->text.data) { 1.1882 + /* mark message seen */ 1.1883 + markseen (stream,mail_elt (stream,msgno),flags); 1.1884 + if (len) *len = p->text.size; 1.1885 + return (char *) p->text.data; 1.1886 + } 1.1887 + if (!stream->dtb) return ""; /* not in cache, must have live driver */ 1.1888 + if (stream->dtb->msgdata) { /* has driver fetch? */ 1.1889 + /* build driver section specifier */ 1.1890 + sprintf (tmp,"%s.MIME",section); 1.1891 + if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && 1.1892 + p->text.data) { 1.1893 + if (len) *len = p->text.size; 1.1894 + return (char *) p->text.data; 1.1895 + } 1.1896 + else return ""; 1.1897 + } 1.1898 + if (len) *len = b->mime.text.size; 1.1899 + if (!b->mime.text.size) { /* empty MIME header -- mark seen anyway */ 1.1900 + markseen (stream,mail_elt (stream,msgno),flags); 1.1901 + return ""; 1.1902 + } 1.1903 + /* have to get it from offset */ 1.1904 + if (stream->private.search.text) 1.1905 + return stream->private.search.text + b->mime.offset; 1.1906 + if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) { 1.1907 + if (len) *len = 0; 1.1908 + return ""; 1.1909 + } 1.1910 + if (bs.dtb->next == mail_string_next) { 1.1911 + if (stream->private.search.string) stream->private.search.text = bs.curpos; 1.1912 + return bs.curpos + b->mime.offset; 1.1913 + } 1.1914 + return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size); 1.1915 +} 1.1916 + 1.1917 +/* Mail fetch message body part 1.1918 + * Accepts: mail stream 1.1919 + * message # to fetch 1.1920 + * MIME section specifier (#.#.#...#) 1.1921 + * pointer to returned length 1.1922 + * flags 1.1923 + * Returns: message body 1.1924 + */ 1.1925 + 1.1926 +char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section, 1.1927 + unsigned long *len,long flags) 1.1928 +{ 1.1929 + GETS_DATA md; 1.1930 + PARTTEXT *p; 1.1931 + STRING bs; 1.1932 + BODY *b; 1.1933 + SIZEDTEXT *t; 1.1934 + char *s,tmp[MAILTMPLEN]; 1.1935 + memset (&stream->private.string,NIL,sizeof (STRING)); 1.1936 + if (!(section && *section)) /* top-level text wanted? */ 1.1937 + return mail_fetch_message (stream,msgno,len,flags); 1.1938 + else if (strlen (section) > (MAILTMPLEN - 20)) return ""; 1.1939 + flags &= ~FT_INTERNAL; /* can't win with this set */ 1.1940 + /* initialize message data identifier */ 1.1941 + INIT_GETS (md,stream,msgno,section,0,0); 1.1942 + /* kludge for old section 0 header */ 1.1943 + if (!strcmp (s = strcpy (tmp,section),"0") || 1.1944 + ((s = strstr (tmp,".0")) && !s[2])) { 1.1945 + SIZEDTEXT ht; 1.1946 + *s = '\0'; /* tie off section */ 1.1947 + /* this silly way so it does mailgets */ 1.1948 + ht.data = (unsigned char *) mail_fetch_header (stream,msgno, 1.1949 + tmp[0] ? tmp : NIL,NIL, 1.1950 + &ht.size,flags); 1.1951 + /* may have UIDs here */ 1.1952 + md.flags = (flags & FT_UID) ? MG_UID : NIL; 1.1953 + return mail_fetch_text_return (&md,&ht,len); 1.1954 + } 1.1955 + if (len) *len = 0; /* default return size */ 1.1956 + if (flags & FT_UID) { /* UID form of call */ 1.1957 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.1958 + else return ""; /* must get UID/msgno map first */ 1.1959 + } 1.1960 + /* must have body */ 1.1961 + if (!(b = mail_body (stream,msgno,section))) return ""; 1.1962 + /* have cached text? */ 1.1963 + if ((t = &(p = &b->contents)->text)->data) { 1.1964 + /* mark message seen */ 1.1965 + markseen (stream,mail_elt (stream,msgno),flags); 1.1966 + return mail_fetch_text_return (&md,t,len); 1.1967 + } 1.1968 + if (!stream->dtb) return ""; /* not in cache, must have live driver */ 1.1969 + if (stream->dtb->msgdata) return 1.1970 + ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ? 1.1971 + mail_fetch_text_return (&md,t,len) : ""; 1.1972 + if (len) *len = t->size; 1.1973 + if (!t->size) { /* empty body part -- mark seen anyway */ 1.1974 + markseen (stream,mail_elt (stream,msgno),flags); 1.1975 + return ""; 1.1976 + } 1.1977 + /* copy body from stringstruct offset */ 1.1978 + if (stream->private.search.text) 1.1979 + return stream->private.search.text + p->offset; 1.1980 + if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) { 1.1981 + if (len) *len = 0; 1.1982 + return ""; 1.1983 + } 1.1984 + if (bs.dtb->next == mail_string_next) { 1.1985 + if (stream->private.search.string) stream->private.search.text = bs.curpos; 1.1986 + return bs.curpos + p->offset; 1.1987 + } 1.1988 + SETPOS (&bs,p->offset); 1.1989 + return mail_fetch_string_return (&md,&bs,t->size,len,flags); 1.1990 +} 1.1991 + 1.1992 +/* Mail fetch partial message text 1.1993 + * Accepts: mail stream 1.1994 + * message # to fetch 1.1995 + * MIME section specifier (#.#.#...#) 1.1996 + * offset of first designed byte or 0 to start at beginning 1.1997 + * maximum number of bytes or 0 for all bytes 1.1998 + * flags 1.1999 + * Returns: T if successful, else NIL 1.2000 + */ 1.2001 + 1.2002 +long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section, 1.2003 + unsigned long first,unsigned long last,long flags) 1.2004 +{ 1.2005 + GETS_DATA md; 1.2006 + PARTTEXT *p = NIL; 1.2007 + MESSAGECACHE *elt; 1.2008 + STRING bs; 1.2009 + BODY *b; 1.2010 + char tmp[MAILTMPLEN]; 1.2011 + unsigned long i; 1.2012 + if (!mailgets) fatal ("mail_partial_text() called without a mailgets!"); 1.2013 + if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL; 1.2014 + if (flags & FT_UID) { /* UID form of call */ 1.2015 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.2016 + else return NIL; /* must get UID/msgno map first */ 1.2017 + } 1.2018 + elt = mail_elt (stream,msgno);/* get cache data */ 1.2019 + flags &= ~FT_INTERNAL; /* bogus if this is set */ 1.2020 + if (section && *section) { /* nested body text wanted? */ 1.2021 + if (!((b = mail_body (stream,msgno,section)) && 1.2022 + (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822"))) 1.2023 + return NIL; /* lose if no body or not MESSAGE/RFC822 */ 1.2024 + p = &b->nested.msg->text; /* point at nested message */ 1.2025 + /* build IMAP-format section specifier */ 1.2026 + sprintf (tmp,"%s.TEXT",section); 1.2027 + } 1.2028 + else { /* else top-level message text wanted */ 1.2029 + p = &elt->private.msg.text; 1.2030 + strcpy (tmp,"TEXT"); 1.2031 + } 1.2032 + 1.2033 + /* initialize message data identifier */ 1.2034 + INIT_GETS (md,stream,msgno,tmp,first,last); 1.2035 + if (p->text.data) { /* is data already cached? */ 1.2036 + INIT (&bs,mail_string,p->text.data,i = p->text.size); 1.2037 + markseen (stream,elt,flags);/* mark message seen */ 1.2038 + } 1.2039 + else { /* else get data from driver */ 1.2040 + if (!stream->dtb) return NIL; 1.2041 + if (stream->dtb->msgdata) /* driver will handle this */ 1.2042 + return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags); 1.2043 + if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL; 1.2044 + if (section && *section) { /* nexted if more complex */ 1.2045 + SETPOS (&bs,p->offset); /* offset stringstruct to data */ 1.2046 + i = p->text.size; /* maximum size of data */ 1.2047 + } 1.2048 + else i = SIZE (&bs); /* just want this much */ 1.2049 + } 1.2050 + if (i <= first) i = first = 0;/* first byte is beyond end of text */ 1.2051 + /* truncate as needed */ 1.2052 + else { /* offset and truncate */ 1.2053 + SETPOS (&bs,first + GETPOS (&bs)); 1.2054 + i -= first; /* reduced size */ 1.2055 + if (last && (i > last)) i = last; 1.2056 + } 1.2057 + /* do the mailgets thing */ 1.2058 + (*mailgets) (mail_read,&bs,i,&md); 1.2059 + return T; /* success */ 1.2060 +} 1.2061 + 1.2062 +/* Mail fetch partial message body part 1.2063 + * Accepts: mail stream 1.2064 + * message # to fetch 1.2065 + * MIME section specifier (#.#.#...#) 1.2066 + * offset of first designed byte or 0 to start at beginning 1.2067 + * maximum number of bytes or 0 for all bytes 1.2068 + * flags 1.2069 + * Returns: T if successful, else NIL 1.2070 + */ 1.2071 + 1.2072 +long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section, 1.2073 + unsigned long first,unsigned long last,long flags) 1.2074 +{ 1.2075 + GETS_DATA md; 1.2076 + PARTTEXT *p; 1.2077 + STRING bs; 1.2078 + BODY *b; 1.2079 + SIZEDTEXT *t; 1.2080 + unsigned long i; 1.2081 + if (!(section && *section)) /* top-level text wanted? */ 1.2082 + return mail_partial_text (stream,msgno,NIL,first,last,flags); 1.2083 + if (!mailgets) fatal ("mail_partial_body() called without a mailgets!"); 1.2084 + if (flags & FT_UID) { /* UID form of call */ 1.2085 + if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID; 1.2086 + else return NIL; /* must get UID/msgno map first */ 1.2087 + } 1.2088 + /* must have body */ 1.2089 + if (!(b = mail_body (stream,msgno,section))) return NIL; 1.2090 + flags &= ~FT_INTERNAL; /* bogus if this is set */ 1.2091 + 1.2092 + /* initialize message data identifier */ 1.2093 + INIT_GETS (md,stream,msgno,section,first,last); 1.2094 + /* have cached text? */ 1.2095 + if ((t = &(p = &b->contents)->text)->data) { 1.2096 + /* mark message seen */ 1.2097 + markseen (stream,mail_elt (stream,msgno),flags); 1.2098 + INIT (&bs,mail_string,t->data,i = t->size); 1.2099 + } 1.2100 + else { /* else get data from driver */ 1.2101 + if (!stream->dtb) return NIL; 1.2102 + if (stream->dtb->msgdata) /* driver will handle this */ 1.2103 + return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL, 1.2104 + flags); 1.2105 + if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL; 1.2106 + if (section && *section) { /* nexted if more complex */ 1.2107 + SETPOS (&bs,p->offset); /* offset stringstruct to data */ 1.2108 + i = t->size; /* maximum size of data */ 1.2109 + } 1.2110 + else i = SIZE (&bs); /* just want this much */ 1.2111 + } 1.2112 + if (i <= first) i = first = 0;/* first byte is beyond end of text */ 1.2113 + else { /* offset and truncate */ 1.2114 + SETPOS (&bs,first + GETPOS (&bs)); 1.2115 + i -= first; /* reduced size */ 1.2116 + if (last && (i > last)) i = last; 1.2117 + } 1.2118 + /* do the mailgets thing */ 1.2119 + (*mailgets) (mail_read,&bs,i,&md); 1.2120 + return T; /* success */ 1.2121 +} 1.2122 + 1.2123 +/* Mail return message text 1.2124 + * Accepts: identifier data 1.2125 + * sized text 1.2126 + * pointer to returned length 1.2127 + * Returns: text 1.2128 + */ 1.2129 + 1.2130 +char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len) 1.2131 +{ 1.2132 + STRING bs; 1.2133 + if (len) *len = t->size; /* return size */ 1.2134 + if (t->size && mailgets) { /* have to do the mailgets thing? */ 1.2135 + /* silly but do it anyway for consistency */ 1.2136 + INIT (&bs,mail_string,t->data,t->size); 1.2137 + return (*mailgets) (mail_read,&bs,t->size,md); 1.2138 + } 1.2139 + return t->size ? (char *) t->data : ""; 1.2140 +} 1.2141 + 1.2142 + 1.2143 +/* Mail return message string 1.2144 + * Accepts: identifier data 1.2145 + * stringstruct 1.2146 + * text length 1.2147 + * pointer to returned length 1.2148 + * flags 1.2149 + * Returns: text, or NIL if stringstruct returned 1.2150 + */ 1.2151 + 1.2152 +char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i, 1.2153 + unsigned long *len,long flags) 1.2154 +{ 1.2155 + char *ret = NIL; 1.2156 + if (len) *len = i; /* return size */ 1.2157 + /* return stringstruct hack */ 1.2158 + if (flags & FT_RETURNSTRINGSTRUCT) { 1.2159 + memcpy (&md->stream->private.string,bs,sizeof (STRING)); 1.2160 + SETPOS (&md->stream->private.string,GETPOS (&md->stream->private.string)); 1.2161 + } 1.2162 + /* have to do the mailgets thing? */ 1.2163 + else if (mailgets) ret = (*mailgets) (mail_read,bs,i,md); 1.2164 + /* special hack to avoid extra copy */ 1.2165 + else if (bs->dtb->next == mail_string_next) ret = bs->curpos; 1.2166 + /* make string copy in memory */ 1.2167 + else ret = textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i); 1.2168 + return ret; 1.2169 +} 1.2170 + 1.2171 +/* Read data from stringstruct 1.2172 + * Accepts: stringstruct 1.2173 + * size of data to read 1.2174 + * buffer to read into 1.2175 + * Returns: T, always, stringstruct updated 1.2176 + */ 1.2177 + 1.2178 +long mail_read (void *stream,unsigned long size,char *buffer) 1.2179 +{ 1.2180 + unsigned long i; 1.2181 + STRING *s = (STRING *) stream; 1.2182 + while (size) { /* until satisfied */ 1.2183 + memcpy (buffer,s->curpos,i = min (s->cursize,size)); 1.2184 + buffer += i; /* update buffer */ 1.2185 + size -= i; /* note that we read this much */ 1.2186 + s->curpos += --i; /* advance that many spaces minus 1 */ 1.2187 + s->cursize -= i; 1.2188 + SNX (s); /* now use SNX to advance the last byte */ 1.2189 + } 1.2190 + return T; 1.2191 +} 1.2192 + 1.2193 +/* Mail fetch UID 1.2194 + * Accepts: mail stream 1.2195 + * message number 1.2196 + * Returns: UID or zero if dead stream 1.2197 + */ 1.2198 + 1.2199 +unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno) 1.2200 +{ 1.2201 + unsigned long uid = mail_elt (stream,msgno)->private.uid; 1.2202 + return uid ? uid : 1.2203 + (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0; 1.2204 +} 1.2205 + 1.2206 + 1.2207 +/* Mail fetch msgno from UID 1.2208 + * Accepts: mail stream 1.2209 + * UID 1.2210 + * Returns: msgno or zero if failed 1.2211 + */ 1.2212 + 1.2213 +unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid) 1.2214 +{ 1.2215 + unsigned long msgno,delta,first,firstuid,last,lastuid,middle,miduid; 1.2216 + if (stream->dtb) { /* active stream? */ 1.2217 + if (stream->dtb->msgno) /* direct way */ 1.2218 + return (*stream->dtb->msgno) (stream,uid); 1.2219 + else if (stream->dtb->uid) {/* indirect way */ 1.2220 + /* Placeholder for now, since currently there are no drivers which 1.2221 + * have a uid method but not a msgno method 1.2222 + */ 1.2223 + for (msgno = 1; msgno <= stream->nmsgs; msgno++) 1.2224 + if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno; 1.2225 + } 1.2226 + /* binary search since have full map */ 1.2227 + else for (first = 1,last = stream->nmsgs, delta = (first <= last) ? 1 : 0; 1.2228 + delta && 1.2229 + (uid >= (firstuid = mail_elt (stream,first)->private.uid)) && 1.2230 + (uid <= (lastuid = mail_elt (stream,last)->private.uid));) { 1.2231 + /* done if match at an endpoint */ 1.2232 + if (uid == firstuid) return first; 1.2233 + if (uid == lastuid) return last; 1.2234 + /* have anything between endpoints? */ 1.2235 + if (delta = ((last - first) / 2)) { 1.2236 + if ((miduid = mail_elt (stream,middle = first + delta)->private.uid) 1.2237 + == uid) 1.2238 + return middle; /* found match in middle */ 1.2239 + else if (uid < miduid) last = middle - 1; 1.2240 + else first = middle + 1; 1.2241 + } 1.2242 + } 1.2243 + } 1.2244 + else { /* dead stream, do linear search for UID */ 1.2245 + for (msgno = 1; msgno <= stream->nmsgs; msgno++) 1.2246 + if (mail_elt (stream,msgno)->private.uid == uid) return msgno; 1.2247 + } 1.2248 + return 0; /* didn't find the UID anywhere */ 1.2249 +} 1.2250 + 1.2251 +/* Mail fetch From string for menu 1.2252 + * Accepts: destination string 1.2253 + * mail stream 1.2254 + * message # to fetch 1.2255 + * desired string length 1.2256 + * Returns: string of requested length 1.2257 + */ 1.2258 + 1.2259 +void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno, 1.2260 + long length) 1.2261 +{ 1.2262 + char *t; 1.2263 + char tmp[MAILTMPLEN]; 1.2264 + ENVELOPE *env = mail_fetchenvelope (stream,msgno); 1.2265 + ADDRESS *adr = env ? env->from : NIL; 1.2266 + memset (s,' ',(size_t)length);/* fill it with spaces */ 1.2267 + s[length] = '\0'; /* tie off with null */ 1.2268 + /* get first from address from envelope */ 1.2269 + while (adr && !adr->host) adr = adr->next; 1.2270 + if (adr) { /* if a personal name exists use it */ 1.2271 + if (!(t = adr->personal)) 1.2272 + sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host); 1.2273 + memcpy (s,t,(size_t) min (length,(long) strlen (t))); 1.2274 + } 1.2275 +} 1.2276 + 1.2277 + 1.2278 +/* Mail fetch Subject string for menu 1.2279 + * Accepts: destination string 1.2280 + * mail stream 1.2281 + * message # to fetch 1.2282 + * desired string length 1.2283 + * Returns: string of no more than requested length 1.2284 + */ 1.2285 + 1.2286 +void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno, 1.2287 + long length) 1.2288 +{ 1.2289 + ENVELOPE *env = mail_fetchenvelope (stream,msgno); 1.2290 + memset (s,'\0',(size_t) length+1); 1.2291 + /* copy subject from envelope */ 1.2292 + if (env && env->subject) strncpy (s,env->subject,(size_t) length); 1.2293 + else *s = ' '; /* if no subject then just a space */ 1.2294 +} 1.2295 + 1.2296 +/* Mail modify flags 1.2297 + * Accepts: mail stream 1.2298 + * sequence 1.2299 + * flag(s) 1.2300 + * option flags 1.2301 + */ 1.2302 + 1.2303 +void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) 1.2304 +{ 1.2305 + MESSAGECACHE *elt; 1.2306 + unsigned long i,uf; 1.2307 + long f; 1.2308 + short nf; 1.2309 + if (!stream->dtb) return; /* no-op if no stream */ 1.2310 + if ((stream->dtb->flagmsg || !stream->dtb->flag) && 1.2311 + ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) : 1.2312 + mail_sequence (stream,sequence)) && 1.2313 + ((f = mail_parse_flags (stream,flag,&uf)) || uf)) 1.2314 + for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++) 1.2315 + if ((elt = mail_elt (stream,i))->sequence) { 1.2316 + struct { /* old flags */ 1.2317 + unsigned int valid : 1; 1.2318 + unsigned int seen : 1; 1.2319 + unsigned int deleted : 1; 1.2320 + unsigned int flagged : 1; 1.2321 + unsigned int answered : 1; 1.2322 + unsigned int draft : 1; 1.2323 + unsigned long user_flags; 1.2324 + } old; 1.2325 + old.valid = elt->valid; old.seen = elt->seen; 1.2326 + old.deleted = elt->deleted; old.flagged = elt->flagged; 1.2327 + old.answered = elt->answered; old.draft = elt->draft; 1.2328 + old.user_flags = elt->user_flags; 1.2329 + elt->valid = NIL; /* prepare for flag alteration */ 1.2330 + if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt); 1.2331 + if (f&fSEEN) elt->seen = nf; 1.2332 + if (f&fDELETED) elt->deleted = nf; 1.2333 + if (f&fFLAGGED) elt->flagged = nf; 1.2334 + if (f&fANSWERED) elt->answered = nf; 1.2335 + if (f&fDRAFT) elt->draft = nf; 1.2336 + /* user flags */ 1.2337 + if (flags & ST_SET) elt->user_flags |= uf; 1.2338 + else elt->user_flags &= ~uf; 1.2339 + elt->valid = T; /* flags now altered */ 1.2340 + if ((old.valid != elt->valid) || (old.seen != elt->seen) || 1.2341 + (old.deleted != elt->deleted) || (old.flagged != elt->flagged) || 1.2342 + (old.answered != elt->answered) || (old.draft != elt->draft) || 1.2343 + (old.user_flags != elt->user_flags)) 1.2344 + MM_FLAGS (stream,elt->msgno); 1.2345 + if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt); 1.2346 + } 1.2347 + /* call driver once */ 1.2348 + if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags); 1.2349 +} 1.2350 + 1.2351 +/* Mail search for messages 1.2352 + * Accepts: mail stream 1.2353 + * character set 1.2354 + * search program 1.2355 + * option flags 1.2356 + * Returns: T if successful, NIL if dead stream, NIL searchpgm or bad charset 1.2357 + */ 1.2358 + 1.2359 +long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm, 1.2360 + long flags) 1.2361 +{ 1.2362 + unsigned long i; 1.2363 + long ret = NIL; 1.2364 + if (!(flags & SE_RETAIN)) /* clear search vector unless retaining */ 1.2365 + for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL; 1.2366 + if (pgm && stream->dtb) /* must have a search program and driver */ 1.2367 + ret = (*(stream->dtb->search ? stream->dtb->search : mail_search_default)) 1.2368 + (stream,charset,pgm,flags); 1.2369 + /* flush search program if requested */ 1.2370 + if (flags & SE_FREE) mail_free_searchpgm (&pgm); 1.2371 + return ret; 1.2372 +} 1.2373 + 1.2374 + 1.2375 +/* Mail search for messages default handler 1.2376 + * Accepts: mail stream 1.2377 + * character set 1.2378 + * search program 1.2379 + * option flags 1.2380 + * Returns: T if successful, NIL if bad charset 1.2381 + */ 1.2382 + 1.2383 +long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm, 1.2384 + long flags) 1.2385 +{ 1.2386 + unsigned long i; 1.2387 + char *msg; 1.2388 + /* make sure that charset is good */ 1.2389 + if (msg = utf8_badcharset (charset)) { 1.2390 + MM_LOG (msg,ERROR); /* output error */ 1.2391 + fs_give ((void **) &msg); 1.2392 + return NIL; 1.2393 + } 1.2394 + utf8_searchpgm (pgm,charset); 1.2395 + for (i = 1; i <= stream->nmsgs; ++i) 1.2396 + if (mail_search_msg (stream,i,NIL,pgm)) { 1.2397 + if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i)); 1.2398 + else { /* mark as searched, notify mail program */ 1.2399 + mail_elt (stream,i)->searched = T; 1.2400 + if (!stream->silent) mm_searched (stream,i); 1.2401 + } 1.2402 + } 1.2403 + return LONGT; /* search completed */ 1.2404 +} 1.2405 + 1.2406 +/* Mail ping mailbox 1.2407 + * Accepts: mail stream 1.2408 + * Returns: stream if still open else NIL 1.2409 + */ 1.2410 + 1.2411 +long mail_ping (MAILSTREAM *stream) 1.2412 +{ 1.2413 + unsigned long i,n,uf,len; 1.2414 + char *s,*f,tmp[MAILTMPLEN],flags[MAILTMPLEN]; 1.2415 + MAILSTREAM *snarf; 1.2416 + MESSAGECACHE *elt; 1.2417 + STRING bs; 1.2418 + long ret; 1.2419 + /* do driver action */ 1.2420 + if ((ret = ((stream && stream->dtb) ? (stream->dtb->ping) (stream) : NIL)) && 1.2421 + stream->snarf.name && /* time to snarf? */ 1.2422 + /* prohibit faster than once/min */ 1.2423 + (time (0) > (time_t) (stream->snarf.time + min(60,mailsnarfinterval))) && 1.2424 + (snarf = mail_open (NIL,stream->snarf.name, 1.2425 + stream->snarf.options | OP_SILENT))) { 1.2426 + if ((n = snarf->nmsgs) && /* yes, have messages to snarf? */ 1.2427 + mail_search_full (snarf,NIL,mail_criteria ("UNDELETED"),SE_FREE)) { 1.2428 + for (i = 1; ret && (i <= n); i++) /* for each message */ 1.2429 + if ((elt = mail_elt (snarf,i))->searched && 1.2430 + (s = mail_fetch_message (snarf,i,&len,FT_PEEK)) && len) { 1.2431 + INIT (&bs,mail_string,s,len); 1.2432 + if (mailsnarfpreserve) { 1.2433 + /* yes, make sure have fast data */ 1.2434 + if (!elt->valid || !elt->day) { 1.2435 + sprintf (tmp,"%lu",n); 1.2436 + mail_fetch_fast (snarf,tmp,NIL); 1.2437 + } 1.2438 + /* initialize flag string */ 1.2439 + memset (flags,0,MAILTMPLEN); 1.2440 + /* output system flags except \Deleted */ 1.2441 + if (elt->seen) strcat (flags," \\Seen"); 1.2442 + if (elt->flagged) strcat (flags," \\Flagged"); 1.2443 + if (elt->answered) strcat (flags," \\Answered"); 1.2444 + if (elt->draft) strcat (flags," \\Draft"); 1.2445 + /* any user flags? */ 1.2446 + for (uf = elt->user_flags,s = flags + strlen (flags); 1.2447 + uf && (f = stream->user_flags[find_rightmost_bit (&uf)]) && 1.2448 + ((MAILTMPLEN - (s - tmp)) > (long) (2 + strlen (f))); 1.2449 + s += strlen (s)) sprintf (s," %s",f); 1.2450 + ret = mail_append_full (stream,stream->mailbox,flags + 1, 1.2451 + mail_date (tmp,elt),&bs); 1.2452 + } 1.2453 + else ret = mail_append (stream,stream->mailbox,&bs); 1.2454 + 1.2455 + if (ret) { /* did snarf succeed? */ 1.2456 + /* driver has per-message (or no) flag call */ 1.2457 + if (snarf->dtb->flagmsg || !snarf->dtb->flag) { 1.2458 + elt->valid = NIL; /* prepare for flag alteration */ 1.2459 + if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt); 1.2460 + /* flags now altered */ 1.2461 + elt->deleted = elt->seen = elt->valid = T; 1.2462 + if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt); 1.2463 + } 1.2464 + /* driver has one-time flag call */ 1.2465 + if (snarf->dtb->flag) { 1.2466 + sprintf (tmp,"%lu",i); 1.2467 + (*snarf->dtb->flag) (snarf,tmp,"\\Deleted \\Seen",ST_SET); 1.2468 + } 1.2469 + } 1.2470 + else { /* copy failed */ 1.2471 + sprintf (tmp,"Unable to move message %lu from %s mailbox", 1.2472 + i,snarf->dtb->name); 1.2473 + mm_log (tmp,WARN); 1.2474 + } 1.2475 + } 1.2476 + } 1.2477 + /* expunge the messages */ 1.2478 + mail_close_full (snarf,n ? CL_EXPUNGE : NIL); 1.2479 + stream->snarf.time = (unsigned long) time (0); 1.2480 + /* Even if the snarf failed, we don't want to return NIL if the stream 1.2481 + * is still alive. Or at least that's what we currently think. 1.2482 + */ 1.2483 + /* redo the driver's action */ 1.2484 + ret = stream->dtb ? (*stream->dtb->ping) (stream) : NIL; 1.2485 + } 1.2486 + return ret; 1.2487 +} 1.2488 + 1.2489 +/* Mail check mailbox 1.2490 + * Accepts: mail stream 1.2491 + */ 1.2492 + 1.2493 +void mail_check (MAILSTREAM *stream) 1.2494 +{ 1.2495 + /* do the driver's action */ 1.2496 + if (stream->dtb) (*stream->dtb->check) (stream); 1.2497 +} 1.2498 + 1.2499 + 1.2500 +/* Mail expunge mailbox 1.2501 + * Accepts: mail stream 1.2502 + * sequence to expunge if non-NIL 1.2503 + * expunge options 1.2504 + * Returns: T on success, NIL on failure 1.2505 + */ 1.2506 + 1.2507 +long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options) 1.2508 +{ 1.2509 + /* do the driver's action */ 1.2510 + return stream->dtb ? (*stream->dtb->expunge) (stream,sequence,options) : NIL; 1.2511 +} 1.2512 + 1.2513 + 1.2514 +/* Mail copy message(s) 1.2515 + * Accepts: mail stream 1.2516 + * sequence 1.2517 + * destination mailbox 1.2518 + * flags 1.2519 + */ 1.2520 + 1.2521 +long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox, 1.2522 + long options) 1.2523 +{ 1.2524 + return stream->dtb ? 1.2525 + SAFE_COPY (stream->dtb,stream,sequence,mailbox,options) : NIL; 1.2526 +} 1.2527 + 1.2528 +/* Append data package to use for old single-message mail_append() interface */ 1.2529 + 1.2530 +typedef struct mail_append_package { 1.2531 + char *flags; /* initial flags */ 1.2532 + char *date; /* message internal date */ 1.2533 + STRING *message; /* stringstruct of message */ 1.2534 +} APPENDPACKAGE; 1.2535 + 1.2536 + 1.2537 +/* Single append message string 1.2538 + * Accepts: mail stream 1.2539 + * package pointer (cast as a void *) 1.2540 + * pointer to return initial flags 1.2541 + * pointer to return message internal date 1.2542 + * pointer to return stringstruct of message to append 1.2543 + * Returns: T, always 1.2544 + */ 1.2545 + 1.2546 +static long mail_append_single (MAILSTREAM *stream,void *data,char **flags, 1.2547 + char **date,STRING **message) 1.2548 +{ 1.2549 + APPENDPACKAGE *ap = (APPENDPACKAGE *) data; 1.2550 + *flags = ap->flags; /* get desired data from the package */ 1.2551 + *date = ap->date; 1.2552 + *message = ap->message; 1.2553 + ap->message = NIL; /* so next callback puts a stop to it */ 1.2554 + return LONGT; /* always return success */ 1.2555 +} 1.2556 + 1.2557 + 1.2558 +/* Mail append message string 1.2559 + * Accepts: mail stream 1.2560 + * destination mailbox 1.2561 + * initial flags 1.2562 + * message internal date 1.2563 + * stringstruct of message to append 1.2564 + * Returns: T on success, NIL on failure 1.2565 + */ 1.2566 + 1.2567 +long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date, 1.2568 + STRING *message) 1.2569 +{ 1.2570 + APPENDPACKAGE ap; 1.2571 + ap.flags = flags; /* load append package */ 1.2572 + ap.date = date; 1.2573 + ap.message = message; 1.2574 + return mail_append_multiple (stream,mailbox,mail_append_single,(void *) &ap); 1.2575 +} 1.2576 + 1.2577 +/* Mail append message(s) 1.2578 + * Accepts: mail stream 1.2579 + * destination mailbox 1.2580 + * append data callback 1.2581 + * arbitrary data for callback use 1.2582 + * Returns: T on success, NIL on failure 1.2583 + */ 1.2584 + 1.2585 +long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af, 1.2586 + void *data) 1.2587 +{ 1.2588 + char *s,tmp[MAILTMPLEN]; 1.2589 + DRIVER *d = NIL; 1.2590 + long ret = NIL; 1.2591 + /* never allow names with newlines */ 1.2592 + if (strpbrk (mailbox,"\015\012")) 1.2593 + MM_LOG ("Can't append to mailbox with such a name",ERROR); 1.2594 + else if (strlen (mailbox) >= 1.2595 + (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) { 1.2596 + sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ? 1.2597 + "invalid remote specification" : "no such mailbox"); 1.2598 + MM_LOG (tmp,ERROR); 1.2599 + } 1.2600 + /* special driver hack? */ 1.2601 + else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) { 1.2602 + /* yes, tie off name at likely delimiter */ 1.2603 + if (!(s = strpbrk (tmp+8,"/\\:"))) { 1.2604 + sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox); 1.2605 + MM_LOG (tmp,ERROR); 1.2606 + return NIL; 1.2607 + } 1.2608 + *s++ = '\0'; /* tie off at delimiter */ 1.2609 + if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) { 1.2610 + sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox); 1.2611 + MM_LOG (tmp,ERROR); 1.2612 + } 1.2613 + else ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data); 1.2614 + } 1.2615 + else if (d = mail_valid (stream,mailbox,NIL)) 1.2616 + ret = SAFE_APPEND (d,stream,mailbox,af,data); 1.2617 + /* No driver, try for TRYCREATE if no stream. Note that we use the 1.2618 + * createProto here, not the appendProto, since the dummy driver already 1.2619 + * took care of the appendProto case. Otherwise, if appendProto is set to 1.2620 + * NIL, we won't get a TRYCREATE. 1.2621 + */ 1.2622 + else if (!stream && (stream = default_proto (NIL)) && stream->dtb && 1.2623 + SAFE_APPEND (stream->dtb,stream,mailbox,af,data)) 1.2624 + /* timing race? */ 1.2625 + MM_NOTIFY (stream,"Append validity confusion",WARN); 1.2626 + /* generate error message */ 1.2627 + else mail_valid (stream,mailbox,"append to mailbox"); 1.2628 + return ret; 1.2629 +} 1.2630 + 1.2631 +/* Mail garbage collect stream 1.2632 + * Accepts: mail stream 1.2633 + * garbage collection flags 1.2634 + */ 1.2635 + 1.2636 +void mail_gc (MAILSTREAM *stream,long gcflags) 1.2637 +{ 1.2638 + MESSAGECACHE *elt; 1.2639 + unsigned long i; 1.2640 + /* do the driver's action first */ 1.2641 + if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags); 1.2642 + stream->msgno = 0; /* nothing cached now */ 1.2643 + if (gcflags & GC_ENV) { /* garbage collect envelopes? */ 1.2644 + if (stream->env) mail_free_envelope (&stream->env); 1.2645 + if (stream->body) mail_free_body (&stream->body); 1.2646 + } 1.2647 + if (gcflags & GC_TEXTS) { /* free texts */ 1.2648 + if (stream->text.data) fs_give ((void **) &stream->text.data); 1.2649 + stream->text.size = 0; 1.2650 + } 1.2651 + /* garbage collect per-message stuff */ 1.2652 + for (i = 1; i <= stream->nmsgs; i++) 1.2653 + if (elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT)) 1.2654 + mail_gc_msg (&elt->private.msg,gcflags); 1.2655 +} 1.2656 + 1.2657 + 1.2658 +/* Mail garbage collect message 1.2659 + * Accepts: message structure 1.2660 + * garbage collection flags 1.2661 + */ 1.2662 + 1.2663 +void mail_gc_msg (MESSAGE *msg,long gcflags) 1.2664 +{ 1.2665 + if (gcflags & GC_ENV) { /* garbage collect envelopes? */ 1.2666 + mail_free_envelope (&msg->env); 1.2667 + mail_free_body (&msg->body); 1.2668 + } 1.2669 + if (gcflags & GC_TEXTS) { /* garbage collect texts */ 1.2670 + if (msg->full.text.data) fs_give ((void **) &msg->full.text.data); 1.2671 + if (msg->header.text.data) { 1.2672 + mail_free_stringlist (&msg->lines); 1.2673 + fs_give ((void **) &msg->header.text.data); 1.2674 + } 1.2675 + if (msg->text.text.data) fs_give ((void **) &msg->text.text.data); 1.2676 + /* now GC all body components */ 1.2677 + if (msg->body) mail_gc_body (msg->body); 1.2678 + } 1.2679 +} 1.2680 + 1.2681 +/* Mail garbage collect texts in BODY structure 1.2682 + * Accepts: BODY structure 1.2683 + */ 1.2684 + 1.2685 +void mail_gc_body (BODY *body) 1.2686 +{ 1.2687 + PART *part; 1.2688 + switch (body->type) { /* free contents */ 1.2689 + case TYPEMULTIPART: /* multiple part */ 1.2690 + for (part = body->nested.part; part; part = part->next) 1.2691 + mail_gc_body (&part->body); 1.2692 + break; 1.2693 + case TYPEMESSAGE: /* encapsulated message */ 1.2694 + if (body->subtype && !strcmp (body->subtype,"RFC822")) { 1.2695 + mail_free_stringlist (&body->nested.msg->lines); 1.2696 + mail_gc_msg (body->nested.msg,GC_TEXTS); 1.2697 + } 1.2698 + break; 1.2699 + default: 1.2700 + break; 1.2701 + } 1.2702 + if (body->mime.text.data) fs_give ((void **) &body->mime.text.data); 1.2703 + if (body->contents.text.data) fs_give ((void **) &body->contents.text.data); 1.2704 +} 1.2705 + 1.2706 +/* Mail get body part 1.2707 + * Accepts: mail stream 1.2708 + * message number 1.2709 + * section specifier 1.2710 + * Returns: pointer to body 1.2711 + */ 1.2712 + 1.2713 +BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,unsigned char *section) 1.2714 +{ 1.2715 + BODY *b = NIL; 1.2716 + PART *pt; 1.2717 + unsigned long i; 1.2718 + /* make sure have a body */ 1.2719 + if (section && *section && mail_fetchstructure (stream,msgno,&b) && b) 1.2720 + while (*section) { /* find desired section */ 1.2721 + if (isdigit (*section)) { /* get section specifier */ 1.2722 + /* make sure what follows is valid */ 1.2723 + if (!(i = strtoul (section,(char **) §ion,10)) || 1.2724 + (*section && ((*section++ != '.') || !*section))) return NIL; 1.2725 + /* multipart content? */ 1.2726 + if (b->type == TYPEMULTIPART) { 1.2727 + /* yes, find desired part */ 1.2728 + if (pt = b->nested.part) while (--i && (pt = pt->next)); 1.2729 + if (!pt) return NIL; /* bad specifier */ 1.2730 + b = &pt->body; /* note new body */ 1.2731 + } 1.2732 + /* otherwise must be section 1 */ 1.2733 + else if (i != 1) return NIL; 1.2734 + /* need to go down further? */ 1.2735 + if (*section) switch (b->type) { 1.2736 + case TYPEMULTIPART: /* multipart */ 1.2737 + break; 1.2738 + case TYPEMESSAGE: /* embedded message */ 1.2739 + if (!strcmp (b->subtype,"RFC822")) { 1.2740 + b = b->nested.msg->body; 1.2741 + break; 1.2742 + } 1.2743 + default: /* bogus subpart specification */ 1.2744 + return NIL; 1.2745 + } 1.2746 + } 1.2747 + else return NIL; /* unknown section specifier */ 1.2748 + } 1.2749 + return b; 1.2750 +} 1.2751 + 1.2752 +/* Mail output date from elt fields 1.2753 + * Accepts: character string to write into 1.2754 + * elt to get data data from 1.2755 + * Returns: the character string 1.2756 + */ 1.2757 + 1.2758 +const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 1.2759 + 1.2760 +const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 1.2761 + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 1.2762 + 1.2763 +char *mail_date (char *string,MESSAGECACHE *elt) 1.2764 +{ 1.2765 + sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d", 1.2766 + elt->day ? elt->day : 1, 1.2767 + months[elt->month ? (elt->month - 1) : 0], 1.2768 + elt->year + BASEYEAR,elt->hours,elt->minutes,elt->seconds, 1.2769 + elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes); 1.2770 + return string; 1.2771 +} 1.2772 + 1.2773 + 1.2774 +/* Mail output extended-ctime format date from elt fields 1.2775 + * Accepts: character string to write into 1.2776 + * elt to get data data from 1.2777 + * Returns: the character string 1.2778 + */ 1.2779 + 1.2780 +char *mail_cdate (char *string,MESSAGECACHE *elt) 1.2781 +{ 1.2782 + char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02d\n"; 1.2783 + int d = elt->day ? elt->day : 1; 1.2784 + int m = elt->month ? (elt->month - 1) : 0; 1.2785 + int y = elt->year + BASEYEAR; 1.2786 + const char *s = months[m]; 1.2787 + if (m < 2) { /* if before March, */ 1.2788 + m += 10; /* January = month 10 of previous year */ 1.2789 + y--; 1.2790 + } 1.2791 + else m -= 2; /* March is month 0 */ 1.2792 + sprintf (string,fmt,days[(int) (d + 2 + ((7 + 31 * m) / 12) 1.2793 +#ifndef USEJULIANCALENDAR 1.2794 +#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */ 1.2795 + + (y / 400) 1.2796 +#ifdef Y4KBUGFIX 1.2797 + - (y / 4000) 1.2798 +#endif 1.2799 +#else /* Orthodox calendar */ 1.2800 + + (2 * (y / 900)) + ((y % 900) >= 200) 1.2801 + + ((y % 900) >= 600) 1.2802 +#endif 1.2803 + - (y / 100) 1.2804 +#endif 1.2805 + + y + (y / 4)) % 7], 1.2806 + s,d,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR, 1.2807 + elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes); 1.2808 + return string; 1.2809 +} 1.2810 + 1.2811 +/* Mail parse date into elt fields 1.2812 + * Accepts: elt to write into 1.2813 + * date string to parse 1.2814 + * Returns: T if parse successful, else NIL 1.2815 + * This routine parses dates as follows: 1.2816 + * . leading three alphas followed by comma and space are ignored 1.2817 + * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy, 1.2818 + * dd mmm yy, dd mmm yyyy, yyyy-mm-dd, yyyymmdd 1.2819 + * . two and three digit years interpreted according to RFC 2822 rules 1.2820 + * . mandatory end of string if yyyy-mm-dd or yyyymmdd; otherwise optional 1.2821 + * space followed by time: 1.2822 + * . time accepted in format hh:mm:ss or hh:mm 1.2823 + * . end of string accepted 1.2824 + * . timezone accepted: hyphen followed by symbolic timezone, or space 1.2825 + * followed by signed numeric timezone or symbolic timezone 1.2826 + * Examples of normal input: 1.2827 + * . IMAP date-only (SEARCH): 1.2828 + * dd-mmm-yyyy 1.2829 + * . IMAP date-time (INTERNALDATE): 1.2830 + * dd-mmm-yyyy hh:mm:ss +zzzz 1.2831 + * . RFC-822: 1.2832 + * www, dd mmm yy hh:mm:ss zzz 1.2833 + * . RFC-2822: 1.2834 + * www, dd mmm yyyy hh:mm:ss +zzzz 1.2835 + */ 1.2836 + 1.2837 +long mail_parse_date (MESSAGECACHE *elt,unsigned char *s) 1.2838 +{ 1.2839 + unsigned long d,m,y; 1.2840 + int mi,ms; 1.2841 + struct tm *t; 1.2842 + time_t tn; 1.2843 + char tmp[MAILTMPLEN]; 1.2844 + static unsigned long maxyear = 0; 1.2845 + if (!maxyear) { /* know the end of time yet? */ 1.2846 + MESSAGECACHE tmpelt; 1.2847 + memset (&tmpelt,0xff,sizeof (MESSAGECACHE)); 1.2848 + maxyear = BASEYEAR + tmpelt.year; 1.2849 + } 1.2850 + /* clear elt */ 1.2851 + elt->zoccident = elt->zhours = elt->zminutes = 1.2852 + elt->hours = elt->minutes = elt->seconds = 1.2853 + elt->day = elt->month = elt->year = 0; 1.2854 + /* make a writeable uppercase copy */ 1.2855 + if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s)); 1.2856 + else return NIL; 1.2857 + /* skip over possible day of week */ 1.2858 + if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') && 1.2859 + (s[4] == ' ')) s += 5; 1.2860 + while (*s == ' ') s++; /* parse first number (probable month) */ 1.2861 + if (!(m = strtoul (s,(char **) &s,10))) return NIL; 1.2862 + 1.2863 + switch (*s) { /* different parse based on delimiter */ 1.2864 + case '/': /* mm/dd/yy format */ 1.2865 + if (isdigit (*++s) && (d = strtoul (s,(char **) &s,10)) && 1.2866 + (*s == '/') && isdigit (*++s)) { 1.2867 + y = strtoul (s,(char **) &s,10); 1.2868 + if (*s == '\0') break; /* must end here */ 1.2869 + } 1.2870 + return NIL; /* bogon */ 1.2871 + case ' ': /* dd mmm yy format */ 1.2872 + while (s[1] == ' ') s++; /* slurp extra whitespace */ 1.2873 + case '-': 1.2874 + if (isdigit (s[1])) { /* possible ISO 8601 date format? */ 1.2875 + y = m; /* yes, first number is year */ 1.2876 + /* get month and day */ 1.2877 + if ((m = strtoul (s+1,(char **) &s,10)) && (*s++ == '-') && 1.2878 + (d = strtoul (s,(char **) &s,10)) && !*s) break; 1.2879 + return NIL; /* syntax error or time present */ 1.2880 + } 1.2881 + d = m; /* dd-mmm-yy[yy], so first number is a day */ 1.2882 + /* make sure string long enough! */ 1.2883 + if (strlen (s) < (size_t) 5) return NIL; 1.2884 + /* Some compilers don't allow `<<' and/or longs in case statements. */ 1.2885 + /* slurp up the month string */ 1.2886 + ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A'); 1.2887 + switch (ms) { /* determine the month */ 1.2888 + case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break; 1.2889 + case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break; 1.2890 + case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break; 1.2891 + case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break; 1.2892 + case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break; 1.2893 + case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break; 1.2894 + case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break; 1.2895 + case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break; 1.2896 + case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break; 1.2897 + case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break; 1.2898 + case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break; 1.2899 + case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break; 1.2900 + default: return NIL; /* unknown month */ 1.2901 + } 1.2902 + if (s[4] == *s) s += 5; /* advance to year */ 1.2903 + else { /* first three were OK, possibly full name */ 1.2904 + mi = *s; /* note delimiter, skip alphas */ 1.2905 + for (s += 4; isalpha (*s); s++); 1.2906 + /* error if delimiter not here */ 1.2907 + if (mi != *s++) return NIL; 1.2908 + } 1.2909 + while (*s == ' ') s++; /* parse year */ 1.2910 + if (isdigit (*s)) { /* must be a digit here */ 1.2911 + y = strtoul (s,(char **) &s,10); 1.2912 + if (*s == '\0' || *s == ' ') break; 1.2913 + } 1.2914 + case '\0': /* ISO 8601 compact date */ 1.2915 + if (m < (BASEYEAR * 10000)) return NIL; 1.2916 + y = m / 10000; /* get year */ 1.2917 + d = (m %= 10000) % 100; /* get day */ 1.2918 + m /= 100; /* and month */ 1.2919 + break; 1.2920 + default: 1.2921 + return NIL; /* unknown date format */ 1.2922 + } 1.2923 + 1.2924 + /* minimal validity check of date */ 1.2925 + if ((d > 31) || (m > 12)) return NIL; 1.2926 + if (y < 49) y += 2000; /* RFC 2282 rules for two digit years 00-49 */ 1.2927 + else if (y < 999) y += 1900; /* 2-digit years 50-99 and 3-digit years */ 1.2928 + /* reject prehistoric and far future years */ 1.2929 + if ((y < BASEYEAR) || (y > maxyear)) return NIL; 1.2930 + /* set values in elt */ 1.2931 + elt->day = d; elt->month = m; elt->year = y - BASEYEAR; 1.2932 + ms = '\0'; /* initially no time zone string */ 1.2933 + if (*s) { /* time specification present? */ 1.2934 + /* parse time */ 1.2935 + d = strtoul (s+1,(char **) &s,10); 1.2936 + if (*s != ':') return NIL; 1.2937 + m = strtoul (++s,(char **) &s,10); 1.2938 + y = (*s == ':') ? strtoul (++s,(char **) &s,10) : 0; 1.2939 + /* validity check time */ 1.2940 + if ((d > 23) || (m > 59) || (y > 60)) return NIL; 1.2941 + /* set values in elt */ 1.2942 + elt->hours = d; elt->minutes = m; elt->seconds = y; 1.2943 + switch (*s) { /* time zone specifier? */ 1.2944 + case ' ': /* numeric time zone */ 1.2945 + while (s[1] == ' ') s++; /* slurp extra whitespace */ 1.2946 + if (!isalpha (s[1])) { /* treat as '-' case if alphabetic */ 1.2947 + /* test for sign character */ 1.2948 + if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++; 1.2949 + /* validate proper timezone */ 1.2950 + if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && (s[2] < '6') && 1.2951 + isdigit(s[3])) { 1.2952 + elt->zhours = (*s - '0') * 10 + (s[1] - '0'); 1.2953 + elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0'); 1.2954 + } 1.2955 + return T; /* all done! */ 1.2956 + } 1.2957 + /* falls through */ 1.2958 + case '-': /* symbolic time zone */ 1.2959 + if (!(ms = *++s)) ms = 'Z'; 1.2960 + else if (*++s) { /* multi-character? */ 1.2961 + ms -= 'A'; ms *= 1024; /* yes, make compressed three-byte form */ 1.2962 + ms += ((*s++ - 'A') * 32); 1.2963 + if (*s) ms += *s++ - 'A'; 1.2964 + if (*s) ms = '\0'; /* more than three characters */ 1.2965 + } 1.2966 + default: /* ignore anything else */ 1.2967 + break; 1.2968 + } 1.2969 + } 1.2970 + 1.2971 + /* This is not intended to be a comprehensive list of all possible 1.2972 + * timezone strings. Such a list would be impractical. Rather, this 1.2973 + * listing is intended to incorporate all military, North American, and 1.2974 + * a few special cases such as Japan and the major European zone names, 1.2975 + * such as what might be expected to be found in a Tenex format mailbox 1.2976 + * and spewed from an IMAP server. The trend is to migrate to numeric 1.2977 + * timezones which lack the flavor but also the ambiguity of the names. 1.2978 + * 1.2979 + * RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the 1.2980 + * 4 CONUS timezones and their summer time variants. [Sorry, Canadian 1.2981 + * Atlantic Provinces, Alaska, and Hawaii.] 1.2982 + */ 1.2983 + switch (ms) { /* determine the timezone */ 1.2984 + /* Universal */ 1.2985 + case (('U'-'A')*1024)+(('T'-'A')*32): 1.2986 +#ifndef STRICT_RFC822_TIMEZONES 1.2987 + case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A': 1.2988 +#endif 1.2989 + /* Greenwich */ 1.2990 + case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A': 1.2991 + case 'Z': elt->zhours = 0; break; 1.2992 + 1.2993 + /* oriental (from Greenwich) timezones */ 1.2994 +#ifndef STRICT_RFC822_TIMEZONES 1.2995 + /* Middle Europe */ 1.2996 + case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A': 1.2997 +#endif 1.2998 +#ifdef BRITISH_SUMMER_TIME 1.2999 + /* British Summer */ 1.3000 + case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3001 +#endif 1.3002 + case 'A': elt->zhours = 1; break; 1.3003 +#ifndef STRICT_RFC822_TIMEZONES 1.3004 + /* Eastern Europe */ 1.3005 + case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A': 1.3006 +#endif 1.3007 + case 'B': elt->zhours = 2; break; 1.3008 + case 'C': elt->zhours = 3; break; 1.3009 + case 'D': elt->zhours = 4; break; 1.3010 + case 'E': elt->zhours = 5; break; 1.3011 + case 'F': elt->zhours = 6; break; 1.3012 + case 'G': elt->zhours = 7; break; 1.3013 + case 'H': elt->zhours = 8; break; 1.3014 +#ifndef STRICT_RFC822_TIMEZONES 1.3015 + /* Japan */ 1.3016 + case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3017 +#endif 1.3018 + case 'I': elt->zhours = 9; break; 1.3019 + case 'K': elt->zhours = 10; break; 1.3020 + case 'L': elt->zhours = 11; break; 1.3021 + case 'M': elt->zhours = 12; break; 1.3022 + 1.3023 + /* occidental (from Greenwich) timezones */ 1.3024 + case 'N': elt->zoccident = 1; elt->zhours = 1; break; 1.3025 + case 'O': elt->zoccident = 1; elt->zhours = 2; break; 1.3026 +#ifndef STRICT_RFC822_TIMEZONES 1.3027 + case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3028 +#endif 1.3029 + case 'P': elt->zoccident = 1; elt->zhours = 3; break; 1.3030 +#ifdef NEWFOUNDLAND_STANDARD_TIME 1.3031 + /* Newfoundland */ 1.3032 + case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3033 + elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break; 1.3034 +#endif 1.3035 +#ifndef STRICT_RFC822_TIMEZONES 1.3036 + /* Atlantic */ 1.3037 + case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3038 +#endif 1.3039 + /* CONUS */ 1.3040 + case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3041 + case 'Q': elt->zoccident = 1; elt->zhours = 4; break; 1.3042 + /* Eastern */ 1.3043 + case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3044 + case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3045 + case 'R': elt->zoccident = 1; elt->zhours = 5; break; 1.3046 + /* Central */ 1.3047 + case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3048 + case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3049 + case 'S': elt->zoccident = 1; elt->zhours = 6; break; 1.3050 + /* Mountain */ 1.3051 + case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3052 + case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3053 + case 'T': elt->zoccident = 1; elt->zhours = 7; break; 1.3054 + /* Pacific */ 1.3055 + case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3056 +#ifndef STRICT_RFC822_TIMEZONES 1.3057 + case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A': 1.3058 +#endif 1.3059 + case 'U': elt->zoccident = 1; elt->zhours = 8; break; 1.3060 +#ifndef STRICT_RFC822_TIMEZONES 1.3061 + /* Yukon */ 1.3062 + case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3063 +#endif 1.3064 + case 'V': elt->zoccident = 1; elt->zhours = 9; break; 1.3065 +#ifndef STRICT_RFC822_TIMEZONES 1.3066 + /* Hawaii */ 1.3067 + case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3068 +#endif 1.3069 + case 'W': elt->zoccident = 1; elt->zhours = 10; break; 1.3070 + /* Nome/Bering/Samoa */ 1.3071 +#ifdef NOME_STANDARD_TIME 1.3072 + case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3073 +#endif 1.3074 +#ifdef BERING_STANDARD_TIME 1.3075 + case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3076 +#endif 1.3077 +#ifdef SAMOA_STANDARD_TIME 1.3078 + case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A': 1.3079 +#endif 1.3080 + case 'X': elt->zoccident = 1; elt->zhours = 11; break; 1.3081 + case 'Y': elt->zoccident = 1; elt->zhours = 12; break; 1.3082 + 1.3083 + default: /* unknown time zones treated as local */ 1.3084 + tn = time (0); /* time now... */ 1.3085 + t = localtime (&tn); /* get local minutes since midnight */ 1.3086 + mi = t->tm_hour * 60 + t->tm_min; 1.3087 + ms = t->tm_yday; /* note Julian day */ 1.3088 + if (t = gmtime (&tn)) { /* minus UTC minutes since midnight */ 1.3089 + mi -= t->tm_hour * 60 + t->tm_min; 1.3090 + /* ms can be one of: 1.3091 + * 36x local time is December 31, UTC is January 1, offset -24 hours 1.3092 + * 1 local time is 1 day ahead of UTC, offset +24 hours 1.3093 + * 0 local time is same day as UTC, no offset 1.3094 + * -1 local time is 1 day behind UTC, offset -24 hours 1.3095 + * -36x local time is January 1, UTC is December 31, offset +24 hours 1.3096 + */ 1.3097 + if (ms -= t->tm_yday) /* correct offset if different Julian day */ 1.3098 + mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60; 1.3099 + if (mi < 0) { /* occidental? */ 1.3100 + mi = abs (mi); /* yup, make positive number */ 1.3101 + elt->zoccident = 1; /* and note west of UTC */ 1.3102 + } 1.3103 + elt->zhours = mi / 60; /* now break into hours and minutes */ 1.3104 + elt->zminutes = mi % 60; 1.3105 + } 1.3106 + break; 1.3107 + } 1.3108 + return T; 1.3109 +} 1.3110 + 1.3111 +/* Mail n messages exist 1.3112 + * Accepts: mail stream 1.3113 + * number of messages 1.3114 + */ 1.3115 + 1.3116 +void mail_exists (MAILSTREAM *stream,unsigned long nmsgs) 1.3117 +{ 1.3118 + char tmp[MAILTMPLEN]; 1.3119 + if (nmsgs > MAXMESSAGES) { 1.3120 + sprintf (tmp,"Mailbox has more messages (%lu) exist than maximum (%lu)", 1.3121 + nmsgs,MAXMESSAGES); 1.3122 + mm_log (tmp,ERROR); 1.3123 + nmsgs = MAXMESSAGES; /* cap to maximum */ 1.3124 + /* probably will crash in mail_elt() soon enough... */ 1.3125 + } 1.3126 + /* make sure cache is large enough */ 1.3127 + (*mailcache) (stream,nmsgs,CH_SIZE); 1.3128 + stream->nmsgs = nmsgs; /* update stream status */ 1.3129 + /* notify main program of change */ 1.3130 + if (!stream->silent) MM_EXISTS (stream,nmsgs); 1.3131 +} 1.3132 + 1.3133 + 1.3134 +/* Mail n messages are recent 1.3135 + * Accepts: mail stream 1.3136 + * number of recent messages 1.3137 + */ 1.3138 + 1.3139 +void mail_recent (MAILSTREAM *stream,unsigned long recent) 1.3140 +{ 1.3141 + char tmp[MAILTMPLEN]; 1.3142 + if (recent <= stream->nmsgs) stream->recent = recent; 1.3143 + else { 1.3144 + sprintf (tmp,"Non-existent recent message(s) %lu, nmsgs=%lu", 1.3145 + recent,stream->nmsgs); 1.3146 + mm_log (tmp,ERROR); 1.3147 + } 1.3148 +} 1.3149 + 1.3150 + 1.3151 +/* Mail message n is expunged 1.3152 + * Accepts: mail stream 1.3153 + * message # 1.3154 + */ 1.3155 + 1.3156 +void mail_expunged (MAILSTREAM *stream,unsigned long msgno) 1.3157 +{ 1.3158 + char tmp[MAILTMPLEN]; 1.3159 + MESSAGECACHE *elt; 1.3160 + if (msgno > stream->nmsgs) { 1.3161 + sprintf (tmp,"Expunge of non-existent message %lu, nmsgs=%lu", 1.3162 + msgno,stream->nmsgs); 1.3163 + mm_log (tmp,ERROR); 1.3164 + } 1.3165 + else { 1.3166 + elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT); 1.3167 + /* notify main program of change */ 1.3168 + if (!stream->silent) MM_EXPUNGED (stream,msgno); 1.3169 + if (elt) { /* if an element is there */ 1.3170 + elt->msgno = 0; /* invalidate its message number and free */ 1.3171 + (*mailcache) (stream,msgno,CH_FREE); 1.3172 + (*mailcache) (stream,msgno,CH_FREESORTCACHE); 1.3173 + } 1.3174 + /* expunge the slot */ 1.3175 + (*mailcache) (stream,msgno,CH_EXPUNGE); 1.3176 + --stream->nmsgs; /* update stream status */ 1.3177 + if (stream->msgno) { /* have stream pointers? */ 1.3178 + /* make sure the short cache is nuked */ 1.3179 + if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS); 1.3180 + else stream->msgno = 0; /* make sure invalidated in any case */ 1.3181 + } 1.3182 + } 1.3183 +} 1.3184 + 1.3185 +/* Mail stream status routines */ 1.3186 + 1.3187 + 1.3188 +/* Mail lock stream 1.3189 + * Accepts: mail stream 1.3190 + */ 1.3191 + 1.3192 +void mail_lock (MAILSTREAM *stream) 1.3193 +{ 1.3194 + if (stream->lock) { 1.3195 + char tmp[MAILTMPLEN]; 1.3196 + sprintf (tmp,"Lock when already locked, mbx=%.80s", 1.3197 + stream->mailbox ? stream->mailbox : "???"); 1.3198 + fatal (tmp); 1.3199 + } 1.3200 + else stream->lock = T; /* lock stream */ 1.3201 +} 1.3202 + 1.3203 + 1.3204 +/* Mail unlock stream 1.3205 + * Accepts: mail stream 1.3206 + */ 1.3207 + 1.3208 +void mail_unlock (MAILSTREAM *stream) 1.3209 +{ 1.3210 + if (!stream->lock) fatal ("Unlock when not locked"); 1.3211 + else stream->lock = NIL; /* unlock stream */ 1.3212 +} 1.3213 + 1.3214 + 1.3215 +/* Mail turn on debugging telemetry 1.3216 + * Accepts: mail stream 1.3217 + */ 1.3218 + 1.3219 +void mail_debug (MAILSTREAM *stream) 1.3220 +{ 1.3221 + stream->debug = T; /* turn on debugging telemetry */ 1.3222 + if (stream->dtb) (*stream->dtb->parameters) (ENABLE_DEBUG,stream); 1.3223 +} 1.3224 + 1.3225 + 1.3226 +/* Mail turn off debugging telemetry 1.3227 + * Accepts: mail stream 1.3228 + */ 1.3229 + 1.3230 +void mail_nodebug (MAILSTREAM *stream) 1.3231 +{ 1.3232 + stream->debug = NIL; /* turn off debugging telemetry */ 1.3233 + if (stream->dtb) (*stream->dtb->parameters) (DISABLE_DEBUG,stream); 1.3234 +} 1.3235 + 1.3236 + 1.3237 +/* Mail log to debugging telemetry 1.3238 + * Accepts: message 1.3239 + * flag that data is "sensitive" 1.3240 + */ 1.3241 + 1.3242 +void mail_dlog (char *string,long flag) 1.3243 +{ 1.3244 + mm_dlog ((debugsensitive || !flag) ? string : "<suppressed>"); 1.3245 +} 1.3246 + 1.3247 +/* Mail parse UID sequence 1.3248 + * Accepts: mail stream 1.3249 + * sequence to parse 1.3250 + * Returns: T if parse successful, else NIL 1.3251 + */ 1.3252 + 1.3253 +long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence) 1.3254 +{ 1.3255 + unsigned long i,j,k,x,y; 1.3256 + for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL; 1.3257 + while (sequence && *sequence){/* while there is something to parse */ 1.3258 + if (*sequence == '*') { /* maximum message */ 1.3259 + i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last; 1.3260 + sequence++; /* skip past * */ 1.3261 + } 1.3262 + /* parse and validate message number */ 1.3263 + /* parse and validate message number */ 1.3264 + else if (!isdigit (*sequence)) { 1.3265 + MM_LOG ("Syntax error in sequence",ERROR); 1.3266 + return NIL; 1.3267 + } 1.3268 + else if (!(i = strtoul (sequence,(char **) &sequence,10))) { 1.3269 + MM_LOG ("UID may not be zero",ERROR); 1.3270 + return NIL; 1.3271 + } 1.3272 + switch (*sequence) { /* see what the delimiter is */ 1.3273 + case ':': /* sequence range */ 1.3274 + if (*++sequence == '*') { /* maximum message */ 1.3275 + j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last; 1.3276 + sequence++; /* skip past * */ 1.3277 + } 1.3278 + /* parse end of range */ 1.3279 + else if (!(j = strtoul (sequence,(char **) &sequence,10))) { 1.3280 + MM_LOG ("UID sequence range invalid",ERROR); 1.3281 + return NIL; 1.3282 + } 1.3283 + if (*sequence && *sequence++ != ',') { 1.3284 + MM_LOG ("UID sequence range syntax error",ERROR); 1.3285 + return NIL; 1.3286 + } 1.3287 + if (i > j) { /* swap the range if backwards */ 1.3288 + x = i; i = j; j = x; 1.3289 + } 1.3290 + x = mail_msgno (stream,i);/* get msgnos */ 1.3291 + y = mail_msgno (stream,j);/* for both UIDS (don't && it) */ 1.3292 + /* easy if both UIDs valid */ 1.3293 + if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T; 1.3294 + /* start UID valid, end is not */ 1.3295 + else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j)) 1.3296 + mail_elt (stream,x++)->sequence = T; 1.3297 + /* end UID valid, start is not */ 1.3298 + else if (y) for (x = 1; x <= y; x++) { 1.3299 + if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T; 1.3300 + } 1.3301 + /* neither is valid, ugh */ 1.3302 + else for (x = 1; x <= stream->nmsgs; x++) 1.3303 + if (((k = mail_uid (stream,x)) >= i) && (k <= j)) 1.3304 + mail_elt (stream,x)->sequence = T; 1.3305 + break; 1.3306 + case ',': /* single message */ 1.3307 + ++sequence; /* skip the delimiter, fall into end case */ 1.3308 + case '\0': /* end of sequence, mark this message */ 1.3309 + if (x = mail_msgno (stream,i)) mail_elt (stream,x)->sequence = T; 1.3310 + break; 1.3311 + default: /* anything else is a syntax error! */ 1.3312 + MM_LOG ("UID sequence syntax error",ERROR); 1.3313 + return NIL; 1.3314 + } 1.3315 + } 1.3316 + return T; /* successfully parsed sequence */ 1.3317 +} 1.3318 + 1.3319 +/* Mail see if line list matches that in cache 1.3320 + * Accepts: candidate line list 1.3321 + * cached line list 1.3322 + * matching flags 1.3323 + * Returns: T if match, NIL if no match 1.3324 + */ 1.3325 + 1.3326 +long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags) 1.3327 +{ 1.3328 + unsigned long i; 1.3329 + unsigned char *s,*t; 1.3330 + STRINGLIST *m; 1.3331 + if (!msglines) return T; /* full header is in cache */ 1.3332 + /* need full header but filtered in cache */ 1.3333 + if ((flags & FT_NOT) || !lines) return NIL; 1.3334 + do { /* make sure all present & accounted for */ 1.3335 + for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) { 1.3336 + for (s = lines->text.data,t = m->text.data,i = lines->text.size; 1.3337 + i && !compare_uchar (*s,*t); s++,t++,i--); 1.3338 + if (!i) break; /* this line matches */ 1.3339 + } 1.3340 + if (!m) return NIL; /* didn't find in the list */ 1.3341 + } 1.3342 + while (lines = lines->next); 1.3343 + return T; /* all lines found */ 1.3344 +} 1.3345 + 1.3346 +/* Mail filter text by header lines 1.3347 + * Accepts: text to filter, with trailing null 1.3348 + * length of text 1.3349 + * list of lines 1.3350 + * fetch flags 1.3351 + * Returns: new text size, text overwritten 1.3352 + */ 1.3353 + 1.3354 +unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines, 1.3355 + long flags) 1.3356 +{ 1.3357 + STRINGLIST *hdrs; 1.3358 + int notfound; 1.3359 + unsigned long i; 1.3360 + char c,*s,*e,*t,tmp[MAILTMPLEN]; 1.3361 + char *src = text; 1.3362 + char *dst = src; 1.3363 + char *end = text + len; 1.3364 + text[len] = '\012'; /* guard against running off buffer */ 1.3365 + while (src < end) { /* process header */ 1.3366 + /* slurp header line name */ 1.3367 + for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; 1.3368 + (s < e) && ((c = (*s ? *s : (*s = ' '))) != ':') && 1.3369 + ((c > ' ') || 1.3370 + ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))); 1.3371 + *t++ = *s++); 1.3372 + *t = '\0'; /* tie off */ 1.3373 + notfound = T; /* not found yet */ 1.3374 + if (i = t - tmp) /* see if found in header */ 1.3375 + for (hdrs = lines; hdrs && notfound; hdrs = hdrs->next) 1.3376 + if ((hdrs->text.size == i) && !compare_csizedtext (tmp,&hdrs->text)) 1.3377 + notfound = NIL; 1.3378 + /* skip header line if not wanted */ 1.3379 + if (i && ((flags & FT_NOT) ? !notfound : notfound)) 1.3380 + while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3381 + (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3382 + (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3383 + (*src++ != '\012')) || ((*src == ' ') || (*src == '\t'))); 1.3384 + else if (src == dst) { /* copy to self */ 1.3385 + while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3386 + (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3387 + (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') && 1.3388 + (*src++ != '\012')) || ((*src == ' ') || (*src == '\t'))); 1.3389 + dst = src; /* update destination */ 1.3390 + } 1.3391 + else { /* copy line and any continuation line */ 1.3392 + while ((((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') && 1.3393 + ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') && 1.3394 + ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') && 1.3395 + ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') && 1.3396 + ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012'))|| 1.3397 + ((*src == ' ') || (*src == '\t'))); 1.3398 + /* in case hit the guard LF */ 1.3399 + if (src > end) dst -= (src - end); 1.3400 + } 1.3401 + } 1.3402 + *dst = '\0'; /* tie off destination */ 1.3403 + return dst - text; 1.3404 +} 1.3405 + 1.3406 +/* Local mail search message 1.3407 + * Accepts: MAIL stream 1.3408 + * message number 1.3409 + * optional section specification 1.3410 + * search program 1.3411 + * Returns: T if found, NIL otherwise 1.3412 + */ 1.3413 + 1.3414 +long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section, 1.3415 + SEARCHPGM *pgm) 1.3416 +{ 1.3417 + unsigned short d; 1.3418 + char tmp[MAILTMPLEN]; 1.3419 + MESSAGECACHE *elt = mail_elt (stream,msgno); 1.3420 + SEARCHHEADER *hdr; 1.3421 + SEARCHOR *or; 1.3422 + SEARCHPGMLIST *not; 1.3423 + unsigned long now = (unsigned long) time (0); 1.3424 + if (pgm->msgno || pgm->uid) { /* message set searches */ 1.3425 + SEARCHSET *set; 1.3426 + /* message sequences */ 1.3427 + if (pgm->msgno) { /* inside this message sequence set */ 1.3428 + for (set = pgm->msgno; set; set = set->next) 1.3429 + if (set->last ? ((set->first <= set->last) ? 1.3430 + ((msgno >= set->first) && (msgno <= set->last)) : 1.3431 + ((msgno >= set->last) && (msgno <= set->first))) : 1.3432 + msgno == set->first) break; 1.3433 + if (!set) return NIL; /* not found within sequence */ 1.3434 + } 1.3435 + if (pgm->uid) { /* inside this unique identifier set */ 1.3436 + unsigned long uid = mail_uid (stream,msgno); 1.3437 + for (set = pgm->uid; set; set = set->next) 1.3438 + if (set->last ? ((set->first <= set->last) ? 1.3439 + ((uid >= set->first) && (uid <= set->last)) : 1.3440 + ((uid >= set->last) && (uid <= set->first))) : 1.3441 + uid == set->first) break; 1.3442 + if (!set) return NIL; /* not found within sequence */ 1.3443 + } 1.3444 + } 1.3445 + 1.3446 + /* Fast data searches */ 1.3447 + /* need to fetch fast data? */ 1.3448 + if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) || 1.3449 + (!elt->year && (pgm->before || pgm->on || pgm->since || 1.3450 + pgm->older || pgm->younger)) || 1.3451 + (!elt->valid && (pgm->answered || pgm->unanswered || 1.3452 + pgm->deleted || pgm->undeleted || 1.3453 + pgm->draft || pgm->undraft || 1.3454 + pgm->flagged || pgm->unflagged || 1.3455 + pgm->recent || pgm->old || 1.3456 + pgm->seen || pgm->unseen || 1.3457 + pgm->keyword || pgm->unkeyword))) { 1.3458 + unsigned long i; 1.3459 + MESSAGECACHE *ielt; 1.3460 + for (i = elt->msgno; /* find last unloaded message in range */ 1.3461 + (i < stream->nmsgs) && (ielt = mail_elt (stream,i+1)) && 1.3462 + ((!ielt->rfc822_size && (pgm->larger || pgm->smaller)) || 1.3463 + (!ielt->year && (pgm->before || pgm->on || pgm->since || 1.3464 + pgm->older || pgm->younger)) || 1.3465 + (!ielt->valid && (pgm->answered || pgm->unanswered || 1.3466 + pgm->deleted || pgm->undeleted || 1.3467 + pgm->draft || pgm->undraft || 1.3468 + pgm->flagged || pgm->unflagged || 1.3469 + pgm->recent || pgm->old || 1.3470 + pgm->seen || pgm->unseen || 1.3471 + pgm->keyword || pgm->unkeyword))); ++i); 1.3472 + if (i == elt->msgno) sprintf (tmp,"%lu",elt->msgno); 1.3473 + else sprintf (tmp,"%lu:%lu",elt->msgno,i); 1.3474 + mail_fetch_fast (stream,tmp,NIL); 1.3475 + } 1.3476 + /* size ranges */ 1.3477 + if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) || 1.3478 + (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL; 1.3479 + /* message flags */ 1.3480 + if ((pgm->answered && !elt->answered) || 1.3481 + (pgm->unanswered && elt->answered) || 1.3482 + (pgm->deleted && !elt->deleted) || 1.3483 + (pgm->undeleted && elt->deleted) || 1.3484 + (pgm->draft && !elt->draft) || 1.3485 + (pgm->undraft && elt->draft) || 1.3486 + (pgm->flagged && !elt->flagged) || 1.3487 + (pgm->unflagged && elt->flagged) || 1.3488 + (pgm->recent && !elt->recent) || 1.3489 + (pgm->old && elt->recent) || 1.3490 + (pgm->seen && !elt->seen) || 1.3491 + (pgm->unseen && elt->seen)) return NIL; 1.3492 + /* keywords */ 1.3493 + if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) || 1.3494 + (pgm->unkeyword && !mail_search_keyword (stream,elt,pgm->unkeyword,NIL))) 1.3495 + return NIL; 1.3496 + /* internal date ranges */ 1.3497 + if (pgm->before || pgm->on || pgm->since) { 1.3498 + d = mail_shortdate (elt->year,elt->month,elt->day); 1.3499 + if (pgm->before && (d >= pgm->before)) return NIL; 1.3500 + if (pgm->on && (d != pgm->on)) return NIL; 1.3501 + if (pgm->since && (d < pgm->since)) return NIL; 1.3502 + } 1.3503 + if (pgm->older || pgm->younger) { 1.3504 + unsigned long msgd = mail_longdate (elt); 1.3505 + if (pgm->older && msgd > (now - pgm->older)) return NIL; 1.3506 + if (pgm->younger && msgd < (now - pgm->younger)) return NIL; 1.3507 + } 1.3508 + 1.3509 + /* envelope searches */ 1.3510 + if (pgm->sentbefore || pgm->senton || pgm->sentsince || 1.3511 + pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject || 1.3512 + pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to || 1.3513 + pgm->message_id || pgm->newsgroups || pgm->followup_to || 1.3514 + pgm->references) { 1.3515 + ENVELOPE *env; 1.3516 + MESSAGECACHE delt; 1.3517 + if (section) { /* use body part envelope */ 1.3518 + BODY *body = mail_body (stream,msgno,section); 1.3519 + env = (body && (body->type == TYPEMESSAGE) && body->subtype && 1.3520 + !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL; 1.3521 + } 1.3522 + else { /* use top level envelope if no section */ 1.3523 + if (pgm->header && !stream->scache && !(stream->dtb->flags & DR_LOCAL)) 1.3524 + mail_fetch_header(stream,msgno,NIL,NIL,NIL,FT_PEEK|FT_SEARCHLOOKAHEAD); 1.3525 + env = mail_fetchenvelope (stream,msgno); 1.3526 + } 1.3527 + if (!env) return NIL; /* no envelope obtained */ 1.3528 + /* sent date ranges */ 1.3529 + if ((pgm->sentbefore || pgm->senton || pgm->sentsince) && 1.3530 + (!mail_parse_date (&delt,env->date) || 1.3531 + !(d = mail_shortdate (delt.year,delt.month,delt.day)) || 1.3532 + (pgm->sentbefore && (d >= pgm->sentbefore)) || 1.3533 + (pgm->senton && (d != pgm->senton)) || 1.3534 + (pgm->sentsince && (d < pgm->sentsince)))) return NIL; 1.3535 + /* search headers */ 1.3536 + if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) || 1.3537 + (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) || 1.3538 + (pgm->from && !mail_search_addr (env->from,pgm->from)) || 1.3539 + (pgm->to && !mail_search_addr (env->to,pgm->to)) || 1.3540 + (pgm->subject && !mail_search_header_text (env->subject,pgm->subject))) 1.3541 + return NIL; 1.3542 + /* These criteria are not supported by IMAP and have to be emulated */ 1.3543 + if ((pgm->return_path && 1.3544 + !mail_search_addr (env->return_path,pgm->return_path)) || 1.3545 + (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) || 1.3546 + (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) || 1.3547 + (pgm->in_reply_to && 1.3548 + !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) || 1.3549 + (pgm->message_id && 1.3550 + !mail_search_header_text (env->message_id,pgm->message_id)) || 1.3551 + (pgm->newsgroups && 1.3552 + !mail_search_header_text (env->newsgroups,pgm->newsgroups)) || 1.3553 + (pgm->followup_to && 1.3554 + !mail_search_header_text (env->followup_to,pgm->followup_to)) || 1.3555 + (pgm->references && 1.3556 + !mail_search_header_text (env->references,pgm->references))) 1.3557 + return NIL; 1.3558 + } 1.3559 + 1.3560 + /* search header lines */ 1.3561 + for (hdr = pgm->header; hdr; hdr = hdr->next) { 1.3562 + char *t,*e,*v; 1.3563 + SIZEDTEXT s; 1.3564 + STRINGLIST sth,stc; 1.3565 + sth.next = stc.next = NIL; /* only one at a time */ 1.3566 + sth.text.data = hdr->line.data; 1.3567 + sth.text.size = hdr->line.size; 1.3568 + /* get the header text */ 1.3569 + if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size, 1.3570 + FT_INTERNAL | FT_PEEK | 1.3571 + (section ? NIL : FT_SEARCHLOOKAHEAD))) && 1.3572 + strchr (t,':')) { 1.3573 + if (hdr->text.size) { /* anything matches empty search string */ 1.3574 + /* non-empty, copy field data */ 1.3575 + s.data = (unsigned char *) fs_get (s.size + 1); 1.3576 + /* for each line */ 1.3577 + for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) { 1.3578 + default: /* non-continuation, skip leading field name */ 1.3579 + while ((t < e) && (*t++ != ':')); 1.3580 + if ((t < e) && (*t == ':')) t++; 1.3581 + case '\t': case ' ': /* copy field data */ 1.3582 + while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++; 1.3583 + *v++ = '\n'; /* tie off line */ 1.3584 + while (((*t == '\015') || (*t == '\012')) && (t < e)) t++; 1.3585 + } 1.3586 + /* calculate true size */ 1.3587 + s.size = v - (char *) s.data; 1.3588 + *v = '\0'; /* tie off results */ 1.3589 + stc.text.data = hdr->text.data; 1.3590 + stc.text.size = hdr->text.size; 1.3591 + /* search header */ 1.3592 + if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data); 1.3593 + else { /* search failed */ 1.3594 + fs_give ((void **) &s.data); 1.3595 + return NIL; 1.3596 + } 1.3597 + } 1.3598 + } 1.3599 + else return NIL; /* no matching header text */ 1.3600 + } 1.3601 + /* search strings */ 1.3602 + if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))|| 1.3603 + (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL))) 1.3604 + return NIL; 1.3605 + /* logical conditions */ 1.3606 + for (or = pgm->or; or; or = or->next) 1.3607 + if (!(mail_search_msg (stream,msgno,section,or->first) || 1.3608 + mail_search_msg (stream,msgno,section,or->second))) return NIL; 1.3609 + for (not = pgm->not; not; not = not->next) 1.3610 + if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL; 1.3611 + return T; 1.3612 +} 1.3613 + 1.3614 +/* Mail search message header null-terminated text 1.3615 + * Accepts: header text 1.3616 + * strings to search 1.3617 + * Returns: T if search found a match 1.3618 + */ 1.3619 + 1.3620 +long mail_search_header_text (char *s,STRINGLIST *st) 1.3621 +{ 1.3622 + SIZEDTEXT h; 1.3623 + /* have any text? */ 1.3624 + if (h.data = (unsigned char *) s) { 1.3625 + h.size = strlen (s); /* yes, get its size */ 1.3626 + return mail_search_header (&h,st); 1.3627 + } 1.3628 + return NIL; 1.3629 +} 1.3630 + 1.3631 + 1.3632 +/* Mail search message header 1.3633 + * Accepts: header as sized text 1.3634 + * strings to search 1.3635 + * Returns: T if search found a match 1.3636 + */ 1.3637 + 1.3638 +long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st) 1.3639 +{ 1.3640 + SIZEDTEXT h; 1.3641 + long ret = LONGT; 1.3642 + /* make UTF-8 version of header */ 1.3643 + utf8_mime2text (hdr,&h,U8T_CANONICAL); 1.3644 + while (h.size && ((h.data[h.size-1]=='\015') || (h.data[h.size-1]=='\012'))) 1.3645 + --h.size; /* slice off trailing newlines */ 1.3646 + do if (h.size ? /* search non-empty string */ 1.3647 + !ssearch (h.data,h.size,st->text.data,st->text.size) : st->text.size) 1.3648 + ret = NIL; 1.3649 + while (ret && (st = st->next)); 1.3650 + if (h.data != hdr->data) fs_give ((void **) &h.data); 1.3651 + return ret; 1.3652 +} 1.3653 + 1.3654 +/* Mail search message body 1.3655 + * Accepts: MAIL stream 1.3656 + * message number 1.3657 + * optional section specification 1.3658 + * string list 1.3659 + * flags 1.3660 + * Returns: T if search found a match 1.3661 + */ 1.3662 + 1.3663 +long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section, 1.3664 + STRINGLIST *st,long flags) 1.3665 +{ 1.3666 + BODY *body; 1.3667 + long ret = NIL; 1.3668 + STRINGLIST *s = mail_newstringlist (); 1.3669 + mailgets_t omg = mailgets; 1.3670 + if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets; 1.3671 + /* strings to search */ 1.3672 + for (stream->private.search.string = s; st;) { 1.3673 + s->text.data = st->text.data; 1.3674 + s->text.size = st->text.size; 1.3675 + if (st = st->next) s = s->next = mail_newstringlist (); 1.3676 + } 1.3677 + stream->private.search.text = NIL; 1.3678 + if (flags) { /* want header? */ 1.3679 + SIZEDTEXT s,t; 1.3680 + s.data = (unsigned char *) 1.3681 + mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK); 1.3682 + utf8_mime2text (&s,&t,U8T_CANONICAL); 1.3683 + ret = mail_search_string_work (&t,&stream->private.search.string); 1.3684 + if (t.data != s.data) fs_give ((void **) &t.data); 1.3685 + } 1.3686 + if (!ret) { /* still looking for match? */ 1.3687 + /* no section, get top-level body */ 1.3688 + if (!section) mail_fetchstructure (stream,msgno,&body); 1.3689 + /* get body of nested message */ 1.3690 + else if ((body = mail_body (stream,msgno,section)) && 1.3691 + (body->type == TYPEMULTIPART) && body->subtype && 1.3692 + !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body; 1.3693 + if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags); 1.3694 + } 1.3695 + mailgets = omg; /* restore former gets routine */ 1.3696 + /* clear searching */ 1.3697 + for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL; 1.3698 + mail_free_stringlist (&stream->private.search.string); 1.3699 + stream->private.search.text = NIL; 1.3700 + return ret; 1.3701 +} 1.3702 + 1.3703 +/* Mail search message body text parts 1.3704 + * Accepts: MAIL stream 1.3705 + * message number 1.3706 + * current body pointer 1.3707 + * hierarchical level prefix 1.3708 + * position at current hierarchical level 1.3709 + * string list 1.3710 + * flags 1.3711 + * Returns: T if search found a match 1.3712 + */ 1.3713 + 1.3714 +long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body, 1.3715 + char *prefix,unsigned long section,long flags) 1.3716 +{ 1.3717 + long ret = NIL; 1.3718 + unsigned long i; 1.3719 + char *s,*t,sect[MAILTMPLEN]; 1.3720 + SIZEDTEXT st,h; 1.3721 + PART *part; 1.3722 + PARAMETER *param; 1.3723 + if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL; 1.3724 + sprintf (sect,"%s%lu",prefix ? prefix : "",section++); 1.3725 + if (flags && prefix) { /* want to search MIME header too? */ 1.3726 + st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size, 1.3727 + FT_INTERNAL | FT_PEEK); 1.3728 + if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result; 1.3729 + else { 1.3730 + /* make UTF-8 version of header */ 1.3731 + utf8_mime2text (&st,&h,U8T_CANONICAL); 1.3732 + ret = mail_search_string_work (&h,&stream->private.search.string); 1.3733 + if (h.data != st.data) fs_give ((void **) &h.data); 1.3734 + } 1.3735 + } 1.3736 + if (!ret) switch (body->type) { 1.3737 + case TYPEMULTIPART: 1.3738 + /* extend prefix if not first time */ 1.3739 + s = prefix ? strcat (sect,".") : ""; 1.3740 + for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next) 1.3741 + ret = mail_search_body (stream,msgno,&part->body,s,i,flags); 1.3742 + break; 1.3743 + case TYPEMESSAGE: 1.3744 + if (!strcmp (body->subtype,"RFC822")) { 1.3745 + if (flags) { /* want to search nested message header? */ 1.3746 + st.data = (unsigned char *) 1.3747 + mail_fetch_header (stream,msgno,sect,NIL,&st.size, 1.3748 + FT_INTERNAL | FT_PEEK); 1.3749 + if (stream->dtb->flags & DR_LOWMEM) ret =stream->private.search.result; 1.3750 + else { 1.3751 + /* make UTF-8 version of header */ 1.3752 + utf8_mime2text (&st,&h,U8T_CANONICAL); 1.3753 + ret = mail_search_string_work (&h,&stream->private.search.string); 1.3754 + if (h.data != st.data) fs_give ((void **) &h.data); 1.3755 + } 1.3756 + } 1.3757 + if (body = body->nested.msg->body) 1.3758 + ret = (body->type == TYPEMULTIPART) ? 1.3759 + mail_search_body (stream,msgno,body,(prefix ? prefix : ""), 1.3760 + section - 1,flags) : 1.3761 + mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags); 1.3762 + break; 1.3763 + } 1.3764 + /* non-MESSAGE/RFC822 falls into text case */ 1.3765 + 1.3766 + case TYPETEXT: 1.3767 + s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK); 1.3768 + if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result; 1.3769 + else { 1.3770 + for (t = NIL,param = body->parameter; param && !t; param = param->next) 1.3771 + if (!strcmp (param->attribute,"CHARSET")) t = param->value; 1.3772 + switch (body->encoding) { /* what encoding? */ 1.3773 + case ENCBASE64: 1.3774 + if (st.data = (unsigned char *) 1.3775 + rfc822_base64 ((unsigned char *) s,i,&st.size)) { 1.3776 + ret = mail_search_string (&st,t,&stream->private.search.string); 1.3777 + fs_give ((void **) &st.data); 1.3778 + } 1.3779 + break; 1.3780 + case ENCQUOTEDPRINTABLE: 1.3781 + if (st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) { 1.3782 + ret = mail_search_string (&st,t,&stream->private.search.string); 1.3783 + fs_give ((void **) &st.data); 1.3784 + } 1.3785 + break; 1.3786 + default: 1.3787 + st.data = (unsigned char *) s; 1.3788 + st.size = i; 1.3789 + ret = mail_search_string (&st,t,&stream->private.search.string); 1.3790 + break; 1.3791 + } 1.3792 + } 1.3793 + break; 1.3794 + } 1.3795 + return ret; 1.3796 +} 1.3797 + 1.3798 +/* Mail search text 1.3799 + * Accepts: sized text to search 1.3800 + * character set of sized text 1.3801 + * string list of search keys 1.3802 + * Returns: T if search found a match 1.3803 + */ 1.3804 + 1.3805 +long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st) 1.3806 +{ 1.3807 + SIZEDTEXT u; 1.3808 + long ret; 1.3809 + STRINGLIST **sc = st; 1.3810 + /* convert to UTF-8 as best we can */ 1.3811 + if (!utf8_text (s,charset,&u,U8T_CANONICAL)) 1.3812 + utf8_text (s,NIL,&u,U8T_CANONICAL); 1.3813 + ret = mail_search_string_work (&u,st); 1.3814 + if (u.data != s->data) fs_give ((void **) &u.data); 1.3815 + return ret; 1.3816 +} 1.3817 + 1.3818 + 1.3819 +/* Mail search text worker routine 1.3820 + * Accepts: sized text to search 1.3821 + * string list of search keys 1.3822 + * Returns: T if search found a match 1.3823 + */ 1.3824 + 1.3825 +long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st) 1.3826 +{ 1.3827 + void *t; 1.3828 + STRINGLIST **sc = st; 1.3829 + while (*sc) { /* run down criteria list */ 1.3830 + if (ssearch (s->data,s->size,(*sc)->text.data,(*sc)->text.size)) { 1.3831 + t = (void *) (*sc); /* found one, need to flush this */ 1.3832 + *sc = (*sc)->next; /* remove it from the list */ 1.3833 + fs_give (&t); /* flush the buffer */ 1.3834 + } 1.3835 + else sc = &(*sc)->next; /* move to next in list */ 1.3836 + } 1.3837 + return *st ? NIL : LONGT; 1.3838 +} 1.3839 + 1.3840 + 1.3841 +/* Mail search keyword 1.3842 + * Accepts: MAIL stream 1.3843 + * elt to get flags from 1.3844 + * keyword list 1.3845 + * T for keyword search, NIL for unkeyword search 1.3846 + * Returns: T if search found a match 1.3847 + */ 1.3848 + 1.3849 +long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st, 1.3850 + long flag) 1.3851 +{ 1.3852 + int i,j; 1.3853 + unsigned long f = 0; 1.3854 + unsigned long tf; 1.3855 + do { 1.3856 + for (i = 0; (j = (i < NUSERFLAGS) && stream->user_flags[i]); ++i) 1.3857 + if (!compare_csizedtext (stream->user_flags[i],&st->text)) { 1.3858 + f |= (1 << i); 1.3859 + break; 1.3860 + } 1.3861 + if (flag && !j) return NIL; 1.3862 + } while (st = st->next); 1.3863 + tf = elt->user_flags & f; /* get set flags which match */ 1.3864 + return flag ? (f == tf) : !tf; 1.3865 +} 1.3866 + 1.3867 +/* Mail search an address list 1.3868 + * Accepts: address list 1.3869 + * string list 1.3870 + * Returns: T if search found a match 1.3871 + */ 1.3872 + 1.3873 +#define SEARCHBUFLEN (size_t) 2000 1.3874 +#define SEARCHBUFSLOP (size_t) 5 1.3875 + 1.3876 +long mail_search_addr (ADDRESS *adr,STRINGLIST *st) 1.3877 +{ 1.3878 + ADDRESS *a,tadr; 1.3879 + SIZEDTEXT txt; 1.3880 + char tmp[SENDBUFLEN + 1]; 1.3881 + size_t i = SEARCHBUFLEN; 1.3882 + size_t k; 1.3883 + long ret = NIL; 1.3884 + if (adr) { 1.3885 + txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP); 1.3886 + /* never an error or next */ 1.3887 + tadr.error = NIL,tadr.next = NIL; 1.3888 + /* write address list */ 1.3889 + for (txt.size = 0,a = adr; a; a = a->next) { 1.3890 + k = (tadr.mailbox = a->mailbox) ? 4 + 2*strlen (a->mailbox) : 3; 1.3891 + if (tadr.personal = a->personal) k += 3 + 2*strlen (a->personal); 1.3892 + if (tadr.adl = a->adl) k += 3 + 2*strlen (a->adl); 1.3893 + if (tadr.host = a->host) k += 3 + 2*strlen (a->host); 1.3894 + if (tadr.personal || tadr.adl) k += 2; 1.3895 + if (k < (SENDBUFLEN-10)) {/* ignore ridiculous addresses */ 1.3896 + tmp[0] = '\0'; 1.3897 + rfc822_write_address (tmp,&tadr); 1.3898 + /* resize buffer if necessary */ 1.3899 + if (((k = strlen (tmp)) + txt.size) > i) 1.3900 + fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN)); 1.3901 + /* add new address */ 1.3902 + memcpy (txt.data + txt.size,tmp,k); 1.3903 + txt.size += k; 1.3904 + /* another address follows */ 1.3905 + if (a->next) txt.data[txt.size++] = ','; 1.3906 + } 1.3907 + } 1.3908 + txt.data[txt.size] = '\0'; /* tie off string */ 1.3909 + ret = mail_search_header (&txt,st); 1.3910 + fs_give ((void **) &txt.data); 1.3911 + } 1.3912 + return ret; 1.3913 +} 1.3914 + 1.3915 +/* Get string for low-memory searching 1.3916 + * Accepts: readin function pointer 1.3917 + * stream to use 1.3918 + * number of bytes 1.3919 + * gets data packet 1.3920 + 1.3921 + * mail stream 1.3922 + * message number 1.3923 + * descriptor string 1.3924 + * option flags 1.3925 + * Returns: NIL, always 1.3926 + */ 1.3927 + 1.3928 +#define SEARCHSLOP 128 1.3929 + 1.3930 +char *mail_search_gets (readfn_t f,void *stream,unsigned long size, 1.3931 + GETS_DATA *md) 1.3932 +{ 1.3933 + unsigned long i; 1.3934 + char tmp[MAILTMPLEN+SEARCHSLOP+1]; 1.3935 + SIZEDTEXT st; 1.3936 + /* better not be called unless searching */ 1.3937 + if (!md->stream->private.search.string) { 1.3938 + sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]", 1.3939 + md->stream->mailbox, 1.3940 + (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what); 1.3941 + fatal (tmp); 1.3942 + } 1.3943 + /* initially no match for search */ 1.3944 + md->stream->private.search.result = NIL; 1.3945 + /* make sure buffer clear */ 1.3946 + memset (st.data = (unsigned char *) tmp,'\0', 1.3947 + (size_t) MAILTMPLEN+SEARCHSLOP+1); 1.3948 + /* read first buffer */ 1.3949 + (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp); 1.3950 + /* search for text */ 1.3951 + if (mail_search_string (&st,NIL,&md->stream->private.search.string)) 1.3952 + md->stream->private.search.result = T; 1.3953 + else if (size -= i) { /* more to do, blat slop down */ 1.3954 + memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP); 1.3955 + do { /* read subsequent buffers one at a time */ 1.3956 + (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP); 1.3957 + st.size = i + SEARCHSLOP; 1.3958 + if (mail_search_string (&st,NIL,&md->stream->private.search.string)) 1.3959 + md->stream->private.search.result = T; 1.3960 + else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP); 1.3961 + } 1.3962 + while ((size -= i) && !md->stream->private.search.result); 1.3963 + } 1.3964 + if (size) { /* toss out everything after that */ 1.3965 + do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp); 1.3966 + while (size -= i); 1.3967 + } 1.3968 + return NIL; 1.3969 +} 1.3970 + 1.3971 +/* Mail parse search criteria 1.3972 + * Accepts: criteria 1.3973 + * Returns: search program if parse successful, else NIL 1.3974 + */ 1.3975 + 1.3976 +SEARCHPGM *mail_criteria (char *criteria) 1.3977 +{ 1.3978 + SEARCHPGM *pgm = NIL; 1.3979 + char *criterion,*r,tmp[MAILTMPLEN]; 1.3980 + int f; 1.3981 + if (criteria) { /* only if criteria defined */ 1.3982 + /* make writeable copy of criteria */ 1.3983 + criteria = cpystr (criteria); 1.3984 + /* for each criterion */ 1.3985 + for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r); 1.3986 + criterion; (criterion = strtok_r (NIL," ",&r))) { 1.3987 + f = NIL; /* init then scan the criterion */ 1.3988 + switch (*ucase (criterion)) { 1.3989 + case 'A': /* possible ALL, ANSWERED */ 1.3990 + if (!strcmp (criterion+1,"LL")) f = T; 1.3991 + else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T; 1.3992 + break; 1.3993 + case 'B': /* possible BCC, BEFORE, BODY */ 1.3994 + if (!strcmp (criterion+1,"CC")) 1.3995 + f = mail_criteria_string (&pgm->bcc,&r); 1.3996 + else if (!strcmp (criterion+1,"EFORE")) 1.3997 + f = mail_criteria_date (&pgm->before,&r); 1.3998 + else if (!strcmp (criterion+1,"ODY")) 1.3999 + f = mail_criteria_string (&pgm->body,&r); 1.4000 + break; 1.4001 + case 'C': /* possible CC */ 1.4002 + if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r); 1.4003 + break; 1.4004 + case 'D': /* possible DELETED */ 1.4005 + if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T; 1.4006 + break; 1.4007 + case 'F': /* possible FLAGGED, FROM */ 1.4008 + if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T; 1.4009 + else if (!strcmp (criterion+1,"ROM")) 1.4010 + f = mail_criteria_string (&pgm->from,&r); 1.4011 + break; 1.4012 + case 'K': /* possible KEYWORD */ 1.4013 + if (!strcmp (criterion+1,"EYWORD")) 1.4014 + f = mail_criteria_string (&pgm->keyword,&r); 1.4015 + break; 1.4016 + 1.4017 + case 'N': /* possible NEW */ 1.4018 + if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T; 1.4019 + break; 1.4020 + case 'O': /* possible OLD, ON */ 1.4021 + if (!strcmp (criterion+1,"LD")) f = pgm->old = T; 1.4022 + else if (!strcmp (criterion+1,"N")) 1.4023 + f = mail_criteria_date (&pgm->on,&r); 1.4024 + break; 1.4025 + case 'R': /* possible RECENT */ 1.4026 + if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T; 1.4027 + break; 1.4028 + case 'S': /* possible SEEN, SINCE, SUBJECT */ 1.4029 + if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T; 1.4030 + else if (!strcmp (criterion+1,"INCE")) 1.4031 + f = mail_criteria_date (&pgm->since,&r); 1.4032 + else if (!strcmp (criterion+1,"UBJECT")) 1.4033 + f = mail_criteria_string (&pgm->subject,&r); 1.4034 + break; 1.4035 + case 'T': /* possible TEXT, TO */ 1.4036 + if (!strcmp (criterion+1,"EXT")) 1.4037 + f = mail_criteria_string (&pgm->text,&r); 1.4038 + else if (!strcmp (criterion+1,"O")) 1.4039 + f = mail_criteria_string (&pgm->to,&r); 1.4040 + break; 1.4041 + case 'U': /* possible UN* */ 1.4042 + if (criterion[1] == 'N') { 1.4043 + if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T; 1.4044 + else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T; 1.4045 + else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T; 1.4046 + else if (!strcmp (criterion+2,"KEYWORD")) 1.4047 + f = mail_criteria_string (&pgm->unkeyword,&r); 1.4048 + else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T; 1.4049 + } 1.4050 + break; 1.4051 + default: /* we will barf below */ 1.4052 + break; 1.4053 + } 1.4054 + if (!f) { /* if can't identify criterion */ 1.4055 + sprintf (tmp,"Unknown search criterion: %.30s",criterion); 1.4056 + MM_LOG (tmp,ERROR); 1.4057 + mail_free_searchpgm (&pgm); 1.4058 + break; 1.4059 + } 1.4060 + } 1.4061 + /* no longer need copy of criteria */ 1.4062 + fs_give ((void **) &criteria); 1.4063 + } 1.4064 + return pgm; 1.4065 +} 1.4066 + 1.4067 +/* Parse a date 1.4068 + * Accepts: pointer to date integer to return 1.4069 + * pointer to strtok state 1.4070 + * Returns: T if successful, else NIL 1.4071 + */ 1.4072 + 1.4073 +int mail_criteria_date (unsigned short *date,char **r) 1.4074 +{ 1.4075 + STRINGLIST *s = NIL; 1.4076 + MESSAGECACHE elt; 1.4077 + /* parse the date and return fn if OK */ 1.4078 + int ret = (mail_criteria_string (&s,r) && 1.4079 + mail_parse_date (&elt,(char *) s->text.data) && 1.4080 + (*date = mail_shortdate (elt.year,elt.month,elt.day))) ? 1.4081 + T : NIL; 1.4082 + if (s) mail_free_stringlist (&s); 1.4083 + return ret; 1.4084 +} 1.4085 + 1.4086 +/* Calculate shortdate from elt values 1.4087 + * Accepts: year (0 = BASEYEAR) 1.4088 + * month (1 = January) 1.4089 + * day 1.4090 + * Returns: shortdate 1.4091 + */ 1.4092 + 1.4093 +unsigned short mail_shortdate (unsigned int year,unsigned int month, 1.4094 + unsigned int day) 1.4095 +{ 1.4096 + return (year << 9) + (month << 5) + day; 1.4097 +} 1.4098 + 1.4099 +/* Parse a string 1.4100 + * Accepts: pointer to stringlist 1.4101 + * pointer to strtok state 1.4102 + * Returns: T if successful, else NIL 1.4103 + */ 1.4104 + 1.4105 +int mail_criteria_string (STRINGLIST **s,char **r) 1.4106 +{ 1.4107 + unsigned long n; 1.4108 + char e,*d,*end = " ",*c = strtok_r (NIL,"",r); 1.4109 + if (!c) return NIL; /* missing argument */ 1.4110 + switch (*c) { /* see what the argument is */ 1.4111 + case '{': /* literal string */ 1.4112 + n = strtoul (c+1,&d,10); /* get its length */ 1.4113 + if ((*d++ == '}') && (*d++ == '\015') && (*d++ == '\012') && 1.4114 + (!(*(c = d + n)) || (*c == ' '))) { 1.4115 + e = *--c; /* store old delimiter */ 1.4116 + *c = '\377'; /* make sure not a space */ 1.4117 + strtok_r (c," ",r); /* reset the strtok mechanism */ 1.4118 + *c = e; /* put character back */ 1.4119 + break; 1.4120 + } 1.4121 + case '\0': /* catch bogons */ 1.4122 + case ' ': 1.4123 + return NIL; 1.4124 + case '"': /* quoted string */ 1.4125 + if (strchr (c+1,'"')) end = "\""; 1.4126 + else return NIL; /* falls through */ 1.4127 + default: /* atomic string */ 1.4128 + if (d = strtok_r (c,end,r)) n = strlen (d); 1.4129 + else return NIL; 1.4130 + break; 1.4131 + } 1.4132 + while (*s) s = &(*s)->next; /* find tail of list */ 1.4133 + *s = mail_newstringlist (); /* make new entry */ 1.4134 + /* return the data */ 1.4135 + (*s)->text.data = (unsigned char *) cpystr (d); 1.4136 + (*s)->text.size = n; 1.4137 + return T; 1.4138 +} 1.4139 + 1.4140 +/* Mail parse set from string 1.4141 + * Accepts: string to parse 1.4142 + * pointer to updated string pointer for return 1.4143 + * Returns: set with pointer updated, or NIL if error 1.4144 + */ 1.4145 + 1.4146 +SEARCHSET *mail_parse_set (char *s,char **ret) 1.4147 +{ 1.4148 + SEARCHSET *cur; 1.4149 + SEARCHSET *set = NIL; 1.4150 + while (isdigit (*s)) { 1.4151 + if (!set) cur = set = mail_newsearchset (); 1.4152 + else cur = cur->next = mail_newsearchset (); 1.4153 + /* parse value */ 1.4154 + if (!(cur->first = strtoul (s,&s,10)) || 1.4155 + ((*s == ':') && !(isdigit (*++s) && (cur->last = strtoul (s,&s,10))))) 1.4156 + break; /* bad value or range */ 1.4157 + if (*s == ',') ++s; /* point to next value if more */ 1.4158 + else { /* end of set */ 1.4159 + *ret = s; /* set return pointer */ 1.4160 + return set; /* return set */ 1.4161 + } 1.4162 + } 1.4163 + mail_free_searchset (&set); /* failure, punt partial set */ 1.4164 + return NIL; 1.4165 +} 1.4166 + 1.4167 + 1.4168 +/* Mail append to set 1.4169 + * Accepts: head of search set or NIL to do nothing 1.4170 + * message to add 1.4171 + * Returns: tail of search set or NIL if did nothing 1.4172 + */ 1.4173 + 1.4174 +SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno) 1.4175 +{ 1.4176 + if (set) { /* find tail */ 1.4177 + while (set->next) set = set->next; 1.4178 + /* start of set if no first member */ 1.4179 + if (!set->first) set->first = msgno; 1.4180 + else if (msgno == (set->last ? set->last : set->first) + 1) 1.4181 + set->last = msgno; /* extend range if 1 past current */ 1.4182 + else (set = set->next = mail_newsearchset ())->first = msgno; 1.4183 + } 1.4184 + return set; 1.4185 +} 1.4186 + 1.4187 +/* Mail sort messages 1.4188 + * Accepts: mail stream 1.4189 + * character set 1.4190 + * search program 1.4191 + * sort program 1.4192 + * option flags 1.4193 + * Returns: vector of sorted message sequences or NIL if error 1.4194 + */ 1.4195 + 1.4196 +unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg, 1.4197 + SORTPGM *pgm,long flags) 1.4198 +{ 1.4199 + unsigned long *ret = NIL; 1.4200 + if (stream->dtb) /* do the driver's action */ 1.4201 + ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs)) 1.4202 + (stream,charset,spg,pgm,flags); 1.4203 + /* flush search/sort programs if requested */ 1.4204 + if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg); 1.4205 + if (flags & SO_FREE) mail_free_sortpgm (&pgm); 1.4206 + return ret; 1.4207 +} 1.4208 + 1.4209 +/* Mail sort messages work routine 1.4210 + * Accepts: mail stream 1.4211 + * character set 1.4212 + * search program 1.4213 + * sort program 1.4214 + * option flags 1.4215 + * Returns: vector of sorted message sequences or NIL if error 1.4216 + */ 1.4217 + 1.4218 +unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg, 1.4219 + SORTPGM *pgm,long flags) 1.4220 +{ 1.4221 + unsigned long i; 1.4222 + SORTCACHE **sc; 1.4223 + unsigned long *ret = NIL; 1.4224 + if (spg) { /* only if a search needs to be done */ 1.4225 + int silent = stream->silent; 1.4226 + stream->silent = T; /* don't pass up mm_searched() events */ 1.4227 + /* search for messages */ 1.4228 + mail_search_full (stream,charset,spg,NIL); 1.4229 + stream->silent = silent; /* restore silence state */ 1.4230 + } 1.4231 + /* initialize progress counters */ 1.4232 + pgm->nmsgs = pgm->progress.cached = 0; 1.4233 + /* pass 1: count messages to sort */ 1.4234 + for (i = 1; i <= stream->nmsgs; ++i) 1.4235 + if (mail_elt (stream,i)->searched) pgm->nmsgs++; 1.4236 + if (pgm->nmsgs) { /* pass 2: sort cache */ 1.4237 + sc = mail_sort_loadcache (stream,pgm); 1.4238 + /* pass 3: sort messages */ 1.4239 + if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags); 1.4240 + fs_give ((void **) &sc); /* don't need sort vector any more */ 1.4241 + } 1.4242 + /* empty sort results */ 1.4243 + else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0, 1.4244 + sizeof (unsigned long)); 1.4245 + /* also return via callback if requested */ 1.4246 + if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs); 1.4247 + return ret; /* return sort results */ 1.4248 +} 1.4249 + 1.4250 +/* Mail sort sortcache vector 1.4251 + * Accepts: mail stream 1.4252 + * sort program 1.4253 + * sortcache vector 1.4254 + * option flags 1.4255 + * Returns: vector of sorted message sequences or NIL if error 1.4256 + */ 1.4257 + 1.4258 +unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc, 1.4259 + long flags) 1.4260 +{ 1.4261 + unsigned long i,*ret; 1.4262 + /* pass 3: sort messages */ 1.4263 + qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare); 1.4264 + /* optional post sorting */ 1.4265 + if (pgm->postsort) (*pgm->postsort) ((void *) sc); 1.4266 + /* pass 4: return results */ 1.4267 + ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long)); 1.4268 + if (flags & SE_UID) /* UID or msgno? */ 1.4269 + for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num); 1.4270 + else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num; 1.4271 + ret[pgm->nmsgs] = 0; /* tie off message list */ 1.4272 + return ret; 1.4273 +} 1.4274 + 1.4275 +/* Mail load sortcache 1.4276 + * Accepts: mail stream, already searched 1.4277 + * sort program 1.4278 + * Returns: vector of sortcache pointers matching search 1.4279 + */ 1.4280 + 1.4281 +static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL}; 1.4282 +static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL}; 1.4283 +static STRINGLIST mailfromline = {{(unsigned char *) "from",4}, 1.4284 + &mailrnfromline}; 1.4285 +static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL}; 1.4286 +static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL}; 1.4287 +static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL}; 1.4288 + 1.4289 +SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm) 1.4290 +{ 1.4291 + char *t,*v,*x,tmp[MAILTMPLEN]; 1.4292 + SORTPGM *pg; 1.4293 + SORTCACHE *s,**sc; 1.4294 + MESSAGECACHE *elt,telt; 1.4295 + ENVELOPE *env; 1.4296 + ADDRESS *adr = NIL; 1.4297 + unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *); 1.4298 + sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i); 1.4299 + /* see what needs to be loaded */ 1.4300 + for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++) 1.4301 + if ((elt = mail_elt (stream,i))->searched) { 1.4302 + sc[pgm->progress.cached++] = 1.4303 + s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE); 1.4304 + s->pgm = pgm; /* note sort program */ 1.4305 + s->num = i; 1.4306 + /* get envelope if cached */ 1.4307 + if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL; 1.4308 + else env = elt->private.msg.env; 1.4309 + for (pg = pgm; pg; pg = pg->next) switch (pg->function) { 1.4310 + case SORTARRIVAL: /* sort by arrival date */ 1.4311 + if (!s->arrival) { 1.4312 + /* internal date unknown but can get? */ 1.4313 + if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) { 1.4314 + sprintf (tmp,"%lu",i); 1.4315 + mail_fetch_fast (stream,tmp,NIL); 1.4316 + } 1.4317 + /* wrong thing before 3-Jan-1970 */ 1.4318 + s->arrival = elt->day ? mail_longdate (elt) : 1; 1.4319 + s->dirty = T; 1.4320 + } 1.4321 + break; 1.4322 + case SORTSIZE: /* sort by message size */ 1.4323 + if (!s->size) { 1.4324 + if (!elt->rfc822_size) { 1.4325 + sprintf (tmp,"%lu",i); 1.4326 + mail_fetch_fast (stream,tmp,NIL); 1.4327 + } 1.4328 + s->size = elt->rfc822_size ? elt->rfc822_size : 1; 1.4329 + s->dirty = T; 1.4330 + } 1.4331 + break; 1.4332 + 1.4333 + case SORTDATE: /* sort by date */ 1.4334 + if (!s->date) { 1.4335 + if (env) t = env->date; 1.4336 + else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL, 1.4337 + FT_INTERNAL | FT_PEEK)) && 1.4338 + (t = strchr (t,':'))) 1.4339 + for (x = ++t; x = strpbrk (x,"\012\015"); x++) 1.4340 + switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){ 1.4341 + case ' ': /* erase continuation newlines */ 1.4342 + case '\t': 1.4343 + memmove (x,v,strlen (v)); 1.4344 + break; 1.4345 + default: /* tie off extraneous text */ 1.4346 + *x = x[1] = '\0'; 1.4347 + } 1.4348 + /* skip leading whitespace */ 1.4349 + if (t) while ((*t == ' ') || (*t == '\t')) t++; 1.4350 + /* parse date from Date: header */ 1.4351 + if (!(t && mail_parse_date (&telt,t) && 1.4352 + (s->date = mail_longdate (&telt)))) { 1.4353 + /* failed, use internal date */ 1.4354 + if (!(s->date = s->arrival)) { 1.4355 + /* internal date unknown but can get? */ 1.4356 + if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) { 1.4357 + sprintf (tmp,"%lu",i); 1.4358 + mail_fetch_fast (stream,tmp,NIL); 1.4359 + } 1.4360 + /* wrong thing before 3-Jan-1970 */ 1.4361 + s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1); 1.4362 + } 1.4363 + } 1.4364 + s->dirty = T; 1.4365 + } 1.4366 + break; 1.4367 + 1.4368 + case SORTFROM: /* sort by first from */ 1.4369 + if (!s->from) { 1.4370 + if (env) s->from = env->from && env->from->mailbox ? 1.4371 + cpystr (env->from->mailbox) : NIL; 1.4372 + else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL, 1.4373 + FT_INTERNAL | FT_PEEK)) && 1.4374 + (t = strchr (t,':'))) { 1.4375 + for (x = ++t; x = strpbrk (x,"\012\015"); x++) 1.4376 + switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){ 1.4377 + case ' ': /* erase continuation newlines */ 1.4378 + case '\t': 1.4379 + memmove (x,v,strlen (v)); 1.4380 + break; 1.4381 + case 'f': /* continuation but with extra "From:" */ 1.4382 + case 'F': 1.4383 + if (v = strchr (v,':')) { 1.4384 + memmove (x,v+1,strlen (v+1)); 1.4385 + break; 1.4386 + } 1.4387 + default: /* tie off extraneous text */ 1.4388 + *x = x[1] = '\0'; 1.4389 + } 1.4390 + if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) { 1.4391 + s->from = adr->mailbox; 1.4392 + adr->mailbox = NIL; 1.4393 + mail_free_address (&adr); 1.4394 + } 1.4395 + } 1.4396 + if (!s->from) s->from = cpystr (""); 1.4397 + s->dirty = T; 1.4398 + } 1.4399 + break; 1.4400 + 1.4401 + case SORTTO: /* sort by first to */ 1.4402 + if (!s->to) { 1.4403 + if (env) s->to = env->to && env->to->mailbox ? 1.4404 + cpystr (env->to->mailbox) : NIL; 1.4405 + else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL, 1.4406 + FT_INTERNAL | FT_PEEK)) && 1.4407 + (t = strchr (t,':'))) { 1.4408 + for (x = ++t; x = strpbrk (x,"\012\015"); x++) 1.4409 + switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){ 1.4410 + case ' ': /* erase continuation newlines */ 1.4411 + case '\t': 1.4412 + memmove (x,v,strlen (v)); 1.4413 + break; 1.4414 + case 't': /* continuation but with extra "To:" */ 1.4415 + case 'T': 1.4416 + if (v = strchr (v,':')) { 1.4417 + memmove (x,v+1,strlen (v+1)); 1.4418 + break; 1.4419 + } 1.4420 + default: /* tie off extraneous text */ 1.4421 + *x = x[1] = '\0'; 1.4422 + } 1.4423 + if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) { 1.4424 + s->to = adr->mailbox; 1.4425 + adr->mailbox = NIL; 1.4426 + mail_free_address (&adr); 1.4427 + } 1.4428 + } 1.4429 + if (!s->to) s->to = cpystr (""); 1.4430 + s->dirty = T; 1.4431 + } 1.4432 + break; 1.4433 + 1.4434 + case SORTCC: /* sort by first cc */ 1.4435 + if (!s->cc) { 1.4436 + if (env) s->cc = env->cc && env->cc->mailbox ? 1.4437 + cpystr (env->cc->mailbox) : NIL; 1.4438 + else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL, 1.4439 + FT_INTERNAL | FT_PEEK)) && 1.4440 + (t = strchr (t,':'))) { 1.4441 + for (x = ++t; x = strpbrk (x,"\012\015"); x++) 1.4442 + switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){ 1.4443 + case ' ': /* erase continuation newlines */ 1.4444 + case '\t': 1.4445 + memmove (x,v,strlen (v)); 1.4446 + break; 1.4447 + case 't': /* continuation but with extra "To:" */ 1.4448 + case 'T': 1.4449 + if (v = strchr (v,':')) { 1.4450 + memmove (x,v+1,strlen (v+1)); 1.4451 + break; 1.4452 + } 1.4453 + default: /* tie off extraneous text */ 1.4454 + *x = x[1] = '\0'; 1.4455 + } 1.4456 + if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) { 1.4457 + s->cc = adr->mailbox; 1.4458 + adr->mailbox = NIL; 1.4459 + mail_free_address (&adr); 1.4460 + } 1.4461 + } 1.4462 + if (!s->cc) s->cc = cpystr (""); 1.4463 + s->dirty = T; 1.4464 + } 1.4465 + break; 1.4466 + 1.4467 + case SORTSUBJECT: /* sort by subject */ 1.4468 + if (!s->subject) { 1.4469 + /* get subject from envelope if have one */ 1.4470 + if (env) t = env->subject ? env->subject : ""; 1.4471 + /* otherwise snarf from header text */ 1.4472 + else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline, 1.4473 + NIL,FT_INTERNAL | FT_PEEK)) && 1.4474 + (t = strchr (t,':'))) 1.4475 + for (x = ++t; x = strpbrk (x,"\012\015"); x++) 1.4476 + switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){ 1.4477 + case ' ': /* erase continuation newlines */ 1.4478 + case '\t': 1.4479 + memmove (x,v,strlen (v)); 1.4480 + break; 1.4481 + default: /* tie off extraneous text */ 1.4482 + *x = x[1] = '\0'; 1.4483 + } 1.4484 + else t = ""; /* empty subject */ 1.4485 + /* strip and cache subject */ 1.4486 + s->refwd = mail_strip_subject (t,&s->subject); 1.4487 + s->dirty = T; 1.4488 + } 1.4489 + break; 1.4490 + default: 1.4491 + fatal ("Unknown sort function"); 1.4492 + } 1.4493 + } 1.4494 + return sc; 1.4495 +} 1.4496 + 1.4497 +/* Strip subjects of extra spaces and leading and trailing cruft for sorting 1.4498 + * Accepts: unstripped subject 1.4499 + * pointer to return stripped subject, in cpystr form 1.4500 + * Returns: T if subject had a re/fwd, NIL otherwise 1.4501 + */ 1.4502 + 1.4503 +unsigned int mail_strip_subject (char *t,char **ret) 1.4504 +{ 1.4505 + SIZEDTEXT src,dst; 1.4506 + unsigned long i,slen; 1.4507 + char c,*s,*x; 1.4508 + unsigned int refwd = NIL; 1.4509 + if (src.size = strlen (t)) { /* have non-empty subject? */ 1.4510 + src.data = (unsigned char *) t; 1.4511 + /* Step 1 */ 1.4512 + /* make copy, convert MIME2 if needed */ 1.4513 + *ret = s = (utf8_mime2text (&src,&dst,U8T_CANONICAL) && 1.4514 + (src.data != dst.data)) ? (char *) dst.data : cpystr (t); 1.4515 + /* convert spaces to tab, strip extra spaces */ 1.4516 + for (x = t = s, c = 'x'; *t; t++) { 1.4517 + if (c != ' ') c = *x++ = ((*t == '\t') ? ' ' : *t); 1.4518 + else if ((*t != '\t') && (*t != ' ')) c = *x++ = *t; 1.4519 + } 1.4520 + *x = '\0'; /* tie off string */ 1.4521 + /* Step 2 */ 1.4522 + for (slen = dst.size; s; slen = strlen (s)) { 1.4523 + for (t = s + slen; t > s; ) switch (t[-1]) { 1.4524 + case ' ': case '\t': /* WSP */ 1.4525 + *--t = '\0'; /* just remove it */ 1.4526 + break; 1.4527 + case ')': /* possible "(fwd)" */ 1.4528 + if ((t >= (s + 5)) && (t[-5] == '(') && 1.4529 + ((t[-4] == 'F') || (t[-4] == 'f')) && 1.4530 + ((t[-3] == 'W') || (t[-3] == 'w')) && 1.4531 + ((t[-2] == 'D') || (t[-2] == 'd'))) { 1.4532 + *(t -= 5) = '\0'; /* remove "(fwd)" */ 1.4533 + refwd = T; /* note a re/fwd */ 1.4534 + break; 1.4535 + } 1.4536 + default: /* not a subj-trailer */ 1.4537 + t = s; 1.4538 + break; 1.4539 + } 1.4540 + /* Steps 3-5 */ 1.4541 + for (t = s; t; ) switch (*s) { 1.4542 + case ' ': case '\t': /* WSP */ 1.4543 + s = t = mail_strip_subject_wsp (s + 1); 1.4544 + break; 1.4545 + case 'r': case 'R': /* possible "re" */ 1.4546 + if (((s[1] == 'E') || (s[1] == 'e')) && 1.4547 + (t = mail_strip_subject_wsp (s + 2)) && 1.4548 + (t = mail_strip_subject_blob (t)) && (*t == ':')) { 1.4549 + s = ++t; /* found "re" */ 1.4550 + refwd = T; /* definitely a re/fwd at this point */ 1.4551 + } 1.4552 + else t = NIL; /* found subj-middle */ 1.4553 + break; 1.4554 + case 'f': case 'F': /* possible "fw" or "fwd" */ 1.4555 + if (((s[1] == 'w') || (s[1] == 'W')) && 1.4556 + (((s[2] == 'd') || (s[2] == 'D')) ? 1.4557 + (t = mail_strip_subject_wsp (s + 3)) : 1.4558 + (t = mail_strip_subject_wsp (s + 2))) && 1.4559 + (t = mail_strip_subject_blob (t)) && (*t == ':')) { 1.4560 + s = ++t; /* found "fwd" */ 1.4561 + refwd = T; /* definitely a re/fwd at this point */ 1.4562 + } 1.4563 + else t = NIL; /* found subj-middle */ 1.4564 + break; 1.4565 + case '[': /* possible subj-blob */ 1.4566 + if ((t = mail_strip_subject_blob (s)) && *t) s = t; 1.4567 + else t = NIL; /* found subj-middle */ 1.4568 + break; 1.4569 + default: 1.4570 + t = NIL; /* found subj-middle */ 1.4571 + break; 1.4572 + } 1.4573 + /* Step 6 */ 1.4574 + /* Netscape-style "[Fwd: ...]"? */ 1.4575 + if ((*s == '[') && ((s[1] == 'F') || (s[1] == 'f')) && 1.4576 + ((s[2] == 'W') || (s[2] == 'w')) && 1.4577 + ((s[3] == 'D') || (s[3] == 'd')) && (s[4] == ':') && 1.4578 + (s[i = strlen (s) - 1] == ']')) { 1.4579 + s[i] = '\0'; /* flush closing "]" */ 1.4580 + s += 5; /* and leading "[Fwd:" */ 1.4581 + refwd = T; /* definitely a re/fwd at this point */ 1.4582 + } 1.4583 + else break; /* don't need to loop back to step 2 */ 1.4584 + } 1.4585 + if (s != (t = *ret)) { /* removed leading text? */ 1.4586 + s = *ret = cpystr (s); /* yes, make a fresh return copy */ 1.4587 + fs_give ((void **) &t); /* flush old copy */ 1.4588 + } 1.4589 + } 1.4590 + else *ret = cpystr (""); /* empty subject */ 1.4591 + return refwd; /* return re/fwd state */ 1.4592 +} 1.4593 + 1.4594 +/* Strip subject wsp helper routine 1.4595 + * Accepts: text 1.4596 + * Returns: pointer to text after blob 1.4597 + */ 1.4598 + 1.4599 +char *mail_strip_subject_wsp (char *s) 1.4600 +{ 1.4601 + while ((*s == ' ') || (*s == '\t')) s++; 1.4602 + return s; 1.4603 +} 1.4604 + 1.4605 + 1.4606 +/* Strip subject blob helper routine 1.4607 + * Accepts: text 1.4608 + * Returns: pointer to text after any blob, NIL if blob-like but not blob 1.4609 + */ 1.4610 + 1.4611 +char *mail_strip_subject_blob (char *s) 1.4612 +{ 1.4613 + if (*s != '[') return s; /* not a blob, ignore */ 1.4614 + /* search for end of blob */ 1.4615 + while (*++s != ']') if ((*s == '[') || !*s) return NIL; 1.4616 + return mail_strip_subject_wsp (s + 1); 1.4617 +} 1.4618 + 1.4619 +/* Sort compare messages 1.4620 + * Accept: first message sort cache element 1.4621 + * second message sort cache element 1.4622 + * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2 1.4623 + */ 1.4624 + 1.4625 +int mail_sort_compare (const void *a1,const void *a2) 1.4626 +{ 1.4627 + int i = 0; 1.4628 + SORTCACHE *s1 = *(SORTCACHE **) a1; 1.4629 + SORTCACHE *s2 = *(SORTCACHE **) a2; 1.4630 + SORTPGM *pgm = s1->pgm; 1.4631 + if (!s1->sorted) { /* this one sorted yet? */ 1.4632 + s1->sorted = T; 1.4633 + pgm->progress.sorted++; /* another sorted message */ 1.4634 + } 1.4635 + if (!s2->sorted) { /* this one sorted yet? */ 1.4636 + s2->sorted = T; 1.4637 + pgm->progress.sorted++; /* another sorted message */ 1.4638 + } 1.4639 + do { 1.4640 + switch (pgm->function) { /* execute search program */ 1.4641 + case SORTDATE: /* sort by date */ 1.4642 + i = compare_ulong (s1->date,s2->date); 1.4643 + break; 1.4644 + case SORTARRIVAL: /* sort by arrival date */ 1.4645 + i = compare_ulong (s1->arrival,s2->arrival); 1.4646 + break; 1.4647 + case SORTSIZE: /* sort by message size */ 1.4648 + i = compare_ulong (s1->size,s2->size); 1.4649 + break; 1.4650 + case SORTFROM: /* sort by first from */ 1.4651 + i = compare_cstring (s1->from,s2->from); 1.4652 + break; 1.4653 + case SORTTO: /* sort by first to */ 1.4654 + i = compare_cstring (s1->to,s2->to); 1.4655 + break; 1.4656 + case SORTCC: /* sort by first cc */ 1.4657 + i = compare_cstring (s1->cc,s2->cc); 1.4658 + break; 1.4659 + case SORTSUBJECT: /* sort by subject */ 1.4660 + i = compare_cstring (s1->subject,s2->subject); 1.4661 + break; 1.4662 + } 1.4663 + if (pgm->reverse) i = -i; /* flip results if necessary */ 1.4664 + } 1.4665 + while (pgm = i ? NIL : pgm->next); 1.4666 + /* return result, avoid 0 if at all possible */ 1.4667 + return i ? i : compare_ulong (s1->num,s2->num); 1.4668 +} 1.4669 + 1.4670 +/* Return message date as an unsigned long seconds since time began 1.4671 + * Accepts: message cache pointer 1.4672 + * Returns: unsigned long of date 1.4673 + * 1.4674 + * This routine, like most UNIX systems, is clueless about leap seconds. 1.4675 + * Thus, it treats 23:59:60 as equivalent to 00:00:00 the next day. 1.4676 + * 1.4677 + * This routine forces any early hours on 1-Jan-1970 in oriental timezones 1.4678 + * to be 1-Jan-1970 00:00:00 UTC, so as to avoid negative longdates. 1.4679 + */ 1.4680 + 1.4681 +unsigned long mail_longdate (MESSAGECACHE *elt) 1.4682 +{ 1.4683 + unsigned long m = elt->month ? elt->month : 1; 1.4684 + unsigned long yr = elt->year + BASEYEAR; 1.4685 + /* number of days since time began */ 1.4686 + unsigned long ret = (elt->day ? (elt->day - 1) : 0) 1.4687 + + 30 * (m - 1) + ((m + (m > 8)) / 2) 1.4688 +#ifndef USEJULIANCALENDAR 1.4689 +#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */ 1.4690 + + ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100)) 1.4691 +#ifdef Y4KBUGFIX 1.4692 + - ((yr / 4000) - (BASEYEAR / 4000)) 1.4693 +#endif 1.4694 + - ((m < 3) ? 1.4695 + !(yr % 4) && ((yr % 100) || (!(yr % 400) 1.4696 +#ifdef Y4KBUGFIX 1.4697 + && (yr % 4000) 1.4698 +#endif 1.4699 + )) : 2) 1.4700 +#else /* Orthodox calendar */ 1.4701 + + ((2*(yr / 900)) - (2*(BASEYEAR / 900))) 1.4702 + + (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200)) 1.4703 + + (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600)) 1.4704 + - ((yr / 100) - (BASEYEAR / 100)) 1.4705 + - ((m < 3) ? 1.4706 + !(yr % 4) && ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600)) 1.4707 + : 2) 1.4708 +#endif 1.4709 +#endif 1.4710 + + elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4); 1.4711 + ret *= 24; ret += elt->hours; /* date value in hours */ 1.4712 + ret *= 60; ret +=elt->minutes;/* date value in minutes */ 1.4713 + yr = (elt->zhours * 60) + elt->zminutes; 1.4714 + if (elt->zoccident) ret += yr;/* occidental timezone, make UTC */ 1.4715 + else if (ret < yr) return 0; /* still 31-Dec-1969 in UTC */ 1.4716 + else ret -= yr; /* oriental timezone, make UTC */ 1.4717 + ret *= 60; ret += elt->seconds; 1.4718 + return ret; 1.4719 +} 1.4720 + 1.4721 +/* Mail thread messages 1.4722 + * Accepts: mail stream 1.4723 + * thread type 1.4724 + * character set 1.4725 + * search program 1.4726 + * option flags 1.4727 + * Returns: thread node tree or NIL if error 1.4728 + */ 1.4729 + 1.4730 +THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset, 1.4731 + SEARCHPGM *spg,long flags) 1.4732 +{ 1.4733 + THREADNODE *ret = NIL; 1.4734 + if (stream->dtb) /* must have a live driver */ 1.4735 + ret = stream->dtb->thread ? /* do driver's action if available */ 1.4736 + (*stream->dtb->thread) (stream,type,charset,spg,flags) : 1.4737 + mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs); 1.4738 + /* flush search/sort programs if requested */ 1.4739 + if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg); 1.4740 + return ret; 1.4741 +} 1.4742 + 1.4743 + 1.4744 +/* Mail thread messages 1.4745 + * Accepts: mail stream 1.4746 + * thread type 1.4747 + * character set 1.4748 + * search program 1.4749 + * option flags 1.4750 + * sorter routine 1.4751 + * Returns: thread node tree or NIL if error 1.4752 + */ 1.4753 + 1.4754 +THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset, 1.4755 + SEARCHPGM *spg,long flags,sorter_t sorter) 1.4756 +{ 1.4757 + THREADER *t; 1.4758 + for (t = &mailthreadlist; t; t = t->next) 1.4759 + if (!compare_cstring (type,t->name)) { 1.4760 + THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter); 1.4761 + if (mailthreadresults) (*mailthreadresults) (stream,ret); 1.4762 + return ret; 1.4763 + } 1.4764 + MM_LOG ("No such thread type",ERROR); 1.4765 + return NIL; 1.4766 +} 1.4767 + 1.4768 +/* Mail thread ordered subject 1.4769 + * Accepts: mail stream 1.4770 + * character set 1.4771 + * search program 1.4772 + * option flags 1.4773 + * sorter routine 1.4774 + * Returns: thread node tree 1.4775 + */ 1.4776 + 1.4777 +THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset, 1.4778 + SEARCHPGM *spg,long flags, 1.4779 + sorter_t sorter) 1.4780 +{ 1.4781 + THREADNODE *thr = NIL; 1.4782 + THREADNODE *cur,*top,**tc; 1.4783 + SORTPGM pgm,pgm2; 1.4784 + SORTCACHE *s; 1.4785 + unsigned long i,j,*lst,*ls; 1.4786 + /* sort by subject+date */ 1.4787 + memset (&pgm,0,sizeof (SORTPGM)); 1.4788 + memset (&pgm2,0,sizeof (SORTPGM)); 1.4789 + pgm.function = SORTSUBJECT; 1.4790 + pgm.next = &pgm2; 1.4791 + pgm2.function = SORTDATE; 1.4792 + if (lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))){ 1.4793 + if (*(ls = lst)) { /* create thread */ 1.4794 + /* note first subject */ 1.4795 + cur = top = thr = mail_newthreadnode 1.4796 + ((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE)); 1.4797 + /* note its number */ 1.4798 + cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst; 1.4799 + i = 1; /* number of threads */ 1.4800 + while (*ls) { /* build tree */ 1.4801 + /* subjects match? */ 1.4802 + s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE); 1.4803 + if (compare_cstring (top->sc->subject,s->subject)) { 1.4804 + i++; /* have a new thread */ 1.4805 + top = top->branch = cur = mail_newthreadnode (s); 1.4806 + } 1.4807 + /* start a child of the top */ 1.4808 + else if (cur == top) cur = cur->next = mail_newthreadnode (s); 1.4809 + /* sibling of child */ 1.4810 + else cur = cur->branch = mail_newthreadnode (s); 1.4811 + /* set to msgno or UID as needed */ 1.4812 + cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num; 1.4813 + } 1.4814 + /* make threadnode cache */ 1.4815 + tc = (THREADNODE **) fs_get (i * sizeof (THREADNODE *)); 1.4816 + /* load threadnode cache */ 1.4817 + for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur; 1.4818 + if (i != j) fatal ("Threadnode cache confusion"); 1.4819 + qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date); 1.4820 + for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1]; 1.4821 + tc[j]->branch = NIL; /* end of root */ 1.4822 + thr = tc[0]; /* head of data */ 1.4823 + fs_give ((void **) &tc); 1.4824 + } 1.4825 + fs_give ((void **) &lst); 1.4826 + } 1.4827 + return thr; 1.4828 +} 1.4829 + 1.4830 +/* Mail thread references 1.4831 + * Accepts: mail stream 1.4832 + * character set 1.4833 + * search program 1.4834 + * option flags 1.4835 + * sorter routine 1.4836 + * Returns: thread node tree 1.4837 + */ 1.4838 + 1.4839 +#define REFHASHSIZE 1009 /* arbitrary prime for hash table size */ 1.4840 + 1.4841 +/* Reference threading container, as described in Jamie Zawinski's web page 1.4842 + * (http://www.jwz.org/doc/threading.html) for this algorithm. These are 1.4843 + * stored as extended data in the hash table (called "id_table" in JWZ's 1.4844 + * document) and are maintained by the hash table routines. The hash table 1.4845 + * routines implement extended data as additional void* words at the end of 1.4846 + * each bucket, hence these strange macros instead of a struct which would 1.4847 + * have been more straightforward. 1.4848 + */ 1.4849 + 1.4850 +#define THREADLINKS 3 /* number of thread links */ 1.4851 + 1.4852 +#define CACHE(data) ((SORTCACHE *) (data)[0]) 1.4853 +#define PARENT(data) ((container_t) (data)[1]) 1.4854 +#define SETPARENT(data,value) ((container_t) (data[1] = value)) 1.4855 +#define SIBLING(data) ((container_t) (data)[2]) 1.4856 +#define SETSIBLING(data,value) ((container_t) (data[2] = value)) 1.4857 +#define CHILD(data) ((container_t) (data)[3]) 1.4858 +#define SETCHILD(data,value) ((container_t) (data[3] = value)) 1.4859 + 1.4860 +THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset, 1.4861 + SEARCHPGM *spg,long flags,sorter_t sorter) 1.4862 +{ 1.4863 + MESSAGECACHE *elt,telt; 1.4864 + ENVELOPE *env; 1.4865 + SORTCACHE *s; 1.4866 + STRINGLIST *st; 1.4867 + HASHENT *he; 1.4868 + THREADNODE **tc,*cur,*lst,*nxt,*sis,*msg; 1.4869 + container_t con,nxc,prc,sib; 1.4870 + void **sub; 1.4871 + char *t,tmp[MAILTMPLEN]; 1.4872 + unsigned long j,nmsgs; 1.4873 + unsigned long i = stream->nmsgs * sizeof (SORTCACHE *); 1.4874 + SORTCACHE **sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i); 1.4875 + HASHTAB *ht = hash_create (REFHASHSIZE); 1.4876 + THREADNODE *root = NIL; 1.4877 + if (spg) { /* only if a search needs to be done */ 1.4878 + int silent = stream->silent; 1.4879 + stream->silent = T; /* don't pass up mm_searched() events */ 1.4880 + /* search for messages */ 1.4881 + mail_search_full (stream,charset,spg,NIL); 1.4882 + stream->silent = silent; /* restore silence state */ 1.4883 + } 1.4884 + 1.4885 + /* create SORTCACHE vector of requested msgs */ 1.4886 + for (i = 1, nmsgs = 0; i <= stream->nmsgs; ++i) 1.4887 + if (mail_elt (stream,i)->searched) 1.4888 + (sc[nmsgs++] = (SORTCACHE *)(*mailcache)(stream,i,CH_SORTCACHE))->num =i; 1.4889 + /* separate pass so can do overview fetch lookahead */ 1.4890 + for (i = 0; i < nmsgs; ++i) { /* for each requested message */ 1.4891 + /* is anything missing in its SORTCACHE? */ 1.4892 + if (!((s = sc[i])->date && s->subject && s->message_id && s->references)) { 1.4893 + /* driver has an overview mechanism? */ 1.4894 + if (stream->dtb && stream->dtb->overview) { 1.4895 + /* yes, find following unloaded entries */ 1.4896 + for (j = i + 1; (j < nmsgs) && !sc[j]->references; ++j); 1.4897 + sprintf (tmp,"%lu",mail_uid (stream,s->num)); 1.4898 + if (i != --j) /* end of range different? */ 1.4899 + sprintf (tmp + strlen (tmp),":%lu",mail_uid (stream,sc[j]->num)); 1.4900 + /* load via overview mechanism */ 1.4901 + mail_fetch_overview (stream,tmp,mail_thread_loadcache); 1.4902 + } 1.4903 + /* still missing data? */ 1.4904 + if (!s->date || !s->subject || !s->message_id || !s->references) { 1.4905 + /* try to load data from envelope */ 1.4906 + if (env = mail_fetch_structure (stream,s->num,NIL,NIL)) { 1.4907 + if (!s->date && env->date && mail_parse_date (&telt,env->date)) 1.4908 + s->date = mail_longdate (&telt); 1.4909 + if (!s->subject && env->subject) 1.4910 + s->refwd = 1.4911 + mail_strip_subject (env->subject,&s->subject); 1.4912 + if (!s->message_id && env->message_id && *env->message_id) 1.4913 + s->message_id = mail_thread_parse_msgid (env->message_id,NIL); 1.4914 + if (!s->references && /* use References: or In-Reply-To: */ 1.4915 + !(s->references = 1.4916 + mail_thread_parse_references (env->references,T))) 1.4917 + s->references = mail_thread_parse_references(env->in_reply_to,NIL); 1.4918 + } 1.4919 + /* last resort */ 1.4920 + if (!s->date && !(s->date = s->arrival)) { 1.4921 + /* internal date unknown but can get? */ 1.4922 + if (!(elt = mail_elt (stream,s->num))->day && 1.4923 + !(stream->dtb->flags & DR_NOINTDATE)) { 1.4924 + sprintf (tmp,"%lu",s->num); 1.4925 + mail_fetch_fast (stream,tmp,NIL); 1.4926 + } 1.4927 + /* wrong thing before 3-Jan-1970 */ 1.4928 + s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1); 1.4929 + } 1.4930 + if (!s->subject) s->subject = cpystr (""); 1.4931 + if (!s->references) s->references = mail_newstringlist (); 1.4932 + s->dirty = T; 1.4933 + } 1.4934 + } 1.4935 + 1.4936 + /* Step 1 (preliminary) */ 1.4937 + /* generate unique string */ 1.4938 + sprintf (tmp,"%s.%lx.%lx@%s",stream->mailbox,stream->uid_validity, 1.4939 + mail_uid (stream,s->num),mylocalhost ()); 1.4940 + /* flush old unique string if not message-id */ 1.4941 + if (s->unique && (s->unique != s->message_id)) 1.4942 + fs_give ((void **) &s->unique); 1.4943 + s->unique = s->message_id ? /* don't permit Message ID duplicates */ 1.4944 + (hash_lookup (ht,s->message_id) ? cpystr (tmp) : s->message_id) : 1.4945 + (s->message_id = cpystr (tmp)); 1.4946 + /* add unique string to hash table */ 1.4947 + hash_add (ht,s->unique,s,THREADLINKS); 1.4948 + } 1.4949 + /* Step 1 */ 1.4950 + for (i = 0; i < nmsgs; ++i) { /* for each message in sortcache */ 1.4951 + /* Step 1A */ 1.4952 + if ((st = (s = sc[i])->references) && st->text.data) 1.4953 + for (con = hash_lookup_and_add (ht,(char *) st->text.data,NIL, 1.4954 + THREADLINKS); st = st->next; con = nxc) { 1.4955 + nxc = hash_lookup_and_add (ht,(char *) st->text.data,NIL,THREADLINKS); 1.4956 + /* only if no parent & won't introduce loop */ 1.4957 + if (!PARENT (nxc) && !mail_thread_check_child (con,nxc)) { 1.4958 + SETPARENT (nxc,con); /* establish parent/child link */ 1.4959 + /* other children become sibling of this one */ 1.4960 + SETSIBLING (nxc,CHILD (con)); 1.4961 + SETCHILD (con,nxc); /* set as child of parent */ 1.4962 + } 1.4963 + } 1.4964 + else con = NIL; /* else message has no ancestors */ 1.4965 + /* Step 1B */ 1.4966 + if ((prc = PARENT ((nxc = hash_lookup (ht,s->unique)))) && 1.4967 + (prc != con)) { /* break links if have a different parent */ 1.4968 + SETPARENT (nxc,NIL); /* easy if direct child */ 1.4969 + if (nxc == CHILD (prc)) SETCHILD (prc,SIBLING (nxc)); 1.4970 + else { /* otherwise hunt through sisters */ 1.4971 + for (sib = CHILD (prc); nxc != SIBLING (sib); sib = SIBLING (sib)); 1.4972 + SETSIBLING (sib,SIBLING (nxc)); 1.4973 + } 1.4974 + SETSIBLING (nxc,NIL); /* no more little sisters either */ 1.4975 + prc = NIL; /* no more parent set */ 1.4976 + } 1.4977 + /* need to set parent, and parent is good? */ 1.4978 + if (!prc && !mail_thread_check_child (con,nxc)) { 1.4979 + SETPARENT (nxc,con); /* establish parent/child link */ 1.4980 + if (con) { /* if non-root parent, set parent's child */ 1.4981 + if (CHILD (con)) { /* have a child already */ 1.4982 + /* find youngest daughter */ 1.4983 + for (con = CHILD (con); SIBLING (con); con = SIBLING (con)); 1.4984 + SETSIBLING (con,nxc); /* add new baby sister */ 1.4985 + } 1.4986 + else SETCHILD (con,nxc);/* set as only child */ 1.4987 + } 1.4988 + } 1.4989 + } 1.4990 + fs_give ((void **) &sc); /* finished with sortcache vector */ 1.4991 + 1.4992 + /* Step 2 */ 1.4993 + /* search hash table for parentless messages */ 1.4994 + for (i = 0, prc = con = NIL; i < ht->size; i++) 1.4995 + for (he = ht->table[i]; he; he = he->next) 1.4996 + if (!PARENT ((nxc = he->data))) { 1.4997 + /* sibling of previous parentless message */ 1.4998 + if (con) con = SETSIBLING (con,nxc); 1.4999 + else prc = con = nxc; /* first parentless message */ 1.5000 + } 1.5001 + /* Once the dummy containers are pruned, we no longer need the parent 1.5002 + * information, so we can convert the containers to THREADNODEs. Since 1.5003 + * we don't need the id_table any more either, we can reset the hash table 1.5004 + * and reuse it as a subject_table. Resetting the hash table will also 1.5005 + * destroy the containers. 1.5006 + */ 1.5007 + /* Step 3 */ 1.5008 + /* prune dummies, convert to threadnode */ 1.5009 + root = mail_thread_c2node (stream,mail_thread_prune_dummy (prc,NIL),flags); 1.5010 + /* Step 4 */ 1.5011 + /* make buffer for sorting */ 1.5012 + tc = (THREADNODE **) fs_get (nmsgs * sizeof (THREADNODE *)); 1.5013 + /* load threadcache and count nodes to sort */ 1.5014 + for (i = 0, cur = root; cur ; cur = cur->branch) tc[i++] = cur; 1.5015 + if (i > 1) { /* only if need to sort */ 1.5016 + qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date); 1.5017 + /* relink siblings */ 1.5018 + for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1]; 1.5019 + tc[j]->branch = NIL; /* end of root */ 1.5020 + root = tc[0]; /* establish new root */ 1.5021 + } 1.5022 + /* Step 5A */ 1.5023 + hash_reset (ht); /* discard containers, reset ht */ 1.5024 + /* Step 5B */ 1.5025 + for (cur = root; cur; cur = cur->branch) 1.5026 + if ((t = (nxt = (cur->sc ? cur : cur->next))->sc->subject) && *t) { 1.5027 + /* add new subject to hash table */ 1.5028 + if (!(sub = hash_lookup (ht,t))) hash_add (ht,t,cur,0); 1.5029 + /* if one in table not dummy and */ 1.5030 + else if ((s = (lst = (THREADNODE *) sub[0])->sc) && 1.5031 + /* current dummy, or not re/fwd and table is */ 1.5032 + (!cur->sc || (!nxt->sc->refwd && s->refwd))) 1.5033 + sub[0] = (void *) cur; /* replace with this message */ 1.5034 + } 1.5035 + 1.5036 + /* Step 5C */ 1.5037 + for (cur = root, sis = NIL; cur; cur = msg) { 1.5038 + /* do nothing if current message or no sub */ 1.5039 + if (!(t = (cur->sc ? cur : cur->next)->sc->subject) || !*t || 1.5040 + ((lst = (THREADNODE *) (sub = hash_lookup (ht,t))[0]) == cur)) 1.5041 + msg = (sis = cur)->branch; 1.5042 + else if (!lst->sc) { /* is message in the table a dummy? */ 1.5043 + /* find youngest daughter of msg in table */ 1.5044 + for (msg = lst->next; msg->branch; msg = msg->branch); 1.5045 + if (!cur->sc) { /* current message a dummy? */ 1.5046 + msg->branch = cur->next;/* current's daughter now dummy's youngest */ 1.5047 + msg = cur->branch; /* continue scan at younger sister */ 1.5048 + /* now delete this node */ 1.5049 + cur->branch = cur->next = NIL; 1.5050 + mail_free_threadnode (&cur); 1.5051 + } 1.5052 + else { /* current message not a dummy */ 1.5053 + msg->branch = cur; /* append as youngest daughter */ 1.5054 + msg = cur->branch; /* continue scan at younger sister */ 1.5055 + cur->branch = NIL; /* lose our younger sisters */ 1.5056 + } 1.5057 + } 1.5058 + else { /* no dummies, is current re/fwd, table not? */ 1.5059 + if (cur->sc->refwd && !lst->sc->refwd) { 1.5060 + if (lst->next) { /* find youngest daughter of msg in table */ 1.5061 + for (msg = lst->next; msg->branch; msg = msg->branch); 1.5062 + msg->branch = cur; /* append as youngest daughter */ 1.5063 + } 1.5064 + else lst->next = cur; /* no children, so make the eldest daughter */ 1.5065 + } 1.5066 + 1.5067 + else { /* no re/fwd, create a new dummy */ 1.5068 + msg = mail_newthreadnode (NIL); 1.5069 + if (lst == root) { /* msg in table is root? */ 1.5070 + root = lst->branch; /* younger sister becomes new root */ 1.5071 + /* no longer older sister either */ 1.5072 + if (lst == sis) sis = NIL; 1.5073 + } 1.5074 + else { /* find older sister of msg in table */ 1.5075 + for (nxt = root; lst != nxt->branch; nxt = nxt->branch); 1.5076 + /* remove from older sister */ 1.5077 + nxt->branch = lst->branch; 1.5078 + } 1.5079 + msg->next = lst; /* msg in table becomes child */ 1.5080 + lst->branch = cur; /* current now little sister of msg in table */ 1.5081 + if (sis) { /* have an elder sister? */ 1.5082 + if (sis == lst) /* rescan if lost her */ 1.5083 + for (sis = root; cur != sis->branch; sis = sis->branch); 1.5084 + sis->branch = msg; /* make dummy younger sister of big sister */ 1.5085 + } 1.5086 + else root = msg; /* otherwise this is the new root */ 1.5087 + sub[0] = sis = msg; /* set new msg in table and new big sister */ 1.5088 + } 1.5089 + msg = cur->branch; /* continue scan at younger sister */ 1.5090 + cur->branch = NIL; /* lose our younger sisters */ 1.5091 + } 1.5092 + if (sis) sis->branch = msg; /* older sister gets this as younger sister */ 1.5093 + else root = msg; /* otherwise this is the new root */ 1.5094 + } 1.5095 + hash_destroy (&ht); /* finished with hash table */ 1.5096 + /* Step 6 */ 1.5097 + /* sort threads */ 1.5098 + root = mail_thread_sort (root,tc); 1.5099 + fs_give ((void **) &tc); /* finished with sort buffer */ 1.5100 + return root; /* return sorted list */ 1.5101 +} 1.5102 + 1.5103 +/* Fetch overview callback to load sortcache for threading 1.5104 + * Accepts: MAIL stream 1.5105 + * UID of this message 1.5106 + * overview of this message 1.5107 + * msgno of this message 1.5108 + */ 1.5109 + 1.5110 +void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov, 1.5111 + unsigned long msgno) 1.5112 +{ 1.5113 + if (msgno && ov) { /* just in case */ 1.5114 + MESSAGECACHE telt; 1.5115 + SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE); 1.5116 + if (!s->subject && ov->subject) { 1.5117 + s->refwd = mail_strip_subject (ov->subject,&s->subject); 1.5118 + s->dirty = T; 1.5119 + } 1.5120 + if (!s->from && ov->from && ov->from->mailbox) { 1.5121 + s->from = cpystr (ov->from->mailbox); 1.5122 + s->dirty = T; 1.5123 + } 1.5124 + if (!s->date && ov->date && mail_parse_date (&telt,ov->date)) { 1.5125 + s->date = mail_longdate (&telt); 1.5126 + s->dirty = T; 1.5127 + } 1.5128 + if (!s->message_id && ov->message_id) { 1.5129 + s->message_id = mail_thread_parse_msgid (ov->message_id,NIL); 1.5130 + s->dirty = T; 1.5131 + } 1.5132 + if (!s->references && 1.5133 + !(s->references = mail_thread_parse_references (ov->references,T))) { 1.5134 + /* don't do In-Reply-To with NNTP mailboxes */ 1.5135 + s->references = mail_newstringlist (); 1.5136 + s->dirty = T; 1.5137 + } 1.5138 + if (!s->size && ov->optional.octets) { 1.5139 + s->size = ov->optional.octets; 1.5140 + s->dirty = T; 1.5141 + } 1.5142 + } 1.5143 +} 1.5144 + 1.5145 +/* Thread parse Message ID 1.5146 + * Accepts: pointer to purported Message ID 1.5147 + * pointer to return pointer 1.5148 + * Returns: Message ID or NIL, return pointer updated 1.5149 + */ 1.5150 + 1.5151 +char *mail_thread_parse_msgid (char *s,char **ss) 1.5152 +{ 1.5153 + char *ret = NIL; 1.5154 + char *t = NIL; 1.5155 + ADDRESS *adr; 1.5156 + if (s) { /* only for non-NIL strings */ 1.5157 + rfc822_skipws (&s); /* skip whitespace */ 1.5158 + /* ignore phrases */ 1.5159 + if (((*s == '<') || (s = rfc822_parse_phrase (s))) && 1.5160 + (adr = rfc822_parse_routeaddr (s,&t,BADHOST))) { 1.5161 + /* make return msgid */ 1.5162 + if (adr->mailbox && adr->host) 1.5163 + sprintf (ret = (char *) fs_get (strlen (adr->mailbox) + 1.5164 + strlen (adr->host) + 2),"%s@%s", 1.5165 + adr->mailbox,adr->host); 1.5166 + mail_free_address (&adr); /* don't need temporary address */ 1.5167 + } 1.5168 + } 1.5169 + if (ss) *ss = t; /* update return pointer */ 1.5170 + return ret; 1.5171 +} 1.5172 + 1.5173 + 1.5174 +/* Thread parse references 1.5175 + * Accepts: pointer to purported references 1.5176 + * parse multiple references flag 1.5177 + * Returns: references or NIL 1.5178 + */ 1.5179 + 1.5180 +STRINGLIST *mail_thread_parse_references (char *s,long flag) 1.5181 +{ 1.5182 + char *t; 1.5183 + STRINGLIST *ret = NIL; 1.5184 + STRINGLIST *cur; 1.5185 + /* found first reference? */ 1.5186 + if (t = mail_thread_parse_msgid (s,&s)) { 1.5187 + (ret = mail_newstringlist ())->text.data = (unsigned char *) t; 1.5188 + ret->text.size = strlen (t); 1.5189 + if (flag) /* parse subsequent references */ 1.5190 + for (cur = ret; t = mail_thread_parse_msgid (s,&s); cur = cur->next) { 1.5191 + (cur->next = mail_newstringlist ())->text.data = (unsigned char *) t; 1.5192 + cur->next->text.size = strlen (t); 1.5193 + } 1.5194 + } 1.5195 + return ret; 1.5196 +} 1.5197 + 1.5198 +/* Prune dummy messages 1.5199 + * Accepts: candidate container to prune 1.5200 + * older sibling of container, if any 1.5201 + * Returns: container in this position, possibly pruned 1.5202 + * All children and younger siblings are also pruned 1.5203 + */ 1.5204 + 1.5205 +container_t mail_thread_prune_dummy (container_t msg,container_t ane) 1.5206 +{ 1.5207 + /* prune container and children */ 1.5208 + container_t ret = msg ? mail_thread_prune_dummy_work (msg,ane) : NIL; 1.5209 + /* prune all younger sisters */ 1.5210 + if (ret) for (ane = ret; ane && (msg = SIBLING (ane)); ane = msg) 1.5211 + msg = mail_thread_prune_dummy_work (msg,ane); 1.5212 + return ret; 1.5213 +} 1.5214 + 1.5215 + 1.5216 +/* Prune dummy messages worker routine 1.5217 + * Accepts: candidate container to prune 1.5218 + * older sibling of container, if any 1.5219 + * Returns: container in this position, possibly pruned 1.5220 + * All children are also pruned 1.5221 + */ 1.5222 + 1.5223 +container_t mail_thread_prune_dummy_work (container_t msg,container_t ane) 1.5224 +{ 1.5225 + container_t cur; 1.5226 + /* get children, if any */ 1.5227 + container_t nxt = mail_thread_prune_dummy (CHILD (msg),NIL); 1.5228 + /* just update children if container has msg */ 1.5229 + if (CACHE (msg)) SETCHILD (msg,nxt); 1.5230 + else if (!nxt) { /* delete dummy with no children */ 1.5231 + nxt = SIBLING (msg); /* get younger sister */ 1.5232 + if (ane) SETSIBLING (ane,nxt); 1.5233 + /* prune younger sister if exists */ 1.5234 + msg = nxt ? mail_thread_prune_dummy_work (nxt,ane) : NIL; 1.5235 + } 1.5236 + /* not if parent root & multiple children */ 1.5237 + else if ((cur = PARENT (msg)) || !SIBLING (nxt)) { 1.5238 + /* OK to promote, try younger sister of aunt */ 1.5239 + if (ane) SETSIBLING (ane,nxt); 1.5240 + /* otherwise promote to child of grandmother */ 1.5241 + else if (cur) SETCHILD (cur,nxt); 1.5242 + SETPARENT (nxt,cur); /* set parent as well */ 1.5243 + /* look for end of siblings in new container */ 1.5244 + for (cur = nxt; SIBLING (cur); cur = SIBLING (cur)); 1.5245 + /* reattach deleted container's siblings */ 1.5246 + SETSIBLING (cur,SIBLING (msg)); 1.5247 + /* prune and return new container */ 1.5248 + msg = mail_thread_prune_dummy_work (nxt,ane); 1.5249 + } 1.5250 + else SETCHILD (msg,nxt); /* in case child pruned */ 1.5251 + return msg; /* return this message */ 1.5252 +} 1.5253 + 1.5254 +/* Test that purported mother is not a child of purported daughter 1.5255 + * Accepts: mother 1.5256 + * purported daugher 1.5257 + * Returns: T if circular parentage exists, else NIL 1.5258 + */ 1.5259 + 1.5260 +long mail_thread_check_child (container_t mother,container_t daughter) 1.5261 +{ 1.5262 + if (mother) { /* only if mother non-NIL */ 1.5263 + if (mother == daughter) return T; 1.5264 + for (daughter = CHILD (daughter); daughter; daughter = SIBLING (daughter)) 1.5265 + if (mail_thread_check_child (mother,daughter)) return T; 1.5266 + } 1.5267 + return NIL; 1.5268 +} 1.5269 + 1.5270 + 1.5271 +/* Generate threadnodes from containers 1.5272 + * Accepts: Mail stream 1.5273 + * container 1.5274 + * flags 1.5275 + * Return: threadnode list 1.5276 + */ 1.5277 + 1.5278 +THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags) 1.5279 +{ 1.5280 + THREADNODE *ret,*cur; 1.5281 + SORTCACHE *s; 1.5282 + container_t nxt; 1.5283 + /* for each container */ 1.5284 + for (ret = cur = NIL; con; con = SIBLING (con)) { 1.5285 + s = CACHE (con); /* yes, get its sortcache */ 1.5286 + /* create node for it */ 1.5287 + if (ret) cur = cur->branch = mail_newthreadnode (s); 1.5288 + else ret = cur = mail_newthreadnode (s); 1.5289 + /* attach sequence or UID for non-dummy */ 1.5290 + if (s) cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num; 1.5291 + /* attach the children */ 1.5292 + if (nxt = CHILD (con)) cur->next = mail_thread_c2node (stream,nxt,flags); 1.5293 + } 1.5294 + return ret; 1.5295 +} 1.5296 + 1.5297 +/* Sort thread tree by date 1.5298 + * Accepts: thread tree to sort 1.5299 + * qsort vector to sort 1.5300 + * Returns: sorted thread tree 1.5301 + */ 1.5302 + 1.5303 +THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc) 1.5304 +{ 1.5305 + unsigned long i,j; 1.5306 + THREADNODE *cur; 1.5307 + /* sort children of each thread */ 1.5308 + for (cur = thr; cur; cur = cur->branch) 1.5309 + if (cur->next) cur->next = mail_thread_sort (cur->next,tc); 1.5310 + /* Must do this in a separate pass since recursive call will clobber tc */ 1.5311 + /* load threadcache and count nodes to sort */ 1.5312 + for (i = 0, cur = thr; cur; cur = cur->branch) tc[i++] = cur; 1.5313 + if (i > 1) { /* only if need to sort */ 1.5314 + qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date); 1.5315 + /* relink root siblings */ 1.5316 + for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1]; 1.5317 + tc[j]->branch = NIL; /* end of root */ 1.5318 + } 1.5319 + return i ? tc[0] : NIL; /* return new head of list */ 1.5320 +} 1.5321 + 1.5322 + 1.5323 +/* Thread compare date 1.5324 + * Accept: first message sort cache element 1.5325 + * second message sort cache element 1.5326 + * Returns: -1 if a1 < a2, 1 if a1 > a2 1.5327 + * 1.5328 + * This assumes that a sort cache element is either a message (with a 1.5329 + * sortcache entry) or a dummy with a message (with sortcache entry) child. 1.5330 + * This is true of both the ORDEREDSUBJECT (no dummies) and REFERENCES 1.5331 + * (dummies only at top-level, and with non-dummy children). 1.5332 + * 1.5333 + * If a new algorithm allows a dummy parent to have a dummy child, this 1.5334 + * routine must be changed if it is to be used by that algorithm. 1.5335 + * 1.5336 + * Messages with bogus dates are always sorted at the top. 1.5337 + */ 1.5338 + 1.5339 +int mail_thread_compare_date (const void *a1,const void *a2) 1.5340 +{ 1.5341 + THREADNODE *t1 = *(THREADNODE **) a1; 1.5342 + THREADNODE *t2 = *(THREADNODE **) a2; 1.5343 + SORTCACHE *s1 = t1->sc ? t1->sc : t1->next->sc; 1.5344 + SORTCACHE *s2 = t2->sc ? t2->sc : t2->next->sc; 1.5345 + int ret = compare_ulong (s1->date,s2->date); 1.5346 + /* use number as final tie-breaker */ 1.5347 + return ret ? ret : compare_ulong (s1->num,s2->num); 1.5348 +} 1.5349 + 1.5350 +/* Mail parse sequence 1.5351 + * Accepts: mail stream 1.5352 + * sequence to parse 1.5353 + * Returns: T if parse successful, else NIL 1.5354 + */ 1.5355 + 1.5356 +long mail_sequence (MAILSTREAM *stream,unsigned char *sequence) 1.5357 +{ 1.5358 + unsigned long i,j,x; 1.5359 + for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL; 1.5360 + while (sequence && *sequence){/* while there is something to parse */ 1.5361 + if (*sequence == '*') { /* maximum message */ 1.5362 + if (stream->nmsgs) i = stream->nmsgs; 1.5363 + else { 1.5364 + MM_LOG ("No messages, so no maximum message number",ERROR); 1.5365 + return NIL; 1.5366 + } 1.5367 + sequence++; /* skip past * */ 1.5368 + } 1.5369 + /* parse and validate message number */ 1.5370 + else if (!isdigit (*sequence)) { 1.5371 + MM_LOG ("Syntax error in sequence",ERROR); 1.5372 + return NIL; 1.5373 + } 1.5374 + else if (!(i = strtoul (sequence,(char **) &sequence,10)) || 1.5375 + (i > stream->nmsgs)) { 1.5376 + MM_LOG ("Sequence out of range",ERROR); 1.5377 + return NIL; 1.5378 + } 1.5379 + switch (*sequence) { /* see what the delimiter is */ 1.5380 + case ':': /* sequence range */ 1.5381 + if (*++sequence == '*') { /* maximum message */ 1.5382 + if (stream->nmsgs) j = stream->nmsgs; 1.5383 + else { 1.5384 + MM_LOG ("No messages, so no maximum message number",ERROR); 1.5385 + return NIL; 1.5386 + } 1.5387 + sequence++; /* skip past * */ 1.5388 + } 1.5389 + /* parse end of range */ 1.5390 + else if (!(j = strtoul (sequence,(char **) &sequence,10)) || 1.5391 + (j > stream->nmsgs)) { 1.5392 + MM_LOG ("Sequence range invalid",ERROR); 1.5393 + return NIL; 1.5394 + } 1.5395 + if (*sequence && *sequence++ != ',') { 1.5396 + MM_LOG ("Sequence range syntax error",ERROR); 1.5397 + return NIL; 1.5398 + } 1.5399 + if (i > j) { /* swap the range if backwards */ 1.5400 + x = i; i = j; j = x; 1.5401 + } 1.5402 + /* mark each item in the sequence */ 1.5403 + while (i <= j) mail_elt (stream,j--)->sequence = T; 1.5404 + break; 1.5405 + case ',': /* single message */ 1.5406 + ++sequence; /* skip the delimiter, fall into end case */ 1.5407 + case '\0': /* end of sequence, mark this message */ 1.5408 + mail_elt (stream,i)->sequence = T; 1.5409 + break; 1.5410 + default: /* anything else is a syntax error! */ 1.5411 + MM_LOG ("Sequence syntax error",ERROR); 1.5412 + return NIL; 1.5413 + } 1.5414 + } 1.5415 + return T; /* successfully parsed sequence */ 1.5416 +} 1.5417 + 1.5418 +/* Parse flag list 1.5419 + * Accepts: MAIL stream 1.5420 + * flag list as a character string 1.5421 + * pointer to user flags to return 1.5422 + * Returns: system flags 1.5423 + */ 1.5424 + 1.5425 +long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf) 1.5426 +{ 1.5427 + char *t,*n,*s,tmp[MAILTMPLEN],msg[MAILTMPLEN]; 1.5428 + short f = 0; 1.5429 + long i,j; 1.5430 + *uf = 0; /* initially no user flags */ 1.5431 + if (flag && *flag) { /* no-op if no flag string */ 1.5432 + /* check if a list and make sure valid */ 1.5433 + if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) || 1.5434 + (strlen (flag) >= MAILTMPLEN)) { 1.5435 + MM_LOG ("Bad flag list",ERROR); 1.5436 + return NIL; 1.5437 + } 1.5438 + /* copy the flag string w/o list construct */ 1.5439 + strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i))); 1.5440 + tmp[j] = '\0'; 1.5441 + while ((t = n) && *t) { /* parse the flags */ 1.5442 + /* find end of flag */ 1.5443 + if (n = strchr (t,' ')) *n++ = '\0'; 1.5444 + if (*t == '\\') { /* system flag? */ 1.5445 + if (!compare_cstring (t+1,"SEEN")) f |= fSEEN; 1.5446 + else if (!compare_cstring (t+1,"DELETED")) f |= fDELETED; 1.5447 + else if (!compare_cstring (t+1,"FLAGGED")) f |= fFLAGGED; 1.5448 + else if (!compare_cstring (t+1,"ANSWERED")) f |= fANSWERED; 1.5449 + else if (!compare_cstring (t+1,"DRAFT")) f |= fDRAFT; 1.5450 + else { 1.5451 + sprintf (msg,"Unsupported system flag: %.80s",t); 1.5452 + MM_LOG (msg,WARN); 1.5453 + } 1.5454 + } 1.5455 + 1.5456 + else { /* keyword flag */ 1.5457 + for (i = j = 0; /* user flag, search through table */ 1.5458 + !i && (j < NUSERFLAGS) && (s = stream->user_flags[j]); ++j) 1.5459 + if (!compare_cstring (t,s)) *uf |= i = 1 << j; 1.5460 + if (!i) { /* flag not found, can it be created? */ 1.5461 + if (stream->kwd_create && (j < NUSERFLAGS) && *t && 1.5462 + (strlen (t) <= MAXUSERFLAG)) { 1.5463 + for (s = t; t && *s; s++) switch (*s) { 1.5464 + default: /* all other characters */ 1.5465 + /* SPACE, CTL, or not CHAR */ 1.5466 + if ((*s > ' ') && (*s < 0x7f)) break; 1.5467 + case '*': case '%': /* list_wildcards */ 1.5468 + case '"': case '\\':/* quoted-specials */ 1.5469 + /* atom_specials */ 1.5470 + case '(': case ')': case '{': 1.5471 + case ']': /* resp-specials */ 1.5472 + sprintf (msg,"Invalid flag: %.80s",t); 1.5473 + MM_LOG (msg,WARN); 1.5474 + t = NIL; 1.5475 + } 1.5476 + if (t) { /* only if valid */ 1.5477 + *uf |= 1 << j; /* set the bit */ 1.5478 + stream->user_flags[j] = cpystr (t); 1.5479 + /* if out of user flags */ 1.5480 + if (j == NUSERFLAGS - 1) stream->kwd_create = NIL; 1.5481 + } 1.5482 + } 1.5483 + else { 1.5484 + if (*t) sprintf (msg,"Unknown flag: %.80s",t); 1.5485 + else strcpy (msg,"Empty flag invalid"); 1.5486 + MM_LOG (msg,WARN); 1.5487 + } 1.5488 + } 1.5489 + } 1.5490 + } 1.5491 + } 1.5492 + return f; 1.5493 +} 1.5494 + 1.5495 +/* Mail check network stream for usability with new name 1.5496 + * Accepts: MAIL stream 1.5497 + * candidate new name 1.5498 + * Returns: T if stream can be used, NIL otherwise 1.5499 + */ 1.5500 + 1.5501 +long mail_usable_network_stream (MAILSTREAM *stream,char *name) 1.5502 +{ 1.5503 + NETMBX smb,nmb,omb; 1.5504 + return (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) && 1.5505 + mail_valid_net_parse (name,&nmb) && 1.5506 + mail_valid_net_parse (stream->mailbox,&smb) && 1.5507 + mail_valid_net_parse (stream->original_mailbox,&omb) && 1.5508 + ((!compare_cstring (smb.host, 1.5509 + trustdns ? tcp_canonical (nmb.host) : nmb.host)&& 1.5510 + !strcmp (smb.service,nmb.service) && 1.5511 + (!nmb.port || (smb.port == nmb.port)) && 1.5512 + (nmb.anoflag == stream->anonymous) && 1.5513 + (!nmb.user[0] || !strcmp (smb.user,nmb.user))) || 1.5514 + (!compare_cstring (omb.host,nmb.host) && 1.5515 + !strcmp (omb.service,nmb.service) && 1.5516 + (!nmb.port || (omb.port == nmb.port)) && 1.5517 + (nmb.anoflag == stream->anonymous) && 1.5518 + (!nmb.user[0] || !strcmp (omb.user,nmb.user))))) ? LONGT : NIL; 1.5519 +} 1.5520 + 1.5521 +/* Mail data structure instantiation routines */ 1.5522 + 1.5523 + 1.5524 +/* Mail instantiate cache elt 1.5525 + * Accepts: initial message number 1.5526 + * Returns: new cache elt 1.5527 + */ 1.5528 + 1.5529 +MESSAGECACHE *mail_new_cache_elt (unsigned long msgno) 1.5530 +{ 1.5531 + MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)), 1.5532 + 0,sizeof (MESSAGECACHE)); 1.5533 + elt->lockcount = 1; /* initially only cache references it */ 1.5534 + elt->msgno = msgno; /* message number */ 1.5535 + return elt; 1.5536 +} 1.5537 + 1.5538 + 1.5539 +/* Mail instantiate envelope 1.5540 + * Returns: new envelope 1.5541 + */ 1.5542 + 1.5543 +ENVELOPE *mail_newenvelope (void) 1.5544 +{ 1.5545 + return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE)); 1.5546 +} 1.5547 + 1.5548 + 1.5549 +/* Mail instantiate address 1.5550 + * Returns: new address 1.5551 + */ 1.5552 + 1.5553 +ADDRESS *mail_newaddr (void) 1.5554 +{ 1.5555 + return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS)); 1.5556 +} 1.5557 + 1.5558 +/* Mail instantiate body 1.5559 + * Returns: new body 1.5560 + */ 1.5561 + 1.5562 +BODY *mail_newbody (void) 1.5563 +{ 1.5564 + return mail_initbody ((BODY *) fs_get (sizeof (BODY))); 1.5565 +} 1.5566 + 1.5567 + 1.5568 +/* Mail initialize body 1.5569 + * Accepts: body 1.5570 + * Returns: body 1.5571 + */ 1.5572 + 1.5573 +BODY *mail_initbody (BODY *body) 1.5574 +{ 1.5575 + memset ((void *) body,0,sizeof (BODY)); 1.5576 + body->type = TYPETEXT; /* content type */ 1.5577 + body->encoding = ENC7BIT; /* content encoding */ 1.5578 + return body; 1.5579 +} 1.5580 + 1.5581 + 1.5582 +/* Mail instantiate body parameter 1.5583 + * Returns: new body part 1.5584 + */ 1.5585 + 1.5586 +PARAMETER *mail_newbody_parameter (void) 1.5587 +{ 1.5588 + return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER)); 1.5589 +} 1.5590 + 1.5591 + 1.5592 +/* Mail instantiate body part 1.5593 + * Returns: new body part 1.5594 + */ 1.5595 + 1.5596 +PART *mail_newbody_part (void) 1.5597 +{ 1.5598 + PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART)); 1.5599 + mail_initbody (&part->body); /* initialize the body */ 1.5600 + return part; 1.5601 +} 1.5602 + 1.5603 + 1.5604 +/* Mail instantiate body message part 1.5605 + * Returns: new body message part 1.5606 + */ 1.5607 + 1.5608 +MESSAGE *mail_newmsg (void) 1.5609 +{ 1.5610 + return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE)); 1.5611 +} 1.5612 + 1.5613 +/* Mail instantiate string list 1.5614 + * Returns: new string list 1.5615 + */ 1.5616 + 1.5617 +STRINGLIST *mail_newstringlist (void) 1.5618 +{ 1.5619 + return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0, 1.5620 + sizeof (STRINGLIST)); 1.5621 +} 1.5622 + 1.5623 + 1.5624 +/* Mail instantiate new search program 1.5625 + * Returns: new search program 1.5626 + */ 1.5627 + 1.5628 +SEARCHPGM *mail_newsearchpgm (void) 1.5629 +{ 1.5630 + return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM)); 1.5631 +} 1.5632 + 1.5633 + 1.5634 +/* Mail instantiate new search program 1.5635 + * Accepts: header line name 1.5636 + * Returns: new search program 1.5637 + */ 1.5638 + 1.5639 +SEARCHHEADER *mail_newsearchheader (char *line,char *text) 1.5640 +{ 1.5641 + SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)), 1.5642 + 0,sizeof (SEARCHHEADER)); 1.5643 + hdr->line.size = strlen ((char *) (hdr->line.data = 1.5644 + (unsigned char *) cpystr (line))); 1.5645 + hdr->text.size = strlen ((char *) (hdr->text.data = 1.5646 + (unsigned char *) cpystr (text))); 1.5647 + return hdr; 1.5648 +} 1.5649 + 1.5650 + 1.5651 +/* Mail instantiate new search set 1.5652 + * Returns: new search set 1.5653 + */ 1.5654 + 1.5655 +SEARCHSET *mail_newsearchset (void) 1.5656 +{ 1.5657 + return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET)); 1.5658 +} 1.5659 + 1.5660 + 1.5661 +/* Mail instantiate new search or 1.5662 + * Returns: new search or 1.5663 + */ 1.5664 + 1.5665 +SEARCHOR *mail_newsearchor (void) 1.5666 +{ 1.5667 + SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0, 1.5668 + sizeof (SEARCHOR)); 1.5669 + or->first = mail_newsearchpgm (); 1.5670 + or->second = mail_newsearchpgm (); 1.5671 + return or; 1.5672 +} 1.5673 + 1.5674 +/* Mail instantiate new searchpgmlist 1.5675 + * Returns: new searchpgmlist 1.5676 + */ 1.5677 + 1.5678 +SEARCHPGMLIST *mail_newsearchpgmlist (void) 1.5679 +{ 1.5680 + SEARCHPGMLIST *pgl = (SEARCHPGMLIST *) 1.5681 + memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST)); 1.5682 + pgl->pgm = mail_newsearchpgm (); 1.5683 + return pgl; 1.5684 +} 1.5685 + 1.5686 + 1.5687 +/* Mail instantiate new sortpgm 1.5688 + * Returns: new sortpgm 1.5689 + */ 1.5690 + 1.5691 +SORTPGM *mail_newsortpgm (void) 1.5692 +{ 1.5693 + return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM)); 1.5694 +} 1.5695 + 1.5696 + 1.5697 +/* Mail instantiate new threadnode 1.5698 + * Accepts: sort cache for thread node 1.5699 + * Returns: new threadnode 1.5700 + */ 1.5701 + 1.5702 +THREADNODE *mail_newthreadnode (SORTCACHE *sc) 1.5703 +{ 1.5704 + THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0, 1.5705 + sizeof (THREADNODE)); 1.5706 + if (sc) thr->sc = sc; /* initialize sortcache */ 1.5707 + return thr; 1.5708 +} 1.5709 + 1.5710 + 1.5711 +/* Mail instantiate new acllist 1.5712 + * Returns: new acllist 1.5713 + */ 1.5714 + 1.5715 +ACLLIST *mail_newacllist (void) 1.5716 +{ 1.5717 + return (ACLLIST *) memset (fs_get (sizeof (ACLLIST)),0,sizeof (ACLLIST)); 1.5718 +} 1.5719 + 1.5720 + 1.5721 +/* Mail instantiate new quotalist 1.5722 + * Returns: new quotalist 1.5723 + */ 1.5724 + 1.5725 +QUOTALIST *mail_newquotalist (void) 1.5726 +{ 1.5727 + return (QUOTALIST *) memset (fs_get (sizeof (QUOTALIST)),0, 1.5728 + sizeof (QUOTALIST)); 1.5729 +} 1.5730 + 1.5731 +/* Mail garbage collection routines */ 1.5732 + 1.5733 + 1.5734 +/* Mail garbage collect body 1.5735 + * Accepts: pointer to body pointer 1.5736 + */ 1.5737 + 1.5738 +void mail_free_body (BODY **body) 1.5739 +{ 1.5740 + if (*body) { /* only free if exists */ 1.5741 + mail_free_body_data (*body);/* free its data */ 1.5742 + fs_give ((void **) body); /* return body to free storage */ 1.5743 + } 1.5744 +} 1.5745 + 1.5746 + 1.5747 +/* Mail garbage collect body data 1.5748 + * Accepts: body pointer 1.5749 + */ 1.5750 + 1.5751 +void mail_free_body_data (BODY *body) 1.5752 +{ 1.5753 + switch (body->type) { /* free contents */ 1.5754 + case TYPEMULTIPART: /* multiple part */ 1.5755 + mail_free_body_part (&body->nested.part); 1.5756 + break; 1.5757 + case TYPEMESSAGE: /* encapsulated message */ 1.5758 + if (body->subtype && !strcmp (body->subtype,"RFC822")) { 1.5759 + mail_free_stringlist (&body->nested.msg->lines); 1.5760 + mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS); 1.5761 + } 1.5762 + if (body->nested.msg) fs_give ((void **) &body->nested.msg); 1.5763 + break; 1.5764 + default: 1.5765 + break; 1.5766 + } 1.5767 + if (body->subtype) fs_give ((void **) &body->subtype); 1.5768 + mail_free_body_parameter (&body->parameter); 1.5769 + if (body->id) fs_give ((void **) &body->id); 1.5770 + if (body->description) fs_give ((void **) &body->description); 1.5771 + if (body->disposition.type) fs_give ((void **) &body->disposition.type); 1.5772 + if (body->disposition.parameter) 1.5773 + mail_free_body_parameter (&body->disposition.parameter); 1.5774 + if (body->language) mail_free_stringlist (&body->language); 1.5775 + if (body->location) fs_give ((void **) &body->location); 1.5776 + if (body->mime.text.data) fs_give ((void **) &body->mime.text.data); 1.5777 + if (body->contents.text.data) fs_give ((void **) &body->contents.text.data); 1.5778 + if (body->md5) fs_give ((void **) &body->md5); 1.5779 + if (mailfreebodysparep && body->sparep) 1.5780 + (*mailfreebodysparep) (&body->sparep); 1.5781 +} 1.5782 + 1.5783 +/* Mail garbage collect body parameter 1.5784 + * Accepts: pointer to body parameter pointer 1.5785 + */ 1.5786 + 1.5787 +void mail_free_body_parameter (PARAMETER **parameter) 1.5788 +{ 1.5789 + if (*parameter) { /* only free if exists */ 1.5790 + if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute); 1.5791 + if ((*parameter)->value) fs_give ((void **) &(*parameter)->value); 1.5792 + /* run down the list as necessary */ 1.5793 + mail_free_body_parameter (&(*parameter)->next); 1.5794 + /* return body part to free storage */ 1.5795 + fs_give ((void **) parameter); 1.5796 + } 1.5797 +} 1.5798 + 1.5799 + 1.5800 +/* Mail garbage collect body part 1.5801 + * Accepts: pointer to body part pointer 1.5802 + */ 1.5803 + 1.5804 +void mail_free_body_part (PART **part) 1.5805 +{ 1.5806 + if (*part) { /* only free if exists */ 1.5807 + mail_free_body_data (&(*part)->body); 1.5808 + /* run down the list as necessary */ 1.5809 + mail_free_body_part (&(*part)->next); 1.5810 + fs_give ((void **) part); /* return body part to free storage */ 1.5811 + } 1.5812 +} 1.5813 + 1.5814 +/* Mail garbage collect message cache 1.5815 + * Accepts: mail stream 1.5816 + * 1.5817 + * The message cache is set to NIL when this function finishes. 1.5818 + */ 1.5819 + 1.5820 +void mail_free_cache (MAILSTREAM *stream) 1.5821 +{ 1.5822 + /* do driver specific stuff first */ 1.5823 + mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS); 1.5824 + /* flush the cache */ 1.5825 + (*mailcache) (stream,(long) 0,CH_INIT); 1.5826 +} 1.5827 + 1.5828 + 1.5829 +/* Mail garbage collect cache element 1.5830 + * Accepts: pointer to cache element pointer 1.5831 + */ 1.5832 + 1.5833 +void mail_free_elt (MESSAGECACHE **elt) 1.5834 +{ 1.5835 + /* only free if exists and no sharers */ 1.5836 + if (*elt && !--(*elt)->lockcount) { 1.5837 + mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS); 1.5838 + if (mailfreeeltsparep && (*elt)->sparep) 1.5839 + (*mailfreeeltsparep) (&(*elt)->sparep); 1.5840 + fs_give ((void **) elt); 1.5841 + } 1.5842 + else *elt = NIL; /* else simply drop pointer */ 1.5843 +} 1.5844 + 1.5845 +/* Mail garbage collect envelope 1.5846 + * Accepts: pointer to envelope pointer 1.5847 + */ 1.5848 + 1.5849 +void mail_free_envelope (ENVELOPE **env) 1.5850 +{ 1.5851 + if (*env) { /* only free if exists */ 1.5852 + if ((*env)->remail) fs_give ((void **) &(*env)->remail); 1.5853 + mail_free_address (&(*env)->return_path); 1.5854 + if ((*env)->date) fs_give ((void **) &(*env)->date); 1.5855 + mail_free_address (&(*env)->from); 1.5856 + mail_free_address (&(*env)->sender); 1.5857 + mail_free_address (&(*env)->reply_to); 1.5858 + if ((*env)->subject) fs_give ((void **) &(*env)->subject); 1.5859 + mail_free_address (&(*env)->to); 1.5860 + mail_free_address (&(*env)->cc); 1.5861 + mail_free_address (&(*env)->bcc); 1.5862 + if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to); 1.5863 + if ((*env)->message_id) fs_give ((void **) &(*env)->message_id); 1.5864 + if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups); 1.5865 + if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to); 1.5866 + if ((*env)->references) fs_give ((void **) &(*env)->references); 1.5867 + if (mailfreeenvelopesparep && (*env)->sparep) 1.5868 + (*mailfreeenvelopesparep) (&(*env)->sparep); 1.5869 + fs_give ((void **) env); /* return envelope to free storage */ 1.5870 + } 1.5871 +} 1.5872 + 1.5873 + 1.5874 +/* Mail garbage collect address 1.5875 + * Accepts: pointer to address pointer 1.5876 + */ 1.5877 + 1.5878 +void mail_free_address (ADDRESS **address) 1.5879 +{ 1.5880 + if (*address) { /* only free if exists */ 1.5881 + if ((*address)->personal) fs_give ((void **) &(*address)->personal); 1.5882 + if ((*address)->adl) fs_give ((void **) &(*address)->adl); 1.5883 + if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox); 1.5884 + if ((*address)->host) fs_give ((void **) &(*address)->host); 1.5885 + if ((*address)->error) fs_give ((void **) &(*address)->error); 1.5886 + if ((*address)->orcpt.type) fs_give ((void **) &(*address)->orcpt.type); 1.5887 + if ((*address)->orcpt.addr) fs_give ((void **) &(*address)->orcpt.addr); 1.5888 + mail_free_address (&(*address)->next); 1.5889 + fs_give ((void **) address);/* return address to free storage */ 1.5890 + } 1.5891 +} 1.5892 + 1.5893 + 1.5894 +/* Mail garbage collect stringlist 1.5895 + * Accepts: pointer to stringlist pointer 1.5896 + */ 1.5897 + 1.5898 +void mail_free_stringlist (STRINGLIST **string) 1.5899 +{ 1.5900 + if (*string) { /* only free if exists */ 1.5901 + if ((*string)->text.data) fs_give ((void **) &(*string)->text.data); 1.5902 + mail_free_stringlist (&(*string)->next); 1.5903 + fs_give ((void **) string); /* return string to free storage */ 1.5904 + } 1.5905 +} 1.5906 + 1.5907 +/* Mail garbage collect searchpgm 1.5908 + * Accepts: pointer to searchpgm pointer 1.5909 + */ 1.5910 + 1.5911 +void mail_free_searchpgm (SEARCHPGM **pgm) 1.5912 +{ 1.5913 + if (*pgm) { /* only free if exists */ 1.5914 + mail_free_searchset (&(*pgm)->msgno); 1.5915 + mail_free_searchset (&(*pgm)->uid); 1.5916 + mail_free_searchor (&(*pgm)->or); 1.5917 + mail_free_searchpgmlist (&(*pgm)->not); 1.5918 + mail_free_searchheader (&(*pgm)->header); 1.5919 + mail_free_stringlist (&(*pgm)->bcc); 1.5920 + mail_free_stringlist (&(*pgm)->body); 1.5921 + mail_free_stringlist (&(*pgm)->cc); 1.5922 + mail_free_stringlist (&(*pgm)->from); 1.5923 + mail_free_stringlist (&(*pgm)->keyword); 1.5924 + mail_free_stringlist (&(*pgm)->subject); 1.5925 + mail_free_stringlist (&(*pgm)->text); 1.5926 + mail_free_stringlist (&(*pgm)->to); 1.5927 + fs_give ((void **) pgm); /* return program to free storage */ 1.5928 + } 1.5929 +} 1.5930 + 1.5931 + 1.5932 +/* Mail garbage collect searchheader 1.5933 + * Accepts: pointer to searchheader pointer 1.5934 + */ 1.5935 + 1.5936 +void mail_free_searchheader (SEARCHHEADER **hdr) 1.5937 +{ 1.5938 + if (*hdr) { /* only free if exists */ 1.5939 + if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data); 1.5940 + if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data); 1.5941 + mail_free_searchheader (&(*hdr)->next); 1.5942 + fs_give ((void **) hdr); /* return header to free storage */ 1.5943 + } 1.5944 +} 1.5945 + 1.5946 + 1.5947 +/* Mail garbage collect searchset 1.5948 + * Accepts: pointer to searchset pointer 1.5949 + */ 1.5950 + 1.5951 +void mail_free_searchset (SEARCHSET **set) 1.5952 +{ 1.5953 + if (*set) { /* only free if exists */ 1.5954 + mail_free_searchset (&(*set)->next); 1.5955 + fs_give ((void **) set); /* return set to free storage */ 1.5956 + } 1.5957 +} 1.5958 + 1.5959 +/* Mail garbage collect searchor 1.5960 + * Accepts: pointer to searchor pointer 1.5961 + */ 1.5962 + 1.5963 +void mail_free_searchor (SEARCHOR **orl) 1.5964 +{ 1.5965 + if (*orl) { /* only free if exists */ 1.5966 + mail_free_searchpgm (&(*orl)->first); 1.5967 + mail_free_searchpgm (&(*orl)->second); 1.5968 + mail_free_searchor (&(*orl)->next); 1.5969 + fs_give ((void **) orl); /* return searchor to free storage */ 1.5970 + } 1.5971 +} 1.5972 + 1.5973 + 1.5974 +/* Mail garbage collect search program list 1.5975 + * Accepts: pointer to searchpgmlist pointer 1.5976 + */ 1.5977 + 1.5978 +void mail_free_searchpgmlist (SEARCHPGMLIST **pgl) 1.5979 +{ 1.5980 + if (*pgl) { /* only free if exists */ 1.5981 + mail_free_searchpgm (&(*pgl)->pgm); 1.5982 + mail_free_searchpgmlist (&(*pgl)->next); 1.5983 + fs_give ((void **) pgl); /* return searchpgmlist to free storage */ 1.5984 + } 1.5985 +} 1.5986 + 1.5987 + 1.5988 +/* Mail garbage collect namespace 1.5989 + * Accepts: poiner to namespace 1.5990 + */ 1.5991 + 1.5992 +void mail_free_namespace (NAMESPACE **n) 1.5993 +{ 1.5994 + if (*n) { 1.5995 + fs_give ((void **) &(*n)->name); 1.5996 + mail_free_namespace (&(*n)->next); 1.5997 + mail_free_body_parameter (&(*n)->param); 1.5998 + fs_give ((void **) n); /* return namespace to free storage */ 1.5999 + } 1.6000 +} 1.6001 + 1.6002 +/* Mail garbage collect sort program 1.6003 + * Accepts: pointer to sortpgm pointer 1.6004 + */ 1.6005 + 1.6006 +void mail_free_sortpgm (SORTPGM **pgm) 1.6007 +{ 1.6008 + if (*pgm) { /* only free if exists */ 1.6009 + mail_free_sortpgm (&(*pgm)->next); 1.6010 + fs_give ((void **) pgm); /* return sortpgm to free storage */ 1.6011 + } 1.6012 +} 1.6013 + 1.6014 + 1.6015 +/* Mail garbage collect thread node 1.6016 + * Accepts: pointer to threadnode pointer 1.6017 + */ 1.6018 + 1.6019 +void mail_free_threadnode (THREADNODE **thr) 1.6020 +{ 1.6021 + if (*thr) { /* only free if exists */ 1.6022 + mail_free_threadnode (&(*thr)->branch); 1.6023 + mail_free_threadnode (&(*thr)->next); 1.6024 + fs_give ((void **) thr); /* return threadnode to free storage */ 1.6025 + } 1.6026 +} 1.6027 + 1.6028 + 1.6029 +/* Mail garbage collect acllist 1.6030 + * Accepts: pointer to acllist pointer 1.6031 + */ 1.6032 + 1.6033 +void mail_free_acllist (ACLLIST **al) 1.6034 +{ 1.6035 + if (*al) { /* only free if exists */ 1.6036 + if ((*al)->identifier) fs_give ((void **) &(*al)->identifier); 1.6037 + if ((*al)->rights) fs_give ((void **) &(*al)->rights); 1.6038 + mail_free_acllist (&(*al)->next); 1.6039 + fs_give ((void **) al); /* return acllist to free storage */ 1.6040 + } 1.6041 +} 1.6042 + 1.6043 + 1.6044 +/* Mail garbage collect quotalist 1.6045 + * Accepts: pointer to quotalist pointer 1.6046 + */ 1.6047 + 1.6048 +void mail_free_quotalist (QUOTALIST **ql) 1.6049 +{ 1.6050 + if (*ql) { /* only free if exists */ 1.6051 + if ((*ql)->name) fs_give ((void **) &(*ql)->name); 1.6052 + mail_free_quotalist (&(*ql)->next); 1.6053 + fs_give ((void **) ql); /* return quotalist to free storage */ 1.6054 + } 1.6055 +} 1.6056 + 1.6057 +/* Link authenicator 1.6058 + * Accepts: authenticator to add to list 1.6059 + */ 1.6060 + 1.6061 +void auth_link (AUTHENTICATOR *auth) 1.6062 +{ 1.6063 + if (!auth->valid || (*auth->valid) ()) { 1.6064 + AUTHENTICATOR **a = &mailauthenticators; 1.6065 + while (*a) a = &(*a)->next; /* find end of list of authenticators */ 1.6066 + *a = auth; /* put authenticator at the end */ 1.6067 + auth->next = NIL; /* this authenticator is the end of the list */ 1.6068 + } 1.6069 +} 1.6070 + 1.6071 + 1.6072 +/* Authenticate access 1.6073 + * Accepts: mechanism name 1.6074 + * responder function 1.6075 + * argument count 1.6076 + * argument vector 1.6077 + * Returns: authenticated user name or NIL 1.6078 + */ 1.6079 + 1.6080 +char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[]) 1.6081 +{ 1.6082 + AUTHENTICATOR *auth; 1.6083 + for (auth = mailauthenticators; auth; auth = auth->next) 1.6084 + if (auth->server && !compare_cstring (auth->name,mechanism)) 1.6085 + return (!(auth->flags & AU_DISABLE) && 1.6086 + ((auth->flags & AU_SECURE) || 1.6087 + !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))) ? 1.6088 + (*auth->server) (resp,argc,argv) : NIL; 1.6089 + return NIL; /* no authenticator found */ 1.6090 +} 1.6091 + 1.6092 +/* Lookup authenticator index 1.6093 + * Accepts: authenticator index 1.6094 + * Returns: authenticator, or 0 if not found 1.6095 + */ 1.6096 + 1.6097 +AUTHENTICATOR *mail_lookup_auth (unsigned long i) 1.6098 +{ 1.6099 + AUTHENTICATOR *auth = mailauthenticators; 1.6100 + while (auth && --i) auth = auth->next; 1.6101 + return auth; 1.6102 +} 1.6103 + 1.6104 + 1.6105 +/* Lookup authenticator name 1.6106 + * Accepts: authenticator name 1.6107 + * required authenticator flags 1.6108 + * Returns: index in authenticator chain, or 0 if not found 1.6109 + */ 1.6110 + 1.6111 +unsigned int mail_lookup_auth_name (char *mechanism,long flags) 1.6112 +{ 1.6113 + int i; 1.6114 + AUTHENTICATOR *auth; 1.6115 + for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next) 1.6116 + if (auth->client && !(flags & ~auth->flags) && 1.6117 + !(auth->flags & AU_DISABLE) && !compare_cstring (auth->name,mechanism)) 1.6118 + return i; 1.6119 + return 0; 1.6120 +} 1.6121 + 1.6122 +/* Standard TCP/IP network driver */ 1.6123 + 1.6124 +static NETDRIVER tcpdriver = { 1.6125 + tcp_open, /* open connection */ 1.6126 + tcp_aopen, /* open preauthenticated connection */ 1.6127 + tcp_getline, /* get a line */ 1.6128 + tcp_getbuffer, /* get a buffer */ 1.6129 + tcp_soutr, /* output pushed data */ 1.6130 + tcp_sout, /* output string */ 1.6131 + tcp_close, /* close connection */ 1.6132 + tcp_host, /* return host name */ 1.6133 + tcp_remotehost, /* return remote host name */ 1.6134 + tcp_port, /* return port number */ 1.6135 + tcp_localhost /* return local host name */ 1.6136 +}; 1.6137 + 1.6138 + 1.6139 +/* Network open 1.6140 + * Accepts: NETMBX specifier to open 1.6141 + * default network driver 1.6142 + * default port 1.6143 + * SSL driver 1.6144 + * SSL service name 1.6145 + * SSL driver port 1.6146 + * Returns: Network stream if success, else NIL 1.6147 + */ 1.6148 + 1.6149 +NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port, 1.6150 + NETDRIVER *ssld,char *ssls,unsigned long sslp) 1.6151 +{ 1.6152 + NETSTREAM *stream = NIL; 1.6153 + char tmp[MAILTMPLEN]; 1.6154 + unsigned long flags = mb->novalidate ? NET_NOVALIDATECERT : 0; 1.6155 + if (strlen (mb->host) >= NETMAXHOST) { 1.6156 + sprintf (tmp,"Invalid host name: %.80s",mb->host); 1.6157 + MM_LOG (tmp,ERROR); 1.6158 + } 1.6159 + /* use designated driver if given */ 1.6160 + else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port, 1.6161 + flags); 1.6162 + else if (mb->sslflag && ssld) /* use ssl if sslflag lit */ 1.6163 + stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,flags); 1.6164 + /* if trysslfirst and can open ssl... */ 1.6165 + else if ((mb->trysslflag || trysslfirst) && ssld && 1.6166 + (stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port, 1.6167 + flags | NET_SILENT | NET_TRYSSL))) { 1.6168 + if (net_sout (stream,"",0)) mb->sslflag = T; 1.6169 + else { 1.6170 + net_close (stream); /* flush fake SSL stream */ 1.6171 + stream = NIL; 1.6172 + } 1.6173 + } 1.6174 + /* default to TCP driver */ 1.6175 + else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port, 1.6176 + flags); 1.6177 + return stream; 1.6178 +} 1.6179 + 1.6180 +/* Network open worker routine 1.6181 + * Accepts: network driver 1.6182 + * host name 1.6183 + * service name to look up port 1.6184 + * port number if service name not found 1.6185 + * port number to override service name 1.6186 + * flags (passed on top of port) 1.6187 + * Returns: Network stream if success, else NIL 1.6188 + */ 1.6189 + 1.6190 +NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service, 1.6191 + unsigned long port,unsigned long portoverride, 1.6192 + unsigned long flags) 1.6193 +{ 1.6194 + NETSTREAM *stream = NIL; 1.6195 + void *tstream; 1.6196 + if (service && (*service == '*')) { 1.6197 + flags |= NET_NOOPENTIMEOUT; /* mark that no timeout is desired */ 1.6198 + ++service; /* no longer need the no timeout indicator */ 1.6199 + } 1.6200 + if (portoverride) { /* explicit port number? */ 1.6201 + service = NIL; /* yes, override service name */ 1.6202 + port = portoverride; /* use that instead of default port */ 1.6203 + } 1.6204 + if (tstream = (*dv->open) (host,service,port | flags)) { 1.6205 + stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM)); 1.6206 + stream->stream = tstream; 1.6207 + stream->dtb = dv; 1.6208 + } 1.6209 + return stream; 1.6210 +} 1.6211 + 1.6212 + 1.6213 +/* Network authenticated open 1.6214 + * Accepts: network driver 1.6215 + * NETMBX specifier 1.6216 + * service specifier 1.6217 + * return user name buffer 1.6218 + * Returns: Network stream if success else NIL 1.6219 + */ 1.6220 + 1.6221 +NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user) 1.6222 +{ 1.6223 + NETSTREAM *stream = NIL; 1.6224 + void *tstream; 1.6225 + if (!dv) dv = &tcpdriver; /* default to TCP driver */ 1.6226 + if (tstream = (*dv->aopen) (mb,service,user)) { 1.6227 + stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM)); 1.6228 + stream->stream = tstream; 1.6229 + stream->dtb = dv; 1.6230 + } 1.6231 + return stream; 1.6232 +} 1.6233 + 1.6234 +/* Network receive line 1.6235 + * Accepts: Network stream 1.6236 + * Returns: text line string or NIL if failure 1.6237 + */ 1.6238 + 1.6239 +char *net_getline (NETSTREAM *stream) 1.6240 +{ 1.6241 + return (*stream->dtb->getline) (stream->stream); 1.6242 +} 1.6243 + 1.6244 + 1.6245 +/* Network receive buffer 1.6246 + * Accepts: Network stream (must be void * for use as readfn_t) 1.6247 + * size in bytes 1.6248 + * buffer to read into 1.6249 + * Returns: T if success, NIL otherwise 1.6250 + */ 1.6251 + 1.6252 +long net_getbuffer (void *st,unsigned long size,char *buffer) 1.6253 +{ 1.6254 + NETSTREAM *stream = (NETSTREAM *) st; 1.6255 + return (*stream->dtb->getbuffer) (stream->stream,size,buffer); 1.6256 +} 1.6257 + 1.6258 + 1.6259 +/* Network send null-terminated string 1.6260 + * Accepts: Network stream 1.6261 + * string pointer 1.6262 + * Returns: T if success else NIL 1.6263 + */ 1.6264 + 1.6265 +long net_soutr (NETSTREAM *stream,char *string) 1.6266 +{ 1.6267 + return (*stream->dtb->soutr) (stream->stream,string); 1.6268 +} 1.6269 + 1.6270 + 1.6271 +/* Network send string 1.6272 + * Accepts: Network stream 1.6273 + * string pointer 1.6274 + * byte count 1.6275 + * Returns: T if success else NIL 1.6276 + */ 1.6277 + 1.6278 +long net_sout (NETSTREAM *stream,char *string,unsigned long size) 1.6279 +{ 1.6280 + return (*stream->dtb->sout) (stream->stream,string,size); 1.6281 +} 1.6282 + 1.6283 +/* Network close 1.6284 + * Accepts: Network stream 1.6285 + */ 1.6286 + 1.6287 +void net_close (NETSTREAM *stream) 1.6288 +{ 1.6289 + if (stream->stream) (*stream->dtb->close) (stream->stream); 1.6290 + fs_give ((void **) &stream); 1.6291 +} 1.6292 + 1.6293 + 1.6294 +/* Network get host name 1.6295 + * Accepts: Network stream 1.6296 + * Returns: host name for this stream 1.6297 + */ 1.6298 + 1.6299 +char *net_host (NETSTREAM *stream) 1.6300 +{ 1.6301 + return (*stream->dtb->host) (stream->stream); 1.6302 +} 1.6303 + 1.6304 + 1.6305 +/* Network get remote host name 1.6306 + * Accepts: Network stream 1.6307 + * Returns: host name for this stream 1.6308 + */ 1.6309 + 1.6310 +char *net_remotehost (NETSTREAM *stream) 1.6311 +{ 1.6312 + return (*stream->dtb->remotehost) (stream->stream); 1.6313 +} 1.6314 + 1.6315 +/* Network return port for this stream 1.6316 + * Accepts: Network stream 1.6317 + * Returns: port number for this stream 1.6318 + */ 1.6319 + 1.6320 +unsigned long net_port (NETSTREAM *stream) 1.6321 +{ 1.6322 + return (*stream->dtb->port) (stream->stream); 1.6323 +} 1.6324 + 1.6325 + 1.6326 +/* Network get local host name 1.6327 + * Accepts: Network stream 1.6328 + * Returns: local host name 1.6329 + */ 1.6330 + 1.6331 +char *net_localhost (NETSTREAM *stream) 1.6332 +{ 1.6333 + return (*stream->dtb->localhost) (stream->stream); 1.6334 +}