diff options
author | rsc <devnull@localhost> | 2005-10-29 16:21:34 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-10-29 16:21:34 +0000 |
commit | 9f1fdc128738b2ed76258ac22a8574c681f3df3a (patch) | |
tree | cd768899b5507cb00c072a3b80450da60bfbfeff /src/cmd/acme/mail/mail.c | |
parent | a078ffc8ab1d8f499b02b1dda2dfe2e9a3f6674d (diff) | |
download | plan9port-9f1fdc128738b2ed76258ac22a8574c681f3df3a.tar.gz plan9port-9f1fdc128738b2ed76258ac22a8574c681f3df3a.tar.bz2 plan9port-9f1fdc128738b2ed76258ac22a8574c681f3df3a.zip |
Add mail (John Cummings)
Diffstat (limited to 'src/cmd/acme/mail/mail.c')
-rw-r--r-- | src/cmd/acme/mail/mail.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/src/cmd/acme/mail/mail.c b/src/cmd/acme/mail/mail.c new file mode 100644 index 00000000..9942868c --- /dev/null +++ b/src/cmd/acme/mail/mail.c @@ -0,0 +1,571 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <plumb.h> +#include <ctype.h> +#include <9pclient.h> /* jpc */ +#include "dat.h" + +char *maildir = "/mail/fs/"; /* mountpoint of mail file system */ +char *mailtermdir = "/mnt/term/mail/fs/"; /* alternate mountpoint */ +char *mboxname = "mbox"; /* mailboxdir/mboxname is mail spool file */ +char *mailboxdir = nil; /* nil == /mail/box/$user */ +char *fsname; /* filesystem for mailboxdir/mboxname is at maildir/fsname */ +char *user; +char *outgoing; + +Window *wbox; +Message mbox; +Message replies; +char *home; +int plumbsendfd; +int plumbseemailfd; +int plumbshowmailfd; +int plumbsendmailfd; +Channel *cplumb; +Channel *cplumbshow; +Channel *cplumbsend; +int wctlfd; +void mainctl(void*); +void plumbproc(void*); +void plumbshowproc(void*); +void plumbsendproc(void*); +void plumbthread(void); +void plumbshowthread(void*); +void plumbsendthread(void*); + +int shortmenu; + +CFsys *upasfs; /*jpc */ +CFsys *acmefs; /*jpc */ + +void +usage(void) +{ + fprint(2, "usage: Mail [-sS] [-o outgoing] [mailboxname [directoryname]]\n"); + threadexitsall("usage"); +} + +void +removeupasfs(void) +{ + char buf[256]; + + if(strcmp(mboxname, "mbox") == 0) + return; + snprint(buf, sizeof buf, "close %s", mboxname); + write(mbox.ctlfd, buf, strlen(buf)); +} + +int +ismaildir(char *s) +{ + char buf[256]; + Dir *d; + int ret; + + snprint(buf, sizeof buf, "%s%s", maildir, s); + d = dirstat(buf); + if(d == nil) + return 0; + ret = d->qid.type & QTDIR; + free(d); + return ret; +} + +void +threadmain(int argc, char *argv[]) +{ + char *s, *name; + char err[ERRMAX], *cmd; + int i, newdir; + Fmt fmt; + + doquote = needsrcquote; + quotefmtinstall(); + + /* open these early so we won't miss notification of new mail messages while we read mbox */ + plumbsendfd = plumbopen("send", OWRITE|OCEXEC); + plumbseemailfd = plumbopen("seemail", OREAD|OCEXEC); + plumbshowmailfd = plumbopen("showmail", OREAD|OCEXEC); + + /* jpc */ + acmefs = nsmount("acme",nil); + upasfs = nsmount("upasfs", nil); + /* jpc end */ + + shortmenu = 0; + ARGBEGIN{ + case 's': + shortmenu = 1; + break; + case 'S': + shortmenu = 2; + break; + case 'o': + outgoing = EARGF(usage()); + break; + case 'm': + smprint(maildir, "%s/", EARGF(usage())); + break; + default: + usage(); + }ARGEND + + name = "mbox"; + + /* bind the terminal /mail/fs directory over the local one */ + if(access(maildir, 0)<0 && access(mailtermdir, 0)==0) { + /* jpc - bind(mailtermdir, maildir, MAFTER); */ + fprint(2,"jpc: trying to bind(mailtermdir, maildir, MAFTER)\n"); + } + + newdir = 1; + if(argc > 0){ + i = strlen(argv[0]); + if(argc>2 || i==0) + usage(); + /* see if the name is that of an existing /mail/fs directory */ + if(argc==1 && strchr(argv[0], '/')==0 && ismaildir(argv[0])){ + name = argv[0]; + mboxname = eappend(estrdup(maildir), "", name); + newdir = 0; + }else{ + if(argv[0][i-1] == '/') + argv[0][i-1] = '\0'; + s = strrchr(argv[0], '/'); + if(s == nil) + mboxname = estrdup(argv[0]); + else{ + *s++ = '\0'; + if(*s == '\0') + usage(); + mailboxdir = argv[0]; + mboxname = estrdup(s); + } + if(argc > 1) + name = argv[1]; + else + name = mboxname; + } + } + + user = getenv("user"); + if(user == nil) + user = "none"; + if(mailboxdir == nil) + mailboxdir = estrstrdup(unsharp("#9/mail/box/"), user); + if(outgoing == nil) + outgoing = estrstrdup(mailboxdir, "/outgoing"); + + // s = estrstrdup(maildir, "ctl"); + mbox.ctlfd = fsopenfd(upasfs,"ctl", ORDWR|OCEXEC); + if(mbox.ctlfd < 0) + error("can't open %s: %r\n", s); + + fsname = estrdup(name); + if(newdir && argc > 0){ + s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1); + for(i=0; i<10; i++){ + sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname); + if(write(mbox.ctlfd, s, strlen(s)) >= 0) + break; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "mbox name in use") == nil) + error("can't create directory %s for mail: %s\n", name, err); + free(fsname); + fsname = emalloc(strlen(name)+10); + sprint(fsname, "%s-%d", name, i); + } + if(i == 10) + error("can't open %s/%s: %r", mailboxdir, mboxname); + free(s); + } + + s = estrstrdup(fsname, "/"); + mbox.name = estrstrdup(maildir, s); + // mbox.name = "/mail/fs/mbox/"; + mbox.level= 0; + readmbox(&mbox, maildir, s); + home = getenv("home"); + if(home == nil) + home = "/"; + + wbox = newwindow(); + winname(wbox, mbox.name); + wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1); + threadcreate(mainctl, wbox, STACK); + + fmtstrinit(&fmt); + fmtprint(&fmt, "Mail"); + if(shortmenu) + fmtprint(&fmt, " -%c", "sS"[shortmenu-1]); + if(outgoing) + fmtprint(&fmt, " -o %s", outgoing); + fmtprint(&fmt, " %s", name); + cmd = fmtstrflush(&fmt); + if(cmd == nil) + sysfatal("out of memory"); + winsetdump(wbox, "/acme/mail", cmd); + mbox.w = wbox; + + mesgmenu(wbox, &mbox); + // sleep(100); + winclean(wbox); + + wctlfd = open("/dev/wctl", OWRITE|OCEXEC); /* for acme window */ + cplumb = chancreate(sizeof(Plumbmsg*), 0); + cplumbshow = chancreate(sizeof(Plumbmsg*), 0); + if(strcmp(name, "mbox") == 0){ + /* + * Avoid creating multiple windows to send mail by only accepting + * sendmail plumb messages if we're reading the main mailbox. + */ + plumbsendmailfd = plumbopen("sendmail", OREAD|OCEXEC); + cplumbsend = chancreate(sizeof(Plumbmsg*), 0); + proccreate(plumbsendproc, nil, STACK); + threadcreate(plumbsendthread, nil, STACK); + } + /* start plumb reader as separate proc ... */ + proccreate(plumbproc, nil, STACK); + proccreate(plumbshowproc, nil, STACK); + threadcreate(plumbshowthread, nil, STACK); + /* ... and use this thread to read the messages */ + plumbthread(); +} + +void +plumbproc(void* v) +{ + Plumbmsg *m; + + threadsetname("plumbproc"); + for(;;){ + m = plumbrecv(plumbseemailfd); + sendp(cplumb, m); + if(m == nil) + threadexits(nil); + } +} + +void +plumbshowproc(void* v) +{ + Plumbmsg *m; + + threadsetname("plumbshowproc"); + for(;;){ + m = plumbrecv(plumbshowmailfd); + sendp(cplumbshow, m); + if(m == nil) + threadexits(nil); + } +} + +void +plumbsendproc(void* v) +{ + Plumbmsg *m; + + threadsetname("plumbsendproc"); + for(;;){ + m = plumbrecv(plumbsendmailfd); + sendp(cplumbsend, m); + if(m == nil) + threadexits(nil); + } +} + +void +newmesg(char *name, char *digest) +{ + Dir *d; + char* tmp; + + if(strncmp(name, mbox.name, strlen(mbox.name)) != 0) { + return; /* message is about another mailbox */ + } + if(mesglookupfile(&mbox, name, digest) != nil) { + return; + } + if (strncmp(name,"/mail/fs/",strlen("/mail/fs/"))==0) { + tmp = name+strlen("/mail/fs/"); + } + d = fsdirstat(upasfs,tmp); + if(d == nil) { + return; + } + if(mesgadd(&mbox, mbox.name, d, digest)) { + mesgmenunew(wbox, &mbox); + } + free(d); +} + +void +showmesg(char *name, char *digest) +{ + char *n; + + if(strncmp(name, mbox.name, strlen(mbox.name)) != 0) + return; /* message is about another mailbox */ + n = estrdup(name+strlen(mbox.name)); + if(n[strlen(n)-1] != '/') + n = egrow(n, "/", nil); + mesgopen(&mbox, mbox.name, name+strlen(mbox.name), nil, 1, digest); + free(n); +} + +void +delmesg(char *name, char *digest, int dodel) +{ + Message *m; + + m = mesglookupfile(&mbox, name, digest); + if(m != nil){ + mesgmenumarkdel(wbox, &mbox, m, 0); + if(dodel) + m->writebackdel = 1; + } +} + +void +plumbthread(void) +{ + Plumbmsg *m; + Plumbattr *a; + char *type, *digest; + + threadsetname("plumbthread"); + while((m = recvp(cplumb)) != nil){ + a = m->attr; + digest = plumblookup(a, "digest"); + type = plumblookup(a, "mailtype"); + if(type == nil) + fprint(2, "Mail: plumb message with no mailtype attribute\n"); + else if(strcmp(type, "new") == 0) + newmesg(m->data, digest); + else if(strcmp(type, "delete") == 0) + delmesg(m->data, digest, 0); + else + fprint(2, "Mail: unknown plumb attribute %s\n", type); + plumbfree(m); + } + threadexits(nil); +} + +void +plumbshowthread(void* v) +{ + Plumbmsg *m; + + threadsetname("plumbshowthread"); + while((m = recvp(cplumbshow)) != nil){ + showmesg(m->data, plumblookup(m->attr, "digest")); + plumbfree(m); + } + threadexits(nil); +} + +void +plumbsendthread(void* v) +{ + Plumbmsg *m; + + threadsetname("plumbsendthread"); + while((m = recvp(cplumbsend)) != nil){ + mkreply(nil, "Mail", m->data, m->attr, nil); + plumbfree(m); + } + threadexits(nil); +} + +int +mboxcommand(Window *w, char *s) +{ + char *args[10], **targs; + Message *m, *next; + int ok, nargs, i, j; + char buf[128]; + + nargs = tokenize(s, args, nelem(args)); + if(nargs == 0) + return 0; + if(strcmp(args[0], "Mail") == 0){ + if(nargs == 1) + mkreply(nil, "Mail", "", nil, nil); + else + mkreply(nil, "Mail", args[1], nil, nil); + return 1; + } + if(strcmp(s, "Del") == 0){ + if(mbox.dirty){ + mbox.dirty = 0; + fprint(2, "mail: mailbox not written\n"); + return 1; + } + ok = 1; + for(m=mbox.head; m!=nil; m=next){ + next = m->next; + if(m->w){ + if(windel(m->w, 0)) + m->w = nil; + else + ok = 0; + } + } + for(m=replies.head; m!=nil; m=next){ + next = m->next; + if(m->w){ + if(windel(m->w, 0)) + m->w = nil; + else + ok = 0; + } + } + if(ok){ + windel(w, 1); + removeupasfs(); + threadexitsall(nil); + } + return 1; + } + if(strcmp(s, "Put") == 0){ + rewritembox(wbox, &mbox); + return 1; + } + if(strcmp(s, "Delmesg") == 0){ + if(nargs > 1){ + for(i=1; i<nargs; i++){ + snprint(buf, sizeof buf, "%s%s", mbox.name, args[i]); + delmesg(buf, nil, 1); + } + } + s = winselection(w); + if(s == nil) + return 1; + nargs = 1; + for(i=0; s[i]; i++) + if(s[i] == '\n') + nargs++; + targs = emalloc(nargs*sizeof(char*)); /* could be too many for a local array */ + nargs = getfields(s, targs, nargs, 1, "\n"); + for(i=0; i<nargs; i++){ + if(!isdigit(targs[i][0])) + continue; + j = atoi(targs[i]); /* easy way to parse the number! */ + if(j == 0) + continue; + snprint(buf, sizeof buf, "%s%d", mbox.name, j); + delmesg(buf, nil, 1); + } + free(s); + free(targs); + return 1; + } + return 0; +} + +void +mainctl(void *v) +{ + Window *w; + Event *e, *e2, *eq, *ea; + int na, nopen; + char *s, *t, *buf; + + w = v; + proccreate(wineventproc, w, STACK); + + for(;;){ + e = recvp(w->cevent); + switch(e->c1){ + default: + Unknown: + print("unknown message %c%c\n", e->c1, e->c2); + break; + + case 'E': /* write to body; can't affect us */ + break; + + case 'F': /* generated by our actions; ignore */ + break; + + case 'K': /* type away; we don't care */ + break; + + case 'M': + switch(e->c2){ + case 'x': + case 'X': + ea = nil; + e2 = nil; + if(e->flag & 2) + e2 = recvp(w->cevent); + if(e->flag & 8){ + ea = recvp(w->cevent); + na = ea->nb; + recvp(w->cevent); + }else + na = 0; + s = e->b; + /* if it's a known command, do it */ + if((e->flag&2) && e->nb==0) + s = e2->b; + if(na){ + t = emalloc(strlen(s)+1+na+1); + sprint(t, "%s %s", s, ea->b); + s = t; + } + /* if it's a long message, it can't be for us anyway */ + if(!mboxcommand(w, s)) /* send it back */ + winwriteevent(w, e); + if(na) + free(s); + break; + + case 'l': + case 'L': + buf = nil; + eq = e; + if(e->flag & 2){ + e2 = recvp(w->cevent); + eq = e2; + } + s = eq->b; + if(eq->q1>eq->q0 && eq->nb==0){ + buf = emalloc((eq->q1-eq->q0)*UTFmax+1); + winread(w, eq->q0, eq->q1, buf); + s = buf; + } + nopen = 0; + do{ + /* skip 'deleted' string if present' */ + if(strncmp(s, deleted, strlen(deleted)) == 0) + s += strlen(deleted); + /* skip mail box name if present */ + if(strncmp(s, mbox.name, strlen(mbox.name)) == 0) + s += strlen(mbox.name); + nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil); + while(*s!='\0' && *s++!='\n') + ; + }while(*s); + if(nopen == 0) /* send it back */ + winwriteevent(w, e); + free(buf); + break; + + case 'I': /* modify away; we don't care */ + case 'D': + case 'd': + case 'i': + break; + + default: + goto Unknown; + } + } + } +} + |