aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/upas/ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/upas/ml')
-rw-r--r--src/cmd/upas/ml/common.c197
-rw-r--r--src/cmd/upas/ml/dat.h25
-rw-r--r--src/cmd/upas/ml/mkfile40
-rw-r--r--src/cmd/upas/ml/ml.c167
-rw-r--r--src/cmd/upas/ml/mlmgr.c110
-rw-r--r--src/cmd/upas/ml/mlowner.c64
6 files changed, 603 insertions, 0 deletions
diff --git a/src/cmd/upas/ml/common.c b/src/cmd/upas/ml/common.c
new file mode 100644
index 00000000..307a4925
--- /dev/null
+++ b/src/cmd/upas/ml/common.c
@@ -0,0 +1,197 @@
+#include "common.h"
+#include "dat.h"
+
+String*
+getaddr(Node *p)
+{
+ for(; p; p = p->next){
+ if(p->s && p->addr)
+ return p->s;
+ }
+ return nil;
+}
+
+/* send messae adding our own reply-to and precedence */
+void
+getaddrs(void)
+{
+ Field *f;
+
+ for(f = firstfield; f; f = f->next){
+ if(f->node->c == FROM && from == nil)
+ from = getaddr(f->node);
+ if(f->node->c == SENDER && sender == nil)
+ sender = getaddr(f->node);
+ }
+}
+
+/* write address file, should be append only */
+void
+writeaddr(char *file, char *addr, int rem, char *listname)
+{
+ int fd;
+ Dir nd;
+
+ fd = open(file, OWRITE);
+ if(fd < 0){
+ fd = create(file, OWRITE, DMAPPEND|0666);
+ if(fd < 0)
+ sysfatal("creating address list %s: %r", file);
+ nulldir(&nd);
+ nd.mode = DMAPPEND|0666;
+ dirwstat(file, &nd);
+ } else
+ seek(fd, 0, 2);
+ if(rem)
+ fprint(fd, "!%s\n", addr);
+ else
+ fprint(fd, "%s\n", addr);
+ close(fd);
+
+ if(*addr != '#')
+ sendnotification(addr, listname, rem);
+}
+
+void
+remaddr(char *addr)
+{
+ Addr **l;
+ Addr *a;
+
+ for(l = &al; *l; l = &(*l)->next){
+ a = *l;
+ if(strcmp(addr, a->addr) == 0){
+ (*l) = a->next;
+ free(a);
+ na--;
+ break;
+ }
+ }
+}
+
+int
+addaddr(char *addr)
+{
+ Addr **l;
+ Addr *a;
+
+ for(l = &al; *l; l = &(*l)->next){
+ if(strcmp(addr, (*l)->addr) == 0)
+ return 0;
+ }
+ na++;
+ *l = a = malloc(sizeof(*a)+strlen(addr)+1);
+ if(a == nil)
+ sysfatal("allocating: %r");
+ a->addr = (char*)&a[1];
+ strcpy(a->addr, addr);
+ a->next = nil;
+ *l = a;
+ return 1;
+}
+
+/* read address file */
+void
+readaddrs(char *file)
+{
+ Biobuf *b;
+ char *p;
+
+ b = Bopen(file, OREAD);
+ if(b == nil)
+ return;
+
+ while((p = Brdline(b, '\n')) != nil){
+ p[Blinelen(b)-1] = 0;
+ if(*p == '#')
+ continue;
+ if(*p == '!')
+ remaddr(p+1);
+ else
+ addaddr(p);
+ }
+ Bterm(b);
+}
+
+/* start a mailer sending to all the receivers */
+int
+startmailer(char *name)
+{
+ int pfd[2];
+ char **av;
+ int ac;
+ Addr *a;
+
+ putenv("upasname", "/dev/null");
+ if(pipe(pfd) < 0)
+ sysfatal("creating pipe: %r");
+ switch(fork()){
+ case -1:
+ sysfatal("starting mailer: %r");
+ case 0:
+ close(pfd[1]);
+ break;
+ default:
+ close(pfd[0]);
+ return pfd[1];
+ }
+
+ dup(pfd[0], 0);
+ close(pfd[0]);
+
+ av = malloc(sizeof(char*)*(na+2));
+ if(av == nil)
+ sysfatal("starting mailer: %r");
+ ac = 0;
+ av[ac++] = name;
+ for(a = al; a != nil; a = a->next)
+ av[ac++] = a->addr;
+ av[ac] = 0;
+ exec("/bin/upas/send", av);
+ sysfatal("execing mailer: %r");
+
+ /* not reached */
+ return -1;
+}
+
+void
+sendnotification(char *addr, char *listname, int rem)
+{
+ int pfd[2];
+ Waitmsg *w;
+
+ putenv("upasname", "/dev/null");
+ if(pipe(pfd) < 0)
+ sysfatal("creating pipe: %r");
+ switch(fork()){
+ case -1:
+ sysfatal("starting mailer: %r");
+ case 0:
+ close(pfd[1]);
+ dup(pfd[0], 0);
+ close(pfd[0]);
+ execl("/bin/upas/send", "mlnotify", addr, nil);
+ sysfatal("execing mailer: %r");
+ break;
+ default:
+ close(pfd[0]);
+ fprint(pfd[1], "From: %s-owner\n\n", listname);
+ if(rem)
+ fprint(pfd[1], "You have removed from the %s mailing list\n", listname);
+ else{
+ fprint(pfd[1], "You have been added to the %s mailing list\n", listname);
+ fprint(pfd[1], "To be removed, send an email to %s-owner containing\n",
+ listname);
+ fprint(pfd[1], "the word 'remove' in the subject or body.\n");
+ }
+ close(pfd[1]);
+
+ /* wait for mailer to end */
+ while(w = wait()){
+ if(w->msg != nil && w->msg[0])
+ sysfatal("%s", w->msg);
+ free(w);
+ }
+ break;
+ }
+}
diff --git a/src/cmd/upas/ml/dat.h b/src/cmd/upas/ml/dat.h
new file mode 100644
index 00000000..3527af50
--- /dev/null
+++ b/src/cmd/upas/ml/dat.h
@@ -0,0 +1,25 @@
+
+#include "../smtp/smtp.h"
+#include "../smtp/y.tab.h"
+
+typedef struct Addr Addr;
+struct Addr
+{
+ char *addr;
+ Addr *next;
+};
+
+String *from;
+String *sender;
+Field *firstfield;
+int na;
+Addr *al;
+
+extern String* getaddr(Node *p);
+extern void getaddrs(void);
+extern void writeaddr(char *file, char *addr, int, char *);
+extern void remaddr(char *addr);
+extern int addaddr(char *addr);
+extern void readaddrs(char *file);
+extern int startmailer(char *name);
+extern void sendnotification(char *addr, char *listname, int rem);
diff --git a/src/cmd/upas/ml/mkfile b/src/cmd/upas/ml/mkfile
new file mode 100644
index 00000000..5142e56f
--- /dev/null
+++ b/src/cmd/upas/ml/mkfile
@@ -0,0 +1,40 @@
+</$objtype/mkfile
+
+TARG=ml\
+ mlowner\
+ mlmgr\
+
+OFILES=\
+ common.$O\
+
+LIB=../common/libcommon.av\
+
+UHFILES= ../common/common.h\
+ ../common/sys.h\
+ dat.h\
+
+HFILES=$UHFILES\
+ ../smtp/y.tab.h\
+
+LIB=../common/libcommon.a$O\
+
+BIN=/$objtype/bin/upas
+
+UPDATE=\
+ mkfile\
+ $UHFILES\
+ ${TARG:%=%.c}\
+ ${OFILES:%.$O=%.c}\
+ ../smtp/rfc822.y\
+
+</sys/src/cmd/mkmany
+CFLAGS=$CFLAGS -I../common
+
+$O.ml: ../smtp/rfc822.tab.$O
+$O.mlowner: ../smtp/rfc822.tab.$O
+
+../smtp/y.tab.h ../smtp/rfc822.tab.$O:
+ @{
+ cd ../smtp
+ mk rfc822.tab.$O
+ }
diff --git a/src/cmd/upas/ml/ml.c b/src/cmd/upas/ml/ml.c
new file mode 100644
index 00000000..8372dc9d
--- /dev/null
+++ b/src/cmd/upas/ml/ml.c
@@ -0,0 +1,167 @@
+#include "common.h"
+#include "dat.h"
+
+Biobuf in;
+
+Addr *al;
+int na;
+String *from;
+String *sender;
+
+void printmsg(int fd, String *msg, char *replyto, char *listname);
+void appendtoarchive(char* listname, String *firstline, String *msg);
+void printsubject(int fd, Field *f, char *listname);
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s address-list-file listname\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ String *msg;
+ String *firstline;
+ char *listname, *alfile;
+ Waitmsg *w;
+ int fd;
+ char *replytoname = nil;
+
+ ARGBEGIN{
+ case 'r':
+ replytoname = ARGF();
+ break;
+ }ARGEND;
+
+ rfork(RFENVG|RFREND);
+
+ if(argc < 2)
+ usage();
+ alfile = argv[0];
+ listname = argv[1];
+ if(replytoname == nil)
+ replytoname = listname;
+
+ readaddrs(alfile);
+
+ if(Binit(&in, 0, OREAD) < 0)
+ sysfatal("opening input: %r");
+
+ msg = s_new();
+ firstline = s_new();
+
+ /* discard the 'From ' line */
+ if(s_read_line(&in, firstline) == nil)
+ sysfatal("reading input: %r");
+
+ /* read up to the first 128k of the message. more is redculous.
+ Not if word documents are distributed. Upped it to 2MB (pb) */
+ if(s_read(&in, msg, 2*1024*1024) <= 0)
+ sysfatal("reading input: %r");
+
+ /* parse the header */
+ yyinit(s_to_c(msg), s_len(msg));
+ yyparse();
+
+ /* get the sender */
+ getaddrs();
+ if(from == nil)
+ from = sender;
+ if(from == nil)
+ sysfatal("message must contain From: or Sender:");
+ if(strcmp(listname, s_to_c(from)) == 0)
+ sysfatal("can't remail messages from myself");
+ addaddr(s_to_c(from));
+
+ /* start the mailer up and return a pipe to it */
+ fd = startmailer(listname);
+
+ /* send message adding our own reply-to and precedence */
+ printmsg(fd, msg, replytoname, listname);
+ close(fd);
+
+ /* wait for mailer to end */
+ while(w = wait()){
+ if(w->msg != nil && w->msg[0])
+ sysfatal("%s", w->msg);
+ free(w);
+ }
+
+ /* if the mailbox exits, cat the mail to the end of it */
+ appendtoarchive(listname, firstline, msg);
+ exits(0);
+}
+
+/* send message filtering Reply-to out of messages */
+void
+printmsg(int fd, String *msg, char *replyto, char *listname)
+{
+ Field *f, *subject;
+ Node *p;
+ char *cp, *ocp;
+
+ subject = nil;
+ cp = s_to_c(msg);
+ for(f = firstfield; f; f = f->next){
+ ocp = cp;
+ for(p = f->node; p; p = p->next)
+ cp = p->end+1;
+ if(f->node->c == REPLY_TO)
+ continue;
+ if(f->node->c == PRECEDENCE)
+ continue;
+ if(f->node->c == SUBJECT){
+ subject = f;
+ continue;
+ }
+ write(fd, ocp, cp-ocp);
+ }
+ printsubject(fd, subject, listname);
+ fprint(fd, "Reply-To: %s\nPrecedence: bulk\n", replyto);
+ write(fd, cp, s_len(msg) - (cp - s_to_c(msg)));
+}
+
+/* if the mailbox exits, cat the mail to the end of it */
+void
+appendtoarchive(char* listname, String *firstline, String *msg)
+{
+ String *mbox;
+ int fd;
+
+ mbox = s_new();
+ mboxpath("mbox", listname, mbox, 0);
+ if(access(s_to_c(mbox), 0) < 0)
+ return;
+ fd = open(s_to_c(mbox), OWRITE);
+ if(fd < 0)
+ return;
+ s_append(msg, "\n");
+ write(fd, s_to_c(firstline), s_len(firstline));
+ write(fd, s_to_c(msg), s_len(msg));
+}
+
+/* add the listname to the subject */
+void
+printsubject(int fd, Field *f, char *listname)
+{
+ char *s, *e;
+ Node *p;
+ char *ln;
+
+ if(f == nil || f->node == nil){
+ fprint(fd, "Subject: [%s]\n", listname);
+ return;
+ }
+ s = e = f->node->end + 1;
+ for(p = f->node; p; p = p->next)
+ e = p->end;
+ *e = 0;
+ ln = smprint("[%s]", listname);
+ if(ln != nil && strstr(s, ln) == nil)
+ fprint(fd, "Subject: %s%s\n", ln, s);
+ else
+ fprint(fd, "Subject:%s\n", s);
+ free(ln);
+}
diff --git a/src/cmd/upas/ml/mlmgr.c b/src/cmd/upas/ml/mlmgr.c
new file mode 100644
index 00000000..a1d1b907
--- /dev/null
+++ b/src/cmd/upas/ml/mlmgr.c
@@ -0,0 +1,110 @@
+#include "common.h"
+#include "dat.h"
+
+int cflag;
+int aflag;
+int rflag;
+
+int createpipeto(char *alfile, char *user, char *listname, int owner);
+
+void
+usage(void)
+{
+ fprint(2, "usage:\t%s -c listname\n", argv0);
+ fprint(2, "\t%s -[ar] listname addr\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *listname, *addr;
+ String *owner, *alfile;
+
+ rfork(RFENVG|RFREND);
+
+ ARGBEGIN{
+ case 'c':
+ cflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ }ARGEND;
+
+ if(aflag + rflag + cflag > 1){
+ fprint(2, "%s: -a, -r, and -c are mutually exclusive\n", argv0);
+ exits("usage");
+ }
+
+ if(argc < 1)
+ usage();
+
+ listname = argv[0];
+ alfile = s_new();
+ mboxpath("address-list", listname, alfile, 0);
+
+ if(cflag){
+ owner = s_copy(listname);
+ s_append(owner, "-owner");
+ if(creatembox(listname, nil) < 0)
+ sysfatal("creating %s's mbox: %r", listname);
+ if(creatembox(s_to_c(owner), nil) < 0)
+ sysfatal("creating %s's mbox: %r", s_to_c(owner));
+ if(createpipeto(s_to_c(alfile), listname, listname, 0) < 0)
+ sysfatal("creating %s's pipeto: %r", s_to_c(owner));
+ if(createpipeto(s_to_c(alfile), s_to_c(owner), listname, 1) < 0)
+ sysfatal("creating %s's pipeto: %r", s_to_c(owner));
+ writeaddr(s_to_c(alfile), "# mlmgr c flag", 0, listname);
+ } else if(rflag){
+ if(argc != 2)
+ usage();
+ addr = argv[1];
+ writeaddr(s_to_c(alfile), "# mlmgr r flag", 0, listname);
+ writeaddr(s_to_c(alfile), addr, 1, listname);
+ } else if(aflag){
+ if(argc != 2)
+ usage();
+ addr = argv[1];
+ writeaddr(s_to_c(alfile), "# mlmgr a flag", 0, listname);
+ writeaddr(s_to_c(alfile), addr, 0, listname);
+ } else
+ usage();
+ exits(0);
+}
+
+int
+createpipeto(char *alfile, char *user, char *listname, int owner)
+{
+ String *f;
+ int fd;
+ Dir *d;
+
+ f = s_new();
+ mboxpath("pipeto", user, f, 0);
+ fprint(2, "creating new pipeto: %s\n", s_to_c(f));
+ fd = create(s_to_c(f), OWRITE, 0775);
+ if(fd < 0)
+ return -1;
+ d = dirfstat(fd);
+ if(d == nil){
+ fprint(fd, "Couldn't stat %s: %r\n", s_to_c(f));
+ return -1;
+ }
+ d->mode |= 0775;
+ if(dirfwstat(fd, d) < 0)
+ fprint(fd, "Couldn't wstat %s: %r\n", s_to_c(f));
+ free(d);
+
+ fprint(fd, "#!/bin/rc\n");
+ if(owner)
+ fprint(fd, "/bin/upas/mlowner %s %s\n", alfile, listname);
+ else
+ fprint(fd, "/bin/upas/ml %s %s\n", alfile, user);
+ close(fd);
+
+ return 0;
+}
diff --git a/src/cmd/upas/ml/mlowner.c b/src/cmd/upas/ml/mlowner.c
new file mode 100644
index 00000000..5bb98a29
--- /dev/null
+++ b/src/cmd/upas/ml/mlowner.c
@@ -0,0 +1,64 @@
+#include "common.h"
+#include "dat.h"
+
+Biobuf in;
+
+String *from;
+String *sender;
+
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s address-list-file listname\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ String *msg;
+ char *alfile;
+ char *listname;
+
+ ARGBEGIN{
+ }ARGEND;
+
+ rfork(RFENVG|RFREND);
+
+ if(argc < 2)
+ usage();
+ alfile = argv[0];
+ listname = argv[1];
+
+ if(Binit(&in, 0, OREAD) < 0)
+ sysfatal("opening input: %r");
+
+ msg = s_new();
+
+ /* discard the 'From ' line */
+ if(s_read_line(&in, msg) == nil)
+ sysfatal("reading input: %r");
+
+ /* read up to the first 128k of the message. more is redculous */
+ if(s_read(&in, s_restart(msg), 128*1024) <= 0)
+ sysfatal("reading input: %r");
+
+ /* parse the header */
+ yyinit(s_to_c(msg), s_len(msg));
+ yyparse();
+
+ /* get the sender */
+ getaddrs();
+ if(from == nil)
+ from = sender;
+ if(from == nil)
+ sysfatal("message must contain From: or Sender:");
+
+ if(strstr(s_to_c(msg), "remove")||strstr(s_to_c(msg), "unsubscribe"))
+ writeaddr(alfile, s_to_c(from), 1, listname);
+ else if(strstr(s_to_c(msg), "subscribe"))
+ writeaddr(alfile, s_to_c(from), 0, listname);
+
+ exits(0);
+}