#include "common.h" #include "send.h" /* globals to all files */ int rmail; char *thissys, *altthissys; int nflg; int xflg; int debug; int rflg; int iflg = 1; int nosummary; /* global to this file */ static String *errstring; static message *mp; static int interrupt; static int savemail; static Biobuf in; static int forked; static int add822headers = 1; static String *arglist; /* predeclared */ static int send(dest *, message *, int); static void lesstedious(void); static void save_mail(message *); static int complain_mail(dest *, message *); static int pipe_mail(dest *, message *); static void appaddr(String *, dest *); static void mkerrstring(String *, message *, dest *, dest *, char *, int); static int replymsg(String *, message *, dest *); static int catchint(void*, char*); void usage(void) { fprint(2, "usage: mail [-birtx] list-of-addresses\n"); exits("usage"); } void main(int argc, char *argv[]) { dest *dp=0; int checkforward; char *base; int rv; /* process args */ ARGBEGIN{ case '#': nflg = 1; break; case 'b': add822headers = 0; break; case 'x': nflg = 1; xflg = 1; break; case 'd': debug = 1; break; case 'i': iflg = 0; break; case 'r': rflg = 1; break; default: usage(); }ARGEND while(*argv){ if(shellchars(*argv)){ fprint(2, "illegal characters in destination\n"); exits("syntax"); } d_insert(&dp, d_new(s_copy(*argv++))); } if (dp == 0) usage(); arglist = d_to(dp); /* * get context: * - whether we're rmail or mail */ base = basename(argv0); checkforward = rmail = (strcmp(base, "rmail")==0) | rflg; thissys = sysname_read(); altthissys = alt_sysname_read(); if(rmail) add822headers = 0; /* * read the mail. If an interrupt occurs while reading, save in * dead.letter */ if (!nflg) { Binit(&in, 0, OREAD); if(!rmail) atnotify(catchint, 1); mp = m_read(&in, rmail, !iflg); if (mp == 0) exit(0); if (interrupt != 0) { save_mail(mp); exit(1); } } else { mp = m_new(); if(default_from(mp) < 0){ fprint(2, "%s: can't determine login name\n", argv0); exit(1); } } errstring = s_new(); getrules(); /* * If this is a gateway, translate the sender address into a local * address. This only happens if mail to the local address is * forwarded to the sender. */ gateway(mp); /* * Protect against shell characters in the sender name for * security reasons. */ mp->sender = escapespecial(mp->sender); if (shellchars(s_to_c(mp->sender))) mp->replyaddr = s_copy("postmaster"); else mp->replyaddr = s_clone(mp->sender); /* * reject messages that have been looping for too long */ if(mp->received > 32) exit(refuse(dp, mp, "possible forward loop", 0, 0)); /* * reject messages that are too long. We don't do it earlier * in m_read since we haven't set up enough things yet. */ if(mp->size < 0) exit(refuse(dp, mp, "message too long", 0, 0)); rv = send(dp, mp, checkforward); if(savemail) save_mail(mp); if(mp) m_free(mp); exit(rv); } /* send a message to a list of sites */ static int send(dest *destp, message *mp, int checkforward) { dest *dp; /* destination being acted upon */ dest *bound; /* bound destinations */ int errors=0; /* bind the destinations to actions */ bound = up_bind(destp, mp, checkforward); if(add822headers && mp->haveto == 0){ if(nosummary) mp->to = d_to(bound); else mp->to = arglist; } /* loop through and execute commands */ for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) { switch (dp->status) { case d_cat: errors += cat_mail(dp, mp); break; case d_pipeto: case d_pipe: if (!rmail && !nflg && !forked) { forked = 1; lesstedious(); } errors += pipe_mail(dp, mp); break; default: errors += complain_mail(dp, mp); break; } } return errors; } /* avoid user tedium (as Mike Lesk said in a previous version) */ static void lesstedious(void) { int i; if(debug) return; switch(fork()){ case -1: break; case 0: sysdetach(); for(i=0; i<3; i++) close(i); savemail = 0; break; default: exit(0); } } /* save the mail */ static void save_mail(message *mp) { Biobuf *fp; String *file; file = s_new(); deadletter(file); fp = sysopen(s_to_c(file), "cAt", 0660); if (fp == 0) return; m_bprint(mp, fp); sysclose(fp); fprint(2, "saved in %s\n", s_to_c(file)); s_free(file); } /* remember the interrupt happened */ static int catchint(void *a, char *msg) { USED(a); if(strstr(msg, "interrupt") || strstr(msg, "hangup")) { interrupt = 1; return 1; } return 0; } /* dispose of incorrect addresses */ static int complain_mail(dest *dp, message *mp) { char *msg; switch (dp->status) { case d_undefined: msg = "Invalid address"; /* a little different, for debugging */ break; case d_syntax: msg = "invalid address"; break; case d_unknown: msg = "unknown user"; break; case d_eloop: case d_loop: msg = "forwarding loop"; break; case d_noforward: if(dp->pstat && *s_to_c(dp->repl2)) return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0); else msg = "destination unknown or forwarding disallowed"; break; case d_pipe: msg = "broken pipe"; break; case d_cat: msg = "broken cat"; break; case d_translate: if(dp->pstat && *s_to_c(dp->repl2)) return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0); else msg = "name translation failed"; break; case d_alias: msg = "broken alias"; break; case d_badmbox: msg = "corrupted mailbox"; break; case d_resource: return refuse(dp, mp, "out of some resource. Try again later.", 0, 1); default: msg = "unknown d_"; break; } if (nflg) { print("%s: %s\n", msg, s_to_c(dp->addr)); return 0; } return refuse(dp, mp, msg, 0, 0); } /* dispose of remote addresses */ static int pipe_mail(dest *dp, message *mp) { dest *next, *list=0; String *cmd; process *pp; int status, r; char *none; String *errstring=s_new(); if (dp->status == d_pipeto) none = "none"; else none = 0; /* * collect the arguments */ next = d_rm_same(&dp); if(xflg) cmd = s_new(); else cmd = s_clone(next->repl1); for(; next != 0; next = d_rm_same(&dp)){ if(xflg){ s_append(cmd, s_to_c(next->addr)); s_append(cmd, "\n"); } else { if (next->repl2 != 0) { s_append(cmd, " "); s_append(cmd, s_to_c(next->repl2)); } } d_insert(&list, next); } if (nflg) { if(xflg) print("%s", s_to_c(cmd)); else print("%s\n", s_to_c(cmd)); s_free(cmd); return 0; } /* * run the process */ pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none); if(pp==0 || pp->std[0]==0 || pp->std[2]==0) return refuse(list, mp, "out of processes, pipes, or memory", 0, 1); pipesig(0); m_print(mp, pp->std[0]->fp, thissys, 0); pipesigoff(); stream_free(pp->std[0]); pp->std[0] = 0; while(s_read_line(pp->std[2]->fp, errstring)) ; status = proc_wait(pp); proc_free(pp); s_free(cmd); /* * return status */ if (status != 0) { r = refuse(list, mp, s_to_c(errstring), status, 0); s_free(errstring); return r; } s_free(errstring); loglist(list, mp, "remote"); return 0; } static void appaddr(String *sp, dest *dp) { dest *parent; String *s; if (dp->parent != 0) { for(parent=dp->parent; parent->parent!=0; parent=parent->parent) ; s = unescapespecial(s_clone(parent->addr)); s_append(sp, s_to_c(s)); s_free(s); s_append(sp, "' alias `"); } s = unescapespecial(s_clone(dp->addr)); s_append(sp, s_to_c(s)); s_free(s); } /* * reject delivery * * returns 0 - if mail has been disposed of * other - if mail has not been disposed */ int refuse(dest *list, message *mp, char *cp, int status, int outofresources) { String *errstring=s_new(); dest *dp; int rv; dp = d_rm(&list); mkerrstring(errstring, mp, dp, list, cp, status); /* * log first in case we get into trouble */ logrefusal(dp, mp, s_to_c(errstring)); /* * bulk mail is never replied to, if we're out of resources, * let the sender try again */ if(rmail){ /* accept it or request a retry */ if(outofresources){ fprint(2, "Mail %s\n", s_to_c(errstring)); rv = 1; /* try again later */ } else if(mp->bulk) rv = 0; /* silently discard bulk */ else rv = replymsg(errstring, mp, dp); /* try later if we can't reply */ } else { /* aysnchronous delivery only happens if !rmail */ if(forked){ /* * if spun off for asynchronous delivery, we own the mail now. * return it or dump it on the floor. rv really doesn't matter. */ rv = 0; if(!outofresources && !mp->bulk) replymsg(errstring, mp, dp); } else { fprint(2, "Mail %s\n", s_to_c(errstring)); savemail = 1; rv = 1; } } s_free(errstring); return rv; } /* make the error message */ static void mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status) { dest *next; char smsg[64]; String *sender; sender = unescapespecial(s_clone(mp->sender)); /* list all aliases */ s_append(errstring, " from '"); s_append(errstring, s_to_c(sender)); s_append(errstring, "'\nto '"); appaddr(errstring, dp); for(next = d_rm(&list); next != 0; next = d_rm(&list)) { s_append(errstring, "'\nand '"); appaddr(errstring, next); d_insert(&dp, next); } s_append(errstring, "'\nfailed with error '"); s_append(errstring, cp); s_append(errstring, "'.\n"); /* >> and | deserve different flavored messages */ switch(dp->status) { case d_pipe: s_append(errstring, "The mailer `"); s_append(errstring, s_to_c(dp->repl1)); sprint(smsg, "' returned error status %x.\n\n", status); s_append(errstring, smsg); break; } s_free(sender); } /* * create a new boundary */ static String* mkboundary(void) { char buf[32]; int i; static int already; if(already == 0){ srand((time(0)<<16)|getpid()); already = 1; } strcpy(buf, "upas-"); for(i = 5; i < sizeof(buf)-1; i++) buf[i] = 'a' + nrand(26); buf[i] = 0; return s_copy(buf); } /* * reply with up to 1024 characters of the * original message */ static int replymsg(String *errstring, message *mp, dest *dp) { message *refp = m_new(); dest *ndp; char *rcvr; int rv; String *boundary; boundary = mkboundary(); refp->bulk = 1; refp->rfc822headers = 1; rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr); ndp = d_new(s_copy(rcvr)); s_append(refp->sender, "postmaster"); s_append(refp->replyaddr, "/dev/null"); s_append(refp->date, thedate()); refp->haveto = 1; s_append(refp->body, "To: "); s_append(refp->body, rcvr); s_append(refp->body, "\n"); s_append(refp->body, "Subject: bounced mail\n"); s_append(refp->body, "MIME-Version: 1.0\n"); s_append(refp->body, "Content-Type: multipart/mixed;\n"); s_append(refp->body, "\tboundary=\""); s_append(refp->body, s_to_c(boundary)); s_append(refp->body, "\"\n"); s_append(refp->body, "Content-Disposition: inline\n"); s_append(refp->body, "\n"); s_append(refp->body, "This is a multi-part message in MIME format.\n"); s_append(refp->body, "--"); s_append(refp->body, s_to_c(boundary)); s_append(refp->body, "\n"); s_append(refp->body, "Content-Disposition: inline\n"); s_append(refp->body, "Content-Type: text/plain; charset=\"US-ASCII\"\n"); s_append(refp->body, "Content-Transfer-Encoding: 7bit\n"); s_append(refp->body, "\n"); s_append(refp->body, "The attached mail"); s_append(refp->body, s_to_c(errstring)); s_append(refp->body, "--"); s_append(refp->body, s_to_c(boundary)); s_append(refp->body, "\n"); s_append(refp->body, "Content-Type: message/rfc822\n"); s_append(refp->body, "Content-Disposition: inline\n\n"); s_append(refp->body, s_to_c(mp->body)); s_append(refp->body, "--"); s_append(refp->body, s_to_c(boundary)); s_append(refp->body, "--\n"); refp->size = s_len(refp->body); rv = send(ndp, refp, 0); m_free(refp); d_free(ndp); return rv; }