📄 mmdf.c
字号:
MM_LOG (tmp,ERROR); return NIL; } /* get first message */ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; if (!(sf = tmpfile ())) { /* must have scratch file */ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); return NIL; } unlink (tmp); } 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 (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); else 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 (MM_APPEND (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[1] = time (0); /* set mtime to now */ /* make sure append wins, fsync() necessary */ if (i || (fflush (df) == EOF) || fsync (fd)) { sprintf (buf,"Message append failed: %s",strerror (errno)); MM_LOG (buf,ERROR); ftruncate (fd,sbuf.st_size); tp[0] = /* preserve \Marked status */ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? sbuf.st_atime : tp[1]; ret = NIL; /* return error */ } else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ 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 i,uf; int c; char tmp[MAILTMPLEN]; int hdrp = T; 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 to scratch file */ /* disregard CRs */ while ((c = (SIZE (msg)) ? (c = 0xff & SNX (msg)) : '\n') == '\r'); /* see if line needs special treatment */ if (hdrp && ((c == 'S') || (c == 'X'))) { /* copy line to buffer */ for (i = 1,tmp[0] = c; (c != '\n') && (i < MAILTMPLEN); ) if ((c = (SIZE (msg)) ? (0xff & SNX (msg)) : '\n') != '\r') tmp[i++] = c; /* insert X- before metadata header */ if ((((i > 6) && (tmp[0] == 'S') && (tmp[1] == 't') && (tmp[2] == 'a') && (tmp[3] == 't') && (tmp[4] == 'u') && (tmp[5] == 's') && (tmp[6] == ':')) || (((i > 5) && (tmp[0] == 'X') && (tmp[1] == '-')) && (((tmp[2] == 'U') && (tmp[3] == 'I') && (tmp[4] == 'D') && (tmp[5] == ':')) || ((i > 6) && (tmp[2] == 'I') && (tmp[3] == 'M') && (tmp[4] == 'A') && (tmp[5] == 'P') && ((tmp[6] == ':') || ((i > 10) && (tmp[6] == 'b') && (tmp[7] == 'a') && (tmp[8] == 's') && (tmp[9] == 'e') && (tmp[10] == ':')))) || ((i > 8) && (tmp[2] == 'S') && (tmp[3] == 't') && (tmp[4] == 'a') && (tmp[5] == 't') && (tmp[6] == 'u') && (tmp[7] == 's') && (tmp[8] == ':')) || ((i > 10) && (tmp[2] == 'K') && (tmp[3] == 'e') && (tmp[4] == 'y') && (tmp[5] == 'w') && (tmp[6] == 'o') && (tmp[7] == 'r') && (tmp[8] == 'd') && (tmp[9] == 's') && (tmp[10] == ':'))))) && (fputs ("X-Original-",sf) == EOF)) return NIL; /* write buffered text */ if (fwrite (tmp,1,i,sf) != i) return NIL; /* set up to copy remainder of line */ if ((c != '\n') && SIZE (msg)) c = 0xff & SNX (msg); else continue; /* end of line or end of message */ } /* check for end of header */ else if (hdrp && (c == '\n')) hdrp = NIL; do switch (c) { /* copy line */ case MMDFCHR: /* flush CTRL/A */ case '\r': /* and CR */ break; default: /* any other character */ if (putc (c,sf) == EOF) return NIL; } while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T)); } /* 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->text.data) fs_give ((void **) &LOCAL->text.data); 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){ if (stream) { /* need to muck with times? */ struct stat sbuf; time_t tp[2]; time_t now = time (0); fstat (fd,&sbuf); /* get file times */ if (LOCAL->ld >= 0) { /* yes, readwrite session? */ tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else if (stream->recent) { /* readonly with recent messages */ if ((sbuf.st_atime >= sbuf.st_mtime) || (sbuf.st_atime >= sbuf.st_ctime)) /* keep past mtime, whack back atime */ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; else now = 0; /* no time change needed */ } /* readonly with no recent messages */ else if ((sbuf.st_atime < sbuf.st_mtime) || (sbuf.st_atime < sbuf.st_ctime)) { tp[0] = now; /* set atime to now */ /* set mtime to (now - 1) if necessary */ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; } else now = 0; /* no time change needed */ /* set the times, note change */ if (now && !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; unsigned 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", (char *) 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; } } /* instantiate first new message */ mail_exists (stream,++nmsgs); (elt = mail_elt (stream,nmsgs))->valid = T; recent++; /* assume recent by default */ elt->recent = T; /* note position/size of internal header */ elt->private.special.offset = j; elt->private.special.text.size = i; s = mmdf_mbxline (stream,&bs,&i); ti = 0; /* assume not a valid date */ zn = 0,t = NIL; if (i) VALID (s,t,ti,zn); if (ti) { /* generate plausible IMAPish date string */ /* this is also part of header */ elt->private.special.text.size += i; date[2] = date[6] = date[20] = '-'; date[11] = ' '; date[14] = date[17] = ':'; /* dd */ date[0] = t[ti - 2]; date[1] = t[ti - 1]; /* mmm */ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; /* hh */ date[12] = t[ti + 1]; date[13] = t[ti + 2]; /* mm */ date[15] = t[ti + 4]; date[16] = t[ti + 5]; if (t[ti += 6]==':'){ /* ss */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -