aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/upas/send/main.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-10-29 16:26:44 +0000
committerrsc <devnull@localhost>2005-10-29 16:26:44 +0000
commit5cdb17983ae6e6367ad7a940cb219eab247a9304 (patch)
tree8ca1ef49af2a96e7daebe624d91fdf679814a057 /src/cmd/upas/send/main.c
parentcd3745196389579fb78b9b01ef1daefb5a57aa71 (diff)
downloadplan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.gz
plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.bz2
plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.zip
Thanks to John Cummings.
Diffstat (limited to 'src/cmd/upas/send/main.c')
-rw-r--r--src/cmd/upas/send/main.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/cmd/upas/send/main.c b/src/cmd/upas/send/main.c
new file mode 100644
index 00000000..6c455833
--- /dev/null
+++ b/src/cmd/upas/send/main.c
@@ -0,0 +1,575 @@
+#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;
+}