URI:
       tmbox.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmbox.c (29364B)
       ---
            1 #include "common.h"
            2 #include <ctype.h>
            3 #include <plumb.h>
            4 #include <libsec.h>
            5 #include <thread.h>
            6 #include "dat.h"
            7 
            8 extern char* dirtab[]; /* jpc */
            9 
           10 typedef struct Header Header;
           11 
           12 struct Header {
           13         char *type;
           14         void (*f)(Message*, Header*, char*);
           15         int len;
           16 };
           17 
           18 /* headers */
           19 static        void        ctype(Message*, Header*, char*);
           20 static        void        cencoding(Message*, Header*, char*);
           21 static        void        cdisposition(Message*, Header*, char*);
           22 static        void        date822(Message*, Header*, char*);
           23 static        void        from822(Message*, Header*, char*);
           24 static        void        to822(Message*, Header*, char*);
           25 static        void        sender822(Message*, Header*, char*);
           26 static        void        replyto822(Message*, Header*, char*);
           27 static        void        subject822(Message*, Header*, char*);
           28 static        void        inreplyto822(Message*, Header*, char*);
           29 static        void        cc822(Message*, Header*, char*);
           30 static        void        bcc822(Message*, Header*, char*);
           31 static        void        messageid822(Message*, Header*, char*);
           32 static        void        mimeversion(Message*, Header*, char*);
           33 static        void        nullsqueeze(Message*);
           34 enum
           35 {
           36         Mhead=        11,        /* offset of first mime header */
           37 };
           38 
           39 Header head[] =
           40 {
           41         { "date:", date822, },
           42         { "from:", from822, },
           43         { "to:", to822, },
           44         { "sender:", sender822, },
           45         { "reply-to:", replyto822, },
           46         { "subject:", subject822, },
           47         { "cc:", cc822, },
           48         { "bcc:", bcc822, },
           49         { "in-reply-to:", inreplyto822, },
           50         { "mime-version:", mimeversion, },
           51         { "message-id:", messageid822, },
           52 
           53 [Mhead]        { "content-type:", ctype, },
           54         { "content-transfer-encoding:", cencoding, },
           55         { "content-disposition:", cdisposition, },
           56         { 0, }
           57 };
           58 
           59 /* static        void        fatal(char *fmt, ...); jpc */
           60 static        void        initquoted(void);
           61 /* static        void        startheader(Message*);
           62 static        void        startbody(Message*); jpc */
           63 static        char*        skipwhite(char*);
           64 static        char*        skiptosemi(char*);
           65 static        char*        getstring(char*, String*, int);
           66 static        void        setfilename(Message*, char*);
           67 /* static        char*        lowercase(char*); jpc */
           68 static        int        is8bit(Message*);
           69 static        int        headerline(char**, String*);
           70 static        void        initheaders(void);
           71 static void        parseattachments(Message*, Mailbox*);
           72 
           73 int                debug;
           74 
           75 char *Enotme = "path not served by this file server";
           76 
           77 enum
           78 {
           79         Chunksize = 1024
           80 };
           81 
           82 Mailboxinit *boxinit[] = {
           83         imap4mbox,
           84         pop3mbox,
           85         plan9mbox
           86 };
           87 
           88 char*
           89 syncmbox(Mailbox *mb, int doplumb)
           90 {
           91         return (*mb->sync)(mb, doplumb);
           92 }
           93 
           94 /* create a new mailbox */
           95 char*
           96 newmbox(char *path, char *name, int std)
           97 {
           98         Mailbox *mb, **l;
           99         char *p, *rv;
          100         int i;
          101 
          102         initheaders();
          103 
          104         mb = emalloc(sizeof(*mb));
          105         strncpy(mb->path, path, sizeof(mb->path)-1);
          106         if(name == nil){
          107                 p = strrchr(path, '/');
          108                 if(p == nil)
          109                         p = path;
          110                 else
          111                         p++;
          112                 if(*p == 0){
          113                         free(mb);
          114                         return "bad mbox name";
          115                 }
          116                 strncpy(mb->name, p, sizeof(mb->name)-1);
          117         } else {
          118                 strncpy(mb->name, name, sizeof(mb->name)-1);
          119         }
          120 
          121         rv = nil;
          122         /* check for a mailbox type */
          123         for(i=0; i<nelem(boxinit); i++)
          124                 if((rv = (*boxinit[i])(mb, path)) != Enotme)
          125                         break;
          126         if(i == nelem(boxinit)){
          127                 free(mb);
          128                 return "bad path";
          129         }
          130 
          131         /* on error, give up */
          132         if(rv){
          133                 free(mb);
          134                 return rv;
          135         }
          136 
          137         /* make sure name isn't taken */
          138         qlock(&mbllock);
          139         for(l = &mbl; *l != nil; l = &(*l)->next){
          140                 if(strcmp((*l)->name, mb->name) == 0){
          141                         if(strcmp(path, (*l)->path) == 0)
          142                                 rv = nil;
          143                         else
          144                                 rv = "mbox name in use";
          145                         if(mb->close)
          146                                 (*mb->close)(mb);
          147                         free(mb);
          148                         qunlock(&mbllock);
          149                         return rv;
          150                 }
          151         }
          152 
          153         /* always try locking */
          154         mb->dolock = 1;
          155 
          156         mb->refs = 1;
          157         mb->next = nil;
          158         mb->id = newid();
          159         mb->root = newmessage(nil);
          160         mb->std = std;
          161         *l = mb;
          162         qunlock(&mbllock);
          163 
          164         qlock(&mb->ql);
          165         if(mb->ctl){
          166                 henter(PATH(mb->id, Qmbox), "ctl",
          167                         (Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
          168         }
          169         rv = syncmbox(mb, 0);
          170         qunlock(&mb->ql);
          171 
          172         return rv;
          173 }
          174 
          175 /* close the named mailbox */
          176 void
          177 freembox(char *name)
          178 {
          179         Mailbox **l, *mb;
          180 
          181         qlock(&mbllock);
          182         for(l=&mbl; *l != nil; l=&(*l)->next){
          183                 if(strcmp(name, (*l)->name) == 0){
          184                         mb = *l;
          185                         *l = mb->next;
          186                         mboxdecref(mb);
          187                         break;
          188                 }
          189         }
          190         hfree(PATH(0, Qtop), name);
          191         qunlock(&mbllock);
          192 }
          193 
          194 static void
          195 initheaders(void)
          196 {
          197         Header *h;
          198         static int already;
          199 
          200         if(already)
          201                 return;
          202         already = 1;
          203 
          204         for(h = head; h->type != nil; h++)
          205                 h->len = strlen(h->type);
          206 }
          207 
          208 /*
          209  *  parse a Unix style header
          210  */
          211 void
          212 parseunix(Message *m)
          213 {
          214         char *p;
          215         String *h;
          216 
          217         h = s_new();
          218         for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
          219                 s_putc(h, *p);
          220         s_terminate(h);
          221         s_restart(h);
          222 
          223         m->unixfrom = s_parse(h, s_reset(m->unixfrom));
          224         m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
          225 
          226         s_free(h);
          227 }
          228 
          229 /*
          230  *  parse a message
          231  */
          232 void
          233 parseheaders(Message *m, int justmime, Mailbox *mb, int addfrom)
          234 {
          235         String *hl;
          236         Header *h;
          237         char *p, *q;
          238         int i;
          239 
          240         if(m->whole == m->whole->whole){
          241                 henter(PATH(mb->id, Qmbox), m->name,
          242                         (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
          243         } else {
          244                 henter(PATH(m->whole->id, Qdir), m->name,
          245                         (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
          246         }
          247         for(i = 0; i < Qmax; i++)
          248                 henter(PATH(m->id, Qdir), dirtab[i],
          249                         (Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
          250 
          251         /* parse mime headers */
          252         p = m->header;
          253         hl = s_new();
          254         while(headerline(&p, hl)){
          255                 if(justmime)
          256                         h = &head[Mhead];
          257                 else
          258                         h = head;
          259                 for(; h->type; h++){
          260                         if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
          261                                 (*h->f)(m, h, s_to_c(hl));
          262                                 break;
          263                         }
          264                 }
          265                 s_reset(hl);
          266         }
          267         s_free(hl);
          268 
          269         /* the blank line isn't really part of the body or header */
          270         if(justmime){
          271                 m->mhend = p;
          272                 m->hend = m->header;
          273         } else {
          274                 m->hend = p;
          275         }
          276         if(*p == '\n')
          277                 p++;
          278         m->rbody = m->body = p;
          279 
          280         /* if type is text, get any nulls out of the body.  This is */
          281         /* for the two seans and imap clients that get confused. */
          282         if(strncmp(s_to_c(m->type), "text/", 5) == 0)
          283                 nullsqueeze(m);
          284 
          285         /* */
          286         /* cobble together Unix-style from line */
          287         /* for local mailbox messages, we end up recreating the */
          288         /* original header. */
          289         /* for pop3 messages, the best we can do is  */
          290         /* use the From: information and the RFC822 date. */
          291         /* */
          292         if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0
          293         || strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){
          294                 if(m->unixdate){
          295                         s_free(m->unixdate);
          296                         m->unixdate = nil;
          297                 }
          298                 /* look for the date in the first Received: line. */
          299                 /* it's likely to be the right time zone (it's */
          300                  /* the local system) and in a convenient format. */
          301                 if(cistrncmp(m->header, "received:", 9)==0){
          302                         if((q = strchr(m->header, ';')) != nil){
          303                                 p = q;
          304                                 while((p = strchr(p, '\n')) != nil){
          305                                         if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
          306                                                 break;
          307                                         p++;
          308                                 }
          309                                 if(p){
          310                                         *p = '\0';
          311                                         m->unixdate = date822tounix(q+1);
          312                                         *p = '\n';
          313                                 }
          314                         }
          315                 }
          316 
          317                 /* fall back on the rfc822 date         */
          318                 if(m->unixdate==nil && m->date822)
          319                         m->unixdate = date822tounix(s_to_c(m->date822));
          320         }
          321 
          322         if(m->unixheader != nil)
          323                 s_free(m->unixheader);
          324 
          325         /* only fake header for top-level messages for pop3 and imap4 */
          326         /* clients (those protocols don't include the unix header). */
          327         /* adding the unix header all the time screws up mime-attached */
          328         /* rfc822 messages. */
          329         if(!addfrom && !m->unixfrom){
          330                 m->unixheader = nil;
          331                 return;
          332         }
          333 
          334         m->unixheader = s_copy("From ");
          335         if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0)
          336                 s_append(m->unixheader, s_to_c(m->unixfrom));
          337         else if(m->from822)
          338                 s_append(m->unixheader, s_to_c(m->from822));
          339         else
          340                 s_append(m->unixheader, "???");
          341 
          342         s_append(m->unixheader, " ");
          343         if(m->unixdate)
          344                 s_append(m->unixheader, s_to_c(m->unixdate));
          345         else
          346                 s_append(m->unixheader, "Thu Jan  1 00:00:00 GMT 1970");
          347 
          348         s_append(m->unixheader, "\n");
          349 }
          350 
          351 String*
          352 promote(String **sp)
          353 {
          354         String *s;
          355 
          356         if(*sp != nil)
          357                 s = s_clone(*sp);
          358         else
          359                 s = nil;
          360         return s;
          361 }
          362 
          363 void
          364 parsebody(Message *m, Mailbox *mb)
          365 {
          366         Message *nm;
          367 
          368         /* recurse */
          369         if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
          370                 parseattachments(m, mb);
          371         } else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
          372                 decode(m);
          373                 parseattachments(m, mb);
          374                 nm = m->part;
          375 
          376                 /* promote headers */
          377                 if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
          378                         m->from822 = promote(&nm->from822);
          379                         m->to822 = promote(&nm->to822);
          380                         m->date822 = promote(&nm->date822);
          381                         m->sender822 = promote(&nm->sender822);
          382                         m->replyto822 = promote(&nm->replyto822);
          383                         m->subject822 = promote(&nm->subject822);
          384                         m->unixdate = promote(&nm->unixdate);
          385                 }
          386         }
          387 }
          388 
          389 void
          390 parse(Message *m, int justmime, Mailbox *mb, int addfrom)
          391 {
          392         parseheaders(m, justmime, mb, addfrom);
          393         parsebody(m, mb);
          394 }
          395 
          396 static void
          397 parseattachments(Message *m, Mailbox *mb)
          398 {
          399         Message *nm, **l;
          400         char *p, *x;
          401 
          402         /* if there's a boundary, recurse... */
          403         if(m->boundary != nil){
          404                 p = m->body;
          405                 nm = nil;
          406                 l = &m->part;
          407                 for(;;){
          408                         x = strstr(p, s_to_c(m->boundary));
          409 
          410                         /* no boundary, we're done */
          411                         if(x == nil){
          412                                 if(nm != nil)
          413                                         nm->rbend = nm->bend = nm->end = m->bend;
          414                                 break;
          415                         }
          416 
          417                         /* boundary must be at the start of a line */
          418                         if(x != m->body && *(x-1) != '\n'){
          419                                 p = x+1;
          420                                 continue;
          421                         }
          422 
          423                         if(nm != nil)
          424                                 nm->rbend = nm->bend = nm->end = x;
          425                         x += strlen(s_to_c(m->boundary));
          426 
          427                         /* is this the last part? ignore anything after it */
          428                         if(strncmp(x, "--", 2) == 0)
          429                                 break;
          430 
          431                         p = strchr(x, '\n');
          432                         if(p == nil)
          433                                 break;
          434                         nm = newmessage(m);
          435                         nm->start = nm->header = nm->body = nm->rbody = ++p;
          436                         nm->mheader = nm->header;
          437                         *l = nm;
          438                         l = &nm->next;
          439                 }
          440                 for(nm = m->part; nm != nil; nm = nm->next)
          441                         parse(nm, 1, mb, 0);
          442                 return;
          443         }
          444 
          445         /* if we've got an rfc822 message, recurse... */
          446         if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
          447                 nm = newmessage(m);
          448                 m->part = nm;
          449                 nm->start = nm->header = nm->body = nm->rbody = m->body;
          450                 nm->end = nm->bend = nm->rbend = m->bend;
          451                 parse(nm, 0, mb, 0);
          452         }
          453 }
          454 
          455 /*
          456  *  pick up a header line
          457  */
          458 static int
          459 headerline(char **pp, String *hl)
          460 {
          461         char *p, *x;
          462 
          463         s_reset(hl);
          464         p = *pp;
          465         x = strpbrk(p, ":\n");
          466         if(x == nil || *x == '\n')
          467                 return 0;
          468         for(;;){
          469                 x = strchr(p, '\n');
          470                 if(x == nil)
          471                         x = p + strlen(p);
          472                 s_nappend(hl, p, x-p);
          473                 p = x;
          474                 if(*p != '\n' || *++p != ' ' && *p != '\t')
          475                         break;
          476                 while(*p == ' ' || *p == '\t')
          477                         p++;
          478                 s_putc(hl, ' ');
          479         }
          480         *pp = p;
          481         return 1;
          482 }
          483 
          484 static String*
          485 addr822(char *p)
          486 {
          487         String *s, *list;
          488         int incomment, addrdone, inanticomment, quoted;
          489         int n;
          490         int c;
          491 
          492         list = s_new();
          493         s = s_new();
          494         quoted = incomment = addrdone = inanticomment = 0;
          495         n = 0;
          496         for(; *p; p++){
          497                 c = *p;
          498 
          499                 /* whitespace is ignored */
          500                 if(!quoted && isspace(c) || c == '\r')
          501                         continue;
          502 
          503                 /* strings are always treated as atoms */
          504                 if(!quoted && c == '"'){
          505                         if(!addrdone && !incomment)
          506                                 s_putc(s, c);
          507                         for(p++; *p; p++){
          508                                 if(!addrdone && !incomment)
          509                                         s_putc(s, *p);
          510                                 if(!quoted && *p == '"')
          511                                         break;
          512                                 if(*p == '\\')
          513                                         quoted = 1;
          514                                 else
          515                                         quoted = 0;
          516                         }
          517                         if(*p == 0)
          518                                 break;
          519                         quoted = 0;
          520                         continue;
          521                 }
          522 
          523                 /* ignore everything in an expicit comment */
          524                 if(!quoted && c == '('){
          525                         incomment = 1;
          526                         continue;
          527                 }
          528                 if(incomment){
          529                         if(!quoted && c == ')')
          530                                 incomment = 0;
          531                         quoted = 0;
          532                         continue;
          533                 }
          534 
          535                 /* anticomments makes everything outside of them comments */
          536                 if(!quoted && c == '<' && !inanticomment){
          537                         inanticomment = 1;
          538                         s = s_reset(s);
          539                         continue;
          540                 }
          541                 if(!quoted && c == '>' && inanticomment){
          542                         addrdone = 1;
          543                         inanticomment = 0;
          544                         continue;
          545                 }
          546 
          547                 /* commas separate addresses */
          548                 if(!quoted && c == ',' && !inanticomment){
          549                         s_terminate(s);
          550                         addrdone = 0;
          551                         if(n++ != 0)
          552                                 s_append(list, " ");
          553                         s_append(list, s_to_c(s));
          554                         s = s_reset(s);
          555                         continue;
          556                 }
          557 
          558                 /* what's left is part of the address */
          559                 s_putc(s, c);
          560 
          561                 /* quoted characters are recognized only as characters */
          562                 if(c == '\\')
          563                         quoted = 1;
          564                 else
          565                         quoted = 0;
          566 
          567         }
          568 
          569         if(*s_to_c(s) != 0){
          570                 s_terminate(s);
          571                 if(n++ != 0)
          572                         s_append(list, " ");
          573                 s_append(list, s_to_c(s));
          574         }
          575         s_free(s);
          576 
          577         if(n == 0){
          578                 s_free(list);
          579                 return nil;
          580         }
          581         return list;
          582 }
          583 
          584 static void
          585 to822(Message *m, Header *h, char *p)
          586 {
          587         p += strlen(h->type);
          588         s_free(m->to822);
          589         m->to822 = addr822(p);
          590 }
          591 
          592 static void
          593 cc822(Message *m, Header *h, char *p)
          594 {
          595         p += strlen(h->type);
          596         s_free(m->cc822);
          597         m->cc822 = addr822(p);
          598 }
          599 
          600 static void
          601 bcc822(Message *m, Header *h, char *p)
          602 {
          603         p += strlen(h->type);
          604         s_free(m->bcc822);
          605         m->bcc822 = addr822(p);
          606 }
          607 
          608 static void
          609 from822(Message *m, Header *h, char *p)
          610 {
          611         p += strlen(h->type);
          612         s_free(m->from822);
          613         m->from822 = addr822(p);
          614 }
          615 
          616 static void
          617 sender822(Message *m, Header *h, char *p)
          618 {
          619         p += strlen(h->type);
          620         s_free(m->sender822);
          621         m->sender822 = addr822(p);
          622 }
          623 
          624 static void
          625 replyto822(Message *m, Header *h, char *p)
          626 {
          627         p += strlen(h->type);
          628         s_free(m->replyto822);
          629         m->replyto822 = addr822(p);
          630 }
          631 
          632 static void
          633 mimeversion(Message *m, Header *h, char *p)
          634 {
          635         p += strlen(h->type);
          636         s_free(m->mimeversion);
          637         m->mimeversion = addr822(p);
          638 }
          639 
          640 static void
          641 killtrailingwhite(char *p)
          642 {
          643         char *e;
          644 
          645         e = p + strlen(p) - 1;
          646         while(e > p && isspace(*e))
          647                 *e-- = 0;
          648 }
          649 
          650 static void
          651 date822(Message *m, Header *h, char *p)
          652 {
          653         p += strlen(h->type);
          654         p = skipwhite(p);
          655         s_free(m->date822);
          656         m->date822 = s_copy(p);
          657         p = s_to_c(m->date822);
          658         killtrailingwhite(p);
          659 }
          660 
          661 static void
          662 subject822(Message *m, Header *h, char *p)
          663 {
          664         p += strlen(h->type);
          665         p = skipwhite(p);
          666         s_free(m->subject822);
          667         m->subject822 = s_copy(p);
          668         p = s_to_c(m->subject822);
          669         killtrailingwhite(p);
          670 }
          671 
          672 static void
          673 inreplyto822(Message *m, Header *h, char *p)
          674 {
          675         p += strlen(h->type);
          676         p = skipwhite(p);
          677         s_free(m->inreplyto822);
          678         m->inreplyto822 = s_copy(p);
          679         p = s_to_c(m->inreplyto822);
          680         killtrailingwhite(p);
          681 }
          682 
          683 static void
          684 messageid822(Message *m, Header *h, char *p)
          685 {
          686         p += strlen(h->type);
          687         p = skipwhite(p);
          688         s_free(m->messageid822);
          689         m->messageid822 = s_copy(p);
          690         p = s_to_c(m->messageid822);
          691         killtrailingwhite(p);
          692 }
          693 
          694 static int
          695 isattribute(char **pp, char *attr)
          696 {
          697         char *p;
          698         int n;
          699 
          700         n = strlen(attr);
          701         p = *pp;
          702         if(cistrncmp(p, attr, n) != 0)
          703                 return 0;
          704         p += n;
          705         while(*p == ' ')
          706                 p++;
          707         if(*p++ != '=')
          708                 return 0;
          709         while(*p == ' ')
          710                 p++;
          711         *pp = p;
          712         return 1;
          713 }
          714 
          715 static void
          716 ctype(Message *m, Header *h, char *p)
          717 {
          718         String *s;
          719 
          720         p += h->len;
          721         p = skipwhite(p);
          722 
          723         p = getstring(p, m->type, 1);
          724 
          725         while(*p){
          726                 if(isattribute(&p, "boundary")){
          727                         s = s_new();
          728                         p = getstring(p, s, 0);
          729                         m->boundary = s_reset(m->boundary);
          730                         s_append(m->boundary, "--");
          731                         s_append(m->boundary, s_to_c(s));
          732                         s_free(s);
          733                 } else if(cistrncmp(p, "multipart", 9) == 0){
          734                         /*
          735                          *  the first unbounded part of a multipart message,
          736                          *  the preamble, is not displayed or saved
          737                          */
          738                 } else if(isattribute(&p, "name")){
          739                         if(m->filename == nil)
          740                                 setfilename(m, p);
          741                 } else if(isattribute(&p, "charset")){
          742                         p = getstring(p, s_reset(m->charset), 0);
          743                 }
          744 
          745                 p = skiptosemi(p);
          746         }
          747 }
          748 
          749 static void
          750 cencoding(Message *m, Header *h, char *p)
          751 {
          752         p += h->len;
          753         p = skipwhite(p);
          754         if(cistrncmp(p, "base64", 6) == 0)
          755                 m->encoding = Ebase64;
          756         else if(cistrncmp(p, "quoted-printable", 16) == 0)
          757                 m->encoding = Equoted;
          758 }
          759 
          760 static void
          761 cdisposition(Message *m, Header *h, char *p)
          762 {
          763         p += h->len;
          764         p = skipwhite(p);
          765         while(*p){
          766                 if(cistrncmp(p, "inline", 6) == 0){
          767                         m->disposition = Dinline;
          768                 } else if(cistrncmp(p, "attachment", 10) == 0){
          769                         m->disposition = Dfile;
          770                 } else if(cistrncmp(p, "filename=", 9) == 0){
          771                         p += 9;
          772                         setfilename(m, p);
          773                 }
          774                 p = skiptosemi(p);
          775         }
          776 
          777 }
          778 
          779 ulong msgallocd, msgfreed;
          780 
          781 Message*
          782 newmessage(Message *parent)
          783 {
          784         /* static int id; jpc */
          785         Message *m;
          786 
          787         msgallocd++;
          788 
          789         m = emalloc(sizeof(*m));
          790         memset(m, 0, sizeof(*m));
          791         m->disposition = Dnone;
          792         m->type = s_copy("text/plain");
          793         m->charset = s_copy("iso-8859-1");
          794         m->id = newid();
          795         if(parent)
          796                 sprint(m->name, "%d", ++(parent->subname));
          797         if(parent == nil)
          798                 parent = m;
          799         m->whole = parent;
          800         m->hlen = -1;
          801         return m;
          802 }
          803 
          804 /* delete a message from a mailbox */
          805 void
          806 delmessage(Mailbox *mb, Message *m)
          807 {
          808         Message **l;
          809         int i;
          810 
          811         mb->vers++;
          812         msgfreed++;
          813 
          814         if(m->whole != m){
          815                 /* unchain from parent */
          816                 for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
          817                         ;
          818                 if(*l != nil)
          819                         *l = m->next;
          820 
          821                 /* clear out of name lookup hash table */
          822                 if(m->whole->whole == m->whole)
          823                         hfree(PATH(mb->id, Qmbox), m->name);
          824                 else
          825                         hfree(PATH(m->whole->id, Qdir), m->name);
          826                 for(i = 0; i < Qmax; i++)
          827                         hfree(PATH(m->id, Qdir), dirtab[i]);
          828         }
          829 
          830         /* recurse through sub-parts */
          831         while(m->part)
          832                 delmessage(mb, m->part);
          833 
          834         /* free memory */
          835         if(m->mallocd)
          836                 free(m->start);
          837         if(m->hallocd)
          838                 free(m->header);
          839         if(m->ballocd)
          840                 free(m->body);
          841         s_free(m->unixfrom);
          842         s_free(m->unixdate);
          843         s_free(m->unixheader);
          844         s_free(m->from822);
          845         s_free(m->sender822);
          846         s_free(m->to822);
          847         s_free(m->bcc822);
          848         s_free(m->cc822);
          849         s_free(m->replyto822);
          850         s_free(m->date822);
          851         s_free(m->inreplyto822);
          852         s_free(m->subject822);
          853         s_free(m->messageid822);
          854         s_free(m->addrs);
          855         s_free(m->mimeversion);
          856         s_free(m->sdigest);
          857         s_free(m->boundary);
          858         s_free(m->type);
          859         s_free(m->charset);
          860         s_free(m->filename);
          861 
          862         free(m);
          863 }
          864 
          865 /* mark messages (identified by path) for deletion */
          866 void
          867 delmessages(int ac, char **av)
          868 {
          869         Mailbox *mb;
          870         Message *m;
          871         int i, needwrite;
          872 
          873         qlock(&mbllock);
          874         for(mb = mbl; mb != nil; mb = mb->next)
          875                 if(strcmp(av[0], mb->name) == 0){
          876                         qlock(&mb->ql);
          877                         break;
          878                 }
          879         qunlock(&mbllock);
          880         if(mb == nil)
          881                 return;
          882 
          883         needwrite = 0;
          884         for(i = 1; i < ac; i++){
          885                 for(m = mb->root->part; m != nil; m = m->next)
          886                         if(strcmp(m->name, av[i]) == 0){
          887                                 if(!m->deleted){
          888                                         mailplumb(mb, m, 1);
          889                                         needwrite = 1;
          890                                         m->deleted = 1;
          891                                         logmsg("deleting", m);
          892                                 }
          893                                 break;
          894                         }
          895         }
          896         if(needwrite)
          897                 syncmbox(mb, 1);
          898         qunlock(&mb->ql);
          899 }
          900 
          901 /*
          902  *  the following are called with the mailbox qlocked
          903  */
          904 void
          905 msgincref(Message *m)
          906 {
          907         m->refs++;
          908 }
          909 void
          910 msgdecref(Mailbox *mb, Message *m)
          911 {
          912         m->refs--;
          913         if(m->refs == 0 && m->deleted)
          914                 syncmbox(mb, 1);
          915 }
          916 
          917 /*
          918  *  the following are called with mbllock'd
          919  */
          920 void
          921 mboxincref(Mailbox *mb)
          922 {
          923         assert(mb->refs > 0);
          924         mb->refs++;
          925 }
          926 void
          927 mboxdecref(Mailbox *mb)
          928 {
          929         assert(mb->refs > 0);
          930         qlock(&mb->ql);
          931         mb->refs--;
          932         if(mb->refs == 0){
          933                 delmessage(mb, mb->root);
          934                 if(mb->ctl)
          935                         hfree(PATH(mb->id, Qmbox), "ctl");
          936                 if(mb->close)
          937                         (*mb->close)(mb);
          938                 free(mb);
          939         } else
          940                 qunlock(&mb->ql);
          941 }
          942 
          943 int
          944 cistrncmp(char *a, char *b, int n)
          945 {
          946         while(n-- > 0){
          947                 if(tolower(*a++) != tolower(*b++))
          948                         return -1;
          949         }
          950         return 0;
          951 }
          952 
          953 int
          954 cistrcmp(char *a, char *b)
          955 {
          956         for(;;){
          957                 if(tolower(*a) != tolower(*b++))
          958                         return -1;
          959                 if(*a++ == 0)
          960                         break;
          961         }
          962         return 0;
          963 }
          964 
          965 static char*
          966 skipwhite(char *p)
          967 {
          968         while(isspace(*p))
          969                 p++;
          970         return p;
          971 }
          972 
          973 static char*
          974 skiptosemi(char *p)
          975 {
          976         while(*p && *p != ';')
          977                 p++;
          978         while(*p == ';' || isspace(*p))
          979                 p++;
          980         return p;
          981 }
          982 
          983 static char*
          984 getstring(char *p, String *s, int dolower)
          985 {
          986         s = s_reset(s);
          987         p = skipwhite(p);
          988         if(*p == '"'){
          989                 p++;
          990                 for(;*p && *p != '"'; p++)
          991                         if(dolower)
          992                                 s_putc(s, tolower(*p));
          993                         else
          994                                 s_putc(s, *p);
          995                 if(*p == '"')
          996                         p++;
          997                 s_terminate(s);
          998 
          999                 return p;
         1000         }
         1001 
         1002         for(; *p && !isspace(*p) && *p != ';'; p++)
         1003                 if(dolower)
         1004                         s_putc(s, tolower(*p));
         1005                 else
         1006                         s_putc(s, *p);
         1007         s_terminate(s);
         1008 
         1009         return p;
         1010 }
         1011 
         1012 static void
         1013 setfilename(Message *m, char *p)
         1014 {
         1015         m->filename = s_reset(m->filename);
         1016         getstring(p, m->filename, 0);
         1017         for(p = s_to_c(m->filename); *p; p++)
         1018                 if(*p == ' ' || *p == '\t' || *p == ';')
         1019                         *p = '_';
         1020 }
         1021 
         1022 /* */
         1023 /* undecode message body */
         1024 /* */
         1025 void
         1026 decode(Message *m)
         1027 {
         1028         int i, len;
         1029         char *x;
         1030 
         1031         if(m->decoded)
         1032                 return;
         1033         switch(m->encoding){
         1034         case Ebase64:
         1035                 len = m->bend - m->body;
         1036                 i = (len*3)/4+1;        /* room for max chars + null */
         1037                 x = emalloc(i);
         1038                 len = dec64((uchar*)x, i, m->body, len);
         1039                 if(m->ballocd)
         1040                         free(m->body);
         1041                 m->body = x;
         1042                 m->bend = x + len;
         1043                 m->ballocd = 1;
         1044                 break;
         1045         case Equoted:
         1046                 len = m->bend - m->body;
         1047                 x = emalloc(len+2);        /* room for null and possible extra nl */
         1048                 len = decquoted(x, m->body, m->bend);
         1049                 if(m->ballocd)
         1050                         free(m->body);
         1051                 m->body = x;
         1052                 m->bend = x + len;
         1053                 m->ballocd = 1;
         1054                 break;
         1055         default:
         1056                 break;
         1057         }
         1058         m->decoded = 1;
         1059 }
         1060 
         1061 /* convert latin1 to utf */
         1062 void
         1063 convert(Message *m)
         1064 {
         1065         int len;
         1066         char *x;
         1067 
         1068         /* don't convert if we're not a leaf, not text, or already converted */
         1069         if(m->converted)
         1070                 return;
         1071         if(m->part != nil)
         1072                 return;
         1073         if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
         1074                 return;
         1075 
         1076         if(cistrcmp(s_to_c(m->charset), "us-ascii") == 0 ||
         1077            cistrcmp(s_to_c(m->charset), "iso-8859-1") == 0){
         1078                 len = is8bit(m);
         1079                 if(len > 0){
         1080                         len = 2*len + m->bend - m->body + 1;
         1081                         x = emalloc(len);
         1082                         len = latin1toutf(x, m->body, m->bend);
         1083                         if(m->ballocd)
         1084                                 free(m->body);
         1085                         m->body = x;
         1086                         m->bend = x + len;
         1087                         m->ballocd = 1;
         1088                 }
         1089         } else if(cistrcmp(s_to_c(m->charset), "iso-8859-2") == 0){
         1090                 len = xtoutf("8859-2", &x, m->body, m->bend);
         1091                 if(len != 0){
         1092                         if(m->ballocd)
         1093                                 free(m->body);
         1094                         m->body = x;
         1095                         m->bend = x + len;
         1096                         m->ballocd = 1;
         1097                 }
         1098         } else if(cistrcmp(s_to_c(m->charset), "iso-8859-15") == 0){
         1099                 len = xtoutf("8859-15", &x, m->body, m->bend);
         1100                 if(len != 0){
         1101                         if(m->ballocd)
         1102                                 free(m->body);
         1103                         m->body = x;
         1104                         m->bend = x + len;
         1105                         m->ballocd = 1;
         1106                 }
         1107         } else if(cistrcmp(s_to_c(m->charset), "big5") == 0){
         1108                 len = xtoutf("big5", &x, m->body, m->bend);
         1109                 if(len != 0){
         1110                         if(m->ballocd)
         1111                                 free(m->body);
         1112                         m->body = x;
         1113                         m->bend = x + len;
         1114                         m->ballocd = 1;
         1115                 }
         1116         } else if(cistrcmp(s_to_c(m->charset), "iso-2022-jp") == 0){
         1117                 len = xtoutf("jis", &x, m->body, m->bend);
         1118                 if(len != 0){
         1119                         if(m->ballocd)
         1120                                 free(m->body);
         1121                         m->body = x;
         1122                         m->bend = x + len;
         1123                         m->ballocd = 1;
         1124                 }
         1125         } else if(cistrcmp(s_to_c(m->charset), "windows-1257") == 0
         1126                         || cistrcmp(s_to_c(m->charset), "windows-1252") == 0){
         1127                 len = is8bit(m);
         1128                 if(len > 0){
         1129                         len = 2*len + m->bend - m->body + 1;
         1130                         x = emalloc(len);
         1131                         len = windows1257toutf(x, m->body, m->bend);
         1132                         if(m->ballocd)
         1133                                 free(m->body);
         1134                         m->body = x;
         1135                         m->bend = x + len;
         1136                         m->ballocd = 1;
         1137                 }
         1138         } else if(cistrcmp(s_to_c(m->charset), "windows-1251") == 0){
         1139                 len = xtoutf("cp1251", &x, m->body, m->bend);
         1140                 if(len != 0){
         1141                         if(m->ballocd)
         1142                                 free(m->body);
         1143                         m->body = x;
         1144                         m->bend = x + len;
         1145                         m->ballocd = 1;
         1146                 }
         1147         } else if(cistrcmp(s_to_c(m->charset), "koi8-r") == 0){
         1148                 len = xtoutf("koi8", &x, m->body, m->bend);
         1149                 if(len != 0){
         1150                         if(m->ballocd)
         1151                                 free(m->body);
         1152                         m->body = x;
         1153                         m->bend = x + len;
         1154                         m->ballocd = 1;
         1155                 }
         1156         }
         1157 
         1158         m->converted = 1;
         1159 }
         1160 
         1161 enum
         1162 {
         1163         Self=        1,
         1164         Hex=        2
         1165 };
         1166 uchar        tableqp[256];
         1167 
         1168 static void
         1169 initquoted(void)
         1170 {
         1171         int c;
         1172 
         1173         memset(tableqp, 0, 256);
         1174         for(c = ' '; c <= '<'; c++)
         1175                 tableqp[c] = Self;
         1176         for(c = '>'; c <= '~'; c++)
         1177                 tableqp[c] = Self;
         1178         tableqp['\t'] = Self;
         1179         tableqp['='] = Hex;
         1180 }
         1181 
         1182 static int
         1183 hex2int(int x)
         1184 {
         1185         if(x >= '0' && x <= '9')
         1186                 return x - '0';
         1187         if(x >= 'A' && x <= 'F')
         1188                 return (x - 'A') + 10;
         1189         if(x >= 'a' && x <= 'f')
         1190                 return (x - 'a') + 10;
         1191         return 0;
         1192 }
         1193 
         1194 static char*
         1195 decquotedline(char *out, char *in, char *e)
         1196 {
         1197         int c, soft;
         1198 
         1199         /* dump trailing white space */
         1200         while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
         1201                 e--;
         1202 
         1203         /* trailing '=' means no newline */
         1204         if(*e == '='){
         1205                 soft = 1;
         1206                 e--;
         1207         } else
         1208                 soft = 0;
         1209 
         1210         while(in <= e){
         1211                 c = (*in++) & 0xff;
         1212                 switch(tableqp[c]){
         1213                 case Self:
         1214                         *out++ = c;
         1215                         break;
         1216                 case Hex:
         1217                         c = hex2int(*in++)<<4;
         1218                         c |= hex2int(*in++);
         1219                         *out++ = c;
         1220                         break;
         1221                 }
         1222         }
         1223         if(!soft)
         1224                 *out++ = '\n';
         1225         *out = 0;
         1226 
         1227         return out;
         1228 }
         1229 
         1230 int
         1231 decquoted(char *out, char *in, char *e)
         1232 {
         1233         char *p, *nl;
         1234 
         1235         if(tableqp[' '] == 0)
         1236                 initquoted();
         1237 
         1238         p = out;
         1239         while((nl = strchr(in, '\n')) != nil && nl < e){
         1240                 p = decquotedline(p, in, nl);
         1241                 in = nl + 1;
         1242         }
         1243         if(in < e)
         1244                 p = decquotedline(p, in, e-1);
         1245 
         1246         /* make sure we end with a new line */
         1247         if(*(p-1) != '\n'){
         1248                 *p++ = '\n';
         1249                 *p = 0;
         1250         }
         1251 
         1252         return p - out;
         1253 }
         1254 
         1255 #if 0 /* jpc */
         1256 static char*
         1257 lowercase(char *p)
         1258 {
         1259         char *op;
         1260         int c;
         1261 
         1262         for(op = p; c = *p; p++)
         1263                 if(isupper(c))
         1264                         *p = tolower(c);
         1265         return op;
         1266 }
         1267 #endif
         1268 
         1269 /*
         1270  *  return number of 8 bit characters
         1271  */
         1272 static int
         1273 is8bit(Message *m)
         1274 {
         1275         int count = 0;
         1276         char *p;
         1277 
         1278         for(p = m->body; p < m->bend; p++)
         1279                 if(*p & 0x80)
         1280                         count++;
         1281         return count;
         1282 }
         1283 
         1284 /* translate latin1 directly since it fits neatly in utf */
         1285 int
         1286 latin1toutf(char *out, char *in, char *e)
         1287 {
         1288         Rune r;
         1289         char *p;
         1290 
         1291         p = out;
         1292         for(; in < e; in++){
         1293                 r = (*in) & 0xff;
         1294                 p += runetochar(p, &r);
         1295         }
         1296         *p = 0;
         1297         return p - out;
         1298 }
         1299 
         1300 /* translate any thing else using the tcs program */
         1301 int
         1302 xtoutf(char *charset, char **out, char *in, char *e)
         1303 {
         1304         char *av[4];
         1305         int totcs[2];
         1306         int fromtcs[2];
         1307         int n, len, sofar;
         1308         char *p;
         1309 
         1310         len = e-in+1;
         1311         sofar = 0;
         1312         *out = p = malloc(len+1);
         1313         if(p == nil)
         1314                 return 0;
         1315 
         1316         av[0] = charset;
         1317         av[1] = "-f";
         1318         av[2] = charset;
         1319         av[3] = 0;
         1320         if(pipe(totcs) < 0)
         1321                 return 0;
         1322         if(pipe(fromtcs) < 0){
         1323                 close(totcs[0]); close(totcs[1]);
         1324                 return 0;
         1325         }
         1326         switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
         1327         case -1:
         1328                 close(fromtcs[0]); close(fromtcs[1]);
         1329                 close(totcs[0]); close(totcs[1]);
         1330                 return 0;
         1331         case 0:
         1332                 close(fromtcs[0]); close(totcs[1]);
         1333                 dup(fromtcs[1], 1);
         1334                 dup(totcs[0], 0);
         1335                 close(fromtcs[1]); close(totcs[0]);
         1336                 dup(open("/dev/null", OWRITE), 2);
         1337                 /*jpc exec("/bin/tcs", av); */
         1338                 exec(unsharp("#9/bin/tcs"), av);
         1339                 /* _exits(0); */
         1340                 threadexits(nil);
         1341         default:
         1342                 close(fromtcs[1]); close(totcs[0]);
         1343                 switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
         1344                 case -1:
         1345                         close(fromtcs[0]); close(totcs[1]);
         1346                         return 0;
         1347                 case 0:
         1348                         close(fromtcs[0]);
         1349                         while(in < e){
         1350                                 n = write(totcs[1], in, e-in);
         1351                                 if(n <= 0)
         1352                                         break;
         1353                                 in += n;
         1354                         }
         1355                         close(totcs[1]);
         1356                         /* _exits(0); */
         1357                         threadexits(nil);
         1358                 default:
         1359                         close(totcs[1]);
         1360                         for(;;){
         1361                                 n = read(fromtcs[0], &p[sofar], len-sofar);
         1362                                 if(n <= 0)
         1363                                         break;
         1364                                 sofar += n;
         1365                                 p[sofar] = 0;
         1366                                 if(sofar == len){
         1367                                         len += 1024;
         1368                                         *out = p = realloc(p, len+1);
         1369                                         if(p == nil)
         1370                                                 return 0;
         1371                                 }
         1372                         }
         1373                         close(fromtcs[0]);
         1374                         break;
         1375                 }
         1376                 break;
         1377         }
         1378         return sofar;
         1379 }
         1380 
         1381 enum {
         1382         Winstart= 0x7f,
         1383         Winend= 0x9f
         1384 };
         1385 
         1386 Rune winchars[] = {
         1387         L'•',
         1388         L'•', L'•', L'‚', L'ƒ', L'„', L'…', L'†', L'‡',
         1389         L'ˆ', L'‰', L'Š', L'‹', L'Œ', L'•', L'•', L'•',
         1390         L'•', L'‘', L'’', L'“', L'”', L'•', L'–', L'—',
         1391         L'˜', L'™', L'š', L'›', L'œ', L'•', L'•', L'Ÿ'
         1392 };
         1393 
         1394 int
         1395 windows1257toutf(char *out, char *in, char *e)
         1396 {
         1397         Rune r;
         1398         char *p;
         1399 
         1400         p = out;
         1401         for(; in < e; in++){
         1402                 r = (*in) & 0xff;
         1403                 if(r >= 0x7f && r <= 0x9f)
         1404                         r = winchars[r-0x7f];
         1405                 p += runetochar(p, &r);
         1406         }
         1407         *p = 0;
         1408         return p - out;
         1409 }
         1410 
         1411 void *
         1412 emalloc(ulong n)
         1413 {
         1414         void *p;
         1415 
         1416         p = mallocz(n, 1);
         1417         if(!p){
         1418                 fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
         1419                 threadexits("out of memory");
         1420         }
         1421         setmalloctag(p, getcallerpc(&n));
         1422         return p;
         1423 }
         1424 
         1425 void *
         1426 erealloc(void *p, ulong n)
         1427 {
         1428         if(n == 0)
         1429                 n = 1;
         1430         p = realloc(p, n);
         1431         if(!p){
         1432                 fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
         1433                 threadexits("out of memory");
         1434         }
         1435         setrealloctag(p, getcallerpc(&p));
         1436         return p;
         1437 }
         1438 
         1439 void
         1440 mailplumb(Mailbox *mb, Message *m, int delete)
         1441 {
         1442         Plumbmsg p;
         1443         Plumbattr a[7];
         1444         char buf[256];
         1445         int ai;
         1446         char lenstr[10], *from, *subject, *date;
         1447         static int fd = -1;
         1448 
         1449         if(m->subject822 == nil)
         1450                 subject = "";
         1451         else
         1452                 subject = s_to_c(m->subject822);
         1453 
         1454         if(m->from822 != nil)
         1455                 from = s_to_c(m->from822);
         1456         else if(m->unixfrom != nil)
         1457                 from = s_to_c(m->unixfrom);
         1458         else
         1459                 from = "";
         1460 
         1461         if(m->unixdate != nil)
         1462                 date = s_to_c(m->unixdate);
         1463         else
         1464                 date = "";
         1465 
         1466         sprint(lenstr, "%ld", m->end-m->start);
         1467 
         1468         if(biffing && !delete)
         1469                 print("[ %s / %s / %s ]\n", from, subject, lenstr);
         1470 
         1471         if(!plumbing)
         1472                 return;
         1473 
         1474         if(fd < 0)
         1475                 fd = plumbopen("send", OWRITE);
         1476         if(fd < 0)
         1477                 return;
         1478 
         1479         p.src = "mailfs";
         1480         p.dst = "seemail";
         1481         p.wdir = "/mail/fs";
         1482         p.type = "text";
         1483 
         1484         ai = 0;
         1485         a[ai].name = "filetype";
         1486         a[ai].value = "mail";
         1487 
         1488         a[++ai].name = "sender";
         1489         a[ai].value = from;
         1490         a[ai-1].next = &a[ai];
         1491 
         1492         a[++ai].name = "length";
         1493         a[ai].value = lenstr;
         1494         a[ai-1].next = &a[ai];
         1495 
         1496         a[++ai].name = "mailtype";
         1497         a[ai].value = delete?"delete":"new";
         1498         a[ai-1].next = &a[ai];
         1499 
         1500         a[++ai].name = "date";
         1501         a[ai].value = date;
         1502         a[ai-1].next = &a[ai];
         1503 
         1504         if(m->sdigest){
         1505                 a[++ai].name = "digest";
         1506                 a[ai].value = s_to_c(m->sdigest);
         1507                 a[ai-1].next = &a[ai];
         1508         }
         1509 
         1510         a[ai].next = nil;
         1511 
         1512         p.attr = a;
         1513         snprint(buf, sizeof(buf), "%s/%s/%s",
         1514                 mntpt, mb->name, m->name);
         1515         p.ndata = strlen(buf);
         1516         p.data = buf;
         1517 
         1518         plumbsend(fd, &p);
         1519 }
         1520 
         1521 /* */
         1522 /* count the number of lines in the body (for imap4) */
         1523 /* */
         1524 void
         1525 countlines(Message *m)
         1526 {
         1527         int i;
         1528         char *p;
         1529 
         1530         i = 0;
         1531         for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
         1532                 i++;
         1533         sprint(m->lines, "%d", i);
         1534 }
         1535 
         1536 char *LOG = "fs";
         1537 
         1538 void
         1539 logmsg(char *s, Message *m)
         1540 {
         1541         int pid;
         1542 
         1543         if(!logging)
         1544                 return;
         1545         pid = getpid();
         1546         if(m == nil)
         1547                 syslog(0, LOG, "%s.%d: %s", user, pid, s);
         1548         else
         1549                 syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
         1550                         user, pid, s,
         1551                         m->from822 ? s_to_c(m->from822) : "?",
         1552                         s_to_c(m->sdigest));
         1553 }
         1554 
         1555 /*
         1556  *  squeeze nulls out of the body
         1557  */
         1558 static void
         1559 nullsqueeze(Message *m)
         1560 {
         1561         char *p, *q;
         1562 
         1563         q = memchr(m->body, 0, m->end-m->body);
         1564         if(q == nil)
         1565                 return;
         1566 
         1567         for(p = m->body; q < m->end; q++){
         1568                 if(*q == 0)
         1569                         continue;
         1570                 *p++ = *q;
         1571         }
         1572         m->bend = m->rbend = m->end = p;
         1573 }
         1574 
         1575 
         1576 /* */
         1577 /* convert an RFC822 date into a Unix style date */
         1578 /* for when the Unix From line isn't there (e.g. POP3). */
         1579 /* enough client programs depend on having a Unix date */
         1580 /* that it's easiest to write this conversion code once, right here. */
         1581 /* */
         1582 /* people don't follow RFC822 particularly closely, */
         1583 /* so we use strtotm, which is a bunch of heuristics. */
         1584 /* */
         1585 
         1586 extern int strtotm(char*, Tm*);
         1587 String*
         1588 date822tounix(char *s)
         1589 {
         1590         char *p, *q;
         1591         Tm tm;
         1592 
         1593         if(strtotm(s, &tm) < 0)
         1594                 return nil;
         1595 
         1596         p = asctime(&tm);
         1597         if(q = strchr(p, '\n'))
         1598                 *q = '\0';
         1599         return s_copy(p);
         1600 }