1 /* ========================================================================
2  * Copyright 1988-2006 University of Washington
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *
11  * ========================================================================
12  */
13 
14 /*
15  * Program:	Berkeley mail routines
16  *
17  * Author:	Mark Crispin
18  *		Networks and Distributed Computing
19  *		Computing & Communications
20  *		University of Washington
21  *		Administration Building, AG-44
22  *		Seattle, WA  98195
23  *		Internet: MRC@CAC.Washington.EDU
24  *
25  * Date:	24 June 1992
26  * Last Edited:	30 August 2006
27  */
28 
29 
30 /* Dedication:
31  *  This file is dedicated with affection to those Merry Marvels of Musical
32  * Madness . . .
33  *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
34  * who entertain, awaken, and outrage Stanford fans in the fact of repeated
35  * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
36  * HUSKY FEVER!!!].
37  *
38  */
39 
40 #include <ctype.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include "mail.h"
44 #include "osdep.h"
45 #include <time.h>
46 #include <sys\stat.h>
47 #include <dos.h>
48 #include "rfc822.h"
49 #include "dummy.h"
50 #include "misc.h"
51 #include "fdstring.h"
52 
53 /* Berkeley I/O stream local data */
54 
55 typedef struct bezerk_local {
56   int fd;			/* file descriptor for I/O */
57   off_t filesize;		/* file size parsed */
58   char *buf;			/* temporary buffer */
59 } BEZERKLOCAL;
60 
61 
62 /* Convenient access to local data */
63 
64 #define LOCAL ((BEZERKLOCAL *) stream->local)
65 
66 /* Function prototypes */
67 
68 DRIVER *bezerk_valid (char *name);
69 long bezerk_isvalid (char *name,char *tmp);
70 int bezerk_valid_line (char *s,char **rx,int *rzn);
71 void *bezerk_parameters (long function,void *value);
72 void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
73 void bezerk_list (MAILSTREAM *stream,char *ref,char *pat);
74 void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat);
75 long bezerk_create (MAILSTREAM *stream,char *mailbox);
76 long bezerk_delete (MAILSTREAM *stream,char *mailbox);
77 long bezerk_rename (MAILSTREAM *stream,char *old,char *newname);
78 MAILSTREAM *bezerk_open (MAILSTREAM *stream);
79 void bezerk_close (MAILSTREAM *stream,long options);
80 char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
81 		     unsigned long *length,long flags);
82 long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,
83 		  long flags);
84 long bezerk_ping (MAILSTREAM *stream);
85 void bezerk_check (MAILSTREAM *stream);
86 long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options);
87 long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
88 		  long options);
89 long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
90 int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
91 		     STRING *msg);
92 void bezerk_gc (MAILSTREAM *stream,long gcflags);
93 char *bezerk_file (char *dst,char *name);
94 long bezerk_badname (char *tmp,char *s);
95 long bezerk_parse (MAILSTREAM *stream);
96 unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
97 			     unsigned long *size);
98 
99 /* Berkeley mail routines */
100 
101 
102 /* Driver dispatch used by MAIL */
103 
104 DRIVER bezerkdriver = {
105   "bezerk",			/* driver name */
106 				/* driver flags */
107   DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
108   (DRIVER *) NIL,		/* next driver */
109   bezerk_valid,			/* mailbox is valid for us */
110   bezerk_parameters,		/* manipulate parameters */
111   bezerk_scan,			/* scan mailboxes */
112   bezerk_list,			/* list mailboxes */
113   bezerk_lsub,			/* list subscribed mailboxes */
114   NIL,				/* subscribe to mailbox */
115   NIL,				/* unsubscribe from mailbox */
116   bezerk_create,		/* create mailbox */
117   bezerk_delete,		/* delete mailbox */
118   bezerk_rename,		/* rename mailbox */
119   mail_status_default,		/* status of mailbox */
120   bezerk_open,			/* open mailbox */
121   bezerk_close,			/* close mailbox */
122   NIL,				/* fetch message "fast" attributes */
123   NIL,				/* fetch message flags */
124   NIL,				/* fetch overview */
125   NIL,				/* fetch message envelopes */
126   bezerk_header,		/* fetch message header */
127   bezerk_text,			/* fetch message text */
128   NIL,				/* fetch partial message text */
129   NIL,				/* unique identifier */
130   NIL,				/* message number */
131   NIL,				/* modify flags */
132   NIL,				/* per-message modify flags */
133   NIL,				/* search for message based on criteria */
134   NIL,				/* sort messages */
135   NIL,				/* thread messages */
136   bezerk_ping,			/* ping mailbox to see if still alive */
137   bezerk_check,			/* check for new messages */
138   bezerk_expunge,		/* expunge deleted messages */
139   bezerk_copy,			/* copy messages to another mailbox */
140   bezerk_append,		/* append string message to mailbox */
141   NIL				/* garbage collect stream */
142 };
143 
144 				/* prototype stream */
145 MAILSTREAM bezerkproto = {&bezerkdriver};
146 
147 /* Berkeley mail validate mailbox
148  * Accepts: mailbox name
149  * Returns: our driver if name is valid, NIL otherwise
150  */
151 
bezerk_valid(char * name)152 DRIVER *bezerk_valid (char *name)
153 {
154   char tmp[MAILTMPLEN];
155   return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL;
156 }
157 
158 
159 /* Berkeley mail test for valid mailbox
160  * Accepts: mailbox name
161  * Returns: T if valid, NIL otherwise
162  */
163 
bezerk_isvalid(char * name,char * tmp)164 long bezerk_isvalid (char *name,char *tmp)
165 {
166   int fd;
167   long ret = NIL;
168   struct stat sbuf;
169   errno = EINVAL;		/* assume invalid argument */
170 				/* if file, get its status */
171   if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
172     if (!sbuf.st_size)errno = 0;/* empty file */
173     else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
174       memset (tmp,'\0',MAILTMPLEN);
175       errno = -1;		/* in case bezerk_valid_line fails */
176       if (read (fd,tmp,MAILTMPLEN-1) >= 0)
177 	ret = bezerk_valid_line (tmp,NIL,NIL);
178       close (fd);		/* close the file */
179     }
180   }
181 				/* in case INBOX but not bezerk format */
182   else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
183 	   ((name[1] == 'N') || (name[1] == 'n')) &&
184 	   ((name[2] == 'B') || (name[2] == 'b')) &&
185 	   ((name[3] == 'O') || (name[3] == 'o')) &&
186 	   ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
187   return ret;			/* return what we should */
188 }
189 
190 /* Validate line
191  * Accepts: pointer to candidate string to validate as a From header
192  *	    return pointer to end of date/time field
193  *	    return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
194  *	    return pointer to offset from t of time zone (if non-zero)
195  * Returns: t,ti,zn set if valid From string, else ti is NIL
196  */
197 
bezerk_valid_line(char * s,char ** rx,int * rzn)198 int bezerk_valid_line (char *s,char **rx,int *rzn)
199 {
200   char *x;
201   int zn;
202   int ti = 0;
203 				/* line must begin with "From " */
204   if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
205 	(s[4] != ' ')) return NIL;
206 				/* find end of line */
207   for (x = s + 5; *x && *x != '\012'; x++);
208   if (!x) return NIL;		/* end of line not found */
209   if (x[-1] == '\015') x--;	/* ignore CR */
210   if ((x - s < 27)) return NIL;	/* line too short */
211   if (x - s >= 41) {		/* possible search for " remote from " */
212     for (zn = -1; x[zn] != ' '; zn--);
213     if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
214 	(x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
215 	(x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
216 	(x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
217       x += zn - 12;
218   }
219   if (x[-5] == ' ') {		/* ends with year? */
220 				/* no timezone? */
221     if (x[-8] == ':') zn = 0,ti = -5;
222 				/* three letter timezone? */
223     else if (x[-9] == ' ') ti = zn = -9;
224 				/* numeric timezone? */
225     else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
226   }
227   else if (x[-4] == ' ') {	/* no year and three leter timezone? */
228     if (x[-9] == ' ') zn = -4,ti = -9;
229   }
230   else if (x[-6] == ' ') {	/* no year and numeric timezone? */
231     if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
232       zn = -6,ti = -11;
233   }
234 				/* time must be www mmm dd hh:mm[:ss] */
235   if (ti && !((x[ti - 3] == ':') &&
236 	      (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
237 	      (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
238 	      (x[ti - 11] == ' '))) return NIL;
239   if (rx) *rx = x;		/* set return values */
240   if (rzn) *rzn = zn;
241   return ti;
242 }
243 
244 /* Berkeley manipulate driver parameters
245  * Accepts: function code
246  *	    function-dependent value
247  * Returns: function-dependent return value
248  */
249 
bezerk_parameters(long function,void * value)250 void *bezerk_parameters (long function,void *value)
251 {
252   return NIL;
253 }
254 
255 
256 /* Berkeley mail scan mailboxes
257  * Accepts: mail stream
258  *	    reference
259  *	    pattern to search
260  *	    string to scan
261  */
262 
bezerk_scan(MAILSTREAM * stream,char * ref,char * pat,char * contents)263 void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
264 {
265   if (stream) dummy_scan (NIL,ref,pat,contents);
266 }
267 
268 
269 /* Berkeley mail list mailboxes
270  * Accepts: mail stream
271  *	    reference
272  *	    pattern to search
273  */
274 
bezerk_list(MAILSTREAM * stream,char * ref,char * pat)275 void bezerk_list (MAILSTREAM *stream,char *ref,char *pat)
276 {
277   if (stream) dummy_list (stream,ref,pat);
278 }
279 
280 
281 /* Berkeley mail list subscribed mailboxes
282  * Accepts: mail stream
283  *	    reference
284  *	    pattern to search
285  */
286 
bezerk_lsub(MAILSTREAM * stream,char * ref,char * pat)287 void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat)
288 {
289   if (stream) dummy_lsub (stream,ref,pat);
290 }
291 
292 /* Berkeley mail create mailbox
293  * Accepts: MAIL stream
294  *	    mailbox name to create
295  * Returns: T on success, NIL on failure
296  */
297 
bezerk_create(MAILSTREAM * stream,char * mailbox)298 long bezerk_create (MAILSTREAM *stream,char *mailbox)
299 {
300   return dummy_create (stream,mailbox);
301 }
302 
303 
304 /* Berkeley mail delete mailbox
305  * Accepts: MAIL stream
306  *	    mailbox name to delete
307  * Returns: T on success, NIL on failure
308  */
309 
bezerk_delete(MAILSTREAM * stream,char * mailbox)310 long bezerk_delete (MAILSTREAM *stream,char *mailbox)
311 {
312   return dummy_delete (stream,mailbox);
313 }
314 
315 
316 /* Berkeley mail rename mailbox
317  * Accepts: MAIL stream
318  *	    old mailbox name
319  *	    new mailbox name (or NIL for delete)
320  * Returns: T on success, NIL on failure
321  */
322 
bezerk_rename(MAILSTREAM * stream,char * old,char * newname)323 long bezerk_rename (MAILSTREAM *stream,char *old,char *newname)
324 {
325   return dummy_rename (stream,old,newname);
326 }
327 
328 /* Berkeley mail open
329  * Accepts: stream to open
330  * Returns: stream on success, NIL on failure
331  */
332 
bezerk_open(MAILSTREAM * stream)333 MAILSTREAM *bezerk_open (MAILSTREAM *stream)
334 {
335   long i;
336   int fd;
337   char *s;
338   char tmp[MAILTMPLEN];
339 				/* return prototype for OP_PROTOTYPE call */
340   if (!stream) return &bezerkproto;
341   if (stream->local) fatal ("bezerk recycle stream");
342   if (!mailboxfile (tmp,stream->mailbox))
343     return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox);
344   if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
345     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
346     mm_log (tmp,ERROR);
347     return NIL;
348   }
349   stream->rdonly = T;		/* this driver is readonly */
350   stream->local = fs_get (sizeof (BEZERKLOCAL));
351 				/* canonicalize the stream mailbox name */
352   fs_give ((void **) &stream->mailbox);
353   if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
354   stream->mailbox = cpystr (tmp);
355   LOCAL->fd = fd;		/* note the file */
356   LOCAL->filesize = 0;		/* initialize parsed file size */
357   LOCAL->buf = NIL;		/* initially no local buffer */
358   stream->sequence++;		/* bump sequence number */
359   stream->uid_validity = time (0);
360 				/* parse mailbox */
361   stream->nmsgs = stream->recent = 0;
362   if (!bezerk_ping (stream)) return NIL;
363   if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
364   stream->perm_seen = stream->perm_deleted =
365     stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL;
366   stream->perm_user_flags = NIL;
367   return stream;		/* return stream to caller */
368 }
369 
370 /* Berkeley mail close
371  * Accepts: MAIL stream
372  *	    close options
373  */
374 
bezerk_close(MAILSTREAM * stream,long options)375 void bezerk_close (MAILSTREAM *stream,long options)
376 {
377   if (stream && LOCAL) {	/* only if a file is open */
378     int silent = stream->silent;
379     stream->silent = T;
380     if (options & CL_EXPUNGE) bezerk_expunge (stream,NIL,NIL);
381     close (LOCAL->fd);		/* close the local file */
382     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
383 				/* nuke the local data */
384     fs_give ((void **) &stream->local);
385     stream->dtb = NIL;		/* log out the DTB */
386   }
387 }
388 
389 /* Berkeley mail fetch message header
390  * Accepts: MAIL stream
391  *	    message # to fetch
392  *	    pointer to returned header text length
393  *	    option flags
394  * Returns: message header in RFC822 format
395  */
396 
bezerk_header(MAILSTREAM * stream,unsigned long msgno,unsigned long * length,long flags)397 char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
398 		     unsigned long *length,long flags)
399 {
400   char tmp[MAILTMPLEN];
401   *length = 0;			/* default to empty */
402   if (flags & FT_UID) return "";/* UID call "impossible" */
403 				/* get to header position */
404   lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET);
405 				/* is buffer big enough? */
406   if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
407   LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
408   LOCAL->buf[*length] = '\0';	/* tie off string */
409 				/* slurp the data */
410   read (LOCAL->fd,LOCAL->buf,(size_t) *length);
411   return LOCAL->buf;
412 }
413 
414 
415 /* Berkeley mail fetch message text (body only)
416  * Accepts: MAIL stream
417  *	    message # to fetch
418  *	    pointer to returned header text length
419  *	    option flags
420  * Returns: T, always
421  */
422 
bezerk_text(MAILSTREAM * stream,unsigned long msgno,STRING * bs,long flags)423 long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
424 {
425   MESSAGECACHE *elt;
426   FDDATA d;
427   unsigned long hdrsize,hdrpos;
428 				/* UID call "impossible" */
429   if (flags & FT_UID) return NIL;
430   elt = mail_elt (stream,msgno);/* if message not seen */
431 				/* mark message as seen */
432   if (elt->seen && !(flags & FT_PEEK)) {
433     elt->seen = T;
434     mm_flags (stream,msgno);
435   }
436 				/* get location of text data */
437   hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize);
438   d.fd = LOCAL->fd;		/* set initial stringstruct */
439   d.pos = hdrpos + hdrsize;
440 				/* flush old buffer */
441   if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
442   d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
443   INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
444   return T;			/* success */
445 }
446 
447 /* Berkeley mail ping mailbox
448  * Accepts: MAIL stream
449  * Returns: T if stream still alive, NIL if not
450  */
451 
bezerk_ping(MAILSTREAM * stream)452 long bezerk_ping (MAILSTREAM *stream)
453 {
454 				/* punt if stream no longer alive */
455   if (!(stream && LOCAL)) return NIL;
456 				/* parse mailbox, punt if parse dies */
457   return (bezerk_parse (stream)) ? T : NIL;
458 }
459 
460 
461 /* Berkeley mail check mailbox (reparses status too)
462  * Accepts: MAIL stream
463  */
464 
bezerk_check(MAILSTREAM * stream)465 void bezerk_check (MAILSTREAM *stream)
466 {
467   unsigned long i = 1;
468   if (bezerk_ping (stream)) {	/* ping mailbox */
469 				/* get new message status */
470     while (i <= stream->nmsgs) mail_elt (stream,i++);
471     mm_log ("Check completed",(long) NIL);
472   }
473 }
474 
475 /* Berkeley mail expunge mailbox
476  * Accepts: MAIL stream
477  *	    sequence to expunge if non-NIL
478  *	    expunge options
479  * Returns: T, always
480  */
481 
bezerk_expunge(MAILSTREAM * stream,char * sequence,long options)482 long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options)
483 {
484   if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",WARN);
485   return LONGT;
486 }
487 
488 /* Berkeley mail copy message(s)
489  * Accepts: MAIL stream
490  *	    sequence
491  *	    destination mailbox
492  *	    copy options
493  * Returns: T if success, NIL if failed
494  */
495 
bezerk_copy(MAILSTREAM * stream,char * sequence,char * mailbox,long options)496 long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
497 {
498   char tmp[MAILTMPLEN];
499   struct stat sbuf;
500   MESSAGECACHE *elt;
501   unsigned long i,j,k;
502   int fd;
503   mailproxycopy_t pc =
504     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
505   if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
506 	mail_sequence (stream,sequence))) return NIL;
507 				/* make sure valid mailbox */
508   if (!bezerk_isvalid (mailbox,tmp) && errno) {
509     if (errno == ENOENT)
510       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
511 		 (long) NIL);
512     else if (pc) return (*pc) (stream,sequence,mailbox,options);
513     else if (mailboxfile (tmp,mailbox)) {
514       sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox);
515       mm_log (tmp,ERROR);
516     }
517     else bezerk_badname (tmp,mailbox);
518     return NIL;
519   }
520 				/* open the destination */
521   if (!mailboxfile (tmp,mailbox) ||
522       (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
523 		  S_IREAD|S_IWRITE)) < 0) {
524     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
525     mm_log (tmp,ERROR);
526     return NIL;
527   }
528 
529   mm_critical (stream);		/* go critical */
530   fstat (fd,&sbuf);		/* get current file size */
531 				/* for each requested message */
532   for (i = 1; i <= stream->nmsgs; i++)
533     if ((elt = mail_elt (stream,i))->sequence) {
534       lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
535 				/* number of bytes to copy */
536       j = elt->private.msg.full.offset + elt->rfc822_size;
537       do {			/* read from source position */
538 	k = min (j,(unsigned long) MAILTMPLEN);
539 	read (LOCAL->fd,tmp,(unsigned int) k);
540 	if (write (fd,tmp,(unsigned int) k) < 0) {
541 	  sprintf (tmp,"Unable to write message: %s",strerror (errno));
542 	  mm_log (tmp,ERROR);
543 	  chsize (fd,sbuf.st_size);
544 	  close (fd);		/* punt */
545 	  mm_nocritical (stream);
546 	  return NIL;
547 	}
548       } while (j -= k);		/* until done */
549     }
550   close (fd);			/* close the file */
551   mm_nocritical (stream);	/* release critical */
552 				/* delete all requested messages */
553   if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
554     if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
555   if (mail_parameters (NIL,GET_COPYUID,NIL))
556     mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
557   return T;
558 }
559 
560 /* Berkeley mail append message from stringstruct
561  * Accepts: MAIL stream
562  *	    destination mailbox
563  *	    append callback
564  *	    data for callback
565  * Returns: T if append successful, else NIL
566  */
567 
568 #define BUFLEN MAILTMPLEN
569 
bezerk_append(MAILSTREAM * stream,char * mailbox,append_t af,void * data)570 long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
571 {
572   struct stat sbuf;
573   int fd;
574   unsigned long i,j;
575   char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
576   FILE *sf,*df;
577   MESSAGECACHE elt;
578   STRING *message;
579   long ret = LONGT;
580 				/* default stream to prototype */
581   if (!stream) stream = &bezerkproto;
582 				/* make sure valid mailbox */
583   if (!bezerk_isvalid (mailbox,tmp) && errno) {
584     if (errno == ENOENT) {
585       if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
586 	  ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
587 	  ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
588 	  ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
589 	  ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
590 	bezerk_create (NIL,"INBOX");
591       else {
592 	mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
593 	return NIL;
594       }
595     }
596     else if (mailboxfile (tmp,mailbox)) {
597       sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox);
598       mm_log (tmp,ERROR);
599     }
600     else bezerk_badname (tmp,mailbox);
601     return NIL;
602   }
603   tzset ();			/* initialize timezone stuff */
604 				/* get first message */
605   if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
606   if (!(sf = tmpfile ())) {	/* must have scratch file */
607     sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
608     mm_log (tmp,ERROR);
609   }
610 
611   do {				/* parse date */
612     if (!date) rfc822_date (date = tmp);
613     if (!mail_parse_date (&elt,date)) {
614       sprintf (tmp,"Bad date in append: %.80s",date);
615       mm_log (tmp,ERROR);
616     }
617     else {			/* user wants to suppress time zones? */
618       if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
619 	time_t when = mail_longdate (&elt);
620 	date = ctime (&when);	/* use traditional date */
621       }
622 				/* use POSIX-style date */
623       else date = mail_cdate (tmp,&elt);
624       if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
625       else if (!bezerk_append_msg (stream,sf,flags,date,message)) {
626 	sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
627 	mm_log (tmp,ERROR);
628       }
629 				/* get next message */
630       else if ((*af) (stream,data,&flags,&date,&message)) continue;
631     }
632     fclose (sf);		/* punt scratch file */
633     return NIL;			/* give up */
634   } while (message);		/* until no more messages */
635   if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
636     sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
637     mm_log (tmp,ERROR);
638     fclose (sf);		/* punt scratch file */
639     return NIL;			/* give up */
640   }
641   i = sbuf.st_size;		/* size of scratch file */
642 
643   mm_critical (stream);		/* go critical */
644 				/* open the destination */
645   if (!mailboxfile (tmp,mailbox) ||
646       ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
647 		   S_IREAD|S_IWRITE)) < 0) ||
648       !(df = fdopen (fd,"ab"))) {
649     mm_nocritical (stream);	/* done with critical */
650     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
651     mm_log (tmp,ERROR);
652     return NIL;
653   }
654   fstat (fd,&sbuf);		/* get current file size */
655   while (i)			/* until written all bytes */
656     if ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) &&
657 	(fwrite (buf,1,j,df) == j)) i -= j;
658   fclose (sf);			/* done with scratch file */
659 				/* make sure append wins */
660   if (i || (fflush (df) == EOF)) {
661     chsize (fd,sbuf.st_size);	/* revert file */
662     close (fd);			/* make sure fclose() doesn't corrupt us */
663     sprintf (buf,"Message append failed: %s",strerror (errno));
664     mm_log (buf,ERROR);
665     ret = NIL;			/* return error */
666   }
667   fclose (df);
668   mm_nocritical (stream);	/* release critical */
669   if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
670     mm_log ("Can not return meaningful APPENDUID with this mailbox format",
671 	    WARN);
672   return ret;
673 }
674 
675 /* Write single message to append scratch file
676  * Accepts: MAIL stream
677  *	    scratch file
678  *	    flags
679  *	    message stringstruct
680  * Returns: NIL if write error, else T
681  */
682 
bezerk_append_msg(MAILSTREAM * stream,FILE * sf,char * flags,char * date,STRING * msg)683 int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
684 		     STRING *msg)
685 {
686   int c;
687   unsigned long i,uf;
688   char tmp[MAILTMPLEN];
689   long f = mail_parse_flags (stream,flags,&uf);
690 				/* build initial header */
691   if ((fprintf (sf,"From %s@%s %sStatus: ",
692 		myusername (),mylocalhost (),date) < 0) ||
693       (f&fSEEN && (putc ('R',sf) == EOF)) ||
694       (fputs ("\nX-Status: ",sf) == EOF) ||
695       (f&fDELETED && (putc ('D',sf) == EOF)) ||
696       (f&fFLAGGED && (putc ('F',sf) == EOF)) ||
697       (f&fANSWERED && (putc ('A',sf) == EOF)) ||
698       (f&fDRAFT && (putc ('T',sf) == EOF)) ||
699       (fputs ("\nX-Keywords:",sf) == EOF)) return NIL;
700   while (uf)			/* write user flags */
701     if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0)
702       return NIL;
703 				/* tie off flags */
704   if (putc ('\n',sf) == EOF) return NIL;
705   while (SIZE (msg)) {		/* copy text to scratch file */
706 				/* possible delimiter if line starts with F */
707     if ((c = 0xff & SNX (msg)) == 'F') {
708 				/* copy line to buffer */
709       for (i = 1,tmp[0] = c; SIZE (msg) && (c != '\n') && (i < MAILTMPLEN);)
710 	if (((c = 0xff & SNX (msg)) != '\r') || !(SIZE (msg)) ||
711 	    (CHR (msg) != '\n')) tmp[i++] = c;
712       if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') &&
713 	  (tmp[4] == ' ')) {	/* possible "From " line? */
714 				/* yes, see if need to write a widget */
715 	if (((c != '\n') || bezerk_valid_line (tmp,NIL,NIL)) &&
716 	    (putc ('>',sf) == EOF)) return NIL;
717       }
718 				/* write buffered text */
719       if (fwrite (tmp,1,i,sf) != i) return NIL;
720       if (c == '\n') continue;	/* all done if got a complete line */
721     }
722 				/* copy line, toss out CR from CRLF */
723     do if (((c == '\r') && SIZE (msg) && ((c = 0xff & SNX (msg)) != '\n') &&
724 	    (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF)) return NIL;
725     while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T));
726   }
727 				/* write trailing newline and return */
728   return (putc ('\n',sf) == EOF) ? NIL : T;
729 }
730 
731 
732 /* Return bad file name error message
733  * Accepts: temporary buffer
734  *	    file name
735  * Returns: long NIL always
736  */
737 
bezerk_badname(char * tmp,char * s)738 long bezerk_badname (char *tmp,char *s)
739 {
740   sprintf (tmp,"Invalid mailbox name: %s",s);
741   mm_log (tmp,ERROR);
742   return (long) NIL;
743 }
744 
745 /* Parse mailbox
746  * Accepts: MAIL stream
747  * Returns: T if parse OK
748  *	    NIL if failure, stream aborted
749  */
750 
bezerk_parse(MAILSTREAM * stream)751 long bezerk_parse (MAILSTREAM *stream)
752 {
753   struct stat sbuf;
754   MESSAGECACHE *elt;
755   char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
756   long i;
757   int j,ti,zn;
758   long curpos = LOCAL->filesize;
759   long nmsgs = stream->nmsgs;
760   long recent = stream->recent;
761   short silent = stream->silent;
762   fstat (LOCAL->fd,&sbuf);	/* get status */
763   if (sbuf.st_size < curpos) {	/* sanity check */
764     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
765     mm_log (tmp,ERROR);
766     bezerk_close (stream,NIL);
767     return NIL;
768   }
769   stream->silent = T;		/* don't pass up mm_exists() events yet */
770   db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
771 				/* while there is data to read */
772   while (i = sbuf.st_size - curpos){
773 				/* get to that position in the file */
774     lseek (LOCAL->fd,curpos,SEEK_SET);
775 				/* read first buffer's worth */
776     read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
777     tmp[j] = '\0';		/* tie off buffer */
778     if (!(ti = bezerk_valid_line (tmp,&t,&zn))) {
779       mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
780       bezerk_close (stream,NIL);
781       return NIL;
782     }
783 
784 				/* swell the cache */
785     mail_exists (stream,++nmsgs);
786 				/* instantiate an elt for this message */
787     (elt = mail_elt (stream,nmsgs))->valid = T;
788     elt->private.uid = ++stream->uid_last;
789 				/* note file offset of header */
790     elt->private.special.offset = curpos;
791 				/* note offset of message */
792     elt->private.msg.full.offset =
793       (s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp;
794 				/* generate plausable IMAPish date string */
795     db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
796 				/* dd */
797     db[0] = t[ti - 2]; db[1] = t[ti - 1];
798 				/* mmm */
799     db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
800 				/* hh */
801     db[12] = t[ti + 1]; db[13] = t[ti + 2];
802 				/* mm */
803     db[15] = t[ti + 4]; db[16] = t[ti + 5];
804     if (t[ti += 6] == ':') {	/* ss if present */
805       db[18] = t[++ti]; db[19] = t[++ti];
806       ti++;			/* move to space */
807     }
808     else db[18] = db[19] = '0';	/* assume 0 seconds */
809 				/* yy -- advance over timezone if necessary */
810     if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
811     db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
812     t = zn ? (t + zn) : "LCL";	/* zzz */
813     db[21] = *t++; db[22] = *t++; db[23] = *t++;
814     if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0';
815     else {			/* numeric time zone */
816       db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0';
817     }
818 				/* set internal date */
819     if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
820 
821     curpos += s - tmp;		/* advance position after header */
822     t = strchr (s,'\012');	/* start of next line */
823 				/* find start of next message */
824     while (!(bezerk_valid_line (s,NIL,NIL))) {
825       if (t) {			/* have next line? */
826 	t++;			/* advance to new line */
827 	curpos += t - s;	/* update position and size */
828 	elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1));
829 	s = t;			/* move to next line */
830 	t = strchr (s,'\012');
831       }
832       else {			/* try next buffer */
833 	j = strlen (s);		/* length of unread data in buffer */
834 	if ((i = sbuf.st_size - curpos) && (i != j)) {
835 				/* get to that position in the file */
836 	  lseek (LOCAL->fd,curpos,SEEK_SET);
837 				/* read another buffer's worth */
838 	  read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
839 	  tmp[j] = '\0';	/* tie off buffer */
840 	  if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox");
841 	}
842 	else {
843 	  curpos += j;		/* last bit of data */
844 	  elt->rfc822_size += j;
845 	  break;
846 	}
847       }
848     }
849   }
850 				/* update parsed file size */
851   LOCAL->filesize = sbuf.st_size;
852   stream->silent = silent;	/* can pass up events now */
853   mail_exists (stream,nmsgs);	/* notify upper level of new mailbox size */
854   mail_recent (stream,recent);	/* and of change in recent messages */
855   return T;			/* return the winnage */
856 }
857 
858 /* Berkeley locate header for a message
859  * Accepts: MAIL stream
860  *	    message number
861  *	    pointer to returned header size
862  * Returns: position of header in file
863  */
864 
bezerk_hdrpos(MAILSTREAM * stream,unsigned long msgno,unsigned long * size)865 unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
866 			     unsigned long *size)
867 {
868   long siz;
869   size_t i = 0;
870   char c = '\0';
871   char *s;
872   char tmp[MAILTMPLEN];
873   MESSAGECACHE *elt = mail_elt (stream,msgno);
874   long pos = elt->private.special.offset + elt->private.msg.full.offset;
875 				/* is size known? */
876   if (!(*size = elt->private.msg.header.text.size)) {
877 				/* get to header position */
878     lseek (LOCAL->fd,pos,SEEK_SET);
879 				/* search message for CRLF CRLF */
880     for (siz = 1; siz <= elt->rfc822_size; siz++) {
881       if (!i &&			/* buffer empty? */
882 	  (read (LOCAL->fd,s = tmp,
883 		 i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
884 	return pos;
885       else i--;
886 				/* two newline sequence? */
887       if ((c == '\012') && (*s == '\012')) {
888 				/* yes, note for later */
889 	elt->private.msg.header.text.size = (*size = siz);
890 	return pos;		/* return to caller */
891       }
892       else if ((c == '\012') && (*s == '\015')) {
893 				/* yes, note for later */
894 	elt->private.msg.header.text.size = (*size = siz + 1);
895 	return pos;		/* return to caller */
896       }
897       else c = *s++;		/* next character */
898     }
899   }
900   return pos;			/* have position */
901 }
902