URI: 
       tmessage.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
       ---
       tmessage.c (11103B)
       ---
            1 #include "common.h"
            2 #include "send.h"
            3 
            4 #include "../smtp/smtp.h"
            5 #include "../smtp/rfc822.tab.h"
            6 
            7 /* global to this file */
            8 static Reprog *rfprog;
            9 static Reprog *fprog;
           10 
           11 #define VMLIMIT (64*1024)
           12 #define MSGLIMIT (128*1024*1024)
           13 
           14 int received;        /* from rfc822.y */
           15 
           16 static String*        getstring(Node *p);
           17 static String*        getaddr(Node *p);
           18 
           19 extern int
           20 default_from(message *mp)
           21 {
           22         char *cp, *lp;
           23 
           24         cp = getenv("upasname");
           25         lp = getlog();
           26         if(lp == nil)
           27                 return -1;
           28 
           29         if(cp && *cp)
           30                 s_append(mp->sender, cp);
           31         else
           32                 s_append(mp->sender, lp);
           33         s_append(mp->date, thedate());
           34         return 0;
           35 }
           36 
           37 extern message *
           38 m_new(void)
           39 {
           40         message *mp;
           41 
           42         mp = (message *)mallocz(sizeof(message), 1);
           43         if (mp == 0) {
           44                 perror("message:");
           45                 exit(1);
           46         }
           47         mp->sender = s_new();
           48         mp->replyaddr = s_new();
           49         mp->date = s_new();
           50         mp->body = s_new();
           51         mp->size = 0;
           52         mp->fd = -1;
           53         return mp;
           54 }
           55 
           56 extern void
           57 m_free(message *mp)
           58 {
           59         if(mp->fd >= 0){
           60                 close(mp->fd);
           61                 sysremove(s_to_c(mp->tmp));
           62                 s_free(mp->tmp);
           63         }
           64         s_free(mp->sender);
           65         s_free(mp->date);
           66         s_free(mp->body);
           67         s_free(mp->havefrom);
           68         s_free(mp->havesender);
           69         s_free(mp->havereplyto);
           70         s_free(mp->havesubject);
           71         free((char *)mp);
           72 }
           73 
           74 /* read a message into a temp file , return an open fd to it */
           75 static int
           76 m_read_to_file(Biobuf *fp, message *mp)
           77 {
           78         int fd;
           79         int n;
           80         String *file;
           81         char buf[4*1024];
           82 
           83         file = s_new();
           84         /*
           85          *  create temp file to be remove on close
           86          */
           87         abspath("mtXXXXXX", UPASTMP, file);
           88         mktemp(s_to_c(file));
           89         if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
           90                 s_free(file);
           91                 return -1;
           92         }
           93         mp->tmp = file;
           94 
           95         /*
           96          *  read the rest into the temp file
           97          */
           98         while((n = Bread(fp, buf, sizeof(buf))) > 0){
           99                 if(write(fd, buf, n) != n){
          100                         close(fd);
          101                         return -1;
          102                 }
          103                 mp->size += n;
          104                 if(mp->size > MSGLIMIT){
          105                         mp->size = -1;
          106                         break;
          107                 }
          108         }
          109 
          110         mp->fd = fd;
          111         return 0;
          112 }
          113 
          114 /* get the first address from a node */
          115 static String*
          116 getaddr(Node *p)
          117 {
          118         for(; p; p = p->next)
          119                 if(p->s && p->addr)
          120                         return s_copy(s_to_c(p->s));
          121         return nil;
          122 }
          123 
          124 /* get the text of a header line minus the field name */
          125 static String*
          126 getstring(Node *p)
          127 {
          128         String *s;
          129 
          130         s = s_new();
          131         if(p == nil)
          132                 return s;
          133 
          134         for(p = p->next; p; p = p->next){
          135                 if(p->s){
          136                         s_append(s, s_to_c(p->s));
          137                 }else{
          138                         s_putc(s, p->c);
          139                         s_terminate(s);
          140                 }
          141                 if(p->white)
          142                         s_append(s, s_to_c(p->white));
          143         }
          144         return s;
          145 }
          146 
          147 #if 0
          148 static char *fieldname[] =
          149 {
          150 [WORD-WORD]        "WORD",
          151 [DATE-WORD]        "DATE",
          152 [RESENT_DATE-WORD]        "RESENT_DATE",
          153 [RETURN_PATH-WORD]        "RETURN_PATH",
          154 [FROM-WORD]        "FROM",
          155 [SENDER-WORD]        "SENDER",
          156 [REPLY_TO-WORD]        "REPLY_TO",
          157 [RESENT_FROM-WORD]        "RESENT_FROM",
          158 [RESENT_SENDER-WORD]        "RESENT_SENDER",
          159 [RESENT_REPLY_TO-WORD]        "RESENT_REPLY_TO",
          160 [SUBJECT-WORD]        "SUBJECT",
          161 [TO-WORD]        "TO",
          162 [CC-WORD]        "CC",
          163 [BCC-WORD]        "BCC",
          164 [RESENT_TO-WORD]        "RESENT_TO",
          165 [RESENT_CC-WORD]        "RESENT_CC",
          166 [RESENT_BCC-WORD]        "RESENT_BCC",
          167 [REMOTE-WORD]        "REMOTE",
          168 [PRECEDENCE-WORD]        "PRECEDENCE",
          169 [MIMEVERSION-WORD]        "MIMEVERSION",
          170 [CONTENTTYPE-WORD]        "CONTENTTYPE",
          171 [MESSAGEID-WORD]        "MESSAGEID",
          172 [RECEIVED-WORD]        "RECEIVED",
          173 [MAILER-WORD]        "MAILER",
          174 [BADTOKEN-WORD]        "BADTOKEN"
          175 };
          176 #endif
          177 
          178 /* fix 822 addresses */
          179 static void
          180 rfc822cruft(message *mp)
          181 {
          182         Field *f;
          183         Node *p;
          184         String *body, *s;
          185         char *cp;
          186 
          187         /*
          188          *  parse headers in in-core part
          189          */
          190         yyinit(s_to_c(mp->body), s_len(mp->body));
          191         mp->rfc822headers = 0;
          192         yyparse();
          193         mp->rfc822headers = 1;
          194         mp->received = received;
          195 
          196         /*
          197          *  remove equivalent systems in all addresses
          198          */
          199         body = s_new();
          200         cp = s_to_c(mp->body);
          201         for(f = firstfield; f; f = f->next){
          202                 if(f->node->c == MIMEVERSION)
          203                         mp->havemime = 1;
          204                 if(f->node->c == FROM)
          205                         mp->havefrom = getaddr(f->node);
          206                 if(f->node->c == SENDER)
          207                         mp->havesender = getaddr(f->node);
          208                 if(f->node->c == REPLY_TO)
          209                         mp->havereplyto = getaddr(f->node);
          210                 if(f->node->c == TO)
          211                         mp->haveto = 1;
          212                 if(f->node->c == DATE)
          213                         mp->havedate = 1;
          214                 if(f->node->c == SUBJECT)
          215                         mp->havesubject = getstring(f->node);
          216                 if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
          217                         s = f->node->next->next->s;
          218                         if(s && (strcmp(s_to_c(s), "bulk") == 0
          219                                 || strcmp(s_to_c(s), "Bulk") == 0))
          220                                         mp->bulk = 1;
          221                 }
          222                 for(p = f->node; p; p = p->next){
          223                         if(p->s){
          224                                 if(p->addr){
          225                                         cp = skipequiv(s_to_c(p->s));
          226                                         s_append(body, cp);
          227                                 } else
          228                                         s_append(body, s_to_c(p->s));
          229                         }else{
          230                                 s_putc(body, p->c);
          231                                 s_terminate(body);
          232                         }
          233                         if(p->white)
          234                                 s_append(body, s_to_c(p->white));
          235                         cp = p->end+1;
          236                 }
          237                 s_append(body, "\n");
          238         }
          239 
          240         if(*s_to_c(body) == 0){
          241                 s_free(body);
          242                 return;
          243         }
          244 
          245         if(*cp != '\n')
          246                 s_append(body, "\n");
          247         s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
          248         s_terminate(body);
          249 
          250         firstfield = 0;
          251         mp->size += s_len(body) - s_len(mp->body);
          252         s_free(mp->body);
          253         mp->body = body;
          254 }
          255 
          256 /* read in a message, interpret the 'From' header */
          257 extern message *
          258 m_read(Biobuf *fp, int rmail, int interactive)
          259 {
          260         message *mp;
          261         Resub subexp[10];
          262         char *line;
          263         int first;
          264         int n;
          265 
          266         mp = m_new();
          267 
          268         /* parse From lines if remote */
          269         if (rmail) {
          270                 /* get remote address */
          271                 String *sender=s_new();
          272 
          273                 if (rfprog == 0)
          274                         rfprog = regcomp(REMFROMRE);
          275                 first = 1;
          276                 while(s_read_line(fp, s_restart(mp->body)) != 0) {
          277                         memset(subexp, 0, sizeof(subexp));
          278                         if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
          279                                 if(first == 0)
          280                                         break;
          281                                 if (fprog == 0)
          282                                         fprog = regcomp(FROMRE);
          283                                 memset(subexp, 0, sizeof(subexp));
          284                                 if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
          285                                         break;
          286                                 s_restart(mp->body);
          287                                 append_match(subexp, s_restart(sender), SENDERMATCH);
          288                                 append_match(subexp, s_restart(mp->date), DATEMATCH);
          289                                 break;
          290                         }
          291                         append_match(subexp, s_restart(sender), REMSENDERMATCH);
          292                         append_match(subexp, s_restart(mp->date), REMDATEMATCH);
          293                         if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){
          294                                 append_match(subexp, mp->sender, REMSYSMATCH);
          295                                 s_append(mp->sender, "!");
          296                         }
          297                         first = 0;
          298                 }
          299                 s_append(mp->sender, s_to_c(sender));
          300 
          301                 s_free(sender);
          302         }
          303         if(*s_to_c(mp->sender)=='\0')
          304                 default_from(mp);
          305 
          306         /* if sender address is unreturnable, treat message as bulk mail */
          307         if(!returnable(s_to_c(mp->sender)))
          308                 mp->bulk = 1;
          309 
          310         /* get body */
          311         if(interactive && !rmail){
          312                 /* user typing on terminal: terminator == '.' or EOF */
          313                 for(;;) {
          314                         line = s_read_line(fp, mp->body);
          315                         if (line == 0)
          316                                 break;
          317                         if (strcmp(".\n", line)==0) {
          318                                 mp->body->ptr -= 2;
          319                                 *mp->body->ptr = '\0';
          320                                 break;
          321                         }
          322                 }
          323                 mp->size = mp->body->ptr - mp->body->base;
          324         } else {
          325                 /*
          326                  *  read up to VMLIMIT bytes (more or less) into main memory.
          327                  *  if message is longer put the rest in a tmp file.
          328                  */
          329                 mp->size = mp->body->ptr - mp->body->base;
          330                 n = s_read(fp, mp->body, VMLIMIT);
          331                 if(n < 0){
          332                         perror("m_read");
          333                         exit(1);
          334                 }
          335                 mp->size += n;
          336                 if(n == VMLIMIT){
          337                         if(m_read_to_file(fp, mp) < 0){
          338                                 perror("m_read_to_file");
          339                                 exit(1);
          340                         }
          341                 }
          342         }
          343 
          344         /*
          345          *  ignore 0 length messages from a terminal
          346          */
          347         if (!rmail && mp->size == 0)
          348                 return 0;
          349 
          350         rfc822cruft(mp);
          351 
          352         return mp;
          353 }
          354 
          355 /* return a piece of message starting at `offset' */
          356 extern int
          357 m_get(message *mp, long offset, char **pp)
          358 {
          359         static char buf[4*1024];
          360 
          361         /*
          362          *  are we past eof?
          363          */
          364         if(offset >= mp->size)
          365                 return 0;
          366 
          367         /*
          368          *  are we in the virtual memory portion?
          369          */
          370         if(offset < s_len(mp->body)){
          371                 *pp = mp->body->base + offset;
          372                 return mp->body->ptr - mp->body->base - offset;
          373         }
          374 
          375         /*
          376          *  read it from the temp file
          377          */
          378         offset -= s_len(mp->body);
          379         if(mp->fd < 0)
          380                 return -1;
          381         if(seek(mp->fd, offset, 0)<0)
          382                 return -1;
          383         *pp = buf;
          384         return read(mp->fd, buf, sizeof buf);
          385 }
          386 
          387 /* output the message body without ^From escapes */
          388 static int
          389 m_noescape(message *mp, Biobuf *fp)
          390 {
          391         long offset;
          392         int n;
          393         char *p;
          394 
          395         for(offset = 0; offset < mp->size; offset += n){
          396                 n = m_get(mp, offset, &p);
          397                 if(n <= 0){
          398                         Bflush(fp);
          399                         return -1;
          400                 }
          401                 if(Bwrite(fp, p, n) < 0)
          402                         return -1;
          403         }
          404         return Bflush(fp);
          405 }
          406 
          407 /*
          408  *  Output the message body with '^From ' escapes.
          409  *  Ensures that any line starting with a 'From ' gets a ' ' stuck
          410  *  in front of it.
          411  */
          412 static int
          413 m_escape(message *mp, Biobuf *fp)
          414 {
          415         char *p, *np;
          416         char *end;
          417         long offset;
          418         int m, n;
          419         char *start;
          420 
          421         for(offset = 0; offset < mp->size; offset += n){
          422                 n = m_get(mp, offset, &start);
          423                 if(n < 0){
          424                         Bflush(fp);
          425                         return -1;
          426                 }
          427 
          428                 p = start;
          429                 for(end = p+n; p < end; p += m){
          430                         np = memchr(p, '\n', end-p);
          431                         if(np == 0){
          432                                 Bwrite(fp, p, end-p);
          433                                 break;
          434                         }
          435                         m = np - p + 1;
          436                         if(m > 5 && strncmp(p, "From ", 5) == 0)
          437                                 Bputc(fp, ' ');
          438                         Bwrite(fp, p, m);
          439                 }
          440         }
          441         Bflush(fp);
          442         return 0;
          443 }
          444 
          445 static int
          446 printfrom(message *mp, Biobuf *fp)
          447 {
          448         String *s;
          449         int rv;
          450 
          451         if(!returnable(s_to_c(mp->sender)))
          452                 return Bprint(fp, "From: Postmaster\n");
          453 
          454         s = username(mp->sender);
          455         if(s) {
          456                 s_append(s, " <");
          457                 s_append(s, s_to_c(mp->sender));
          458                 s_append(s, ">");
          459         } else {
          460                 s = s_copy(s_to_c(mp->sender));
          461         }
          462         s = unescapespecial(s);
          463         rv = Bprint(fp, "From: %s\n", s_to_c(s));
          464         s_free(s);
          465         return rv;
          466 }
          467 
          468 static char *
          469 rewritezone(char *z)
          470 {
          471         int mindiff;
          472         char s;
          473         Tm *tm;
          474         static char x[7];
          475 
          476         tm = localtime(time(0));
          477         mindiff = tm->tzoff/60;
          478 
          479         /* if not in my timezone, don't change anything */
          480         if(strcmp(tm->zone, z) != 0)
          481                 return z;
          482 
          483         if(mindiff < 0){
          484                 s = '-';
          485                 mindiff = -mindiff;
          486         } else
          487                 s = '+';
          488 
          489         sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
          490         return x;
          491 }
          492 
          493 int
          494 isutf8(String *s)
          495 {
          496         char *p;
          497 
          498         for(p = s_to_c(s);  *p; p++)
          499                 if(*p&0x80)
          500                         return 1;
          501         return 0;
          502 }
          503 
          504 void
          505 printutf8mime(Biobuf *b)
          506 {
          507         Bprint(b, "MIME-Version: 1.0\n");
          508         Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
          509         Bprint(b, "Content-Transfer-Encoding: 8bit\n");
          510 }
          511 
          512 /* output a message */
          513 extern int
          514 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
          515 {
          516         String *date, *sender;
          517         char *f[6];
          518         int n;
          519 
          520         sender = unescapespecial(s_clone(mp->sender));
          521 
          522         if (remote != 0){
          523                 if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
          524                         s_free(sender);
          525                         return -1;
          526                 }
          527         } else {
          528                 if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
          529                         s_free(sender);
          530                         return -1;
          531                 }
          532         }
          533         s_free(sender);
          534         if(!rmail && !mp->havedate){
          535                 /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
          536                 date = s_copy(s_to_c(mp->date));
          537                 n = getfields(s_to_c(date), f, 6, 1, " \t");
          538                 if(n == 6)
          539                         Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
          540                          f[5], f[3], rewritezone(f[4]));
          541         }
          542         if(!rmail && !mp->havemime && isutf8(mp->body))
          543                 printutf8mime(fp);
          544         if(mp->to){
          545                 /* add the to: line */
          546                 if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
          547                         return -1;
          548                 /* add the from: line */
          549                 if (!mp->havefrom && printfrom(mp, fp) < 0)
          550                         return -1;
          551                 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
          552                         if (Bprint(fp, "\n") < 0)
          553                                 return -1;
          554         } else if(!rmail){
          555                 /* add the from: line */
          556                 if (!mp->havefrom && printfrom(mp, fp) < 0)
          557                         return -1;
          558                 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
          559                         if (Bprint(fp, "\n") < 0)
          560                                 return -1;
          561         }
          562 
          563         if (!mbox)
          564                 return m_noescape(mp, fp);
          565         return m_escape(mp, fp);
          566 }
          567 
          568 /* print just the message body */
          569 extern int
          570 m_bprint(message *mp, Biobuf *fp)
          571 {
          572         return m_noescape(mp, fp);
          573 }