* Clean up some function definitions to comply with strict
[alpine.git] / imap / src / osdep / os2 / unixnt.c
blob0bf45ca2446f0a553dae1c36f0882ed0bd03a31c
1 /* ========================================================================
2 * Copyright 1988-2008 University of Washington
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
8 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
15 * Program: UNIX mail routines
17 * Author: Mark Crispin
18 * UW Technology
19 * University of Washington
20 * Seattle, WA 98195
21 * Internet: MRC@CAC.Washington.EDU
23 * Date: 20 December 1989
24 * Last Edited: 27 March 2008
28 /* DEDICATION
30 * This file is dedicated to my dog, Unix, also known as Yun-chan and
31 * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
32 * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
33 * a two-month bout with cirrhosis of the liver.
35 * He was a dear friend, and I miss him terribly.
37 * Lift a leg, Yunie. Luv ya forever!!!!
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #ifndef errno
44 extern int errno; /* just in case */
45 #endif
46 #include "mail.h"
47 #include "osdep.h"
48 #include <time.h>
49 #include <fcntl.h>
50 #include <sys/stat.h>
51 #include <sys/utime.h>
52 #include "unixnt.h"
53 #include "pseudo.h"
54 #include "fdstring.h"
55 #include "misc.h"
56 #include "dummy.h"
58 /* UNIX I/O stream local data */
60 typedef struct unix_local {
61 unsigned int dirty : 1; /* disk copy needs updating */
62 unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
63 unsigned int pseudo : 1; /* uses a pseudo message */
64 unsigned int appending : 1; /* don't mark new messages as old */
65 int fd; /* mailbox file descriptor */
66 int ld; /* lock file descriptor */
67 char *lname; /* lock file name */
68 off_t filesize; /* file size parsed */
69 time_t filetime; /* last file time */
70 unsigned char *buf; /* temporary buffer */
71 unsigned long buflen; /* current size of temporary buffer */
72 unsigned long uid; /* current text uid */
73 SIZEDTEXT text; /* current text */
74 unsigned long textlen; /* current text length */
75 char *line; /* returned line */
76 char *linebuf; /* line readin buffer */
77 unsigned long linebuflen; /* current line readin buffer length */
78 } UNIXLOCAL;
81 /* Convenient access to local data */
83 #define LOCAL ((UNIXLOCAL *) stream->local)
86 /* UNIX protected file structure */
88 typedef struct unix_file {
89 MAILSTREAM *stream; /* current stream */
90 off_t curpos; /* current file position */
91 off_t protect; /* protected position */
92 off_t filepos; /* current last written file position */
93 char *buf; /* overflow buffer */
94 size_t buflen; /* current overflow buffer length */
95 char *bufpos; /* current buffer position */
96 } UNIXFILE;
98 /* Function prototypes */
100 DRIVER *unix_valid (char *name);
101 void *unix_parameters (long function,void *value);
102 void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
103 void unix_list (MAILSTREAM *stream,char *ref,char *pat);
104 void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
105 long unix_create (MAILSTREAM *stream,char *mailbox);
106 long unix_delete (MAILSTREAM *stream,char *mailbox);
107 long unix_rename (MAILSTREAM *stream,char *old,char *newname);
108 MAILSTREAM *unix_open (MAILSTREAM *stream);
109 void unix_close (MAILSTREAM *stream,long options);
110 char *unix_header (MAILSTREAM *stream,unsigned long msgno,
111 unsigned long *length,long flags);
112 long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
113 char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
114 unsigned long *length,long flags);
115 void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
116 long unix_ping (MAILSTREAM *stream);
117 void unix_check (MAILSTREAM *stream);
118 long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
119 long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
120 long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
121 int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
122 STRING *msg);
123 int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
125 void unix_abort (MAILSTREAM *stream);
126 char *unix_file (char *dst,char *name);
127 int unix_lock (char *file,int flags,int mode,char *lock,int op);
128 void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
129 int unix_parse (MAILSTREAM *stream,char *lock,int op);
130 char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
131 unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
132 unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
133 unsigned long uid,long flag);
134 long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
135 long flags);
136 long unix_extend (MAILSTREAM *stream,unsigned long size);
137 void unix_write (UNIXFILE *f,char *s,unsigned long i);
138 void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
140 /* UNIX mail routines */
143 /* Driver dispatch used by MAIL */
145 DRIVER unixdriver = {
146 "unix", /* driver name */
147 /* driver flags */
148 DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
149 (DRIVER *) NIL, /* next driver */
150 unix_valid, /* mailbox is valid for us */
151 unix_parameters, /* manipulate parameters */
152 unix_scan, /* scan mailboxes */
153 unix_list, /* list mailboxes */
154 unix_lsub, /* list subscribed mailboxes */
155 NIL, /* subscribe to mailbox */
156 NIL, /* unsubscribe from mailbox */
157 unix_create, /* create mailbox */
158 unix_delete, /* delete mailbox */
159 unix_rename, /* rename mailbox */
160 mail_status_default, /* status of mailbox */
161 unix_open, /* open mailbox */
162 unix_close, /* close mailbox */
163 NIL, /* fetch message "fast" attributes */
164 NIL, /* fetch message flags */
165 NIL, /* fetch overview */
166 NIL, /* fetch message envelopes */
167 unix_header, /* fetch message header */
168 unix_text, /* fetch message text */
169 NIL, /* fetch partial message text */
170 NIL, /* unique identifier */
171 NIL, /* message number */
172 NIL, /* modify flags */
173 unix_flagmsg, /* per-message modify flags */
174 NIL, /* search for message based on criteria */
175 NIL, /* sort messages */
176 NIL, /* thread messages */
177 unix_ping, /* ping mailbox to see if still alive */
178 unix_check, /* check for new messages */
179 unix_expunge, /* expunge deleted messages */
180 unix_copy, /* copy messages to another mailbox */
181 unix_append, /* append string message to mailbox */
182 NIL /* garbage collect stream */
185 /* prototype stream */
186 MAILSTREAM unixproto = {&unixdriver};
188 /* driver parameters */
189 static long unix_fromwidget = T;
191 /* UNIX mail validate mailbox
192 * Accepts: mailbox name
193 * Returns: our driver if name is valid, NIL otherwise
196 DRIVER *unix_valid (char *name)
198 int fd;
199 DRIVER *ret = NIL;
200 int c,r;
201 char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
202 struct stat sbuf;
203 struct utimbuf times;
204 errno = EINVAL; /* assume invalid argument */
205 /* must be non-empty file */
206 if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
207 ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
208 if (!sbuf.st_size)errno = 0;/* empty file */
209 else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
210 memset (tmp,'\0',MAILTMPLEN);
211 if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
212 else { /* ignore leading whitespace */
213 for (s = tmp,c = '\n';
214 (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
215 c = *s++);
216 if (c == '\n') { /* at start of a line? */
217 VALID (s,t,r,c); /* yes, validate format */
218 if (r) ret = &unixdriver;
219 else errno = -1; /* invalid format */
222 close (fd); /* close the file */
223 /* \Marked status? */
224 if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
225 /* yes, preserve atime and mtime */
226 times.actime = sbuf.st_atime;
227 times.modtime = sbuf.st_mtime;
228 utime (file,&times); /* set the times */
232 return ret; /* return what we should */
234 /* UNIX manipulate driver parameters
235 * Accepts: function code
236 * function-dependent value
237 * Returns: function-dependent return value
240 void *unix_parameters (long function,void *value)
242 void *ret = NIL;
243 switch ((int) function) {
244 case SET_FROMWIDGET:
245 unix_fromwidget = (long) value;
246 case GET_FROMWIDGET:
247 ret = (void *) unix_fromwidget;
248 break;
250 return ret;
253 /* UNIX mail scan mailboxes
254 * Accepts: mail stream
255 * reference
256 * pattern to search
257 * string to scan
260 void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
262 if (stream) dummy_scan (NIL,ref,pat,contents);
266 /* UNIX mail list mailboxes
267 * Accepts: mail stream
268 * reference
269 * pattern to search
272 void unix_list (MAILSTREAM *stream,char *ref,char *pat)
274 if (stream) dummy_list (NIL,ref,pat);
278 /* UNIX mail list subscribed mailboxes
279 * Accepts: mail stream
280 * reference
281 * pattern to search
284 void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
286 if (stream) dummy_lsub (NIL,ref,pat);
289 /* UNIX mail create mailbox
290 * Accepts: MAIL stream
291 * mailbox name to create
292 * Returns: T on success, NIL on failure
295 long unix_create (MAILSTREAM *stream,char *mailbox)
297 char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
298 long ret = NIL;
299 int fd;
300 time_t ti = time (0);
301 if (!(s = dummy_file (mbx,mailbox))) {
302 sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
303 mm_log (tmp,ERROR);
305 /* create underlying file */
306 else if (dummy_create_path (stream,s,NIL)) {
307 if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
308 if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
309 sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
310 mm_log (tmp,ERROR);
311 unlink (mbx); /* delete the file */
313 else { /* initialize header */
314 memset (tmp,'\0',MAILTMPLEN);
315 sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
316 if (s = strpbrk (tmp,"\r\n")) *s = '\0';
317 strcat (tmp,"\r\nDate: ");
318 rfc822_fixed_date (s = tmp + strlen (tmp));
319 sprintf (s += strlen (s), /* write the pseudo-header */
320 "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
321 pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
322 (unsigned long) ti,pseudo_msg);
323 if (write (fd,tmp,strlen (tmp)) > 0) {
324 close (fd); /* close file */
325 ret = T;
327 else {
328 sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
329 strerror (errno));
330 mm_log (tmp,ERROR);
331 close (fd); /* close file before unlinking */
332 unlink (mbx); /* delete the file */
336 return ret;
339 /* UNIX mail delete mailbox
340 * Accepts: MAIL stream
341 * mailbox name to delete
342 * Returns: T on success, NIL on failure
345 long unix_delete (MAILSTREAM *stream,char *mailbox)
347 return unix_rename (stream,mailbox,NIL);
351 /* UNIX mail rename mailbox
352 * Accepts: MAIL stream
353 * old mailbox name
354 * new mailbox name (or NIL for delete)
355 * Returns: T on success, NIL on failure
358 long unix_rename (MAILSTREAM *stream,char *old,char *newname)
360 long ret = NIL;
361 char c,*s = NIL;
362 char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
363 int fd,ld;
364 struct stat sbuf;
365 mm_critical (stream); /* get the c-client lock */
366 if (!dummy_file (file,old) ||
367 (newname && (!(s = dummy_file (tmp,newname)) ||
368 ((s = strrchr (s,'\\')) && !s[1]))))
369 sprintf (tmp,newname ?
370 "Can't rename mailbox %.80s to %.80s: invalid name" :
371 "Can't delete mailbox %.80s: invalid name",
372 old,newname);
373 else if ((ld = lockname (lock,file,NIL)) < 0)
374 sprintf (tmp,"Can't get lock for mailbox %.80s",old);
376 else { /* lock out other c-clients */
377 if (flock (ld,LOCK_EX|LOCK_NB)) {
378 close (ld); /* couldn't lock, give up on it then */
379 sprintf (tmp,"Mailbox %.80s is in use by another process",old);
381 /* lock out non c-client applications */
382 else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
383 LOCK_EX)) < 0)
384 sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
385 else {
386 unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
387 if (newname) { /* want rename? */
388 /* found superior to destination name? */
389 if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
390 ((tmp[1] != ':') || (s != tmp + 2))) {
391 c = s[1]; /* remember character after delimiter */
392 *s = s[1] = '\0'; /* tie off name at delimiter */
393 /* name doesn't exist, create it */
394 if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
395 *s = '\\'; /* restore delimiter */
396 if (!dummy_create (stream,newname)) {
397 flock (ld,LOCK_UN);
398 close (ld); /* close c-client lock */
399 unlink (lock); /* and delete it */
400 mm_nocritical (stream);
401 return NIL; /* couldn't create superior */
404 else *s = '\\'; /* restore delimiter */
405 s[1] = c; /* restore character after delimiter */
407 if (rename (file,tmp))
408 sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
409 strerror (errno));
410 else ret = T; /* set success */
412 else if (unlink (file)) /* want delete */
413 sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
414 else ret = T; /* set success */
415 flock (ld,LOCK_UN); /* release c-client lock */
416 close (ld); /* close c-client lock */
417 unlink (lock); /* and delete it */
420 mm_nocritical (stream); /* no longer critical */
421 if (!ret) mm_log (tmp,ERROR); /* log error */
422 return ret; /* return success or failure */
425 /* UNIX mail open
426 * Accepts: Stream to open
427 * Returns: Stream on success, NIL on failure
430 MAILSTREAM *unix_open (MAILSTREAM *stream)
432 int fd;
433 char tmp[MAILTMPLEN];
434 /* return prototype for OP_PROTOTYPE call */
435 if (!stream) return &unixproto;
436 if (stream->local) fatal ("unix recycle stream");
437 stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
438 /* note if an INBOX or not */
439 stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
440 /* canonicalize the stream mailbox name */
441 if (!dummy_file (tmp,stream->mailbox)) {
442 sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
443 mm_log (tmp,ERROR);
444 return NIL;
446 /* flush old name */
447 fs_give ((void **) &stream->mailbox);
448 /* save canonical name */
449 stream->mailbox = cpystr (tmp);
450 LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
451 LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
452 LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
453 LOCAL->text.size = CHUNKSIZE - 1;
454 LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
455 LOCAL->linebuflen = CHUNKSIZE - 1;
456 stream->sequence++; /* bump sequence number */
457 if (!stream->rdonly) { /* make lock for read/write access */
458 if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
459 mm_log ("Can't open mailbox lock, access is readonly",WARN);
460 /* can get the lock? */
461 else if (flock (fd,LOCK_EX|LOCK_NB)) {
462 if (!stream->silent)
463 mm_log ("Mailbox is open by another process, access is readonly",WARN);
464 close (fd);
466 else { /* got the lock, nobody else can alter state */
467 LOCAL->ld = fd; /* note lock's fd and name */
468 LOCAL->lname = cpystr (tmp);
472 /* parse mailbox */
473 stream->nmsgs = stream->recent = 0;
474 /* will we be able to get write access? */
475 if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
476 mm_log ("Can't get write access to mailbox, access is readonly",WARN);
477 flock (LOCAL->ld,LOCK_UN); /* release the lock */
478 close (LOCAL->ld); /* close the lock file */
479 LOCAL->ld = -1; /* no more lock fd */
480 unlink (LOCAL->lname); /* delete it */
482 /* reset UID validity */
483 stream->uid_validity = stream->uid_last = 0;
484 if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
485 unix_abort (stream); /* abort if can't get RW silent stream */
486 /* parse mailbox */
487 else if (unix_parse (stream,tmp,LOCK_SH)) {
488 unix_unlock (LOCAL->fd,stream,tmp);
489 mail_unlock (stream);
490 mm_nocritical (stream); /* done with critical */
492 if (!LOCAL) return NIL; /* failure if stream died */
493 /* make sure upper level knows readonly */
494 stream->rdonly = (LOCAL->ld < 0);
495 /* notify about empty mailbox */
496 if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
497 if (!stream->rdonly) { /* flags stick if readwrite */
498 stream->perm_seen = stream->perm_deleted =
499 stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
500 /* have permanent keywords */
501 stream->perm_user_flags = 0xffffffff;
502 /* and maybe can create them too */
503 stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
505 return stream; /* return stream alive to caller */
509 /* UNIX mail close
510 * Accepts: MAIL stream
511 * close options
514 void unix_close (MAILSTREAM *stream,long options)
516 int silent = stream->silent;
517 stream->silent = T; /* go silent */
518 /* expunge if requested */
519 if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
520 /* else dump final checkpoint */
521 else if (LOCAL->dirty) unix_check (stream);
522 stream->silent = silent; /* restore old silence state */
523 unix_abort (stream); /* now punt the file and local data */
526 /* UNIX mail fetch message header
527 * Accepts: MAIL stream
528 * message # to fetch
529 * pointer to returned header text length
530 * option flags
531 * Returns: message header in RFC822 format
534 /* lines to filter from header */
535 static STRINGLIST *unix_hlines = NIL;
537 char *unix_header (MAILSTREAM *stream,unsigned long msgno,
538 unsigned long *length,long flags)
540 MESSAGECACHE *elt;
541 unsigned char *s;
542 *length = 0; /* default to empty */
543 if (flags & FT_UID) return "";/* UID call "impossible" */
544 elt = mail_elt (stream,msgno);/* get cache */
545 if (!unix_hlines) { /* once only code */
546 STRINGLIST *lines = unix_hlines = mail_newstringlist ();
547 lines->text.size = strlen ((char *) (lines->text.data =
548 (unsigned char *) "Status"));
549 lines = lines->next = mail_newstringlist ();
550 lines->text.size = strlen ((char *) (lines->text.data =
551 (unsigned char *) "X-Status"));
552 lines = lines->next = mail_newstringlist ();
553 lines->text.size = strlen ((char *) (lines->text.data =
554 (unsigned char *) "X-Keywords"));
555 lines = lines->next = mail_newstringlist ();
556 lines->text.size = strlen ((char *) (lines->text.data =
557 (unsigned char *) "X-UID"));
558 lines = lines->next = mail_newstringlist ();
559 lines->text.size = strlen ((char *) (lines->text.data =
560 (unsigned char *) "X-IMAP"));
561 lines = lines->next = mail_newstringlist ();
562 lines->text.size = strlen ((char *) (lines->text.data =
563 (unsigned char *) "X-IMAPbase"));
565 /* go to header position */
566 lseek (LOCAL->fd,elt->private.special.offset +
567 elt->private.msg.header.offset,L_SET);
569 if (flags & FT_INTERNAL) { /* initial data OK? */
570 if (elt->private.msg.header.text.size > LOCAL->buflen) {
571 fs_give ((void **) &LOCAL->buf);
572 LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
573 elt->private.msg.header.text.size) + 1);
575 /* read message */
576 read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
577 /* got text, tie off string */
578 LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
580 else { /* need to make a CRLF version */
581 read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
582 elt->private.msg.header.text.size);
583 /* tie off string, and convert to CRLF */
584 s[elt->private.msg.header.text.size] = '\0';
585 *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
586 elt->private.msg.header.text.size);
587 fs_give ((void **) &s); /* free readin buffer */
589 *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
590 return LOCAL->buf; /* return processed copy */
593 /* UNIX mail fetch message text
594 * Accepts: MAIL stream
595 * message # to fetch
596 * pointer to returned stringstruct
597 * option flags
598 * Returns: T on success, NIL if failure
601 long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
603 char *s;
604 unsigned long i;
605 MESSAGECACHE *elt;
606 /* UID call "impossible" */
607 if (flags & FT_UID) return NIL;
608 elt = mail_elt (stream,msgno);/* get cache element */
609 /* if message not seen */
610 if (!(flags & FT_PEEK) && !elt->seen) {
611 /* mark message seen and dirty */
612 elt->seen = elt->private.dirty = LOCAL->dirty = T;
613 mm_flags (stream,msgno);
615 s = unix_text_work (stream,elt,&i,flags);
616 INIT (bs,mail_string,s,i); /* set up stringstruct */
617 return T; /* success */
620 /* UNIX mail fetch message text worker routine
621 * Accepts: MAIL stream
622 * message cache element
623 * pointer to returned header text length
624 * option flags
627 char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
628 unsigned long *length,long flags)
630 FDDATA d;
631 STRING bs;
632 unsigned char c,*s,tmp[CHUNKSIZE];
633 /* go to text position */
634 lseek (LOCAL->fd,elt->private.special.offset +
635 elt->private.msg.text.offset,L_SET);
636 if (flags & FT_INTERNAL) { /* initial data OK? */
637 if (elt->private.msg.text.text.size > LOCAL->buflen) {
638 fs_give ((void **) &LOCAL->buf);
639 LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
640 elt->private.msg.text.text.size) + 1);
642 /* read message */
643 read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
644 /* got text, tie off string */
645 LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
646 return LOCAL->buf;
648 /* have it cached already? */
649 if (elt->private.uid != LOCAL->uid) {
650 /* not cached, cache it now */
651 LOCAL->uid = elt->private.uid;
652 /* is buffer big enough? */
653 if (elt->rfc822_size > LOCAL->text.size) {
654 /* excessively conservative, but the right thing is too hard to do */
655 fs_give ((void **) &LOCAL->text.data);
656 LOCAL->text.data = (unsigned char *)
657 fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
659 d.fd = LOCAL->fd; /* yes, set up file descriptor */
660 d.pos = elt->private.special.offset + elt->private.msg.text.offset;
661 d.chunk = tmp; /* initial buffer chunk */
662 d.chunksize = CHUNKSIZE; /* file chunk size */
663 INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
664 for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
665 case '\r': /* carriage return seen */
666 *s++ = c; /* copy it and any succeeding LF */
667 if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
668 break;
669 case '\n':
670 *s++ = '\r'; /* insert a CR */
671 default:
672 *s++ = c; /* copy characters */
674 *s = '\0'; /* tie off buffer */
675 /* calculate length of cached data */
676 LOCAL->textlen = s - LOCAL->text.data;
678 *length = LOCAL->textlen; /* return from cache */
679 return (char *) LOCAL->text.data;
682 /* UNIX per-message modify flag
683 * Accepts: MAIL stream
684 * message cache element
687 void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
689 /* only after finishing */
690 if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
694 /* UNIX mail ping mailbox
695 * Accepts: MAIL stream
696 * Returns: T if stream alive, else NIL
699 long unix_ping (MAILSTREAM *stream)
701 char lock[MAILTMPLEN];
702 struct stat sbuf;
703 /* big no-op if not readwrite */
704 if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
705 if (stream->rdonly) { /* does he want to give up readwrite? */
706 /* checkpoint if we changed something */
707 if (LOCAL->dirty) unix_check (stream);
708 flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
709 close (LOCAL->ld); /* close the readwrite lock file */
710 LOCAL->ld = -1; /* no more readwrite lock fd */
711 unlink (LOCAL->lname); /* delete the readwrite lock file */
713 else { /* get current mailbox size */
714 if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
715 else if (stat (stream->mailbox,&sbuf)) {
716 sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
717 strerror (errno));
718 MM_LOG (LOCAL->buf,ERROR);
719 unix_abort (stream);
720 return NIL;
722 /* parse if mailbox changed */
723 if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
724 unix_parse (stream,lock,LOCK_EX)) {
725 /* force checkpoint if double-dirty */
726 if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
727 /* unlock mailbox */
728 else unix_unlock (LOCAL->fd,stream,lock);
729 mail_unlock (stream); /* and stream */
730 mm_nocritical (stream); /* done with critical */
734 return LOCAL ? LONGT : NIL; /* return if still alive */
737 /* UNIX mail check mailbox
738 * Accepts: MAIL stream
741 void unix_check (MAILSTREAM *stream)
743 char lock[MAILTMPLEN];
744 /* parse and lock mailbox */
745 if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
746 unix_parse (stream,lock,LOCK_EX)) {
747 /* any unsaved changes? */
748 if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
749 if (!stream->silent) mm_log ("Checkpoint completed",NIL);
751 /* no checkpoint needed, just unlock */
752 else unix_unlock (LOCAL->fd,stream,lock);
753 mail_unlock (stream); /* unlock the stream */
754 mm_nocritical (stream); /* done with critical */
759 /* UNIX mail expunge mailbox
760 * Accepts: MAIL stream
761 * sequence to expunge if non-NIL
762 * expunge options
763 * Returns: T, always
766 long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
768 long ret;
769 unsigned long i;
770 char lock[MAILTMPLEN];
771 char *msg = NIL;
772 /* parse and lock mailbox */
773 if (ret = (sequence ? ((options & EX_UID) ?
774 mail_uid_sequence (stream,sequence) :
775 mail_sequence (stream,sequence)) : LONGT) &&
776 LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
777 unix_parse (stream,lock,LOCK_EX)) {
778 /* check expunged messages if not dirty */
779 for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
780 MESSAGECACHE *elt = mail_elt (stream,i);
781 if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
783 if (!LOCAL->dirty) { /* not dirty and no expunged messages */
784 unix_unlock (LOCAL->fd,stream,lock);
785 msg = "No messages deleted, so no update needed";
787 else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
788 if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
789 else msg = "Mailbox checkpointed, but no messages expunged";
791 /* rewrite failed */
792 else unix_unlock (LOCAL->fd,stream,lock);
793 mail_unlock (stream); /* unlock the stream */
794 mm_nocritical (stream); /* done with critical */
795 if (msg && !stream->silent) mm_log (msg,NIL);
797 else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
798 return ret;
801 /* UNIX mail copy message(s)
802 * Accepts: MAIL stream
803 * sequence
804 * destination mailbox
805 * copy options
806 * Returns: T if copy successful, else NIL
809 long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
811 struct stat sbuf;
812 int fd;
813 char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
814 struct utimbuf times;
815 unsigned long i,j;
816 MESSAGECACHE *elt;
817 long ret = T;
818 mailproxycopy_t pc =
819 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
820 copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
821 NIL : mail_parameters (NIL,GET_COPYUID,NIL));
822 SEARCHSET *source = cu ? mail_newsearchset () : NIL;
823 SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
824 MAILSTREAM *tstream = NIL;
825 if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
826 mail_sequence (stream,sequence))) return NIL;
827 /* make sure destination is valid */
828 if (!(unix_valid (mailbox) || !errno))
829 switch (errno) {
830 case ENOENT: /* no such file? */
831 if (compare_cstring (mailbox,"INBOX")) {
832 mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
833 return NIL;
835 if (pc) return (*pc) (stream,sequence,mailbox,options);
836 unix_create (NIL,"INBOX");/* create empty INBOX */
837 case EACCES: /* file protected */
838 sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
839 MM_LOG (LOCAL->buf,ERROR);
840 return NIL;
841 case EINVAL:
842 if (pc) return (*pc) (stream,sequence,mailbox,options);
843 sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
844 mm_log (LOCAL->buf,ERROR);
845 return NIL;
846 default:
847 if (pc) return (*pc) (stream,sequence,mailbox,options);
848 sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
849 mm_log (LOCAL->buf,ERROR);
850 return NIL;
853 /* try to open rewrite for UIDPLUS */
854 if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
855 OP_SILENT|OP_NOKOD)) && tstream->rdonly)
856 tstream = mail_close (tstream);
857 if (cu && !tstream) { /* wanted a COPYUID? */
858 sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
859 mailbox);
860 MM_LOG (LOCAL->buf,WARN);
861 cu = NIL; /* don't try to do COPYUID */
863 LOCAL->buf[0] = '\0';
864 mm_critical (stream); /* go critical */
865 if ((fd = unix_lock (dummy_file (file,mailbox),
866 O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
867 lock,LOCK_EX)) < 0) {
868 mm_nocritical (stream); /* done with critical */
869 sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
870 mm_log (LOCAL->buf,ERROR); /* log the error */
871 return NIL; /* failed */
873 fstat (fd,&sbuf); /* get current file size */
874 /* write all requested messages to mailbox */
875 for (i = 1; ret && (i <= stream->nmsgs); i++)
876 if ((elt = mail_elt (stream,i))->sequence) {
877 lseek (LOCAL->fd,elt->private.special.offset,L_SET);
878 read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
879 if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
880 LOCAL->buf[j - 1] = '\r';
881 LOCAL->buf[j++] = '\n';
883 if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
884 else { /* internal header succeeded */
885 s = unix_header (stream,i,&j,NIL);
886 /* header size, sans trailing newline */
887 if (j && (s[j - 4] == '\r')) j -= 2;
888 if (write (fd,s,j) < 0) ret = NIL;
889 else { /* message header succeeded */
890 j = tstream ? /* write UIDPLUS data if have readwrite */
891 unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
892 unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
893 if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
894 else { /* message status succeeded */
895 s = unix_text_work (stream,elt,&j,NIL);
896 if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
897 ret = NIL;
898 else if (cu) { /* need to pass back new UID? */
899 mail_append_set (source,mail_uid (stream,i));
900 mail_append_set (dest,tstream->uid_last);
907 if (!ret || fsync (fd)) { /* force out the update */
908 sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
909 ftruncate (fd,sbuf.st_size);
910 ret = NIL;
912 /* force UIDVALIDITY assignment now */
913 if (tstream && !tstream->uid_validity)
914 tstream->uid_validity = (unsigned long) time (0);
915 /* return sets if doing COPYUID */
916 if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
917 else { /* flush any sets we may have built */
918 mail_free_searchset (&source);
919 mail_free_searchset (&dest);
921 times.modtime = time (0); /* set mtime to now */
922 /* set atime to now-1 if successful copy */
923 if (ret) times.actime = times.modtime - 1;
925 else times.actime = /* else preserve \Marked status */
926 ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
927 sbuf.st_atime : times.modtime;
928 utime (file,&times); /* set the times */
929 unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
930 if (tstream) { /* update last UID if we can */
931 UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
932 local->dirty = T; /* do a rewrite */
933 local->appending = T; /* but not at the cost of marking as old */
934 tstream = mail_close (tstream);
936 /* log the error */
937 if (!ret) mm_log (LOCAL->buf,ERROR);
938 /* delete if requested message */
939 else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
940 if ((elt = mail_elt (stream,i))->sequence)
941 elt->deleted = elt->private.dirty = LOCAL->dirty = T;
942 mm_nocritical (stream); /* release critical */
943 return ret;
946 /* UNIX mail append message from stringstruct
947 * Accepts: MAIL stream
948 * destination mailbox
949 * append callback
950 * data for callback
951 * Returns: T if append successful, else NIL
954 #define BUFLEN 8*MAILTMPLEN
956 long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
958 struct stat sbuf;
959 int fd;
960 unsigned long i;
961 char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
962 lock[MAILTMPLEN];
963 struct utimbuf times;
964 FILE *sf,*df;
965 MESSAGECACHE elt;
966 STRING *message;
967 unsigned long uidlocation = 0;
968 appenduid_t au = (appenduid_t)
969 (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
970 mail_parameters (NIL,GET_APPENDUID,NIL));
971 SEARCHSET *dst = au ? mail_newsearchset () : NIL;
972 long ret = LONGT;
973 MAILSTREAM *tstream = NIL;
974 if (!stream) { /* stream specified? */
975 stream = &unixproto; /* no, default stream to prototype */
976 for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
977 fs_give ((void **) &stream->user_flags[i]);
979 if (!unix_valid (mailbox)) switch (errno) {
980 case ENOENT: /* no such file? */
981 if (!compare_cstring (mailbox,"INBOX")) {
982 mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
983 return NIL;
985 unix_create (NIL,"INBOX"); /* create empty INBOX */
986 case 0: /* merely empty file? */
987 tstream = stream;
988 break;
989 case EACCES: /* file protected */
990 sprintf (tmp,"Can't access destination: %.80s",mailbox);
991 MM_LOG (tmp,ERROR);
992 return NIL;
993 case EINVAL:
994 sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
995 mm_log (tmp,ERROR);
996 return NIL;
997 default:
998 sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
999 mm_log (tmp,ERROR);
1000 return NIL;
1002 /* get sniffing stream for keywords */
1003 else if (!(tstream = mail_open (NIL,mailbox,
1004 OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
1005 sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
1006 MM_LOG (tmp,ERROR);
1007 return NIL;
1010 /* get first message */
1011 if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
1012 if (!(sf = tmpfile ())) { /* must have scratch file */
1013 sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
1014 if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
1015 sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
1016 mm_log (tmp,ERROR);
1017 return NIL;
1019 unlink (tmp);
1021 do { /* parse date */
1022 if (!date) rfc822_date (date = tmp);
1023 if (!mail_parse_date (&elt,date)) {
1024 sprintf (tmp,"Bad date in append: %.80s",date);
1025 mm_log (tmp,ERROR);
1027 else { /* user wants to suppress time zones? */
1028 if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
1029 time_t when = mail_longdate (&elt);
1030 date = ctime (&when); /* use traditional date */
1032 /* use POSIX-style date */
1033 else date = mail_cdate (tmp,&elt);
1034 if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
1035 else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
1036 sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
1037 mm_log (tmp,ERROR);
1039 /* get next message */
1040 else if ((*af) (tstream,data,&flags,&date,&message)) continue;
1042 fclose (sf); /* punt scratch file */
1043 return NIL; /* give up */
1044 } while (message); /* until no more messages */
1045 if (fflush (sf)) {
1046 sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
1047 mm_log (tmp,ERROR);
1048 fclose (sf); /* punt scratch file */
1049 return NIL; /* give up */
1051 i = ftell (sf); /* size of scratch file */
1053 /* close sniffing stream */
1054 if (tstream != stream) tstream = mail_close (tstream);
1055 mm_critical (stream); /* go critical */
1056 /* try to open readwrite for UIDPLUS */
1057 if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
1058 OP_SILENT|OP_NOKOD)) && tstream->rdonly)
1059 tstream = mail_close (tstream);
1060 if (au && !tstream) { /* wanted an APPENDUID? */
1061 sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
1062 MM_LOG (tmp,WARN);
1063 au = NIL;
1065 if (((fd = unix_lock (dummy_file (file,mailbox),
1066 O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
1067 lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
1068 mm_nocritical (stream); /* done with critical */
1069 sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
1070 mm_log (tmp,ERROR);
1071 return NIL;
1073 fstat (fd,&sbuf); /* get current file size */
1074 rewind (sf);
1075 times.modtime = time (0); /* set mtime to now */
1076 /* write all messages */
1077 if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
1078 (fflush (df) == EOF) || fsync (fd)) {
1079 sprintf (buf,"Message append failed: %s",strerror (errno));
1080 mm_log (buf,ERROR);
1081 ftruncate (fd,sbuf.st_size);/* revert file */
1082 times.actime = /* preserve \Marked status */
1083 ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
1084 sbuf.st_atime : times.modtime;
1085 ret = NIL; /* return error */
1087 /* set atime to now-1 if successful copy */
1088 else times.actime = times.modtime - 1;
1089 utime (file,&times); /* set the times */
1090 fclose (sf); /* done with scratch file */
1091 /* force UIDVALIDITY assignment now */
1092 if (tstream && !tstream->uid_validity)
1093 tstream->uid_validity = (unsigned long) time (0);
1094 /* return sets if doing APPENDUID */
1095 if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
1096 else mail_free_searchset (&dst);
1097 flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
1098 if (lock && *lock) unlink (lock);
1099 fclose (df); /* close mailbox */
1100 if (tstream) { /* update last UID if we can */
1101 UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
1102 local->dirty = T; /* do a rewrite */
1103 local->appending = T; /* but not at the cost of marking as old */
1104 tstream = mail_close (tstream);
1106 mm_nocritical (stream); /* release critical */
1107 return ret;
1110 /* Collect and write single message to append scratch file
1111 * Accepts: MAIL stream
1112 * scratch file
1113 * flags
1114 * date
1115 * message stringstruct
1116 * Returns: NIL if write error, else T
1119 int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
1120 STRING *msg)
1122 unsigned char *s,*t;
1123 unsigned long uf;
1124 long f = mail_parse_flags (stream,flags,&uf);
1125 /* write metadata */
1126 if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
1127 for (s = date; *s; *s++) switch (*s) {
1128 default:
1129 if (putc (*s,sf) == EOF) return NIL;
1130 case '\r': case '\n':
1131 break;
1133 if (fputs ("\r\n",sf) == EOF) return NIL;
1134 while (uf) /* write user flags */
1135 if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
1136 (fprintf (sf," %s",s) < 0)) return NIL;
1137 if (fputs ("\r\n",sf) == EOF) return NIL;
1138 while (SIZE (msg)) { /* copy text to scratch file */
1139 for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
1140 if (!*s) *s = 0x80; /* disallow NUL */
1141 /* write buffered text */
1142 if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
1143 SETPOS (msg,GETPOS (msg) + msg->cursize);
1144 else return NIL; /* failed */
1146 /* write trailing CRLF and return */
1147 return (fputs ("\r\n",sf) == EOF) ? NIL : T;
1150 /* Append messages from scratch file to mailbox
1151 * Accepts: MAIL stream
1152 * source file
1153 * destination file
1154 * uidset to update if non-NIL
1155 * Returns: T if success, NIL if failure
1158 int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
1160 int ti,zn,c;
1161 long f;
1162 unsigned long i,j;
1163 char *x,tmp[MAILTMPLEN];
1164 int hdrp = T;
1165 /* get message metadata line */
1166 while (fgets (tmp,MAILTMPLEN,sf)) {
1167 if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
1168 f = strtol (tmp,&x,10); /* get flags */
1169 if (!((*x++ == ' ') && isdigit (*x))) return NIL;
1170 i = strtoul (x,&x,10); /* get message size */
1171 if ((*x++ != ' ') || /* build initial header */
1172 (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
1173 (f&fSEEN && (putc ('R',df) == EOF)) ||
1174 (fputs ("\r\nX-Status: ",df) == EOF) ||
1175 (f&fDELETED && (putc ('D',df) == EOF)) ||
1176 (f&fFLAGGED && (putc ('F',df) == EOF)) ||
1177 (f&fANSWERED && (putc ('A',df) == EOF)) ||
1178 (f&fDRAFT && (putc ('T',df) == EOF)) ||
1179 (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
1180 /* copy keywords */
1181 while ((c = getc (sf)) != '\n') switch (c) {
1182 case EOF:
1183 return NIL;
1184 default:
1185 if (putc (c,df) == EOF) return NIL;
1187 if ((putc ('\n',df) == EOF) ||
1188 (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
1189 return NIL;
1191 for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
1192 /* get read line length */
1193 if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
1194 i -= j; /* number of bytes left */
1195 if (!j) continue; /* do nothing if line emptied */
1196 /* complete line? */
1197 if ((c == '\n')) switch (tmp[0]) {
1198 case 'F': /* possible "From " (case counts here) */
1199 if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
1200 (tmp[3] == 'm') && (tmp[4] == ' ')) {
1201 if (!unix_fromwidget) {
1202 VALID (tmp,x,ti,zn);/* conditional, only write widget if */
1203 if (!ti) break; /* it looks like a valid header */
1204 } /* write the widget */
1205 if (putc ('>',df) == EOF) return NIL;
1207 break;
1208 case 'S': case 's': /* possible "Status:" */
1209 if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
1210 ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
1211 ((tmp[3] == 't') || (tmp[3] == 'T')) &&
1212 ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
1213 ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
1214 (fputs ("X-Original-",df) == EOF)) return NIL;
1215 break;
1216 case 'X': case 'x': /* possible X-??? header */
1217 if (hdrp && (tmp[1] == '-') &&
1218 /* possible X-UID: */
1219 (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
1220 ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
1221 ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
1222 /* possible X-IMAP: */
1223 ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
1224 ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
1225 ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
1226 ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
1227 ((tmp[6] == ':') ||
1228 /* or X-IMAPbase: */
1229 ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
1230 ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
1231 ((tmp[8] == 's') || (tmp[8] == 'S')) &&
1232 ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
1233 /* possible X-Status: */
1234 ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
1235 ((tmp[3] == 't') || (tmp[3] == 'T')) &&
1236 ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
1237 ((tmp[5] == 't') || (tmp[5] == 'T')) &&
1238 ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
1239 ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
1240 /* possible X-Keywords: */
1241 ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
1242 ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
1243 ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
1244 ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
1245 ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
1246 ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
1247 ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
1248 ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
1249 (fputs ("X-Original-",df) == EOF)) return NIL;
1250 break;
1251 case '\n': /* blank line */
1252 hdrp = NIL;
1253 break;
1254 default: /* nothing to do */
1255 break;
1257 /* just write the line */
1258 if (fwrite (tmp,1,j,df) != j) return NIL;
1260 if (i) return NIL; /* didn't read entire message */
1261 /* update set */
1262 if (stream) mail_append_set (set,stream->uid_last);
1264 return T;
1267 /* Internal routines */
1270 /* UNIX mail abort stream
1271 * Accepts: MAIL stream
1274 void unix_abort (MAILSTREAM *stream)
1276 if (LOCAL) { /* only if a file is open */
1277 if (LOCAL->fd >= 0) close (LOCAL->fd);
1278 if (LOCAL->ld >= 0) { /* have a mailbox lock? */
1279 flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
1280 close (LOCAL->ld); /* close the lock file */
1281 unlink (LOCAL->lname); /* and delete it */
1283 if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
1284 /* free local text buffers */
1285 if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
1286 if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
1287 if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
1288 if (LOCAL->line) fs_give ((void **) &LOCAL->line);
1289 /* nuke the local data */
1290 fs_give ((void **) &stream->local);
1291 stream->dtb = NIL; /* log out the DTB */
1295 /* UNIX open and lock mailbox
1296 * Accepts: file name to open/lock
1297 * file open mode
1298 * destination buffer for lock file name
1299 * type of locking operation (LOCK_SH or LOCK_EX)
1302 int unix_lock (char *file,int flags,int mode,char *lock,int op)
1304 int fd,ld,j;
1305 int i = LOCKTIMEOUT * 60 - 1;
1306 char tmp[MAILTMPLEN];
1307 time_t t;
1308 struct stat sb;
1309 sprintf (lock,"%s.lock",file);/* build lock filename */
1310 do { /* until OK or out of tries */
1311 t = time (0); /* get the time now */
1312 /* try to get the lock */
1313 if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
1314 close (ld); /* got it, close the lock file! */
1315 else if (errno != EEXIST) { /* miscellaneous error */
1316 sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
1317 if (!(i%15)) mm_log (tmp,WARN);
1319 /* lock exists, still active? */
1320 else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
1321 ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
1322 close (ld); /* got timed-out lock file */
1323 else { /* active lock, try again */
1324 if (!(i%15)) {
1325 sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
1326 file,i);
1327 mm_log (tmp,WARN);
1329 sleep (1); /* wait a second before next retry */
1331 } while (*lock && ld < 0 && i--);
1332 /* open file */
1333 if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
1334 else { /* open failed */
1335 j = errno; /* preserve error code */
1336 if (*lock) unlink (lock); /* flush the lock file if any */
1337 errno = j; /* restore error code */
1339 return fd;
1342 /* UNIX unlock and close mailbox
1343 * Accepts: file descriptor
1344 * (optional) mailbox stream to check atime/mtime
1345 * (optional) lock file name
1348 void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
1350 if (stream) { /* need to muck with times? */
1351 struct stat sbuf;
1352 struct utimbuf times;
1353 time_t now = time (0);
1354 fstat (fd,&sbuf); /* get file times */
1355 if (LOCAL->ld >= 0) { /* yes, readwrite session? */
1356 times.actime = now; /* set atime to now */
1357 /* set mtime to (now - 1) if necessary */
1358 times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
1360 else if (stream->recent) { /* readonly with recent messages */
1361 if ((sbuf.st_atime >= sbuf.st_mtime) ||
1362 (sbuf.st_atime >= sbuf.st_ctime))
1363 /* keep past mtime, whack back atime */
1364 times.actime = (times.modtime = (sbuf.st_mtime < now) ?
1365 sbuf.st_mtime : now) - 1;
1366 else now = 0; /* no time change needed */
1368 /* readonly with no recent messages */
1369 else if ((sbuf.st_atime < sbuf.st_mtime) ||
1370 (sbuf.st_atime < sbuf.st_ctime)) {
1371 times.actime = now; /* set atime to now */
1372 /* set mtime to (now - 1) if necessary */
1373 times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
1375 else now = 0; /* no time change needed */
1376 /* set the times, note change */
1377 if (now && !utime (stream->mailbox,&times))
1378 LOCAL->filetime = times.modtime;
1380 flock (fd,LOCK_UN); /* release flock'ers */
1381 if (!stream) close (fd); /* close the file if no stream */
1382 /* flush the lock file if any */
1383 if (lock && *lock) unlink (lock);
1386 /* UNIX mail parse and lock mailbox
1387 * Accepts: MAIL stream
1388 * space to write lock file name
1389 * type of locking operation
1390 * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
1393 int unix_parse (MAILSTREAM *stream,char *lock,int op)
1395 int zn;
1396 unsigned long i,j,k,m;
1397 unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
1398 int ti = 0,retain = T;
1399 unsigned long nmsgs = stream->nmsgs;
1400 unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
1401 unsigned long recent = stream->recent;
1402 unsigned long oldnmsgs = stream->nmsgs;
1403 short silent = stream->silent;
1404 short pseudoseen = NIL;
1405 struct stat sbuf;
1406 STRING bs;
1407 FDDATA d;
1408 MESSAGECACHE *elt;
1409 mail_lock (stream); /* guard against recursion or pingers */
1410 /* toss out previous descriptor */
1411 if (LOCAL->fd >= 0) close (LOCAL->fd);
1412 mm_critical (stream); /* open and lock mailbox (shared OK) */
1413 if ((LOCAL->fd = unix_lock (stream->mailbox,
1414 O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
1415 NIL,lock,op)) < 0) {
1416 sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
1417 mm_log (tmp,ERROR);
1418 unix_abort (stream);
1419 mail_unlock (stream);
1420 mm_nocritical (stream); /* done with critical */
1421 return NIL;
1423 fstat (LOCAL->fd,&sbuf); /* get status */
1424 /* validate change in size */
1425 if (sbuf.st_size < LOCAL->filesize) {
1426 sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
1427 (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
1428 mm_log (tmp,ERROR); /* this is pretty bad */
1429 unix_unlock (LOCAL->fd,stream,lock);
1430 unix_abort (stream);
1431 mail_unlock (stream);
1432 mm_nocritical (stream); /* done with critical */
1433 return NIL;
1436 /* new data? */
1437 else if (i = sbuf.st_size - LOCAL->filesize) {
1438 d.fd = LOCAL->fd; /* yes, set up file descriptor */
1439 d.pos = LOCAL->filesize; /* get to that position in the file */
1440 d.chunk = LOCAL->buf; /* initial buffer chunk */
1441 d.chunksize = CHUNKSIZE; /* file chunk size */
1442 INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
1443 /* skip leading whitespace for broken MTAs */
1444 while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
1445 (c == ' ') || (c == '\t')) SNX (&bs);
1446 if (SIZE (&bs)) { /* read new data */
1447 /* remember internal header position */
1448 j = LOCAL->filesize + GETPOS (&bs);
1449 s = unix_mbxline (stream,&bs,&i);
1450 t = NIL,zn = 0;
1451 if (i) VALID (s,t,ti,zn); /* see if valid From line */
1452 if (!ti) { /* someone pulled the rug from under us */
1453 sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
1454 (char *) s);
1455 mm_log (tmp,ERROR);
1456 unix_unlock (LOCAL->fd,stream,lock);
1457 unix_abort (stream);
1458 mail_unlock (stream);
1459 mm_nocritical (stream); /* done with critical */
1460 return NIL;
1462 stream->silent = T; /* quell main program new message events */
1463 do { /* found a message */
1464 /* instantiate first new message */
1465 mail_exists (stream,++nmsgs);
1466 (elt = mail_elt (stream,nmsgs))->valid = T;
1467 recent++; /* assume recent by default */
1468 elt->recent = T;
1469 /* note position/size of internal header */
1470 elt->private.special.offset = j;
1471 elt->private.msg.header.offset = elt->private.special.text.size = i;
1473 /* generate plausible IMAPish date string */
1474 date[2] = date[6] = date[20] = '-'; date[11] = ' ';
1475 date[14] = date[17] = ':';
1476 /* dd */
1477 date[0] = t[ti - 2]; date[1] = t[ti - 1];
1478 /* mmm */
1479 date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
1480 /* hh */
1481 date[12] = t[ti + 1]; date[13] = t[ti + 2];
1482 /* mm */
1483 date[15] = t[ti + 4]; date[16] = t[ti + 5];
1484 if (t[ti += 6] == ':') {/* ss */
1485 date[18] = t[++ti]; date[19] = t[++ti];
1486 ti++; /* move to space */
1488 else date[18] = date[19] = '0';
1489 /* yy -- advance over timezone if necessary */
1490 if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
1491 date[7] = t[ti + 1]; date[8] = t[ti + 2];
1492 date[9] = t[ti + 3]; date[10] = t[ti + 4];
1493 /* zzz */
1494 t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
1495 date[21] = *t++; date[22] = *t++; date[23] = *t++;
1496 if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
1497 else { /* numeric time zone */
1498 date[24] = *t++; date[25] = *t++;
1499 date[26] = '\0'; date[20] = ' ';
1501 /* set internal date */
1502 if (!mail_parse_date (elt,date)) {
1503 sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
1504 mm_log (tmp,WARN);
1507 do { /* look for message body */
1508 s = t = unix_mbxline (stream,&bs,&i);
1509 if (i) switch (*s) { /* check header lines */
1510 case 'X': /* possible X-???: line */
1511 if (s[1] == '-') { /* must be immediately followed by hyphen */
1512 /* X-Status: becomes Status: in S case */
1513 if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
1514 s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
1515 /* possible X-Keywords */
1516 else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
1517 s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
1518 s[8] == 'd' && s[9] == 's' && s[10] == ':') {
1519 SIZEDTEXT uf;
1520 retain = NIL; /* don't retain continuation */
1521 s += 11; /* flush leading whitespace */
1522 while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
1523 while (*s == ' ') s++;
1524 /* find end of keyword */
1525 if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
1526 /* got a keyword? */
1527 if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
1528 uf.data = (unsigned char *) s;
1529 uf.size = k;
1530 for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
1531 if (!compare_csizedtext (stream->user_flags[j],&uf)) {
1532 elt->user_flags |= ((long) 1) << j;
1533 break;
1536 s = u; /* advance to next keyword */
1538 break;
1541 /* possible X-IMAP */
1542 else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
1543 (s[5] == 'P') && ((m = (s[6] == ':')) ||
1544 ((s[6] == 'b') && (s[7] == 'a') &&
1545 (s[8] == 's') && (s[9] == 'e') &&
1546 (s[10] == ':')))) {
1547 retain = NIL; /* don't retain continuation */
1548 if ((nmsgs == 1) && !stream->uid_validity) {
1549 /* advance to data */
1550 s += m ? 7 : 11;
1551 /* flush whitespace */
1552 while (*s == ' ') s++;
1553 j = 0; /* slurp UID validity */
1554 /* found a digit? */
1555 while (isdigit (*s)) {
1556 j *= 10; /* yes, add it in */
1557 j += *s++ - '0';
1559 /* flush whitespace */
1560 while (*s == ' ') s++;
1561 /* must have valid UID validity and UID last */
1562 if (j && isdigit (*s)) {
1563 /* pseudo-header seen if X-IMAP */
1564 if (m) pseudoseen = LOCAL->pseudo = T;
1565 /* save UID validity */
1566 stream->uid_validity = j;
1567 j = 0; /* slurp UID last */
1568 while (isdigit (*s)) {
1569 j *= 10; /* yes, add it in */
1570 j += *s++ - '0';
1572 /* save UID last */
1573 stream->uid_last = j;
1574 /* process keywords */
1575 for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
1576 s = u,j++) {
1577 /* flush leading whitespace */
1578 while (*s == ' ') s++;
1579 u = strpbrk (s," \n\r");
1580 /* got a keyword? */
1581 if ((j < NUSERFLAGS) && (k = (u - s)) &&
1582 (k <= MAXUSERFLAG)) {
1583 if (stream->user_flags[j])
1584 fs_give ((void **) &stream->user_flags[j]);
1585 stream->user_flags[j] = (char *) fs_get (k + 1);
1586 strncpy (stream->user_flags[j],s,k);
1587 stream->user_flags[j][k] = '\0';
1592 break;
1595 /* possible X-UID */
1596 else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
1597 s[5] == ':') {
1598 retain = NIL; /* don't retain continuation */
1599 /* only believe if have a UID validity */
1600 if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
1601 s += 6; /* advance to UID value */
1602 /* flush whitespace */
1603 while (*s == ' ') s++;
1604 j = 0;
1605 /* found a digit? */
1606 while (isdigit (*s)) {
1607 j *= 10; /* yes, add it in */
1608 j += *s++ - '0';
1610 /* flush remainder of line */
1611 while (*s != '\n') s++;
1612 /* make sure not duplicated */
1613 if (elt->private.uid)
1614 sprintf (tmp,"Message %lu UID %lu already has UID %lu",
1615 pseudoseen ? elt->msgno - 1 : elt->msgno,
1616 j,elt->private.uid);
1617 /* make sure UID doesn't go backwards */
1618 else if (j <= prevuid)
1619 sprintf (tmp,"Message %lu UID %lu less than %lu",
1620 pseudoseen ? elt->msgno - 1 : elt->msgno,
1621 j,prevuid + 1);
1622 #if 0 /* this is currently broken by UIDPLUS */
1623 /* or skip by mailbox's recorded last */
1624 else if (j > stream->uid_last)
1625 sprintf (tmp,"Message %lu UID %lu greater than last %lu",
1626 pseudoseen ? elt->msgno - 1 : elt->msgno,
1627 j,stream->uid_last);
1628 #endif
1629 else { /* normal UID case */
1630 prevuid = elt->private.uid = j;
1631 #if 1 /* temporary kludge for UIDPLUS */
1632 if (prevuid > stream->uid_last) {
1633 stream->uid_last = prevuid;
1634 LOCAL->ddirty = LOCAL->dirty = T;
1636 #endif
1637 break; /* exit this cruft */
1639 mm_log (tmp,WARN);
1640 /* invalidate UID validity */
1641 stream->uid_validity = 0;
1642 elt->private.uid = 0;
1644 break;
1647 /* otherwise fall into S case */
1649 case 'S': /* possible Status: line */
1650 if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
1651 s[4] == 'u' && s[5] == 's' && s[6] == ':') {
1652 retain = NIL; /* don't retain continuation */
1653 s += 6; /* advance to status flags */
1654 do switch (*s++) {/* parse flags */
1655 case 'R': /* message read */
1656 elt->seen = T;
1657 break;
1658 case 'O': /* message old */
1659 if (elt->recent) {
1660 elt->recent = NIL;
1661 recent--; /* it really wasn't recent */
1663 break;
1664 case 'D': /* message deleted */
1665 elt->deleted = T;
1666 break;
1667 case 'F': /* message flagged */
1668 elt->flagged = T;
1669 break;
1670 case 'A': /* message answered */
1671 elt->answered = T;
1672 break;
1673 case 'T': /* message is a draft */
1674 elt->draft = T;
1675 break;
1676 default: /* some other crap */
1677 break;
1678 } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
1679 break; /* all done */
1681 /* otherwise fall into default case */
1683 default: /* ordinary header line */
1684 if ((*s == 'S') || (*s == 's') ||
1685 (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
1686 unsigned char *e,*v;
1687 /* must match what mail_filter() does */
1688 for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
1689 (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
1690 ((c > ' ') || ((c != ' ') && (c != '\t') &&
1691 (c != '\r') && (c != '\n')));
1692 *v++ = *u++);
1693 *v = '\0'; /* tie off */
1694 /* matches internal header? */
1695 if (!compare_cstring (tmp,"STATUS") ||
1696 !compare_cstring (tmp,"X-STATUS") ||
1697 !compare_cstring (tmp,"X-KEYWORDS") ||
1698 !compare_cstring (tmp,"X-UID") ||
1699 !compare_cstring (tmp,"X-IMAP") ||
1700 !compare_cstring (tmp,"X-IMAPBASE")) {
1701 char err[MAILTMPLEN];
1702 sprintf (err,"Discarding bogus %s header in message %lu",
1703 (char *) tmp,elt->msgno);
1704 mm_log (err,WARN);
1705 retain = NIL; /* don't retain continuation */
1706 break; /* different case or something */
1709 /* retain or non-continuation? */
1710 if (retain || ((*s != ' ') && (*s != '\t'))) {
1711 retain = T; /* retaining continuation now */
1712 /* line length in CRLF format newline */
1713 k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
1714 /* header size */
1715 elt->rfc822_size = elt->private.spare.data += k;
1717 else {
1718 char err[MAILTMPLEN];
1719 sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
1720 elt->msgno,(char *) s);
1721 if (u = strpbrk (err,"\r\n")) *u = '\0';
1722 mm_log (err,WARN);
1723 break; /* different case or something */
1725 break;
1727 } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
1728 /* "internal" header sans trailing newline */
1729 if (i) elt->private.spare.data -= 2;
1730 /* assign a UID if none found */
1731 if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
1732 prevuid = elt->private.uid = ++stream->uid_last;
1733 elt->private.dirty = T;
1734 LOCAL->ddirty = T; /* force update */
1736 else elt->private.dirty = elt->recent;
1738 /* note size of header, location of text */
1739 elt->private.msg.header.text.size =
1740 (elt->private.msg.text.offset =
1741 (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
1742 elt->private.special.text.size;
1743 k = m = 0; /* no previous line size yet */
1744 /* note current position */
1745 j = LOCAL->filesize + GETPOS (&bs);
1746 if (i) do { /* look for next message */
1747 s = unix_mbxline (stream,&bs,&i);
1748 if (i) { /* got new data? */
1749 VALID (s,t,ti,zn); /* yes, parse line */
1750 if (!ti) { /* not a header line, add it to message */
1751 if (s[i - 1] == '\n')
1752 elt->rfc822_size +=
1753 k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
1754 else { /* file does not end with newline! */
1755 elt->rfc822_size += i;
1756 k = m = 0;
1758 /* update current position */
1759 j = LOCAL->filesize + GETPOS (&bs);
1762 } while (i && !ti); /* until found a header */
1763 elt->private.msg.text.text.size = j -
1764 (elt->private.special.offset + elt->private.msg.text.offset);
1765 if (k == 2) { /* last line was blank? */
1766 elt->private.msg.text.text.size -= (m ? 1 : 2);
1767 elt->rfc822_size -= 2;
1769 /* until end of buffer */
1770 } while (!stream->sniff && i);
1771 if (pseudoseen) { /* flush pseudo-message if present */
1772 /* decrement recent count */
1773 if (mail_elt (stream,1)->recent) recent--;
1774 /* and the exists count */
1775 mail_exists (stream,nmsgs--);
1776 mail_expunged(stream,1);/* fake an expunge of that message */
1778 /* need to start a new UID validity? */
1779 if (!stream->uid_validity) {
1780 stream->uid_validity = (unsigned long) time (0);
1781 if (nmsgs) { /* don't bother if empty file */
1782 /* make dirty to restart UID epoch */
1783 LOCAL->ddirty = LOCAL->dirty = T;
1784 /* need to rewrite msg 1 if not pseudo */
1785 if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
1786 mm_log ("Assigning new unique identifiers to all messages",NIL);
1789 stream->nmsgs = oldnmsgs; /* whack it back down */
1790 stream->silent = silent; /* restore old silent setting */
1791 /* notify upper level of new mailbox sizes */
1792 mail_exists (stream,nmsgs);
1793 mail_recent (stream,recent);
1794 /* mark dirty so O flags are set */
1795 if (recent) LOCAL->dirty = T;
1798 /* no change, don't babble if never got time */
1799 else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
1800 mm_log ("New mailbox modification time but apparently no changes",WARN);
1801 /* update parsed file size and time */
1802 LOCAL->filesize = sbuf.st_size;
1803 LOCAL->filetime = sbuf.st_mtime;
1804 return T; /* return the winnage */
1807 /* UNIX read line from mailbox
1808 * Accepts: mail stream
1809 * stringstruct
1810 * pointer to line size
1811 * Returns: pointer to input line
1814 char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
1816 unsigned long i,j,k,m;
1817 char *s,*t,*te;
1818 char *ret = "";
1819 /* flush old buffer */
1820 if (LOCAL->line) fs_give ((void **) &LOCAL->line);
1821 /* if buffer needs refreshing */
1822 if (!bs->cursize) SETPOS (bs,GETPOS (bs));
1823 if (SIZE (bs)) { /* find newline */
1824 /* end of fast scan */
1825 te = (t = (s = bs->curpos) + bs->cursize) - 12;
1826 while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1827 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1828 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1829 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
1830 --s; /* back up */
1831 break; /* exit loop */
1833 /* final character-at-a-time scan */
1834 while ((s < t) && (*s != '\n')) ++s;
1835 /* difficult case if line spans buffer */
1836 if ((i = s - bs->curpos) == bs->cursize) {
1837 /* have space in line buffer? */
1838 if (i > LOCAL->linebuflen) {
1839 fs_give ((void **) &LOCAL->linebuf);
1840 LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
1842 /* remember what we have so far */
1843 memcpy (LOCAL->linebuf,bs->curpos,i);
1844 /* load next buffer */
1845 SETPOS (bs,k = GETPOS (bs) + i);
1846 /* end of fast scan */
1847 te = (t = (s = bs->curpos) + bs->cursize) - 12;
1848 /* fast scan in overlap buffer */
1849 while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1850 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1851 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
1852 (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
1853 --s; /* back up */
1854 break; /* exit loop */
1857 /* final character-at-a-time scan */
1858 while ((s < t) && (*s != '\n')) ++s;
1859 /* huge line? */
1860 if ((j = s - bs->curpos) == bs->cursize) {
1861 SETPOS (bs,GETPOS (bs) + j);
1862 /* look for end of line (s-l-o-w!!) */
1863 for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
1864 SETPOS (bs,k); /* go back to where it started */
1866 /* got size of data, make buffer for return */
1867 ret = LOCAL->line = (char *) fs_get (i + j + 2);
1868 /* copy first chunk */
1869 memcpy (ret,LOCAL->linebuf,i);
1870 while (j) { /* copy remainder */
1871 if (!bs->cursize) SETPOS (bs,GETPOS (bs));
1872 memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
1873 i += k; /* account for this much read in */
1874 j -= k;
1875 bs->curpos += k; /* increment new position */
1876 bs->cursize -= k; /* eat that many bytes */
1878 if (!bs->cursize) SETPOS (bs,GETPOS (bs));
1879 /* read newline at end */
1880 if (SIZE (bs)) ret[i++] = SNX (bs);
1881 ret[i] = '\0'; /* makes debugging easier */
1883 else { /* this is easy */
1884 ret = bs->curpos; /* string it at this position */
1885 bs->curpos += ++i; /* increment new position */
1886 bs->cursize -= i; /* eat that many bytes */
1888 *size = i; /* return that to user */
1890 else *size = 0; /* end of data, return empty */
1891 return ret;
1894 /* UNIX make pseudo-header
1895 * Accepts: MAIL stream
1896 * buffer to write pseudo-header
1897 * Returns: length of pseudo-header
1900 unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
1902 int i;
1903 char *s,*t,tmp[MAILTMPLEN];
1904 time_t now = time(0);
1905 rfc822_fixed_date (tmp);
1906 sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
1907 pseudo_from,ctime (&now),
1908 tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
1909 (unsigned long) now,mylocalhost (),stream->uid_validity,
1910 stream->uid_last);
1911 for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
1912 if (stream->user_flags[i])
1913 sprintf (t += strlen (t)," %s",stream->user_flags[i]);
1914 strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
1915 for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
1916 if (*s == '\n') *t++ = '\r';
1917 *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
1918 *t = '\0'; /* tie off pseudo header */
1919 return t - hdr; /* return length of pseudo header */
1922 /* UNIX make status string
1923 * Accepts: MAIL stream
1924 * destination string to write
1925 * message cache entry
1926 * UID to write if non-zero (else use elt->private.uid)
1927 * non-zero flag to write UID (.LT. 0 to write UID base info too)
1928 * Returns: length of string
1931 unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
1932 unsigned long uid,long flag)
1934 char *t,stack[64];
1935 char *s = status;
1936 unsigned long n;
1937 unsigned long pad = 50;
1938 /* This used to use sprintf(), but thanks to certain cretinous C libraries
1939 with horribly slow implementations of sprintf() I had to change it to this
1940 mess. At least it should be fast. */
1941 *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
1942 *s++ = ':'; *s++ = ' ';
1943 if (elt->seen) *s++ = 'R';
1944 /* only write O if have a UID */
1945 if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
1946 *s++ = '\r'; *s++ = '\n';
1947 *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
1948 *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
1949 if (elt->deleted) *s++ = 'D';
1950 if (elt->flagged) *s++ = 'F';
1951 if (elt->answered) *s++ = 'A';
1952 if (elt->draft) *s++ = 'T';
1953 *s++ = '\r'; *s++ = '\n';
1955 *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
1956 *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
1957 if (n = elt->user_flags) do {
1958 *s++ = ' ';
1959 for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
1960 } while (n);
1961 n = s - status; /* get size of stuff so far */
1962 /* pad X-Keywords to make size constant */
1963 if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
1964 *s++ = '\r'; *s++ = '\n';
1965 if (flag) { /* want to include UID? */
1966 t = stack;
1967 /* push UID digits on the stack */
1968 n = uid ? uid : elt->private.uid;
1969 do *t++ = (char) (n % 10) + '0';
1970 while (n /= 10);
1971 *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
1972 *s++ = ' ';
1973 /* pop UID from stack */
1974 while (t > stack) *s++ = *--t;
1975 *s++ = '\r'; *s++ = '\n';
1977 /* end of extended message status */
1978 *s++ = '\r'; *s++ = '\n'; *s = '\0';
1979 return s - status; /* return size of resulting string */
1982 /* Rewrite mailbox file
1983 * Accepts: MAIL stream, must be critical and locked
1984 * return pointer to number of expunged messages if want expunge
1985 * lock file name
1986 * expunge sequence, not deleted flag
1987 * Returns: T if success and mailbox unlocked, NIL if failure
1990 #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
1992 long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
1993 long flags)
1995 MESSAGECACHE *elt;
1996 UNIXFILE f;
1997 char *s;
1998 struct utimbuf times;
1999 long ret,flag;
2000 unsigned long i,j;
2001 unsigned long recent = stream->recent;
2002 unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
2003 if (nexp) *nexp = 0; /* initially nothing expunged */
2004 /* calculate size of mailbox after rewrite */
2005 for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
2006 elt = mail_elt (stream,i); /* get cache */
2007 if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
2008 /* add RFC822 size of this message */
2009 size += elt->private.special.text.size + elt->private.spare.data +
2010 unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
2011 elt->private.msg.text.text.size + 2;
2012 flag = 1; /* only count X-IMAPbase once */
2015 if (!size) { /* no messages and no pseudo, make one now */
2016 size = unix_pseudo (stream,LOCAL->buf);
2017 LOCAL->pseudo = T;
2019 /* extend the file as necessary */
2020 if (ret = unix_extend (stream,size)) {
2021 /* Set up buffered I/O file structure
2022 * curpos current position being written through buffering
2023 * filepos current position being written physically to the disk
2024 * bufpos current position being written in the buffer
2025 * protect current maximum position that can be written to the disk
2026 * before buffering is forced
2027 * The code tries to buffer so that that disk is written in multiples of
2028 * OVERBLOWBUFLEN bytes.
2030 f.stream = stream; /* note mail stream */
2031 f.curpos = f.filepos = 0; /* start of file */
2032 f.protect = stream->nmsgs ? /* initial protection pointer */
2033 mail_elt (stream,1)->private.special.offset : 8192;
2034 f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
2036 if (LOCAL->pseudo) /* update pseudo-header */
2037 unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
2038 /* loop through all messages */
2039 for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
2040 elt = mail_elt (stream,i);/* get cache */
2041 /* expunge this message? */
2042 if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
2043 /* one less recent message */
2044 if (elt->recent) --recent;
2045 mail_expunged(stream,i);/* notify upper levels */
2046 ++*nexp; /* count up one more expunged message */
2048 else { /* preserve this message */
2049 i++; /* advance to next message */
2050 if ((flag < 0) || /* need to rewrite message? */
2051 elt->private.dirty ||
2052 (((unsigned long) f.curpos) != elt->private.special.offset) ||
2053 (elt->private.msg.header.text.size !=
2054 (elt->private.spare.data +
2055 unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
2056 unsigned long newoffset = f.curpos;
2057 /* yes, seek to internal header */
2058 lseek (LOCAL->fd,elt->private.special.offset,L_SET);
2059 read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
2060 /* protection pointer moves to RFC822 header */
2061 f.protect = elt->private.special.offset +
2062 elt->private.msg.header.offset;
2063 /* write internal header */
2064 unix_write (&f,LOCAL->buf,elt->private.special.text.size);
2065 /* get RFC822 header */
2066 s = unix_header (stream,elt->msgno,&j,NIL);
2067 /* in case this got decremented */
2068 elt->private.msg.header.offset = elt->private.special.text.size;
2069 /* header size, sans trailing newline */
2070 if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
2071 if (j != elt->private.spare.data) fatal ("header size inconsistent");
2072 /* protection pointer moves to RFC822 text */
2073 f.protect = elt->private.special.offset +
2074 elt->private.msg.text.offset;
2075 unix_write (&f,s,j); /* write RFC822 header */
2076 /* write status and UID */
2077 unix_write (&f,LOCAL->buf,
2078 j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
2079 flag = 1; /* only write X-IMAPbase once */
2080 /* new file header size */
2081 elt->private.msg.header.text.size = elt->private.spare.data + j;
2083 /* did text move? */
2084 if (f.curpos != f.protect) {
2085 /* get message text */
2086 s = unix_text_work (stream,elt,&j,FT_INTERNAL);
2087 /* can't happen it says here */
2088 if (j > elt->private.msg.text.text.size)
2089 fatal ("text size inconsistent");
2090 /* new text offset, status/UID may change it */
2091 elt->private.msg.text.offset = f.curpos - newoffset;
2092 /* protection pointer moves to next message */
2093 f.protect = (i <= stream->nmsgs) ?
2094 mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
2095 unix_write (&f,s,j);/* write text */
2096 /* write trailing newline */
2097 unix_write (&f,"\r\n",2);
2099 else { /* tie off header and status */
2100 unix_write (&f,NIL,NIL);
2101 /* protection pointer moves to next message */
2102 f.protect = (i <= stream->nmsgs) ?
2103 mail_elt (stream,i)->private.special.offset : size;
2104 /* locate end of message text */
2105 j = f.filepos + elt->private.msg.text.text.size;
2106 /* trailing newline already there? */
2107 if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
2108 else { /* trailing newline missing, write it */
2109 f.curpos = f.filepos = j;
2110 unix_write (&f,"\r\n",2);
2113 /* new internal header offset */
2114 elt->private.special.offset = newoffset;
2115 elt->private.dirty =NIL;/* message is now clean */
2117 else { /* no need to rewrite this message */
2118 /* tie off previous message if needed */
2119 unix_write (&f,NIL,NIL);
2120 /* protection pointer moves to next message */
2121 f.protect = (i <= stream->nmsgs) ?
2122 mail_elt (stream,i)->private.special.offset : size;
2123 /* locate end of message text */
2124 j = f.filepos + elt->private.special.text.size +
2125 elt->private.msg.header.text.size +
2126 elt->private.msg.text.text.size;
2127 /* trailing newline already there? */
2128 if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
2129 else { /* trailing newline missing, write it */
2130 f.curpos = f.filepos = j;
2131 unix_write (&f,"\r\n",2);
2137 unix_write (&f,NIL,NIL); /* tie off final message */
2138 if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
2139 fs_give ((void **) &f.buf); /* free buffer */
2140 /* make sure tied off */
2141 ftruncate (LOCAL->fd,LOCAL->filesize = size);
2142 fsync (LOCAL->fd); /* make sure the updates take */
2143 if (size && (flag < 0)) fatal ("lost UID base information");
2144 /* no longer dirty */
2145 LOCAL->ddirty = LOCAL->dirty = NIL;
2146 /* notify upper level of new mailbox sizes */
2147 mail_exists (stream,stream->nmsgs);
2148 mail_recent (stream,recent);
2149 /* set atime to now, mtime a second earlier */
2150 times.modtime = (times.actime = time (0)) -1;
2151 /* set the times, note change */
2152 if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
2153 /* flush the lock file */
2154 unix_unlock (LOCAL->fd,stream,lock);
2156 return ret; /* return state from algorithm */
2159 /* Extend UNIX mailbox file
2160 * Accepts: MAIL stream
2161 * new desired size
2162 * Return: T if success, else NIL
2165 long unix_extend (MAILSTREAM *stream,unsigned long size)
2167 unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
2168 size - ((unsigned long) LOCAL->filesize) : 0;
2169 if (i) { /* does the mailbox need to grow? */
2170 if (i > LOCAL->buflen) { /* make sure have enough space */
2171 /* this user won the lottery all right */
2172 fs_give ((void **) &LOCAL->buf);
2173 LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
2175 memset (LOCAL->buf,'\0',i); /* get a block of nulls */
2176 while (T) { /* until write successful or punt */
2177 lseek (LOCAL->fd,LOCAL->filesize,L_SET);
2178 if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
2179 else {
2180 long e = errno; /* note error before doing ftruncate */
2181 ftruncate (LOCAL->fd,LOCAL->filesize);
2182 if (mm_diskerror (stream,e,NIL)) {
2183 fsync (LOCAL->fd); /* user chose to punt */
2184 sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
2185 if (!stream->silent) mm_log (LOCAL->buf,ERROR);
2186 return NIL;
2191 return LONGT;
2194 /* Write data to buffered file
2195 * Accepts: buffered file pointer
2196 * file data or NIL to indicate "flush buffer"
2197 * date size (ignored for "flush buffer")
2198 * Does not return until success
2201 void unix_write (UNIXFILE *f,char *buf,unsigned long size)
2203 unsigned long i,j,k;
2204 if (buf) { /* doing buffered write? */
2205 i = f->bufpos - f->buf; /* yes, get size of current buffer data */
2206 /* yes, have space in current buffer chunk? */
2207 if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
2208 /* yes, fill up buffer as much as we can */
2209 memcpy (f->bufpos,buf,k = min (j,size));
2210 f->bufpos += k; /* new buffer position */
2211 f->curpos += k; /* new current position */
2212 if (j -= k) return; /* all done if still have buffer free space */
2213 buf += k; /* full, get new unwritten data pointer */
2214 size -= k; /* new data size */
2215 i += k; /* new buffer data size */
2217 /* This chunk of the buffer is full. See if can make some space by
2218 * writing to the disk, if there's enough unprotected space to do so.
2219 * Try to fill out any unaligned chunk, along with any subsequent full
2220 * chunks that will fit in unprotected space.
2222 /* any unprotected space we can write to? */
2223 if (j = min (i,f->protect - f->filepos)) {
2224 /* yes, filepos not at chunk boundary? */
2225 if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
2226 j -= k; /* yes, and can write out partial chunk */
2227 else k = 0; /* no partial chunk to write */
2228 /* if at least a chunk free, write that too */
2229 if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
2230 if (k) { /* write data if there is anything we can */
2231 unix_phys_write (f,f->buf,k);
2232 /* slide buffer */
2233 if (i -= k) memmove (f->buf,f->buf + k,i);
2234 f->bufpos = f->buf + i; /* new end of buffer */
2238 /* Have flushed the buffer as best as possible. All done if no more
2239 * data to write. Otherwise, if the buffer is empty AND if the unwritten
2240 * data is larger than a chunk AND the unprotected space is also larger
2241 * than a chunk, then write as many chunks as we can directly from the
2242 * data. Buffer the rest, expanding the buffer as needed.
2244 if (size) { /* have more data that we need to buffer? */
2245 /* can write any of it to disk instead? */
2246 if ((f->bufpos == f->buf) &&
2247 ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
2248 /* write as much as we can right now */
2249 unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
2250 buf += j; /* new data pointer */
2251 size -= j; /* new data size */
2252 f->curpos += j; /* advance current pointer */
2254 if (size) { /* still have data that we need to buffer? */
2255 /* yes, need to expand the buffer? */
2256 if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
2257 /* note current position in buffer */
2258 j = f->bufpos - f->buf;
2259 i += OVERFLOWBUFLEN; /* yes, grow another chunk */
2260 fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
2261 /* in case buffer relocated */
2262 f->bufpos = f->buf + j;
2264 /* buffer remaining data */
2265 memcpy (f->bufpos,buf,size);
2266 f->bufpos += size; /* new end of buffer */
2267 f->curpos += size; /* advance current pointer */
2271 else { /* flush buffer to disk */
2272 unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
2273 f->bufpos = f->buf; /* reset buffer */
2274 /* update positions */
2275 f->curpos = f->protect = f->filepos;
2279 /* Physical disk write
2280 * Accepts: buffered file pointer
2281 * buffer address
2282 * buffer size
2283 * Does not return until success
2286 void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
2288 MAILSTREAM *stream = f->stream;
2289 /* write data at desired position */
2290 while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
2291 (write (LOCAL->fd,buf,size) < 0))) {
2292 int e;
2293 char tmp[MAILTMPLEN];
2294 sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
2295 mm_log (tmp,ERROR);
2296 mm_diskerror (NIL,e,T); /* serious problem, must retry */
2298 f->filepos += size; /* update file position */