diff options
Diffstat (limited to 'src/cmd/upas/ml')
-rw-r--r-- | src/cmd/upas/ml/common.c | 197 | ||||
-rw-r--r-- | src/cmd/upas/ml/dat.h | 25 | ||||
-rw-r--r-- | src/cmd/upas/ml/mkfile | 40 | ||||
-rw-r--r-- | src/cmd/upas/ml/ml.c | 167 | ||||
-rw-r--r-- | src/cmd/upas/ml/mlmgr.c | 110 | ||||
-rw-r--r-- | src/cmd/upas/ml/mlowner.c | 64 |
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); +} |