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