123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- #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;
- 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)
- return refuse(list, mp, s_to_c(errstring), status, 0);
- 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;
- }
|