unixnt.c   unixnt.c 
/* ========================================================================
* Copyright 1988-2006 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* ========================================================================
*/
/* /*
* Program: UNIX mail routines * Program: UNIX mail routines
* *
* Author: Mark Crispin * Author: Mark Crispin
* Networks and Distributed Computing * Networks and Distributed Computing
* Computing & Communications * Computing & Communications
* University of Washington * University of Washington
* Administration Building, AG-44 * Administration Building, AG-44
* Seattle, WA 98195 * Seattle, WA 98195
* Internet: MRC@CAC.Washington.EDU * Internet: MRC@CAC.Washington.EDU
* *
* Date: 20 December 1989 * Date: 20 December 1989
if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && * Last Edited: 30 August 2006
((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
* Last Edited: 4 November 2004
*
* The IMAP toolkit provided in this Distribution is
* Copyright 1988-2004 University of Washington.
* The full text of our legal notices is contained in the file called
* CPYRIGHT, included with this Distribution.
*/ */
/* DEDICATION /* DEDICATION
* *
* This file is dedicated to my dog, Unix, also known as Yun-chan and * This file is dedicated to my dog, Unix, also known as Yun-chan and
* Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
* passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, af ter * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, af ter
* a two-month bout with cirrhosis of the liver. * a two-month bout with cirrhosis of the liver.
* *
* He was a dear friend, and I miss him terribly. * He was a dear friend, and I miss him terribly.
skipping to change at line 57 skipping to change at line 60
#include <sys/utime.h> #include <sys/utime.h>
#include "unixnt.h" #include "unixnt.h"
#include "pseudo.h" #include "pseudo.h"
#include "fdstring.h" #include "fdstring.h"
#include "misc.h" #include "misc.h"
#include "dummy.h" #include "dummy.h"
/* UNIX I/O stream local data */ /* UNIX I/O stream local data */
typedef struct unix_local { typedef struct unix_local {
unsigned int dirty : 1; /* disk copy needs updating */ unsigned int dirty : 1; /* disk copy needs updating */
unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
unsigned int pseudo : 1; /* uses a pseudo message */ unsigned int pseudo : 1; /* uses a pseudo message */
int fd; /* mailbox file descriptor */ int fd; /* mailbox file descriptor */
int ld; /* lock file descriptor */ int ld; /* lock file descriptor */
char *lname; /* lock file name */ char *lname; /* lock file name */
off_t filesize; /* file size parsed */ off_t filesize; /* file size parsed */
time_t filetime; /* last file time */ time_t filetime; /* last file time */
time_t lastsnarf; /* last snarf time (for mbox driver) */ time_t lastsnarf; /* last snarf time (for mbox driver) */
unsigned char *buf; /* temporary buffer */ unsigned char *buf; /* temporary buffer */
unsigned long buflen; /* current size of temporary buffer */ unsigned long buflen; /* current size of temporary buffer */
unsigned long uid; /* current text uid */ unsigned long uid; /* current text uid */
skipping to change at line 90 skipping to change at line 94
off_t curpos; /* current file position */ off_t curpos; /* current file position */
off_t protect; /* protected position */ off_t protect; /* protected position */
off_t filepos; /* current last written file position */ off_t filepos; /* current last written file position */
char *buf; /* overflow buffer */ char *buf; /* overflow buffer */
size_t buflen; /* current overflow buffer length */ size_t buflen; /* current overflow buffer length */
char *bufpos; /* current buffer position */ char *bufpos; /* current buffer position */
} UNIXFILE; } UNIXFILE;
/* Function prototypes */ /* Function prototypes */
DRIVER *unix_valid (char *name); DRIVER *unix_valid (char *name);
long unix_isvalid_fd (int fd);
void *unix_parameters (long function,void *value); void *unix_parameters (long function,void *value);
void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
void unix_list (MAILSTREAM *stream,char *ref,char *pat); void unix_list (MAILSTREAM *stream,char *ref,char *pat);
void unix_lsub (MAILSTREAM *stream,char *ref,char *pat); void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
long unix_create (MAILSTREAM *stream,char *mailbox); long unix_create (MAILSTREAM *stream,char *mailbox);
long unix_delete (MAILSTREAM *stream,char *mailbox); long unix_delete (MAILSTREAM *stream,char *mailbox);
long unix_rename (MAILSTREAM *stream,char *old,char *newname); long unix_rename (MAILSTREAM *stream,char *old,char *newname);
MAILSTREAM *unix_open (MAILSTREAM *stream); MAILSTREAM *unix_open (MAILSTREAM *stream);
void unix_close (MAILSTREAM *stream,long options); void unix_close (MAILSTREAM *stream,long options);
char *unix_header (MAILSTREAM *stream,unsigned long msgno, char *unix_header (MAILSTREAM *stream,unsigned long msgno,
unsigned long *length,long flags); unsigned long *length,long flags);
long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flag s); long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flag s);
char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
unsigned long *length,long flags); unsigned long *length,long flags);
void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
long unix_ping (MAILSTREAM *stream); long unix_ping (MAILSTREAM *stream);
void unix_check (MAILSTREAM *stream); void unix_check (MAILSTREAM *stream);
void unix_check (MAILSTREAM *stream); long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
void unix_expunge (MAILSTREAM *stream);
long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long option s); long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long option s);
long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
STRING *msg); STRING *msg);
int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
void unix_abort (MAILSTREAM *stream); void unix_abort (MAILSTREAM *stream);
char *unix_file (char *dst,char *name); char *unix_file (char *dst,char *name);
int unix_lock (char *file,int flags,int mode,char *lock,int op); int unix_lock (char *file,int flags,int mode,char *lock,int op);
void unix_unlock (int fd,MAILSTREAM *stream,char *lock); void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
int unix_parse (MAILSTREAM *stream,char *lock,int op); int unix_parse (MAILSTREAM *stream,char *lock,int op);
char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr); unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *e lt, unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *e lt,
long flag); unsigned long uid,long flag);
long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock); long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
long flags);
long unix_extend (MAILSTREAM *stream,unsigned long size); long unix_extend (MAILSTREAM *stream,unsigned long size);
void unix_write (UNIXFILE *f,char *s,unsigned long i); void unix_write (UNIXFILE *f,char *s,unsigned long i);
void unix_phys_write (UNIXFILE *f,char *buf,size_t size); void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
/* UNIX mail routines */ /* UNIX mail routines */
/* Driver dispatch used by MAIL */ /* Driver dispatch used by MAIL */
DRIVER unixdriver = { DRIVER unixdriver = {
"unix", /* driver name */ "unix", /* driver name */
/* driver flags */ /* driver flags */
skipping to change at line 427 skipping to change at line 431
if (!dummy_file (tmp,stream->mailbox)) { if (!dummy_file (tmp,stream->mailbox)) {
sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
return NIL; return NIL;
} }
/* flush old name */ /* flush old name */
fs_give ((void **) &stream->mailbox); fs_give ((void **) &stream->mailbox);
/* save canonical name */ /* save canonical name */
stream->mailbox = cpystr (tmp); stream->mailbox = cpystr (tmp);
LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1); LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
LOCAL->text.data = (unsigned char *) LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
fs_get ((LOCAL->text.size = MAXMESSAGESIZE) + 1); LOCAL->text.size = CHUNKSIZE - 1;
stream->sequence++; /* bump sequence number */ stream->sequence++; /* bump sequence number */
if (!stream->rdonly) { /* make lock for read/write access */ if (!stream->rdonly) { /* make lock for read/write access */
if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0) if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
mm_log ("Can't open mailbox lock, access is readonly",WARN); mm_log ("Can't open mailbox lock, access is readonly",WARN);
/* can get the lock? */ /* can get the lock? */
else if (flock (fd,LOCK_EX|LOCK_NB)) { else if (flock (fd,LOCK_EX|LOCK_NB)) {
mm_log ("Mailbox is open by another process, access is readonly",WARN ); mm_log ("Mailbox is open by another process, access is readonly",WARN );
close (fd); close (fd);
} }
else { /* got the lock, nobody else can alter state */ else { /* got the lock, nobody else can alter state */
skipping to change at line 490 skipping to change at line 494
/* UNIX mail close /* UNIX mail close
* Accepts: MAIL stream * Accepts: MAIL stream
* close options * close options
*/ */
void unix_close (MAILSTREAM *stream,long options) void unix_close (MAILSTREAM *stream,long options)
{ {
int silent = stream->silent; int silent = stream->silent;
stream->silent = T; /* go silent */ stream->silent = T; /* go silent */
/* expunge if requested */ /* expunge if requested */
if (options & CL_EXPUNGE) unix_expunge (stream); if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
/* else dump final checkpoint */ /* else dump final checkpoint */
else if (LOCAL->dirty) unix_check (stream); else if (LOCAL->dirty) unix_check (stream);
stream->silent = silent; /* restore old silence state */ stream->silent = silent; /* restore old silence state */
unix_abort (stream); /* now punt the file and local data */ unix_abort (stream); /* now punt the file and local data */
} }
/* UNIX mail fetch message header /* UNIX mail fetch message header
* Accepts: MAIL stream * Accepts: MAIL stream
* message # to fetch * message # to fetch
* pointer to returned header text length * pointer to returned header text length
* option flags * option flags
skipping to change at line 599 skipping to change at line 603
* message cache element * message cache element
* pointer to returned header text length * pointer to returned header text length
* option flags * option flags
*/ */
char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
unsigned long *length,long flags) unsigned long *length,long flags)
{ {
FDDATA d; FDDATA d;
STRING bs; STRING bs;
unsigned char *s,tmp[CHUNK]; unsigned char *s,tmp[CHUNKSIZE];
/* go to text position */ /* go to text position */
lseek (LOCAL->fd,elt->private.special.offset + lseek (LOCAL->fd,elt->private.special.offset +
elt->private.msg.text.offset,L_SET); elt->private.msg.text.offset,L_SET);
if (flags & FT_INTERNAL) { /* initial data OK? */ if (flags & FT_INTERNAL) { /* initial data OK? */
if (elt->private.msg.text.text.size > LOCAL->buflen) { if (elt->private.msg.text.text.size > LOCAL->buflen) {
fs_give ((void **) &LOCAL->buf); fs_give ((void **) &LOCAL->buf);
LOCAL->buf = (char *) fs_get ((LOCAL->buflen = LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
elt->private.msg.text.text.size) + 1); elt->private.msg.text.text.size) + 1);
} }
/* read message */ /* read message */
skipping to change at line 629 skipping to change at line 633
/* is buffer big enough? */ /* is buffer big enough? */
if (elt->rfc822_size > LOCAL->text.size) { if (elt->rfc822_size > LOCAL->text.size) {
/* excessively conservative, but the right thing is too hard to do */ /* excessively conservative, but the right thing is too hard to do */
fs_give ((void **) &LOCAL->text.data); fs_give ((void **) &LOCAL->text.data);
LOCAL->text.data = (unsigned char *) LOCAL->text.data = (unsigned char *)
fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
} }
d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.fd = LOCAL->fd; /* yes, set up file descriptor */
d.pos = elt->private.special.offset + elt->private.msg.text.offset; d.pos = elt->private.special.offset + elt->private.msg.text.offset;
d.chunk = tmp; /* initial buffer chunk */ d.chunk = tmp; /* initial buffer chunk */
d.chunksize = CHUNK; /* file chunk size */ d.chunksize = CHUNKSIZE; /* file chunk size */
INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) { for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) {
case '\r': /* carriage return seen */ case '\r': /* carriage return seen */
*s++ = SNX (&bs); /* copy it and any succeeding LF */ *s++ = SNX (&bs); /* copy it and any succeeding LF */
if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
break; break;
case '\n': case '\n':
*s++ = '\r'; /* insert a CR */ *s++ = '\r'; /* insert a CR */
default: default:
*s++ = SNX (&bs); /* copy characters */ *s++ = SNX (&bs); /* copy characters */
skipping to change at line 680 skipping to change at line 684
if (stream->rdonly) { /* does he want to give up readwrite? */ if (stream->rdonly) { /* does he want to give up readwrite? */
/* checkpoint if we changed something */ /* checkpoint if we changed something */
if (LOCAL->dirty) unix_check (stream); if (LOCAL->dirty) unix_check (stream);
flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
close (LOCAL->ld); /* close the readwrite lock file */ close (LOCAL->ld); /* close the readwrite lock file */
LOCAL->ld = -1; /* no more readwrite lock fd */ LOCAL->ld = -1; /* no more readwrite lock fd */
unlink (LOCAL->lname); /* delete the readwrite lock file */ unlink (LOCAL->lname); /* delete the readwrite lock file */
} }
else { /* get current mailbox size */ else { /* get current mailbox size */
if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
else stat (stream->mailbox,&sbuf); else if (stat (stream->mailbox,&sbuf)) {
sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
strerror (errno));
MM_LOG (LOCAL->buf,ERROR);
unix_abort (stream);
return NIL;
}
/* parse if mailbox changed */ /* parse if mailbox changed */
if ((sbuf.st_size != LOCAL->filesize) && if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
unix_parse (stream,lock,LOCK_SH)) { unix_parse (stream,lock,LOCK_SH)) {
/* force checkpoint if double-dirty */
if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
/* unlock mailbox */ /* unlock mailbox */
unix_unlock (LOCAL->fd,stream,lock); else unix_unlock (LOCAL->fd,stream,lock);
mail_unlock (stream); /* and stream */ mail_unlock (stream); /* and stream */
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
} }
} }
} }
return LOCAL ? LONGT : NIL; /* return if still alive */ return LOCAL ? LONGT : NIL; /* return if still alive */
} }
/* UNIX mail check mailbox /* UNIX mail check mailbox
* Accepts: MAIL stream * Accepts: MAIL stream
*/ */
void unix_check (MAILSTREAM *stream) void unix_check (MAILSTREAM *stream)
{ {
char lock[MAILTMPLEN]; char lock[MAILTMPLEN];
/* parse and lock mailbox */ /* parse and lock mailbox */
if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
unix_parse (stream,lock,LOCK_EX)) { unix_parse (stream,lock,LOCK_EX)) {
/* any unsaved changes? */ /* any unsaved changes? */
if (LOCAL->dirty && unix_rewrite (stream,NIL,lock)) { if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
if (!stream->silent) mm_log ("Checkpoint completed",NIL); if (!stream->silent) mm_log ("Checkpoint completed",NIL);
} }
/* no checkpoint needed, just unlock */ /* no checkpoint needed, just unlock */
else unix_unlock (LOCAL->fd,stream,lock); else unix_unlock (LOCAL->fd,stream,lock);
mail_unlock (stream); /* unlock the stream */ mail_unlock (stream); /* unlock the stream */
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
} }
} }
/* UNIX mail expunge mailbox /* UNIX mail expunge mailbox
* Accepts: MAIL stream * Accepts: MAIL stream
* sequence to expunge if non-NIL
* expunge options
* Returns: T, always
*/ */
void unix_expunge (MAILSTREAM *stream) long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
{ {
long ret;
unsigned long i; unsigned long i;
char lock[MAILTMPLEN]; char lock[MAILTMPLEN];
char *msg = NIL; char *msg = NIL;
/* parse and lock mailbox */ /* parse and lock mailbox */
if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && if (ret = (sequence ? ((options & EX_UID) ?
mail_uid_sequence (stream,sequence) :
mail_sequence (stream,sequence)) : LONGT) &&
LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
unix_parse (stream,lock,LOCK_EX)) { unix_parse (stream,lock,LOCK_EX)) {
/* count expunged messages if not dirty */ /* check expunged messages if not dirty */
if (!LOCAL->dirty) for (i = 1; i <= stream->nmsgs; i++) for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
MESSAGECACHE *elt = mail_elt (stream,i);
if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
}
if (!LOCAL->dirty) { /* not dirty and no expunged messages */ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
unix_unlock (LOCAL->fd,stream,lock); unix_unlock (LOCAL->fd,stream,lock);
msg = "No messages deleted, so no update needed"; msg = "No messages deleted, so no update needed";
} }
else if (unix_rewrite (stream,&i,lock)) { else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
else msg = "Mailbox checkpointed, but no messages expunged"; else msg = "Mailbox checkpointed, but no messages expunged";
} }
/* rewrite failed */ /* rewrite failed */
else unix_unlock (LOCAL->fd,stream,lock); else unix_unlock (LOCAL->fd,stream,lock);
mail_unlock (stream); /* unlock the stream */ mail_unlock (stream); /* unlock the stream */
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
if (msg && !stream->silent) mm_log (msg,NIL); if (msg && !stream->silent) mm_log (msg,NIL);
} }
else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WA RN); else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WA RN);
return ret;
} }
/* UNIX mail copy message(s) /* UNIX mail copy message(s)
* Accepts: MAIL stream * Accepts: MAIL stream
* sequence * sequence
* destination mailbox * destination mailbox
* copy options * copy options
* Returns: T if copy successful, else NIL * Returns: T if copy successful, else NIL
*/ */
long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long option s) long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long option s)
{ {
struct stat sbuf; struct stat sbuf;
int fd; int fd;
char *s,file[MAILTMPLEN],lock[MAILTMPLEN]; char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
struct utimbuf times; struct utimbuf times;
unsigned long i,j; unsigned long i,j;
MESSAGECACHE *elt; MESSAGECACHE *elt;
long ret = T; long ret = T;
mailproxycopy_t pc = mailproxycopy_t pc =
(mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
NIL : mail_parameters (NIL,GET_COPYUID,NIL));
SEARCHSET *source = cu ? mail_newsearchset () : NIL;
SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
MAILSTREAM *tstream = NIL;
if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
mail_sequence (stream,sequence))) return NIL; mail_sequence (stream,sequence))) return NIL;
/* make sure valid mailbox */ if (unix_valid (mailbox)) { /* make sure valid mailbox */
if (!unix_valid (mailbox)) switch (errno) { /* try to open rewrite */
if (!(tstream = mail_open_work (&unixdriver,NIL,mailbox,
OP_SILENT|OP_NOKOD)))
/* failing that, settle for a sniff */
tstream = mail_open_work (&unixdriver,NIL,mailbox,
OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF);
}
else switch (errno) {
case ENOENT: /* no such file? */ case ENOENT: /* no such file? */
if (!compare_cstring (mailbox,"INBOX")) { if (compare_cstring (mailbox,"INBOX")) {
if (pc) return (*pc) (stream,sequence,mailbox,options); mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
unix_create (NIL,"INBOX");/* create empty INBOX */ return NIL;
break;
} }
mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); if (pc) return (*pc) (stream,sequence,mailbox,options);
return NIL; unix_create (NIL,"INBOX");/* create empty INBOX */
case 0: /* merely empty file? */ case 0: /* merely empty file? */
tstream = &unixproto;
break; break;
case EINVAL: case EINVAL:
if (pc) return (*pc) (stream,sequence,mailbox,options); if (pc) return (*pc) (stream,sequence,mailbox,options);
sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox); sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
mm_log (LOCAL->buf,ERROR); mm_log (LOCAL->buf,ERROR);
return NIL; return NIL;
default: default:
if (pc) return (*pc) (stream,sequence,mailbox,options); if (pc) return (*pc) (stream,sequence,mailbox,options);
sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox); sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
mm_log (LOCAL->buf,ERROR); mm_log (LOCAL->buf,ERROR);
skipping to change at line 800 skipping to change at line 834
mm_critical (stream); /* go critical */ mm_critical (stream); /* go critical */
if ((fd = unix_lock (dummy_file (file,mailbox), if ((fd = unix_lock (dummy_file (file,mailbox),
O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
lock,LOCK_EX)) < 0) { lock,LOCK_EX)) < 0) {
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errn o)); sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errn o));
mm_log (LOCAL->buf,ERROR); /* log the error */ mm_log (LOCAL->buf,ERROR); /* log the error */
return NIL; /* failed */ return NIL; /* failed */
} }
fstat (fd,&sbuf); /* get current file size */ fstat (fd,&sbuf); /* get current file size */
/* can't do COPYUID if no UIDVALIDITY */
if (!tstream->uid_validity) cu = NIL;
/* write all requested messages to mailbox * / /* write all requested messages to mailbox * /
for (i = 1; ret && (i <= stream->nmsgs); i++) for (i = 1; ret && (i <= stream->nmsgs); i++)
if ((elt = mail_elt (stream,i))->sequence) { if ((elt = mail_elt (stream,i))->sequence) {
lseek (LOCAL->fd,elt->private.special.offset,L_SET); lseek (LOCAL->fd,elt->private.special.offset,L_SET);
read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') { if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
LOCAL->buf[j - 1] = '\r'; LOCAL->buf[j - 1] = '\r';
LOCAL->buf[j++] = '\n'; LOCAL->buf[j++] = '\n';
} }
if (write (fd,LOCAL->buf,j) < 0) ret = NIL; if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
else { /* internal header succeeded */ else { /* internal header succeeded */
s = unix_header (stream,i,&j,NIL); s = unix_header (stream,i,&j,NIL);
/* header size, sans trailing newline */ /* header size, sans trailing newline */
if (j && (s[j - 4] == '\r')) j -= 2; if (j && (s[j - 4] == '\r')) j -= 2;
if (write (fd,s,j) < 0) ret = NIL; if (write (fd,s,j) < 0) ret = NIL;
else { /* message header succeeded */ else { /* message header succeeded */
j = unix_xstatus (stream,LOCAL->buf,elt,NIL); j = unix_xstatus (stream,LOCAL->buf,elt,
cu ? ++(tstream->uid_last) : NIL,cu ? LONGT : NI
L);
if (write (fd,LOCAL->buf,j) < 0) ret = NIL; if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
else { /* message status succeeded */ else { /* message status succeeded */
s = unix_text_work (stream,elt,&j,NIL); s = unix_text_work (stream,elt,&j,NIL);
if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0)) if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
ret = NIL; ret = NIL;
else if (cu) { /* need to pass back new UID? */
mail_append_set (source,mail_uid (stream,i));
mail_append_set (dest,tstream->uid_last);
}
} }
} }
} }
} }
if (!ret || fsync (fd)) { /* force out the update */ if (!ret || fsync (fd)) { /* force out the update */
sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
ftruncate (fd,sbuf.st_size); ftruncate (fd,sbuf.st_size);
ret = NIL; ret = NIL;
} }
/* return sets if doing COPYUID */
if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
else { /* flush any sets we may have built */
mail_free_searchset (&source);
mail_free_searchset (&dest);
}
times.modtime = time (0); /* set mtime to now */ times.modtime = time (0); /* set mtime to now */
/* set atime to now-1 if successful copy */ /* set atime to now-1 if successful copy */
if (ret) times.actime = times.modtime - 1; if (ret) times.actime = times.modtime - 1;
else times.actime = /* else preserve \Marked status */ else times.actime = /* else preserve \Marked status */
((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime) ) ? ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime) ) ?
sbuf.st_atime : times.modtime; sbuf.st_atime : times.modtime;
utime (file,&times); /* set the times */ utime (file,&times); /* set the times */
unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
mm_nocritical (stream); /* release critical */ mm_nocritical (stream); /* release critical */
/* update last UID if we can */
if (!tstream->rdonly) ((UNIXLOCAL *) tstream->local)->dirty = T;
tstream = mail_close (tstream);
/* log the error */ /* log the error */
if (!ret) mm_log (LOCAL->buf,ERROR); if (!ret) mm_log (LOCAL->buf,ERROR);
/* delete if requested message */ /* delete if requested message */
else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
if ((elt = mail_elt (stream,i))->sequence) if ((elt = mail_elt (stream,i))->sequence)
elt->deleted = elt->private.dirty = LOCAL->dirty = T; elt->deleted = elt->private.dirty = LOCAL->dirty = T;
return ret; return ret;
} }
/* UNIX mail append message from stringstruct /* UNIX mail append message from stringstruct
* Accepts: MAIL stream * Accepts: MAIL stream
skipping to change at line 863 skipping to change at line 913
* data for callback * data for callback
* Returns: T if append successful, else NIL * Returns: T if append successful, else NIL
*/ */
#define BUFLEN 8*MAILTMPLEN #define BUFLEN 8*MAILTMPLEN
long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
{ {
struct stat sbuf; struct stat sbuf;
int fd; int fd;
unsigned long i,j; unsigned long i;
char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN], char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
lock[MAILTMPLEN]; lock[MAILTMPLEN];
struct utimbuf times; struct utimbuf times;
FILE *sf,*df; FILE *sf,*df;
MESSAGECACHE elt; MESSAGECACHE elt;
STRING *message; STRING *message;
unsigned long uidlocation = 0;
appenduid_t au = (appenduid_t)
(mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
mail_parameters (NIL,GET_APPENDUID,NIL));
SEARCHSET *dst = au ? mail_newsearchset () : NIL;
long ret = LONGT; long ret = LONGT;
MAILSTREAM *tstream = NIL;
if (!stream) { /* stream specified? */ if (!stream) { /* stream specified? */
stream = &unixproto; /* no, default stream to prototype */ stream = &unixproto; /* no, default stream to prototype */
for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
fs_give ((void **) &stream->user_flags[i]); fs_give ((void **) &stream->user_flags[i]);
stream->kwd_create = T; /* allow new flags */ stream->kwd_create = T; /* allow new flags */
} }
/* make sure valid mailbox */ if (unix_valid (mailbox)) { /* make sure valid mailbox */
if (!unix_valid (mailbox)) switch (errno) { if (!(tstream = mail_open (NIL,mailbox,
OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
MM_LOG (tmp,ERROR);
return NIL;
}
}
else switch (errno) {
case ENOENT: /* no such file? */ case ENOENT: /* no such file? */
if (!compare_cstring (mailbox,"INBOX")) { if (!compare_cstring (mailbox,"INBOX")) {
unix_create (NIL,"INBOX");/* create empty INBOX */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL
break; );
return NIL;
} }
mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); unix_create (NIL,"INBOX"); /* create empty INBOX */
return NIL;
case 0: /* merely empty file? */ case 0: /* merely empty file? */
tstream = stream;
break; break;
case EINVAL: case EINVAL:
sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox); sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
return NIL; return NIL;
default: default:
sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox); sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
return NIL; return NIL;
} }
/* get first message */ /* get first message */
if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
if (!(sf = tmpfile ())) { /* must have scratch file */ if (!(sf = tmpfile ())) { /* must have scratch file */
sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)) ; sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)) ;
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
return NIL; return NIL;
} }
unlink (tmp); unlink (tmp);
} }
do { /* parse date */ do { /* parse date */
skipping to change at line 922 skipping to change at line 985
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
} }
else { /* user wants to suppress time zones? */ else { /* user wants to suppress time zones? */
if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
time_t when = mail_longdate (&elt); time_t when = mail_longdate (&elt);
date = ctime (&when); /* use traditional date */ date = ctime (&when); /* use traditional date */
} }
/* use POSIX-style date */ /* use POSIX-style date */
else date = mail_cdate (tmp,&elt); else date = mail_cdate (tmp,&elt);
if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR); if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
else if (!unix_append_msg (stream,sf,flags,date,message)) { else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
} }
/* get next message */ /* get next message */
else if ((*af) (stream,data,&flags,&date,&message)) continue; else if ((*af) (tstream,data,&flags,&date,&message)) continue;
} }
fclose (sf); /* punt scratch file */ fclose (sf); /* punt scratch file */
return NIL; /* give up */ return NIL; /* give up */
} while (message); /* until no more messages */ } while (message); /* until no more messages */
if (fflush (sf) || fstat (fileno (sf),&sbuf)) { if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
fclose (sf); /* punt scratch file */ fclose (sf); /* punt scratch file */
return NIL; /* give up */ return NIL; /* give up */
} }
i = sbuf.st_size; /* size of scratch file */ i = sbuf.st_size; /* size of scratch file */
/* close sniffing stream */
if (tstream != stream) tstream = mail_close (tstream);
mm_critical (stream); /* go critical */ mm_critical (stream); /* go critical */
/* try to open readwrite */
if (!(tstream = mail_open_work (&unixdriver,NIL,mailbox,
OP_SILENT|OP_NOKOD))) {
/* damn, someone else has it open */
if (!(tstream = mail_open_work (&unixdriver,NIL,mailbox,
OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF)
)){
sprintf (tmp,"Unable to re-sniff mailbox for APPEND: %.80s",mailbox);
MM_LOG (tmp,ERROR);
return NIL;
}
}
if (((fd = unix_lock (dummy_file (file,mailbox), if (((fd = unix_lock (dummy_file (file,mailbox),
O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE, O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
mm_log (tmp,ERROR); mm_log (tmp,ERROR);
return NIL; return NIL;
} }
fstat (fd,&sbuf); /* get current file size */ fstat (fd,&sbuf); /* get current file size */
/* can't do APPENDUID if no UIDVALIDITY */
if (!tstream->uid_validity) au = NIL;
rewind (sf); rewind (sf);
for (; i && ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) &&
(fwrite (buf,1,j,df) == j)); i -= j);
fclose (sf); /* done with scratch file */
times.modtime = time (0); /* set mtime to now */ times.modtime = time (0); /* set mtime to now */
/* make sure append wins, fsync() necessary /* write all messages */
*/ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
if (i || (fflush (df) == EOF) || fsync (fd)) { (fflush (df) == EOF) || fsync (fd)) {
sprintf (buf,"Message append failed: %s",strerror (errno)); sprintf (buf,"Message append failed: %s",strerror (errno));
mm_log (buf,ERROR); mm_log (buf,ERROR);
ftruncate (fd,sbuf.st_size);/* revert file */ ftruncate (fd,sbuf.st_size);/* revert file */
times.actime = /* preserve \Marked status */ times.actime = /* preserve \Marked status */
((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
sbuf.st_atime : times.modtime; sbuf.st_atime : times.modtime;
ret = NIL; /* return error */ ret = NIL; /* return error */
} }
/* set atime to now-1 if successful copy */ /* set atime to now-1 if successful copy */
else times.actime = times.modtime - 1; else times.actime = times.modtime - 1;
utime (file,&times); /* set the times */ utime (file,&times); /* set the times */
fclose (sf); /* done with scratch file */
/* return sets if doing APPENDUID */
if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
else mail_free_searchset (&dst);
unix_unlock (fd,NIL,lock); /* unlock and close mailbox */ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
fclose (df); /* note that unix_unlock() released the fd * / fclose (df); /* note that unix_unlock() released the fd * /
mm_nocritical (stream); /* release critical */ mm_nocritical (stream); /* release critical */
/* update last UID if we can */
if (!tstream->rdonly) ((UNIXLOCAL *) tstream->local)->dirty = T;
tstream = mail_close (tstream);
return ret; return ret;
} }
/* Write single message to append scratch file /* Collect and write single message to append scratch file
* Accepts: MAIL stream * Accepts: MAIL stream
* scratch file * scratch file
* flags * flags
* message stringstruct * message stringstruct
* Returns: NIL if write error, else T * Returns: NIL if write error, else T
*/ */
int unix_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
STRING *msg) STRING *msg)
{ {
unsigned char *s,*t;
unsigned long uf;
long f = mail_parse_flags (stream,flags,&uf);
/* write metadata, note date ends with NL */
if (fprintf (sf,"%ld %lu %lu %s",f,uf,SIZE (msg) + 1,date) < 0) return NI
L;
while (SIZE (msg)) { /* copy text to scratch file */
for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++
s)
if (!*s) *s = 0x80; /* disallow NUL */
/* write buffered text */
if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
SETPOS (msg,GETPOS (msg) + msg->cursize);
else return NIL; /* failed */
}
/* write trailing newline and return */
return (putc ('\n',sf) == EOF) ? NIL : T;
}
/* Append messages from scratch file to mailbox
* Accepts: MAIL stream
* source file
* destination file
* uidset to update if non-NIL
* Returns: T if success, NIL if failure
*/
int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
{
int ti,zn,c; int ti,zn,c;
unsigned long i,uf; long f;
unsigned long i,j,uf;
char *x,tmp[MAILTMPLEN]; char *x,tmp[MAILTMPLEN];
int hdrp = T; int hdrp = T;
long f = mail_parse_flags (stream,flags,&uf); /* get message metadata line */
/* tie off date without newline */ while (fgets (tmp,MAILTMPLEN,sf)) {
if (x = strpbrk (strcpy (tmp,date),"\r\n")) *x = '\0'; if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
/* build initial header */ f = strtol (tmp,&x,10); /* get flags */
if ((fprintf (sf,"From %s@%s %s\r\nStatus: ", if (!((*x++ == ' ') && isdigit (*x))) return NIL;
myusername (),mylocalhost (),tmp) < 0) || uf = strtoul (x,&x,10); /* get keywords */
(f&fSEEN && (putc ('R',sf) == EOF)) || if (!((*x++ == ' ') && isdigit (*x))) return NIL;
(fputs ("\r\nX-Status: ",sf) == EOF) || i = strtoul (x,&x,10); /* get message size */
(f&fDELETED && (putc ('D',sf) == EOF)) || if ((*x++ != ' ') || /* build initial header */
(f&fFLAGGED && (putc ('F',sf) == EOF)) || (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0
(f&fANSWERED && (putc ('A',sf) == EOF)) || )||
(f&fDRAFT && (putc ('T',sf) == EOF)) || (f&fSEEN && (putc ('R',df) == EOF)) ||
(fputs ("\r\nX-Keywords:",sf) == EOF)) return NIL; (fputs ("\nX-Status: ",df) == EOF) ||
while (uf) /* write user flags */ (f&fDELETED && (putc ('D',df) == EOF)) ||
if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0 (f&fFLAGGED && (putc ('F',df) == EOF)) ||
) (f&fANSWERED && (putc ('A',df) == EOF)) ||
(f&fDRAFT && (putc ('T',df) == EOF)) ||
(fputs ("\nX-Keywords:",df) == EOF)) return NIL;
while (uf) /* write user flags */
if (fprintf (df," %s",stream->user_flags[find_rightmost_bit (&uf)]) <
0)
return NIL;
if (set && (fprintf (df,"\nX-UID: %lu",++(stream->uid_last)) < 0))
return NIL; return NIL;
/* tie off flags */ /* finish last metadata header */
if ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) return NIL; if (putc ('\n',df) == EOF) return NIL;
while (SIZE (msg)) { /* copy text to scratch file */ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
/* disregard CRs */ /* get read line length */
while ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') == '\r'); if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
/* see if line needs special treatment */ i -= j; /* number of bytes left */
if ((c == 'F') || (hdrp && ((c == 'S') || (c == 'X')))) { if (!j) continue; /* do nothing if line emptied */
/* copy line to buffer */ /* complete line? */
for (i = 1,tmp[0] = c; (c != '\n') && (i < (MAILTMPLEN - 1)); ) if ((c == '\n')) switch (tmp[0]) {
switch (c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') { case 'F': /* possible "From " (case counts her
case '\r': /* ignore CR */ e) */
break; if ((i > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o')
case '\n': /* write CRLF for NL */ &&
tmp[i++] = '\r'; (tmp[3] == 'm') && (tmp[4] == ' ')) {
default: /* other characters */ if (!unix_fromwidget) {
tmp[i++] = c; VALID (tmp,x,ti,zn);/* conditional, only write widget if */
if (!ti) break; /* it looks like a valid header */
} /* write the widget */
if (putc ('>',df) == EOF) return NIL;
} }
if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') break;
&& case 'S': case 's': /* possible "Status:" */
(tmp[4] == ' ')) { /* possible "From " line? */ if (hdrp && (i > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
/* yes, see if need to write a widget */ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
if (!(ti = unix_fromwidget || (c != '\n'))) VALID (tmp,x,ti,zn); ((tmp[3] == 't') || (tmp[3] == 'T')) &&
if (ti && (putc ('>',sf) == EOF)) return NIL; ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
} ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
/* insert X- before metadata header */ (fputs ("X-Original-",df) == EOF)) return NIL;
else if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && case 'X': case 'x': /* possible X-??? header */
(tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && if (hdrp && (tmp[1] == '-') &&
(tmp[5] == 's') && (tmp[6] == ':')) || /* possible X-UID: */
(((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((i > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
(((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
(tmp[5] == ':')) || ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && /* possible X-IMAP: */
(tmp[4] == 'A') && (tmp[5] == 'P') && ((i > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
((tmp[6] == ':') || ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
(tmp[8] == 's') && (tmp[9] == 'e') && ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
(tmp[10] == ':')))) || ((tmp[6] == ':') ||
((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && /* or X-IMAPbase: */
(tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && ((i > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
(tmp[7] == 's') && (tmp[8] == ':')) || ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && ((tmp[8] == 's') || (tmp[8] == 'S')) &&
(tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':'))))
(tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && ||
(tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF) /* possible X-Status: */
) ((i > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
return NIL; ((tmp[3] == 't') || (tmp[3] == 'T')) &&
/* write buffered text */ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
if (fwrite (tmp,1,i,sf) != i) return NIL; ((tmp[5] == 't') || (tmp[5] == 'T')) &&
/* set up to copy remainder of line */ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
else continue; /* end of line or end of message */ /* possible X-Keywords: */
} ((i > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
/* check for end of header */ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
if (hdrp && (c == '\n')) hdrp = NIL; ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
do switch (c) { /* copy line, writing CRLF for NL */ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
case '\r': /* ignore CR */ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
break; ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
case '\n': /* insert CR before LF */ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
if (putc ('\r',sf) == EOF) return NIL; ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
default: (fputs ("X-Original-",df) == EOF)) return NIL;
if (putc (c,sf) == EOF) return NIL; default: /* nothing to do */
break;
}
/* just write the line */
if (fwrite (tmp,1,j,df) != j) return NIL;
} }
while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); if (i) return NIL; /* didn't read entire message */
/* update set */
mail_append_set (set,stream->uid_last);
} }
/* write trailing newline and return */ return T;
return ((putc ('\r', sf) == EOF) || (putc ('\n',sf) == EOF)) ? NIL : T;
} }
/* Internal routines */ /* Internal routines */
/* UNIX mail abort stream /* UNIX mail abort stream
* Accepts: MAIL stream * Accepts: MAIL stream
*/ */
void unix_abort (MAILSTREAM *stream) void unix_abort (MAILSTREAM *stream)
{ {
if (LOCAL) { /* only if a file is open */ if (LOCAL) { /* only if a file is open */
skipping to change at line 1236 skipping to change at line 1359
unix_abort (stream); unix_abort (stream);
mail_unlock (stream); mail_unlock (stream);
mm_nocritical (stream); /* done with critical */ mm_nocritical (stream); /* done with critical */
return NIL; return NIL;
} }
/* new data? */ /* new data? */
else if (i = sbuf.st_size - LOCAL->filesize) { else if (i = sbuf.st_size - LOCAL->filesize) {
d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.fd = LOCAL->fd; /* yes, set up file descriptor */
d.pos = LOCAL->filesize; /* get to that position in the file */ d.pos = LOCAL->filesize; /* get to that position in the file */
d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunk = LOCAL->buf; /* initial buffer chunk */
d.chunksize = CHUNK; /* file chunk size */ d.chunksize = CHUNKSIZE; /* file chunk size */
INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
/* skip leading whitespace for broken MTAs * / /* skip leading whitespace for broken MTAs * /
while (((c = CHR (&bs)) == '\n') || (c == '\r') || while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
(c == ' ') || (c == '\t')) SNX (&bs); (c == ' ') || (c == '\t')) SNX (&bs);
if (SIZE (&bs)) { /* read new data */ if (SIZE (&bs)) { /* read new data */
/* remember internal header position */ /* remember internal header position */
j = LOCAL->filesize + GETPOS (&bs); j = LOCAL->filesize + GETPOS (&bs);
s = unix_mbxline (stream,&bs,&i); s = unix_mbxline (stream,&bs,&i);
t = NIL,zn = 0; t = NIL,zn = 0;
if (i) VALID (s,t,ti,zn); /* see if valid From line */ if (i) VALID (s,t,ti,zn); /* see if valid From line */
skipping to change at line 1320 skipping to change at line 1443
s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
s[8] == 'd' && s[9] == 's' && s[10] == ':') { s[8] == 'd' && s[9] == 's' && s[10] == ':') {
SIZEDTEXT uf; SIZEDTEXT uf;
retain = NIL; /* don't retain continuation */ retain = NIL; /* don't retain continuation */
s += 11; /* flush leading whitespace */ s += 11; /* flush leading whitespace */
while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n') )){ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n') )){
while (*s == ' ') s++; while (*s == ' ') s++;
/* find end of keyword */ /* find end of keyword */
if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
/* got a keyword? */ /* got a keyword? */
if ((k = (u - s)) && (k < MAXUSERFLAG)) { if ((k = (u - s)) <= MAXUSERFLAG) {
uf.data = (unsigned char *) s; uf.data = (unsigned char *) s;
uf.size = k; uf.size = k;
for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; + +j) for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; + +j)
if (!compare_csizedtext (stream->user_flags[j],&uf)) { if (!compare_csizedtext (stream->user_flags[j],&uf)) {
elt->user_flags |= ((long) 1) << j; elt->user_flags |= ((long) 1) << j;
break; break;
} }
} }
s = u; /* advance to next keyword */ s = u; /* advance to next keyword */
} }
skipping to change at line 1373 skipping to change at line 1496
} }
/* save UID last */ /* save UID last */
stream->uid_last = j; stream->uid_last = j;
/* process keywords */ /* process keywords */
for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n' )); for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n' ));
s = u,j++) { s = u,j++) {
/* flush leading whitespace */ /* flush leading whitespace */
while (*s == ' ') s++; while (*s == ' ') s++;
u = strpbrk (s," \n\r"); u = strpbrk (s," \n\r");
/* got a keyword? */ /* got a keyword? */
if ((k = (u - s)) && j < NUSERFLAGS) { if ((j < NUSERFLAGS) && ((k = (u - s)) <= MAXUSERFLAG) ) {
if (stream->user_flags[j]) if (stream->user_flags[j])
fs_give ((void **) &stream->user_flags[j]); fs_give ((void **) &stream->user_flags[j]);
stream->user_flags[j] = (char *) fs_get (k + 1); stream->user_flags[j] = (char *) fs_get (k + 1);
strncpy (stream->user_flags[j],s,k); strncpy (stream->user_flags[j],s,k);
stream->user_flags[j][k] = '\0'; stream->user_flags[j][k] = '\0';
} }
} }
} }
} }
break; break;
skipping to change at line 1412 skipping to change at line 1535
/* make sure not duplicated */ /* make sure not duplicated */
if (elt->private.uid) if (elt->private.uid)
sprintf (tmp,"Message %lu UID %lu already has UID %lu", sprintf (tmp,"Message %lu UID %lu already has UID %lu",
pseudoseen ? elt->msgno - 1 : elt->msgno, pseudoseen ? elt->msgno - 1 : elt->msgno,
j,elt->private.uid); j,elt->private.uid);
/* make sure UID doesn't go backwards */ /* make sure UID doesn't go backwards */
else if (j <= prevuid) else if (j <= prevuid)
sprintf (tmp,"Message %lu UID %lu less than %lu", sprintf (tmp,"Message %lu UID %lu less than %lu",
pseudoseen ? elt->msgno - 1 : elt->msgno, pseudoseen ? elt->msgno - 1 : elt->msgno,
j,prevuid + 1); j,prevuid + 1);
#if 0 /* this is currently broken by UIDPLUS */
/* or skip by mailbox's recorded last */ /* or skip by mailbox's recorded last */
else if (j > stream->uid_last) else if (j > stream->uid_last)
sprintf (tmp,"Message %lu UID %lu greater than last %lu" , sprintf (tmp,"Message %lu UID %lu greater than last %lu" ,
pseudoseen ? elt->msgno - 1 : elt->msgno, pseudoseen ? elt->msgno - 1 : elt->msgno,
j,stream->uid_last); j,stream->uid_last);
#endif
else { /* normal UID case */ else { /* normal UID case */
prevuid = elt->private.uid = j; prevuid = elt->private.uid = j;
#if 1 /* temporary kludge for UIDPLUS */
if (prevuid > stream->uid_last) {
stream->uid_last = prevuid;
LOCAL->ddirty = LOCAL->dirty = T;
}
#endif
break; /* exit this cruft */ break; /* exit this cruft */
} }
mm_log (tmp,WARN); mm_log (tmp,WARN);
/* invalidate UID validity */ /* invalidate UID validity */
stream->uid_validity = 0; stream->uid_validity = 0;
elt->private.uid = 0; elt->private.uid = 0;
} }
break; break;
} }
} }
skipping to change at line 1495 skipping to change at line 1626
retain = NIL; /* don't retain continuation */ retain = NIL; /* don't retain continuation */
break; /* different case or something */ break; /* different case or something */
} }
} }
/* retain or non-continuation? */ /* retain or non-continuation? */
if (retain || ((*s != ' ') && (*s != '\t'))) { if (retain || ((*s != ' ') && (*s != '\t'))) {
retain = T; /* retaining continuation now */ retain = T; /* retaining continuation now */
/* line length in CRLF format newline */ /* line length in CRLF format newline */
k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0); k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
/* "internal" header size */ /* "internal" header size */
elt->private.data += k; elt->private.spare.data += k;
/* message size */ /* message size */
elt->rfc822_size += k; elt->rfc822_size += k;
} }
else { else {
char err[MAILTMPLEN]; char err[MAILTMPLEN];
sprintf (err,"Discarding bogus continuation in msg %lu: %.80s" , sprintf (err,"Discarding bogus continuation in msg %lu: %.80s" ,
elt->msgno,(char *) s); elt->msgno,(char *) s);
if (u = strpbrk (err,"\r\n")) *u = '\0'; if (u = strpbrk (err,"\r\n")) *u = '\0';
mm_log (err,WARN); mm_log (err,WARN);
break; /* different case or something */ break; /* different case or something */
} }
break; break;
} }
} while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
/* "internal" header sans trailing newline * / /* "internal" header sans trailing newline * /
if (i) elt->private.data -= 2; if (i) elt->private.spare.data -= 2;
/* assign a UID if none found */ /* assign a UID if none found */
if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
prevuid = elt->private.uid = ++stream->uid_last; prevuid = elt->private.uid = ++stream->uid_last;
elt->private.dirty = T; elt->private.dirty = T;
} }
else elt->private.dirty = elt->recent; else elt->private.dirty = elt->recent;
/* note size of header, location of text */ /* note size of header, location of text */
elt->private.msg.header.text.size = elt->private.msg.header.text.size =
(elt->private.msg.text.offset = (elt->private.msg.text.offset =
(LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
skipping to change at line 1544 skipping to change at line 1675
j = LOCAL->filesize + GETPOS (&bs); j = LOCAL->filesize + GETPOS (&bs);
} }
} }
} while (i && !ti); /* until found a header */ } while (i && !ti); /* until found a header */
elt->private.msg.text.text.size = j - elt->private.msg.text.text.size = j -
(elt->private.special.offset + elt->private.msg.text.offset); (elt->private.special.offset + elt->private.msg.text.offset);
if (k == 2) { /* last line was blank? */ if (k == 2) { /* last line was blank? */
elt->private.msg.text.text.size -= (m ? 1 : 2); elt->private.msg.text.text.size -= (m ? 1 : 2);
elt->rfc822_size -= 2; elt->rfc822_size -= 2;
} }
} while (i); /* until end of buffer */ /* until end of buffer */
} while (!stream->sniff && i);
if (pseudoseen) { /* flush pseudo-message if present * / if (pseudoseen) { /* flush pseudo-message if present * /
/* decrement recent count */ /* decrement recent count */
if (mail_elt (stream,1)->recent) recent--; if (mail_elt (stream,1)->recent) recent--;
/* and the exists count */ /* and the exists count */
mail_exists (stream,nmsgs--); mail_exists (stream,nmsgs--);
mail_expunged(stream,1);/* fake an expunge of that message */ mail_expunged(stream,1);/* fake an expunge of that message */
} }
/* need to start a new UID validity? */ /* need to start a new UID validity? */
if (!stream->uid_validity) { if (!stream->uid_validity) {
stream->uid_validity = time (0); stream->uid_validity = (unsigned long) time (0);
if (nmsgs) { /* don't bother if empty file */ if (nmsgs) { /* don't bother if empty file */
LOCAL->dirty = T; /* make dirty to restart UID epoch */ /* make dirty to restart UID epoch */
LOCAL->ddirty = LOCAL->dirty = T;
/* need to rewrite msg 1 if not pseudo */ /* need to rewrite msg 1 if not pseudo */
if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
mm_log ("Assigning new unique identifiers to all messages",NIL); mm_log ("Assigning new unique identifiers to all messages",NIL);
} }
} }
stream->nmsgs = oldnmsgs; /* whack it back down */ stream->nmsgs = oldnmsgs; /* whack it back down */
stream->silent = silent; /* restore old silent setting */ stream->silent = silent; /* restore old silent setting */
/* notify upper level of new mailbox sizes * / /* notify upper level of new mailbox sizes * /
mail_exists (stream,nmsgs); mail_exists (stream,nmsgs);
mail_recent (stream,recent); mail_recent (stream,recent);
skipping to change at line 1589 skipping to change at line 1722
/* UNIX read line from mailbox /* UNIX read line from mailbox
* Accepts: mail stream * Accepts: mail stream
* stringstruct * stringstruct
* pointer to line size * pointer to line size
* Returns: pointer to input line * Returns: pointer to input line
*/ */
char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
{ {
unsigned long i,j,k,m; unsigned long i,j,k,m;
char *s,*t,*te,p1[CHUNK]; char *s,*t,*te;
char *tmp = (char *) fs_get (CHUNKSIZE);
unsigned long tmplen = CHUNKSIZE;
char *ret = ""; char *ret = "";
/* flush old buffer */ /* flush old buffer */
if (LOCAL->line) fs_give ((void **) &LOCAL->line); if (LOCAL->line) fs_give ((void **) &LOCAL->line);
/* if buffer needs refreshing */ /* if buffer needs refreshing */
if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (!bs->cursize) SETPOS (bs,GETPOS (bs));
if (SIZE (bs)) { /* find newline */ if (SIZE (bs)) { /* find newline */
/* end of fast scan */ /* end of fast scan */
te = (t = (s = bs->curpos) + bs->cursize) - 12; te = (t = (s = bs->curpos) + bs->cursize) - 12;
while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') | | while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') | |
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
--s; /* back up */ --s; /* back up */
break; /* exit loop */ break; /* exit loop */
} }
/* final character-at-a-time scan */ /* final character-at-a-time scan */
while ((s < t) && (*s != '\n')) ++s; while ((s < t) && (*s != '\n')) ++s;
/* difficult case if line spans buffer */ /* difficult case if line spans buffer */
if ((i = s - bs->curpos) == bs->cursize) { if ((i = s - bs->curpos) == bs->cursize) {
memcpy (p1,bs->curpos,i); /* remember what we have so far */ if (i > tmplen) { /* have space in tmp buffer? */
fs_give ((void **) &tmp);
tmp = (char *) fs_get (tmplen = i);
}
memcpy (tmp,bs->curpos,i);/* remember what we have so far */
/* load next buffer */ /* load next buffer */
SETPOS (bs,k = GETPOS (bs) + i); SETPOS (bs,k = GETPOS (bs) + i);
/* end of fast scan */ /* end of fast scan */
te = (t = (s = bs->curpos) + bs->cursize) - 12; te = (t = (s = bs->curpos) + bs->cursize) - 12;
/* fast scan in overlap buffer */ /* fast scan in overlap buffer */
while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
(*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
--s; /* back up */ --s; /* back up */
skipping to change at line 1633 skipping to change at line 1772
while ((s < t) && (*s != '\n')) ++s; while ((s < t) && (*s != '\n')) ++s;
/* huge line? */ /* huge line? */
if ((j = s - bs->curpos) == bs->cursize) { if ((j = s - bs->curpos) == bs->cursize) {
SETPOS (bs,GETPOS (bs) + j); SETPOS (bs,GETPOS (bs) + j);
/* look for end of line (s-l-o-w!!) */ /* look for end of line (s-l-o-w!!) */
for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
SETPOS (bs,k); /* go back to where it started */ SETPOS (bs,k); /* go back to where it started */
} }
/* got size of data, make buffer for return */ /* got size of data, make buffer for return */
ret = LOCAL->line = (char *) fs_get (i + j + 2); ret = LOCAL->line = (char *) fs_get (i + j + 2);
memcpy (ret,p1,i); /* copy first chunk */ memcpy (ret,tmp,i); /* copy first chunk */
while (j) { /* copy remainder */ while (j) { /* copy remainder */
if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (!bs->cursize) SETPOS (bs,GETPOS (bs));
memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
i += k; /* account for this much read in */ i += k; /* account for this much read in */
j -= k; j -= k;
bs->curpos += k; /* increment new position */ bs->curpos += k; /* increment new position */
bs->cursize -= k; /* eat that many bytes */ bs->cursize -= k; /* eat that many bytes */
} }
if (!bs->cursize) SETPOS (bs,GETPOS (bs)); if (!bs->cursize) SETPOS (bs,GETPOS (bs));
if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */ if (SIZE (bs)) SNX (bs); /* skip over newline if one seen */
skipping to change at line 1655 skipping to change at line 1794
ret[i] = '\0'; /* makes debugging easier */ ret[i] = '\0'; /* makes debugging easier */
} }
else { /* this is easy */ else { /* this is easy */
ret = bs->curpos; /* string it at this position */ ret = bs->curpos; /* string it at this position */
bs->curpos += ++i; /* increment new position */ bs->curpos += ++i; /* increment new position */
bs->cursize -= i; /* eat that many bytes */ bs->cursize -= i; /* eat that many bytes */
} }
*size = i; /* return that to user */ *size = i; /* return that to user */
} }
else *size = 0; /* end of data, return empty */ else *size = 0; /* end of data, return empty */
fs_give ((void **) &tmp);
return ret; return ret;
} }
/* UNIX make pseudo-header /* UNIX make pseudo-header
* Accepts: MAIL stream * Accepts: MAIL stream
* buffer to write pseudo-header * buffer to write pseudo-header
* Returns: length of pseudo-header * Returns: length of pseudo-header
*/ */
unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr) unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
{ {
skipping to change at line 1688 skipping to change at line 1828
for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++) for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
if (*s == '\n') *t++ = '\r'; if (*s == '\n') *t++ = '\r';
*t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
*t = '\0'; /* tie off pseudo header */ *t = '\0'; /* tie off pseudo header */
return t - hdr; /* return length of pseudo header */ return t - hdr; /* return length of pseudo header */
} }
/* UNIX make status string /* UNIX make status string
* Accepts: MAIL stream * Accepts: MAIL stream
* destination string to write * destination string to write
* message cache entry * message cache entry
* UID to write if non-zero (else use elt->private.uid)
* non-zero flag to write UID (.LT. 0 to write UID base info too) * non-zero flag to write UID (.LT. 0 to write UID base info too)
* Returns: length of string * Returns: length of string
*/ */
unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *e lt, unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *e lt,
long flag) unsigned long uid,long flag)
{ {
char *t,stack[64]; char *t,stack[64];
char *s = status; char *s = status;
unsigned long n; unsigned long n;
unsigned long pad = 50; unsigned long pad = 50;
/* This used to use sprintf(), but thanks to certain cretinous C librarie s /* This used to use sprintf(), but thanks to certain cretinous C librarie s
with horribly slow implementations of sprintf() I had to change it to this with horribly slow implementations of sprintf() I had to change it to this
mess. At least it should be fast. */ mess. At least it should be fast. */
/* need to write X-IMAPbase: header? */
if ((flag < 0) && !stream->uid_nosticky) {
*s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
*s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
t = stack;
n = stream->uid_validity; /* push UID validity digits on the stack */
do *t++ = (char) (n % 10) + '0';
while (n /= 10);
/* pop UID validity digits from stack */
while (t > stack) *s++ = *--t;
*s++ = ' ';
n = stream->uid_last; /* push UID last digits on the stack */
do *t++ = (char) (n % 10) + '0';
while (n /= 10);
/* pop UID last digits from stack */
while (t > stack) *s++ = *--t;
for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
for (*s++ = ' '; *t; *s++ = *t++);
*s++ = '\r'; *s++ = '\n';
pad += 30; /* increased padding if have IMAPbase */
}
*s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
*s++ = ':'; *s++ = ' '; *s++ = ':'; *s++ = ' ';
if (elt->seen) *s++ = 'R'; if (elt->seen) *s++ = 'R';
if (flag) *s++ = 'O'; /* only write O if have a UID */ if (flag) *s++ = 'O'; /* only write O if have a UID */
*s++ = '\r'; *s++ = '\n'; *s++ = '\r'; *s++ = '\n';
*s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
*s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
if (elt->deleted) *s++ = 'D'; if (elt->deleted) *s++ = 'D';
if (elt->flagged) *s++ = 'F'; if (elt->flagged) *s++ = 'F';
if (elt->answered) *s++ = 'A'; if (elt->answered) *s++ = 'A';
skipping to change at line 1747 skipping to change at line 1867
if (n = elt->user_flags) do { if (n = elt->user_flags) do {
*s++ = ' '; *s++ = ' ';
for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
} while (n); } while (n);
n = s - status; /* get size of stuff so far */ n = s - status; /* get size of stuff so far */
/* pad X-Keywords to make size constant */ /* pad X-Keywords to make size constant */
if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
*s++ = '\r'; *s++ = '\n'; *s++ = '\r'; *s++ = '\n';
if (flag) { /* want to include UID? */ if (flag) { /* want to include UID? */
t = stack; t = stack;
n = elt->private.uid; /* push UID digits on the stack */ /* push UID digits on the stack */
n = uid ? uid : elt->private.uid;
do *t++ = (char) (n % 10) + '0'; do *t++ = (char) (n % 10) + '0';
while (n /= 10); while (n /= 10);
*s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
*s++ = ' '; *s++ = ' ';
/* pop UID from stack */ /* pop UID from stack */
while (t > stack) *s++ = *--t; while (t > stack) *s++ = *--t;
*s++ = '\r'; *s++ = '\n'; *s++ = '\r'; *s++ = '\n';
} }
/* end of extended message status */ /* end of extended message status */
*s++ = '\r'; *s++ = '\n'; *s = '\0'; *s++ = '\r'; *s++ = '\n'; *s = '\0';
return s - status; /* return size of resulting string */ return s - status; /* return size of resulting string */
} }
/* Rewrite mailbox file /* Rewrite mailbox file
* Accepts: MAIL stream, must be critical and locked * Accepts: MAIL stream, must be critical and locked
* return pointer to number of expunged messages if want expunge * return pointer to number of expunged messages if want expunge
* lock file name * lock file name
* expunge sequence, not deleted flag
* Returns: T if success and mailbox unlocked, NIL if failure * Returns: T if success and mailbox unlocked, NIL if failure
*/ */
#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ #define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock) long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
long flags)
{ {
MESSAGECACHE *elt; MESSAGECACHE *elt;
UNIXFILE f; UNIXFILE f;
char *s; char *s;
struct utimbuf times; struct utimbuf times;
long ret,flag; long ret,flag;
unsigned long i,j; unsigned long i,j;
unsigned long recent = stream->recent; unsigned long recent = stream->recent;
unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0; unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
if (nexp) *nexp = 0; /* initially nothing expunged */ if (nexp) *nexp = 0; /* initially nothing expunged */
/* calculate size of mailbox after rewrite * / /* calculate size of mailbox after rewrite * /
for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
if (!(elt = mail_elt (stream,i))->deleted || !nexp) { elt = mail_elt (stream,i); /* get cache */
if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
/* add RFC822 size of this message */ /* add RFC822 size of this message */
size += elt->private.special.text.size + elt->private.data + size += elt->private.special.text.size + elt->private.spare.data +
unix_xstatus (stream,LOCAL->buf,elt,flag) + unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
elt->private.msg.text.text.size + 2; elt->private.msg.text.text.size + 2;
flag = 1; /* only count X-IMAPbase once */ flag = 1; /* only count X-IMAPbase once */
} }
}
if (!size) { /* no messages and no pseudo, make one now * / if (!size) { /* no messages and no pseudo, make one now * /
size = unix_pseudo (stream,LOCAL->buf); size = unix_pseudo (stream,LOCAL->buf);
LOCAL->pseudo = T; LOCAL->pseudo = T;
} }
/* extend the file as necessary */ /* extend the file as necessary */
if (ret = unix_extend (stream,size)) { if (ret = unix_extend (stream,size)) {
/* Set up buffered I/O file structure /* Set up buffered I/O file structure
* curpos current position being written through buffering * curpos current position being written through buffering
* filepos current position being written physically to the disk * filepos current position being written physically to the disk
* bufpos current position being written in the buffer * bufpos current position being written in the buffer
skipping to change at line 1814 skipping to change at line 1939
f.stream = stream; /* note mail stream */ f.stream = stream; /* note mail stream */
f.curpos = f.filepos = 0; /* start of file */ f.curpos = f.filepos = 0; /* start of file */
f.protect = stream->nmsgs ? /* initial protection pointer */ f.protect = stream->nmsgs ? /* initial protection pointer */
mail_elt (stream,1)->private.special.offset : 8192; mail_elt (stream,1)->private.special.offset : 8192;
f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
if (LOCAL->pseudo) /* update pseudo-header */ if (LOCAL->pseudo) /* update pseudo-header */
unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf)); unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
/* loop through all messages */ /* loop through all messages */
for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
elt = mail_elt (stream,i);/* get cache */ elt = mail_elt (stream,i);/* get cache */
if (nexp && elt->deleted){/* expunge this message? */ /* expunge this message? */
if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
/* one less recent message */ /* one less recent message */
if (elt->recent) --recent; if (elt->recent) --recent;
mail_expunged(stream,i);/* notify upper levels */ mail_expunged(stream,i);/* notify upper levels */
++*nexp; /* count up one more expunged message */ ++*nexp; /* count up one more expunged message */
} }
else { /* preserve this message */ else { /* preserve this message */
i++; /* advance to next message */ i++; /* advance to next message */
if ((flag < 0) || /* need to rewrite message? */ if ((flag < 0) || /* need to rewrite message? */
elt->private.dirty || elt->private.dirty ||
(((unsigned long) f.curpos) != elt->private.special.offset) || (((unsigned long) f.curpos) != elt->private.special.offset) ||
(elt->private.msg.header.text.size != (elt->private.msg.header.text.size !=
(elt->private.data + (elt->private.spare.data +
unix_xstatus (stream,LOCAL->buf,elt,flag)))) { unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
unsigned long newoffset = f.curpos; unsigned long newoffset = f.curpos;
/* yes, seek to internal header */ /* yes, seek to internal header */
lseek (LOCAL->fd,elt->private.special.offset,L_SET); lseek (LOCAL->fd,elt->private.special.offset,L_SET);
read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
/* protection pointer moves to RFC822 header */ /* protection pointer moves to RFC822 header */
f.protect = elt->private.special.offset + f.protect = elt->private.special.offset +
elt->private.msg.header.offset; elt->private.msg.header.offset;
/* write internal header */ /* write internal header */
unix_write (&f,LOCAL->buf,elt->private.special.text.size); unix_write (&f,LOCAL->buf,elt->private.special.text.size);
/* get RFC822 header */ /* get RFC822 header */
s = unix_header (stream,elt->msgno,&j,NIL); s = unix_header (stream,elt->msgno,&j,NIL);
/* in case this got decremented */ /* in case this got decremented */
elt->private.msg.header.offset = elt->private.special.text.size; elt->private.msg.header.offset = elt->private.special.text.size;
/* header size, sans trailing newline */ /* header size, sans trailing newline */
if ((j < 4) || (s[j - 4] == '\r')) j -= 2; if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
if (j != elt->private.data) fatal ("header size inconsistent"); if (j != elt->private.spare.data) fatal ("header size inconsistent ");
/* protection pointer moves to RFC822 text * / /* protection pointer moves to RFC822 text * /
f.protect = elt->private.special.offset + f.protect = elt->private.special.offset +
elt->private.msg.text.offset; elt->private.msg.text.offset;
unix_write (&f,s,j); /* write RFC822 header */ unix_write (&f,s,j); /* write RFC822 header */
/* write status and UID */ /* write status and UID */
unix_write (&f,LOCAL->buf, unix_write (&f,LOCAL->buf,
j = unix_xstatus (stream,LOCAL->buf,elt,flag)); j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
flag = 1; /* only write X-IMAPbase once */ flag = 1; /* only write X-IMAPbase once */
/* new file header size */ /* new file header size */
elt->private.msg.header.text.size = elt->private.data + j; elt->private.msg.header.text.size = elt->private.spare.data + j;
/* did text move? */ /* did text move? */
if (f.curpos != f.protect) { if (f.curpos != f.protect) {
/* get message text */ /* get message text */
s = unix_text_work (stream,elt,&j,FT_INTERNAL); s = unix_text_work (stream,elt,&j,FT_INTERNAL);
/* can't happen it says here */ /* can't happen it says here */
if (j > elt->private.msg.text.text.size) if (j > elt->private.msg.text.text.size)
fatal ("text size inconsistent"); fatal ("text size inconsistent");
/* new text offset, status/UID may change it */ /* new text offset, status/UID may change it */
elt->private.msg.text.offset = f.curpos - newoffset; elt->private.msg.text.offset = f.curpos - newoffset;
/* protection pointer moves to next message */ /* protection pointer moves to next message */
skipping to change at line 1914 skipping to change at line 2040
} }
} }
} }
unix_write (&f,NIL,NIL); /* tie off final message */ unix_write (&f,NIL,NIL); /* tie off final message */
if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent "); if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent ");
fs_give ((void **) &f.buf); /* free buffer */ fs_give ((void **) &f.buf); /* free buffer */
/* make sure tied off */ /* make sure tied off */
ftruncate (LOCAL->fd,LOCAL->filesize = size); ftruncate (LOCAL->fd,LOCAL->filesize = size);
fsync (LOCAL->fd); /* make sure the updates take */ fsync (LOCAL->fd); /* make sure the updates take */
if (size && (flag < 0)) fatal ("lost UID base information"); if (size && (flag < 0)) fatal ("lost UID base information");
LOCAL->dirty = NIL; /* no longer dirty */ /* no longer dirty */
LOCAL->ddirty = LOCAL->dirty = NIL;
/* notify upper level of new mailbox sizes * / /* notify upper level of new mailbox sizes * /
mail_exists (stream,stream->nmsgs); mail_exists (stream,stream->nmsgs);
mail_recent (stream,recent); mail_recent (stream,recent);
/* set atime to now, mtime a second earlier */ /* set atime to now, mtime a second earlier */
times.modtime = (times.actime = time (0)) -1; times.modtime = (times.actime = time (0)) -1;
/* set the times, note change */ /* set the times, note change */
if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime; if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
/* flush the lock file */ /* flush the lock file */
unix_unlock (LOCAL->fd,stream,lock); unix_unlock (LOCAL->fd,stream,lock);
} }
 End of changes. 91 change blocks. 
185 lines changed or deleted 318 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/