main.c (11855B)
1 #include "common.h" 2 #include "send.h" 3 4 /* globals to all files */ 5 int rmail; 6 char *thissys, *altthissys; 7 int nflg; 8 int xflg; 9 int debug; 10 int rflg; 11 int iflg = 1; 12 int nosummary; 13 14 /* global to this file */ 15 static String *errstring; 16 static message *mp; 17 static int interrupt; 18 static int savemail; 19 static Biobuf in; 20 static int forked; 21 static int add822headers = 1; 22 static String *arglist; 23 24 /* predeclared */ 25 static int send(dest *, message *, int); 26 static void lesstedious(void); 27 static void save_mail(message *); 28 static int complain_mail(dest *, message *); 29 static int pipe_mail(dest *, message *); 30 static void appaddr(String *, dest *); 31 static void mkerrstring(String *, message *, dest *, dest *, char *, int); 32 static int replymsg(String *, message *, dest *); 33 static int catchint(void*, char*); 34 35 void 36 usage(void) 37 { 38 fprint(2, "usage: mail [-birtx] list-of-addresses\n"); 39 exits("usage"); 40 } 41 42 void 43 main(int argc, char *argv[]) 44 { 45 dest *dp=0; 46 int checkforward; 47 char *base; 48 int rv; 49 50 /* process args */ 51 ARGBEGIN{ 52 case '#': 53 nflg = 1; 54 break; 55 case 'b': 56 add822headers = 0; 57 break; 58 case 'x': 59 nflg = 1; 60 xflg = 1; 61 break; 62 case 'd': 63 debug = 1; 64 break; 65 case 'i': 66 iflg = 0; 67 break; 68 case 'r': 69 rflg = 1; 70 break; 71 default: 72 usage(); 73 }ARGEND 74 75 while(*argv){ 76 if(shellchars(*argv)){ 77 fprint(2, "illegal characters in destination\n"); 78 exits("syntax"); 79 } 80 d_insert(&dp, d_new(s_copy(*argv++))); 81 } 82 83 if (dp == 0) 84 usage(); 85 arglist = d_to(dp); 86 87 /* 88 * get context: 89 * - whether we're rmail or mail 90 */ 91 base = basename(argv0); 92 checkforward = rmail = (strcmp(base, "rmail")==0) | rflg; 93 thissys = sysname_read(); 94 altthissys = alt_sysname_read(); 95 if(rmail) 96 add822headers = 0; 97 98 /* 99 * read the mail. If an interrupt occurs while reading, save in 100 * dead.letter 101 */ 102 if (!nflg) { 103 Binit(&in, 0, OREAD); 104 if(!rmail) 105 atnotify(catchint, 1); 106 mp = m_read(&in, rmail, !iflg); 107 if (mp == 0) 108 exit(0); 109 if (interrupt != 0) { 110 save_mail(mp); 111 exit(1); 112 } 113 } else { 114 mp = m_new(); 115 if(default_from(mp) < 0){ 116 fprint(2, "%s: can't determine login name\n", argv0); 117 exit(1); 118 } 119 } 120 errstring = s_new(); 121 getrules(); 122 123 /* 124 * If this is a gateway, translate the sender address into a local 125 * address. This only happens if mail to the local address is 126 * forwarded to the sender. 127 */ 128 gateway(mp); 129 130 /* 131 * Protect against shell characters in the sender name for 132 * security reasons. 133 */ 134 mp->sender = escapespecial(mp->sender); 135 if (shellchars(s_to_c(mp->sender))) 136 mp->replyaddr = s_copy("postmaster"); 137 else 138 mp->replyaddr = s_clone(mp->sender); 139 140 /* 141 * reject messages that have been looping for too long 142 */ 143 if(mp->received > 32) 144 exit(refuse(dp, mp, "possible forward loop", 0, 0)); 145 146 /* 147 * reject messages that are too long. We don't do it earlier 148 * in m_read since we haven't set up enough things yet. 149 */ 150 if(mp->size < 0) 151 exit(refuse(dp, mp, "message too long", 0, 0)); 152 153 rv = send(dp, mp, checkforward); 154 if(savemail) 155 save_mail(mp); 156 if(mp) 157 m_free(mp); 158 exit(rv); 159 } 160 161 /* send a message to a list of sites */ 162 static int 163 send(dest *destp, message *mp, int checkforward) 164 { 165 dest *dp; /* destination being acted upon */ 166 dest *bound; /* bound destinations */ 167 int errors=0; 168 169 /* bind the destinations to actions */ 170 bound = up_bind(destp, mp, checkforward); 171 if(add822headers && mp->haveto == 0){ 172 if(nosummary) 173 mp->to = d_to(bound); 174 else 175 mp->to = arglist; 176 } 177 178 /* loop through and execute commands */ 179 for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) { 180 switch (dp->status) { 181 case d_cat: 182 errors += cat_mail(dp, mp); 183 break; 184 case d_pipeto: 185 case d_pipe: 186 if (!rmail && !nflg && !forked) { 187 forked = 1; 188 lesstedious(); 189 } 190 errors += pipe_mail(dp, mp); 191 break; 192 default: 193 errors += complain_mail(dp, mp); 194 break; 195 } 196 } 197 198 return errors; 199 } 200 201 /* avoid user tedium (as Mike Lesk said in a previous version) */ 202 static void 203 lesstedious(void) 204 { 205 int i; 206 207 if(debug) 208 return; 209 210 switch(fork()){ 211 case -1: 212 break; 213 case 0: 214 sysdetach(); 215 for(i=0; i<3; i++) 216 close(i); 217 savemail = 0; 218 break; 219 default: 220 exit(0); 221 } 222 } 223 224 225 /* save the mail */ 226 static void 227 save_mail(message *mp) 228 { 229 Biobuf *fp; 230 String *file; 231 232 file = s_new(); 233 deadletter(file); 234 fp = sysopen(s_to_c(file), "cAt", 0660); 235 if (fp == 0) 236 return; 237 m_bprint(mp, fp); 238 sysclose(fp); 239 fprint(2, "saved in %s\n", s_to_c(file)); 240 s_free(file); 241 } 242 243 /* remember the interrupt happened */ 244 245 static int 246 catchint(void *a, char *msg) 247 { 248 USED(a); 249 if(strstr(msg, "interrupt") || strstr(msg, "hangup")) { 250 interrupt = 1; 251 return 1; 252 } 253 return 0; 254 } 255 256 /* dispose of incorrect addresses */ 257 static int 258 complain_mail(dest *dp, message *mp) 259 { 260 char *msg; 261 262 switch (dp->status) { 263 case d_undefined: 264 msg = "Invalid address"; /* a little different, for debugging */ 265 break; 266 case d_syntax: 267 msg = "invalid address"; 268 break; 269 case d_unknown: 270 msg = "unknown user"; 271 break; 272 case d_eloop: 273 case d_loop: 274 msg = "forwarding loop"; 275 break; 276 case d_noforward: 277 if(dp->pstat && *s_to_c(dp->repl2)) 278 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0); 279 else 280 msg = "destination unknown or forwarding disallowed"; 281 break; 282 case d_pipe: 283 msg = "broken pipe"; 284 break; 285 case d_cat: 286 msg = "broken cat"; 287 break; 288 case d_translate: 289 if(dp->pstat && *s_to_c(dp->repl2)) 290 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0); 291 else 292 msg = "name translation failed"; 293 break; 294 case d_alias: 295 msg = "broken alias"; 296 break; 297 case d_badmbox: 298 msg = "corrupted mailbox"; 299 break; 300 case d_resource: 301 return refuse(dp, mp, "out of some resource. Try again later.", 0, 1); 302 default: 303 msg = "unknown d_"; 304 break; 305 } 306 if (nflg) { 307 print("%s: %s\n", msg, s_to_c(dp->addr)); 308 return 0; 309 } 310 return refuse(dp, mp, msg, 0, 0); 311 } 312 313 /* dispose of remote addresses */ 314 static int 315 pipe_mail(dest *dp, message *mp) 316 { 317 dest *next, *list=0; 318 String *cmd; 319 process *pp; 320 int status; 321 char *none; 322 String *errstring=s_new(); 323 324 if (dp->status == d_pipeto) 325 none = "none"; 326 else 327 none = 0; 328 /* 329 * collect the arguments 330 */ 331 next = d_rm_same(&dp); 332 if(xflg) 333 cmd = s_new(); 334 else 335 cmd = s_clone(next->repl1); 336 for(; next != 0; next = d_rm_same(&dp)){ 337 if(xflg){ 338 s_append(cmd, s_to_c(next->addr)); 339 s_append(cmd, "\n"); 340 } else { 341 if (next->repl2 != 0) { 342 s_append(cmd, " "); 343 s_append(cmd, s_to_c(next->repl2)); 344 } 345 } 346 d_insert(&list, next); 347 } 348 349 if (nflg) { 350 if(xflg) 351 print("%s", s_to_c(cmd)); 352 else 353 print("%s\n", s_to_c(cmd)); 354 s_free(cmd); 355 return 0; 356 } 357 358 /* 359 * run the process 360 */ 361 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none); 362 if(pp==0 || pp->std[0]==0 || pp->std[2]==0) 363 return refuse(list, mp, "out of processes, pipes, or memory", 0, 1); 364 pipesig(0); 365 m_print(mp, pp->std[0]->fp, thissys, 0); 366 pipesigoff(); 367 stream_free(pp->std[0]); 368 pp->std[0] = 0; 369 while(s_read_line(pp->std[2]->fp, errstring)) 370 ; 371 status = proc_wait(pp); 372 proc_free(pp); 373 s_free(cmd); 374 375 /* 376 * return status 377 */ 378 if (status != 0) 379 return refuse(list, mp, s_to_c(errstring), status, 0); 380 loglist(list, mp, "remote"); 381 return 0; 382 } 383 384 static void 385 appaddr(String *sp, dest *dp) 386 { 387 dest *parent; 388 String *s; 389 390 if (dp->parent != 0) { 391 for(parent=dp->parent; parent->parent!=0; parent=parent->parent) 392 ; 393 s = unescapespecial(s_clone(parent->addr)); 394 s_append(sp, s_to_c(s)); 395 s_free(s); 396 s_append(sp, "' alias `"); 397 } 398 s = unescapespecial(s_clone(dp->addr)); 399 s_append(sp, s_to_c(s)); 400 s_free(s); 401 } 402 403 /* 404 * reject delivery 405 * 406 * returns 0 - if mail has been disposed of 407 * other - if mail has not been disposed 408 */ 409 int 410 refuse(dest *list, message *mp, char *cp, int status, int outofresources) 411 { 412 String *errstring=s_new(); 413 dest *dp; 414 int rv; 415 416 dp = d_rm(&list); 417 mkerrstring(errstring, mp, dp, list, cp, status); 418 419 /* 420 * log first in case we get into trouble 421 */ 422 logrefusal(dp, mp, s_to_c(errstring)); 423 424 /* 425 * bulk mail is never replied to, if we're out of resources, 426 * let the sender try again 427 */ 428 if(rmail){ 429 /* accept it or request a retry */ 430 if(outofresources){ 431 fprint(2, "Mail %s\n", s_to_c(errstring)); 432 rv = 1; /* try again later */ 433 } else if(mp->bulk) 434 rv = 0; /* silently discard bulk */ 435 else 436 rv = replymsg(errstring, mp, dp); /* try later if we can't reply */ 437 } else { 438 /* aysnchronous delivery only happens if !rmail */ 439 if(forked){ 440 /* 441 * if spun off for asynchronous delivery, we own the mail now. 442 * return it or dump it on the floor. rv really doesn't matter. 443 */ 444 rv = 0; 445 if(!outofresources && !mp->bulk) 446 replymsg(errstring, mp, dp); 447 } else { 448 fprint(2, "Mail %s\n", s_to_c(errstring)); 449 savemail = 1; 450 rv = 1; 451 } 452 } 453 454 s_free(errstring); 455 return rv; 456 } 457 458 /* make the error message */ 459 static void 460 mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status) 461 { 462 dest *next; 463 char smsg[64]; 464 String *sender; 465 466 sender = unescapespecial(s_clone(mp->sender)); 467 468 /* list all aliases */ 469 s_append(errstring, " from '"); 470 s_append(errstring, s_to_c(sender)); 471 s_append(errstring, "'\nto '"); 472 appaddr(errstring, dp); 473 for(next = d_rm(&list); next != 0; next = d_rm(&list)) { 474 s_append(errstring, "'\nand '"); 475 appaddr(errstring, next); 476 d_insert(&dp, next); 477 } 478 s_append(errstring, "'\nfailed with error '"); 479 s_append(errstring, cp); 480 s_append(errstring, "'.\n"); 481 482 /* >> and | deserve different flavored messages */ 483 switch(dp->status) { 484 case d_pipe: 485 s_append(errstring, "The mailer `"); 486 s_append(errstring, s_to_c(dp->repl1)); 487 sprint(smsg, "' returned error status %x.\n\n", status); 488 s_append(errstring, smsg); 489 break; 490 } 491 492 s_free(sender); 493 } 494 495 /* 496 * create a new boundary 497 */ 498 static String* 499 mkboundary(void) 500 { 501 char buf[32]; 502 int i; 503 static int already; 504 505 if(already == 0){ 506 srand((time(0)<<16)|getpid()); 507 already = 1; 508 } 509 strcpy(buf, "upas-"); 510 for(i = 5; i < sizeof(buf)-1; i++) 511 buf[i] = 'a' + nrand(26); 512 buf[i] = 0; 513 return s_copy(buf); 514 } 515 516 /* 517 * reply with up to 1024 characters of the 518 * original message 519 */ 520 static int 521 replymsg(String *errstring, message *mp, dest *dp) 522 { 523 message *refp = m_new(); 524 dest *ndp; 525 char *rcvr; 526 int rv; 527 String *boundary; 528 529 boundary = mkboundary(); 530 531 refp->bulk = 1; 532 refp->rfc822headers = 1; 533 rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr); 534 ndp = d_new(s_copy(rcvr)); 535 s_append(refp->sender, "postmaster"); 536 s_append(refp->replyaddr, "/dev/null"); 537 s_append(refp->date, thedate()); 538 refp->haveto = 1; 539 s_append(refp->body, "To: "); 540 s_append(refp->body, rcvr); 541 s_append(refp->body, "\n"); 542 s_append(refp->body, "Subject: bounced mail\n"); 543 s_append(refp->body, "MIME-Version: 1.0\n"); 544 s_append(refp->body, "Content-Type: multipart/mixed;\n"); 545 s_append(refp->body, "\tboundary=\""); 546 s_append(refp->body, s_to_c(boundary)); 547 s_append(refp->body, "\"\n"); 548 s_append(refp->body, "Content-Disposition: inline\n"); 549 s_append(refp->body, "\n"); 550 s_append(refp->body, "This is a multi-part message in MIME format.\n"); 551 s_append(refp->body, "--"); 552 s_append(refp->body, s_to_c(boundary)); 553 s_append(refp->body, "\n"); 554 s_append(refp->body, "Content-Disposition: inline\n"); 555 s_append(refp->body, "Content-Type: text/plain; charset=\"US-ASCII\"\n"); 556 s_append(refp->body, "Content-Transfer-Encoding: 7bit\n"); 557 s_append(refp->body, "\n"); 558 s_append(refp->body, "The attached mail"); 559 s_append(refp->body, s_to_c(errstring)); 560 s_append(refp->body, "--"); 561 s_append(refp->body, s_to_c(boundary)); 562 s_append(refp->body, "\n"); 563 s_append(refp->body, "Content-Type: message/rfc822\n"); 564 s_append(refp->body, "Content-Disposition: inline\n\n"); 565 s_append(refp->body, s_to_c(mp->body)); 566 s_append(refp->body, "--"); 567 s_append(refp->body, s_to_c(boundary)); 568 s_append(refp->body, "--\n"); 569 570 refp->size = s_len(refp->body); 571 rv = send(ndp, refp, 0); 572 m_free(refp); 573 d_free(ndp); 574 return rv; 575 }