📄 mmdf.c
字号:
mm_log (LOCAL->buf,ERROR); return NIL; } LOCAL->buf[0] = '\0'; mm_critical (stream); /* go critical */ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) { mm_nocritical (stream); /* done with critical */ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); mm_log (LOCAL->buf,ERROR); /* log the error */ return NIL; /* failed */ } fstat (fd,&sbuf); /* get current file size */ /* write all requested messages to mailbox */ for (i = 1; ret && (i <= stream->nmsgs); i++) if ((elt = mail_elt (stream,i))->sequence) { lseek (LOCAL->fd,elt->private.special.offset,L_SET); read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; else { /* internal header succeeded */ s = mmdf_header (stream,i,&j,FT_INTERNAL); /* header size, sans trailing newline */ if (j && (s[j - 2] == '\n')) j--; if (write (fd,s,j) < 0) ret = NIL; else { /* message header succeeded */ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL); if (write (fd,LOCAL->buf,j) < 0) ret = NIL; else { /* message status succeeded */ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0)) ret = NIL; } } } } if (!ret || fsync (fd)) { /* force out the update */ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); ftruncate (fd,sbuf.st_size); ret = NIL; } tp[0] = sbuf.st_atime; /* preserve atime */ tp[1] = time (0); /* set mtime to now */ utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ mm_nocritical (stream); /* release critical */ /* log the error */ if (!ret) mm_log (LOCAL->buf,ERROR); /* delete if requested message */ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) elt->deleted = elt->private.dirty = LOCAL->dirty = T; return ret;}/* MMDF mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */#define BUFLEN 8*MAILTMPLENlong mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data){ struct stat sbuf; int fd; unsigned long i,j; char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; time_t tp[2]; FILE *sf,*df; MESSAGECACHE elt; DOTLOCK lock; STRING *message; long ret = LONGT; /* default stream to prototype */ if (!stream) stream = user_flags (&mmdfproto); /* make sure valid mailbox */ if (!mmdf_valid (mailbox)) switch (errno) { case ENOENT: /* no such file? */ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) && ((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]) mmdf_create (NIL,"INBOX"); else { mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); return NIL; } /* falls through */ case 0: /* INBOX ENOENT or empty file? */ break; case EINVAL: sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; default: sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; } /* get first message */ if (!(*af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } do { /* parse date */ if (!date) rfc822_date (date = tmp); if (!mail_parse_date (&elt,date)) { sprintf (tmp,"Bad date in append: %.80s",date); mm_log (tmp,ERROR); } else { /* user wants to suppress time zones? */ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { time_t when = mail_longdate (&elt); date = ctime (&when); /* use traditional date */ } /* use POSIX-style date */ else date = mail_cdate (tmp,&elt); if (!mmdf_append_msg (stream,sf,flags,date,message)) { sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); } /* get next message */ else if ((*af) (stream,data,&flags,&date,&message)) continue; } fclose (sf); /* punt scratch file */ return NIL; /* give up */ } while (message); /* until no more messages */ if (fflush (sf) || fstat (fileno (sf),&sbuf)) { sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); mm_log (tmp,ERROR); fclose (sf); /* punt scratch file */ return NIL; /* give up */ } i = sbuf.st_size; /* size of scratch file */ mm_critical (stream); /* go critical */ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT, S_IREAD|S_IWRITE,&lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) { mm_nocritical (stream); /* done with critical */ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } fstat (fd,&sbuf); /* get current file size */ 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 */ tp[0] = sbuf.st_atime; /* preserve atime */ /* make sure append wins */ if (i || (fflush (df) == EOF)) { sprintf (buf,"Message append failed: %s",strerror (errno)); mm_log (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[1] = sbuf.st_mtime; /* preserve mtime */ ret = NIL; /* return error */ } else tp[1] = time (0); /* set mtime to now */ utime (file,tp); /* set the times */ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ fclose (df); mm_nocritical (stream); /* release critical */ return ret;}/* Write single message to append scratch file * Accepts: MAIL stream * scratch file * flags * message stringstruct * Returns: NIL if write error, else T */int mmdf_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, STRING *msg){ unsigned long uf; int c; long f = mail_parse_flags (stream,flags,&uf); /* build initial header */ if ((fprintf (sf,"%sFrom %s@%s %sStatus: ", mmdfhdr,myusername (),mylocalhost (),date) < 0) || (f&fSEEN && (putc ('R',sf) == EOF)) || (fputs ("\nX-Status: ",sf) == EOF) || (f&fDELETED && (putc ('D',sf) == EOF)) || (f&fFLAGGED && (putc ('F',sf) == EOF)) || (f&fANSWERED && (putc ('A',sf) == EOF)) || (f&fDRAFT && (putc ('T',sf) == EOF)) || (fputs ("\nX-Keywords:",sf) == EOF)) return NIL; while (uf) /* write user flags */ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0) return NIL; /* tie off flags */ if (putc ('\n',sf) == EOF) return NIL; while (SIZE (msg)) { /* copy text, flush CTRL/A and CR from CRLF */ if (((c = 0xff & SNX (msg)) != MMDFCHR) && (((c == '\r') && SIZE (msg) && ((c = SNX (msg)) != '\n') && (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF))) return NIL; } /* write trailer and return */ return (fputs (mmdfhdr,sf) == EOF) ? NIL : T;}/* Internal routines *//* MMDF mail abort stream * Accepts: MAIL stream */void mmdf_abort (MAILSTREAM *stream){ if (LOCAL) { /* only if a file is open */ if (LOCAL->fd >= 0) close (LOCAL->fd); if (LOCAL->ld >= 0) { /* have a mailbox lock? */ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ close (LOCAL->ld); /* close the lock file */ unlink (LOCAL->lname); /* and delete it */ } if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); /* free local text buffers */ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); if (LOCAL->line) fs_give ((void **) &LOCAL->line); /* nuke the local data */ fs_give ((void **) &stream->local); stream->dtb = NIL; /* log out the DTB */ }}/* MMDF open and lock mailbox * Accepts: file name to open/lock * file open mode * destination buffer for lock file name * type of locking operation (LOCK_SH or LOCK_EX) */int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op){ int fd; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_FILELOCK,NIL); /* try locking the easy way */ if (dotlock_lock (file,lock,-1)) { /* got dotlock file, easy open */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } /* no dot lock file, open file now */ else if ((fd = open (file,flags,mode)) >= 0) { /* try paranoid way to make a dot lock file */ if (dotlock_lock (file,lock,fd)) { close (fd); /* get fresh fd in case of timing race */ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); else dotlock_unlock (lock); /* open failed, free the dotlock */ } else flock (fd,op); /* paranoid way failed, just flock() it */ } (*bn) (BLOCK_NONE,NIL); return fd;}/* MMDF unlock and close mailbox * Accepts: file descriptor * (optional) mailbox stream to check atime/mtime * (optional) lock file name */void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock){ struct stat sbuf; time_t tp[2]; fstat (fd,&sbuf); /* get file times */ /* if stream and csh would think new mail */ if (stream && (sbuf.st_atime <= sbuf.st_mtime)) { tp[0] = time (0); /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = tp[0] > sbuf.st_mtime ? sbuf.st_mtime : tp[0] - 1; /* set the times, note change */ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; } flock (fd,LOCK_UN); /* release flock'ers */ if (!stream) close (fd); /* close the file if no stream */ dotlock_unlock (lock); /* flush the lock file if any */}/* MMDF mail parse and lock mailbox * Accepts: MAIL stream * space to write lock file name * type of locking operation * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure */int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op){ int ti,zn,m; unsigned long i,j,k; char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; int retain = T; unsigned long nmsgs = stream->nmsgs; unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; unsigned long recent = stream->recent; unsigned long oldnmsgs = stream->nmsgs; short silent = stream->silent; short pseudoseen = NIL; struct stat sbuf; STRING bs; FDDATA d; MESSAGECACHE *elt; mail_lock (stream); /* guard against recursion or pingers */ /* toss out previous descriptor */ if (LOCAL->fd >= 0) close (LOCAL->fd); mm_critical (stream); /* open and lock mailbox (shared OK) */ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ? O_RDWR : O_RDONLY,NIL,lock,op)) < 0) { sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); mm_log (tmp,ERROR); mmdf_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } fstat (LOCAL->fd,&sbuf); /* get status */ /* validate change in size */ if (sbuf.st_size < LOCAL->filesize) { sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); mm_log (tmp,ERROR); /* this is pretty bad */ mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); mm_nocritical (stream); /* done with critical */ return NIL; } /* new data? */ else if (i = sbuf.st_size - LOCAL->filesize) { d.fd = LOCAL->fd; /* yes, set up file descriptor */ d.pos = LOCAL->filesize; /* get to that position in the file */ d.chunk = LOCAL->buf; /* initial buffer chunk */ d.chunksize = CHUNK; /* file chunk size */ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ /* skip leading whitespace for broken MTAs */ while (((c = CHR (&bs)) == '\n') || (c == '\r') || (c == ' ') || (c == '\t')) SNX (&bs); if (SIZE (&bs)) { /* read new data */ /* remember internal header position */ j = LOCAL->filesize + GETPOS (&bs); s = mmdf_mbxline (stream,&bs,&i); stream->silent = T; /* quell main program new message events */ do { /* read MMDF header */ if (!(i && ISMMDF (s))){/* see if valid MMDF header */ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", s); /* see if we can back up to a line */ if (i && (j > MMDFHDRLEN)) { SETPOS (&bs,j -= MMDFHDRLEN); /* read previous line */ s = mmdf_mbxline (stream,&bs,&i); /* kill the error if it looks good */ if (i && ISMMDF (s)) tmp[0] = '\0'; } if (tmp[0]) { mm_log (tmp,ERROR); mmdf_unlock (LOCAL->fd,stream,lock); mmdf_abort (stream); mail_unlock (stream); mm_nocritical(stream); return NIL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -