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 12 /* global to this file */ 13 static String *errstring; 14 static message *mp; 15 static int interrupt; 16 static int savemail; 17 static Biobuf in; 18 19 /* predeclared */ 20 static int send(dest *, message *, int); 21 static void lesstedious(void); 22 static void save_mail(message *); 23 static int complain_mail(dest *, message *); 24 static int pipe_mail(dest *, message *); 25 static int cat_mail(dest *, message *); 26 static void appaddr(String *, dest *); 27 static int refuse(dest *, message *, char *, int); 28 static void mkerrstring(String *, message *, dest *, dest *, char *, int); 29 static int replymsg(String *, message *, dest *); 30 31 void 32 main(int argc, char *argv[]) 33 { 34 dest *dp=0; 35 int checkforward; 36 char *base; 37 int rv, holding; 38 39 srand(time(0)); 40 41 /* process args */ 42 ARGBEGIN{ 43 case '#': 44 nflg = 1; 45 break; 46 case 'x': 47 nflg = 1; 48 xflg = 1; 49 break; 50 case 'd': 51 debug = 1; 52 break; 53 case 'r': 54 rflg = 1; 55 break; 56 default: 57 fprint(2, "usage: mail [-x] list-of-addresses\n"); 58 exit(1); 59 }ARGEND 60 61 while(*argv) 62 d_insert(&dp, d_new(s_copy(*argv++))); 63 64 if (dp == 0) { 65 fprint(2, "usage: mail [-#] address-list\n"); 66 exit(1); 67 } 68 69 /* 70 * get context: 71 * - whether we're rmail or mail 72 */ 73 base = basename(argv0); 74 checkforward = rmail = (strcmp(base, "rmail")==0) | rflg; 75 thissys = sysname_read(); 76 altthissys = alt_sysname_read(); 77 78 /* 79 * read the mail. If an interrupt occurs while reading, save in 80 * dead.letter 81 */ 82 if (!nflg) { 83 Binit(&in, 0, OREAD); 84 if(rmail) 85 mp = m_read(&in, rmail); 86 else { 87 holding = holdon(); 88 mp = m_read(&in, rmail); 89 holdoff(holding); 90 } 91 if (mp == 0) 92 exit(0); 93 if (interrupt != 0) { 94 save_mail(mp); 95 exit(1); 96 } 97 } else { 98 mp = m_new(); 99 default_from(mp); 100 } 101 errstring = s_new(); 102 getrules(); 103 104 /* 105 * If this is a gateway, translate the sender address into a local 106 * address. This only happens if mail to the local address is 107 * forwarded to the sender. 108 */ 109 gateway(mp); 110 111 /* 112 * Protect against shell characters in the sender name for 113 * security reasons. 114 */ 115 USE(s_restart(mp->sender)); 116 if (shellchars(s_to_c(mp->sender))) 117 mp->replyaddr = s_copy("postmaster"); 118 else 119 mp->replyaddr = s_clone(mp->sender); 120 USE(s_restart(mp->replyaddr)); 121 122 /* 123 * reject messages that are too long. We don't do it earlier 124 * in m_read since we haven't set up enough things yet. 125 */ 126 if(mp->size < 0) 127 exit(refuse(dp, mp, "message too long", 0)); 128 129 rv = send(dp, mp, checkforward); 130 if(savemail) 131 save_mail(mp); 132 if(mp) 133 m_free(mp); 134 exit(rv); 135 } 136 137 138 139 /* send a message to a list of sites */ 140 static int 141 send(dest *destp, message *mp, int checkforward) 142 { 143 dest *dp; /* destination being acted upon */ 144 dest *bound; /* bound destinations */ 145 int errors=0; 146 static int forked; 147 148 /* bind the destinations to actions */ 149 bound = up_bind(destp, mp, checkforward); 150 151 /* loop through and execute commands */ 152 for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) { 153 switch (dp->status) { 154 case d_cat: 155 errors += cat_mail(dp, mp); 156 break; 157 case d_pipeto: 158 case d_pipe: 159 if (!rmail && !nflg && !forked) { 160 forked = 1; 161 lesstedious(); 162 } 163 errors += pipe_mail(dp, mp); 164 break; 165 default: 166 errors += complain_mail(dp, mp); 167 break; 168 } 169 } 170 171 return errors; 172 } 173 174 /* avoid user tedium (as Mike Lesk said in a previous version) */ 175 static void 176 lesstedious(void) 177 { 178 int i; 179 180 if(debug) 181 return; 182 183 switch(fork()){ 184 case -1: 185 break; 186 case 0: 187 rfork(RFENVG|RFNAMEG|RFNOTEG); 188 for(i=0; i<nsysfile; i++) 189 close(i); 190 savemail = 0; 191 break; 192 default: 193 exit(0); 194 } 195 } 196 197 198 /* save the mail */ 199 static void 200 save_mail(message *mp) 201 { 202 Biobuf *fp; 203 String *file; 204 static saved = 0; 205 206 file = s_new(); 207 mboxpath("dead.letter", getlog(), file, 0); 208 if ((fp = sysopen(s_to_c(file), "cA", 0660)) == 0) 209 return; 210 m_bprint(mp, fp); 211 sysclose(fp); 212 fprint(2, "saved in %s\n", s_to_c(file)); 213 s_free(file); 214 } 215 216 /* remember the interrupt happened */ 217 /* dispose of incorrect addresses */ 218 static int 219 complain_mail(dest *dp, message *mp) 220 { 221 char *msg; 222 223 switch (dp->status) { 224 case d_undefined: 225 msg = "Invalid address"; /* a little different, for debugging */ 226 break; 227 case d_syntax: 228 msg = "invalid address"; 229 break; 230 case d_unknown: 231 msg = "unknown user"; 232 break; 233 case d_eloop: 234 case d_loop: 235 msg = "forwarding loop"; 236 break; 237 case d_noforward: 238 if(dp->pstat && *s_to_c(dp->repl2)) 239 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat); 240 else 241 msg = "destination unknown or forwarding disallowed"; 242 break; 243 case d_pipe: 244 msg = "broken pipe"; 245 break; 246 case d_cat: 247 msg = "broken cat"; 248 break; 249 case d_translate: 250 if(dp->pstat && *s_to_c(dp->repl2)) 251 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat); 252 else 253 msg = "name translation failed"; 254 break; 255 case d_alias: 256 msg = "broken alias"; 257 break; 258 case d_badmbox: 259 msg = "corrupted mailbox"; 260 break; 261 case d_resource: 262 msg = "out of some resource. Try again later."; 263 break; 264 default: 265 msg = "unknown d_"; 266 break; 267 } 268 if (nflg) { 269 print("%s: %s\n", msg, s_to_c(dp->addr)); 270 return 0; 271 } 272 return refuse(dp, mp, msg, 0); 273 } 274 275 /* dispose of remote addresses */ 276 static int 277 pipe_mail(dest *dp, message *mp) 278 { 279 String *file; 280 dest *next, *list=0; 281 String *cmd; 282 process *pp; 283 int status, none; 284 String *errstring=s_new(); 285 286 none = dp->status == d_pipeto; 287 /* 288 * collect the arguments 289 */ 290 file = s_new(); 291 abspath(s_to_c(dp->addr), MAILROOT, file); 292 next = d_rm_same(&dp); 293 if(xflg) 294 cmd = s_new(); 295 else 296 cmd = s_clone(s_restart(next->repl1)); 297 for(; next != 0; next = d_rm_same(&dp)){ 298 if(xflg){ 299 s_append(cmd, s_to_c(next->addr)); 300 s_append(cmd, "\n"); 301 } else { 302 if (next->repl2 != 0) { 303 s_append(cmd, " "); 304 s_append(cmd, s_to_c(next->repl2)); 305 } 306 } 307 d_insert(&list, next); 308 } 309 310 if (nflg) { 311 if(xflg) 312 print("%s", s_to_c(cmd)); 313 else 314 print("%s\n", s_to_c(cmd)); 315 s_free(cmd); 316 s_free(file); 317 return 0; 318 } 319 320 /* 321 * run the process 322 */ 323 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none); 324 if(pp==0 || pp->std[0]==0 || pp->std[2]==0) 325 return refuse(list, mp, "out of processes, pipes, or memory", 0); 326 m_print(mp, pp->std[0]->fp, thissys, 0); 327 stream_free(pp->std[0]); 328 pp->std[0] = 0; 329 while(s_read_line(pp->std[2]->fp, errstring)) 330 ; 331 status = proc_wait(pp); 332 proc_free(pp); 333 s_free(cmd); 334 335 /* 336 * return status 337 */ 338 if (status != 0) 339 return refuse(list, mp, s_to_c(errstring), status); 340 loglist(list, mp, "remote"); 341 return 0; 342 } 343 344 /* dispose of local addresses */ 345 static int 346 cat_mail(dest *dp, message *mp) 347 { 348 Biobuf *fp; 349 char *rcvr, *cp; 350 Lock *l; 351 String *tmp; 352 353 if (nflg) { 354 if(!xflg) 355 print("cat >> %s\n", s_to_c(dp->repl1)); 356 else 357 print("%s\n", s_to_c(dp->addr)); 358 return 0; 359 } 360 l = lock(s_to_c(dp->repl1)); 361 if(l == 0) 362 return refuse(dp, mp, "can't lock mail file", 0); 363 fp = sysopen(s_to_c(dp->repl1), "cal", MBOXMODE); 364 if (fp == 0){ 365 tmp = s_append(0, s_to_c(dp->repl1)); 366 s_append(tmp, ".tmp"); 367 fp = sysopen(s_to_c(tmp), "cal", MBOXMODE); 368 if(fp == 0){ 369 unlock(l); 370 return refuse(dp, mp, "mail file cannot be opened", 0); 371 } 372 syslog(0, "mail", "error: used %s", s_to_c(tmp)); 373 s_free(tmp); 374 } 375 if(m_print(mp, fp, (char *)0, 1) < 0 376 || Bprint(fp, "\n") < 0 377 || Bflush(fp) < 0){ 378 sysclose(fp); 379 unlock(l); 380 return refuse(dp, mp, "error writing mail file", 0); 381 } 382 sysclose(fp); 383 unlock(l); 384 rcvr = s_to_c(dp->addr); 385 if(cp = strrchr(rcvr, '!')) 386 rcvr = cp+1; 387 logdelivery(dp, rcvr, mp); 388 return 0; 389 } 390 391 static void 392 appaddr(String *sp, dest *dp) 393 { 394 dest *parent; 395 396 if (dp->parent != 0) { 397 for(parent=dp->parent; parent->parent!=0; parent=parent->parent) 398 ; 399 s_append(sp, s_to_c(parent->addr)); 400 s_append(sp, "' alias `"); 401 } 402 s_append(sp, s_to_c(dp->addr)); 403 } 404 405 /* reject delivery */ 406 static int 407 refuse(dest *list, message *mp, char *cp, int status) 408 { 409 String *errstring=s_new(); 410 dest *dp; 411 int rv; 412 413 dp = d_rm(&list); 414 mkerrstring(errstring, mp, dp, list, cp, status); 415 416 /* 417 * if on a tty just report the error. Otherwise send mail 418 * reporting the error. N.B. To avoid mail loops, don't 419 * send mail reporting a failure of mail to reach the postmaster. 420 */ 421 if (!rmail) { 422 fprint(2, "%s\n", s_to_c(errstring)); 423 savemail = 1; 424 rv = 1; 425 } else { 426 if (strcmp(s_to_c(mp->replyaddr), "postmaster")!=0) 427 rv = replymsg(errstring, mp, dp); 428 else 429 rv = 1; 430 } 431 logrefusal(dp, mp, s_to_c(errstring)); 432 s_free(errstring); 433 return rv; 434 } 435 436 /* make the error message */ 437 static void 438 mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status) 439 { 440 dest *next; 441 char smsg[64]; 442 443 /* list all aliases */ 444 s_append(errstring, "Mail to `"); 445 appaddr(errstring, dp); 446 for(next = d_rm(&list); next != 0; next = d_rm(&list)) { 447 s_append(errstring, "', '"); 448 appaddr(errstring, next); 449 d_insert(&dp, next); 450 } 451 s_append(errstring, "' from '"); 452 s_append(errstring, s_to_c(mp->sender)); 453 s_append(errstring, "' failed.\n"); 454 455 /* >> and | deserve different flavored messages */ 456 switch(dp->status) { 457 case d_pipe: 458 s_append(errstring, "The mailer `"); 459 s_append(errstring, s_to_c(dp->repl1)); 460 sprint(smsg, "' returned error status %x.\n", status); 461 s_append(errstring, smsg); 462 s_append(errstring, "The error message was:\n"); 463 s_append(errstring, cp); 464 break; 465 default: 466 s_append(errstring, "The error message was:\n"); 467 s_append(errstring, cp); 468 break; 469 } 470 } 471 472 /* 473 * reply with up to 1024 characters of the 474 * original message 475 */ 476 static int 477 replymsg(String *errstring, message *mp, dest *dp) 478 { 479 message *refp = m_new(); 480 dest *ndp; 481 char *rcvr; 482 int rv; 483 484 rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr); 485 ndp = d_new(s_copy(rcvr)); 486 s_append(refp->sender, "postmaster"); 487 s_append(refp->replyaddr, "postmaster"); 488 s_append(refp->date, thedate()); 489 s_append(refp->body, s_to_c(errstring)); 490 s_append(refp->body, "\nThe message began:\n"); 491 s_nappend(refp->body, s_to_c(mp->body), 8*1024); 492 refp->size = strlen(s_to_c(refp->body)); 493 rv = send(ndp, refp, 0); 494 m_free(refp); 495 d_free(ndp); 496 return rv; 497 } 498