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