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,×); /* set the times */ | utime (file,×); /* 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,×); /* set the times */ | utime (file,×); /* 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,×)) LOCAL->filetime = times.modtime; | if (!utime (stream->mailbox,×)) 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/ |