#include "common.h" #include #include #include "util.h" /* * Generate plumber messages as a plan b mail box * changes. We run upas/fs and mail2fs in a cpu server * and not at the user terminal. Users get their msgs * already processed for reading. */ enum { // for plumbmsg Old, New }; typedef struct Msg Msg; struct Msg{ char* path; char* from; char* date; Msg* next; int visited; }; int hopt; // send plumb msgs for old ones int debug; char* mboxdir; // only used by plumbmsg() int octopus; // plumb using /mnt/ports/post void printmsgs(Msg* m) { if(m == nil) fprint(2, "no msgs\n"); for(; m != nil; m = m->next) fprint(2, "%s\tfrom %s\n", m->path, m->from); } static int cmpent(void* a1, void* a2) { Dir* d1 = a1; Dir* d2 = a2; int n1, n2; n1 = atoi(mailnamenb(d1->name)); n2 = atoi(mailnamenb(d2->name)); return n2 - n1; } Msg* newmsg(char* dir) { static char buf[512]; Msg* m; char* fname; int fd; char* s; int nr; fname = smprint("%s/raw", dir); m = mallocz(sizeof(Msg), 1); if(m == nil || fname == nil) sysfatal("not enough memory"); fd = open(fname, OREAD); if(fd < 0){ // maybe just a new one, give it some time. sleep(1000); fd = open(fname, OREAD); } free(fname); if(fd < 0) goto fail; nr = read(fd, buf, sizeof(buf)-1); close(fd); if(nr <= 5 || strncmp(buf, "From ", 5)) goto fail; buf[nr] = 0; s = strchr(buf+5, ' '); if(s == nil) goto fail; *s = 0; m->from = strdup(buf+5); m->path = strdup(dir); if(m->from == nil || m->path == nil) goto fail; return m; fail: /* Message may have been archived in the * mean while. In any case, this is not to * be considered an error. */ if(debug) fprint(2, "%s: %r\n", dir); free(m->path); free(m->from); free(m); return nil; } void freemsg(Msg* m) { if(m != nil){ free(m->path); free(m->from); free(m->date); } free(m); } int isnew(Msg* m, Msg* msgs) { for(; msgs != nil; msgs = msgs->next) if(strcmp(msgs->path, m->path) == 0){ msgs->visited = 1; return 0; } return 1; } void oplumbmsg(Msg* m, int what) { char* str[2] = {"gone", "new"}; char msg[80]; char* e; int fd; e = msg+sizeof(msg); e = seprint(msg, e, "/msgs: %s %s %s\n", m->path, m->from, str[what]); fd = open("/mnt/ports/post", OWRITE); if(fd < 0) sysfatal("ports open: %r"); if(write(fd, msg, e-msg) != e - msg) sysfatal("pors write: %r"); close(fd); } void plumbmsg(Msg* m, int what) { static int fd = -1; char* str[2] = { "delete", "new" }; Plumbmsg p; Plumbattr a[10]; int ai; assert(what == 0 || what == 1); if(debug) fprint(2, "%s msg %s from %s\n", str[what], m->path, m->from); if(octopus){ oplumbmsg(m, what); return; } memset(&p, 0, sizeof(p)); p.src = "mailplumb"; p.dst = "seemail"; p.wdir = mboxdir; p.type = "text"; ai = 0; a[ai].name = "filetype"; a[ai].value = "mail"; a[++ai].name = "sender"; a[ai].value = m->from; a[ai-1].next = &a[ai]; a[++ai].name = "length"; a[ai].value = "42"; a[ai-1].next = &a[ai]; a[++ai].name = "mailtype"; a[ai].value = str[what]; a[ai-1].next = &a[ai]; a[++ai].name = "digest"; a[ai].value = m->path; a[ai-1].next = &a[ai]; a[ai].next = nil; p.attr = a; p.ndata = strlen(m->path); p.data = m->path; if(fd < 0) fd = plumbopen("send", OWRITE); if(fd < 0) sysfatal("plumb: %r\n"); if (plumbsend(fd, &p) < 0){ fprint(2, "plumbsend: %r\n"); close(fd); fd = -1; } } Msg** readmdir(char* dir, Msg* msgs, Msg** mp, int initial) { Msg* m; Dir* d; int n; int fd; int i; char* tf; if(dir == nil) return mp; fd = open(dir, OREAD); if(fd < 0){ fprint(2, "%s: %r\n", dir); return mp; } n = dirreadall(fd, &d); close(fd); if(n < 0) fprint(2, "%s: %r\n", dir); if(n <= 0) return mp; qsort(d, n, sizeof(Dir), cmpent); for(i = 0; i < n; i++) if(isdigit(d[i].name[0])){ tf = smprint("%s/%s", dir, d[i].name); m = newmsg(tf); free(tf); if(m != nil){ if(isnew(m, msgs)){ *mp = m; mp = &m->next; m->visited = 1; m->date = strdup(ctime(d[i].mtime)); m->date[strlen(m->date)-1] = 0; if(!initial || hopt) plumbmsg(m, New); }else freemsg(m); } } free(d); return mp; } Msg* readmbox(char* mbox, Qid* q, Msg* old, int initial) { Msg** mp; Dir* d; int fd; int i; int n; char* dir; Msg* msgs; Msg* m; msgs = old; for(mp = &msgs; *mp != nil; mp = &(*mp)->next) (*mp)->visited = 0; fd = open(mbox, OREAD); if(fd < 0) sysfatal("%s: %r\n", mbox); d = dirfstat(fd); if(d == nil) sysfatal("%s: %r\n", mbox); *q = d->qid; free(d); n = dirreadall(fd, &d); close(fd); if(n <= 0) return nil; qsort(d, n, sizeof(Dir), cmpent); for(i = 0; i < n; i++) if(d[i].qid.type&QTDIR){ dir = smprint("%s/%s", mbox, d[i].name); mp = readmdir(dir, msgs, mp, initial); free(dir); } for(mp = &msgs; (m = *mp) != nil; ) if(!m->visited){ *mp = m->next; plumbmsg(m, Old); freemsg(m); }else mp = &m->next; free(d); return msgs; } void watchmbox(char* dir, Qid q, Msg* msgs) { Dir* d; Qid nqid; for(;;){ sleep(octopus ? 5000 : 20000); d = dirstat(dir); if(d == nil) sysfatal("%s: %r\n", dir); nqid = d->qid; free(d); if(octopus && nqid.path == q.path && nqid.vers == q.vers) continue; msgs = readmbox(dir, &q, msgs, 0); } } void usage(void) { fprint(2, "usage: %s [-dho] [dir]\n", argv0); exits("usage"); } void main(int argc, char*argv[]) { char* dir; char* top; Msg* m; Qid mqid; Msg* msgs; top = smprint("/mail/box/%s", getuser()); ARGBEGIN{ case 'd': debug++; break; case 'h': hopt++; break; case 'o': octopus++; break; default: usage(); }ARGEND; if(argc == 1) dir = cleanpath(argv[0], top); else { if(argc != 0) usage(); dir = smprint("%s/msgs", top); } mboxdir = dir; msgs = readmbox(dir, &mqid, nil, 1); if(debug){ fprint(2, "initial msgs:\n"); printmsgs(msgs); } switch(rfork(RFPROC|RFNOTEG|RFFDG|RFNOWAIT)){ case -1: sysfatal("fork: %r"); case 0: if(hopt) for(m = msgs; m != nil; m = m->next) plumbmsg(m, New); watchmbox(dir, mqid, msgs); exits(nil); } exits(nil); }