1 /* ========================================================================
2  * Copyright 2008-2010 Mark Crispin
3  * ========================================================================
4  */
5 
6 /*
7  * Program:	Mailbox Access routines
8  *
9  * Author:	Mark Crispin
10  *
11  * Date:	22 November 1989
12  * Last Edited:	15 November 2010
13  *
14  * Previous versions of this file were
15 *
16  * Copyright 1988-2008 University of Washington
17  *
18  * Licensed under the Apache License, Version 2.0 (the "License");
19  * you may not use this file except in compliance with the License.
20  * You may obtain a copy of the License at
21  *
22  *     http://www.apache.org/licenses/LICENSE-2.0
23  *
24  */
25 
26 
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <time.h>
30 #include "c-client.h"
31 
32 char *Panda_copyright = "Copyright 2008-2010 Mark Crispin\n";
33 
34 char *UW_copyright = "Copyright 1988-2008 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";
35 
36 /* c-client global data */
37 				/* version of this library */
38 static char *mailcclientversion = CCLIENTVERSION;
39 				/* Minimum in range of encryption supported */
40 static int encryption_range_min = 0;
41 				/* Maximum in range of encryption supported */
42 static int encryption_range_max = 0;
43 				/* app identity */
44 static IDLIST *idapp = NIL;
45 				/* list of mail drivers */
46 static DRIVER *maildrivers = NIL;
47 				/* list of authenticators */
48 static AUTHENTICATOR *mailauthenticators = NIL;
49 				/* SSL driver pointer */
50 static NETDRIVER *mailssldriver = NIL;
51 				/* pointer to alternate gets function */
52 static mailgets_t mailgets = NIL;
53 				/* pointer to read progress function */
54 static readprogress_t mailreadprogress = NIL;
55 				/* mail cache manipulation function */
56 static mailcache_t mailcache = mm_cache;
57 				/* RFC-822 output generator */
58 static rfc822out_t mail822out = NIL;
59 				/* RFC-822 output generator (new style) */
60 static rfc822outfull_t mail822outfull = NIL;
61 				/* Erase password (client side) */
62 static deletepwd_t erase_password = NIL;
63 				/* SMTP verbose callback */
64 static smtpverbose_t mailsmtpverbose = mm_dlog;
65 				/* proxy copy routine */
66 static mailproxycopy_t mailproxycopy = NIL;
67 				/* RFC-822 external line parse */
68 static parseline_t mailparseline = NIL;
69 				/* RFC-822 external phrase parser */
70 static parsephrase_t mailparsephrase = NIL;
71 static kinit_t mailkinit = NIL;	/* application kinit callback */
72 				/* note network sent command */
73 static sendcommand_t mailsendcommand = NIL;
74 				/* newsrc file name decision function */
75 static newsrcquery_t mailnewsrcquery = NIL;
76 				/* ACL results callback */
77 static getacl_t mailaclresults = NIL;
78 				/* list rights results callback */
79 static listrights_t maillistrightsresults = NIL;
80 				/* my rights results callback */
81 static myrights_t mailmyrightsresults = NIL;
82 				/* quota results callback */
83 static quota_t mailquotaresults = NIL;
84 				/* quota root results callback */
85 static quotaroot_t mailquotarootresults = NIL;
86 				/* sorted results callback */
87 static sortresults_t mailsortresults = NIL;
88 				/* threaded results callback */
89 static threadresults_t mailthreadresults = NIL;
90 				/* COPY UID results */
91 static copyuid_t mailcopyuid = NIL;
92 				/* APPEND UID results */
93 static appenduid_t mailappenduid = NIL;
94 
95 static oauth2getaccesscode_t oauth2getaccesscode = NIL;
96 
97 static oauth2clientinfo_t oauth2clientinfo = NIL;
98 
99 static oauth2deviceinfo_t oauth2deviceinfo = NIL;
100 				/* free elt extra stuff callback */
101 static freeeltsparep_t mailfreeeltsparep = NIL;
102 				/* free envelope extra stuff callback */
103 static freeenvelopesparep_t mailfreeenvelopesparep = NIL;
104 				/* free body extra stuff callback */
105 static freebodysparep_t mailfreebodysparep = NIL;
106 				/* free stream extra stuff callback */
107 static freestreamsparep_t mailfreestreamsparep = NIL;
108 				/* SSL start routine */
109 static sslstart_t mailsslstart = NIL;
110 				/* SSL certificate query */
111 static sslcertificatequery_t mailsslcertificatequery = NIL;
112 				/* SSL client certificate */
113 static sslclientcert_t mailsslclientcert = NIL;
114 				/* SSL client private key */
115 static sslclientkey_t mailsslclientkey = NIL;
116 				/* SSL failure notify */
117 static sslfailure_t mailsslfailure = NIL;
118 				/* snarf interval */
119 static long mailsnarfinterval = 60;
120 				/* snarf preservation */
121 static long mailsnarfpreserve = NIL;
122 				/* newsrc name uses canonical host */
123 static long mailnewsrccanon = LONGT;
124 
125 				/* supported threaders */
126 static THREADER mailthreadordsub = {
127   "ORDEREDSUBJECT",mail_thread_orderedsubject,NIL
128 };
129 static THREADER mailthreadlist = {
130   "REFERENCES",mail_thread_references,&mailthreadordsub
131 };
132 
133 				/* server name */
134 static char *servicename = "unknown";
135 				/* server externally-set authentication ID */
136 static char *externalauthid = NIL;
137 static int expungeatping = T;	/* mail_ping() may call mm_expunged() */
138 static int trysslfirst = NIL;	/* always try SSL first */
139 static int notimezones = NIL;	/* write timezones in "From " header */
140 static int trustdns = T;	/* do DNS canonicalization */
141 static int saslusesptrname = T;	/* SASL uses name from DNS PTR lookup */
142 				/* trustdns also must be set */
143 static int debugsensitive = NIL;/* debug telemetry includes sensitive data */
144 
145 /* Default mail cache handler
146  * Accepts: pointer to cache handle
147  *	    message number
148  *	    caching function
149  * Returns: cache data
150  */
151 
mm_cache(MAILSTREAM * stream,unsigned long msgno,long op)152 void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
153 {
154   size_t n;
155   void *ret = NIL;
156   unsigned long i;
157   switch ((int) op) {		/* what function? */
158   case CH_INIT:			/* initialize cache */
159     if (stream->cache) {	/* flush old cache contents */
160       while (stream->cachesize) {
161 	mm_cache (stream,stream->cachesize,CH_FREE);
162 	mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE);
163       }
164       fs_give ((void **) &stream->cache);
165       fs_give ((void **) &stream->sc);
166       stream->nmsgs = 0;	/* can't have any messages now */
167     }
168     break;
169   case CH_SIZE:			/* (re-)size the cache */
170     if (!stream->cache)	{	/* have a cache already? */
171 				/* no, create new cache */
172       n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
173       stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n);
174       stream->sc = (SORTCACHE **) memset (fs_get (n),0,n);
175     }
176 				/* is existing cache size large neough */
177     else if (msgno > stream->cachesize) {
178       i = stream->cachesize;	/* remember old size */
179       n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
180       fs_resize ((void **) &stream->cache,n);
181       fs_resize ((void **) &stream->sc,n);
182       while (i < stream->cachesize) {
183 	stream->cache[i] = NIL;
184 	stream->sc[i++] = NIL;
185       }
186     }
187     break;
188 
189   case CH_MAKEELT:		/* return elt, make if necessary */
190     if (!stream->cache[msgno - 1])
191       stream->cache[msgno - 1] = mail_new_cache_elt (msgno);
192 				/* falls through */
193   case CH_ELT:			/* return elt */
194     ret = (void *) stream->cache[msgno - 1];
195     break;
196   case CH_SORTCACHE:		/* return sortcache entry, make if needed */
197     if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] =
198       (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE));
199     ret = (void *) stream->sc[msgno - 1];
200     break;
201   case CH_FREE:			/* free elt */
202     mail_free_elt (&stream->cache[msgno - 1]);
203     break;
204   case CH_FREESORTCACHE:
205     if (stream->sc[msgno - 1]) {
206       if (stream->sc[msgno - 1]->from)
207 	fs_give ((void **) &stream->sc[msgno - 1]->from);
208       if (stream->sc[msgno - 1]->to)
209 	fs_give ((void **) &stream->sc[msgno - 1]->to);
210       if (stream->sc[msgno - 1]->cc)
211 	fs_give ((void **) &stream->sc[msgno - 1]->cc);
212       if (stream->sc[msgno - 1]->subject)
213 	fs_give ((void **) &stream->sc[msgno - 1]->subject);
214       if (stream->sc[msgno - 1]->unique &&
215 	  (stream->sc[msgno - 1]->unique != stream->sc[msgno - 1]->message_id))
216 	fs_give ((void **) &stream->sc[msgno - 1]->unique);
217       if (stream->sc[msgno - 1]->message_id)
218 	fs_give ((void **) &stream->sc[msgno - 1]->message_id);
219       if (stream->sc[msgno - 1]->references)
220 	mail_free_stringlist (&stream->sc[msgno - 1]->references);
221       fs_give ((void **) &stream->sc[msgno - 1]);
222     }
223     break;
224   case CH_EXPUNGE:		/* expunge cache slot */
225     for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) {
226       if ((stream->cache[i] = stream->cache[msgno]) != NULL)
227 	stream->cache[i]->msgno = msgno;
228       stream->sc[i] = stream->sc[msgno];
229     }
230     stream->cache[i] = NIL;	/* top of cache goes away */
231     stream->sc[i] = NIL;
232     break;
233   default:
234     fatal ("Bad mm_cache op");
235     break;
236   }
237   return ret;
238 }
239 
240 /* Dummy string driver for complete in-memory strings */
241 
242 static void mail_string_init (STRING *s,void *data,unsigned long size);
243 static char mail_string_next (STRING *s);
244 static void mail_string_setpos (STRING *s,unsigned long i);
245 
246 STRINGDRIVER mail_string = {
247   mail_string_init,		/* initialize string structure */
248   mail_string_next,		/* get next byte in string structure */
249   mail_string_setpos		/* set position in string structure */
250 };
251 
252 
253 /* Initialize mail string structure for in-memory string
254  * Accepts: string structure
255  *	    pointer to string
256  *	    size of string
257  */
258 
mail_string_init(STRING * s,void * data,unsigned long size)259 static void mail_string_init (STRING *s,void *data,unsigned long size)
260 {
261 				/* set initial string pointers */
262   s->chunk = s->curpos = (char *) (s->data = data);
263 				/* and sizes */
264   s->size = s->chunksize = s->cursize = size;
265   s->data1 = s->offset = 0;	/* never any offset */
266 }
267 
268 
269 /* Get next character from string
270  * Accepts: string structure
271  * Returns: character, string structure chunk refreshed
272  */
273 
mail_string_next(STRING * s)274 static char mail_string_next (STRING *s)
275 {
276   return *s->curpos++;		/* return the last byte */
277 }
278 
279 
280 /* Set string pointer position
281  * Accepts: string structure
282  *	    new position
283  */
284 
mail_string_setpos(STRING * s,unsigned long i)285 static void mail_string_setpos (STRING *s,unsigned long i)
286 {
287   s->curpos = s->chunk + i;	/* set new position */
288   s->cursize = s->chunksize - i;/* and new size */
289 }
290 
291 /* Mail routines
292  *
293  *  mail_xxx routines are the interface between this module and the outside
294  * world.  Only these routines should be referenced by external callers.
295  *
296  *  Note that there is an important difference between a "sequence" and a
297  * "message #" (msgno).  A sequence is a string representing a sequence in
298  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
299  * is a single integer.
300  *
301  */
302 
303 /* Mail version check
304  * Accepts: version
305  */
306 
mail_versioncheck(char * version)307 void mail_versioncheck (char *version)
308 {
309 				/* attempt to protect again wrong .h */
310   if (strcmp (version,mailcclientversion)) {
311     char tmp[MAILTMPLEN];
312     sprintf (tmp,"c-client library version skew, app=%.100s library=%.100s",
313 	     version,mailcclientversion);
314     fatal (tmp);
315   }
316 }
317 
318 
319 /* Mail link driver
320  * Accepts: driver to add to list
321  */
322 
mail_link(DRIVER * driver)323 void mail_link (DRIVER *driver)
324 {
325   DRIVER **d = &maildrivers;
326   while (*d) d = &(*d)->next;	/* find end of list of drivers */
327   *d = driver;			/* put driver at the end */
328   driver->next = NIL;		/* this driver is the end of the list */
329 }
330 
free_id(IDLIST ** idp)331 void free_id(IDLIST **idp)
332 {
333    if(!idp || !*idp) return;
334 
335    if((*idp)->name) fs_give((void **) &(*idp)->name);
336    if((*idp)->value) fs_give((void **) &(*idp)->value);
337    if((*idp)->next) free_id (&(*idp)->next);
338    fs_give((void **)idp);
339 }
340 
341 /* Mail manipulate driver parameters
342  * Accepts: mail stream
343  *	    function code
344  *	    function-dependent value
345  * Returns: function-dependent return value
346  */
347 
mail_parameters(MAILSTREAM * stream,long function,void * value)348 void *mail_parameters (MAILSTREAM *stream,long function,void *value)
349 {
350   void *r,*ret = NIL;
351   DRIVER *d;
352   AUTHENTICATOR *a;
353   switch ((int) function) {
354   case SET_INBOXPATH:
355     fatal ("SET_INBOXPATH not permitted");
356   case GET_INBOXPATH:
357     if ((stream || (stream = mail_open (NIL,"INBOX",OP_PROTOTYPE))) &&
358 	stream->dtb) ret = (*stream->dtb->parameters) (function,value);
359     break;
360   case SET_THREADERS:
361     fatal ("SET_THREADERS not permitted");
362   case GET_THREADERS:		/* use stream dtb instead of global */
363     ret = (stream && stream->dtb) ?
364 				/* KLUDGE ALERT: note stream passed as value */
365       (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
366     break;
367   case SET_NAMESPACE:
368     fatal ("SET_NAMESPACE not permitted");
369     break;
370   case SET_NEWSRC:		/* too late on open stream */
371     if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL))))
372       fatal ("SET_NEWSRC not permitted");
373     else ret = env_parameters (function,value);
374     break;
375   case GET_NAMESPACE:
376     ret = (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL)) ?
377 				/* KLUDGE ALERT: note stream passed as value */
378       (*stream->dtb->parameters) (function,stream) :
379 	env_parameters (function,value);
380     break;
381   case GET_NEWSRC:		/* use stream dtb instead of environment */
382     ret = (stream && stream->dtb) ?
383 				/* KLUDGE ALERT: note stream passed as value */
384       (*stream->dtb->parameters) (function,stream) :
385 	env_parameters (function,value);
386     break;
387   case ENABLE_DEBUG:
388     fatal ("ENABLE_DEBUG not permitted");
389   case DISABLE_DEBUG:
390     fatal ("DISABLE_DEBUG not permitted");
391   case SET_DIRFMTTEST:
392     fatal ("SET_DIRFMTTEST not permitted");
393   case GET_DIRFMTTEST:
394     if (!(stream && stream->dtb &&
395 	  (ret = (*stream->dtb->parameters) (function,NIL))))
396       fatal ("GET_DIRFMTTEST not permitted");
397     break;
398 
399   case SET_DRIVERS:
400     fatal ("SET_DRIVERS not permitted");
401   case GET_DRIVERS:		/* always return global */
402     ret = (void *) maildrivers;
403     break;
404   case SET_DRIVER:
405     fatal ("SET_DRIVER not permitted");
406   case GET_DRIVER:
407     for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
408 	 d = d->next);
409     ret = (void *) d;
410     break;
411   case ENABLE_DRIVER:
412     for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
413 	 d = d->next);
414     if ((ret = (void *) d) != NULL) d->flags &= ~DR_DISABLE;
415     break;
416   case DISABLE_DRIVER:
417     for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
418 	 d = d->next);
419     if ((ret = (void *) d) != NULL) d->flags |= DR_DISABLE;
420     break;
421   case ENABLE_AUTHENTICATOR:
422     for (a = mailauthenticators;/* scan authenticators */
423 	 a && compare_cstring (a->name,(char *) value); a = a->next);
424     if ((ret = (void *) a) != NULL) a->flags &= ~AU_DISABLE;
425     break;
426   case DISABLE_AUTHENTICATOR:
427     for (a = mailauthenticators;/* scan authenticators */
428 	 a && compare_cstring (a->name,(char *) value); a = a->next);
429     if ((ret = (void *) a) != NULL) a->flags |= AU_DISABLE;
430     break;
431   case UNHIDE_AUTHENTICATOR:
432     for (a = mailauthenticators;/* scan authenticators */
433 	 a && compare_cstring (a->name,(char *) value); a = a->next);
434     if ((ret = (void *) a) != NULL) a->flags &= ~AU_HIDE;
435     break;
436   case HIDE_AUTHENTICATOR:
437     for (a = mailauthenticators;/* scan authenticators */
438 	 a && compare_cstring (a->name,(char *) value); a = a->next);
439     if ((ret = (void *) a) != NULL) a->flags |= AU_HIDE;
440     break;
441   case SET_EXTERNALAUTHID:
442     if (value) {		/* setting external authentication ID */
443       externalauthid = cpystr ((char *) value);
444       mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"EXTERNAL");
445     }
446     else {			/* clearing external authentication ID */
447       if (externalauthid) fs_give ((void **) &externalauthid);
448       mail_parameters (NIL,HIDE_AUTHENTICATOR,"EXTERNAL");
449     }
450   case GET_EXTERNALAUTHID:
451     ret = (void *) externalauthid;
452     break;
453 
454   case SET_GETS:
455     mailgets = (mailgets_t) value;
456   case GET_GETS:
457     ret = (void *) mailgets;
458     break;
459   case SET_READPROGRESS:
460     mailreadprogress = (readprogress_t) value;
461   case GET_READPROGRESS:
462     ret = (void *) mailreadprogress;
463     break;
464   case SET_CACHE:
465     mailcache = (mailcache_t) value;
466   case GET_CACHE:
467     ret = (void *) mailcache;
468     break;
469   case SET_RFC822OUTPUT:
470     mail822out = (rfc822out_t) value;
471   case GET_RFC822OUTPUT:
472     ret = (void *) mail822out;
473     break;
474   case SET_RFC822OUTPUTFULL:
475     mail822outfull = (rfc822outfull_t) value;
476   case GET_RFC822OUTPUTFULL:
477     ret = (void *) mail822outfull;
478     break;
479   case SET_SMTPVERBOSE:
480     mailsmtpverbose = (smtpverbose_t) value;
481   case GET_SMTPVERBOSE:
482     ret = (void *) mailsmtpverbose;
483     break;
484   case SET_MAILPROXYCOPY:
485     mailproxycopy = (mailproxycopy_t) value;
486   case GET_MAILPROXYCOPY:
487     ret = (void *) mailproxycopy;
488     break;
489   case SET_PARSELINE:
490     mailparseline = (parseline_t) value;
491   case GET_PARSELINE:
492     ret = (void *) mailparseline;
493     break;
494   case SET_PARSEPHRASE:
495     mailparsephrase = (parsephrase_t) value;
496   case GET_PARSEPHRASE:
497     ret = (void *) mailparsephrase;
498     break;
499   case SET_NEWSRCQUERY:
500     mailnewsrcquery = (newsrcquery_t) value;
501   case GET_NEWSRCQUERY:
502     ret = (void *) mailnewsrcquery;
503     break;
504   case SET_NEWSRCCANONHOST:
505     mailnewsrccanon = (long) value;
506   case GET_NEWSRCCANONHOST:
507     ret = (void *) mailnewsrccanon;
508     break;
509 
510   case SET_COPYUID:
511     mailcopyuid = (copyuid_t) value;
512   case GET_COPYUID:
513     ret = (void *) mailcopyuid;
514     break;
515   case SET_APPENDUID:
516     mailappenduid = (appenduid_t) value;
517   case GET_APPENDUID:
518     ret = (void *) mailappenduid;
519     break;
520   case SET_FREEENVELOPESPAREP:
521     mailfreeenvelopesparep = (freeenvelopesparep_t) value;
522   case GET_FREEENVELOPESPAREP:
523     ret = (void *) mailfreeenvelopesparep;
524     break;
525   case SET_FREEELTSPAREP:
526     mailfreeeltsparep = (freeeltsparep_t) value;
527   case GET_FREEELTSPAREP:
528     ret = (void *) mailfreeeltsparep;
529     break;
530   case SET_FREESTREAMSPAREP:
531     mailfreestreamsparep = (freestreamsparep_t) value;
532   case GET_FREESTREAMSPAREP:
533     ret = (void *) mailfreestreamsparep;
534     break;
535   case SET_FREEBODYSPAREP:
536     mailfreebodysparep = (freebodysparep_t) value;
537   case GET_FREEBODYSPAREP:
538     ret = (void *) mailfreebodysparep;
539     break;
540 
541   case SET_SSLSTART:
542     mailsslstart = (sslstart_t) value;
543   case GET_SSLSTART:
544     ret = (void *) mailsslstart;
545     break;
546   case SET_SSLCERTIFICATEQUERY:
547     mailsslcertificatequery = (sslcertificatequery_t) value;
548   case GET_SSLCERTIFICATEQUERY:
549     ret = (void *) mailsslcertificatequery;
550     break;
551   case SET_SSLCLIENTCERT:
552     mailsslclientcert = (sslclientcert_t) value;
553   case GET_SSLCLIENTCERT:
554     ret = (void *) mailsslclientcert;
555     break;
556   case SET_SSLCLIENTKEY:
557     mailsslclientkey = (sslclientkey_t) value;
558   case GET_SSLCLIENTKEY:
559     ret = (void *) mailsslclientkey;
560     break;
561   case SET_SSLFAILURE:
562     mailsslfailure = (sslfailure_t) value;
563   case GET_SSLFAILURE:
564     ret = (void *) mailsslfailure;
565     break;
566   case SET_ENCRYPTION_RANGE_MIN:
567     encryption_range_min =  *(int *) value;
568   case GET_ENCRYPTION_RANGE_MIN:
569     ret = (void *) &encryption_range_min;
570     break;
571   case SET_ENCRYPTION_RANGE_MAX:
572     encryption_range_max =  *(int *) value;
573   case GET_ENCRYPTION_RANGE_MAX:
574     ret = (void *) &encryption_range_max;
575     break;
576   case SET_KINIT:
577     mailkinit = (kinit_t) value;
578   case GET_KINIT:
579     ret = (void *) mailkinit;
580     break;
581   case SET_SENDCOMMAND:
582     mailsendcommand = (sendcommand_t) value;
583   case GET_SENDCOMMAND:
584     ret = (void *) mailsendcommand;
585     break;
586   case SET_ERASEPASSWORD:
587     erase_password = (deletepwd_t) value;
588   case GET_ERASEPASSWORD:
589     ret = (void *) erase_password;
590     break;
591 
592   case SET_SERVICENAME:
593     servicename = (char *) value;
594   case GET_SERVICENAME:
595     ret = (void *) servicename;
596     break;
597   case SET_EXPUNGEATPING:
598     expungeatping = (value ? T : NIL);
599   case GET_EXPUNGEATPING:
600     ret = (void *) (expungeatping ? VOIDT : NIL);
601     break;
602   case SET_SORTRESULTS:
603     mailsortresults = (sortresults_t) value;
604   case GET_SORTRESULTS:
605     ret = (void *) mailsortresults;
606     break;
607   case SET_THREADRESULTS:
608     mailthreadresults = (threadresults_t) value;
609   case GET_THREADRESULTS:
610     ret = (void *) mailthreadresults;
611     break;
612   case SET_SSLDRIVER:
613     mailssldriver = (NETDRIVER *) value;
614   case GET_SSLDRIVER:
615     ret = (void *) mailssldriver;
616     break;
617   case SET_TRYSSLFIRST:
618     trysslfirst = (value ? T : NIL);
619   case GET_TRYSSLFIRST:
620     ret = (void *) (trysslfirst ? VOIDT : NIL);
621     break;
622   case SET_NOTIMEZONES:
623     notimezones = (value ? T : NIL);
624   case GET_NOTIMEZONES:
625     ret = (void *) (notimezones ? VOIDT : NIL);
626     break;
627   case SET_TRUSTDNS:
628     trustdns = (value ? T : NIL);
629   case GET_TRUSTDNS:
630     ret = (void *) (trustdns ? VOIDT : NIL);
631     break;
632   case SET_SASLUSESPTRNAME:
633     saslusesptrname = (value ? T : NIL);
634   case GET_SASLUSESPTRNAME:
635     ret = (void *) (saslusesptrname ? VOIDT : NIL);
636     break;
637   case SET_DEBUGSENSITIVE:
638     debugsensitive = (value ? T : NIL);
639   case GET_DEBUGSENSITIVE:
640     ret = (void *) (debugsensitive ? VOIDT : NIL);
641     break;
642 
643   case SET_ACL:
644     mailaclresults = (getacl_t) value;
645   case GET_ACL:
646     ret = (void *) mailaclresults;
647     break;
648   case SET_LISTRIGHTS:
649     maillistrightsresults = (listrights_t) value;
650   case GET_LISTRIGHTS:
651     ret = (void *) maillistrightsresults;
652     break;
653   case SET_MYRIGHTS:
654     mailmyrightsresults = (myrights_t) value;
655   case GET_MYRIGHTS:
656     ret = (void *) mailmyrightsresults;
657     break;
658   case SET_QUOTA:
659     mailquotaresults = (quota_t) value;
660   case GET_QUOTA:
661     ret = (void *) mailquotaresults;
662     break;
663   case SET_QUOTAROOT:
664     mailquotarootresults = (quotaroot_t) value;
665   case GET_QUOTAROOT:
666     ret = (void *) mailquotarootresults;
667     break;
668   case SET_SNARFINTERVAL:
669     mailsnarfinterval = (long) value;
670   case GET_SNARFINTERVAL:
671     ret = (void *) mailsnarfinterval;
672     break;
673   case SET_SNARFPRESERVE:
674     mailsnarfpreserve = (long) value;
675   case GET_SNARFPRESERVE:
676     ret = (void *) mailsnarfpreserve;
677     break;
678   case SET_SNARFMAILBOXNAME:
679     if (stream) {		/* have a stream? */
680       if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
681       stream->snarf.name = cpystr ((char *) value);
682     }
683     else fatal ("SET_SNARFMAILBOXNAME with no stream");
684   case GET_SNARFMAILBOXNAME:
685     if (stream) ret = (void *) stream->snarf.name;
686     break;
687   case SET_IDPARAMS:		/* program id */
688     idapp = (IDLIST *) value;
689   case GET_IDPARAMS:
690     ret = (void *) idapp;
691     break;
692   case SET_OA2CLIENTGETACCESSCODE:
693     oauth2getaccesscode = (oauth2getaccesscode_t) value;
694   case GET_OA2CLIENTGETACCESSCODE:
695     ret = (void *) oauth2getaccesscode;
696     break;
697   case SET_OA2CLIENTINFO:
698     oauth2clientinfo = (oauth2clientinfo_t) value;
699   case GET_OA2CLIENTINFO:
700     ret = (void *) oauth2clientinfo;
701     break;
702   case SET_OA2DEVICEINFO:
703     oauth2deviceinfo = (oauth2deviceinfo_t) value;
704   case GET_OA2DEVICEINFO:
705     ret = (void *) oauth2deviceinfo;
706     break;
707   default:
708     if ((r = smtp_parameters (function,value)) != NULL) ret = r;
709     if ((r = env_parameters (function,value)) != NULL) ret = r;
710     if ((r = tcp_parameters (function,value)) != NULL) ret = r;
711     if ((r = http_parameters (function,value)) != NULL) ret = r;
712     if ((r = utf8_parameters (function,value)) != NULL) ret = r;
713     if (stream && stream->dtb) {/* if have stream, do for its driver only */
714       if ((r = (*stream->dtb->parameters) (function,value)) != NULL) ret = r;
715     }
716 				/* else do all drivers */
717     else for (d = maildrivers; d; d = d->next)
718       if ((r = (d->parameters) (function,value)) != NULL) ret = r;
719     break;
720   }
721   return ret;
722 }
723 
724 /* Mail validate mailbox name
725  * Accepts: MAIL stream
726  *	    mailbox name
727  *	    purpose string for error message
728  * Return: driver factory on success, NIL on failure
729  */
730 
mail_valid(MAILSTREAM * stream,char * mailbox,char * purpose)731 DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
732 {
733   char tmp[MAILTMPLEN];
734   DRIVER *factory = NIL;
735 				/* never allow names with newlines */
736   if (strpbrk (mailbox,"\015\012")) {
737     if (purpose) {		/* if want an error message */
738       sprintf (tmp,"Can't %s with such a name",purpose);
739       MM_LOG (tmp,ERROR);
740     }
741     return NIL;
742   }
743 				/* validate name, find driver factory */
744   if (strlen (mailbox) < (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50))
745     for (factory = maildrivers; factory &&
746 	 ((factory->flags & DR_DISABLE) ||
747 	  ((factory->flags & DR_LOCAL) && (*mailbox == '{')) ||
748 	  !(*factory->valid) (mailbox));
749 	 factory = factory->next);
750 				/* validate factory against non-dummy stream */
751   if (factory && stream && stream->dtb && (stream->dtb != factory) &&
752       strcmp (stream->dtb->name,"dummy"))
753 				/* factory invalid; if dummy, use stream */
754     factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
755   if (!factory && purpose) {	/* if want an error message */
756     sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ?
757 	     "invalid remote specification" : "no such mailbox");
758     MM_LOG (tmp,ERROR);
759   }
760   return factory;		/* return driver factory */
761 }
762 
763 /* Mail validate network mailbox name
764  * Accepts: mailbox name
765  *	    mailbox driver to validate against
766  *	    pointer to where to return host name if non-NIL
767  *	    pointer to where to return mailbox name if non-NIL
768  * Returns: driver on success, NIL on failure
769  */
770 
mail_valid_net(char * name,DRIVER * drv,char * host,char * mailbox)771 DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
772 {
773   NETMBX mb;
774   if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
775     return NIL;
776   if (host) strcpy (host,mb.host);
777   if (mailbox) strcpy (mailbox,mb.mailbox);
778   return drv;
779 }
780 
781 
782 /* Mail validate network mailbox name
783  * Accepts: mailbox name
784  *	    NETMBX structure to return values
785  * Returns: T on success, NIL on failure
786  */
787 
mail_valid_net_parse(char * name,NETMBX * mb)788 long mail_valid_net_parse (char *name,NETMBX *mb)
789 {
790   return mail_valid_net_parse_work (name,mb,"imap");
791 }
792 
793 /* Mail validate network mailbox name worker routine
794  * Accepts: mailbox name
795  *	    NETMBX structure to return values
796  *	    default service
797  * Returns: T on success, NIL on failure
798  */
799 
mail_valid_net_parse_work(char * name,NETMBX * mb,char * service)800 long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service)
801 {
802   int i,j;
803   char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN];
804 				/* initialize structure */
805   memset (mb,'\0',sizeof (NETMBX));
806 				/* must have host specification */
807   if (*name++ != '{') return NIL;
808   if (*name == '[') {		/* if domain literal, find its ending */
809     if (!((v = strpbrk (name,"]}")) && (*v++ == ']'))) return NIL;
810   }
811 				/* find end of host name */
812   else if (!(v = strpbrk (name,"/:}"))) return NIL;
813 				/* validate length, find mailbox part */
814   if (!((i = v - name) && (i < NETMAXHOST) && (t = strchr (v,'}')) &&
815 	((j = t - v) < MAILTMPLEN) && (strlen (t+1) < (size_t) NETMAXMBX)))
816     return NIL;			/* invalid mailbox */
817   strncpy (mb->host,name,i);	/* set host name */
818   strncpy (mb->orighost,name,i);
819   mb->host[i] = mb->orighost[i] = '\0';
820   strcpy (mb->mailbox,t+1);	/* set mailbox name */
821   if (t - v) {			/* any switches or port specification? */
822     strncpy (t = tmp,v,j);	/* copy it */
823     tmp[j] = '\0';		/* tie it off */
824     c = *t++;			/* get first delimiter */
825     do switch (c) {		/* act based upon the character */
826     case ':':			/* port specification */
827       if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL;
828       c = t ? *t++ : '\0';	/* get delimiter, advance pointer */
829       break;
830     case '/':			/* switch */
831 				/* find delimiter */
832       if ((t = strpbrk (s = t,"/:=")) != NULL) {
833 	c = *t;			/* remember delimiter for later */
834 	*t++ = '\0';		/* tie off switch name */
835       }
836       else c = '\0';		/* no delimiter */
837       if (c == '=') {		/* parse switches which take arguments */
838 	if (*t == '"') {	/* quoted string? */
839 	  for (v = arg,i = 0,++t; (c = *t++) != '"';) {
840 	    if (!c) return NIL;	/* unterminated string */
841 				/* quote next character */
842 	    if (c == '\\') c = *t++;
843 	    if (!c) return NIL;	/* can't quote NUL either */
844 	    arg[i++] = c;
845 	  }
846 	  c = *t++;		/* remember delimiter for later */
847 	  arg[i] = '\0';	/* tie off argument */
848 	}
849 	else {			/* non-quoted argument */
850 	  if ((t = strpbrk (v = t,"/:")) != NULL) {
851 	    c = *t;		/* remember delimiter for later */
852 	    *t++ = '\0';	/* tie off switch name */
853 	  }
854 	  else c = '\0';	/* no delimiter */
855 	  i = strlen (v);	/* length of argument */
856 	}
857 	if (!compare_cstring (s,"service") && (i < NETMAXSRV) && !*mb->service)
858 	  lcase (strcpy (mb->service,v));
859 	else if (!compare_cstring (s,"user") && (i < NETMAXUSER) && !*mb->user)
860 	  strcpy (mb->user,v);
861 	else if (!compare_cstring (s,"authuser") && (i < NETMAXUSER) &&
862 		 !*mb->authuser) strcpy (mb->authuser,v);
863 	else if (!compare_cstring (s,"auth") && (i < NETMAXAUTH) &&
864 		 !*mb->auth) strcpy (mb->auth,v);
865 	else return NIL;
866       }
867 
868       else {			/* non-argument switch */
869 	if (!compare_cstring (s,"anonymous")) mb->anoflag = T;
870 	else if (!compare_cstring (s,"debug")) mb->dbgflag = T;
871 	else if (!compare_cstring (s,"readonly")) mb->readonlyflag = T;
872 	else if (!compare_cstring (s,"secure")) mb->secflag = T;
873 	else if (!compare_cstring (s,"norsh")) mb->norsh = T;
874 	else if (!compare_cstring (s,"loser")) mb->loser = T;
875 	else if ((!compare_cstring (s,"starttls") || !compare_cstring (s,"tls")) && !mb->notlsflag)
876 	  mb->tlsflag = T;
877 	else if (!compare_cstring (s,"tls-sslv23") && !mb->notlsflag)
878 	  mb->tlssslv23 = mb->tlsflag = T;
879 	else if ((!compare_cstring (s,"notls") || !compare_cstring(s,"nostarttls")) && !mb->tlsflag)
880 	  mb->notlsflag = T;
881 	else if (!compare_cstring (s,"tryssl"))
882 	  mb->trysslflag = mailssldriver? T : NIL;
883 	else if (mailssldriver && !compare_cstring (s,"ssl") && !mb->tlsflag)
884 	  mb->sslflag = mb->notlsflag = T;
885 	else if (!compare_cstring(s, "tls1")
886 			&& !mb->tls1_1 && !mb->tls1_2 && !mb->tls1_3)
887 	  mb->sslflag = mb->notlsflag = mb->tls1 = T;
888 	else if (!compare_cstring(s, "tls1_1")
889 			&& !mb->tls1 && !mb->tls1_2 && !mb->tls1_3)
890 	  mb->sslflag = mb->notlsflag = mb->tls1_1 = T;
891 	else if (!compare_cstring(s, "tls1_2")
892 			&& !mb->tls1 && !mb->tls1_1 && !mb->tls1_3)
893 	  mb->sslflag = mb->notlsflag = mb->tls1_2 = T;
894 	else if (!compare_cstring(s, "tls1_3")
895 			&& !mb->tls1 && !mb->tls1_1 && !mb->tls1_2)
896 	  mb->sslflag = mb->notlsflag = mb->tls1_3 = T;
897 	else if (mailssldriver && !compare_cstring (s,"novalidate-cert"))
898 	  mb->novalidate = T;
899 				/* hack for compatibility with the past */
900 	else if (mailssldriver && !compare_cstring (s,"validate-cert"));
901 				/* service switches below here */
902 	else if (*mb->service) return NIL;
903 	else if (!compare_cstring (s,"imap") ||
904 		 !compare_cstring (s,"nntp") ||
905 		 !compare_cstring (s,"pop3") ||
906 		 !compare_cstring (s,"smtp") ||
907 		 !compare_cstring (s,"submit"))
908 	  lcase (strcpy (mb->service,s));
909 	else if (!compare_cstring (s,"imap2") ||
910 		 !compare_cstring (s,"imap2bis") ||
911 		 !compare_cstring (s,"imap4") ||
912 		 !compare_cstring (s,"imap4rev1"))
913 	  strcpy (mb->service,"imap");
914 	else if (!compare_cstring (s,"pop"))
915 	  strcpy (mb->service,"pop3");
916 	else return NIL;	/* invalid non-argument switch */
917       }
918       break;
919     default:			/* anything else is bogus */
920       return NIL;
921     } while (c);		/* see if anything more to parse */
922   }
923 				/* default mailbox name */
924   if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX");
925 				/* default service name */
926   if (!*mb->service) strcpy (mb->service,service);
927 				/* /norsh only valid if imap */
928   if (mb->norsh && strcmp (mb->service,"imap")) return NIL;
929   return T;
930 }
931 
932 /* Mail scan mailboxes for string
933  * Accepts: mail stream
934  *	    reference
935  *	    pattern to search
936  *	    contents to search
937  */
938 
mail_scan(MAILSTREAM * stream,char * ref,char * pat,char * contents)939 void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
940 {
941   int remote = ((*pat == '{') || (ref && *ref == '{'));
942   DRIVER *d;
943   if (ref && (strlen (ref) > NETMAXMBX)) {
944     char tmp[MAILTMPLEN];
945     sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
946     MM_LOG (tmp,ERROR);
947     return;
948   }
949   if (strlen (pat) > NETMAXMBX) {
950     char tmp[MAILTMPLEN];
951     sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
952     MM_LOG (tmp,ERROR);
953     return;
954   }
955   if (*pat == '{') ref = NIL;	/* ignore reference if pattern is remote */
956   if (stream) {			/* if have a stream, do it for that stream */
957     if ((d = stream->dtb) && d->scan &&
958 	!(((d->flags & DR_LOCAL) && remote)))
959       (*d->scan) (stream,ref,pat,contents);
960   }
961 				/* otherwise do for all DTB's */
962   else for (d = maildrivers; d; d = d->next)
963     if (d->scan && !((d->flags & DR_DISABLE) ||
964 		     ((d->flags & DR_LOCAL) && remote)))
965       (d->scan) (NIL,ref,pat,contents);
966 }
967 
968 /* Mail list mailboxes
969  * Accepts: mail stream
970  *	    reference
971  *	    pattern to search
972  */
973 
mail_list(MAILSTREAM * stream,char * ref,char * pat)974 void mail_list (MAILSTREAM *stream,char *ref,char *pat)
975 {
976   int remote = ((*pat == '{') || (ref && *ref == '{'));
977   DRIVER *d = maildrivers;
978   if (ref && (strlen (ref) > NETMAXMBX)) {
979     char tmp[MAILTMPLEN];
980     sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
981     MM_LOG (tmp,ERROR);
982     return;
983   }
984   if (strlen (pat) > NETMAXMBX) {
985     char tmp[MAILTMPLEN];
986     sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
987     MM_LOG (tmp,ERROR);
988     return;
989   }
990   if (*pat == '{') ref = NIL;	/* ignore reference if pattern is remote */
991   if (stream && stream->dtb) {	/* if have a stream, do it for that stream */
992     if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
993       (*d->list) (stream,ref,pat);
994   }
995 				/* otherwise do for all DTB's */
996   else do if (!((d->flags & DR_DISABLE) ||
997 		((d->flags & DR_LOCAL) && remote)))
998     (d->list) (NIL,ref,pat);
999   while ((d = d->next) != NULL);	/* until at the end */
1000 }
1001 
1002 /* Mail list subscribed mailboxes
1003  * Accepts: mail stream
1004  *	    pattern to search
1005  */
1006 
mail_lsub(MAILSTREAM * stream,char * ref,char * pat)1007 void mail_lsub (MAILSTREAM *stream,char *ref,char *pat)
1008 {
1009   int remote = ((*pat == '{') || (ref && *ref == '{'));
1010   DRIVER *d = maildrivers;
1011   if (ref && (strlen (ref) > NETMAXMBX)) {
1012     char tmp[MAILTMPLEN];
1013     sprintf (tmp,"Invalid LSUB reference specification: %.80s",ref);
1014     MM_LOG (tmp,ERROR);
1015     return;
1016   }
1017   if (strlen (pat) > NETMAXMBX) {
1018     char tmp[MAILTMPLEN];
1019     sprintf (tmp,"Invalid LSUB pattern specification: %.80s",pat);
1020     MM_LOG (tmp,ERROR);
1021     return;
1022   }
1023   if (*pat == '{') ref = NIL;	/* ignore reference if pattern is remote */
1024   if (stream && stream->dtb) {	/* if have a stream, do it for that stream */
1025     if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
1026       (*d->lsub) (stream,ref,pat);
1027   }
1028 				/* otherwise do for all DTB's */
1029   else do if (!((d->flags & DR_DISABLE) ||
1030 		((d->flags & DR_LOCAL) && remote)))
1031     (d->lsub) (NIL,ref,pat);
1032   while ((d = d->next) != NULL);	/* until at the end */
1033 }
1034 
1035 /* Mail subscribe to mailbox
1036  * Accepts: mail stream
1037  *	    mailbox to add to subscription list
1038  * Returns: T on success, NIL on failure
1039  */
1040 
mail_subscribe(MAILSTREAM * stream,char * mailbox)1041 long mail_subscribe (MAILSTREAM *stream,char *mailbox)
1042 {
1043   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
1044   return factory ?
1045     (factory->subscribe ?
1046      (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL;
1047 }
1048 
1049 
1050 /* Mail unsubscribe to mailbox
1051  * Accepts: mail stream
1052  *	    mailbox to delete from subscription list
1053  * Returns: T on success, NIL on failure
1054  */
1055 
mail_unsubscribe(MAILSTREAM * stream,char * mailbox)1056 long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
1057 {
1058   DRIVER *factory = mail_valid (stream,mailbox,NIL);
1059   return (factory && factory->unsubscribe) ?
1060     (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox);
1061 }
1062 
1063 /* Mail create mailbox
1064  * Accepts: mail stream
1065  *	    mailbox name to create
1066  * Returns: T on success, NIL on failure
1067  */
1068 
mail_create(MAILSTREAM * stream,char * mailbox)1069 long mail_create (MAILSTREAM *stream,char *mailbox)
1070 {
1071   MAILSTREAM *ts;
1072   char *s,*t,tmp[MAILTMPLEN];
1073   size_t i;
1074   DRIVER *d;
1075 				/* never allow names with newlines */
1076   if ((s = strpbrk (mailbox,"\015\012")) != NULL) {
1077     MM_LOG ("Can't create mailbox with such a name",ERROR);
1078     return NIL;
1079   }
1080   if (strlen (mailbox) >= (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
1081     sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ?
1082 	     "invalid remote specification" : "no such mailbox");
1083     MM_LOG (tmp,ERROR);
1084     return NIL;
1085   }
1086 				/* create of INBOX invalid */
1087   if (!compare_cstring (mailbox,"INBOX")) {
1088     MM_LOG ("Can't create INBOX",ERROR);
1089     return NIL;
1090   }
1091 				/* validate name */
1092   if ((s = mail_utf7_valid (mailbox)) != NULL) {
1093     sprintf (tmp,"Can't create %s: %.80s",s,mailbox);
1094     MM_LOG (tmp,ERROR);
1095     return NIL;
1096   }
1097 
1098 				/* see if special driver hack */
1099   if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) &&
1100       ((mailbox[2] == 'r') || (mailbox[2] == 'R')) &&
1101       ((mailbox[3] == 'i') || (mailbox[3] == 'I')) &&
1102       ((mailbox[4] == 'v') || (mailbox[4] == 'V')) &&
1103       ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
1104       ((mailbox[6] == 'r') || (mailbox[6] == 'R')) && (mailbox[7] == '.')) {
1105 				/* copy driver until likely delimiter */
1106     if ((s = strpbrk (t = mailbox+8,"/\\:")) && (i = s - t)) {
1107       strncpy (tmp,t,i);
1108       tmp[i] = '\0';
1109     }
1110     else {
1111       sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox);
1112       MM_LOG (tmp,ERROR);
1113       return NIL;
1114     }
1115     for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next);
1116     if (d) mailbox = ++s;	/* skip past driver specification */
1117     else {
1118       sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox);
1119       MM_LOG (tmp,ERROR);
1120       return NIL;
1121     }
1122   }
1123 				/* use stream if one given or deterministic */
1124   else if ((stream && stream->dtb) ||
1125 	   (((*mailbox == '{') || (*mailbox == '#')) &&
1126 	    (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT))))
1127     d = stream->dtb;
1128   else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb;
1129   else {			/* failed utterly */
1130     sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox);
1131     MM_LOG (tmp,ERROR);
1132     return NIL;
1133   }
1134   return (*d->create) (stream,mailbox);
1135 }
1136 
1137 /* Mail delete mailbox
1138  * Accepts: mail stream
1139  *	    mailbox name to delete
1140  * Returns: T on success, NIL on failure
1141  */
1142 
mail_delete(MAILSTREAM * stream,char * mailbox)1143 long mail_delete (MAILSTREAM *stream,char *mailbox)
1144 {
1145   DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox");
1146   if (!dtb) return NIL;
1147   if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
1148       ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
1149       ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
1150       ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
1151       ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
1152     MM_LOG ("Can't delete INBOX",ERROR);
1153     return NIL;
1154   }
1155   return SAFE_DELETE (dtb,stream,mailbox);
1156 }
1157 
1158 
1159 /* Mail rename mailbox
1160  * Accepts: mail stream
1161  *	    old mailbox name
1162  *	    new mailbox name
1163  * Returns: T on success, NIL on failure
1164  */
1165 
mail_rename(MAILSTREAM * stream,char * old,char * newname)1166 long mail_rename (MAILSTREAM *stream,char *old,char *newname)
1167 {
1168   char *s,tmp[MAILTMPLEN];
1169   DRIVER *dtb = mail_valid (stream,old,"rename mailbox");
1170   if (!dtb) return NIL;
1171 				/* validate name */
1172   if ((s = mail_utf7_valid (newname)) != NULL) {
1173     sprintf (tmp,"Can't rename to %s: %.80s",s,newname);
1174     MM_LOG (tmp,ERROR);
1175     return NIL;
1176   }
1177   if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) {
1178     sprintf (tmp,"Can't rename %.80s: mailbox %.80s already exists",
1179 	     old,newname);
1180     MM_LOG (tmp,ERROR);
1181     return NIL;
1182   }
1183   return SAFE_RENAME (dtb,stream,old,newname);
1184 }
1185 
1186 /* Validate mailbox as Modified UTF-7
1187  * Accepts: candidate mailbox name
1188  * Returns: error string if error, NIL if valid
1189  */
1190 
mail_utf7_valid(char * mailbox)1191 char *mail_utf7_valid (char *mailbox)
1192 {
1193   char *s;
1194   for (s = mailbox; *s; s++) {	/* make sure valid name */
1195 				/* reserved for future use with UTF-8 */
1196     if (*s & 0x80) return "mailbox name with 8-bit octet";
1197 				/* validate modified UTF-7 */
1198     else if (*s == '&') while (*++s != '-') switch (*s) {
1199     case '\0':
1200       return "unterminated modified UTF-7 name";
1201     case '+':			/* valid modified BASE64 */
1202     case ',':
1203       break;			/* all OK so far */
1204     default:			/* must be alphanumeric */
1205       if (!isalnum (*s)) return "invalid modified UTF-7 name";
1206       break;
1207     }
1208   }
1209   return NIL;			/* all OK */
1210 }
1211 
1212 /* Mail status of mailbox
1213  * Accepts: mail stream if open on this mailbox
1214  *	    mailbox name
1215  *	    status flags
1216  * Returns: T on success, NIL on failure
1217  */
1218 
mail_status(MAILSTREAM * stream,char * mbx,long flags)1219 long mail_status (MAILSTREAM *stream,char *mbx,long flags)
1220 {
1221   DRIVER *dtb = mail_valid (stream,mbx,"get status of mailbox");
1222   if (!dtb) return NIL;		/* only if valid */
1223   if (stream && ((dtb != stream->dtb) ||
1224 		 ((dtb->flags & DR_LOCAL) && strcmp (mbx,stream->mailbox) &&
1225 		  strcmp (mbx,stream->original_mailbox))))
1226     stream = NIL;		/* stream not suitable */
1227   return SAFE_STATUS (dtb,stream,mbx,flags);
1228 }
1229 
1230 
1231 /* Mail status of mailbox default handler
1232  * Accepts: mail stream
1233  *	    mailbox name
1234  *	    status flags
1235  * Returns: T on success, NIL on failure
1236  */
1237 
mail_status_default(MAILSTREAM * stream,char * mbx,long flags)1238 long mail_status_default (MAILSTREAM *stream,char *mbx,long flags)
1239 {
1240   MAILSTATUS status;
1241   unsigned long i;
1242   MAILSTREAM *tstream = NIL;
1243 				/* make temporary stream (unless this mbx) */
1244   if (!stream && !(stream = tstream =
1245 		   mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
1246   status.flags = flags;		/* return status values */
1247   status.messages = stream->nmsgs;
1248   status.recent = stream->recent;
1249   if (flags & SA_UNSEEN)	/* must search to get unseen messages */
1250     for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
1251       if (!mail_elt (stream,i)->seen) status.unseen++;
1252   status.uidnext = stream->uid_last + 1;
1253   status.uidvalidity = stream->uid_validity;
1254   MM_STATUS(stream,mbx,&status);/* pass status to main program */
1255   if (tstream) mail_close (tstream);
1256   return T;			/* success */
1257 }
1258 
1259 /* Mail renew stream
1260  * Accepts: stream to renew
1261  * returns: 0 for success, 1 for failure
1262  */
mail_renew_stream(MAILSTREAM * stream)1263 long mail_renew_stream (MAILSTREAM *stream)
1264 {
1265   MAILSTREAM *m = mail_open(NIL, stream->original_mailbox, OP_SILENT);
1266   long rv = stream && m ? (stream->dtb->renew)(stream, m) : 1;
1267   mail_close(m);
1268   return rv;
1269 }
1270 /* Mail open
1271  * Accepts: candidate stream for recycling
1272  *	    mailbox name
1273  *	    open options
1274  * Returns: stream to use on success, NIL on failure
1275  */
1276 
mail_open(MAILSTREAM * stream,char * name,long options)1277 MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
1278 {
1279   int i;
1280   char c,*s,tmp[MAILTMPLEN];
1281   NETMBX mb;
1282   DRIVER *d;
1283   switch (name[0]) {		/* see if special handling */
1284   case '#':			/* possible special hacks */
1285     if (((name[1] == 'M') || (name[1] == 'm')) &&
1286 	((name[2] == 'O') || (name[2] == 'o')) &&
1287 	((name[3] == 'V') || (name[3] == 'v')) &&
1288 	((name[4] == 'E') || (name[4] == 'e')) && (c = name[5]) &&
1289 	(s = strchr (name+6,c)) && (i = s - (name + 6)) && (i < MAILTMPLEN)) {
1290       if ((stream = mail_open (stream,s+1,options)) != NULL) {
1291 	strncpy (tmp,name+6,i);	/* copy snarf mailbox name */
1292 	tmp[i] = '\0';		/* tie off name */
1293 	mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
1294 	stream->snarf.options = options;
1295 	mail_ping (stream);	/* do initial snarf */
1296 				/* punt if can't do initial snarf */
1297 	if (!stream->snarf.time) stream = mail_close (stream);
1298       }
1299       return stream;
1300     }
1301 				/* special POP hack */
1302     else if (((name[1] == 'P') || (name[1] == 'p')) &&
1303 	     ((name[2] == 'O') || (name[2] == 'o')) &&
1304 	     ((name[3] == 'P') || (name[3] == 'p')) &&
1305 	     mail_valid_net_parse_work (name+4,&mb,"pop3") &&
1306 	!strcmp (mb.service,"pop3") && !mb.anoflag && !mb.readonlyflag) {
1307       if ((stream = mail_open (stream,mb.mailbox,options)) != NULL) {
1308 	sprintf (tmp,"{%.255s",mb.host);
1309 	if (mb.port) sprintf (tmp + strlen (tmp),":%lu",mb.port);
1310 	if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=%.64s",mb.user);
1311 	if (mb.dbgflag) strcat (tmp,"/debug");
1312 	if (mb.secflag) strcat (tmp,"/secure");
1313 	if (mb.tlsflag) strcat (tmp,"/starttls");
1314 	if (mb.notlsflag) strcat (tmp,"/notls");
1315 	if (mb.sslflag) strcat (tmp,"/ssl");
1316 	if (mb.tls1) strcat (tmp,"/tls1");
1317 	if (mb.tls1_1) strcat (tmp,"/tls1_1");
1318 	if (mb.tls1_2) strcat (tmp,"/tls1_2");
1319 	if (mb.tls1_3) strcat (tmp,"/tls1_3");
1320 	if (mb.trysslflag) strcat (tmp,"/tryssl");
1321 	if (mb.novalidate) strcat (tmp,"/novalidate-cert");
1322 	strcat (tmp,"/pop3/loser}");
1323 	mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
1324 	mail_ping (stream);	/* do initial snarf */
1325       }
1326       return stream;		/* return local mailbox stream */
1327     }
1328 
1329     else if ((options & OP_PROTOTYPE) &&
1330 	     ((name[1] == 'D') || (name[1] == 'd')) &&
1331 	     ((name[2] == 'R') || (name[2] == 'r')) &&
1332 	     ((name[3] == 'I') || (name[3] == 'i')) &&
1333 	     ((name[4] == 'V') || (name[4] == 'v')) &&
1334 	     ((name[5] == 'E') || (name[5] == 'e')) &&
1335 	     ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) {
1336       sprintf (tmp,"%.80s",name+8);
1337 				/* tie off name at likely delimiter */
1338       if ((s = strpbrk (tmp,"/\\:")) != NULL) *s++ = '\0';
1339       else {
1340 	sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name);
1341 	MM_LOG (tmp,ERROR);
1342 	return mail_close (stream);
1343       }
1344       for (d = maildrivers; d && compare_cstring (d->name,tmp); d = d->next);
1345       if (d) return (*d->open) (NIL);
1346       sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name);
1347       MM_LOG (tmp,ERROR);
1348       return mail_close (stream);
1349     }
1350 				/* fall through to default case */
1351   default:			/* not special hack (but could be # name */
1352     d = mail_valid (NIL,name,(options & OP_SILENT) ?
1353 		    (char *) NIL : "open mailbox");
1354   }
1355   return d ? mail_open_work (d,stream,name,options) : stream;
1356 }
1357 
1358 /* Mail open worker routine
1359  * Accepts: factory
1360  *	    candidate stream for recycling
1361  *	    mailbox name
1362  *	    open options
1363  * Returns: stream to use on success, NIL on failure
1364  */
1365 
mail_open_work(DRIVER * d,MAILSTREAM * stream,char * name,long options)1366 MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
1367 			    long options)
1368 {
1369   int i;
1370   char tmp[MAILTMPLEN];
1371   NETMBX mb;
1372   if (options & OP_PROTOTYPE) return (*d->open) (NIL);
1373   /* name is copied here in case the caller does a re-open using
1374    * stream->mailbox or stream->original_mailbox as the argument.
1375    */
1376   name = cpystr (name);		/* make copy of name */
1377   if (stream) {			/* recycling requested? */
1378     if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
1379 	((d->flags & DR_HALFOPEN) || !(options & OP_HALFOPEN)) &&
1380 	mail_usable_network_stream (stream,name)) {
1381 				/* yes, checkpoint if needed */
1382       if (d->flags & DR_XPOINT) mail_check (stream);
1383       mail_free_cache (stream);	/* clean up stream */
1384       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
1385       if (stream->original_mailbox)
1386 	fs_give ((void **) &stream->original_mailbox);
1387 				/* flush user flags */
1388       for (i = 0; i < NUSERFLAGS; i++)
1389 	if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
1390     }
1391     else {			/* stream not recycleable, babble if net */
1392       if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) &&
1393 	  mail_valid_net_parse (stream->mailbox,&mb)) {
1394 	sprintf (tmp,"Closing connection to %.80s",mb.host);
1395 	MM_LOG (tmp,(long) NIL);
1396       }
1397 				/* flush the old stream */
1398       stream = mail_close (stream);
1399     }
1400   }
1401 				/* check if driver does not support halfopen */
1402   else if ((options & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)) {
1403     fs_give ((void **) &name);
1404     return NIL;
1405   }
1406 
1407 				/* instantiate new stream if not recycling */
1408   if (!stream) (*mailcache) (stream = (MAILSTREAM *)
1409 			     memset (fs_get (sizeof (MAILSTREAM)),0,
1410 				     sizeof (MAILSTREAM)),(long) 0,CH_INIT);
1411   stream->dtb = d;		/* set dispatch */
1412 				/* set mailbox name */
1413   stream->mailbox = cpystr (stream->original_mailbox = name);
1414 				/* initialize stream flags */
1415   stream->inbox = stream->lock = NIL;
1416   stream->debug = (options & OP_DEBUG) ? T : NIL;
1417   stream->rdonly = (options & OP_READONLY) ? T : NIL;
1418   stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
1419   stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
1420   stream->silent = (options & OP_SILENT) ? T : NIL;
1421   stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
1422   stream->secure = (options & OP_SECURE) ? T : NIL;
1423   stream->tryssl = (options & OP_TRYSSL) ? T : NIL;
1424   stream->mulnewsrc = (options & OP_MULNEWSRC) ? T : NIL;
1425   stream->nokod = (options & OP_NOKOD) ? T : NIL;
1426   stream->sniff = (options & OP_SNIFF) ? T : NIL;
1427   stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
1428     stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL;
1429   stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL;
1430   stream->uid_last = 0;		/* default UID validity */
1431   stream->uid_validity = (unsigned long) time (0);
1432 				/* have driver open, flush if failed */
1433   return ((*d->open) (stream)) ? stream : mail_close (stream);
1434 }
1435 
1436 /* Mail close
1437  * Accepts: mail stream
1438  *	    close options
1439  * Returns: NIL, always
1440  */
1441 
mail_close_full(MAILSTREAM * stream,long options)1442 MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
1443 {
1444   int i;
1445   if (stream) {			/* make sure argument given */
1446 				/* do the driver's close action */
1447     if (stream->dtb) (*stream->dtb->close) (stream,options);
1448     stream->dtb = NIL;		/* resign driver */
1449     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
1450     if (stream->auth.name) fs_give ((void **) &stream->auth.name);
1451     if (stream->original_mailbox)
1452       fs_give ((void **) &stream->original_mailbox);
1453     if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
1454     stream->sequence++;		/* invalidate sequence */
1455 				/* flush user flags */
1456     for (i = 0; i < NUSERFLAGS; i++)
1457       if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
1458     mail_free_cache (stream);	/* finally free the stream's storage */
1459     if (mailfreestreamsparep && stream->sparep)
1460       (*mailfreestreamsparep) (&stream->sparep);
1461     if (!stream->use) fs_give ((void **) &stream);
1462   }
1463   return NIL;
1464 }
1465 
1466 /* Mail make handle
1467  * Accepts: mail stream
1468  * Returns: handle
1469  *
1470  *  Handles provide a way to have multiple pointers to a stream yet allow the
1471  * stream's owner to nuke it or recycle it.
1472  */
1473 
mail_makehandle(MAILSTREAM * stream)1474 MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
1475 {
1476   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
1477   handle->stream = stream;	/* copy stream */
1478 				/* and its sequence */
1479   handle->sequence = stream->sequence;
1480   stream->use++;		/* let stream know another handle exists */
1481   return handle;
1482 }
1483 
mail_free_idlist(IDLIST ** idlist)1484 void mail_free_idlist (IDLIST **idlist)
1485 {
1486   if (idlist && *idlist){
1487      if((*idlist)->name) fs_give((void **)&(*idlist)->name);
1488      if((*idlist)->value) fs_give((void **)&(*idlist)->value);
1489      if((*idlist)->next) mail_free_idlist(&(*idlist)->next);
1490      fs_give((void **) idlist);
1491   }
1492 }
1493 
1494 
1495 /* Mail release handle
1496  * Accepts: Mail handle
1497  */
1498 
mail_free_handle(MAILHANDLE ** handle)1499 void mail_free_handle (MAILHANDLE **handle)
1500 {
1501   MAILSTREAM *s;
1502   if (*handle) {		/* only free if exists */
1503 				/* resign stream, flush unreferenced zombies */
1504     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
1505     fs_give ((void **) handle);	/* now flush the handle */
1506   }
1507 }
1508 
1509 
1510 /* Mail get stream handle
1511  * Accepts: Mail handle
1512  * Returns: mail stream or NIL if stream gone
1513  */
1514 
mail_stream(MAILHANDLE * handle)1515 MAILSTREAM *mail_stream (MAILHANDLE *handle)
1516 {
1517   MAILSTREAM *s = handle->stream;
1518   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
1519 }
1520 
1521 /* Mail fetch cache element
1522  * Accepts: mail stream
1523  *	    message # to fetch
1524  * Returns: cache element of this message
1525  * Can also be used to create cache elements for new messages.
1526  */
1527 
mail_elt(MAILSTREAM * stream,unsigned long msgno)1528 MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
1529 {
1530   if (msgno < 1 || msgno > stream->nmsgs) {
1531     char tmp[MAILTMPLEN];
1532     sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu, mbx=%.80s",
1533 	     msgno,stream->nmsgs,stream->mailbox ? stream->mailbox : "???");
1534     fatal (tmp);
1535   }
1536   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
1537 }
1538 
1539 
1540 /* Mail fetch fast information
1541  * Accepts: mail stream
1542  *	    sequence
1543  *	    option flags
1544  *
1545  * Generally, mail_fetch_structure is preferred
1546  */
1547 
mail_fetch_fast(MAILSTREAM * stream,char * sequence,long flags)1548 void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags)
1549 {
1550   				/* do the driver's action */
1551   if (stream->dtb && stream->dtb->fast)
1552     (*stream->dtb->fast) (stream,sequence,flags);
1553 }
1554 
1555 
1556 /* Mail fetch flags
1557  * Accepts: mail stream
1558  *	    sequence
1559  *	    option flags
1560  */
1561 
mail_fetch_flags(MAILSTREAM * stream,char * sequence,long flags)1562 void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags)
1563 {
1564   				/* do the driver's action */
1565   if (stream->dtb && stream->dtb->msgflags)
1566     (*stream->dtb->msgflags) (stream,sequence,flags);
1567 }
1568 
1569 /* Mail fetch message overview
1570  * Accepts: mail stream
1571  *	    UID sequence to fetch
1572  *	    pointer to overview return function
1573  */
1574 
mail_fetch_overview(MAILSTREAM * stream,char * sequence,overview_t ofn)1575 void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
1576 {
1577   if (stream->dtb && mail_uid_sequence (stream,sequence) &&
1578       !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
1579       mail_ping (stream))
1580     mail_fetch_overview_default (stream,ofn);
1581 }
1582 
1583 
1584 /* Mail fetch message overview using sequence numbers instead of UIDs
1585  * Accepts: mail stream
1586  *	    sequence to fetch
1587  *	    pointer to overview return function
1588  */
1589 
mail_fetch_overview_sequence(MAILSTREAM * stream,char * sequence,overview_t ofn)1590 void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence,
1591 				   overview_t ofn)
1592 {
1593   if (stream->dtb && mail_sequence (stream,sequence) &&
1594       !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
1595       mail_ping (stream))
1596     mail_fetch_overview_default (stream,ofn);
1597 }
1598 
1599 
1600 /* Mail fetch message overview default handler
1601  * Accepts: mail stream with sequence bits lit
1602  *	    pointer to overview return function
1603  */
1604 
mail_fetch_overview_default(MAILSTREAM * stream,overview_t ofn)1605 void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn)
1606 {
1607   MESSAGECACHE *elt;
1608   ENVELOPE *env;
1609   OVERVIEW ov;
1610   unsigned long i;
1611   ov.optional.lines = 0;
1612   ov.optional.xref = NIL;
1613   for (i = 1; i <= stream->nmsgs; i++)
1614     if (((elt = mail_elt (stream,i))->sequence) &&
1615 	(env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
1616       ov.subject = env->subject;
1617       ov.from = env->from;
1618       ov.date = env->date;
1619       ov.message_id = env->message_id;
1620       ov.references = env->references;
1621       ov.optional.octets = elt->rfc822_size;
1622       (*ofn) (stream,mail_uid (stream,i),&ov,i);
1623     }
1624 }
1625 
1626 /* Mail fetch message structure
1627  * Accepts: mail stream
1628  *	    message # to fetch
1629  *	    pointer to return body
1630  *	    option flags
1631  * Returns: envelope of this message, body returned in body value
1632  *
1633  * Fetches the "fast" information as well
1634  */
1635 
mail_fetch_structure(MAILSTREAM * stream,unsigned long msgno,BODY ** body,long flags)1636 ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
1637 				BODY **body,long flags)
1638 {
1639   ENVELOPE **env;
1640   BODY **b;
1641   MESSAGECACHE *elt;
1642   char c,*s,*hdr;
1643   unsigned long hdrsize;
1644   STRING bs;
1645 				/* do the driver's action if specified */
1646   if (stream->dtb && stream->dtb->structure)
1647     return (*stream->dtb->structure) (stream,msgno,body,flags);
1648   if (flags & FT_UID) {		/* UID form of call */
1649     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
1650     else return NIL;		/* must get UID/msgno map first */
1651   }
1652   elt = mail_elt (stream,msgno);/* get elt for real message number */
1653   if (stream->scache) {		/* short caching */
1654     if (msgno != stream->msgno){/* garbage collect if not same message */
1655       mail_gc (stream,GC_ENV | GC_TEXTS);
1656       stream->msgno = msgno;	/* this is the current message now */
1657     }
1658     env = &stream->env;		/* get pointers to envelope and body */
1659     b = &stream->body;
1660   }
1661   else {			/* get pointers to elt envelope and body */
1662     env = &elt->private.msg.env;
1663     b = &elt->private.msg.body;
1664   }
1665 
1666   if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) {
1667     mail_free_envelope (env);	/* flush old envelope and body */
1668     mail_free_body (b);
1669 				/* see if need to fetch the whole thing */
1670     if (body || !elt->rfc822_size) {
1671       s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL);
1672 				/* make copy in case body fetch smashes it */
1673       hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize);
1674       hdr[hdrsize] = '\0';	/* tie off header */
1675       (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK);
1676       if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs);
1677       if (body)			/* only parse body if requested */
1678 	rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags);
1679       else
1680 	rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
1681       fs_give ((void **) &hdr);	/* flush header */
1682     }
1683     else {			/* can save memory doing it this way */
1684       hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL);
1685       if (hdrsize) {		/* in case null header */
1686 	c = hdr[hdrsize];	/* preserve what's there */
1687 	hdr[hdrsize] = '\0';	/* tie off header */
1688 	rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
1689 	hdr[hdrsize] = c;	/* restore in case cached data */
1690       }
1691       else *env = mail_newenvelope ();
1692     }
1693   }
1694 				/* if need date, have date in envelope? */
1695   if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date);
1696 				/* sigh, fill in bogus default */
1697   if (!elt->day) elt->day = elt->month = 1;
1698   if (body) *body = *b;		/* return the body */
1699   return *env;			/* return the envelope */
1700 }
1701 
1702 /* Mail mark single message (internal use only)
1703  * Accepts: mail stream
1704  *	    elt to mark
1705  *	    fetch flags
1706  */
1707 
markseen(MAILSTREAM * stream,MESSAGECACHE * elt,long flags)1708 static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags)
1709 {
1710   unsigned long i;
1711   char sequence[20];
1712   MESSAGECACHE *e;
1713 				/* non-peeking and needs to set \Seen? */
1714   if (!(flags & FT_PEEK) && !elt->seen) {
1715     if (stream->dtb->flagmsg){	/* driver wants per-message call? */
1716       elt->valid = NIL;		/* do pre-alteration driver call */
1717       (*stream->dtb->flagmsg) (stream,elt);
1718 				/* set seen, do post-alteration driver call */
1719       elt->seen = elt->valid = T;
1720       (*stream->dtb->flagmsg) (stream,elt);
1721     }
1722     if (stream->dtb->flag) {	/* driver wants one-time call?  */
1723 				/* better safe than sorry, save seq bits */
1724       for (i = 1; i <= stream->nmsgs; i++) {
1725 	e = mail_elt (stream,i);
1726 	e->private.sequence = e->sequence;
1727       }
1728 				/* call driver to set the message */
1729       sprintf (sequence,"%lu",elt->msgno);
1730       (*stream->dtb->flag) (stream,sequence,"\\Seen",ST_SET);
1731 				/* restore sequence bits */
1732       for (i = 1; i <= stream->nmsgs; i++) {
1733 	e = mail_elt (stream,i);
1734 	e->sequence = e->private.sequence;
1735       }
1736     }
1737 				/* notify mail program of flag change */
1738     MM_FLAGS (stream,elt->msgno);
1739   }
1740 }
1741 
1742 /* Mail fetch message
1743  * Accepts: mail stream
1744  *	    message # to fetch
1745  *	    pointer to returned length
1746  *	    flags
1747  * Returns: message text
1748  */
1749 
mail_fetch_message(MAILSTREAM * stream,unsigned long msgno,unsigned long * len,long flags)1750 char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
1751 			  unsigned long *len,long flags)
1752 {
1753   GETS_DATA md;
1754   SIZEDTEXT *t;
1755   STRING bs;
1756   MESSAGECACHE *elt;
1757   char *s,*u;
1758   unsigned long i,j;
1759   if (len) *len = 0;		/* default return size */
1760   if (flags & FT_UID) {		/* UID form of call */
1761     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
1762     else return "";		/* must get UID/msgno map first */
1763   }
1764 				/* initialize message data identifier */
1765   INIT_GETS (md,stream,msgno,"",0,0);
1766 				/* is data already cached? */
1767   if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) {
1768     markseen (stream,elt,flags);/* mark message seen */
1769     return mail_fetch_text_return (&md,t,len);
1770   }
1771   if (!stream->dtb) return "";	/* not in cache, must have live driver */
1772   if (stream->dtb->msgdata) return
1773     ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ?
1774       mail_fetch_text_return (&md,t,len) : "";
1775 				/* ugh, have to do this the crufty way */
1776   u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags);
1777 				/* copy in case text method stomps on it */
1778   s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i);
1779   if ((*stream->dtb->text) (stream,msgno,&bs,flags)) {
1780     t = &stream->text;		/* build combined copy */
1781     if (t->data) fs_give ((void **) &t->data);
1782     t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1);
1783     if (!elt->rfc822_size) elt->rfc822_size = t->size;
1784     else if (elt->rfc822_size != t->size) {
1785       char tmp[MAILTMPLEN];
1786       sprintf (tmp,"Calculated RFC822.SIZE (%lu) != reported size (%lu)",
1787 	       t->size,elt->rfc822_size);
1788       mm_log (tmp,WARN);	/* bug trap */
1789     }
1790     memcpy (t->data,s,(size_t) i);
1791     for (u = (char *) t->data + i, j = SIZE (&bs); j;) {
1792       memcpy (u,bs.curpos,bs.cursize);
1793       u += bs.cursize;		/* update text */
1794       j -= bs.cursize;
1795       bs.curpos += (bs.cursize -1);
1796       bs.cursize = 0;
1797       (*bs.dtb->next) (&bs);	/* advance to next buffer's worth */
1798     }
1799     *u = '\0';			/* tie off data */
1800     u = mail_fetch_text_return (&md,t,len);
1801   }
1802   else u = "";
1803   fs_give ((void **) &s);	/* finished with copy of header */
1804   return u;
1805 }
1806 
1807 /* Mail fetch message header
1808  * Accepts: mail stream
1809  *	    message # to fetch
1810  *	    MIME section specifier (#.#.#...#)
1811  *	    list of lines to fetch
1812  *	    pointer to returned length
1813  *	    flags
1814  * Returns: message header in RFC822 format
1815  *
1816  * Note: never calls a mailgets routine
1817  */
1818 
mail_fetch_header(MAILSTREAM * stream,unsigned long msgno,char * section,STRINGLIST * lines,unsigned long * len,long flags)1819 char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
1820 			 STRINGLIST *lines,unsigned long *len,long flags)
1821 {
1822   STRING bs;
1823   BODY *b = NIL;
1824   SIZEDTEXT *t = NIL,rt;
1825   MESSAGE *m = NIL;
1826   MESSAGECACHE *elt;
1827   char tmp[MAILTMPLEN];
1828   if (len) *len = 0;		/* default return size */
1829   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
1830   if (flags & FT_UID) {		/* UID form of call */
1831     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
1832     else return "";		/* must get UID/msgno map first */
1833   }
1834   elt = mail_elt (stream,msgno);/* get cache data */
1835   if (section && *section) {	/* nested body header wanted? */
1836     if (!((b = mail_body (stream,msgno,section)) &&
1837 	  (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
1838       return "";		/* lose if no body or not MESSAGE/RFC822 */
1839     m = b->nested.msg;		/* point to nested message */
1840   }
1841 				/* else top-level message header wanted */
1842   else m = &elt->private.msg;
1843   if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) {
1844     if (lines) textcpy (t = &stream->text,&m->header.text);
1845     else t = &m->header.text;	/* in cache, and cache is valid */
1846     markseen (stream,elt,flags);/* mark message seen */
1847   }
1848 
1849   else if (stream->dtb) {	/* not in cache, has live driver? */
1850     if (stream->dtb->msgdata) {	/* has driver section fetch? */
1851 				/* build driver section specifier */
1852       if (section && *section) sprintf (tmp,"%s.HEADER",section);
1853       else strcpy (tmp,"HEADER");
1854       if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) {
1855 	t = &m->header.text;	/* fetch data */
1856 				/* don't need to postprocess lines */
1857 	if (m->lines) lines = NIL;
1858 	else if (lines) textcpy (t = &stream->text,&m->header.text);
1859       }
1860     }
1861     else if (b) {		/* nested body wanted? */
1862       if (stream->private.search.text) {
1863 	rt.data = (unsigned char *) stream->private.search.text +
1864 	  b->nested.msg->header.offset;
1865 	rt.size = b->nested.msg->header.text.size;
1866 	t = &rt;
1867       }
1868       else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) {
1869 	if ((bs.dtb->next == mail_string_next) && !lines) {
1870 	  rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset;
1871 	  rt.size = b->nested.msg->header.text.size;
1872 	  if (stream->private.search.string)
1873 	    stream->private.search.text = bs.curpos;
1874 	  t = &rt;		/* special hack to avoid extra copy */
1875 	}
1876 	else textcpyoffstring (t = &stream->text,&bs,
1877 			       b->nested.msg->header.offset,
1878 			       b->nested.msg->header.text.size);
1879       }
1880     }
1881     else {			/* top-level header fetch */
1882 				/* mark message seen */
1883       markseen (stream,elt,flags);
1884       if ((rt.data = (unsigned char *)
1885 	  (*stream->dtb->header) (stream,msgno,&rt.size,flags)) != NULL) {
1886 				/* make a safe copy if need to filter */
1887 	if (lines) textcpy (t = &stream->text,&rt);
1888 	else t = &rt;		/* top level header */
1889       }
1890     }
1891   }
1892   if (!t || !t->data) return "";/* error if no string */
1893 				/* filter headers if requested */
1894   if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags);
1895   if (len) *len = t->size;	/* return size if requested */
1896   return (char *) t->data;	/* and text */
1897 }
1898 
1899 /* Mail fetch message text
1900  * Accepts: mail stream
1901  *	    message # to fetch
1902  *	    MIME section specifier (#.#.#...#)
1903  *	    pointer to returned length
1904  *	    flags
1905  * Returns: message text
1906  */
1907 
mail_fetch_text(MAILSTREAM * stream,unsigned long msgno,char * section,unsigned long * len,long flags)1908 char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
1909 		       unsigned long *len,long flags)
1910 {
1911   GETS_DATA md;
1912   PARTTEXT *p;
1913   STRING bs;
1914   MESSAGECACHE *elt;
1915   BODY *b = NIL;
1916   char tmp[MAILTMPLEN];
1917   unsigned long i;
1918   if (len) *len = 0;		/* default return size */
1919   memset (&stream->private.string,NIL,sizeof (STRING));
1920   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
1921   if (flags & FT_UID) {		/* UID form of call */
1922     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
1923     else return "";		/* must get UID/msgno map first */
1924   }
1925   elt = mail_elt (stream,msgno);/* get cache data */
1926   if (section && *section) {	/* nested body text wanted? */
1927     if (!((b = mail_body (stream,msgno,section)) &&
1928 	  (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
1929       return "";		/* lose if no body or not MESSAGE/RFC822 */
1930     p = &b->nested.msg->text;	/* point at nested message */
1931 				/* build IMAP-format section specifier */
1932     sprintf (tmp,"%s.TEXT",section);
1933     flags &= ~FT_INTERNAL;	/* can't win with this set */
1934   }
1935   else {			/* top-level message text wanted */
1936     p = &elt->private.msg.text;
1937     strcpy (tmp,"TEXT");
1938   }
1939 				/* initialize message data identifier */
1940   INIT_GETS (md,stream,msgno,section,0,0);
1941   if (p->text.data) {		/* is data already cached? */
1942     markseen (stream,elt,flags);/* mark message seen */
1943     return mail_fetch_text_return (&md,&p->text,len);
1944   }
1945   if (!stream->dtb) return "";	/* not in cache, must have live driver */
1946   if (stream->dtb->msgdata) return
1947     ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)?
1948       mail_fetch_text_return (&md,&p->text,len) : "";
1949   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return "";
1950   if (section && *section) {	/* nested is more complex */
1951     SETPOS (&bs,p->offset);
1952     i = p->text.size;		/* just want this much */
1953   }
1954   else i = SIZE (&bs);		/* want entire text */
1955   return mail_fetch_string_return (&md,&bs,i,len,flags);
1956 }
1957 
1958 /* Mail fetch message body part MIME headers
1959  * Accepts: mail stream
1960  *	    message # to fetch
1961  *	    MIME section specifier (#.#.#...#)
1962  *	    pointer to returned length
1963  *	    flags
1964  * Returns: message text
1965  */
1966 
mail_fetch_mime(MAILSTREAM * stream,unsigned long msgno,char * section,unsigned long * len,long flags)1967 char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
1968 		       unsigned long *len,long flags)
1969 {
1970   PARTTEXT *p;
1971   STRING bs;
1972   BODY *b;
1973   char tmp[MAILTMPLEN];
1974   if (len) *len = 0;		/* default return size */
1975   if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
1976   if (flags & FT_UID) {		/* UID form of call */
1977     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
1978     else return "";		/* must get UID/msgno map first */
1979   }
1980   flags &= ~FT_INTERNAL;	/* can't win with this set */
1981   if (!(section && *section && (b = mail_body (stream,msgno,section))))
1982     return "";			/* not valid section */
1983 				/* in cache? */
1984   if ((p = &b->mime)->text.data) {
1985 				/* mark message seen */
1986     markseen (stream,mail_elt (stream,msgno),flags);
1987     if (len) *len = p->text.size;
1988     return (char *) p->text.data;
1989   }
1990   if (!stream->dtb) return "";	/* not in cache, must have live driver */
1991   if (stream->dtb->msgdata) {	/* has driver fetch? */
1992 				/* build driver section specifier */
1993     sprintf (tmp,"%s.MIME",section);
1994     if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) &&
1995 	p->text.data) {
1996       if (len) *len = p->text.size;
1997       return (char *) p->text.data;
1998     }
1999     else return "";
2000   }
2001   if (len) *len = b->mime.text.size;
2002   if (!b->mime.text.size) {	/* empty MIME header -- mark seen anyway */
2003     markseen (stream,mail_elt (stream,msgno),flags);
2004     return "";
2005   }
2006 				/* have to get it from offset */
2007   if (stream->private.search.text)
2008     return stream->private.search.text + b->mime.offset;
2009   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
2010     if (len) *len = 0;
2011     return "";
2012   }
2013   if (bs.dtb->next == mail_string_next) {
2014     if (stream->private.search.string) stream->private.search.text = bs.curpos;
2015     return bs.curpos + b->mime.offset;
2016   }
2017   return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size);
2018 }
2019 
2020 /* Mail fetch message body part
2021  * Accepts: mail stream
2022  *	    message # to fetch
2023  *	    MIME section specifier (#.#.#...#)
2024  *	    pointer to returned length
2025  *	    flags
2026  * Returns: message body
2027  */
2028 
mail_fetch_body(MAILSTREAM * stream,unsigned long msgno,char * section,unsigned long * len,long flags)2029 char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
2030 		       unsigned long *len,long flags)
2031 {
2032   GETS_DATA md;
2033   PARTTEXT *p;
2034   STRING bs;
2035   BODY *b;
2036   SIZEDTEXT *t;
2037   char *s,tmp[MAILTMPLEN];
2038   memset (&stream->private.string,NIL,sizeof (STRING));
2039   if (!(section && *section))	/* top-level text wanted? */
2040     return mail_fetch_message (stream,msgno,len,flags);
2041   else if (strlen (section) > (MAILTMPLEN - 20)) return "";
2042   flags &= ~FT_INTERNAL;	/* can't win with this set */
2043 				/* initialize message data identifier */
2044   INIT_GETS (md,stream,msgno,section,0,0);
2045 				/* kludge for old section 0 header */
2046   if (!strcmp (s = strcpy (tmp,section),"0") ||
2047       ((s = strstr (tmp,".0")) && !s[2])) {
2048     SIZEDTEXT ht;
2049     *s = '\0';			/* tie off section */
2050 				/* this silly way so it does mailgets */
2051     ht.data = (unsigned char *) mail_fetch_header (stream,msgno,
2052 						   tmp[0] ? tmp : NIL,NIL,
2053 						   &ht.size,flags);
2054 				/* may have UIDs here */
2055     md.flags = (flags & FT_UID) ? MG_UID : NIL;
2056     return mail_fetch_text_return (&md,&ht,len);
2057   }
2058   if (len) *len = 0;		/* default return size */
2059   if (flags & FT_UID) {		/* UID form of call */
2060     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
2061     else return "";		/* must get UID/msgno map first */
2062   }
2063 				/* must have body */
2064   if (!(b = mail_body (stream,msgno,section))) return "";
2065 				/* have cached text? */
2066   if ((t = &(p = &b->contents)->text)->data) {
2067 				/* mark message seen */
2068     markseen (stream,mail_elt (stream,msgno),flags);
2069     return mail_fetch_text_return (&md,t,len);
2070   }
2071   if (!stream->dtb) return "";	/* not in cache, must have live driver */
2072   if (stream->dtb->msgdata) return
2073     ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ?
2074       mail_fetch_text_return (&md,t,len) : "";
2075   if (len) *len = t->size;
2076   if (!t->size) {		/* empty body part -- mark seen anyway */
2077     markseen (stream,mail_elt (stream,msgno),flags);
2078     return "";
2079   }
2080 				/* copy body from stringstruct offset */
2081   if (stream->private.search.text)
2082     return stream->private.search.text + p->offset;
2083   if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
2084     if (len) *len = 0;
2085     return "";
2086   }
2087   if (bs.dtb->next == mail_string_next) {
2088     if (stream->private.search.string) stream->private.search.text = bs.curpos;
2089     return bs.curpos + p->offset;
2090   }
2091   SETPOS (&bs,p->offset);
2092   return mail_fetch_string_return (&md,&bs,t->size,len,flags);
2093 }
2094 
2095 /* Mail fetch partial message text
2096  * Accepts: mail stream
2097  *	    message # to fetch
2098  *	    MIME section specifier (#.#.#...#)
2099  *	    offset of first designed byte or 0 to start at beginning
2100  *	    maximum number of bytes or 0 for all bytes
2101  *	    flags
2102  * Returns: T if successful, else NIL
2103  */
2104 
mail_partial_text(MAILSTREAM * stream,unsigned long msgno,char * section,unsigned long first,unsigned long last,long flags)2105 long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
2106 			unsigned long first,unsigned long last,long flags)
2107 {
2108   GETS_DATA md;
2109   PARTTEXT *p = NIL;
2110   MESSAGECACHE *elt;
2111   STRING bs;
2112   BODY *b;
2113   char tmp[MAILTMPLEN];
2114   unsigned long i;
2115   if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
2116   if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
2117   if (flags & FT_UID) {		/* UID form of call */
2118     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
2119     else return NIL;		/* must get UID/msgno map first */
2120   }
2121   elt = mail_elt (stream,msgno);/* get cache data */
2122   flags &= ~FT_INTERNAL;	/* bogus if this is set */
2123   if (section && *section) {	/* nested body text wanted? */
2124     if (!((b = mail_body (stream,msgno,section)) &&
2125 	  (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
2126       return NIL;		/* lose if no body or not MESSAGE/RFC822 */
2127     p = &b->nested.msg->text;	/* point at nested message */
2128 				/* build IMAP-format section specifier */
2129     sprintf (tmp,"%s.TEXT",section);
2130   }
2131   else {			/* else top-level message text wanted */
2132     p = &elt->private.msg.text;
2133     strcpy (tmp,"TEXT");
2134   }
2135 
2136 				/* initialize message data identifier */
2137   INIT_GETS (md,stream,msgno,tmp,first,last);
2138   if (p->text.data) {		/* is data already cached? */
2139     INIT (&bs,mail_string,p->text.data,i = p->text.size);
2140     markseen (stream,elt,flags);/* mark message seen */
2141   }
2142   else {			/* else get data from driver */
2143     if (!stream->dtb) return NIL;
2144     if (stream->dtb->msgdata)	/* driver will handle this */
2145       return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags);
2146     if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
2147     if (section && *section) {	/* nexted if more complex */
2148       SETPOS (&bs,p->offset);	/* offset stringstruct to data */
2149       i = p->text.size;		/* maximum size of data */
2150     }
2151     else i = SIZE (&bs);	/* just want this much */
2152   }
2153   if (i <= first) i = first = 0;/* first byte is beyond end of text */
2154 				/* truncate as needed */
2155   else {			/* offset and truncate */
2156     SETPOS (&bs,first + GETPOS (&bs));
2157     i -= first;			/* reduced size */
2158     if (last && (i > last)) i = last;
2159   }
2160 				/* do the mailgets thing */
2161   (*mailgets) (mail_read,&bs,i,&md);
2162   return T;			/* success */
2163 }
2164 
2165 /* Mail fetch partial message body part
2166  * Accepts: mail stream
2167  *	    message # to fetch
2168  *	    MIME section specifier (#.#.#...#)
2169  *	    offset of first designed byte or 0 to start at beginning
2170  *	    maximum number of bytes or 0 for all bytes
2171  *	    flags
2172  * Returns: T if successful, else NIL
2173  */
2174 
mail_partial_body(MAILSTREAM * stream,unsigned long msgno,char * section,unsigned long first,unsigned long last,long flags)2175 long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
2176 			unsigned long first,unsigned long last,long flags)
2177 {
2178   GETS_DATA md;
2179   PARTTEXT *p;
2180   STRING bs;
2181   BODY *b;
2182   SIZEDTEXT *t;
2183   unsigned long i;
2184   if (!(section && *section))	/* top-level text wanted? */
2185     return mail_partial_text (stream,msgno,NIL,first,last,flags);
2186   if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
2187   if (flags & FT_UID) {		/* UID form of call */
2188     if ((msgno = mail_msgno (stream,msgno)) != 0L) flags &= ~FT_UID;
2189     else return NIL;		/* must get UID/msgno map first */
2190   }
2191 				/* must have body */
2192   if (!(b = mail_body (stream,msgno,section))) return NIL;
2193   flags &= ~FT_INTERNAL;	/* bogus if this is set */
2194 
2195 				/* initialize message data identifier */
2196   INIT_GETS (md,stream,msgno,section,first,last);
2197 				/* have cached text? */
2198   if ((t = &(p = &b->contents)->text)->data) {
2199 				/* mark message seen */
2200     markseen (stream,mail_elt (stream,msgno),flags);
2201     INIT (&bs,mail_string,t->data,i = t->size);
2202   }
2203   else {			/* else get data from driver */
2204     if (!stream->dtb) return NIL;
2205     if (stream->dtb->msgdata)	/* driver will handle this */
2206       return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL,
2207 				      flags);
2208     if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
2209     if (section && *section) {	/* nexted if more complex */
2210       SETPOS (&bs,p->offset);	/* offset stringstruct to data */
2211       i = t->size;		/* maximum size of data */
2212     }
2213     else i = SIZE (&bs);	/* just want this much */
2214   }
2215   if (i <= first) i = first = 0;/* first byte is beyond end of text */
2216   else {			/* offset and truncate */
2217     SETPOS (&bs,first + GETPOS (&bs));
2218     i -= first;			/* reduced size */
2219     if (last && (i > last)) i = last;
2220   }
2221 				/* do the mailgets thing */
2222   (*mailgets) (mail_read,&bs,i,&md);
2223   return T;			/* success */
2224 }
2225 
2226 /* Mail return message text
2227  * Accepts: identifier data
2228  *	    sized text
2229  *	    pointer to returned length
2230  * Returns: text
2231  */
2232 
mail_fetch_text_return(GETS_DATA * md,SIZEDTEXT * t,unsigned long * len)2233 char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len)
2234 {
2235   STRING bs;
2236   if (len) *len = t->size;	/* return size */
2237   if (t->size && mailgets) {	/* have to do the mailgets thing? */
2238 				/* silly but do it anyway for consistency */
2239     INIT (&bs,mail_string,t->data,t->size);
2240     return (*mailgets) (mail_read,&bs,t->size,md);
2241   }
2242   return t->size ? (char *) t->data : "";
2243 }
2244 
2245 
2246 /* Mail return message string
2247  * Accepts: identifier data
2248  *	    stringstruct
2249  *	    text length
2250  *	    pointer to returned length
2251  *	    flags
2252  * Returns: text, or NIL if stringstruct returned
2253  */
2254 
mail_fetch_string_return(GETS_DATA * md,STRING * bs,unsigned long i,unsigned long * len,long flags)2255 char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
2256 				unsigned long *len,long flags)
2257 {
2258   char *ret = NIL;
2259   if (len) *len = i;		/* return size */
2260 				/* return stringstruct hack */
2261   if (flags & FT_RETURNSTRINGSTRUCT) {
2262     memcpy (&md->stream->private.string,bs,sizeof (STRING));
2263     SETPOS (&md->stream->private.string,GETPOS (&md->stream->private.string));
2264   }
2265 				/* have to do the mailgets thing? */
2266   else if (mailgets) ret = (*mailgets) (mail_read,bs,i,md);
2267 				/* special hack to avoid extra copy */
2268   else if (bs->dtb->next == mail_string_next) ret = bs->curpos;
2269 				/* make string copy in memory */
2270   else ret = textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i);
2271   return ret;
2272 }
2273 
2274 /* Read data from stringstruct
2275  * Accepts: stringstruct
2276  *	    size of data to read
2277  *	    buffer to read into
2278  * Returns: T, always, stringstruct updated
2279  */
2280 
mail_read(void * stream,unsigned long size,char * buffer)2281 long mail_read (void *stream,unsigned long size,char *buffer)
2282 {
2283   unsigned long i;
2284   STRING *s = (STRING *) stream;
2285   while (size) {		/* until satisfied */
2286     memcpy (buffer,s->curpos,i = min (s->cursize,size));
2287     buffer += i;		/* update buffer */
2288     size -= i;			/* note that we read this much */
2289     s->curpos += --i;		/* advance that many spaces minus 1 */
2290     s->cursize -= i;
2291     SNX (s);			/* now use SNX to advance the last byte */
2292   }
2293   return T;
2294 }
2295 
2296 /* Mail fetch UID
2297  * Accepts: mail stream
2298  *	    message number
2299  * Returns: UID or zero if dead stream
2300  */
2301 
mail_uid(MAILSTREAM * stream,unsigned long msgno)2302 unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno)
2303 {
2304   unsigned long uid = mail_elt (stream,msgno)->private.uid;
2305   return uid ? uid :
2306     (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0;
2307 }
2308 
2309 
2310 /* Mail fetch msgno from UID
2311  * Accepts: mail stream
2312  *	    UID
2313  * Returns: msgno or zero if failed
2314  */
2315 
mail_msgno(MAILSTREAM * stream,unsigned long uid)2316 unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid)
2317 {
2318   unsigned long msgno,delta,first,firstuid,last,lastuid,middle,miduid;
2319   if (stream->dtb) {		/* active stream? */
2320     if (stream->dtb->msgno)	/* direct way */
2321       return (*stream->dtb->msgno) (stream,uid);
2322     else if (stream->dtb->uid) {/* indirect way */
2323       /* Placeholder for now, since currently there are no drivers which
2324        * have a uid method but not a msgno method
2325        */
2326       for (msgno = 1; msgno <= stream->nmsgs; msgno++)
2327 	if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno;
2328     }
2329 				/* binary search since have full map */
2330     else for (first = 1,last = stream->nmsgs, delta = (first <= last) ? 1 : 0;
2331 	      delta &&
2332 	      (uid >= (firstuid = mail_elt (stream,first)->private.uid)) &&
2333 		(uid <= (lastuid = mail_elt (stream,last)->private.uid));) {
2334 				/* done if match at an endpoint */
2335 	if (uid == firstuid) return first;
2336 	if (uid == lastuid) return last;
2337 				/* have anything between endpoints? */
2338 	if ((delta = ((last - first) / 2)) != 0L){
2339 	  if ((miduid = mail_elt (stream,middle = first + delta)->private.uid)
2340 	      == uid)
2341 	    return middle;	/* found match in middle */
2342 	  else if (uid < miduid) last = middle - 1;
2343 	  else first = middle + 1;
2344 	}
2345     }
2346   }
2347   else {			/* dead stream, do linear search for UID */
2348     for (msgno = 1; msgno <= stream->nmsgs; msgno++)
2349       if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
2350   }
2351   return 0;			/* didn't find the UID anywhere */
2352 }
2353 
2354 /* Mail fetch From string for menu
2355  * Accepts: destination string
2356  *	    mail stream
2357  *	    message # to fetch
2358  *	    desired string length
2359  * Returns: string of requested length
2360  */
2361 
mail_fetchfrom(char * s,MAILSTREAM * stream,unsigned long msgno,long length)2362 void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
2363 		     long length)
2364 {
2365   char *t;
2366   char tmp[MAILTMPLEN];
2367   ENVELOPE *env = mail_fetchenvelope (stream,msgno);
2368   ADDRESS *adr = env ? env->from : NIL;
2369   memset (s,' ',(size_t)length);/* fill it with spaces */
2370   s[length] = '\0';		/* tie off with null */
2371 				/* get first from address from envelope */
2372   while (adr && !adr->host) adr = adr->next;
2373   if (adr) {			/* if a personal name exists use it */
2374     if (!(t = adr->personal))
2375       sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host);
2376     memcpy (s,t,(size_t) min (length,(long) strlen (t)));
2377   }
2378 }
2379 
2380 
2381 /* Mail fetch Subject string for menu
2382  * Accepts: destination string
2383  *	    mail stream
2384  *	    message # to fetch
2385  *	    desired string length
2386  * Returns: string of no more than requested length
2387  */
2388 
mail_fetchsubject(char * s,MAILSTREAM * stream,unsigned long msgno,long length)2389 void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
2390 			long length)
2391 {
2392   ENVELOPE *env = mail_fetchenvelope (stream,msgno);
2393   memset (s,'\0',(size_t) length+1);
2394 				/* copy subject from envelope */
2395   if (env && env->subject) strncpy (s,env->subject,(size_t) length);
2396   else *s = ' ';		/* if no subject then just a space */
2397 }
2398 
2399 /* Mail modify flags
2400  * Accepts: mail stream
2401  *	    sequence
2402  *	    flag(s)
2403  *	    option flags
2404  */
2405 
mail_flag(MAILSTREAM * stream,char * sequence,char * flag,long flags)2406 void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
2407 {
2408   MESSAGECACHE *elt;
2409   unsigned long i,uf;
2410   long f;
2411   short nf;
2412   if (!stream->dtb) return;	/* no-op if no stream */
2413   if ((stream->dtb->flagmsg || !stream->dtb->flag) &&
2414       ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
2415        mail_sequence (stream,sequence)) &&
2416       ((f = mail_parse_flags (stream,flag,&uf)) || uf))
2417     for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
2418       if ((elt = mail_elt (stream,i))->sequence) {
2419 	struct {		/* old flags */
2420 	  unsigned int valid : 1;
2421 	  unsigned int seen : 1;
2422 	  unsigned int deleted : 1;
2423 	  unsigned int flagged : 1;
2424 	  unsigned int answered : 1;
2425 	  unsigned int draft : 1;
2426 	  unsigned long user_flags;
2427 	} old;
2428 	old.valid = elt->valid; old.seen = elt->seen;
2429 	old.deleted = elt->deleted; old.flagged = elt->flagged;
2430 	old.answered = elt->answered; old.draft = elt->draft;
2431 	old.user_flags = elt->user_flags;
2432 	elt->valid = NIL;	/* prepare for flag alteration */
2433 	if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
2434 	if (f&fSEEN) elt->seen = nf;
2435 	if (f&fDELETED) elt->deleted = nf;
2436 	if (f&fFLAGGED) elt->flagged = nf;
2437 	if (f&fANSWERED) elt->answered = nf;
2438 	if (f&fDRAFT) elt->draft = nf;
2439 				/* user flags */
2440 	if (flags & ST_SET) elt->user_flags |= uf;
2441 	else elt->user_flags &= ~uf;
2442 	elt->valid = T;		/* flags now altered */
2443 	if ((old.valid != elt->valid) || (old.seen != elt->seen) ||
2444 	    (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
2445 	    (old.answered != elt->answered) || (old.draft != elt->draft) ||
2446 	    (old.user_flags != elt->user_flags))
2447 	  MM_FLAGS (stream,elt->msgno);
2448 	if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
2449       }
2450 				/* call driver once */
2451   if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags);
2452 }
2453 
2454 /* Mail search for messages
2455  * Accepts: mail stream
2456  *	    character set
2457  *	    search program
2458  *	    option flags
2459  * Returns: T if successful, NIL if dead stream, NIL searchpgm or bad charset
2460  */
2461 
mail_search_full(MAILSTREAM * stream,char * charset,SEARCHPGM * pgm,long flags)2462 long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
2463 		       long flags)
2464 {
2465   unsigned long i;
2466   long ret = NIL;
2467   if (!(flags & SE_RETAIN))	/* clear search vector unless retaining */
2468     for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
2469   if (pgm && stream->dtb)	/* must have a search program and driver */
2470     ret = (*(stream->dtb->search ? stream->dtb->search : mail_search_default))
2471       (stream,charset,pgm,flags);
2472 				/* flush search program if requested */
2473   if (flags & SE_FREE) mail_free_searchpgm (&pgm);
2474   return ret;
2475 }
2476 
2477 
2478 /* Mail search for messages default handler
2479  * Accepts: mail stream
2480  *	    character set
2481  *	    search program
2482  *	    option flags
2483  * Returns: T if successful, NIL if bad charset
2484  */
2485 
mail_search_default(MAILSTREAM * stream,char * charset,SEARCHPGM * pgm,long flags)2486 long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
2487 			  long flags)
2488 {
2489   unsigned long i;
2490   char *msg;
2491 				/* make sure that charset is good */
2492   if ((msg = utf8_badcharset (charset)) != NULL) {
2493     MM_LOG (msg,ERROR);		/* output error */
2494     fs_give ((void **) &msg);
2495     return NIL;
2496   }
2497   utf8_searchpgm (pgm,charset);
2498   for (i = 1; i <= stream->nmsgs; ++i)
2499     if (mail_search_msg (stream,i,NIL,pgm)) {
2500       if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
2501       else {			/* mark as searched, notify mail program */
2502 	mail_elt (stream,i)->searched = T;
2503 	if (!stream->silent) mm_searched (stream,i);
2504       }
2505     }
2506   return LONGT;		/* search completed */
2507 }
2508 
2509 /* Mail ping mailbox
2510  * Accepts: mail stream
2511  * Returns: stream if still open else NIL
2512  */
2513 
mail_ping(MAILSTREAM * stream)2514 long mail_ping (MAILSTREAM *stream)
2515 {
2516   unsigned long i,n,uf,len;
2517   char *s,*f,tmp[MAILTMPLEN],flags[MAILTMPLEN];
2518   MAILSTREAM *snarf;
2519   MESSAGECACHE *elt;
2520   STRING bs;
2521   long ret;
2522 				/* do driver action */
2523   if ((ret = ((stream && stream->dtb) ? (stream->dtb->ping) (stream) : NIL)) &&
2524       stream->snarf.name &&	/* time to snarf? */
2525 				/* prohibit faster than once/min */
2526       (time (0) > (time_t) (stream->snarf.time + min(60,mailsnarfinterval))) &&
2527       (snarf = mail_open (NIL,stream->snarf.name,
2528 			  stream->snarf.options | OP_SILENT))) {
2529     if ((n = snarf->nmsgs) &&	/* yes, have messages to snarf? */
2530 	mail_search_full (snarf,NIL,mail_criteria ("UNDELETED"),SE_FREE)) {
2531       for (i = 1; ret && (i <= n); i++)	/* for each message */
2532 	if ((elt = mail_elt (snarf,i))->searched &&
2533 	    (s = mail_fetch_message (snarf,i,&len,FT_PEEK)) && len) {
2534 	  INIT (&bs,mail_string,s,len);
2535 	  if (mailsnarfpreserve) {
2536 				/* yes, make sure have fast data */
2537 	    if (!elt->valid || !elt->day) {
2538 	      sprintf (tmp,"%lu",n);
2539 	      mail_fetch_fast (snarf,tmp,NIL);
2540 	    }
2541 				/* initialize flag string */
2542 	    memset (flags,0,MAILTMPLEN);
2543 				/* output system flags except \Deleted */
2544 	    if (elt->seen) strcat (flags," \\Seen");
2545 	    if (elt->flagged) strcat (flags," \\Flagged");
2546 	    if (elt->answered) strcat (flags," \\Answered");
2547 	    if (elt->draft) strcat (flags," \\Draft");
2548 				/* any user flags? */
2549 	    for (uf = elt->user_flags,s = flags + strlen (flags);
2550 		 uf && (f = stream->user_flags[find_rightmost_bit (&uf)]) &&
2551 		   ((MAILTMPLEN - (s - tmp)) > (long) (2 + strlen (f)));
2552 		 s += strlen (s)) sprintf (s," %s",f);
2553 	    ret = mail_append_full (stream,stream->mailbox,flags + 1,
2554 				    mail_date (tmp,elt),&bs);
2555 	  }
2556 	  else ret = mail_append (stream,stream->mailbox,&bs);
2557 
2558 	  if (ret) {		/* did snarf succeed? */
2559 				/* driver has per-message (or no) flag call */
2560 	    if (snarf->dtb->flagmsg || !snarf->dtb->flag) {
2561 	      elt->valid = NIL;	/* prepare for flag alteration */
2562 	      if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
2563 				/* flags now altered */
2564 	      elt->deleted = elt->seen = elt->valid = T;
2565 	      if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
2566 	    }
2567 				/* driver has one-time flag call */
2568 	    if (snarf->dtb->flag) {
2569 	      sprintf (tmp,"%lu",i);
2570 	      (*snarf->dtb->flag) (snarf,tmp,"\\Deleted \\Seen",ST_SET);
2571 	    }
2572 	  }
2573 	  else {		/* copy failed */
2574 	    sprintf (tmp,"Unable to move message %lu from %s mailbox",
2575 		     i,snarf->dtb->name);
2576 	    mm_log (tmp,WARN);
2577 	  }
2578 	}
2579     }
2580 				/* expunge the messages */
2581     mail_close_full (snarf,n ? CL_EXPUNGE : NIL);
2582     stream->snarf.time = (unsigned long) time (0);
2583     /* Even if the snarf failed, we don't want to return NIL if the stream
2584      * is still alive.  Or at least that's what we currently think.
2585      */
2586   				/* redo the driver's action */
2587     ret = stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
2588   }
2589   return ret;
2590 }
2591 
2592 /* Mail check mailbox
2593  * Accepts: mail stream
2594  */
2595 
mail_check(MAILSTREAM * stream)2596 void mail_check (MAILSTREAM *stream)
2597 {
2598   				/* do the driver's action */
2599   if (stream->dtb) (*stream->dtb->check) (stream);
2600 }
2601 
2602 
2603 /* Mail expunge mailbox
2604  * Accepts: mail stream
2605  *	    sequence to expunge if non-NIL
2606  *	    expunge options
2607  * Returns: T on success, NIL on failure
2608  */
2609 
mail_expunge_full(MAILSTREAM * stream,char * sequence,long options)2610 long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options)
2611 {
2612   				/* do the driver's action */
2613   return stream->dtb ? (*stream->dtb->expunge) (stream,sequence,options) : NIL;
2614 }
2615 
2616 
2617 /* Mail copy message(s)
2618  * Accepts: mail stream
2619  *	    sequence
2620  *	    destination mailbox
2621  *	    flags
2622  */
2623 
mail_copy_full(MAILSTREAM * stream,char * sequence,char * mailbox,long options)2624 long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
2625 		     long options)
2626 {
2627   return stream->dtb ?
2628     SAFE_COPY (stream->dtb,stream,sequence,mailbox,options) : NIL;
2629 }
2630 
2631 /* Append data package to use for old single-message mail_append() interface */
2632 
2633 typedef struct mail_append_package {
2634   char *flags;			/* initial flags */
2635   char *date;			/* message internal date */
2636   STRING *message;		/* stringstruct of message */
2637 } APPENDPACKAGE;
2638 
2639 
2640 /* Single append message string
2641  * Accepts: mail stream
2642  *	    package pointer (cast as a void *)
2643  *	    pointer to return initial flags
2644  *	    pointer to return message internal date
2645  *	    pointer to return stringstruct of message to append
2646  * Returns: T, always
2647  */
2648 
mail_append_single(MAILSTREAM * stream,void * data,char ** flags,char ** date,STRING ** message)2649 static long mail_append_single (MAILSTREAM *stream,void *data,char **flags,
2650 				char **date,STRING **message)
2651 {
2652   APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
2653   *flags = ap->flags;		/* get desired data from the package */
2654   *date = ap->date;
2655   *message = ap->message;
2656   ap->message = NIL;		/* so next callback puts a stop to it */
2657   return LONGT;			/* always return success */
2658 }
2659 
2660 
2661 /* Mail append message string
2662  * Accepts: mail stream
2663  *	    destination mailbox
2664  *	    initial flags
2665  *	    message internal date
2666  *	    stringstruct of message to append
2667  * Returns: T on success, NIL on failure
2668  */
2669 
mail_append_full(MAILSTREAM * stream,char * mailbox,char * flags,char * date,STRING * message)2670 long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
2671 		       STRING *message)
2672 {
2673   APPENDPACKAGE ap;
2674   ap.flags = flags;		/* load append package */
2675   ap.date = date;
2676   ap.message = message;
2677   return mail_append_multiple (stream,mailbox,mail_append_single,(void *) &ap);
2678 }
2679 
2680 /* Mail append message(s)
2681  * Accepts: mail stream
2682  *	    destination mailbox
2683  *	    append data callback
2684  *	    arbitrary data for callback use
2685  * Returns: T on success, NIL on failure
2686  */
2687 
mail_append_multiple(MAILSTREAM * stream,char * mailbox,append_t af,void * data)2688 long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
2689 			   void *data)
2690 {
2691   char *s,tmp[MAILTMPLEN];
2692   DRIVER *d = NIL;
2693   long ret = NIL;
2694 				/* never allow names with newlines */
2695   if (strpbrk (mailbox,"\015\012"))
2696     MM_LOG ("Can't append to mailbox with such a name",ERROR);
2697   else if (strlen (mailbox) >=
2698 	   (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
2699     sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
2700 	     "invalid remote specification" : "no such mailbox");
2701     MM_LOG (tmp,ERROR);
2702   }
2703 				/* special driver hack? */
2704   else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) {
2705 				/* yes, tie off name at likely delimiter */
2706     if (!(s = strpbrk (tmp+8,"/\\:"))) {
2707       sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
2708       MM_LOG (tmp,ERROR);
2709       return NIL;
2710     }
2711     *s++ = '\0';		/* tie off at delimiter */
2712     if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) {
2713       sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
2714       MM_LOG (tmp,ERROR);
2715     }
2716     else ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data);
2717   }
2718   else if ((d = mail_valid (stream,mailbox,NIL)) != NULL)
2719     ret = SAFE_APPEND (d,stream,mailbox,af,data);
2720   /* No driver, try for TRYCREATE if no stream.  Note that we use the
2721    * createProto here, not the appendProto, since the dummy driver already
2722    * took care of the appendProto case.  Otherwise, if appendProto is set to
2723    * NIL, we won't get a TRYCREATE.
2724    */
2725   else if (!stream && (stream = default_proto (NIL)) && stream->dtb &&
2726 	   SAFE_APPEND (stream->dtb,stream,mailbox,af,data))
2727 				/* timing race? */
2728     MM_NOTIFY (stream,"Append validity confusion",WARN);
2729 				/* generate error message */
2730   else mail_valid (stream,mailbox,"append to mailbox");
2731   return ret;
2732 }
2733 
2734 /* Mail garbage collect stream
2735  * Accepts: mail stream
2736  *	    garbage collection flags
2737  */
2738 
mail_gc(MAILSTREAM * stream,long gcflags)2739 void mail_gc (MAILSTREAM *stream,long gcflags)
2740 {
2741   MESSAGECACHE *elt;
2742   unsigned long i;
2743   				/* do the driver's action first */
2744   if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags);
2745   stream->msgno = 0;		/* nothing cached now */
2746   if (gcflags & GC_ENV) {	/* garbage collect envelopes? */
2747     if (stream->env) mail_free_envelope (&stream->env);
2748     if (stream->body) mail_free_body (&stream->body);
2749   }
2750   if (gcflags & GC_TEXTS) {	/* free texts */
2751     if (stream->text.data) fs_give ((void **) &stream->text.data);
2752     stream->text.size = 0;
2753   }
2754 				/* garbage collect per-message stuff */
2755   for (i = 1; i <= stream->nmsgs; i++)
2756     if ((elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT)) != NULL)
2757       mail_gc_msg (&elt->private.msg,gcflags);
2758 }
2759 
2760 
2761 /* Mail garbage collect message
2762  * Accepts: message structure
2763  *	    garbage collection flags
2764  */
2765 
mail_gc_msg(MESSAGE * msg,long gcflags)2766 void mail_gc_msg (MESSAGE *msg,long gcflags)
2767 {
2768   if (gcflags & GC_ENV) {	/* garbage collect envelopes? */
2769     mail_free_envelope (&msg->env);
2770     mail_free_body (&msg->body);
2771   }
2772   if (gcflags & GC_TEXTS) {	/* garbage collect texts */
2773     if (msg->full.text.data) fs_give ((void **) &msg->full.text.data);
2774     if (msg->header.text.data) {
2775       mail_free_stringlist (&msg->lines);
2776       fs_give ((void **) &msg->header.text.data);
2777     }
2778     if (msg->text.text.data) fs_give ((void **) &msg->text.text.data);
2779 				/* now GC all body components */
2780     if (msg->body) mail_gc_body (msg->body);
2781   }
2782 }
2783 
2784 /* Mail garbage collect texts in BODY structure
2785  * Accepts: BODY structure
2786  */
2787 
mail_gc_body(BODY * body)2788 void mail_gc_body (BODY *body)
2789 {
2790   PART *part;
2791   switch (body->type) {		/* free contents */
2792   case TYPEMULTIPART:		/* multiple part */
2793     for (part = body->nested.part; part; part = part->next)
2794       mail_gc_body (&part->body);
2795     break;
2796   case TYPEMESSAGE:		/* encapsulated message */
2797     if (body->subtype && !strcmp (body->subtype,"RFC822")) {
2798       mail_free_stringlist (&body->nested.msg->lines);
2799       mail_gc_msg (body->nested.msg,GC_TEXTS);
2800     }
2801     break;
2802   default:
2803     break;
2804   }
2805   if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
2806   if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
2807 }
2808 /* Mail get body section
2809  * Accepts: body of message
2810  *          section specifier
2811  * Returns: pointer to body at given section
2812  */
2813 
mail_body_section(BODY * b,unsigned char * section)2814 BODY *mail_body_section (BODY *b, unsigned char *section)
2815 {
2816   PART *pt;
2817   unsigned long i;
2818 				/* make sure have a body */
2819   if (section && *section && b)
2820     while (*section) {		/* find desired section */
2821       if (isdigit (*section)) {	/* get section specifier */
2822 				/* make sure what follows is valid */
2823 	if (!(i = strtoul (section,(char **) &section,10)) ||
2824 	    (*section && ((*section++ != '.') || !*section))) return NIL;
2825 				/* multipart content? */
2826 	if (b->type == TYPEMULTIPART) {
2827 				/* yes, find desired part */
2828 	  if ((pt = b->nested.part) != NULL) while (--i && (pt = pt->next));
2829 	  if (!pt) return NIL;	/* bad specifier */
2830 	  b = &pt->body;	/* note new body */
2831 	}
2832 				/* otherwise must be section 1 */
2833 	else if (i != 1) return NIL;
2834 				/* need to go down further? */
2835 	if (*section) switch (b->type) {
2836 	case TYPEMULTIPART:	/* multipart */
2837 	  break;
2838 	case TYPEMESSAGE:	/* embedded message */
2839 	  if (!strcmp (b->subtype,"RFC822")) {
2840 	    b = b->nested.msg->body;
2841 	    break;
2842 	  }
2843 	default:		/* bogus subpart specification */
2844 	  return NIL;
2845 	}
2846       }
2847       else return NIL;		/* unknown section specifier */
2848     }
2849   return b;
2850 }
2851 
2852 /* Mail get body part
2853  * Accepts: mail stream
2854  *	    message number
2855  *	    section specifier
2856  * Returns: pointer to body
2857  */
2858 
mail_body(MAILSTREAM * stream,unsigned long msgno,unsigned char * section)2859 BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,unsigned char *section)
2860 {
2861   BODY *b = NIL;
2862 				/* make sure have a body */
2863   if (section && *section && mail_fetchstructure (stream,msgno,&b) && b)
2864     return mail_body_section(b, section);
2865   return b;
2866 }
2867 
2868 /* Mail output date from elt fields
2869  * Accepts: character string to write into
2870  *	    elt to get data data from
2871  * Returns: the character string
2872  */
2873 
2874 const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
2875 
2876 const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
2877 			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2878 
mail_date(char * string,MESSAGECACHE * elt)2879 char *mail_date (char *string,MESSAGECACHE *elt)
2880 {
2881   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
2882 	   elt->day ? elt->day : 1,
2883 	   months[elt->month ? (elt->month - 1) : 0],
2884 	   elt->year + BASEYEAR,elt->hours,elt->minutes,elt->seconds,
2885 	   elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
2886   return string;
2887 }
2888 
2889 
2890 /* Mail output extended-ctime format date from elt fields
2891  * Accepts: character string to write into
2892  *	    elt to get data data from
2893  * Returns: the character string
2894  */
2895 
mail_cdate(char * string,MESSAGECACHE * elt)2896 char *mail_cdate (char *string,MESSAGECACHE *elt)
2897 {
2898   char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02d\n";
2899   int d = elt->day ? elt->day : 1;
2900   int m = elt->month ? (elt->month - 1) : 0;
2901   int y = elt->year + BASEYEAR;
2902   const char *s = months[m];
2903   if (m < 2) {			/* if before March, */
2904     m += 10;			/* January = month 10 of previous year */
2905     y--;
2906   }
2907   else m -= 2;			/* March is month 0 */
2908   sprintf (string,fmt,days[(int) (d + 2 + ((7 + 31 * m) / 12)
2909 #ifndef USEJULIANCALENDAR
2910 #ifndef USEORTHODOXCALENDAR	/* Gregorian calendar */
2911 				  + (y / 400)
2912 #ifdef Y4KBUGFIX
2913 				  - (y / 4000)
2914 #endif
2915 #else				/* Orthodox calendar */
2916 				  + (2 * (y / 900)) + ((y % 900) >= 200)
2917 				  + ((y % 900) >= 600)
2918 #endif
2919 				  - (y / 100)
2920 #endif
2921 				  + y + (y / 4)) % 7],
2922 	   s,d,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR,
2923 	   elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes);
2924   return string;
2925 }
2926 
2927 /* Mail parse date into elt fields
2928  * Accepts: elt to write into
2929  *	    date string to parse
2930  * Returns: T if parse successful, else NIL
2931  * This routine parses dates as follows:
2932  * . leading three alphas followed by comma and space are ignored
2933  * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
2934  *    dd mmm yy, dd mmm yyyy, yyyy-mm-dd, yyyymmdd
2935  * . two and three digit years interpreted according to RFC 2822 rules
2936  * . mandatory end of string if yyyy-mm-dd or yyyymmdd; otherwise optional
2937  *    space followed by time:
2938  * . time accepted in format hh:mm:ss or hh:mm
2939  * . end of string accepted
2940  * . timezone accepted: hyphen followed by symbolic timezone, or space
2941  *    followed by signed numeric timezone or symbolic timezone
2942  * Examples of normal input:
2943  * . IMAP date-only (SEARCH):
2944  *    dd-mmm-yyyy
2945  * . IMAP date-time (INTERNALDATE):
2946  *    dd-mmm-yyyy hh:mm:ss +zzzz
2947  * . RFC-822:
2948  *    www, dd mmm yy hh:mm:ss zzz
2949  * . RFC-2822:
2950  *    www, dd mmm yyyy hh:mm:ss +zzzz
2951  */
2952 
mail_parse_date(MESSAGECACHE * elt,unsigned char * s)2953 long mail_parse_date (MESSAGECACHE *elt,unsigned char *s)
2954 {
2955   unsigned long d,m,y;
2956   int mi,ms;
2957   struct tm *t;
2958   time_t tn;
2959   char tmp[MAILTMPLEN];
2960   static unsigned long maxyear = 0;
2961   if (!maxyear) {		/* know the end of time yet? */
2962     MESSAGECACHE tmpelt;
2963     memset (&tmpelt,0xff,sizeof (MESSAGECACHE));
2964     maxyear = BASEYEAR + tmpelt.year;
2965   }
2966 				/* clear elt */
2967   elt->zoccident = elt->zhours = elt->zminutes =
2968     elt->hours = elt->minutes = elt->seconds =
2969       elt->day = elt->month = elt->year = 0;
2970 				/* make a writeable uppercase copy */
2971   if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s));
2972   else return NIL;
2973 				/* skip over possible day of week */
2974   if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
2975       (s[4] == ' ')) s += 5;
2976   while (*s == ' ') s++;	/* parse first number (probable month) */
2977   if (!(m = strtoul (s,(char **) &s,10))) return NIL;
2978 
2979   switch (*s) {			/* different parse based on delimiter */
2980   case '/':			/* mm/dd/yy format */
2981     if (isdigit (*++s) && (d = strtoul (s,(char **) &s,10)) &&
2982 	(*s == '/') && isdigit (*++s)) {
2983       y = strtoul (s,(char **) &s,10);
2984       if (*s == '\0') break;	/* must end here */
2985     }
2986     return NIL;			/* bogon */
2987   case ' ':			/* dd mmm yy format */
2988     while (s[1] == ' ') s++;	/* slurp extra whitespace */
2989   case '-':
2990     if (isdigit (s[1])) {	/* possible ISO 8601 date format? */
2991       y = m;			/* yes, first number is year */
2992 				/* get month and day */
2993       if ((m = strtoul (s+1,(char **) &s,10)) && (*s++ == '-') &&
2994 	  (d = strtoul (s,(char **) &s,10)) && !*s) break;
2995       return NIL;		/* syntax error or time present */
2996     }
2997     d = m;			/* dd-mmm-yy[yy], so first number is a day */
2998 				/* make sure string long enough! */
2999     if (strlen (s) < (size_t) 5) return NIL;
3000     /* Some compilers don't allow `<<' and/or longs in case statements. */
3001 				/* slurp up the month string */
3002     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
3003     switch (ms) {		/* determine the month */
3004     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
3005     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
3006     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
3007     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
3008     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
3009     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
3010     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
3011     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
3012     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
3013     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
3014     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
3015     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
3016     default: return NIL;	/* unknown month */
3017     }
3018     if (s[4] == *s) s += 5;	/* advance to year */
3019     else {			/* first three were OK, possibly full name */
3020       mi = *s;			/* note delimiter, skip alphas */
3021       for (s += 4; isalpha (*s); s++);
3022 				/* error if delimiter not here */
3023       if (mi != *s++) return NIL;
3024     }
3025     while (*s == ' ') s++;	/* parse year */
3026     if (isdigit (*s)) {		/* must be a digit here */
3027       y = strtoul (s,(char **) &s,10);
3028       if (*s == '\0' || *s == ' ') break;
3029     }
3030   case '\0':			/* ISO 8601 compact date */
3031     if (m < (BASEYEAR * 10000)) return NIL;
3032     y = m / 10000;		/* get year */
3033     d = (m %= 10000) % 100;	/* get day */
3034     m /= 100;			/* and month */
3035     break;
3036   default:
3037     return NIL;			/* unknown date format */
3038   }
3039 
3040 				/* minimal validity check of date */
3041   if ((d > 31) || (m > 12)) return NIL;
3042   if (y < 49) y += 2000;	/* RFC 2282 rules for two digit years 00-49 */
3043   else if (y < 999) y += 1900;	/* 2-digit years 50-99 and 3-digit years */
3044 				/* reject prehistoric and far future years */
3045   if ((y < BASEYEAR) || (y > maxyear)) return NIL;
3046 				/* set values in elt */
3047   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
3048   ms = '\0';			/* initially no time zone string */
3049   if (*s) {			/* time specification present? */
3050 				/* parse time */
3051     d = strtoul (s+1,(char **) &s,10);
3052     if (*s != ':') return NIL;
3053     m = strtoul (++s,(char **) &s,10);
3054     y = (*s == ':') ? strtoul (++s,(char **) &s,10) : 0;
3055 				/* validity check time */
3056     if ((d > 23) || (m > 59) || (y > 60)) return NIL;
3057 				/* set values in elt */
3058     elt->hours = d; elt->minutes = m; elt->seconds = y;
3059     switch (*s) {		/* time zone specifier? */
3060     case ' ':			/* numeric time zone */
3061       while (s[1] == ' ') s++;	/* slurp extra whitespace */
3062       if (!isalpha (s[1])) {	/* treat as '-' case if alphabetic */
3063 				/* test for sign character */
3064 	if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
3065 				/* validate proper timezone */
3066 	if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && (s[2] < '6') &&
3067 	    isdigit(s[3])) {
3068 	  elt->zhours = (*s - '0') * 10 + (s[1] - '0');
3069 	  elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
3070 	}
3071 	return T;		/* all done! */
3072       }
3073 				/* falls through */
3074     case '-':			/* symbolic time zone */
3075       if (!(ms = *++s)) ms = 'Z';
3076       else if (*++s) {		/* multi-character? */
3077 	ms -= 'A'; ms *= 1024;	/* yes, make compressed three-byte form */
3078 	ms += ((*s++ - 'A') * 32);
3079 	if (*s) ms += *s++ - 'A';
3080 	if (*s) ms = '\0';	/* more than three characters */
3081       }
3082     default:			/* ignore anything else */
3083       break;
3084     }
3085   }
3086 
3087   /*  This is not intended to be a comprehensive list of all possible
3088    * timezone strings.  Such a list would be impractical.  Rather, this
3089    * listing is intended to incorporate all military, North American, and
3090    * a few special cases such as Japan and the major European zone names,
3091    * such as what might be expected to be found in a Tenex format mailbox
3092    * and spewed from an IMAP server.  The trend is to migrate to numeric
3093    * timezones which lack the flavor but also the ambiguity of the names.
3094    *
3095    *  RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the
3096    * 4 CONUS timezones and their summer time variants.  [Sorry, Canadian
3097    * Atlantic Provinces, Alaska, and Hawaii.]
3098    */
3099   switch (ms) {			/* determine the timezone */
3100 				/* Universal */
3101   case (('U'-'A')*1024)+(('T'-'A')*32):
3102 #ifndef STRICT_RFC822_TIMEZONES
3103   case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A':
3104 #endif
3105 				/* Greenwich */
3106   case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
3107   case 'Z': elt->zhours = 0; break;
3108 
3109     /* oriental (from Greenwich) timezones */
3110 #ifndef STRICT_RFC822_TIMEZONES
3111 				/* Middle Europe */
3112   case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
3113 #endif
3114 #ifdef BRITISH_SUMMER_TIME
3115 				/* British Summer */
3116   case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3117 #endif
3118   case 'A': elt->zhours = 1; break;
3119 #ifndef STRICT_RFC822_TIMEZONES
3120 				/* Eastern Europe */
3121   case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
3122 #endif
3123   case 'B': elt->zhours = 2; break;
3124   case 'C': elt->zhours = 3; break;
3125   case 'D': elt->zhours = 4; break;
3126   case 'E': elt->zhours = 5; break;
3127   case 'F': elt->zhours = 6; break;
3128   case 'G': elt->zhours = 7; break;
3129   case 'H': elt->zhours = 8; break;
3130 #ifndef STRICT_RFC822_TIMEZONES
3131 				/* Japan */
3132   case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3133 #endif
3134   case 'I': elt->zhours = 9; break;
3135   case 'K': elt->zhours = 10; break;
3136   case 'L': elt->zhours = 11; break;
3137   case 'M': elt->zhours = 12; break;
3138 
3139 	/* occidental (from Greenwich) timezones */
3140   case 'N': elt->zoccident = 1; elt->zhours = 1; break;
3141   case 'O': elt->zoccident = 1; elt->zhours = 2; break;
3142 #ifndef STRICT_RFC822_TIMEZONES
3143   case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3144 #endif
3145   case 'P': elt->zoccident = 1; elt->zhours = 3; break;
3146 #ifdef NEWFOUNDLAND_STANDARD_TIME
3147 				/* Newfoundland */
3148   case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3149     elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break;
3150 #endif
3151 #ifndef STRICT_RFC822_TIMEZONES
3152 				/* Atlantic */
3153   case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3154 #endif
3155 	/* CONUS */
3156   case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3157   case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
3158 				/* Eastern */
3159   case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3160   case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3161   case 'R': elt->zoccident = 1; elt->zhours = 5; break;
3162 				/* Central */
3163   case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3164   case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3165   case 'S': elt->zoccident = 1; elt->zhours = 6; break;
3166 				/* Mountain */
3167   case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3168   case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3169   case 'T': elt->zoccident = 1; elt->zhours = 7; break;
3170 				/* Pacific */
3171   case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3172 #ifndef STRICT_RFC822_TIMEZONES
3173   case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
3174 #endif
3175   case 'U': elt->zoccident = 1; elt->zhours = 8; break;
3176 #ifndef STRICT_RFC822_TIMEZONES
3177 				/* Yukon */
3178   case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3179 #endif
3180   case 'V': elt->zoccident = 1; elt->zhours = 9; break;
3181 #ifndef STRICT_RFC822_TIMEZONES
3182 				/* Hawaii */
3183   case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3184 #endif
3185   case 'W': elt->zoccident = 1; elt->zhours = 10; break;
3186 				/* Nome/Bering/Samoa */
3187 #ifdef NOME_STANDARD_TIME
3188   case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3189 #endif
3190 #ifdef BERING_STANDARD_TIME
3191   case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3192 #endif
3193 #ifdef SAMOA_STANDARD_TIME
3194   case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
3195 #endif
3196   case 'X': elt->zoccident = 1; elt->zhours = 11; break;
3197   case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
3198 
3199   default:			/* unknown time zones treated as local */
3200     tn = time (0);		/* time now... */
3201     t = localtime (&tn);	/* get local minutes since midnight */
3202     mi = t->tm_hour * 60 + t->tm_min;
3203     ms = t->tm_yday;		/* note Julian day */
3204     if ((t = gmtime (&tn)) != NULL) {	/* minus UTC minutes since midnight */
3205       mi -= t->tm_hour * 60 + t->tm_min;
3206 	/* ms can be one of:
3207 	 *  36x  local time is December 31, UTC is January 1, offset -24 hours
3208 	 *    1  local time is 1 day ahead of UTC, offset +24 hours
3209 	 *    0  local time is same day as UTC, no offset
3210 	 *   -1  local time is 1 day behind UTC, offset -24 hours
3211 	 * -36x  local time is January 1, UTC is December 31, offset +24 hours
3212 	 */
3213       if (ms -= t->tm_yday)	/* correct offset if different Julian day */
3214 	mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
3215       if (mi < 0) {		/* occidental? */
3216 	mi = abs (mi);		/* yup, make positive number */
3217 	elt->zoccident = 1;	/* and note west of UTC */
3218       }
3219       elt->zhours = mi / 60;	/* now break into hours and minutes */
3220       elt->zminutes = mi % 60;
3221     }
3222     break;
3223   }
3224   return T;
3225 }
3226 
3227 /* Mail n messages exist
3228  * Accepts: mail stream
3229  *	    number of messages
3230  */
3231 
mail_exists(MAILSTREAM * stream,unsigned long nmsgs)3232 void mail_exists (MAILSTREAM *stream,unsigned long nmsgs)
3233 {
3234   char tmp[MAILTMPLEN];
3235   if (nmsgs > MAXMESSAGES) {
3236     sprintf (tmp,"Mailbox has more messages (%lu) exist than maximum (%lu)",
3237 	     nmsgs,MAXMESSAGES);
3238     mm_log (tmp,ERROR);
3239     nmsgs = MAXMESSAGES;	/* cap to maximum */
3240     /* probably will crash in mail_elt() soon enough... */
3241   }
3242 				/* make sure cache is large enough */
3243   (*mailcache) (stream,nmsgs,CH_SIZE);
3244   stream->nmsgs = nmsgs;	/* update stream status */
3245 				/* notify main program of change */
3246   if (!stream->silent) MM_EXISTS (stream,nmsgs);
3247 }
3248 
3249 
3250 /* Mail n messages are recent
3251  * Accepts: mail stream
3252  *	    number of recent messages
3253  */
3254 
mail_recent(MAILSTREAM * stream,unsigned long recent)3255 void mail_recent (MAILSTREAM *stream,unsigned long recent)
3256 {
3257   char tmp[MAILTMPLEN];
3258   if (recent <= stream->nmsgs) stream->recent = recent;
3259   else {
3260     sprintf (tmp,"Non-existent recent message(s) %lu, nmsgs=%lu",
3261 	     recent,stream->nmsgs);
3262     mm_log (tmp,ERROR);
3263   }
3264 }
3265 
3266 
3267 /* Mail message n is expunged
3268  * Accepts: mail stream
3269  *	    message #
3270  */
3271 
mail_expunged(MAILSTREAM * stream,unsigned long msgno)3272 void mail_expunged (MAILSTREAM *stream,unsigned long msgno)
3273 {
3274   char tmp[MAILTMPLEN];
3275   MESSAGECACHE *elt;
3276   if (msgno > stream->nmsgs) {
3277     sprintf (tmp,"Expunge of non-existent message %lu, nmsgs=%lu",
3278 	     msgno,stream->nmsgs);
3279     mm_log (tmp,ERROR);
3280   }
3281   else {
3282     elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
3283 				/* notify main program of change */
3284     if (!stream->silent) MM_EXPUNGED (stream,msgno);
3285     if (elt) {			/* if an element is there */
3286       elt->msgno = 0;		/* invalidate its message number and free */
3287       (*mailcache) (stream,msgno,CH_FREE);
3288       (*mailcache) (stream,msgno,CH_FREESORTCACHE);
3289     }
3290 				/* expunge the slot */
3291     (*mailcache) (stream,msgno,CH_EXPUNGE);
3292     --stream->nmsgs;		/* update stream status */
3293     if (stream->msgno) {	/* have stream pointers? */
3294 				/* make sure the short cache is nuked */
3295       if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS);
3296       else stream->msgno = 0;	/* make sure invalidated in any case */
3297     }
3298   }
3299 }
3300 
3301 /* Mail stream status routines */
3302 
3303 
3304 /* Mail lock stream
3305  * Accepts: mail stream
3306  */
3307 
mail_lock(MAILSTREAM * stream)3308 void mail_lock (MAILSTREAM *stream)
3309 {
3310   if (stream->lock) {
3311     char tmp[MAILTMPLEN];
3312     sprintf (tmp,"Lock when already locked, mbx=%.80s",
3313 	     stream->mailbox ? stream->mailbox : "???");
3314     fatal (tmp);
3315   }
3316   else stream->lock = T;	/* lock stream */
3317 }
3318 
3319 
3320 /* Mail unlock stream
3321  * Accepts: mail stream
3322  */
3323 
mail_unlock(MAILSTREAM * stream)3324 void mail_unlock (MAILSTREAM *stream)
3325 {
3326   if (!stream->lock) fatal ("Unlock when not locked");
3327   else stream->lock = NIL;	/* unlock stream */
3328 }
3329 
3330 
3331 /* Mail turn on debugging telemetry
3332  * Accepts: mail stream
3333  */
3334 
mail_debug(MAILSTREAM * stream)3335 void mail_debug (MAILSTREAM *stream)
3336 {
3337   stream->debug = T;		/* turn on debugging telemetry */
3338   if (stream->dtb) (*stream->dtb->parameters) (ENABLE_DEBUG,stream);
3339 }
3340 
3341 
3342 /* Mail turn off debugging telemetry
3343  * Accepts: mail stream
3344  */
3345 
mail_nodebug(MAILSTREAM * stream)3346 void mail_nodebug (MAILSTREAM *stream)
3347 {
3348   stream->debug = NIL;		/* turn off debugging telemetry */
3349   if (stream->dtb) (*stream->dtb->parameters) (DISABLE_DEBUG,stream);
3350 }
3351 
3352 
3353 /* Mail log to debugging telemetry
3354  * Accepts: message
3355  *	    flag that data is "sensitive"
3356  */
3357 
mail_dlog(char * string,long flag)3358 void mail_dlog (char *string,long flag)
3359 {
3360   mm_dlog ((debugsensitive || !flag) ? string : "<suppressed>");
3361 }
3362 
3363 /* Mail parse UID sequence
3364  * Accepts: mail stream
3365  *	    sequence to parse
3366  * Returns: T if parse successful, else NIL
3367  */
3368 
mail_uid_sequence(MAILSTREAM * stream,unsigned char * sequence)3369 long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence)
3370 {
3371   unsigned long i,j,k,x,y;
3372   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
3373   while (sequence && *sequence){/* while there is something to parse */
3374     if (*sequence == '*') {	/* maximum message */
3375       i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
3376       sequence++;		/* skip past * */
3377     }
3378 				/* parse and validate message number */
3379 				/* parse and validate message number */
3380     else if (!isdigit (*sequence)) {
3381       MM_LOG ("Syntax error in sequence",ERROR);
3382       return NIL;
3383     }
3384     else if (!(i = strtoul (sequence,(char **) &sequence,10))) {
3385       MM_LOG ("UID may not be zero",ERROR);
3386       return NIL;
3387     }
3388     switch (*sequence) {	/* see what the delimiter is */
3389     case ':':			/* sequence range */
3390       if (*++sequence == '*') {	/* maximum message */
3391 	j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
3392 	sequence++;		/* skip past * */
3393       }
3394 				/* parse end of range */
3395       else if (!(j = strtoul (sequence,(char **) &sequence,10))) {
3396 	MM_LOG ("UID sequence range invalid",ERROR);
3397 	return NIL;
3398       }
3399       if (*sequence && *sequence++ != ',') {
3400 	MM_LOG ("UID sequence range syntax error",ERROR);
3401 	return NIL;
3402       }
3403       if (i > j) {		/* swap the range if backwards */
3404 	x = i; i = j; j = x;
3405       }
3406       x = mail_msgno (stream,i);/* get msgnos */
3407       y = mail_msgno (stream,j);/* for both UIDS (don't && it) */
3408 				/* easy if both UIDs valid */
3409       if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T;
3410 				/* start UID valid, end is not */
3411       else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j))
3412 	mail_elt (stream,x++)->sequence = T;
3413 				/* end UID valid, start is not */
3414       else if (y) for (x = 1; x <= y; x++) {
3415 	if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T;
3416       }
3417 				/* neither is valid, ugh */
3418       else for (x = 1; x <= stream->nmsgs; x++)
3419 	if (((k = mail_uid (stream,x)) >= i) && (k <= j))
3420 	  mail_elt (stream,x)->sequence = T;
3421       break;
3422     case ',':			/* single message */
3423       ++sequence;		/* skip the delimiter, fall into end case */
3424     case '\0':			/* end of sequence, mark this message */
3425       if ((x = mail_msgno (stream,i)) != 0L) mail_elt (stream,x)->sequence = T;
3426       break;
3427     default:			/* anything else is a syntax error! */
3428       MM_LOG ("UID sequence syntax error",ERROR);
3429       return NIL;
3430     }
3431   }
3432   return T;			/* successfully parsed sequence */
3433 }
3434 
3435 /* Mail see if line list matches that in cache
3436  * Accepts: candidate line list
3437  *	    cached line list
3438  *	    matching flags
3439  * Returns: T if match, NIL if no match
3440  */
3441 
mail_match_lines(STRINGLIST * lines,STRINGLIST * msglines,long flags)3442 long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags)
3443 {
3444   unsigned long i;
3445   unsigned char *s,*t;
3446   STRINGLIST *m;
3447   if (!msglines) return T;	/* full header is in cache */
3448 				/* need full header but filtered in cache */
3449   if ((flags & FT_NOT) || !lines) return NIL;
3450   do {				/* make sure all present & accounted for */
3451     for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) {
3452       for (s = lines->text.data,t = m->text.data,i = lines->text.size;
3453 	   i && !compare_uchar (*s,*t); s++,t++,i--);
3454       if (!i) break;		/* this line matches */
3455     }
3456     if (!m) return NIL;		/* didn't find in the list */
3457   }
3458   while ((lines = lines->next) != NULL);
3459   return T;			/* all lines found */
3460 }
3461 
3462 /* Mail filter text by header lines
3463  * Accepts: text to filter, with trailing null
3464  *	    length of text
3465  *	    list of lines
3466  *	    fetch flags
3467  * Returns: new text size, text overwritten
3468  */
3469 
mail_filter(char * text,unsigned long len,STRINGLIST * lines,long flags)3470 unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
3471 			   long flags)
3472 {
3473   STRINGLIST *hdrs;
3474   int notfound;
3475   unsigned long i;
3476   char c,*s,*e,*t,tmp[MAILTMPLEN];
3477   char *src = text;
3478   char *dst = src;
3479   char *end = text + len;
3480   text[len] = '\012';		/* guard against running off buffer */
3481   while (src < end) {		/* process header */
3482 				/* slurp header line name */
3483     for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp;
3484 	 (s < e) && ((c = (*s ? *s : (*s = ' '))) != ':') &&
3485 	 ((c > ' ') ||
3486 	  ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012')));
3487 	 *t++ = *s++);
3488     *t = '\0';			/* tie off */
3489     notfound = T;		/* not found yet */
3490     if ((i = t - tmp) != 0L)	/* see if found in header */
3491       for (hdrs = lines; hdrs && notfound; hdrs = hdrs->next)
3492 	if ((hdrs->text.size == i) && !compare_csizedtext (tmp,&hdrs->text))
3493 	  notfound = NIL;
3494 				/* skip header line if not wanted */
3495     if (i && ((flags & FT_NOT) ? !notfound : notfound))
3496       while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3497 	      (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3498 	      (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3499 	      (*src++ != '\012')) ||
3500 	     ((src < end) && ((*src == ' ') || (*src == '\t'))));
3501     else if (src == dst) {	/* copy to self */
3502       while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3503 	      (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3504 	      (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
3505 	      (*src++ != '\012')) ||
3506 	     ((src < end) && ((*src == ' ') || (*src == '\t'))));
3507       dst = src;		/* update destination */
3508     }
3509     else {			/* copy line and any continuation line */
3510       while ((((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
3511 	      ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
3512 	      ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
3513 	      ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
3514 	      ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012'))||
3515 	     ((src < end) && ((*src == ' ') || (*src == '\t'))));
3516 				/* in case hit the guard LF */
3517       if (src > end) dst -= (src - end);
3518     }
3519   }
3520   *dst = '\0';			/* tie off destination */
3521   return dst - text;
3522 }
3523 
3524 /* Local mail search message
3525  * Accepts: MAIL stream
3526  *	    message number
3527  *	    optional section specification
3528  *	    search program
3529  * Returns: T if found, NIL otherwise
3530  */
3531 
mail_search_msg(MAILSTREAM * stream,unsigned long msgno,char * section,SEARCHPGM * pgm)3532 long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
3533 		      SEARCHPGM *pgm)
3534 {
3535   unsigned short d;
3536   char tmp[MAILTMPLEN];
3537   MESSAGECACHE *elt = mail_elt (stream,msgno);
3538   SEARCHHEADER *hdr;
3539   SEARCHOR *or;
3540   SEARCHPGMLIST *not;
3541   unsigned long now = (unsigned long) time (0);
3542   if (pgm->msgno || pgm->uid) {	/* message set searches */
3543     SEARCHSET *set;
3544 				/* message sequences */
3545     if (pgm->msgno) {		/* inside this message sequence set */
3546       for (set = pgm->msgno; set; set = set->next)
3547 	if (set->last ? ((set->first <= set->last) ?
3548 			 ((msgno >= set->first) && (msgno <= set->last)) :
3549 			 ((msgno >= set->last) && (msgno <= set->first))) :
3550 	    msgno == set->first) break;
3551       if (!set) return NIL;	/* not found within sequence */
3552     }
3553     if (pgm->uid) {		/* inside this unique identifier set */
3554       unsigned long uid = mail_uid (stream,msgno);
3555       for (set = pgm->uid; set; set = set->next)
3556 	if (set->last ? ((set->first <= set->last) ?
3557 			 ((uid >= set->first) && (uid <= set->last)) :
3558 			 ((uid >= set->last) && (uid <= set->first))) :
3559 	    uid == set->first) break;
3560       if (!set) return NIL;	/* not found within sequence */
3561     }
3562   }
3563 
3564   /* Fast data searches */
3565 				/* need to fetch fast data? */
3566   if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) ||
3567       (!elt->year && (pgm->before || pgm->on || pgm->since ||
3568 		      pgm->older || pgm->younger)) ||
3569       (!elt->valid && (pgm->answered || pgm->unanswered ||
3570 		       pgm->deleted || pgm->undeleted ||
3571 		       pgm->draft || pgm->undraft ||
3572 		       pgm->flagged || pgm->unflagged ||
3573 		       pgm->recent || pgm->old ||
3574 		       pgm->seen || pgm->unseen ||
3575 		       pgm->keyword || pgm->unkeyword))) {
3576     unsigned long i;
3577     MESSAGECACHE *ielt;
3578     for (i = elt->msgno;	/* find last unloaded message in range */
3579 	 (i < stream->nmsgs) && (ielt = mail_elt (stream,i+1)) &&
3580 	   ((!ielt->rfc822_size && (pgm->larger || pgm->smaller)) ||
3581 	    (!ielt->year && (pgm->before || pgm->on || pgm->since ||
3582 			     pgm->older || pgm->younger)) ||
3583 	    (!ielt->valid && (pgm->answered || pgm->unanswered ||
3584 			      pgm->deleted || pgm->undeleted ||
3585 			      pgm->draft || pgm->undraft ||
3586 			      pgm->flagged || pgm->unflagged ||
3587 			      pgm->recent || pgm->old ||
3588 			      pgm->seen || pgm->unseen ||
3589 			      pgm->keyword || pgm->unkeyword))); ++i);
3590     if (i == elt->msgno) sprintf (tmp,"%lu",elt->msgno);
3591     else sprintf (tmp,"%lu:%lu",elt->msgno,i);
3592     mail_fetch_fast (stream,tmp,NIL);
3593   }
3594 				/* size ranges */
3595   if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) ||
3596       (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL;
3597 				/* message flags */
3598   if ((pgm->answered && !elt->answered) ||
3599       (pgm->unanswered && elt->answered) ||
3600       (pgm->deleted && !elt->deleted) ||
3601       (pgm->undeleted && elt->deleted) ||
3602       (pgm->draft && !elt->draft) ||
3603       (pgm->undraft && elt->draft) ||
3604       (pgm->flagged && !elt->flagged) ||
3605       (pgm->unflagged && elt->flagged) ||
3606       (pgm->recent && !elt->recent) ||
3607       (pgm->old && elt->recent) ||
3608       (pgm->seen && !elt->seen) ||
3609       (pgm->unseen && elt->seen)) return NIL;
3610 				/* keywords */
3611   if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
3612       (pgm->unkeyword && !mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
3613     return NIL;
3614 				/* internal date ranges */
3615   if (pgm->before || pgm->on || pgm->since) {
3616     d = mail_shortdate (elt->year,elt->month,elt->day);
3617     if (pgm->before && (d >= pgm->before)) return NIL;
3618     if (pgm->on && (d != pgm->on)) return NIL;
3619     if (pgm->since && (d < pgm->since)) return NIL;
3620   }
3621   if (pgm->older || pgm->younger) {
3622     unsigned long msgd = mail_longdate (elt);
3623     if (pgm->older && msgd > (now - pgm->older)) return NIL;
3624     if (pgm->younger && msgd < (now - pgm->younger)) return NIL;
3625   }
3626 
3627 				/* envelope searches */
3628   if (pgm->sentbefore || pgm->senton || pgm->sentsince ||
3629       pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject ||
3630       pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to ||
3631       pgm->message_id || pgm->newsgroups || pgm->followup_to ||
3632       pgm->references) {
3633     ENVELOPE *env;
3634     MESSAGECACHE delt;
3635     if (section) {		/* use body part envelope */
3636       BODY *body = mail_body (stream,msgno,section);
3637       env = (body && (body->type == TYPEMESSAGE) && body->subtype &&
3638 	     !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL;
3639     }
3640     else {			/* use top level envelope if no section */
3641       if (pgm->header && !stream->scache && !(stream->dtb->flags & DR_LOCAL))
3642 	mail_fetch_header(stream,msgno,NIL,NIL,NIL,FT_PEEK|FT_SEARCHLOOKAHEAD);
3643       env = mail_fetchenvelope (stream,msgno);
3644     }
3645     if (!env) return NIL;	/* no envelope obtained */
3646 				/* sent date ranges */
3647     if ((pgm->sentbefore || pgm->senton || pgm->sentsince) &&
3648 	(!mail_parse_date (&delt,env->date) ||
3649 	 !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
3650 	 (pgm->sentbefore && (d >= pgm->sentbefore)) ||
3651 	 (pgm->senton && (d != pgm->senton)) ||
3652 	 (pgm->sentsince && (d < pgm->sentsince)))) return NIL;
3653 				/* search headers */
3654     if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
3655 	(pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
3656 	(pgm->from && !mail_search_addr (env->from,pgm->from)) ||
3657 	(pgm->to && !mail_search_addr (env->to,pgm->to)) ||
3658 	(pgm->subject && !mail_search_header_text (env->subject,pgm->subject)))
3659       return NIL;
3660     /* These criteria are not supported by IMAP and have to be emulated */
3661     if ((pgm->return_path &&
3662 	 !mail_search_addr (env->return_path,pgm->return_path)) ||
3663 	(pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
3664 	(pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
3665 	(pgm->in_reply_to &&
3666 	 !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
3667 	(pgm->message_id &&
3668 	!mail_search_header_text (env->message_id,pgm->message_id)) ||
3669 	(pgm->newsgroups &&
3670 	 !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
3671 	(pgm->followup_to &&
3672 	 !mail_search_header_text (env->followup_to,pgm->followup_to)) ||
3673 	(pgm->references &&
3674 	 !mail_search_header_text (env->references,pgm->references)))
3675       return NIL;
3676   }
3677 
3678 				/* search header lines */
3679   for (hdr = pgm->header; hdr; hdr = hdr->next) {
3680     char *t,*e,*v;
3681     SIZEDTEXT s;
3682     STRINGLIST sth,stc;
3683     sth.next = stc.next = NIL;	/* only one at a time */
3684     sth.text.data = hdr->line.data;
3685     sth.text.size = hdr->line.size;
3686 				/* get the header text */
3687     if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
3688 				FT_INTERNAL | FT_PEEK |
3689 				(section ? NIL : FT_SEARCHLOOKAHEAD))) &&
3690 	strchr (t,':')) {
3691       if (hdr->text.size) {	/* anything matches empty search string */
3692 				/* non-empty, copy field data */
3693 	s.data = (unsigned char *) fs_get (s.size + 1);
3694 				/* for each line */
3695 	for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
3696 	default:		/* non-continuation, skip leading field name */
3697 	  while ((t < e) && (*t++ != ':'));
3698 	  if ((t < e) && (*t == ':')) t++;
3699 	case '\t': case ' ':	/* copy field data  */
3700 	  while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
3701 	  *v++ = '\n';		/* tie off line */
3702 	  while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
3703 	}
3704 				/* calculate true size */
3705 	s.size = v - (char *) s.data;
3706 	*v = '\0';		/* tie off results */
3707 	stc.text.data = hdr->text.data;
3708 	stc.text.size = hdr->text.size;
3709 				/* search header */
3710 	if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
3711 	else {			/* search failed */
3712 	  fs_give ((void **) &s.data);
3713 	  return NIL;
3714 	}
3715       }
3716     }
3717     else return NIL;		/* no matching header text */
3718   }
3719 				/* search strings */
3720   if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))||
3721       (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL)))
3722     return NIL;
3723 				/* logical conditions */
3724   for (or = pgm->or; or; or = or->next)
3725     if (!(mail_search_msg (stream,msgno,section,or->first) ||
3726 	  mail_search_msg (stream,msgno,section,or->second))) return NIL;
3727   for (not = pgm->not; not; not = not->next)
3728     if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL;
3729   return T;
3730 }
3731 
3732 /* Mail search message header null-terminated text
3733  * Accepts: header text
3734  *	    strings to search
3735  * Returns: T if search found a match
3736  */
3737 
mail_search_header_text(char * s,STRINGLIST * st)3738 long mail_search_header_text (char *s,STRINGLIST *st)
3739 {
3740   SIZEDTEXT h;
3741 				/* have any text? */
3742   if ((h.data = (unsigned char *) s) != NULL) {
3743     h.size = strlen (s);	/* yes, get its size */
3744     return mail_search_header (&h,st);
3745   }
3746   return NIL;
3747 }
3748 
3749 
3750 /* Mail search message header
3751  * Accepts: header as sized text
3752  *	    strings to search
3753  * Returns: T if search found a match
3754  */
3755 
mail_search_header(SIZEDTEXT * hdr,STRINGLIST * st)3756 long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st)
3757 {
3758   SIZEDTEXT h;
3759   long ret = LONGT;
3760 				/* make UTF-8 version of header */
3761   utf8_mime2text (hdr,&h,U8T_CANONICAL);
3762   while (h.size && ((h.data[h.size-1]=='\015') || (h.data[h.size-1]=='\012')))
3763     --h.size;			/* slice off trailing newlines */
3764   do if (h.size ?		/* search non-empty string */
3765 	 !ssearch (h.data,h.size,st->text.data,st->text.size) : st->text.size)
3766     ret = NIL;
3767   while (ret && (st = st->next));
3768   if (h.data != hdr->data) fs_give ((void **) &h.data);
3769   return ret;
3770 }
3771 
3772 /* Mail search message body
3773  * Accepts: MAIL stream
3774  *	    message number
3775  *	    optional section specification
3776  *	    string list
3777  *	    flags
3778  * Returns: T if search found a match
3779  */
3780 
mail_search_text(MAILSTREAM * stream,unsigned long msgno,char * section,STRINGLIST * st,long flags)3781 long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
3782 		       STRINGLIST *st,long flags)
3783 {
3784   BODY *body;
3785   long ret = NIL;
3786   STRINGLIST *s = mail_newstringlist ();
3787   mailgets_t omg = mailgets;
3788   if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets;
3789 				/* strings to search */
3790   for (stream->private.search.string = s; st;) {
3791     s->text.data = st->text.data;
3792     s->text.size = st->text.size;
3793     if ((st = st->next) != NULL) s = s->next = mail_newstringlist ();
3794   }
3795   stream->private.search.text = NIL;
3796   if (flags) {			/* want header? */
3797     SIZEDTEXT s,t;
3798     s.data = (unsigned char *)
3799       mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK);
3800     utf8_mime2text (&s,&t,U8T_CANONICAL);
3801     ret = mail_search_string_work (&t,&stream->private.search.string);
3802     if (t.data != s.data) fs_give ((void **) &t.data);
3803   }
3804   if (!ret) {			/* still looking for match? */
3805 				/* no section, get top-level body */
3806     if (!section) mail_fetchstructure (stream,msgno,&body);
3807 				/* get body of nested message */
3808     else if ((body = mail_body (stream,msgno,section)) &&
3809 	     (body->type == TYPEMULTIPART) && body->subtype &&
3810 	     !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body;
3811     if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags);
3812   }
3813   mailgets = omg;		/* restore former gets routine */
3814 				/* clear searching */
3815   for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL;
3816   mail_free_stringlist (&stream->private.search.string);
3817   stream->private.search.text = NIL;
3818   return ret;
3819 }
3820 
3821 /* Mail search message body text parts
3822  * Accepts: MAIL stream
3823  *	    message number
3824  *	    current body pointer
3825  *	    hierarchical level prefix
3826  *	    position at current hierarchical level
3827  *	    string list
3828  *	    flags
3829  * Returns: T if search found a match
3830  */
3831 
mail_search_body(MAILSTREAM * stream,unsigned long msgno,BODY * body,char * prefix,unsigned long section,long flags)3832 long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
3833 		       char *prefix,unsigned long section,long flags)
3834 {
3835   long ret = NIL;
3836   unsigned long i;
3837   char *s,*t,sect[MAILTMPLEN];
3838   SIZEDTEXT st,h;
3839   PART *part;
3840   PARAMETER *param;
3841   if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL;
3842   sprintf (sect,"%s%lu",prefix ? prefix : "",section++);
3843   if (flags && prefix) {	/* want to search MIME header too? */
3844     st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size,
3845 						 FT_INTERNAL | FT_PEEK);
3846     if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
3847     else {
3848 				/* make UTF-8 version of header */
3849       utf8_mime2text (&st,&h,U8T_CANONICAL);
3850       ret = mail_search_string_work (&h,&stream->private.search.string);
3851       if (h.data != st.data) fs_give ((void **) &h.data);
3852     }
3853   }
3854   if (!ret) switch (body->type) {
3855   case TYPEMULTIPART:
3856 				/* extend prefix if not first time */
3857     s = prefix ? strcat (sect,".") : "";
3858     for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next)
3859       ret = mail_search_body (stream,msgno,&part->body,s,i,flags);
3860     break;
3861   case TYPEMESSAGE:
3862     if (!strcmp (body->subtype,"RFC822")) {
3863       if (flags) {		/* want to search nested message header? */
3864 	st.data = (unsigned char *)
3865 	  mail_fetch_header (stream,msgno,sect,NIL,&st.size,
3866 			     FT_INTERNAL | FT_PEEK);
3867 	if (stream->dtb->flags & DR_LOWMEM) ret =stream->private.search.result;
3868 	else {
3869 				/* make UTF-8 version of header */
3870 	  utf8_mime2text (&st,&h,U8T_CANONICAL);
3871 	  ret = mail_search_string_work (&h,&stream->private.search.string);
3872 	  if (h.data != st.data) fs_give ((void **) &h.data);
3873 	}
3874       }
3875       if ((body = body->nested.msg->body) != NULL)
3876 	ret = (body->type == TYPEMULTIPART) ?
3877 	  mail_search_body (stream,msgno,body,(prefix ? prefix : ""),
3878 			    section - 1,flags) :
3879 	mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags);
3880       break;
3881     }
3882 				/* non-MESSAGE/RFC822 falls into text case */
3883 
3884   case TYPETEXT:
3885     s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK);
3886     if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
3887     else {
3888       for (t = NIL,param = body->parameter; param && !t; param = param->next)
3889 	if (!strcmp (param->attribute,"CHARSET")) t = param->value;
3890       switch (body->encoding) {	/* what encoding? */
3891       case ENCBASE64:
3892 	if ((st.data = (unsigned char *)
3893 	    rfc822_base64 ((unsigned char *) s,i,&st.size)) != NULL) {
3894 	  ret = mail_search_string (&st,t,&stream->private.search.string);
3895 	  fs_give ((void **) &st.data);
3896 	}
3897 	break;
3898       case ENCQUOTEDPRINTABLE:
3899 	if ((st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) != NULL) {
3900 	  ret = mail_search_string (&st,t,&stream->private.search.string);
3901 	  fs_give ((void **) &st.data);
3902 	}
3903 	break;
3904       default:
3905 	st.data = (unsigned char *) s;
3906 	st.size = i;
3907 	ret = mail_search_string (&st,t,&stream->private.search.string);
3908 	break;
3909       }
3910     }
3911     break;
3912   }
3913   return ret;
3914 }
3915 
3916 /* Mail search text
3917  * Accepts: sized text to search
3918  *	    character set of sized text
3919  *	    string list of search keys
3920  * Returns: T if search found a match
3921  */
3922 
mail_search_string(SIZEDTEXT * s,char * charset,STRINGLIST ** st)3923 long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st)
3924 {
3925   SIZEDTEXT u;
3926   long ret;
3927   STRINGLIST **sc = st;
3928 				/* convert to UTF-8 as best we can */
3929   if (!utf8_text (s,charset,&u,U8T_CANONICAL))
3930     utf8_text (s,NIL,&u,U8T_CANONICAL);
3931   ret = mail_search_string_work (&u,st);
3932   if (u.data != s->data) fs_give ((void **) &u.data);
3933   return ret;
3934 }
3935 
3936 
3937 /* Mail search text worker routine
3938  * Accepts: sized text to search
3939  *	    string list of search keys
3940  * Returns: T if search found a match
3941  */
3942 
mail_search_string_work(SIZEDTEXT * s,STRINGLIST ** st)3943 long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st)
3944 {
3945   void *t;
3946   STRINGLIST **sc = st;
3947   while (*sc) {			/* run down criteria list */
3948     if (ssearch (s->data,s->size,(*sc)->text.data,(*sc)->text.size)) {
3949       t = (void *) (*sc);	/* found one, need to flush this */
3950       *sc = (*sc)->next;	/* remove it from the list */
3951       fs_give (&t);		/* flush the buffer */
3952     }
3953     else sc = &(*sc)->next;	/* move to next in list */
3954   }
3955   return *st ? NIL : LONGT;
3956 }
3957 
3958 
3959 /* Mail search keyword
3960  * Accepts: MAIL stream
3961  *	    elt to get flags from
3962  *	    keyword list
3963  *	    T for keyword search, NIL for unkeyword search
3964  * Returns: T if search found a match
3965  */
3966 
mail_search_keyword(MAILSTREAM * stream,MESSAGECACHE * elt,STRINGLIST * st,long flag)3967 long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st,
3968 			  long flag)
3969 {
3970   int i,j;
3971   unsigned long f = 0;
3972   unsigned long tf;
3973   do {
3974     for (i = 0; (j = (i < NUSERFLAGS) && stream->user_flags[i]); ++i)
3975       if (!compare_csizedtext (stream->user_flags[i],&st->text)) {
3976 	f |= (1 << i);
3977 	break;
3978       }
3979     if (flag && !j) return NIL;
3980   } while ((st = st->next) != NULL);
3981   tf = elt->user_flags & f;	/* get set flags which match */
3982   return flag ? (f == tf) : !tf;
3983 }
3984 
3985 /* Mail search an address list
3986  * Accepts: address list
3987  *	    string list
3988  * Returns: T if search found a match
3989  */
3990 
3991 #define SEARCHBUFLEN (size_t) 2000
3992 #define SEARCHBUFSLOP (size_t) 5
3993 
mail_search_addr(ADDRESS * adr,STRINGLIST * st)3994 long mail_search_addr (ADDRESS *adr,STRINGLIST *st)
3995 {
3996   ADDRESS *a,tadr;
3997   SIZEDTEXT txt;
3998   char tmp[SENDBUFLEN + 1];
3999   size_t i = SEARCHBUFLEN;
4000   size_t k;
4001   long ret = NIL;
4002   if (adr) {
4003     txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP);
4004 				/* never an error or next */
4005     tadr.error = NIL,tadr.next = NIL;
4006 				/* write address list */
4007     for (txt.size = 0,a = adr; a; a = a->next) {
4008       k = (tadr.mailbox = a->mailbox) ? 4 + 2*strlen (a->mailbox) : 3;
4009       if ((tadr.personal = a->personal) != NULL) k += 3 + 2*strlen (a->personal);
4010       if ((tadr.adl = a->adl) != NULL) k += 3 + 2*strlen (a->adl);
4011       if ((tadr.host = a->host) != NULL) k += 3 + 2*strlen (a->host);
4012       if (tadr.personal || tadr.adl) k += 2;
4013       if (k < (SENDBUFLEN-10)) {/* ignore ridiculous addresses */
4014 	tmp[0] = '\0';
4015 	rfc822_write_address (tmp,&tadr);
4016 				/* resize buffer if necessary */
4017 	if (((k = strlen (tmp)) + txt.size) > i)
4018 	  fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN));
4019 				/* add new address */
4020 	memcpy (txt.data + txt.size,tmp,k);
4021 	txt.size += k;
4022 				/* another address follows */
4023 	if (a->next) txt.data[txt.size++] = ',';
4024       }
4025     }
4026     txt.data[txt.size] = '\0';	/* tie off string */
4027     ret = mail_search_header (&txt,st);
4028     fs_give ((void **) &txt.data);
4029   }
4030   return ret;
4031 }
4032 
4033 /* Get string for low-memory searching
4034  * Accepts: readin function pointer
4035  *	    stream to use
4036  *	    number of bytes
4037  *	    gets data packet
4038 
4039  *	    mail stream
4040  *	    message number
4041  *	    descriptor string
4042  *	    option flags
4043  * Returns: NIL, always
4044  */
4045 
4046 #define SEARCHSLOP 128
4047 
mail_search_gets(readfn_t f,void * stream,unsigned long size,GETS_DATA * md)4048 char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
4049 			GETS_DATA *md)
4050 {
4051   unsigned long i;
4052   char tmp[MAILTMPLEN+SEARCHSLOP+1];
4053   SIZEDTEXT st;
4054 				/* better not be called unless searching */
4055   if (!md->stream->private.search.string) {
4056     sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
4057 	     md->stream->mailbox,
4058 	     (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
4059     fatal (tmp);
4060   }
4061 				/* initially no match for search */
4062   md->stream->private.search.result = NIL;
4063 				/* make sure buffer clear */
4064   memset (st.data = (unsigned char *) tmp,'\0',
4065 	  (size_t) MAILTMPLEN+SEARCHSLOP+1);
4066 				/* read first buffer */
4067   (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp);
4068 				/* search for text */
4069   if (mail_search_string (&st,NIL,&md->stream->private.search.string))
4070     md->stream->private.search.result = T;
4071   else if (size -= i) {		/* more to do, blat slop down */
4072     memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP);
4073     do {			/* read subsequent buffers one at a time */
4074       (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP);
4075       st.size = i + SEARCHSLOP;
4076       if (mail_search_string (&st,NIL,&md->stream->private.search.string))
4077 	md->stream->private.search.result = T;
4078       else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP);
4079     }
4080     while ((size -= i) && !md->stream->private.search.result);
4081   }
4082   if (size) {			/* toss out everything after that */
4083     do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp);
4084     while (size -= i);
4085   }
4086   return NIL;
4087 }
4088 
4089 /* Mail parse search criteria
4090  * Accepts: criteria
4091  * Returns: search program if parse successful, else NIL
4092  */
4093 
mail_criteria(char * criteria)4094 SEARCHPGM *mail_criteria (char *criteria)
4095 {
4096   SEARCHPGM *pgm = NIL;
4097   char *criterion,*r,tmp[MAILTMPLEN];
4098   int f;
4099   if (criteria) {		/* only if criteria defined */
4100 				/* make writeable copy of criteria */
4101     criteria = cpystr (criteria);
4102 				/* for each criterion */
4103     for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
4104 	 criterion; (criterion = strtok_r (NIL," ",&r))) {
4105       f = NIL;			/* init then scan the criterion */
4106       switch (*ucase (criterion)) {
4107       case 'A':			/* possible ALL, ANSWERED */
4108 	if (!strcmp (criterion+1,"LL")) f = T;
4109 	else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
4110 	break;
4111       case 'B':			/* possible BCC, BEFORE, BODY */
4112 	if (!strcmp (criterion+1,"CC"))
4113 	  f = mail_criteria_string (&pgm->bcc,&r);
4114 	else if (!strcmp (criterion+1,"EFORE"))
4115 	  f = mail_criteria_date (&pgm->before,&r);
4116 	else if (!strcmp (criterion+1,"ODY"))
4117 	  f = mail_criteria_string (&pgm->body,&r);
4118 	break;
4119       case 'C':			/* possible CC */
4120 	if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
4121 	break;
4122       case 'D':			/* possible DELETED */
4123 	if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
4124 	break;
4125       case 'F':			/* possible FLAGGED, FROM */
4126 	if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
4127 	else if (!strcmp (criterion+1,"ROM"))
4128 	  f = mail_criteria_string (&pgm->from,&r);
4129 	break;
4130       case 'K':			/* possible KEYWORD */
4131 	if (!strcmp (criterion+1,"EYWORD"))
4132 	  f = mail_criteria_string (&pgm->keyword,&r);
4133 	break;
4134 
4135       case 'N':			/* possible NEW */
4136 	if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
4137 	break;
4138       case 'O':			/* possible OLD, ON */
4139 	if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
4140 	else if (!strcmp (criterion+1,"N"))
4141 	  f = mail_criteria_date (&pgm->on,&r);
4142 	break;
4143       case 'R':			/* possible RECENT */
4144 	if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
4145 	break;
4146       case 'S':			/* possible SEEN, SINCE, SUBJECT */
4147 	if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
4148 	else if (!strcmp (criterion+1,"INCE"))
4149 	  f = mail_criteria_date (&pgm->since,&r);
4150 	else if (!strcmp (criterion+1,"UBJECT"))
4151 	  f = mail_criteria_string (&pgm->subject,&r);
4152 	break;
4153       case 'T':			/* possible TEXT, TO */
4154 	if (!strcmp (criterion+1,"EXT"))
4155 	  f = mail_criteria_string (&pgm->text,&r);
4156 	else if (!strcmp (criterion+1,"O"))
4157 	  f = mail_criteria_string (&pgm->to,&r);
4158 	break;
4159       case 'U':			/* possible UN* */
4160 	if (criterion[1] == 'N') {
4161 	  if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
4162 	  else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
4163 	  else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
4164 	  else if (!strcmp (criterion+2,"KEYWORD"))
4165 	    f = mail_criteria_string (&pgm->unkeyword,&r);
4166 	  else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
4167 	}
4168 	break;
4169       default:			/* we will barf below */
4170 	break;
4171       }
4172       if (!f) {			/* if can't identify criterion */
4173 	sprintf (tmp,"Unknown search criterion: %.30s",criterion);
4174 	MM_LOG (tmp,ERROR);
4175 	mail_free_searchpgm (&pgm);
4176 	break;
4177       }
4178     }
4179 				/* no longer need copy of criteria */
4180     fs_give ((void **) &criteria);
4181   }
4182   return pgm;
4183 }
4184 
4185 /* Parse a date
4186  * Accepts: pointer to date integer to return
4187  *	    pointer to strtok state
4188  * Returns: T if successful, else NIL
4189  */
4190 
mail_criteria_date(unsigned short * date,char ** r)4191 int mail_criteria_date (unsigned short *date,char **r)
4192 {
4193   STRINGLIST *s = NIL;
4194   MESSAGECACHE elt;
4195 				/* parse the date and return fn if OK */
4196   int ret = (mail_criteria_string (&s,r) &&
4197 	     mail_parse_date (&elt,(char *) s->text.data) &&
4198 	     (*date = mail_shortdate (elt.year,elt.month,elt.day))) ?
4199 	       T : NIL;
4200   if (s) mail_free_stringlist (&s);
4201   return ret;
4202 }
4203 
4204 /* Calculate shortdate from elt values
4205  * Accepts: year (0 = BASEYEAR)
4206  *	    month (1 = January)
4207  *	    day
4208  * Returns: shortdate
4209  */
4210 
mail_shortdate(unsigned int year,unsigned int month,unsigned int day)4211 unsigned short mail_shortdate (unsigned int year,unsigned int month,
4212 			       unsigned int day)
4213 {
4214   return (year << 9) + (month << 5) + day;
4215 }
4216 
4217 /* Parse a string
4218  * Accepts: pointer to stringlist
4219  *	    pointer to strtok state
4220  * Returns: T if successful, else NIL
4221  */
4222 
mail_criteria_string(STRINGLIST ** s,char ** r)4223 int mail_criteria_string (STRINGLIST **s,char **r)
4224 {
4225   unsigned long n;
4226   char e,*d,*end = " ",*c = strtok_r (NIL,"",r);
4227   if (!c) return NIL;		/* missing argument */
4228   switch (*c) {			/* see what the argument is */
4229   case '{':			/* literal string */
4230     n = strtoul (c+1,&d,10);	/* get its length */
4231     if ((*d++ == '}') && (*d++ == '\015') && (*d++ == '\012') &&
4232 	(!(*(c = d + n)) || (*c == ' '))) {
4233       e = *--c;			/* store old delimiter */
4234       *c = '\377';		/* make sure not a space */
4235       strtok_r (c," ",r);	/* reset the strtok mechanism */
4236       *c = e;			/* put character back */
4237       break;
4238     }
4239   case '\0':			/* catch bogons */
4240   case ' ':
4241     return NIL;
4242   case '"':			/* quoted string */
4243     if (strchr (c+1,'"')) end = "\"";
4244     else return NIL;		/* falls through */
4245   default:			/* atomic string */
4246     if ((d = strtok_r (c,end,r)) != NULL) n = strlen (d);
4247     else return NIL;
4248     break;
4249   }
4250   while (*s) s = &(*s)->next;	/* find tail of list */
4251   *s = mail_newstringlist ();	/* make new entry */
4252 				/* return the data */
4253   (*s)->text.data = (unsigned char *) cpystr (d);
4254   (*s)->text.size = n;
4255   return T;
4256 }
4257 
4258 /* Mail parse set from string
4259  * Accepts: string to parse
4260  *	    pointer to updated string pointer for return
4261  * Returns: set with pointer updated, or NIL if error
4262  */
4263 
mail_parse_set(char * s,char ** ret)4264 SEARCHSET *mail_parse_set (char *s,char **ret)
4265 {
4266   SEARCHSET *cur;
4267   SEARCHSET *set = NIL;
4268   while (isdigit (*s)) {
4269     if (!set) cur = set = mail_newsearchset ();
4270     else cur = cur->next = mail_newsearchset ();
4271 				/* parse value */
4272     if (!(cur->first = strtoul (s,&s,10)) ||
4273 	((*s == ':') && !(isdigit (*++s) && (cur->last = strtoul (s,&s,10)))))
4274       break;			/* bad value or range */
4275     if (*s == ',') ++s;		/* point to next value if more */
4276     else {			/* end of set */
4277       *ret = s;			/* set return pointer */
4278       return set;		/* return set */
4279     }
4280   }
4281   mail_free_searchset (&set);	/* failure, punt partial set */
4282   return NIL;
4283 }
4284 
4285 
4286 /* Mail append to set
4287  * Accepts: head of search set or NIL to do nothing
4288  *	    message to add
4289  * Returns: tail of search set or NIL if did nothing
4290  */
4291 
mail_append_set(SEARCHSET * set,unsigned long msgno)4292 SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno)
4293 {
4294   if (set) {			/* find tail */
4295     while (set->next) set = set->next;
4296 				/* start of set if no first member */
4297     if (!set->first) set->first = msgno;
4298     else if (msgno == (set->last ? set->last : set->first) + 1)
4299       set->last = msgno;	/* extend range if 1 past current */
4300     else (set = set->next = mail_newsearchset ())->first = msgno;
4301   }
4302   return set;
4303 }
4304 
4305 /* Mail sort messages
4306  * Accepts: mail stream
4307  *	    character set
4308  *	    search program
4309  *	    sort program
4310  *	    option flags
4311  * Returns: vector of sorted message sequences or NIL if error
4312  */
4313 
mail_sort(MAILSTREAM * stream,char * charset,SEARCHPGM * spg,SORTPGM * pgm,long flags)4314 unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
4315 			  SORTPGM *pgm,long flags)
4316 {
4317   unsigned long *ret = NIL;
4318   if (stream->dtb)		/* do the driver's action */
4319     ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs))
4320       (stream,charset,spg,pgm,flags);
4321 				/* flush search/sort programs if requested */
4322   if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
4323   if (flags & SO_FREE) mail_free_sortpgm (&pgm);
4324   return ret;
4325 }
4326 
4327 /* Mail sort messages work routine
4328  * Accepts: mail stream
4329  *	    character set
4330  *	    search program
4331  *	    sort program
4332  *	    option flags
4333  * Returns: vector of sorted message sequences or NIL if error
4334  */
4335 
mail_sort_msgs(MAILSTREAM * stream,char * charset,SEARCHPGM * spg,SORTPGM * pgm,long flags)4336 unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
4337 			       SORTPGM *pgm,long flags)
4338 {
4339   unsigned long i;
4340   SORTCACHE **sc;
4341   unsigned long *ret = NIL;
4342   if (spg) {			/* only if a search needs to be done */
4343     int silent = stream->silent;
4344     stream->silent = T;		/* don't pass up mm_searched() events */
4345 				/* search for messages */
4346     mail_search_full (stream,charset,spg,NIL);
4347     stream->silent = silent;	/* restore silence state */
4348   }
4349 				/* initialize progress counters */
4350   pgm->nmsgs = pgm->progress.cached = 0;
4351 				/* pass 1: count messages to sort */
4352   for (i = 1; i <= stream->nmsgs; ++i)
4353     if (mail_elt (stream,i)->searched) pgm->nmsgs++;
4354   if (pgm->nmsgs) {		/* pass 2: sort cache */
4355     sc = mail_sort_loadcache (stream,pgm);
4356 				/* pass 3: sort messages */
4357     if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
4358     fs_give ((void **) &sc);	/* don't need sort vector any more */
4359   }
4360 				/* empty sort results */
4361   else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
4362 				       sizeof (unsigned long));
4363 				/* also return via callback if requested */
4364   if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs);
4365   return ret;			/* return sort results */
4366 }
4367 
4368 /* Mail sort sortcache vector
4369  * Accepts: mail stream
4370  *	    sort program
4371  *	    sortcache vector
4372  *	    option flags
4373  * Returns: vector of sorted message sequences or NIL if error
4374  */
4375 
mail_sort_cache(MAILSTREAM * stream,SORTPGM * pgm,SORTCACHE ** sc,long flags)4376 unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
4377 				long flags)
4378 {
4379   unsigned long i,*ret;
4380 				/* pass 3: sort messages */
4381   qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare);
4382 				/* optional post sorting */
4383   if (pgm->postsort) (*pgm->postsort) ((void *) sc);
4384 				/* pass 4: return results */
4385   ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long));
4386   if (flags & SE_UID)		/* UID or msgno? */
4387     for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num);
4388   else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num;
4389   ret[pgm->nmsgs] = 0;		/* tie off message list */
4390   return ret;
4391 }
4392 
4393 /* Mail load sortcache
4394  * Accepts: mail stream, already searched
4395  *	    sort program
4396  * Returns: vector of sortcache pointers matching search
4397  */
4398 
4399 static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL};
4400 static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL};
4401 static STRINGLIST mailfromline = {{(unsigned char *) "from",4},
4402 				    &mailrnfromline};
4403 static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL};
4404 static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL};
4405 static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL};
4406 
mail_sort_loadcache(MAILSTREAM * stream,SORTPGM * pgm)4407 SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
4408 {
4409   char *t,*v,*x,tmp[MAILTMPLEN];
4410   SORTPGM *pg;
4411   SORTCACHE *s,**sc;
4412   MESSAGECACHE *elt,telt;
4413   ENVELOPE *env;
4414   ADDRESS *adr = NIL;
4415   unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *);
4416   sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
4417 				/* see what needs to be loaded */
4418   for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
4419     if ((elt = mail_elt (stream,i))->searched) {
4420       sc[pgm->progress.cached++] =
4421 	s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
4422       s->pgm = pgm;		/* note sort program */
4423       s->num = i;
4424 				/* get envelope if cached */
4425       if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL;
4426       else env = elt->private.msg.env;
4427       for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
4428       case SORTARRIVAL:		/* sort by arrival date */
4429 	if (!s->arrival) {
4430 				/* internal date unknown but can get? */
4431 	  if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
4432 	    sprintf (tmp,"%lu",i);
4433 	    mail_fetch_fast (stream,tmp,NIL);
4434 	  }
4435 				/* wrong thing before 3-Jan-1970 */
4436 	  s->arrival = elt->day ? mail_longdate (elt) : 1;
4437 	  s->dirty = T;
4438 	}
4439 	break;
4440       case SORTSIZE:		/* sort by message size */
4441 	if (!s->size) {
4442 	  if (!elt->rfc822_size) {
4443 	    sprintf (tmp,"%lu",i);
4444 	    mail_fetch_fast (stream,tmp,NIL);
4445 	  }
4446 	  s->size = elt->rfc822_size ? elt->rfc822_size : 1;
4447 	  s->dirty = T;
4448 	}
4449 	break;
4450 
4451       case SORTDATE:		/* sort by date */
4452 	if (!s->date) {
4453 	  if (env) t = env->date;
4454 	  else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL,
4455 					   FT_INTERNAL | FT_PEEK)) &&
4456 		   (t = strchr (t,':')))
4457 	    for (x = ++t; (x = strpbrk (x,"\012\015")) != NULL; x++)
4458 	      switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
4459 	      case ' ':		/* erase continuation newlines */
4460 	      case '\t':
4461 		memmove (x,v,strlen (v));
4462 		break;
4463 	      default:		/* tie off extraneous text */
4464 		*x = x[1] = '\0';
4465 	      }
4466 				/* skip leading whitespace */
4467 	  if (t) while ((*t == ' ') || (*t == '\t')) t++;
4468 				/* parse date from Date: header */
4469 	  if (!(t && mail_parse_date (&telt,t) &&
4470 		(s->date = mail_longdate (&telt)))) {
4471 				/* failed, use internal date */
4472 	    if (!(s->date = s->arrival)) {
4473 				/* internal date unknown but can get? */
4474 	      if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
4475 		sprintf (tmp,"%lu",i);
4476 		mail_fetch_fast (stream,tmp,NIL);
4477 	      }
4478 				/* wrong thing before 3-Jan-1970 */
4479 	      s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
4480 	    }
4481 	  }
4482 	  s->dirty = T;
4483 	}
4484 	break;
4485 
4486       case SORTFROM:		/* sort by first from */
4487 	if (!s->from) {
4488 	  if (env) s->from = env->from && env->from->mailbox ?
4489 	    cpystr (env->from->mailbox) : NIL;
4490 	  else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL,
4491 					   FT_INTERNAL | FT_PEEK)) &&
4492 		   (t = strchr (t,':'))) {
4493 	    for (x = ++t; (x = strpbrk (x,"\012\015")) != NULL; x++)
4494 	      switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
4495 	      case ' ':		/* erase continuation newlines */
4496 	      case '\t':
4497 		memmove (x,v,strlen (v));
4498 		break;
4499 	      case 'f':		/* continuation but with extra "From:" */
4500 	      case 'F':
4501 		if ((v = strchr (v,':')) != NULL) {
4502 		  memmove (x,v+1,strlen (v+1));
4503 		  break;
4504 		}
4505 	      default:		/* tie off extraneous text */
4506 		*x = x[1] = '\0';
4507 	      }
4508 	    rfc822_parse_adrlist (&adr,t,BADHOST);
4509 	    if (adr) {
4510 	      s->from = adr->mailbox;
4511 	      adr->mailbox = NIL;
4512 	      mail_free_address (&adr);
4513 	    }
4514 	  }
4515 	  if (!s->from) s->from = cpystr ("");
4516 	  s->dirty = T;
4517 	}
4518 	break;
4519 
4520       case SORTTO:		/* sort by first to */
4521 	if (!s->to) {
4522 	  if (env) s->to = env->to && env->to->mailbox ?
4523 	    cpystr (env->to->mailbox) : NIL;
4524 	  else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL,
4525 					   FT_INTERNAL | FT_PEEK)) &&
4526 		   (t = strchr (t,':'))) {
4527 	    for (x = ++t; (x = strpbrk (x,"\012\015")) != NULL; x++)
4528 	      switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
4529 	      case ' ':		/* erase continuation newlines */
4530 	      case '\t':
4531 		memmove (x,v,strlen (v));
4532 		break;
4533 	      case 't':		/* continuation but with extra "To:" */
4534 	      case 'T':
4535 		if ((v = strchr (v,':')) != NULL) {
4536 		  memmove (x,v+1,strlen (v+1));
4537 		  break;
4538 		}
4539 	      default:		/* tie off extraneous text */
4540 		*x = x[1] = '\0';
4541 	      }
4542 	    rfc822_parse_adrlist (&adr,t,BADHOST);
4543 	    if (adr) {
4544 	      s->to = adr->mailbox;
4545 	      adr->mailbox = NIL;
4546 	      mail_free_address (&adr);
4547 	    }
4548 	  }
4549 	  if (!s->to) s->to = cpystr ("");
4550 	  s->dirty = T;
4551 	}
4552 	break;
4553 
4554       case SORTCC:		/* sort by first cc */
4555 	if (!s->cc) {
4556 	  if (env) s->cc = env->cc && env->cc->mailbox ?
4557 	    cpystr (env->cc->mailbox) : NIL;
4558 	  else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL,
4559 					   FT_INTERNAL | FT_PEEK)) &&
4560 		   (t = strchr (t,':'))) {
4561 	    for (x = ++t; (x = strpbrk (x,"\012\015")) != NULL; x++)
4562 	      switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
4563 	      case ' ':		/* erase continuation newlines */
4564 	      case '\t':
4565 		memmove (x,v,strlen (v));
4566 		break;
4567 	      case 'c':		/* continuation but with extra "cc:" */
4568 	      case 'C':
4569 		if ((v = strchr (v,':')) != NULL) {
4570 		  memmove (x,v+1,strlen (v+1));
4571 		  break;
4572 		}
4573 	      default:		/* tie off extraneous text */
4574 		*x = x[1] = '\0';
4575 	      }
4576 	    rfc822_parse_adrlist (&adr,t,BADHOST);
4577 	    if (adr) {
4578 	      s->cc = adr->mailbox;
4579 	      adr->mailbox = NIL;
4580 	      mail_free_address (&adr);
4581 	    }
4582 	  }
4583 	  if (!s->cc) s->cc = cpystr ("");
4584 	  s->dirty = T;
4585 	}
4586 	break;
4587 
4588       case SORTSUBJECT:		/* sort by subject */
4589 	if (!s->subject) {
4590 				/* get subject from envelope if have one */
4591 	  if (env) t = env->subject ? env->subject : "";
4592 				/* otherwise snarf from header text */
4593 	  else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline,
4594 					   NIL,FT_INTERNAL | FT_PEEK)) &&
4595 		   (t = strchr (t,':')))
4596 	    for (x = ++t; (x = strpbrk (x,"\012\015")) != NULL; x++)
4597 	      switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
4598 	      case ' ':		/* erase continuation newlines */
4599 	      case '\t':
4600 		memmove (x,v,strlen (v));
4601 		break;
4602 	      default:		/* tie off extraneous text */
4603 		*x = x[1] = '\0';
4604 	      }
4605 	  else t = "";		/* empty subject */
4606 				/* strip and cache subject */
4607 	  s->refwd = mail_strip_subject (t,&s->subject);
4608 	  s->dirty = T;
4609 	}
4610 	break;
4611       default:
4612 	fatal ("Unknown sort function");
4613       }
4614     }
4615   return sc;
4616 }
4617 
4618 /* Strip subjects of extra spaces and leading and trailing cruft for sorting
4619  * Accepts: unstripped subject
4620  *	    pointer to return stripped subject, in cpystr form
4621  * Returns: T if subject had a re/fwd, NIL otherwise
4622  */
4623 
mail_strip_subject(char * t,char ** ret)4624 unsigned int mail_strip_subject (char *t,char **ret)
4625 {
4626   SIZEDTEXT src,dst;
4627   unsigned long i,slen;
4628   char c,*s,*x;
4629   unsigned int refwd = NIL;
4630   if ((src.size = strlen (t)) != 0) {	/* have non-empty subject? */
4631     src.data = (unsigned char *) t;
4632 			/* Step 1 */
4633 				/* make copy, convert MIME2 if needed */
4634     *ret = s = (utf8_mime2text (&src,&dst,U8T_CANONICAL) &&
4635 		(src.data != dst.data)) ? (char *) dst.data : cpystr (t);
4636 				/* convert spaces to tab, strip extra spaces */
4637     for (x = t = s, c = 'x'; *t; t++) {
4638       if (c != ' ') c = *x++ = ((*t == '\t') ? ' ' : *t);
4639       else if ((*t != '\t') && (*t != ' ')) c = *x++ = *t;
4640     }
4641     *x = '\0';			/* tie off string */
4642 			/* Step 2 */
4643     for (slen = dst.size; s; slen = strlen (s))  {
4644       for (t = s + slen; t > s; ) switch (t[-1]) {
4645       case ' ': case '\t':	/* WSP */
4646 	*--t = '\0';		/* just remove it */
4647 	break;
4648       case ')':			/* possible "(fwd)" */
4649 	if ((t >= (s + 5)) && (t[-5] == '(') &&
4650 	    ((t[-4] == 'F') || (t[-4] == 'f')) &&
4651 	    ((t[-3] == 'W') || (t[-3] == 'w')) &&
4652 	    ((t[-2] == 'D') || (t[-2] == 'd'))) {
4653 	  *(t -= 5) = '\0';	/* remove "(fwd)" */
4654 	  refwd = T;		/* note a re/fwd */
4655 	  break;
4656 	}
4657       default:			/* not a subj-trailer */
4658 	t = s;
4659 	break;
4660       }
4661 			/* Steps 3-5 */
4662       for (t = s; t; ) switch (*s) {
4663       case ' ': case '\t':	/* WSP */
4664 	s = t = mail_strip_subject_wsp (s + 1);
4665 	break;
4666       case 'r': case 'R':	/* possible "re" */
4667 	if (((s[1] == 'E') || (s[1] == 'e')) &&
4668 	    (t = mail_strip_subject_wsp (s + 2)) &&
4669 	    (t = mail_strip_subject_blob (t)) && (*t == ':')) {
4670 	  s = ++t;		/* found "re" */
4671 	  refwd = T;		/* definitely a re/fwd at this point */
4672 	}
4673 	else t = NIL;		/* found subj-middle */
4674 	break;
4675       case 'f': case 'F':	/* possible "fw" or "fwd" */
4676 	if (((s[1] == 'w') || (s[1] == 'W')) &&
4677 	    (((s[2] == 'd') || (s[2] == 'D')) ?
4678 	     (t = mail_strip_subject_wsp (s + 3)) :
4679 	     (t = mail_strip_subject_wsp (s + 2))) &&
4680 	    (t = mail_strip_subject_blob (t)) && (*t == ':')) {
4681 	  s = ++t;		/* found "fwd" */
4682 	  refwd = T;		/* definitely a re/fwd at this point */
4683 	}
4684 	else t = NIL;		/* found subj-middle */
4685 	break;
4686       case '[':			/* possible subj-blob */
4687 	if ((t = mail_strip_subject_blob (s)) && *t) s = t;
4688 	else t = NIL;		/* found subj-middle */
4689 	break;
4690       default:
4691 	t = NIL;		/* found subj-middle */
4692 	break;
4693       }
4694 			/* Step 6 */
4695 				/* Netscape-style "[Fwd: ...]"? */
4696       if ((*s == '[') && ((s[1] == 'F') || (s[1] == 'f')) &&
4697 	  ((s[2] == 'W') || (s[2] == 'w')) &&
4698 	  ((s[3] == 'D') || (s[3] == 'd')) && (s[4] == ':') &&
4699 	  (s[i = strlen (s) - 1] == ']')) {
4700 	s[i] = '\0';		/* flush closing "]" */
4701 	s += 5;			/* and leading "[Fwd:" */
4702 	refwd = T;		/* definitely a re/fwd at this point */
4703       }
4704       else break;		/* don't need to loop back to step 2 */
4705     }
4706     if (s != (t = *ret)) {	/* removed leading text? */
4707       s = *ret = cpystr (s);	/* yes, make a fresh return copy */
4708       fs_give ((void **) &t);	/* flush old copy */
4709     }
4710   }
4711   else *ret = cpystr ("");	/* empty subject */
4712   return refwd;			/* return re/fwd state */
4713 }
4714 
4715 /* Strip subject wsp helper routine
4716  * Accepts: text
4717  * Returns: pointer to text after blob
4718  */
4719 
mail_strip_subject_wsp(char * s)4720 char *mail_strip_subject_wsp (char *s)
4721 {
4722   while ((*s == ' ') || (*s == '\t')) s++;
4723   return s;
4724 }
4725 
4726 
4727 /* Strip subject blob helper routine
4728  * Accepts: text
4729  * Returns: pointer to text after any blob, NIL if blob-like but not blob
4730  */
4731 
mail_strip_subject_blob(char * s)4732 char *mail_strip_subject_blob (char *s)
4733 {
4734   if (*s != '[') return s;	/* not a blob, ignore */
4735 				/* search for end of blob */
4736   while (*++s != ']') if ((*s == '[') || !*s) return NIL;
4737   return mail_strip_subject_wsp (s + 1);
4738 }
4739 
4740 /* Sort compare messages
4741  * Accept: first message sort cache element
4742  *	   second message sort cache element
4743  * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
4744  */
4745 
mail_sort_compare(const void * a1,const void * a2)4746 int mail_sort_compare (const void *a1,const void *a2)
4747 {
4748   int i = 0;
4749   SORTCACHE *s1 = *(SORTCACHE **) a1;
4750   SORTCACHE *s2 = *(SORTCACHE **) a2;
4751   SORTPGM *pgm = s1->pgm;
4752   if (!s1->sorted) {		/* this one sorted yet? */
4753     s1->sorted = T;
4754     pgm->progress.sorted++;	/* another sorted message */
4755   }
4756   if (!s2->sorted) {		/* this one sorted yet? */
4757     s2->sorted = T;
4758     pgm->progress.sorted++;	/* another sorted message */
4759   }
4760   do {
4761     switch (pgm->function) {	/* execute search program */
4762     case SORTDATE:		/* sort by date */
4763       i = compare_ulong (s1->date,s2->date);
4764       break;
4765     case SORTARRIVAL:		/* sort by arrival date */
4766       i = compare_ulong (s1->arrival,s2->arrival);
4767       break;
4768     case SORTSIZE:		/* sort by message size */
4769       i = compare_ulong (s1->size,s2->size);
4770       break;
4771     case SORTFROM:		/* sort by first from */
4772       i = compare_string (s1->from,s2->from);
4773       break;
4774     case SORTTO:		/* sort by first to */
4775       i = compare_string (s1->to,s2->to);
4776       break;
4777     case SORTCC:		/* sort by first cc */
4778       i = compare_string (s1->cc,s2->cc);
4779       break;
4780     case SORTSUBJECT:		/* sort by subject */
4781       i = compare_string (s1->subject,s2->subject);
4782       break;
4783     }
4784     if (pgm->reverse) i = -i;	/* flip results if necessary */
4785   }
4786   while ((pgm = i ? NIL : pgm->next) != NULL);
4787 				/* return result, avoid 0 if at all possible */
4788   return i ? i : compare_ulong (s1->num,s2->num);
4789 }
4790 
4791 /* Return message date as an unsigned long seconds since time began
4792  * Accepts: message cache pointer
4793  * Returns: unsigned long of date
4794  *
4795  * This routine, like most UNIX systems, is clueless about leap seconds.
4796  * Thus, it treats 23:59:60 as equivalent to 00:00:00 the next day.
4797  *
4798  * This routine forces any early hours on 1-Jan-1970 in oriental timezones
4799  * to be 1-Jan-1970 00:00:00 UTC, so as to avoid negative longdates.
4800  */
4801 
mail_longdate(MESSAGECACHE * elt)4802 unsigned long mail_longdate (MESSAGECACHE *elt)
4803 {
4804   unsigned long m = elt->month ? elt->month : 1;
4805   unsigned long yr = elt->year + BASEYEAR;
4806 				/* number of days since time began */
4807   unsigned long ret = (elt->day ? (elt->day - 1) : 0)
4808     + 30 * (m - 1) + ((m + (m > 8)) / 2)
4809 #ifndef USEJULIANCALENDAR
4810 #ifndef USEORTHODOXCALENDAR	/* Gregorian calendar */
4811     + ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100))
4812 #ifdef Y4KBUGFIX
4813     - ((yr / 4000) - (BASEYEAR / 4000))
4814 #endif
4815     - ((m < 3) ?
4816        !(yr % 4) && ((yr % 100) || (!(yr % 400)
4817 #ifdef Y4KBUGFIX
4818 				    && (yr % 4000)
4819 #endif
4820 				    )) : 2)
4821 #else				/* Orthodox calendar */
4822     + ((2*(yr / 900)) - (2*(BASEYEAR / 900)))
4823     + (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200))
4824     + (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600))
4825     - ((yr / 100) - (BASEYEAR / 100))
4826     - ((m < 3) ?
4827        !(yr % 4) && ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600))
4828        : 2)
4829 #endif
4830 #endif
4831     + elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4);
4832   ret *= 24; ret += elt->hours;	/* date value in hours */
4833   ret *= 60; ret +=elt->minutes;/* date value in minutes */
4834   yr = (elt->zhours * 60) + elt->zminutes;
4835   if (elt->zoccident) ret += yr;/* occidental timezone, make UTC */
4836   else if (ret < yr) return 0;	/* still 31-Dec-1969 in UTC */
4837   else ret -= yr;		/* oriental timezone, make UTC */
4838   ret *= 60; ret += elt->seconds;
4839   return ret;
4840 }
4841 
4842 /* Mail thread messages
4843  * Accepts: mail stream
4844  *	    thread type
4845  *	    character set
4846  *	    search program
4847  *	    option flags
4848  * Returns: thread node tree or NIL if error
4849  */
4850 
mail_thread(MAILSTREAM * stream,char * type,char * charset,SEARCHPGM * spg,long flags)4851 THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
4852 			 SEARCHPGM *spg,long flags)
4853 {
4854   THREADNODE *ret = NIL;
4855   if (stream->dtb)		/* must have a live driver */
4856     ret = stream->dtb->thread ?	/* do driver's action if available */
4857       (*stream->dtb->thread) (stream,type,charset,spg,flags) :
4858 	mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
4859 				/* flush search/sort programs if requested */
4860   if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
4861   return ret;
4862 }
4863 
4864 
4865 /* Mail thread messages
4866  * Accepts: mail stream
4867  *	    thread type
4868  *	    character set
4869  *	    search program
4870  *	    option flags
4871  *	    sorter routine
4872  * Returns: thread node tree or NIL if error
4873  */
4874 
mail_thread_msgs(MAILSTREAM * stream,char * type,char * charset,SEARCHPGM * spg,long flags,sorter_t sorter)4875 THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
4876 			      SEARCHPGM *spg,long flags,sorter_t sorter)
4877 {
4878   THREADER *t;
4879   for (t = &mailthreadlist; t; t = t->next)
4880     if (!compare_cstring (type,t->name)) {
4881       THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter);
4882       if (mailthreadresults) (*mailthreadresults) (stream,ret);
4883       return ret;
4884     }
4885   MM_LOG ("No such thread type",ERROR);
4886   return NIL;
4887 }
4888 
4889 /* Mail thread ordered subject
4890  * Accepts: mail stream
4891  *	    character set
4892  *	    search program
4893  *	    option flags
4894  *	    sorter routine
4895  * Returns: thread node tree
4896  */
4897 
mail_thread_orderedsubject(MAILSTREAM * stream,char * charset,SEARCHPGM * spg,long flags,sorter_t sorter)4898 THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
4899 					SEARCHPGM *spg,long flags,
4900 					sorter_t sorter)
4901 {
4902   THREADNODE *thr = NIL;
4903   THREADNODE *cur,*top,**tc;
4904   SORTPGM pgm,pgm2;
4905   SORTCACHE *s;
4906   unsigned long i,j,*lst,*ls;
4907 				/* sort by subject+date */
4908   memset (&pgm,0,sizeof (SORTPGM));
4909   memset (&pgm2,0,sizeof (SORTPGM));
4910   pgm.function = SORTSUBJECT;
4911   pgm.next = &pgm2;
4912   pgm2.function = SORTDATE;
4913   if ((lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))) != NULL){
4914     if (*(ls = lst)) {		/* create thread */
4915 				/* note first subject */
4916       cur = top = thr = mail_newthreadnode
4917 	((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE));
4918 				/* note its number */
4919       cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst;
4920       i = 1;			/* number of threads */
4921       while (*ls) {		/* build tree */
4922 				/* subjects match? */
4923 	s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE);
4924 	if (compare_cstring (top->sc->subject,s->subject)) {
4925 	  i++;			/* have a new thread */
4926 	  top = top->branch = cur = mail_newthreadnode (s);
4927 	}
4928 				/* start a child of the top */
4929 	else if (cur == top) cur = cur->next = mail_newthreadnode (s);
4930 				/* sibling of child */
4931 	else cur = cur->branch = mail_newthreadnode (s);
4932 				/* set to msgno or UID as needed */
4933 	cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
4934       }
4935 				/* make threadnode cache */
4936       tc = (THREADNODE **) fs_get (i * sizeof (THREADNODE *));
4937 				/* load threadnode cache */
4938       for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
4939       if (i != j) fatal ("Threadnode cache confusion");
4940       qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
4941       for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
4942       tc[j]->branch = NIL;	/* end of root */
4943       thr = tc[0];		/* head of data */
4944       fs_give ((void **) &tc);
4945     }
4946     fs_give ((void **) &lst);
4947   }
4948   return thr;
4949 }
4950 
4951 /* Mail thread references
4952  * Accepts: mail stream
4953  *	    character set
4954  *	    search program
4955  *	    option flags
4956  *	    sorter routine
4957  * Returns: thread node tree
4958  */
4959 
4960 #define REFHASHSIZE 1009	/* arbitrary prime for hash table size */
4961 
4962 /*  Reference threading container, as described in Jamie Zawinski's web page
4963  * (http://www.jwz.org/doc/threading.html) for this algorithm.  These are
4964  * stored as extended data in the hash table (called "id_table" in JWZ's
4965  * document) and are maintained by the hash table routines.  The hash table
4966  * routines implement extended data as additional void* words at the end of
4967  * each bucket, hence these strange macros instead of a struct which would
4968  * have been more straightforward.
4969  */
4970 
4971 #define THREADLINKS 3		/* number of thread links */
4972 
4973 #define CACHE(data) ((SORTCACHE *) (data)[0])
4974 #define PARENT(data) ((container_t) (data)[1])
4975 #define SETPARENT(data,value) ((container_t) (data[1] = value))
4976 #define SIBLING(data) ((container_t) (data)[2])
4977 #define SETSIBLING(data,value) ((container_t) (data[2] = value))
4978 #define CHILD(data) ((container_t) (data)[3])
4979 #define SETCHILD(data,value) ((container_t) (data[3] = value))
4980 
mail_thread_references(MAILSTREAM * stream,char * charset,SEARCHPGM * spg,long flags,sorter_t sorter)4981 THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset,
4982 				    SEARCHPGM *spg,long flags,sorter_t sorter)
4983 {
4984   MESSAGECACHE *elt,telt;
4985   ENVELOPE *env;
4986   SORTCACHE *s;
4987   STRINGLIST *st;
4988   HASHENT *he;
4989   THREADNODE **tc,*cur,*lst,*nxt,*sis,*msg;
4990   container_t con,nxc,prc,sib;
4991   void **sub;
4992   char *t,tmp[MAILTMPLEN];
4993   unsigned long j,nmsgs;
4994   unsigned long i = stream->nmsgs * sizeof (SORTCACHE *);
4995   SORTCACHE **sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
4996   HASHTAB *ht = hash_create (REFHASHSIZE);
4997   THREADNODE *root = NIL;
4998   if (spg) {			/* only if a search needs to be done */
4999     int silent = stream->silent;
5000     stream->silent = T;		/* don't pass up mm_searched() events */
5001 				/* search for messages */
5002     mail_search_full (stream,charset,spg,NIL);
5003     stream->silent = silent;	/* restore silence state */
5004   }
5005 
5006 				/* create SORTCACHE vector of requested msgs */
5007   for (i = 1, nmsgs = 0; i <= stream->nmsgs; ++i)
5008     if (mail_elt (stream,i)->searched)
5009       (sc[nmsgs++] = (SORTCACHE *)(*mailcache)(stream,i,CH_SORTCACHE))->num =i;
5010 	/* separate pass so can do overview fetch lookahead */
5011   for (i = 0; i < nmsgs; ++i) {	/* for each requested message */
5012 				/* is anything missing in its SORTCACHE? */
5013     if (!((s = sc[i])->date && s->subject && s->message_id && s->references)) {
5014 				/* driver has an overview mechanism? */
5015       if (stream->dtb && stream->dtb->overview) {
5016 				/* yes, find following unloaded entries */
5017 	for (j = i + 1; (j < nmsgs) && !sc[j]->references; ++j);
5018 	sprintf (tmp,"%lu",mail_uid (stream,s->num));
5019 	if (i != --j)		/* end of range different? */
5020 	  sprintf (tmp + strlen (tmp),":%lu",mail_uid (stream,sc[j]->num));
5021 				/* load via overview mechanism */
5022 	mail_fetch_overview (stream,tmp,mail_thread_loadcache);
5023       }
5024 				/* still missing data? */
5025       if (!s->date || !s->subject || !s->message_id || !s->references) {
5026 				/* try to load data from envelope */
5027 	if ((env = mail_fetch_structure (stream,s->num,NIL,NIL)) != NULL) {
5028 	  if (!s->date && env->date && mail_parse_date (&telt,env->date))
5029 	    s->date = mail_longdate (&telt);
5030 	  if (!s->subject && env->subject)
5031 	    s->refwd =
5032 	      mail_strip_subject (env->subject,&s->subject);
5033 	  if (!s->message_id && env->message_id && *env->message_id)
5034 	    s->message_id = mail_thread_parse_msgid (env->message_id,NIL);
5035 	  if (!s->references &&	/* use References: or In-Reply-To: */
5036 	      !(s->references =
5037 		mail_thread_parse_references (env->references,T)))
5038 	    s->references = mail_thread_parse_references(env->in_reply_to,NIL);
5039 	}
5040 				/* last resort */
5041 	if (!s->date && !(s->date = s->arrival)) {
5042 				/* internal date unknown but can get? */
5043 	  if (!(elt = mail_elt (stream,s->num))->day &&
5044 	      !(stream->dtb->flags & DR_NOINTDATE)) {
5045 	    sprintf (tmp,"%lu",s->num);
5046 	    mail_fetch_fast (stream,tmp,NIL);
5047 	  }
5048 				/* wrong thing before 3-Jan-1970 */
5049 	  s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
5050 	}
5051 	if (!s->subject) s->subject = cpystr ("");
5052 	if (!s->references) s->references = mail_newstringlist ();
5053 	s->dirty = T;
5054       }
5055     }
5056 
5057 			/* Step 1 (preliminary) */
5058 				/* generate unique string */
5059     sprintf (tmp,"%s.%lx.%lx@%s",stream->mailbox,stream->uid_validity,
5060 	     mail_uid (stream,s->num),mylocalhost ());
5061 				/* flush old unique string if not message-id */
5062     if (s->unique && (s->unique != s->message_id))
5063       fs_give ((void **) &s->unique);
5064     s->unique = s->message_id ?	/* don't permit Message ID duplicates */
5065       (hash_lookup (ht,s->message_id) ? cpystr (tmp) : s->message_id) :
5066 	(s->message_id = cpystr (tmp));
5067 				/* add unique string to hash table */
5068     hash_add (ht,s->unique,s,THREADLINKS);
5069   }
5070 			/* Step 1 */
5071   for (i = 0; i < nmsgs; ++i) {	/* for each message in sortcache */
5072 			/* Step 1A */
5073     if ((st = (s = sc[i])->references) && st->text.data)
5074       for (con = hash_lookup_and_add (ht,(char *) st->text.data,NIL,
5075 			THREADLINKS); (st = st->next) != NULL; con = nxc) {
5076 	nxc = hash_lookup_and_add (ht,(char *) st->text.data,NIL,THREADLINKS);
5077 				/* only if no parent & won't introduce loop */
5078 	if (!PARENT (nxc) && !mail_thread_check_child (con,nxc)) {
5079 	  SETPARENT (nxc,con);	/* establish parent/child link */
5080 				/* other children become sibling of this one */
5081 	  SETSIBLING (nxc,CHILD (con));
5082 	  SETCHILD (con,nxc);	/* set as child of parent */
5083 	}
5084       }
5085     else con = NIL;		/* else message has no ancestors */
5086 			/* Step 1B */
5087     if ((prc = PARENT ((nxc = hash_lookup (ht,s->unique)))) &&
5088 	(prc != con)) {		/* break links if have a different parent */
5089       SETPARENT (nxc,NIL);	/* easy if direct child */
5090       if (nxc == CHILD (prc)) SETCHILD (prc,SIBLING (nxc));
5091       else {			/* otherwise hunt through sisters */
5092 	for (sib = CHILD (prc); nxc != SIBLING (sib); sib = SIBLING (sib));
5093 	SETSIBLING (sib,SIBLING (nxc));
5094       }
5095       SETSIBLING (nxc,NIL);	/* no more little sisters either */
5096       prc = NIL;		/* no more parent set */
5097     }
5098 				/* need to set parent, and parent is good? */
5099     if (!prc && !mail_thread_check_child (con,nxc)) {
5100       SETPARENT (nxc,con);	/* establish parent/child link */
5101       if (con) {		/* if non-root parent, set parent's child */
5102 	if (CHILD (con)) {	/* have a child already */
5103 				/* find youngest daughter */
5104 	  for (con = CHILD (con); SIBLING (con); con = SIBLING (con));
5105 	  SETSIBLING (con,nxc);	/* add new baby sister */
5106 	}
5107 	else SETCHILD (con,nxc);/* set as only child */
5108       }
5109     }
5110   }
5111   fs_give ((void **) &sc);	/* finished with sortcache vector */
5112 
5113 			/* Step 2 */
5114 				/* search hash table for parentless messages */
5115   for (i = 0, prc = con = NIL; i < ht->size; i++)
5116     for (he = ht->table[i]; he; he = he->next)
5117       if (!PARENT ((nxc = he->data))) {
5118 				/* sibling of previous parentless message */
5119 	if (con) con = SETSIBLING (con,nxc);
5120 	else prc = con = nxc;	/* first parentless message */
5121       }
5122   /*  Once the dummy containers are pruned, we no longer need the parent
5123    * information, so we can convert the containers to THREADNODEs.  Since
5124    * we don't need the id_table any more either, we can reset the hash table
5125    * and reuse it as a subject_table.  Resetting the hash table will also
5126    * destroy the containers.
5127    */
5128 			/* Step 3 */
5129 				/* prune dummies, convert to threadnode */
5130   root = mail_thread_c2node (stream,mail_thread_prune_dummy (prc,NIL),flags);
5131 			/* Step 4 */
5132 				/* make buffer for sorting */
5133   tc = (THREADNODE **) fs_get (nmsgs * sizeof (THREADNODE *));
5134 				/* load threadcache and count nodes to sort */
5135   for (i = 0, cur = root; cur ; cur = cur->branch) tc[i++] = cur;
5136   if (i > 1) {			/* only if need to sort */
5137     qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
5138 				/* relink siblings */
5139     for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
5140     tc[j]->branch = NIL;	/* end of root */
5141     root = tc[0];		/* establish new root */
5142   }
5143 			/* Step 5A */
5144   hash_reset (ht);		/* discard containers, reset ht */
5145 			/* Step 5B */
5146   for (cur = root; cur; cur = cur->branch)
5147     if ((t = (nxt = (cur->sc ? cur : cur->next))->sc->subject) && *t) {
5148 				/* add new subject to hash table */
5149       if (!(sub = hash_lookup (ht,t))) hash_add (ht,t,cur,0);
5150 				/* if one in table not dummy and */
5151       else if ((s = (lst = (THREADNODE *) sub[0])->sc) &&
5152 				/* current dummy, or not re/fwd and table is */
5153 	       (!cur->sc || (!nxt->sc->refwd && s->refwd)))
5154 	sub[0] = (void *) cur;	/* replace with this message */
5155     }
5156 
5157 			/* Step 5C */
5158   for (cur = root, sis = NIL; cur; cur = msg) {
5159 				/* do nothing if current message or no sub */
5160     if (!(t = (cur->sc ? cur : cur->next)->sc->subject) || !*t ||
5161 	((lst = (THREADNODE *) (sub = hash_lookup (ht,t))[0]) == cur))
5162       msg = (sis = cur)->branch;
5163     else if (!lst->sc) {	/* is message in the table a dummy? */
5164 				/* find youngest daughter of msg in table */
5165       for (msg = lst->next; msg->branch; msg = msg->branch);
5166       if (!cur->sc) {		/* current message a dummy? */
5167 	msg->branch = cur->next;/* current's daughter now dummy's youngest */
5168 	msg = cur->branch;	/* continue scan at younger sister */
5169 				/* now delete this node */
5170 	cur->branch = cur->next = NIL;
5171 	mail_free_threadnode (&cur);
5172       }
5173       else {			/* current message not a dummy */
5174 	msg->branch = cur;	/* append as youngest daughter */
5175 	msg = cur->branch;	/* continue scan at younger sister */
5176 	cur->branch = NIL;	/* lose our younger sisters */
5177       }
5178     }
5179     else {			/* no dummies, is current re/fwd, table not? */
5180       if (cur->sc->refwd && !lst->sc->refwd) {
5181 	if (lst->next) {	/* find youngest daughter of msg in table */
5182 	  for (msg = lst->next; msg->branch; msg = msg->branch);
5183 	  msg->branch = cur;	/* append as youngest daughter */
5184 	}
5185 	else lst->next = cur;	/* no children, so make the eldest daughter */
5186       }
5187 
5188       else {			/* no re/fwd, create a new dummy */
5189 	msg = mail_newthreadnode (NIL);
5190 	if (lst == root) {	/* msg in table is root? */
5191 	  root = lst->branch;	/* younger sister becomes new root */
5192 				/* no longer older sister either */
5193 	  if (lst == sis) sis = NIL;
5194 	}
5195 	else {			/* find older sister of msg in table */
5196 	  for (nxt = root; lst != nxt->branch; nxt = nxt->branch);
5197 				/* remove from older sister */
5198 	  nxt->branch = lst->branch;
5199 	}
5200 	msg->next = lst;	/* msg in table becomes child */
5201 	lst->branch = cur;	/* current now little sister of msg in table */
5202 	if (sis) {		/* have an elder sister? */
5203 	  if (sis == lst)	/* rescan if lost her */
5204 	    for (sis = root; cur != sis->branch; sis = sis->branch);
5205 	  sis->branch = msg;	/* make dummy younger sister of big sister */
5206 	}
5207 	else root = msg;	/* otherwise this is the new root */
5208 	sub[0] = sis = msg;	/* set new msg in table and new big sister */
5209       }
5210       msg = cur->branch;	/* continue scan at younger sister */
5211       cur->branch = NIL;	/* lose our younger sisters */
5212     }
5213     if (sis) sis->branch = msg;	/* older sister gets this as younger sister */
5214     else root = msg;		/* otherwise this is the new root */
5215   }
5216   hash_destroy (&ht);		/* finished with hash table */
5217 			/* Step 6 */
5218 				/* sort threads */
5219   root = mail_thread_sort (root,tc);
5220   fs_give ((void **) &tc);	/* finished with sort buffer */
5221   return root;			/* return sorted list */
5222 }
5223 
5224 /* Fetch overview callback to load sortcache for threading
5225  * Accepts: MAIL stream
5226  *	    UID of this message
5227  *	    overview of this message
5228  *	    msgno of this message
5229  */
5230 
mail_thread_loadcache(MAILSTREAM * stream,unsigned long uid,OVERVIEW * ov,unsigned long msgno)5231 void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
5232 			    unsigned long msgno)
5233 {
5234   if (msgno && ov) {		/* just in case */
5235     MESSAGECACHE telt, *elt;
5236     ENVELOPE *env;
5237     SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE);
5238     if (!s->subject && ov->subject) {
5239       s->refwd = mail_strip_subject (ov->subject,&s->subject);
5240       s->dirty = T;
5241     }
5242     if (!s->from && ov->from && ov->from->mailbox) {
5243       s->from = cpystr (ov->from->mailbox);
5244       s->dirty = T;
5245     }
5246     if (!s->date && ov->date && mail_parse_date (&telt,ov->date)) {
5247       s->date = mail_longdate (&telt);
5248       s->dirty = T;
5249     }
5250     if (!s->message_id && ov->message_id) {
5251       s->message_id = mail_thread_parse_msgid (ov->message_id,NIL);
5252       s->dirty = T;
5253     }
5254     if (!s->references &&
5255 	!(s->references = mail_thread_parse_references (ov->references,T))
5256 	&& stream->dtb && !strcmp(stream->dtb->name, "imap")
5257 	&& (elt = mail_elt (stream, msgno)) != NULL
5258 	&& (env = elt->private.msg.env) != NULL
5259 	&& env->in_reply_to
5260 	&& !(s->references = mail_thread_parse_references(env->in_reply_to, NIL))) {
5261 				/* don't do In-Reply-To with NNTP mailboxes */
5262       s->references = mail_newstringlist ();
5263       s->dirty = T;
5264     }
5265     if (!s->size && ov->optional.octets) {
5266       s->size = ov->optional.octets;
5267       s->dirty = T;
5268     }
5269   }
5270 }
5271 
5272 /* Thread parse Message ID
5273  * Accepts: pointer to purported Message ID
5274  *	    pointer to return pointer
5275  * Returns: Message ID or NIL, return pointer updated
5276  */
5277 
mail_thread_parse_msgid(char * s,char ** ss)5278 char *mail_thread_parse_msgid (char *s,char **ss)
5279 {
5280   char *ret = NIL;
5281   char *t = NIL;
5282   ADDRESS *adr;
5283   if (s) {			/* only for non-NIL strings */
5284     rfc822_skipws (&s);		/* skip whitespace */
5285 				/* ignore phrases */
5286     if (((*s == '<') || (s = rfc822_parse_phrase (s))) &&
5287 	(adr = rfc822_parse_routeaddr (s,&t,BADHOST))) {
5288 				/* make return msgid */
5289       if (adr->mailbox && adr->host)
5290 	sprintf (ret = (char *) fs_get (strlen (adr->mailbox) +
5291 					strlen (adr->host) + 2),"%s@%s",
5292 		 adr->mailbox,adr->host);
5293       mail_free_address (&adr);	/* don't need temporary address */
5294     }
5295   }
5296   if (ss) *ss = t;		/* update return pointer */
5297   return ret;
5298 }
5299 
5300 
5301 /* Thread parse references
5302  * Accepts: pointer to purported references
5303  *	    parse multiple references flag
5304  * Returns: references or NIL
5305  */
5306 
mail_thread_parse_references(char * s,long flag)5307 STRINGLIST *mail_thread_parse_references (char *s,long flag)
5308 {
5309   char *t;
5310   STRINGLIST *ret = NIL;
5311   STRINGLIST *cur;
5312 				/* found first reference? */
5313   if ((t = mail_thread_parse_msgid (s,&s)) != NULL) {
5314     (ret = mail_newstringlist ())->text.data = (unsigned char *) t;
5315     ret->text.size = strlen (t);
5316     if (flag)			/* parse subsequent references */
5317       for (cur = ret; (t = mail_thread_parse_msgid (s,&s)) != NULL; cur = cur->next) {
5318 	(cur->next = mail_newstringlist ())->text.data = (unsigned char *) t;
5319 	cur->next->text.size = strlen (t);
5320       }
5321   }
5322   return ret;
5323 }
5324 
5325 /* Prune dummy messages
5326  * Accepts: candidate container to prune
5327  *	    older sibling of container, if any
5328  * Returns: container in this position, possibly pruned
5329  * All children and younger siblings are also pruned
5330  */
5331 
mail_thread_prune_dummy(container_t msg,container_t ane)5332 container_t mail_thread_prune_dummy (container_t msg,container_t ane)
5333 {
5334 				/* prune container and children */
5335   container_t ret = msg ? mail_thread_prune_dummy_work (msg,ane) : NIL;
5336 				/* prune all younger sisters */
5337   if (ret) for (ane = ret; ane && (msg = SIBLING (ane)); ane = msg)
5338     msg = mail_thread_prune_dummy_work (msg,ane);
5339   return ret;
5340 }
5341 
5342 
5343 /* Prune dummy messages worker routine
5344  * Accepts: candidate container to prune
5345  *	    older sibling of container, if any
5346  * Returns: container in this position, possibly pruned
5347  * All children are also pruned
5348  */
5349 
mail_thread_prune_dummy_work(container_t msg,container_t ane)5350 container_t mail_thread_prune_dummy_work (container_t msg,container_t ane)
5351 {
5352   container_t cur;
5353 				/* get children, if any */
5354   container_t nxt = mail_thread_prune_dummy (CHILD (msg),NIL);
5355 				/* just update children if container has msg */
5356   if (CACHE (msg)) SETCHILD (msg,nxt);
5357   else if (!nxt) {		/* delete dummy with no children */
5358     nxt = SIBLING (msg);	/* get younger sister */
5359     if (ane) SETSIBLING (ane,nxt);
5360 				/* prune younger sister if exists */
5361     msg = nxt ? mail_thread_prune_dummy_work (nxt,ane) : NIL;
5362   }
5363 				/* not if parent root & multiple children */
5364   else if ((cur = PARENT (msg)) || !SIBLING (nxt)) {
5365 				/* OK to promote, try younger sister of aunt */
5366     if (ane) SETSIBLING (ane,nxt);
5367 				/* otherwise promote to child of grandmother */
5368     else if (cur) SETCHILD (cur,nxt);
5369     SETPARENT (nxt,cur);	/* set parent as well */
5370 				/* look for end of siblings in new container */
5371     for (cur = nxt; SIBLING (cur); cur = SIBLING (cur));
5372 				/* reattach deleted container's siblings */
5373     SETSIBLING (cur,SIBLING (msg));
5374 				/* prune and return new container */
5375     msg = mail_thread_prune_dummy_work (nxt,ane);
5376   }
5377   else SETCHILD (msg,nxt);	/* in case child pruned */
5378   return msg;			/* return this message */
5379 }
5380 
5381 /* Test that purported mother is not a child of purported daughter
5382  * Accepts: mother
5383  *	    purported daughter
5384  * Returns: T if circular parentage exists, else NIL
5385  */
5386 
mail_thread_check_child(container_t mother,container_t daughter)5387 long mail_thread_check_child (container_t mother,container_t daughter)
5388 {
5389   if (mother) {			/* only if mother non-NIL */
5390     if (mother == daughter) return T;
5391     for (daughter = CHILD (daughter); daughter; daughter = SIBLING (daughter))
5392       if (mail_thread_check_child (mother,daughter)) return T;
5393   }
5394   return NIL;
5395 }
5396 
5397 
5398 /* Generate threadnodes from containers
5399  * Accepts: Mail stream
5400  *	    container
5401  *	    flags
5402  * Return: threadnode list
5403  */
5404 
mail_thread_c2node(MAILSTREAM * stream,container_t con,long flags)5405 THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags)
5406 {
5407   THREADNODE *ret,*cur;
5408   SORTCACHE *s;
5409   container_t nxt;
5410 				/* for each container */
5411   for (ret = cur = NIL; con; con = SIBLING (con)) {
5412     s = CACHE (con);		/* yes, get its sortcache */
5413 				/* create node for it */
5414     if (ret) cur = cur->branch = mail_newthreadnode (s);
5415     else ret = cur = mail_newthreadnode (s);
5416 				/* attach sequence or UID for non-dummy */
5417     if (s) cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
5418 				/* attach the children */
5419     if ((nxt = CHILD (con)) != NULL) cur->next = mail_thread_c2node (stream,nxt,flags);
5420   }
5421   return ret;
5422 }
5423 
5424 /* Sort thread tree by date
5425  * Accepts: thread tree to sort
5426  *	    qsort vector to sort
5427  * Returns: sorted thread tree
5428  */
5429 
mail_thread_sort(THREADNODE * thr,THREADNODE ** tc)5430 THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc)
5431 {
5432   unsigned long i,j;
5433   THREADNODE *cur;
5434 				/* sort children of each thread */
5435   for (cur = thr; cur; cur = cur->branch)
5436     if (cur->next) cur->next = mail_thread_sort (cur->next,tc);
5437   /* Must do this in a separate pass since recursive call will clobber tc */
5438 				/* load threadcache and count nodes to sort */
5439   for (i = 0, cur = thr; cur; cur = cur->branch) tc[i++] = cur;
5440   if (i > 1) {			/* only if need to sort */
5441     qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
5442 				/* relink root siblings */
5443     for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
5444     tc[j]->branch = NIL;	/* end of root */
5445   }
5446   return i ? tc[0] : NIL;	/* return new head of list */
5447 }
5448 
5449 
5450 /* Thread compare date
5451  * Accept: first message sort cache element
5452  *	   second message sort cache element
5453  * Returns: -1 if a1 < a2, 1 if a1 > a2
5454  *
5455  * This assumes that a sort cache element is either a message (with a
5456  * sortcache entry) or a dummy with a message (with sortcache entry) child.
5457  * This is true of both the ORDEREDSUBJECT (no dummies) and REFERENCES
5458  * (dummies only at top-level, and with non-dummy children).
5459  *
5460  * If a new algorithm allows a dummy parent to have a dummy child, this
5461  * routine must be changed if it is to be used by that algorithm.
5462  *
5463  * Messages with bogus dates are always sorted at the top.
5464  */
5465 
mail_thread_compare_date(const void * a1,const void * a2)5466 int mail_thread_compare_date (const void *a1,const void *a2)
5467 {
5468   THREADNODE *t1 = *(THREADNODE **) a1;
5469   THREADNODE *t2 = *(THREADNODE **) a2;
5470   SORTCACHE *s1 = t1->sc ? t1->sc : t1->next->sc;
5471   SORTCACHE *s2 = t2->sc ? t2->sc : t2->next->sc;
5472   int ret = compare_ulong (s1->date,s2->date);
5473 				/* use number as final tie-breaker */
5474   return ret ? ret : compare_ulong (s1->num,s2->num);
5475 }
5476 
5477 /* Mail parse sequence
5478  * Accepts: mail stream
5479  *	    sequence to parse
5480  * Returns: T if parse successful, else NIL
5481  */
5482 
mail_sequence(MAILSTREAM * stream,unsigned char * sequence)5483 long mail_sequence (MAILSTREAM *stream,unsigned char *sequence)
5484 {
5485   unsigned long i,j,x;
5486   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
5487   while (sequence && *sequence){/* while there is something to parse */
5488     if (*sequence == '*') {	/* maximum message */
5489       if (stream->nmsgs) i = stream->nmsgs;
5490       else {
5491 	MM_LOG ("No messages, so no maximum message number",ERROR);
5492 	return NIL;
5493       }
5494       sequence++;		/* skip past * */
5495     }
5496 				/* parse and validate message number */
5497     else if (!isdigit (*sequence)) {
5498       MM_LOG ("Syntax error in sequence",ERROR);
5499       return NIL;
5500     }
5501     else if (!(i = strtoul (sequence,(char **) &sequence,10)) ||
5502 	     (i > stream->nmsgs)) {
5503       MM_LOG ("Sequence out of range",ERROR);
5504       return NIL;
5505     }
5506     switch (*sequence) {	/* see what the delimiter is */
5507     case ':':			/* sequence range */
5508       if (*++sequence == '*') {	/* maximum message */
5509 	if (stream->nmsgs) j = stream->nmsgs;
5510 	else {
5511 	  MM_LOG ("No messages, so no maximum message number",ERROR);
5512 	  return NIL;
5513 	}
5514 	sequence++;		/* skip past * */
5515       }
5516 				/* parse end of range */
5517       else if (!(j = strtoul (sequence,(char **) &sequence,10)) ||
5518 	       (j > stream->nmsgs)) {
5519 	MM_LOG ("Sequence range invalid",ERROR);
5520 	return NIL;
5521       }
5522       if (*sequence && *sequence++ != ',') {
5523 	MM_LOG ("Sequence range syntax error",ERROR);
5524 	return NIL;
5525       }
5526       if (i > j) {		/* swap the range if backwards */
5527 	x = i; i = j; j = x;
5528       }
5529 				/* mark each item in the sequence */
5530       while (i <= j) mail_elt (stream,j--)->sequence = T;
5531       break;
5532     case ',':			/* single message */
5533       ++sequence;		/* skip the delimiter, fall into end case */
5534     case '\0':			/* end of sequence, mark this message */
5535       mail_elt (stream,i)->sequence = T;
5536       break;
5537     default:			/* anything else is a syntax error! */
5538       MM_LOG ("Sequence syntax error",ERROR);
5539       return NIL;
5540     }
5541   }
5542   return T;			/* successfully parsed sequence */
5543 }
5544 
5545 /* Parse flag list
5546  * Accepts: MAIL stream
5547  *	    flag list as a character string
5548  *	    pointer to user flags to return
5549  * Returns: system flags
5550  */
5551 
mail_parse_flags(MAILSTREAM * stream,char * flag,unsigned long * uf)5552 long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf)
5553 {
5554   char *t,*n,*s,tmp[MAILTMPLEN],msg[MAILTMPLEN];
5555   short f = 0;
5556   long i,j;
5557   *uf = 0;			/* initially no user flags */
5558   if (flag && *flag) {		/* no-op if no flag string */
5559 				/* check if a list and make sure valid */
5560     if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) ||
5561 	(strlen (flag) >= MAILTMPLEN)) {
5562       MM_LOG ("Bad flag list",ERROR);
5563       return NIL;
5564     }
5565 				/* copy the flag string w/o list construct */
5566     strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i)));
5567     tmp[j] = '\0';
5568     while ((t = n) && *t) {	/* parse the flags */
5569 				/* find end of flag */
5570       if ((n = strchr (t,' ')) != NULL) *n++ = '\0';
5571       if (*t == '\\') {		/* system flag? */
5572 	if (!compare_cstring (t+1,"SEEN")) f |= fSEEN;
5573 	else if (!compare_cstring (t+1,"DELETED")) f |= fDELETED;
5574 	else if (!compare_cstring (t+1,"FLAGGED")) f |= fFLAGGED;
5575 	else if (!compare_cstring (t+1,"ANSWERED")) f |= fANSWERED;
5576 	else if (!compare_cstring (t+1,"DRAFT")) f |= fDRAFT;
5577 	else {
5578 	  sprintf (msg,"Unsupported system flag: %.80s",t);
5579 	  MM_LOG (msg,WARN);
5580 	}
5581       }
5582 
5583       else {			/* keyword flag */
5584 	for (i = j = 0;		/* user flag, search through table */
5585 	     !i && (j < NUSERFLAGS) && (s = stream->user_flags[j]); ++j)
5586 	  if (!compare_cstring (t,s)) *uf |= i = 1 << j;
5587 	if (!i) {		/* flag not found, can it be created? */
5588 	  if (stream->kwd_create && (j < NUSERFLAGS) && *t &&
5589 	      (strlen (t) <= MAXUSERFLAG)) {
5590 	    for (s = t; t && *s; s++) switch (*s) {
5591 	    default:		/* all other characters */
5592 				/* SPACE, CTL, or not CHAR */
5593 	      if ((*s > ' ') && (*s < 0x7f)) break;
5594 	    case '*': case '%':	/* list_wildcards */
5595 	    case '"': case '\\':/* quoted-specials */
5596 				/* atom_specials */
5597 	    case '(': case ')': case '{':
5598 	    case ']':		/* resp-specials */
5599 	      sprintf (msg,"Invalid flag: %.80s",t);
5600 	      MM_LOG (msg,WARN);
5601 	      t = NIL;
5602 	    }
5603 	    if (t) {		/* only if valid */
5604 	      *uf |= 1 << j;	/* set the bit */
5605 	      stream->user_flags[j] = cpystr (t);
5606 				/* if out of user flags */
5607 	      if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
5608 	    }
5609 	  }
5610 	  else {
5611 	    if (*t) sprintf (msg,"Unknown flag: %.80s",t);
5612 	    else strcpy (msg,"Empty flag invalid");
5613 	    MM_LOG (msg,WARN);
5614 	  }
5615 	}
5616       }
5617     }
5618   }
5619   return f;
5620 }
5621 
5622 /* Mail check network stream for usability with new name
5623  * Accepts: MAIL stream
5624  *	    candidate new name
5625  * Returns: T if stream can be used, NIL otherwise
5626  */
5627 
mail_usable_network_stream(MAILSTREAM * stream,char * name)5628 long mail_usable_network_stream (MAILSTREAM *stream,char *name)
5629 {
5630   NETMBX smb,nmb,omb;
5631   char *s = NIL;
5632   long ret= (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) &&
5633 	     mail_valid_net_parse (name,&nmb) &&
5634 	     mail_valid_net_parse (stream->mailbox,&smb) &&
5635 	     mail_valid_net_parse (stream->original_mailbox,&omb) &&
5636 	     ((!compare_cstring (smb.host,trustdns ?
5637 				 (s = tcp_canonical (nmb.host)) : nmb.host) &&
5638 	       !strcmp (smb.service,nmb.service) &&
5639 	       (!nmb.port || (smb.port == nmb.port)) &&
5640 	       (nmb.anoflag == stream->anonymous) &&
5641 	       (!nmb.user[0] || !strcmp (smb.user,nmb.user))) ||
5642 	      (!compare_cstring (omb.host,nmb.host) &&
5643 	       !strcmp (omb.service,nmb.service) &&
5644 	       (!nmb.port || (omb.port == nmb.port)) &&
5645 	       (nmb.anoflag == stream->anonymous) &&
5646 	       (!nmb.user[0] || !strcmp (omb.user,nmb.user))))) ? LONGT : NIL;
5647   if(s) fs_give((void **) &s);
5648   return ret;
5649 }
5650 
5651 /* Mail data structure instantiation routines */
5652 
5653 
5654 /* Mail instantiate cache elt
5655  * Accepts: initial message number
5656  * Returns: new cache elt
5657  */
5658 
mail_new_cache_elt(unsigned long msgno)5659 MESSAGECACHE *mail_new_cache_elt (unsigned long msgno)
5660 {
5661   MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)),
5662 					       0,sizeof (MESSAGECACHE));
5663   elt->lockcount = 1;		/* initially only cache references it */
5664   elt->msgno = msgno;		/* message number */
5665   return elt;
5666 }
5667 
5668 
5669 /* Mail instantiate envelope
5670  * Returns: new envelope
5671  */
5672 
mail_newenvelope(void)5673 ENVELOPE *mail_newenvelope (void)
5674 {
5675   return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE));
5676 }
5677 
5678 
5679 /* Mail instantiate address
5680  * Returns: new address
5681  */
5682 
mail_newaddr(void)5683 ADDRESS *mail_newaddr (void)
5684 {
5685   return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
5686 }
5687 
5688 /* Mail instantiate body
5689  * Returns: new body
5690  */
5691 
mail_newbody(void)5692 BODY *mail_newbody (void)
5693 {
5694   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
5695 }
5696 
5697 
5698 /* Mail initialize body
5699  * Accepts: body
5700  * Returns: body
5701  */
5702 
mail_initbody(BODY * body)5703 BODY *mail_initbody (BODY *body)
5704 {
5705   memset ((void *) body,0,sizeof (BODY));
5706   body->type = TYPETEXT;	/* content type */
5707   body->encoding = ENC7BIT;	/* content encoding */
5708   return body;
5709 }
5710 
5711 
5712 /* Mail instantiate body parameter
5713  * Returns: new body part
5714  */
5715 
mail_newbody_parameter(void)5716 PARAMETER *mail_newbody_parameter (void)
5717 {
5718   return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER));
5719 }
5720 
5721 
5722 /* Mail instantiate body part
5723  * Returns: new body part
5724  */
5725 
mail_newbody_part(void)5726 PART *mail_newbody_part (void)
5727 {
5728   PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART));
5729   mail_initbody (&part->body);	/* initialize the body */
5730   return part;
5731 }
5732 
5733 
5734 /* Mail instantiate body message part
5735  * Returns: new body message part
5736  */
5737 
mail_newmsg(void)5738 MESSAGE *mail_newmsg (void)
5739 {
5740   return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE));
5741 }
5742 
5743 /* Mail instantiate string list
5744  * Returns: new string list
5745  */
5746 
mail_newstringlist(void)5747 STRINGLIST *mail_newstringlist (void)
5748 {
5749   return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0,
5750 				sizeof (STRINGLIST));
5751 }
5752 
5753 
5754 /* Mail instantiate new search program
5755  * Returns: new search program
5756  */
5757 
mail_newsearchpgm(void)5758 SEARCHPGM *mail_newsearchpgm (void)
5759 {
5760   return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM));
5761 }
5762 
5763 
5764 /* Mail instantiate new search program
5765  * Accepts: header line name
5766  * Returns: new search program
5767  */
5768 
mail_newsearchheader(char * line,char * text)5769 SEARCHHEADER *mail_newsearchheader (char *line,char *text)
5770 {
5771   SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)),
5772 					       0,sizeof (SEARCHHEADER));
5773   hdr->line.size = strlen ((char *) (hdr->line.data =
5774 				     (unsigned char *) cpystr (line)));
5775   hdr->text.size = strlen ((char *) (hdr->text.data =
5776 				     (unsigned char *) cpystr (text)));
5777   return hdr;
5778 }
5779 
5780 
5781 /* Mail instantiate new search set
5782  * Returns: new search set
5783  */
5784 
mail_newsearchset(void)5785 SEARCHSET *mail_newsearchset (void)
5786 {
5787   return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET));
5788 }
5789 
5790 
5791 /* Mail instantiate new search or
5792  * Returns: new search or
5793  */
5794 
mail_newsearchor(void)5795 SEARCHOR *mail_newsearchor (void)
5796 {
5797   SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0,
5798 				      sizeof (SEARCHOR));
5799   or->first = mail_newsearchpgm ();
5800   or->second = mail_newsearchpgm ();
5801   return or;
5802 }
5803 
5804 /* Mail instantiate new searchpgmlist
5805  * Returns: new searchpgmlist
5806  */
5807 
mail_newsearchpgmlist(void)5808 SEARCHPGMLIST *mail_newsearchpgmlist (void)
5809 {
5810   SEARCHPGMLIST *pgl = (SEARCHPGMLIST *)
5811     memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST));
5812   pgl->pgm = mail_newsearchpgm ();
5813   return pgl;
5814 }
5815 
5816 
5817 /* Mail instantiate new sortpgm
5818  * Returns: new sortpgm
5819  */
5820 
mail_newsortpgm(void)5821 SORTPGM *mail_newsortpgm (void)
5822 {
5823   return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM));
5824 }
5825 
5826 
5827 /* Mail instantiate new threadnode
5828  * Accepts: sort cache for thread node
5829  * Returns: new threadnode
5830  */
5831 
mail_newthreadnode(SORTCACHE * sc)5832 THREADNODE *mail_newthreadnode (SORTCACHE *sc)
5833 {
5834   THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0,
5835 					   sizeof (THREADNODE));
5836   if (sc) thr->sc = sc;		/* initialize sortcache */
5837   return thr;
5838 }
5839 
5840 
5841 /* Mail instantiate new acllist
5842  * Returns: new acllist
5843  */
5844 
mail_newacllist(void)5845 ACLLIST *mail_newacllist (void)
5846 {
5847   return (ACLLIST *) memset (fs_get (sizeof (ACLLIST)),0,sizeof (ACLLIST));
5848 }
5849 
5850 
5851 /* Mail instantiate new quotalist
5852  * Returns: new quotalist
5853  */
5854 
mail_newquotalist(void)5855 QUOTALIST *mail_newquotalist (void)
5856 {
5857   return (QUOTALIST *) memset (fs_get (sizeof (QUOTALIST)),0,
5858 			       sizeof (QUOTALIST));
5859 }
5860 
5861 /* Mail garbage collection routines */
5862 
5863 
5864 /* Mail garbage collect body
5865  * Accepts: pointer to body pointer
5866  */
5867 
mail_free_body(BODY ** body)5868 void mail_free_body (BODY **body)
5869 {
5870   if (*body) {			/* only free if exists */
5871     mail_free_body_data (*body);/* free its data */
5872     fs_give ((void **) body);	/* return body to free storage */
5873   }
5874 }
5875 
5876 
5877 /* Mail garbage collect body data
5878  * Accepts: body pointer
5879  */
5880 
mail_free_body_data(BODY * body)5881 void mail_free_body_data (BODY *body)
5882 {
5883   switch (body->type) {		/* free contents */
5884   case TYPEMULTIPART:		/* multiple part */
5885     mail_free_body_part (&body->nested.part);
5886     break;
5887   case TYPEMESSAGE:		/* encapsulated message */
5888     if (body->subtype && !strcmp (body->subtype,"RFC822")) {
5889       mail_free_stringlist (&body->nested.msg->lines);
5890       mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS);
5891     }
5892     if (body->nested.msg) fs_give ((void **) &body->nested.msg);
5893     break;
5894   default:
5895     break;
5896   }
5897   if (body->subtype) fs_give ((void **) &body->subtype);
5898   mail_free_body_parameter (&body->parameter);
5899   if (body->id) fs_give ((void **) &body->id);
5900   if (body->description) fs_give ((void **) &body->description);
5901   if (body->disposition.type) fs_give ((void **) &body->disposition.type);
5902   if (body->disposition.parameter)
5903     mail_free_body_parameter (&body->disposition.parameter);
5904   if (body->language) mail_free_stringlist (&body->language);
5905   if (body->location) fs_give ((void **) &body->location);
5906   if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
5907   if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
5908   if (body->md5) fs_give ((void **) &body->md5);
5909   if (mailfreebodysparep && body->sparep)
5910       (*mailfreebodysparep) (&body->sparep);
5911 }
5912 
5913 /* Mail garbage collect body parameter
5914  * Accepts: pointer to body parameter pointer
5915  */
5916 
mail_free_body_parameter(PARAMETER ** parameter)5917 void mail_free_body_parameter (PARAMETER **parameter)
5918 {
5919   if (*parameter) {		/* only free if exists */
5920     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
5921     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
5922 				/* run down the list as necessary */
5923     mail_free_body_parameter (&(*parameter)->next);
5924 				/* return body part to free storage */
5925     fs_give ((void **) parameter);
5926   }
5927 }
5928 
5929 
5930 /* Mail garbage collect body part
5931  * Accepts: pointer to body part pointer
5932  */
5933 
mail_free_body_part(PART ** part)5934 void mail_free_body_part (PART **part)
5935 {
5936   if (*part) {			/* only free if exists */
5937     mail_free_body_data (&(*part)->body);
5938 				/* run down the list as necessary */
5939     mail_free_body_part (&(*part)->next);
5940     fs_give ((void **) part);	/* return body part to free storage */
5941   }
5942 }
5943 
5944 /* Mail garbage collect message cache
5945  * Accepts: mail stream
5946  *
5947  * The message cache is set to NIL when this function finishes.
5948  */
5949 
mail_free_cache(MAILSTREAM * stream)5950 void mail_free_cache (MAILSTREAM *stream)
5951 {
5952 				/* do driver specific stuff first */
5953   mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS);
5954 				/* flush the cache */
5955   (*mailcache) (stream,(long) 0,CH_INIT);
5956 }
5957 
5958 
5959 /* Mail garbage collect cache element
5960  * Accepts: pointer to cache element pointer
5961  */
5962 
mail_free_elt(MESSAGECACHE ** elt)5963 void mail_free_elt (MESSAGECACHE **elt)
5964 {
5965 				/* only free if exists and no sharers */
5966   if (*elt && !--(*elt)->lockcount) {
5967     mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS);
5968     if (mailfreeeltsparep && (*elt)->sparep)
5969       (*mailfreeeltsparep) (&(*elt)->sparep);
5970     fs_give ((void **) elt);
5971   }
5972   else *elt = NIL;		/* else simply drop pointer */
5973 }
5974 
5975 /* Mail garbage collect envelope
5976  * Accepts: pointer to envelope pointer
5977  */
5978 
mail_free_envelope(ENVELOPE ** env)5979 void mail_free_envelope (ENVELOPE **env)
5980 {
5981   if (*env) {			/* only free if exists */
5982     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
5983     mail_free_address (&(*env)->return_path);
5984     if ((*env)->date) fs_give ((void **) &(*env)->date);
5985     mail_free_address (&(*env)->from);
5986     mail_free_address (&(*env)->sender);
5987     mail_free_address (&(*env)->reply_to);
5988     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
5989     mail_free_address (&(*env)->to);
5990     mail_free_address (&(*env)->cc);
5991     mail_free_address (&(*env)->bcc);
5992     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
5993     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
5994     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
5995     if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
5996     if ((*env)->references) fs_give ((void **) &(*env)->references);
5997     if (mailfreeenvelopesparep && (*env)->sparep)
5998       (*mailfreeenvelopesparep) (&(*env)->sparep);
5999     fs_give ((void **) env);	/* return envelope to free storage */
6000   }
6001 }
6002 
6003 
6004 /* Mail garbage collect address
6005  * Accepts: pointer to address pointer
6006  */
6007 
mail_free_address(ADDRESS ** address)6008 void mail_free_address (ADDRESS **address)
6009 {
6010   if (*address) {		/* only free if exists */
6011     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
6012     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
6013     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
6014     if ((*address)->host) fs_give ((void **) &(*address)->host);
6015     if ((*address)->error) fs_give ((void **) &(*address)->error);
6016     if ((*address)->orcpt.type) fs_give ((void **) &(*address)->orcpt.type);
6017     if ((*address)->orcpt.addr) fs_give ((void **) &(*address)->orcpt.addr);
6018     mail_free_address (&(*address)->next);
6019     fs_give ((void **) address);/* return address to free storage */
6020   }
6021 }
6022 
6023 
6024 /* Mail garbage collect stringlist
6025  * Accepts: pointer to stringlist pointer
6026  */
6027 
mail_free_stringlist(STRINGLIST ** string)6028 void mail_free_stringlist (STRINGLIST **string)
6029 {
6030   if (*string) {		/* only free if exists */
6031     if ((*string)->text.data) fs_give ((void **) &(*string)->text.data);
6032     mail_free_stringlist (&(*string)->next);
6033     fs_give ((void **) string);	/* return string to free storage */
6034   }
6035 }
6036 
6037 /* Mail garbage collect searchpgm
6038  * Accepts: pointer to searchpgm pointer
6039  */
6040 
mail_free_searchpgm(SEARCHPGM ** pgm)6041 void mail_free_searchpgm (SEARCHPGM **pgm)
6042 {
6043   if (*pgm) {			/* only free if exists */
6044     mail_free_searchset (&(*pgm)->msgno);
6045     mail_free_searchset (&(*pgm)->uid);
6046     mail_free_searchor (&(*pgm)->or);
6047     mail_free_searchpgmlist (&(*pgm)->not);
6048     mail_free_searchheader (&(*pgm)->header);
6049     mail_free_stringlist (&(*pgm)->bcc);
6050     mail_free_stringlist (&(*pgm)->body);
6051     mail_free_stringlist (&(*pgm)->cc);
6052     mail_free_stringlist (&(*pgm)->from);
6053     mail_free_stringlist (&(*pgm)->keyword);
6054     mail_free_stringlist (&(*pgm)->subject);
6055     mail_free_stringlist (&(*pgm)->text);
6056     mail_free_stringlist (&(*pgm)->to);
6057     mail_free_stringlist (&(*pgm)->x_gm_ext1);
6058     fs_give ((void **) pgm);	/* return program to free storage */
6059   }
6060 }
6061 
6062 
6063 /* Mail garbage collect searchheader
6064  * Accepts: pointer to searchheader pointer
6065  */
6066 
mail_free_searchheader(SEARCHHEADER ** hdr)6067 void mail_free_searchheader (SEARCHHEADER **hdr)
6068 {
6069   if (*hdr) {			/* only free if exists */
6070     if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data);
6071     if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data);
6072     mail_free_searchheader (&(*hdr)->next);
6073     fs_give ((void **) hdr);	/* return header to free storage */
6074   }
6075 }
6076 
6077 
6078 /* Mail garbage collect searchset
6079  * Accepts: pointer to searchset pointer
6080  */
6081 
mail_free_searchset(SEARCHSET ** set)6082 void mail_free_searchset (SEARCHSET **set)
6083 {
6084   if (*set) {			/* only free if exists */
6085     mail_free_searchset (&(*set)->next);
6086     fs_give ((void **) set);	/* return set to free storage */
6087   }
6088 }
6089 
6090 /* Mail garbage collect searchor
6091  * Accepts: pointer to searchor pointer
6092  */
6093 
mail_free_searchor(SEARCHOR ** orl)6094 void mail_free_searchor (SEARCHOR **orl)
6095 {
6096   if (*orl) {			/* only free if exists */
6097     mail_free_searchpgm (&(*orl)->first);
6098     mail_free_searchpgm (&(*orl)->second);
6099     mail_free_searchor (&(*orl)->next);
6100     fs_give ((void **) orl);	/* return searchor to free storage */
6101   }
6102 }
6103 
6104 
6105 /* Mail garbage collect search program list
6106  * Accepts: pointer to searchpgmlist pointer
6107  */
6108 
mail_free_searchpgmlist(SEARCHPGMLIST ** pgl)6109 void mail_free_searchpgmlist (SEARCHPGMLIST **pgl)
6110 {
6111   if (*pgl) {			/* only free if exists */
6112     mail_free_searchpgm (&(*pgl)->pgm);
6113     mail_free_searchpgmlist (&(*pgl)->next);
6114     fs_give ((void **) pgl);	/* return searchpgmlist to free storage */
6115   }
6116 }
6117 
6118 
6119 /* Mail garbage collect namespace
6120  * Accepts: pointer to namespace
6121  */
6122 
mail_free_namespace(NAMESPACE ** n)6123 void mail_free_namespace (NAMESPACE **n)
6124 {
6125   if (*n) {
6126     fs_give ((void **) &(*n)->name);
6127     mail_free_namespace (&(*n)->next);
6128     mail_free_body_parameter (&(*n)->param);
6129     fs_give ((void **) n);	/* return namespace to free storage */
6130   }
6131 }
6132 
6133 /* Mail garbage collect sort program
6134  * Accepts: pointer to sortpgm pointer
6135  */
6136 
mail_free_sortpgm(SORTPGM ** pgm)6137 void mail_free_sortpgm (SORTPGM **pgm)
6138 {
6139   if (*pgm) {			/* only free if exists */
6140     mail_free_sortpgm (&(*pgm)->next);
6141     fs_give ((void **) pgm);	/* return sortpgm to free storage */
6142   }
6143 }
6144 
6145 
6146 /* Mail garbage collect thread node
6147  * Accepts: pointer to threadnode pointer
6148  */
6149 
mail_free_threadnode(THREADNODE ** thr)6150 void mail_free_threadnode (THREADNODE **thr)
6151 {
6152   if (*thr) {			/* only free if exists */
6153     mail_free_threadnode (&(*thr)->branch);
6154     mail_free_threadnode (&(*thr)->next);
6155     fs_give ((void **) thr);	/* return threadnode to free storage */
6156   }
6157 }
6158 
6159 
6160 /* Mail garbage collect acllist
6161  * Accepts: pointer to acllist pointer
6162  */
6163 
mail_free_acllist(ACLLIST ** al)6164 void mail_free_acllist (ACLLIST **al)
6165 {
6166   if (*al) {			/* only free if exists */
6167     if ((*al)->identifier) fs_give ((void **) &(*al)->identifier);
6168     if ((*al)->rights) fs_give ((void **) &(*al)->rights);
6169     mail_free_acllist (&(*al)->next);
6170     fs_give ((void **) al);	/* return acllist to free storage */
6171   }
6172 }
6173 
6174 
6175 /* Mail garbage collect quotalist
6176  * Accepts: pointer to quotalist pointer
6177  */
6178 
mail_free_quotalist(QUOTALIST ** ql)6179 void mail_free_quotalist (QUOTALIST **ql)
6180 {
6181   if (*ql) {			/* only free if exists */
6182     if ((*ql)->name) fs_give ((void **) &(*ql)->name);
6183     mail_free_quotalist (&(*ql)->next);
6184     fs_give ((void **) ql);	/* return quotalist to free storage */
6185   }
6186 }
6187 
6188 /* Link authenticator
6189  * Accepts: authenticator to add to list
6190  */
6191 
auth_link(AUTHENTICATOR * auth)6192 void auth_link (AUTHENTICATOR *auth)
6193 {
6194   if (!auth->valid || (*auth->valid) ()) {
6195     AUTHENTICATOR **a = &mailauthenticators;
6196     while (*a) a = &(*a)->next;	/* find end of list of authenticators */
6197     *a = auth;			/* put authenticator at the end */
6198     auth->next = NIL;		/* this authenticator is the end of the list */
6199   }
6200 }
6201 
6202 
6203 /* Authenticate access
6204  * Accepts: mechanism name
6205  *	    responder function
6206  *	    argument count
6207  *	    argument vector
6208  * Returns: authenticated user name or NIL
6209  */
6210 
mail_auth(char * mechanism,authresponse_t resp,int argc,char * argv[])6211 char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[])
6212 {
6213   AUTHENTICATOR *auth;
6214   for (auth = mailauthenticators; auth; auth = auth->next)
6215     if (auth->server && !compare_cstring (auth->name,mechanism))
6216       return (!(auth->flags & AU_DISABLE) &&
6217 	      ((auth->flags & AU_SECURE) ||
6218 	       !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))) ?
6219 	(*auth->server) (resp,argc,argv) : NIL;
6220   return NIL;			/* no authenticator found */
6221 }
6222 
6223 /* Lookup authenticator index
6224  * Accepts: authenticator index
6225  * Returns: authenticator, or 0 if not found
6226  */
6227 
mail_lookup_auth(unsigned long i)6228 AUTHENTICATOR *mail_lookup_auth (unsigned long i)
6229 {
6230   AUTHENTICATOR *auth = mailauthenticators;
6231   while (auth && --i) auth = auth->next;
6232   return auth;
6233 }
6234 
6235 
6236 /* Lookup authenticator name
6237  * Accepts: authenticator name
6238  *	    required authenticator flags
6239  * Returns: index in authenticator chain, or 0 if not found
6240  */
6241 
mail_lookup_auth_name(char * mechanism,long flags)6242 unsigned int mail_lookup_auth_name (char *mechanism,long flags)
6243 {
6244   int i;
6245   AUTHENTICATOR *auth;
6246   for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
6247     if (auth->client && !(flags & ~auth->flags) &&
6248 	!(auth->flags & AU_DISABLE) && !compare_cstring (auth->name,mechanism))
6249       return i;
6250   return 0;
6251 }
6252 /* Client side callback warning to delete wrong password */
delete_password(NETMBX * mb,char * user)6253 void delete_password(NETMBX *mb, char *user)
6254 {
6255   deletepwd_t ep = mail_parameters(NULL, GET_ERASEPASSWORD, NULL);
6256   if (ep) (ep)(mb, user);
6257 }
6258 /* Standard TCP/IP network driver */
6259 
6260 static NETDRIVER tcpdriver = {
6261   tcp_open,			/* open connection */
6262   tcp_aopen,			/* open preauthenticated connection */
6263   tcp_getline,			/* get a line */
6264   tcp_getbuffer,		/* get a buffer */
6265   tcp_soutr,			/* output pushed data */
6266   tcp_sout,			/* output string */
6267   tcp_close,			/* close connection */
6268   tcp_host,			/* return host name */
6269   tcp_remotehost,		/* return remote host name */
6270   tcp_port,			/* return port number */
6271   tcp_localhost,		/* return local host name */
6272   tcp_getsize			/* read a specific number of bytes */
6273 };
6274 
6275 
6276 /* Network open
6277  * Accepts: NETMBX specifier to open
6278  *	    default network driver
6279  *	    default port
6280  *	    SSL driver
6281  *	    SSL service name
6282  *	    SSL driver port
6283  * Returns: Network stream if success, else NIL
6284  */
6285 
net_open(NETMBX * mb,NETDRIVER * dv,unsigned long port,NETDRIVER * ssld,char * ssls,unsigned long sslp)6286 NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
6287 		     NETDRIVER *ssld,char *ssls,unsigned long sslp)
6288 {
6289   NETSTREAM *stream = NIL;
6290   char tmp[MAILTMPLEN];
6291   unsigned long flags = mb->novalidate ? NET_NOVALIDATECERT : 0;
6292   flags |= mb->tls1 ? NET_TRYTLS1
6293 		: mb->tls1_1 ? NET_TRYTLS1_1
6294 		: mb->tls1_2 ? NET_TRYTLS1_2
6295 		: mb->tls1_3 ? NET_TRYTLS1_3 : 0;
6296   if (strlen (mb->host) >= NETMAXHOST) {
6297     sprintf (tmp,"Invalid host name: %.80s",mb->host);
6298     MM_LOG (tmp,ERROR);
6299   }
6300 				/* use designated driver if given */
6301   else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port,
6302 				       flags);
6303   else if (mb->sslflag && ssld)	/* use ssl if sslflag lit */
6304     stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,flags);
6305 				/* if trysslfirst and can open ssl... */
6306   else if ((mb->trysslflag || trysslfirst) && ssld &&
6307 	   (stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,
6308 				    flags | NET_SILENT | NET_TRYSSL))) {
6309     if (net_sout (stream,"",0)) mb->sslflag = T;
6310     else {
6311       net_close (stream);	/* flush fake SSL stream */
6312       stream = NIL;
6313     }
6314   }
6315 				/* default to TCP driver */
6316   else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port,
6317 			       flags);
6318   return stream;
6319 }
6320 
6321 /* Network open worker routine
6322  * Accepts: network driver
6323  *	    host name
6324  *	    service name to look up port
6325  *	    port number if service name not found
6326  *	    port number to override service name
6327  *	    flags (passed on top of port)
6328  * Returns: Network stream if success, else NIL
6329  */
6330 
net_open_work(NETDRIVER * dv,char * host,char * service,unsigned long port,unsigned long portoverride,unsigned long flags)6331 NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
6332 			  unsigned long port,unsigned long portoverride,
6333 			  unsigned long flags)
6334 {
6335   NETSTREAM *stream = NIL;
6336   void *tstream;
6337   if (service && (*service == '*')) {
6338     flags |= NET_NOOPENTIMEOUT;	/* mark that no timeout is desired */
6339     ++service;			/* no longer need the no timeout indicator */
6340   }
6341   if (portoverride) {		/* explicit port number? */
6342     service = NIL;		/* yes, override service name */
6343     port = portoverride;	/* use that instead of default port */
6344   }
6345   if ((tstream = (*dv->open) (host,service,port | flags)) != NULL){
6346     stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
6347     stream->stream = tstream;
6348     stream->dtb = dv;
6349   }
6350   return stream;
6351 }
6352 
6353 
6354 /* Network authenticated open
6355  * Accepts: network driver
6356  *	    NETMBX specifier
6357  *	    service specifier
6358  *	    return user name buffer
6359  * Returns: Network stream if success else NIL
6360  */
6361 
net_aopen(NETDRIVER * dv,NETMBX * mb,char * service,char * user)6362 NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user)
6363 {
6364   NETSTREAM *stream = NIL;
6365   void *tstream;
6366   if (!dv) dv = &tcpdriver;	/* default to TCP driver */
6367   if ((tstream = (*dv->aopen) (mb,service,user)) != NULL) {
6368     stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
6369     stream->stream = tstream;
6370     stream->dtb = dv;
6371   }
6372   return stream;
6373 }
6374 
6375 /* Network receive line
6376  * Accepts: Network stream
6377  * Returns: text line string or NIL if failure
6378  */
6379 
net_getline(NETSTREAM * stream)6380 char *net_getline (NETSTREAM *stream)
6381 {
6382   return (*stream->dtb->getline) (stream->stream);
6383 }
6384 
6385 
net_getsize(NETSTREAM * stream,unsigned long size)6386 char *net_getsize (NETSTREAM *stream, unsigned long size)
6387 {
6388   return (*stream->dtb->getsize) (stream->stream, size);
6389 }
6390 
6391 
6392 
6393 /* Network receive buffer
6394  * Accepts: Network stream (must be void * for use as readfn_t)
6395  *	    size in bytes
6396  *	    buffer to read into
6397  * Returns: T if success, NIL otherwise
6398  */
6399 
net_getbuffer(void * st,unsigned long size,char * buffer)6400 long net_getbuffer (void *st,unsigned long size,char *buffer)
6401 {
6402   NETSTREAM *stream = (NETSTREAM *) st;
6403   return (*stream->dtb->getbuffer) (stream->stream,size,buffer);
6404 }
6405 
6406 
6407 /* Network send null-terminated string
6408  * Accepts: Network stream
6409  *	    string pointer
6410  * Returns: T if success else NIL
6411  */
6412 
net_soutr(NETSTREAM * stream,char * string)6413 long net_soutr (NETSTREAM *stream,char *string)
6414 {
6415   return (*stream->dtb->soutr) (stream->stream,string);
6416 }
6417 
6418 
6419 /* Network send string
6420  * Accepts: Network stream
6421  *	    string pointer
6422  *	    byte count
6423  * Returns: T if success else NIL
6424  */
6425 
net_sout(NETSTREAM * stream,char * string,unsigned long size)6426 long net_sout (NETSTREAM *stream,char *string,unsigned long size)
6427 {
6428   return (*stream->dtb->sout) (stream->stream,string,size);
6429 }
6430 
6431 /* Network close
6432  * Accepts: Network stream
6433  */
6434 
net_close(NETSTREAM * stream)6435 void net_close (NETSTREAM *stream)
6436 {
6437   if (stream->stream) (*stream->dtb->close) (stream->stream);
6438   fs_give ((void **) &stream);
6439 }
6440 
6441 
6442 /* Network get host name
6443  * Accepts: Network stream
6444  * Returns: host name for this stream
6445  */
6446 
net_host(NETSTREAM * stream)6447 char *net_host (NETSTREAM *stream)
6448 {
6449   return (*stream->dtb->host) (stream->stream);
6450 }
6451 
6452 
6453 /* Network get remote host name
6454  * Accepts: Network stream
6455  * Returns: host name for this stream
6456  */
6457 
net_remotehost(NETSTREAM * stream)6458 char *net_remotehost (NETSTREAM *stream)
6459 {
6460   return (*stream->dtb->remotehost) (stream->stream);
6461 }
6462 
6463 /* Network return port for this stream
6464  * Accepts: Network stream
6465  * Returns: port number for this stream
6466  */
6467 
net_port(NETSTREAM * stream)6468 unsigned long net_port (NETSTREAM *stream)
6469 {
6470   return (*stream->dtb->port) (stream->stream);
6471 }
6472 
6473 
6474 /* Network get local host name
6475  * Accepts: Network stream
6476  * Returns: local host name
6477  */
6478 
net_localhost(NETSTREAM * stream)6479 char *net_localhost (NETSTREAM *stream)
6480 {
6481   return (*stream->dtb->localhost) (stream->stream);
6482 }
6483 
free_c_client_module_globals(void)6484 void free_c_client_module_globals(void)
6485 {
6486    env_end();
6487    tcp_end();
6488 }
6489 
6490 /* OAUTH2 support code goes here. This is necessary because
6491  * 1. it helps to coordinate two different methods, such as XOAUTH2 and
6492  *    OAUTHBEARER, which use the same code, so it can all go in one place
6493  *
6494  * 2. It helps with coordinating with the client when the server requires
6495  *    the deviceinfo method.
6496  */
6497 
6498 #include "oauth2_aux.c"
6499