bezrkdos.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:25k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: Berkeley mail routines
  3.  *
  4.  * Author: Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 24 June 1992
  13.  * Last Edited: 24 August 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. /* Dedication:
  36.  *  This file is dedicated with affection to those Merry Marvels of Musical
  37.  * Madness . . .
  38.  *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
  39.  * who entertain, awaken, and outrage Stanford fans in the fact of repeated
  40.  * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
  41.  * HUSKY FEVER!!!].
  42.  *
  43.  */
  44. #include <ctype.h>
  45. #include <errno.h>
  46. #include <fcntl.h>
  47. #include "mail.h"
  48. #include "osdep.h"
  49. #include <time.h>
  50. #include <sysstat.h>
  51. #include <dos.h>
  52. #include "bezrkdos.h"
  53. #include "rfc822.h"
  54. #include "dummy.h"
  55. #include "misc.h"
  56. #include "fdstring.h"
  57. /* Berkeley mail routines */
  58. /* Driver dispatch used by MAIL */
  59. DRIVER bezerkdriver = {
  60.   "bezerk", /* driver name */
  61. /* driver flags */
  62.   DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
  63.   (DRIVER *) NIL, /* next driver */
  64.   bezerk_valid, /* mailbox is valid for us */
  65.   bezerk_parameters, /* manipulate parameters */
  66.   bezerk_scan, /* scan mailboxes */
  67.   bezerk_list, /* list mailboxes */
  68.   bezerk_lsub, /* list subscribed mailboxes */
  69.   NIL, /* subscribe to mailbox */
  70.   NIL, /* unsubscribe from mailbox */
  71.   bezerk_create, /* create mailbox */
  72.   bezerk_delete, /* delete mailbox */
  73.   bezerk_rename, /* rename mailbox */
  74.   NIL, /* status of mailbox */
  75.   bezerk_open, /* open mailbox */
  76.   bezerk_close, /* close mailbox */
  77.   NIL, /* fetch message "fast" attributes */
  78.   NIL, /* fetch message flags */
  79.   NIL, /* fetch overview */
  80.   NIL, /* fetch message envelopes */
  81.   bezerk_header, /* fetch message header */
  82.   bezerk_text, /* fetch message text */
  83.   NIL, /* fetch partial message text */
  84.   NIL, /* unique identifier */
  85.   NIL, /* message number */
  86.   NIL, /* modify flags */
  87.   NIL, /* per-message modify flags */
  88.   NIL, /* search for message based on criteria */
  89.   NIL, /* sort messages */
  90.   NIL, /* thread messages */
  91.   bezerk_ping, /* ping mailbox to see if still alive */
  92.   bezerk_check, /* check for new messages */
  93.   bezerk_expunge, /* expunge deleted messages */
  94.   bezerk_copy, /* copy messages to another mailbox */
  95.   bezerk_append, /* append string message to mailbox */
  96.   NIL /* garbage collect stream */
  97. };
  98. /* prototype stream */
  99. MAILSTREAM bezerkproto = {&bezerkdriver};
  100. /* Berkeley mail validate mailbox
  101.  * Accepts: mailbox name
  102.  * Returns: our driver if name is valid, NIL otherwise
  103.  */
  104. DRIVER *bezerk_valid (char *name)
  105. {
  106.   char tmp[MAILTMPLEN];
  107.   return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL;
  108. }
  109. /* Berkeley mail test for valid mailbox
  110.  * Accepts: mailbox name
  111.  * Returns: T if valid, NIL otherwise
  112.  */
  113. long bezerk_isvalid (char *name,char *tmp)
  114. {
  115.   int fd;
  116.   long ret = NIL;
  117.   struct stat sbuf;
  118.   errno = EINVAL; /* assume invalid argument */
  119. /* if file, get its status */
  120.   if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
  121.     if (!sbuf.st_size)errno = 0;/* empty file */
  122.     else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
  123.       memset (tmp,'',MAILTMPLEN);
  124.       errno = -1; /* in case bezerk_valid_line fails */
  125.       if (read (fd,tmp,MAILTMPLEN-1) >= 0)
  126. ret = bezerk_valid_line (tmp,NIL,NIL);
  127.       close (fd); /* close the file */
  128.     }
  129.   }
  130. /* in case INBOX but not bezerk format */
  131.   else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
  132.    ((name[1] == 'N') || (name[1] == 'n')) &&
  133.    ((name[2] == 'B') || (name[2] == 'b')) &&
  134.    ((name[3] == 'O') || (name[3] == 'o')) &&
  135.    ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
  136.   return ret; /* return what we should */
  137. }
  138. /* Validate line
  139.  * Accepts: pointer to candidate string to validate as a From header
  140.  *     return pointer to end of date/time field
  141.  *     return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
  142.  *     return pointer to offset from t of time zone (if non-zero)
  143.  * Returns: t,ti,zn set if valid From string, else ti is NIL
  144.  */
  145. int bezerk_valid_line (char *s,char **rx,int *rzn)
  146. {
  147.   char *x;
  148.   int zn;
  149.   int ti = 0;
  150. /* line must begin with "From " */
  151.   if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
  152. (s[4] != ' ')) return NIL;
  153. /* find end of line */
  154.   for (x = s + 5; *x && *x != '12'; x++);
  155.   if (!x) return NIL; /* end of line not found */
  156.   if (x[-1] == '15') x--; /* ignore CR */
  157.   if ((x - s < 27)) return NIL; /* line too short */
  158.   if (x - s >= 41) { /* possible search for " remote from " */
  159.     for (zn = -1; x[zn] != ' '; zn--);
  160.     if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
  161. (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
  162. (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
  163. (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
  164.       x += zn - 12;
  165.   }
  166.   if (x[-5] == ' ') { /* ends with year? */
  167. /* no timezone? */
  168.     if (x[-8] == ':') zn = 0,ti = -5;
  169. /* three letter timezone? */
  170.     else if (x[-9] == ' ') ti = zn = -9;
  171. /* numeric timezone? */
  172.     else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
  173.   }
  174.   else if (x[-4] == ' ') { /* no year and three leter timezone? */
  175.     if (x[-9] == ' ') zn = -4,ti = -9;
  176.   }
  177.   else if (x[-6] == ' ') { /* no year and numeric timezone? */
  178.     if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
  179.       zn = -6,ti = -11;
  180.   }
  181. /* time must be www mmm dd hh:mm[:ss] */
  182.   if (ti && !((x[ti - 3] == ':') &&
  183.       (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
  184.       (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
  185.       (x[ti - 11] == ' '))) return NIL;
  186.   if (rx) *rx = x; /* set return values */
  187.   if (rzn) *rzn = zn;
  188.   return ti;
  189. }
  190. /* Berkeley manipulate driver parameters
  191.  * Accepts: function code
  192.  *     function-dependent value
  193.  * Returns: function-dependent return value
  194.  */
  195. void *bezerk_parameters (long function,void *value)
  196. {
  197.   return NIL;
  198. }
  199. /* Berkeley mail scan mailboxes
  200.  * Accepts: mail stream
  201.  *     reference
  202.  *     pattern to search
  203.  *     string to scan
  204.  */
  205. void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  206. {
  207.   if (stream) dummy_scan (NIL,ref,pat,contents);
  208. }
  209. /* Berkeley mail list mailboxes
  210.  * Accepts: mail stream
  211.  *     reference
  212.  *     pattern to search
  213.  */
  214. void bezerk_list (MAILSTREAM *stream,char *ref,char *pat)
  215. {
  216.   if (stream) dummy_list (stream,ref,pat);
  217. }
  218. /* Berkeley mail list subscribed mailboxes
  219.  * Accepts: mail stream
  220.  *     reference
  221.  *     pattern to search
  222.  */
  223. void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat)
  224. {
  225.   if (stream) dummy_lsub (stream,ref,pat);
  226. }
  227. /* Berkeley mail create mailbox
  228.  * Accepts: MAIL stream
  229.  *     mailbox name to create
  230.  * Returns: T on success, NIL on failure
  231.  */
  232. long bezerk_create (MAILSTREAM *stream,char *mailbox)
  233. {
  234.   return dummy_create (stream,mailbox);
  235. }
  236. /* Berkeley mail delete mailbox
  237.  * Accepts: MAIL stream
  238.  *     mailbox name to delete
  239.  * Returns: T on success, NIL on failure
  240.  */
  241. long bezerk_delete (MAILSTREAM *stream,char *mailbox)
  242. {
  243.   return dummy_delete (stream,mailbox);
  244. }
  245. /* Berkeley mail rename mailbox
  246.  * Accepts: MAIL stream
  247.  *     old mailbox name
  248.  *     new mailbox name (or NIL for delete)
  249.  * Returns: T on success, NIL on failure
  250.  */
  251. long bezerk_rename (MAILSTREAM *stream,char *old,char *newname)
  252. {
  253.   return dummy_rename (stream,old,newname);
  254. }
  255. /* Berkeley mail open
  256.  * Accepts: stream to open
  257.  * Returns: stream on success, NIL on failure
  258.  */
  259. MAILSTREAM *bezerk_open (MAILSTREAM *stream)
  260. {
  261.   long i;
  262.   int fd;
  263.   char *s;
  264.   char tmp[MAILTMPLEN];
  265. /* return prototype for OP_PROTOTYPE call */
  266.   if (!stream) return &bezerkproto;
  267.   if (stream->local) fatal ("bezerk recycle stream");
  268.   if (!mailboxfile (tmp,stream->mailbox))
  269.     return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox);
  270.   if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
  271.     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  272.     mm_log (tmp,ERROR);
  273.     return NIL;
  274.   }
  275.   stream->rdonly = T; /* this driver is readonly */
  276.   stream->local = fs_get (sizeof (BEZERKLOCAL));
  277. /* canonicalize the stream mailbox name */
  278.   fs_give ((void **) &stream->mailbox);
  279.   if (s = strchr ((s = strrchr (tmp,'\')) ? s : tmp,'.')) *s = '';
  280.   stream->mailbox = cpystr (tmp);
  281.   LOCAL->fd = fd; /* note the file */
  282.   LOCAL->filesize = 0; /* initialize parsed file size */
  283.   LOCAL->buf = NIL; /* initially no local buffer */
  284.   stream->sequence++; /* bump sequence number */
  285.   stream->uid_validity = time (0);
  286. /* parse mailbox */
  287.   stream->nmsgs = stream->recent = 0;
  288.   if (!bezerk_ping (stream)) return NIL;
  289.   if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
  290.   stream->perm_seen = stream->perm_deleted =
  291.     stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL;
  292.   stream->perm_user_flags = NIL;
  293.   return stream; /* return stream to caller */
  294. }
  295. /* Berkeley mail close
  296.  * Accepts: MAIL stream
  297.  *     close options
  298.  */
  299. void bezerk_close (MAILSTREAM *stream,long options)
  300. {
  301.   if (stream && LOCAL) { /* only if a file is open */
  302.     int silent = stream->silent;
  303.     stream->silent = T;
  304.     if (options & CL_EXPUNGE) bezerk_expunge (stream);
  305.     close (LOCAL->fd); /* close the local file */
  306.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  307. /* nuke the local data */
  308.     fs_give ((void **) &stream->local);
  309.     stream->dtb = NIL; /* log out the DTB */
  310.   }
  311. }
  312. /* Berkeley mail fetch message header
  313.  * Accepts: MAIL stream
  314.  *     message # to fetch
  315.  *     pointer to returned header text length
  316.  *     option flags
  317.  * Returns: message header in RFC822 format
  318.  */
  319. char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
  320.      unsigned long *length,long flags)
  321. {
  322.   char tmp[MAILTMPLEN];
  323.   *length = 0; /* default to empty */
  324.   if (flags & FT_UID) return "";/* UID call "impossible" */
  325. /* get to header position */
  326.   lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET);
  327. /* is buffer big enough? */
  328.   if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  329.   LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
  330.   LOCAL->buf[*length] = ''; /* tie off string */
  331. /* slurp the data */
  332.   read (LOCAL->fd,LOCAL->buf,(size_t) *length);
  333.   return LOCAL->buf;
  334. }
  335. /* Berkeley mail fetch message text (body only)
  336.  * Accepts: MAIL stream
  337.  *     message # to fetch
  338.  *     pointer to returned header text length
  339.  *     option flags
  340.  * Returns: T, always
  341.  */
  342. long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  343. {
  344.   MESSAGECACHE *elt;
  345.   FDDATA d;
  346.   unsigned long hdrsize,hdrpos;
  347. /* UID call "impossible" */
  348.   if (flags & FT_UID) return NIL;
  349.   elt = mail_elt (stream,msgno);/* if message not seen */
  350. /* mark message as seen */
  351.   if (elt->seen && !(flags & FT_PEEK)) {
  352.     elt->seen = T;
  353.     mm_flags (stream,msgno);
  354.   }
  355. /* get location of text data */
  356.   hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize);
  357.   d.fd = LOCAL->fd; /* set initial stringstruct */
  358.   d.pos = hdrpos + hdrsize;
  359. /* flush old buffer */
  360.   if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  361.   d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNK);
  362.   INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
  363.   return T; /* success */
  364. }
  365. /* Berkeley mail ping mailbox
  366.  * Accepts: MAIL stream
  367.  * Returns: T if stream still alive, NIL if not
  368.  */
  369. long bezerk_ping (MAILSTREAM *stream)
  370. {
  371. /* punt if stream no longer alive */
  372.   if (!(stream && LOCAL)) return NIL;
  373. /* parse mailbox, punt if parse dies */
  374.   return (bezerk_parse (stream)) ? T : NIL;
  375. }
  376. /* Berkeley mail check mailbox (reparses status too)
  377.  * Accepts: MAIL stream
  378.  */
  379. void bezerk_check (MAILSTREAM *stream)
  380. {
  381.   unsigned long i = 1;
  382.   if (bezerk_ping (stream)) { /* ping mailbox */
  383. /* get new message status */
  384.     while (i <= stream->nmsgs) mail_elt (stream,i++);
  385.     mm_log ("Check completed",(long) NIL);
  386.   }
  387. }
  388. /* Berkeley mail expunge mailbox
  389.  * Accepts: MAIL stream
  390.  */
  391. void bezerk_expunge (MAILSTREAM *stream)
  392. {
  393.   mm_log ("Expunge ignored on readonly mailbox",WARN);
  394. }
  395. /* Berkeley mail copy message(s)
  396.  * Accepts: MAIL stream
  397.  *     sequence
  398.  *     destination mailbox
  399.  *     copy options
  400.  * Returns: T if success, NIL if failed
  401.  */
  402. long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  403. {
  404.   char tmp[MAILTMPLEN];
  405.   struct stat sbuf;
  406.   MESSAGECACHE *elt;
  407.   unsigned long i,j,k;
  408.   int fd;
  409.   mailproxycopy_t pc =
  410.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  411.   if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
  412. mail_sequence (stream,sequence))) return NIL;
  413. /* make sure valid mailbox */
  414.   if (!bezerk_isvalid (mailbox,tmp) && errno) {
  415.     if (errno == ENOENT)
  416.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  417.  (long) NIL);
  418.     else if (pc) return (*pc) (stream,sequence,mailbox,options);
  419.     else if (mailboxfile (tmp,mailbox)) {
  420.       sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox);
  421.       mm_log (tmp,ERROR);
  422.     }
  423.     else bezerk_badname (tmp,mailbox);
  424.     return NIL;
  425.   }
  426. /* open the destination */
  427.   if ((fd = open (mailboxfile (tmp,mailbox),
  428.   O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  429.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  430.     mm_log (tmp,ERROR);
  431.     return NIL;
  432.   }
  433.   mm_critical (stream); /* go critical */
  434.   fstat (fd,&sbuf); /* get current file size */
  435. /* for each requested message */
  436.   for (i = 1; i <= stream->nmsgs; i++)
  437.     if ((elt = mail_elt (stream,i))->sequence) {
  438.       lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
  439. /* number of bytes to copy */
  440.       j = elt->private.msg.full.offset + elt->rfc822_size;
  441.       do { /* read from source position */
  442. k = min (j,(unsigned long) MAILTMPLEN);
  443. read (LOCAL->fd,tmp,(unsigned int) k);
  444. if (write (fd,tmp,(unsigned int) k) < 0) {
  445.   sprintf (tmp,"Unable to write message: %s",strerror (errno));
  446.   mm_log (tmp,ERROR);
  447.   chsize (fd,sbuf.st_size);
  448.   close (fd); /* punt */
  449.   mm_nocritical (stream);
  450.   return NIL;
  451. }
  452.       } while (j -= k); /* until done */
  453.     }
  454.   close (fd); /* close the file */
  455.   mm_nocritical (stream); /* release critical */
  456. /* delete all requested messages */
  457.   if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
  458.     if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
  459.   return T;
  460. }
  461. /* Berkeley mail append message from stringstruct
  462.  * Accepts: MAIL stream
  463.  *     destination mailbox
  464.  *     stringstruct of messages to append
  465.  * Returns: T if append successful, else NIL
  466.  */
  467. long bezerk_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  468.    STRING *message)
  469. {
  470.   struct stat sbuf;
  471.   int i,fd,ti;
  472.   char c,*s,*x,tmp[MAILTMPLEN];
  473.   MESSAGECACHE elt;
  474.   long j,n,ok = T;
  475.   time_t t = time (0);
  476.   long sz = SIZE (message);
  477.   long size = 0;
  478.   unsigned long uf;
  479.   short f = (short) mail_parse_flags (stream,flags,&uf);
  480. /* parse date */
  481.   if (!date) rfc822_date (date = tmp);
  482.   if (!mail_parse_date (&elt,date)) {
  483.     sprintf (tmp,"Bad date in append: %.80ss",date);
  484.     mm_log (tmp,ERROR);
  485.     return NIL;
  486.   }
  487. /* make sure valid mailbox */
  488.   if (!bezerk_isvalid (mailbox,tmp) && errno) {
  489.     if (errno == ENOENT) {
  490.       if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
  491.   ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
  492.   ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
  493.   ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
  494.   ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
  495. bezerk_create (NIL,"INBOX");
  496.       else {
  497. mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  498. return NIL;
  499.       }
  500.     }
  501.     else if (mailboxfile (tmp,mailbox)) {
  502.       sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox);
  503.       mm_log (tmp,ERROR);
  504.     }
  505.     else bezerk_badname (tmp,mailbox);
  506.     return NIL;
  507.   }
  508. /* open the destination */
  509.   if ((fd = open (mailboxfile (tmp,mailbox),
  510.   O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  511.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  512.     mm_log (tmp,ERROR);
  513.     return NIL;
  514.   }
  515.   mm_critical (stream); /* go critical */
  516.   tzset (); /* initialize timezone stuff */
  517. /* calculate data size w/o CR's */
  518.   while (sz--) if ((c = SNX (message)) != '15') size++;
  519.   SETPOS (message,(long) 0); /* back to start */
  520.   fstat (fd,&sbuf); /* get current file size */
  521.   strcpy (tmp,"From someone "); /* start header */
  522. /* user wants to suppress time zones? */
  523.   if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
  524.     time_t when = mail_longdate (elt);
  525.     strcat (buf,ctime (&when));
  526.   }
  527. /* write the date given */
  528.   else mail_cdate (buf + strlen (buf),&elt);
  529.   sprintf (tmp + strlen (tmp),"Status: %sOnX-Status: %s%s%sn",
  530.    f&fSEEN ? "R" : "",f&fDELETED ? "D" : "",
  531.    f&fFLAGGED ? "F" : "",f&fANSWERED ? "A" : "");
  532.   i = strlen (tmp); /* initial buffer space used */
  533.   while (ok && size--) { /* copy text, tossing out CR's */
  534. /* if at start of line */
  535.     if ((c == 'n') && (size > 5)) {
  536.       n = GETPOS (message); /* prepend a broket if needed */
  537.       if ((SNX (message) == 'F') && (SNX (message) == 'r') &&
  538.   (SNX (message) == 'o') && (SNX (message) == 'm') &&
  539.   (SNX (message) == ' ')) {
  540. for (j = 6; (j < size) && (SNX (message) != 'n'); j++);
  541. if (j < size) { /* copy line */
  542.   SETPOS (message,n); /* restore position */
  543.   x = s = (char *) fs_get ((size_t) j + 1);
  544.   while (j--) if ((c = SNX (message)) != '15') *x++ = c;
  545.   *x = ''; /* tie off line */
  546.   if (ti = bezerk_valid_line (s,NIL,NIL))
  547.     ok = bezerk_append_putc (fd,tmp,&i,'>');
  548.   fs_give ((void **) &s);
  549. }
  550.       }
  551.       SETPOS (message,n); /* restore position as needed */
  552.     }
  553. /* copy another character */
  554.     if ((c = SNX (message)) != '15') ok = bezerk_append_putc (fd,tmp,&i,c);
  555.   }
  556. /* write trailing newline */
  557.   if (ok) ok = bezerk_append_putc (fd,tmp,&i,'n');
  558.   if (!(ok && (ok = (write (fd,tmp,i) >= 0)))) {
  559.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  560.     mm_log (tmp,ERROR);
  561.     chsize (fd,sbuf.st_size);
  562.   }  
  563.   close (fd); /* close the file */
  564.   mm_nocritical (stream); /* release critical */
  565.   return T; /* return success */
  566. }
  567. /* Berkeley mail append character
  568.  * Accepts: file descriptor
  569.  *     output buffer
  570.  *     pointer to current size of output buffer
  571.  *     character to append
  572.  * Returns: T if append successful, else NIL
  573.  */
  574. long bezerk_append_putc (int fd,char *s,int *i,char c)
  575. {
  576.   s[(*i)++] = c;
  577.   if (*i == MAILTMPLEN) { /* dump if buffer filled */
  578.     if (write (fd,s,*i) < 0) return NIL;
  579.     *i = 0; /* reset */
  580.   }
  581.   return T;
  582. }
  583. /* Return bad file name error message
  584.  * Accepts: temporary buffer
  585.  *     file name
  586.  * Returns: long NIL always
  587.  */
  588. long bezerk_badname (char *tmp,char *s)
  589. {
  590.   sprintf (tmp,"Invalid mailbox name: %s",s);
  591.   mm_log (tmp,ERROR);
  592.   return (long) NIL;
  593. }
  594. /* Parse mailbox
  595.  * Accepts: MAIL stream
  596.  * Returns: T if parse OK
  597.  *     NIL if failure, stream aborted
  598.  */
  599. long bezerk_parse (MAILSTREAM *stream)
  600. {
  601.   struct stat sbuf;
  602.   MESSAGECACHE *elt;
  603.   char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
  604.   long i;
  605.   int j,ti,zn;
  606.   long curpos = LOCAL->filesize;
  607.   long nmsgs = stream->nmsgs;
  608.   long recent = stream->recent;
  609.   short silent = stream->silent;
  610.   fstat (LOCAL->fd,&sbuf); /* get status */
  611.   if (sbuf.st_size < curpos) { /* sanity check */
  612.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  613.     mm_log (tmp,ERROR);
  614.     bezerk_close (stream,NIL);
  615.     return NIL;
  616.   }
  617.   stream->silent = T; /* don't pass up mm_exists() events yet */
  618.   db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
  619.   while (sbuf.st_size - curpos){/* while there is data to read */
  620. /* get to that position in the file */
  621.     lseek (LOCAL->fd,curpos,SEEK_SET);
  622. /* read first buffer's worth */
  623.     read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
  624.     tmp[j] = ''; /* tie off buffer */
  625.     if (!(ti = bezerk_valid_line (tmp,&t,&zn))) {
  626.       mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
  627.       bezerk_close (stream,NIL);
  628.       return NIL;
  629.     }
  630. /* swell the cache */
  631.     mail_exists (stream,++nmsgs);
  632. /* instantiate an elt for this message */
  633.     (elt = mail_elt (stream,nmsgs))->valid = T;
  634.     elt->private.uid = ++stream->uid_last;
  635. /* note file offset of header */
  636.     elt->private.special.offset = curpos;
  637. /* note offset of message */
  638.     elt->private.msg.full.offset =
  639.       (s = ((*t == '15') ? (t + 2) : (t + 1))) - tmp;
  640. /* generate plausable IMAPish date string */
  641.     db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
  642. /* dd */
  643.     db[0] = t[ti - 2]; db[1] = t[ti - 1];
  644. /* mmm */
  645.     db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
  646. /* hh */
  647.     db[12] = t[ti + 1]; db[13] = t[ti + 2];
  648. /* mm */
  649.     db[15] = t[ti + 4]; db[16] = t[ti + 5];
  650.     if (t[ti += 6] == ':') { /* ss if present */
  651.       db[18] = t[++ti]; db[19] = t[++ti];
  652.       ti++; /* move to space */
  653.     }
  654.     else db[18] = db[19] = '0'; /* assume 0 seconds */
  655. /* yy -- advance over timezone if necessary */
  656.     if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
  657.     db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
  658.     t = zn ? (t + zn) : "LCL"; /* zzz */
  659.     db[21] = *t++; db[22] = *t++; db[23] = *t++;
  660.     if ((db[21] != '+') && (db[21] != '-')) db[24] = '';
  661.     else { /* numeric time zone */
  662.       db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '';
  663.     }
  664. /* set internal date */
  665.     if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
  666.     curpos += s - tmp; /* advance position after header */
  667.     t = strchr (s,'12'); /* start of next line */
  668. /* find start of next message */
  669.     while (!(bezerk_valid_line (s,NIL,NIL))) {
  670.       if (t) { /* have next line? */
  671. t++; /* advance to new line */
  672. curpos += t - s; /* update position and size */
  673. elt->rfc822_size += ((t - s) + ((t[-2] == '15') ? 0 : 1));
  674. s = t; /* move to next line */
  675. t = strchr (s,'12');
  676.       }
  677.       else { /* try next buffer */
  678. j = strlen (s); /* length of unread data in buffer */
  679. if ((i = sbuf.st_size - curpos) && (i != j)) {
  680. /* get to that position in the file */
  681.   lseek (LOCAL->fd,curpos,SEEK_SET);
  682. /* read another buffer's worth */
  683.   read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
  684.   tmp[j] = ''; /* tie off buffer */
  685.   if (!(t = strchr (s,'12'))) fatal ("Line too long in mailbox");
  686. }
  687. else {
  688.   curpos += j; /* last bit of data */
  689.   elt->rfc822_size += j;
  690.   break;
  691. }
  692.       }
  693.     }
  694.   }
  695. /* update parsed file size */
  696.   LOCAL->filesize = sbuf.st_size;
  697.   stream->silent = silent; /* can pass up events now */
  698.   mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
  699.   mail_recent (stream,recent); /* and of change in recent messages */
  700.   return T; /* return the winnage */
  701. }
  702. /* Berkeley locate header for a message
  703.  * Accepts: MAIL stream
  704.  *     message number
  705.  *     pointer to returned header size
  706.  * Returns: position of header in file
  707.  */
  708. unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
  709.      unsigned long *size)
  710. {
  711.   long siz;
  712.   size_t i = 0;
  713.   char c = '';
  714.   char *s;
  715.   char tmp[MAILTMPLEN];
  716.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  717.   long pos = elt->private.special.offset + elt->private.msg.full.offset;
  718. /* is size known? */
  719.   if (!(*size = elt->private.msg.header.text.size)) {
  720. /* get to header position */
  721.     lseek (LOCAL->fd,pos,SEEK_SET);
  722. /* search message for CRLF CRLF */
  723.     for (siz = 1; siz <= elt->rfc822_size; siz++) {
  724.       if (!i && /* buffer empty? */
  725.   (read (LOCAL->fd,s = tmp,
  726.  i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
  727. return pos;
  728.       else i--;
  729. /* two newline sequence? */
  730.       if ((c == '12') && (*s == '12')) {
  731. /* yes, note for later */
  732. elt->private.msg.header.text.size = (*size = siz);
  733. return pos; /* return to caller */
  734.       }
  735.       else if ((c == '12') && (*s == '15')) {
  736. /* yes, note for later */
  737. elt->private.msg.header.text.size = (*size = siz + 1);
  738. return pos; /* return to caller */
  739.       }
  740.       else c = *s++; /* next character */
  741.     }
  742.   }
  743.   return pos; /* have position */
  744. }