12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571 |
- #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 stdmbox[Pathlen];
- 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();
- if(stdmbox[0] == 0)
- snprint(stdmbox, sizeof(stdmbox), "/mail/box/%s/mbox", user);
- 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;
- }
- }
- // all mailboxes in /mail/box/$user are locked using /mail/box/$user/mbox
- p = strrchr(stdmbox, '/');
- mb->dolock = strncmp(mb->path, stdmbox, p - stdmbox) == 0;
- 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 mailbox
- void
- freembox(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 void
- initheaders(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
- */
- void
- parseunix(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
- */
- void
- parseheaders(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;
- }
- void
- parsebody(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);
- }
- }
- }
- void
- parse(Message *m, int justmime, Mailbox *mb, int addfrom)
- {
- parseheaders(m, justmime, mb, addfrom);
- parsebody(m, mb);
- }
- static void
- parseattachments(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 int
- headerline(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 void
- to822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->to822);
- m->to822 = addr822(p);
- }
- static void
- cc822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->cc822);
- m->cc822 = addr822(p);
- }
- static void
- bcc822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->bcc822);
- m->bcc822 = addr822(p);
- }
- static void
- from822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->from822);
- m->from822 = addr822(p);
- }
- static void
- sender822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->sender822);
- m->sender822 = addr822(p);
- }
- static void
- replyto822(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->replyto822);
- m->replyto822 = addr822(p);
- }
- static void
- mimeversion(Message *m, Header *h, char *p)
- {
- p += strlen(h->type);
- s_free(m->mimeversion);
- m->mimeversion = addr822(p);
- }
- static void
- killtrailingwhite(char *p)
- {
- char *e;
- e = p + strlen(p) - 1;
- while(e > p && isspace(*e))
- *e-- = 0;
- }
- static void
- date822(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 void
- subject822(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 void
- inreplyto822(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 void
- messageid822(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 int
- isattribute(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 void
- ctype(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);
- }
- }
- static void
- cencoding(Message *m, Header *h, char *p)
- {
- p += h->len;
- p = skipwhite(p);
- if(cistrncmp(p, "base64", 6) == 0)
- m->encoding = Ebase64;
- else if(cistrncmp(p, "quoted-printable", 16) == 0)
- m->encoding = Equoted;
- }
- static void
- cdisposition(Message *m, Header *h, char *p)
- {
- p += h->len;
- p = skipwhite(p);
- while(*p){
- if(cistrncmp(p, "inline", 6) == 0){
- m->disposition = Dinline;
- } else if(cistrncmp(p, "attachment", 10) == 0){
- m->disposition = Dfile;
- } else if(cistrncmp(p, "filename=", 9) == 0){
- p += 9;
- setfilename(m, p);
- }
- p = skiptosemi(p);
- }
- }
- ulong msgallocd, msgfreed;
- Message*
- newmessage(Message *parent)
- {
- static int id;
- Message *m;
- msgallocd++;
- m = emalloc(sizeof(*m));
- memset(m, 0, sizeof(*m));
- m->disposition = Dnone;
- m->type = s_copy("text/plain");
- m->charset = s_copy("iso-8859-1");
- m->id = newid();
- if(parent)
- sprint(m->name, "%d", ++(parent->subname));
- if(parent == nil)
- parent = m;
- m->whole = parent;
- m->hlen = -1;
- return m;
- }
- // delete a message from a mailbox
- void
- delmessage(Mailbox *mb, Message *m)
- {
- Message **l;
- int i;
- mb->vers++;
- msgfreed++;
- if(m->whole != m){
- // unchain from parent
- for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
- ;
- if(*l != nil)
- *l = m->next;
- // clear out of name lookup hash table
- if(m->whole->whole == m->whole)
- hfree(PATH(mb->id, Qmbox), m->name);
- else
- hfree(PATH(m->whole->id, Qdir), m->name);
- for(i = 0; i < Qmax; i++)
- hfree(PATH(m->id, Qdir), dirtab[i]);
- }
- /* recurse through sub-parts */
- while(m->part)
- delmessage(mb, m->part);
- /* free memory */
- if(m->mallocd)
- free(m->start);
- if(m->hallocd)
- free(m->header);
- if(m->ballocd)
- free(m->body);
- s_free(m->unixfrom);
- s_free(m->unixdate);
- s_free(m->unixheader);
- s_free(m->from822);
- s_free(m->sender822);
- s_free(m->to822);
- s_free(m->bcc822);
- s_free(m->cc822);
- s_free(m->replyto822);
- s_free(m->date822);
- s_free(m->inreplyto822);
- s_free(m->subject822);
- s_free(m->messageid822);
- s_free(m->addrs);
- s_free(m->mimeversion);
- s_free(m->sdigest);
- s_free(m->boundary);
- s_free(m->type);
- s_free(m->charset);
- s_free(m->filename);
- free(m);
- }
- // mark messages (identified by path) for deletion
- void
- delmessages(int ac, char **av)
- {
- Mailbox *mb;
- Message *m;
- int i, needwrite;
- qlock(&mbllock);
- for(mb = mbl; mb != nil; mb = mb->next)
- if(strcmp(av[0], mb->name) == 0){
- qlock(mb);
- break;
- }
- qunlock(&mbllock);
- if(mb == nil)
- return;
- needwrite = 0;
- for(i = 1; i < ac; i++){
- for(m = mb->root->part; m != nil; m = m->next)
- if(strcmp(m->name, av[i]) == 0){
- if(!m->deleted){
- mailplumb(mb, m, 1);
- needwrite = 1;
- m->deleted = 1;
- logmsg("deleting", m);
- }
- break;
- }
- }
- if(needwrite)
- syncmbox(mb, 1);
- qunlock(mb);
- }
- /*
- * the following are called with the mailbox qlocked
- */
- void
- msgincref(Message *m)
- {
- m->refs++;
- }
- void
- msgdecref(Mailbox *mb, Message *m)
- {
- m->refs--;
- if(m->refs == 0 && m->deleted)
- syncmbox(mb, 1);
- }
- /*
- * the following are called with mbllock'd
- */
- void
- mboxincref(Mailbox *mb)
- {
- assert(mb->refs > 0);
- mb->refs++;
- }
- void
- mboxdecref(Mailbox *mb)
- {
- assert(mb->refs > 0);
- qlock(mb);
- mb->refs--;
- if(mb->refs == 0){
- delmessage(mb, mb->root);
- if(mb->ctl)
- hfree(PATH(mb->id, Qmbox), "ctl");
- if(mb->close)
- (*mb->close)(mb);
- free(mb);
- } else
- qunlock(mb);
- }
- int
- cistrncmp(char *a, char *b, int n)
- {
- while(n-- > 0){
- if(tolower(*a++) != tolower(*b++))
- return -1;
- }
- return 0;
- }
- int
- cistrcmp(char *a, char *b)
- {
- for(;;){
- if(tolower(*a) != tolower(*b++))
- return -1;
- if(*a++ == 0)
- break;
- }
- return 0;
- }
- static char*
- skipwhite(char *p)
- {
- while(isspace(*p))
- p++;
- return p;
- }
- static char*
- skiptosemi(char *p)
- {
- while(*p && *p != ';')
- p++;
- while(*p == ';' || isspace(*p))
- p++;
- return p;
- }
- static char*
- getstring(char *p, String *s, int dolower)
- {
- s = s_reset(s);
- p = skipwhite(p);
- if(*p == '"'){
- p++;
- for(;*p && *p != '"'; p++)
- if(dolower)
- s_putc(s, tolower(*p));
- else
- s_putc(s, *p);
- if(*p == '"')
- p++;
- s_terminate(s);
- return p;
- }
- for(; *p && !isspace(*p) && *p != ';'; p++)
- if(dolower)
- s_putc(s, tolower(*p));
- else
- s_putc(s, *p);
- s_terminate(s);
- return p;
- }
- static void
- setfilename(Message *m, char *p)
- {
- m->filename = s_reset(m->filename);
- getstring(p, m->filename, 0);
- for(p = s_to_c(m->filename); *p; p++)
- if(*p == ' ' || *p == '\t' || *p == ';')
- *p = '_';
- }
- //
- // undecode message body
- //
- void
- decode(Message *m)
- {
- int i, len;
- char *x;
- if(m->decoded)
- return;
- switch(m->encoding){
- case Ebase64:
- len = m->bend - m->body;
- i = (len*3)/4+1; // room for max chars + null
- x = emalloc(i);
- len = dec64((uchar*)x, i, m->body, len);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- break;
- case Equoted:
- len = m->bend - m->body;
- x = emalloc(len+2); // room for null and possible extra nl
- len = decquoted(x, m->body, m->bend);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- break;
- default:
- break;
- }
- m->decoded = 1;
- }
- // convert latin1 to utf
- void
- convert(Message *m)
- {
- int len;
- char *x;
- // don't convert if we're not a leaf, not text, or already converted
- if(m->converted)
- return;
- if(m->part != nil)
- return;
- if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
- return;
- if(cistrcmp(s_to_c(m->charset), "us-ascii") == 0 ||
- cistrcmp(s_to_c(m->charset), "iso-8859-1") == 0){
- len = is8bit(m);
- if(len > 0){
- len = 2*len + m->bend - m->body + 1;
- x = emalloc(len);
- len = latin1toutf(x, m->body, m->bend);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
- } else if(cistrcmp(s_to_c(m->charset), "iso-8859-2") == 0){
- len = xtoutf("8859-2", &x, m->body, m->bend);
- if(len != 0){
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
- } else if(cistrcmp(s_to_c(m->charset), "iso-8859-15") == 0){
- len = xtoutf("8859-15", &x, m->body, m->bend);
- if(len != 0){
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
- } else if(cistrcmp(s_to_c(m->charset), "big5") == 0){
- len = xtoutf("big5", &x, m->body, m->bend);
- if(len != 0){
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
- } else if(cistrcmp(s_to_c(m->charset), "windows-1257") == 0
- || cistrcmp(s_to_c(m->charset), "windows-1252") == 0){
- len = is8bit(m);
- if(len > 0){
- len = 2*len + m->bend - m->body + 1;
- x = emalloc(len);
- len = windows1257toutf(x, m->body, m->bend);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
- }
- m->converted = 1;
- }
- enum
- {
- Self= 1,
- Hex= 2,
- };
- uchar tableqp[256];
- static void
- initquoted(void)
- {
- int c;
- memset(tableqp, 0, 256);
- for(c = ' '; c <= '<'; c++)
- tableqp[c] = Self;
- for(c = '>'; c <= '~'; c++)
- tableqp[c] = Self;
- tableqp['\t'] = Self;
- tableqp['='] = Hex;
- }
- static int
- hex2int(int x)
- {
- if(x >= '0' && x <= '9')
- return x - '0';
- if(x >= 'A' && x <= 'F')
- return (x - 'A') + 10;
- if(x >= 'a' && x <= 'f')
- return (x - 'a') + 10;
- return 0;
- }
- static char*
- decquotedline(char *out, char *in, char *e)
- {
- int c, soft;
- /* dump trailing white space */
- while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
- e--;
- /* trailing '=' means no newline */
- if(*e == '='){
- soft = 1;
- e--;
- } else
- soft = 0;
- while(in <= e){
- c = (*in++) & 0xff;
- switch(tableqp[c]){
- case Self:
- *out++ = c;
- break;
- case Hex:
- c = hex2int(*in++)<<4;
- c |= hex2int(*in++);
- *out++ = c;
- break;
- }
- }
- if(!soft)
- *out++ = '\n';
- *out = 0;
- return out;
- }
- int
- decquoted(char *out, char *in, char *e)
- {
- char *p, *nl;
- if(tableqp[' '] == 0)
- initquoted();
- p = out;
- while((nl = strchr(in, '\n')) != nil && nl < e){
- p = decquotedline(p, in, nl);
- in = nl + 1;
- }
- if(in < e)
- p = decquotedline(p, in, e-1);
- // make sure we end with a new line
- if(*(p-1) != '\n'){
- *p++ = '\n';
- *p = 0;
- }
- return p - out;
- }
- static char*
- lowercase(char *p)
- {
- char *op;
- int c;
- for(op = p; c = *p; p++)
- if(isupper(c))
- *p = tolower(c);
- return op;
- }
- /*
- * return number of 8 bit characters
- */
- static int
- is8bit(Message *m)
- {
- int count = 0;
- char *p;
- for(p = m->body; p < m->bend; p++)
- if(*p & 0x80)
- count++;
- return count;
- }
- // translate latin1 directly since it fits neatly in utf
- int
- latin1toutf(char *out, char *in, char *e)
- {
- Rune r;
- char *p;
- p = out;
- for(; in < e; in++){
- r = (*in) & 0xff;
- p += runetochar(p, &r);
- }
- *p = 0;
- return p - out;
- }
- // translate any thing else using the tcs program
- int
- xtoutf(char *charset, char **out, char *in, char *e)
- {
- char *av[4];
- int totcs[2];
- int fromtcs[2];
- int n, len, sofar;
- char *p;
- len = e-in+1;
- sofar = 0;
- *out = p = malloc(len+1);
- if(p == nil)
- return 0;
- av[0] = charset;
- av[1] = "-f";
- av[2] = charset;
- av[3] = 0;
- if(pipe(totcs) < 0)
- return 0;
- if(pipe(fromtcs) < 0){
- close(totcs[0]); close(totcs[1]);
- return 0;
- }
- switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
- case -1:
- close(fromtcs[0]); close(fromtcs[1]);
- close(totcs[0]); close(totcs[1]);
- return 0;
- case 0:
- close(fromtcs[0]); close(totcs[1]);
- dup(fromtcs[1], 1);
- dup(totcs[0], 0);
- close(fromtcs[1]); close(totcs[0]);
- dup(open("/dev/null", OWRITE), 2);
- exec("/bin/tcs", av);
- _exits(0);
- default:
- close(fromtcs[1]); close(totcs[0]);
- switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
- case -1:
- close(fromtcs[0]); close(totcs[1]);
- return 0;
- case 0:
- close(fromtcs[0]);
- while(in < e){
- n = write(totcs[1], in, e-in);
- if(n <= 0)
- break;
- in += n;
- }
- close(totcs[1]);
- _exits(0);
- default:
- close(totcs[1]);
- for(;;){
- n = read(fromtcs[0], &p[sofar], len-sofar);
- if(n <= 0)
- break;
- sofar += n;
- p[sofar] = 0;
- if(sofar == len){
- len += 1024;
- *out = p = realloc(p, len+1);
- if(p == nil)
- return 0;
- }
- }
- close(fromtcs[0]);
- break;
- }
- break;
- }
- return sofar;
- }
- enum {
- Winstart= 0x7f,
- Winend= 0x9f,
- };
- Rune winchars[] = {
- L'•',
- L'•', L'•', L'‚', L'ƒ', L'„', L'…', L'†', L'‡',
- L'ˆ', L'‰', L'Š', L'‹', L'Œ', L'•', L'•', L'•',
- L'•', L'‘', L'’', L'“', L'”', L'•', L'–', L'—',
- L'˜', L'™', L'š', L'›', L'œ', L'•', L'•', L'Ÿ',
- };
- int
- windows1257toutf(char *out, char *in, char *e)
- {
- Rune r;
- char *p;
- p = out;
- for(; in < e; in++){
- r = (*in) & 0xff;
- if(r >= 0x7f && r <= 0x9f)
- r = winchars[r-0x7f];
- p += runetochar(p, &r);
- }
- *p = 0;
- return p - out;
- }
- void *
- emalloc(ulong n)
- {
- void *p;
- p = mallocz(n, 1);
- if(!p){
- fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
- exits("out of memory");
- }
- setmalloctag(p, getcallerpc(&n));
- return p;
- }
- void *
- erealloc(void *p, ulong n)
- {
- if(n == 0)
- n = 1;
- p = realloc(p, n);
- if(!p){
- fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
- exits("out of memory");
- }
- setrealloctag(p, getcallerpc(&p));
- return p;
- }
- void
- mailplumb(Mailbox *mb, Message *m, int delete)
- {
- Plumbmsg p;
- Plumbattr a[7];
- char buf[256];
- int ai;
- char lenstr[10], *from, *subject, *date;
- static int fd = -1;
- if(m->subject822 == nil)
- subject = "";
- else
- subject = s_to_c(m->subject822);
- if(m->from822 != nil)
- from = s_to_c(m->from822);
- else if(m->unixfrom != nil)
- from = s_to_c(m->unixfrom);
- else
- from = "";
- if(m->unixdate != nil)
- date = s_to_c(m->unixdate);
- else
- date = "";
- sprint(lenstr, "%ld", m->end-m->start);
- if(biffing && !delete)
- print("[ %s / %s / %s ]\n", from, subject, lenstr);
- if(!plumbing)
- return;
- if(fd < 0)
- fd = plumbopen("send", OWRITE);
- if(fd < 0)
- return;
- p.src = "mailfs";
- p.dst = "seemail";
- p.wdir = "/mail/fs";
- p.type = "text";
- ai = 0;
- a[ai].name = "filetype";
- a[ai].value = "mail";
- a[++ai].name = "sender";
- a[ai].value = from;
- a[ai-1].next = &a[ai];
- a[++ai].name = "length";
- a[ai].value = lenstr;
- a[ai-1].next = &a[ai];
- a[++ai].name = "mailtype";
- a[ai].value = delete?"delete":"new";
- a[ai-1].next = &a[ai];
- a[++ai].name = "date";
- a[ai].value = date;
- a[ai-1].next = &a[ai];
- if(m->sdigest){
- a[++ai].name = "digest";
- a[ai].value = s_to_c(m->sdigest);
- a[ai-1].next = &a[ai];
- }
- a[ai].next = nil;
- p.attr = a;
- snprint(buf, sizeof(buf), "%s/%s/%s",
- mntpt, mb->name, m->name);
- p.ndata = strlen(buf);
- p.data = buf;
- plumbsend(fd, &p);
- }
- //
- // count the number of lines in the body (for imap4)
- //
- void
- countlines(Message *m)
- {
- int i;
- char *p;
- i = 0;
- for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
- i++;
- sprint(m->lines, "%d", i);
- }
- char *LOG = "fs";
- void
- logmsg(char *s, Message *m)
- {
- int pid;
- if(!logging)
- return;
- pid = getpid();
- if(m == nil)
- syslog(0, LOG, "%s.%d: %s", user, pid, s);
- else
- syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
- user, pid, s,
- m->from822 ? s_to_c(m->from822) : "?",
- s_to_c(m->sdigest));
- }
- /*
- * squeeze nulls out of the body
- */
- static void
- nullsqueeze(Message *m)
- {
- char *p, *q;
- q = memchr(m->body, 0, m->end-m->body);
- if(q == nil)
- return;
- for(p = m->body; q < m->end; q++){
- if(*q == 0)
- continue;
- *p++ = *q;
- }
- m->bend = m->rbend = m->end = p;
- }
- //
- // convert an RFC822 date into a Unix style date
- // for when the Unix From line isn't there (e.g. POP3).
- // enough client programs depend on having a Unix date
- // that it's easiest to write this conversion code once, right here.
- //
- // people don't follow RFC822 particularly closely,
- // so we use strtotm, which is a bunch of heuristics.
- //
- extern int strtotm(char*, Tm*);
- String*
- date822tounix(char *s)
- {
- char *p, *q;
- Tm tm;
- if(strtotm(s, &tm) < 0)
- return nil;
- p = asctime(&tm);
- if(q = strchr(p, '\n'))
- *q = '\0';
- return s_copy(p);
- }
|