📄 mbox.c
字号:
#include "common.h"#include <ctype.h>#include <plumb.h>#include <libsec.h>#include "dat.h"typedef struct Header Header;struct Header { char *type; void (*f)(Message*, Header*, char*); int len;};/* headers */static void ctype(Message*, Header*, char*);static void cencoding(Message*, Header*, char*);static void cdisposition(Message*, Header*, char*);static void date822(Message*, Header*, char*);static void from822(Message*, Header*, char*);static void to822(Message*, Header*, char*);static void sender822(Message*, Header*, char*);static void replyto822(Message*, Header*, char*);static void subject822(Message*, Header*, char*);static void inreplyto822(Message*, Header*, char*);static void cc822(Message*, Header*, char*);static void bcc822(Message*, Header*, char*);static void messageid822(Message*, Header*, char*);static void mimeversion(Message*, Header*, char*);static void nullsqueeze(Message*);enum{ Mhead= 11, /* offset of first mime header */};Header head[] ={ { "date:", date822, }, { "from:", from822, }, { "to:", to822, }, { "sender:", sender822, }, { "reply-to:", replyto822, }, { "subject:", subject822, }, { "cc:", cc822, }, { "bcc:", bcc822, }, { "in-reply-to:", inreplyto822, }, { "mime-version:", mimeversion, }, { "message-id:", messageid822, },[Mhead] { "content-type:", ctype, }, { "content-transfer-encoding:", cencoding, }, { "content-disposition:", cdisposition, }, { 0, },};static void fatal(char *fmt, ...);static void initquoted(void);static void startheader(Message*);static void startbody(Message*);static char* skipwhite(char*);static char* skiptosemi(char*);static char* getstring(char*, String*, int);static void setfilename(Message*, char*);static char* lowercase(char*);static int is8bit(Message*);static int headerline(char**, String*);static void initheaders(void);static void parseattachments(Message*, Mailbox*);int debug;char *Enotme = "path not served by this file server";enum{ Chunksize = 1024,};Mailboxinit *boxinit[] = { imap4mbox, pop3mbox, plan9mbox,};char*syncmbox(Mailbox *mb, int doplumb){ return (*mb->sync)(mb, doplumb);}/* create a new mailbox */char*newmbox(char *path, char *name, int std){ Mailbox *mb, **l; char *p, *rv; int i; initheaders(); mb = emalloc(sizeof(*mb)); strncpy(mb->path, path, sizeof(mb->path)-1); if(name == nil){ p = strrchr(path, '/'); if(p == nil) p = path; else p++; if(*p == 0){ free(mb); return "bad mbox name"; } strncpy(mb->name, p, sizeof(mb->name)-1); } else { strncpy(mb->name, name, sizeof(mb->name)-1); } rv = nil; // check for a mailbox type for(i=0; i<nelem(boxinit); i++) if((rv = (*boxinit[i])(mb, path)) != Enotme) break; if(i == nelem(boxinit)){ free(mb); return "bad path"; } // on error, give up if(rv){ free(mb); return rv; } // make sure name isn't taken qlock(&mbllock); for(l = &mbl; *l != nil; l = &(*l)->next){ if(strcmp((*l)->name, mb->name) == 0){ if(strcmp(path, (*l)->path) == 0) rv = nil; else rv = "mbox name in use"; if(mb->close) (*mb->close)(mb); free(mb); qunlock(&mbllock); return rv; } } // always try locking mb->dolock = 1; mb->refs = 1; mb->next = nil; mb->id = newid(); mb->root = newmessage(nil); mb->std = std; *l = mb; qunlock(&mbllock); qlock(mb); if(mb->ctl){ henter(PATH(mb->id, Qmbox), "ctl", (Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb); } rv = syncmbox(mb, 0); qunlock(mb); return rv;}// close the named mailboxvoidfreembox(char *name){ Mailbox **l, *mb; qlock(&mbllock); for(l=&mbl; *l != nil; l=&(*l)->next){ if(strcmp(name, (*l)->name) == 0){ mb = *l; *l = mb->next; mboxdecref(mb); break; } } hfree(PATH(0, Qtop), name); qunlock(&mbllock);}static voidinitheaders(void){ Header *h; static int already; if(already) return; already = 1; for(h = head; h->type != nil; h++) h->len = strlen(h->type);}/* * parse a Unix style header */voidparseunix(Message *m){ char *p; String *h; h = s_new(); for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++) s_putc(h, *p); s_terminate(h); s_restart(h); m->unixfrom = s_parse(h, s_reset(m->unixfrom)); m->unixdate = s_append(s_reset(m->unixdate), h->ptr); s_free(h);}/* * parse a message */voidparseheaders(Message *m, int justmime, Mailbox *mb, int addfrom){ String *hl; Header *h; char *p, *q; int i; if(m->whole == m->whole->whole){ henter(PATH(mb->id, Qmbox), m->name, (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb); } else { henter(PATH(m->whole->id, Qdir), m->name, (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb); } for(i = 0; i < Qmax; i++) henter(PATH(m->id, Qdir), dirtab[i], (Qid){PATH(m->id, i), 0, QTFILE}, m, mb); // parse mime headers p = m->header; hl = s_new(); while(headerline(&p, hl)){ if(justmime) h = &head[Mhead]; else h = head; for(; h->type; h++){ if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){ (*h->f)(m, h, s_to_c(hl)); break; } } s_reset(hl); } s_free(hl); // the blank line isn't really part of the body or header if(justmime){ m->mhend = p; m->hend = m->header; } else { m->hend = p; } if(*p == '\n') p++; m->rbody = m->body = p; // if type is text, get any nulls out of the body. This is // for the two seans and imap clients that get confused. if(strncmp(s_to_c(m->type), "text/", 5) == 0) nullsqueeze(m); // // cobble together Unix-style from line // for local mailbox messages, we end up recreating the // original header. // for pop3 messages, the best we can do is // use the From: information and the RFC822 date. // if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0 || strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){ if(m->unixdate){ s_free(m->unixdate); m->unixdate = nil; } // look for the date in the first Received: line. // it's likely to be the right time zone (it's // the local system) and in a convenient format. if(cistrncmp(m->header, "received:", 9)==0){ if((q = strchr(m->header, ';')) != nil){ p = q; while((p = strchr(p, '\n')) != nil){ if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n') break; p++; } if(p){ *p = '\0'; m->unixdate = date822tounix(q+1); *p = '\n'; } } } // fall back on the rfc822 date if(m->unixdate==nil && m->date822) m->unixdate = date822tounix(s_to_c(m->date822)); } if(m->unixheader != nil) s_free(m->unixheader); // only fake header for top-level messages for pop3 and imap4 // clients (those protocols don't include the unix header). // adding the unix header all the time screws up mime-attached // rfc822 messages. if(!addfrom && !m->unixfrom){ m->unixheader = nil; return; } m->unixheader = s_copy("From "); if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0) s_append(m->unixheader, s_to_c(m->unixfrom)); else if(m->from822) s_append(m->unixheader, s_to_c(m->from822)); else s_append(m->unixheader, "???"); s_append(m->unixheader, " "); if(m->unixdate) s_append(m->unixheader, s_to_c(m->unixdate)); else s_append(m->unixheader, "Thu Jan 1 00:00:00 GMT 1970"); s_append(m->unixheader, "\n");}String*promote(String **sp){ String *s; if(*sp != nil) s = s_clone(*sp); else s = nil; return s;}voidparsebody(Message *m, Mailbox *mb){ Message *nm; // recurse if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){ parseattachments(m, mb); } else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){ decode(m); parseattachments(m, mb); nm = m->part; // promote headers if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){ m->from822 = promote(&nm->from822); m->to822 = promote(&nm->to822); m->date822 = promote(&nm->date822); m->sender822 = promote(&nm->sender822); m->replyto822 = promote(&nm->replyto822); m->subject822 = promote(&nm->subject822); m->unixdate = promote(&nm->unixdate); } }}voidparse(Message *m, int justmime, Mailbox *mb, int addfrom){ parseheaders(m, justmime, mb, addfrom); parsebody(m, mb);}static voidparseattachments(Message *m, Mailbox *mb){ Message *nm, **l; char *p, *x; // if there's a boundary, recurse... if(m->boundary != nil){ p = m->body; nm = nil; l = &m->part; for(;;){ x = strstr(p, s_to_c(m->boundary)); /* no boundary, we're done */ if(x == nil){ if(nm != nil) nm->rbend = nm->bend = nm->end = m->bend; break; } /* boundary must be at the start of a line */ if(x != m->body && *(x-1) != '\n'){ p = x+1; continue; } if(nm != nil) nm->rbend = nm->bend = nm->end = x; x += strlen(s_to_c(m->boundary)); /* is this the last part? ignore anything after it */ if(strncmp(x, "--", 2) == 0) break; p = strchr(x, '\n'); if(p == nil) break; nm = newmessage(m); nm->start = nm->header = nm->body = nm->rbody = ++p; nm->mheader = nm->header; *l = nm; l = &nm->next; } for(nm = m->part; nm != nil; nm = nm->next) parse(nm, 1, mb, 0); return; } // if we've got an rfc822 message, recurse... if(strcmp(s_to_c(m->type), "message/rfc822") == 0){ nm = newmessage(m); m->part = nm; nm->start = nm->header = nm->body = nm->rbody = m->body; nm->end = nm->bend = nm->rbend = m->bend; parse(nm, 0, mb, 0); }}/* * pick up a header line */static intheaderline(char **pp, String *hl){ char *p, *x; s_reset(hl); p = *pp; x = strpbrk(p, ":\n"); if(x == nil || *x == '\n') return 0; for(;;){ x = strchr(p, '\n'); if(x == nil) x = p + strlen(p); s_nappend(hl, p, x-p); p = x; if(*p != '\n' || *++p != ' ' && *p != '\t') break; while(*p == ' ' || *p == '\t') p++; s_putc(hl, ' '); } *pp = p; return 1;}static String*addr822(char *p){ String *s, *list; int incomment, addrdone, inanticomment, quoted; int n; int c; list = s_new(); s = s_new(); quoted = incomment = addrdone = inanticomment = 0; n = 0; for(; *p; p++){ c = *p; // whitespace is ignored if(!quoted && isspace(c) || c == '\r') continue; // strings are always treated as atoms if(!quoted && c == '"'){ if(!addrdone && !incomment) s_putc(s, c); for(p++; *p; p++){ if(!addrdone && !incomment) s_putc(s, *p); if(!quoted && *p == '"') break; if(*p == '\\') quoted = 1; else quoted = 0; } if(*p == 0) break; quoted = 0; continue; } // ignore everything in an expicit comment if(!quoted && c == '('){ incomment = 1; continue; } if(incomment){ if(!quoted && c == ')') incomment = 0; quoted = 0; continue; } // anticomments makes everything outside of them comments if(!quoted && c == '<' && !inanticomment){ inanticomment = 1; s = s_reset(s); continue; } if(!quoted && c == '>' && inanticomment){ addrdone = 1; inanticomment = 0; continue; } // commas separate addresses if(!quoted && c == ',' && !inanticomment){ s_terminate(s); addrdone = 0; if(n++ != 0) s_append(list, " "); s_append(list, s_to_c(s)); s = s_reset(s); continue; } // what's left is part of the address s_putc(s, c); // quoted characters are recognized only as characters if(c == '\\') quoted = 1; else quoted = 0; } if(*s_to_c(s) != 0){ s_terminate(s); if(n++ != 0) s_append(list, " "); s_append(list, s_to_c(s)); } s_free(s); if(n == 0){ s_free(list); return nil; } return list;}static voidto822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->to822); m->to822 = addr822(p);}static voidcc822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->cc822); m->cc822 = addr822(p);}static voidbcc822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->bcc822); m->bcc822 = addr822(p);}static voidfrom822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->from822); m->from822 = addr822(p);}static voidsender822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->sender822); m->sender822 = addr822(p);}static voidreplyto822(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->replyto822); m->replyto822 = addr822(p);}static voidmimeversion(Message *m, Header *h, char *p){ p += strlen(h->type); s_free(m->mimeversion); m->mimeversion = addr822(p);}static voidkilltrailingwhite(char *p){ char *e; e = p + strlen(p) - 1; while(e > p && isspace(*e)) *e-- = 0;}static voiddate822(Message *m, Header *h, char *p){ p += strlen(h->type); p = skipwhite(p); s_free(m->date822); m->date822 = s_copy(p); p = s_to_c(m->date822); killtrailingwhite(p);}static voidsubject822(Message *m, Header *h, char *p){ p += strlen(h->type); p = skipwhite(p); s_free(m->subject822); m->subject822 = s_copy(p); p = s_to_c(m->subject822); killtrailingwhite(p);}static voidinreplyto822(Message *m, Header *h, char *p){ p += strlen(h->type); p = skipwhite(p); s_free(m->inreplyto822); m->inreplyto822 = s_copy(p); p = s_to_c(m->inreplyto822); killtrailingwhite(p);}static voidmessageid822(Message *m, Header *h, char *p){ p += strlen(h->type); p = skipwhite(p); s_free(m->messageid822); m->messageid822 = s_copy(p); p = s_to_c(m->messageid822); killtrailingwhite(p);}static intisattribute(char **pp, char *attr){ char *p; int n; n = strlen(attr); p = *pp; if(cistrncmp(p, attr, n) != 0) return 0; p += n; while(*p == ' ') p++; if(*p++ != '=') return 0; while(*p == ' ') p++; *pp = p; return 1;}static voidctype(Message *m, Header *h, char *p){ String *s; p += h->len; p = skipwhite(p); p = getstring(p, m->type, 1); while(*p){ if(isattribute(&p, "boundary")){ s = s_new(); p = getstring(p, s, 0); m->boundary = s_reset(m->boundary); s_append(m->boundary, "--"); s_append(m->boundary, s_to_c(s)); s_free(s); } else if(cistrncmp(p, "multipart", 9) == 0){ /* * the first unbounded part of a multipart message, * the preamble, is not displayed or saved */ } else if(isattribute(&p, "name")){ if(m->filename == nil) setfilename(m, p); } else if(isattribute(&p, "charset")){ p = getstring(p, s_reset(m->charset), 0); } p = skiptosemi(p);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -