message.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 }