From a150899221a5badff9740703b754f901b4f52762 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Thu, 14 Apr 2011 17:27:24 +0000 Subject: fill /acme --- acme/mail/src/mesg.c | 1322 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1322 insertions(+) create mode 100644 acme/mail/src/mesg.c (limited to 'acme/mail/src/mesg.c') diff --git a/acme/mail/src/mesg.c b/acme/mail/src/mesg.c new file mode 100644 index 000000000..620e76e3e --- /dev/null +++ b/acme/mail/src/mesg.c @@ -0,0 +1,1322 @@ +#include +#include +#include +#include +#include +#include +#include "dat.h" + +enum +{ + DIRCHUNK = 32*sizeof(Dir) +}; + +char regexchars[] = "\\/[].+?()*^$"; +char deleted[] = "(deleted)-"; +char deletedrx[] = "\\(deleted\\)-"; +char deletedrx01[] = "(\\(deleted\\)-)?"; +char deletedaddr[] = "-#0;/^\\(deleted\\)-/"; + +struct{ + char *type; + char *port; + char *suffix; +} ports[] = { + "text/", "edit", ".txt", + /* text must be first for plumbport() */ + "image/gif", "image", ".gif", + "image/jpeg", "image", ".jpg", + "image/jpeg", "image", ".jpeg", + "image/png", "image", ".png", + "image/tiff", "image", ".tif", + "application/postscript", "postscript", ".ps", + "application/pdf", "postscript", ".pdf", + "application/msword", "msword", ".doc", + "application/rtf", "msword", ".rtf", + "audio/x-wav", "wav", ".wav", + nil, nil +}; + +char *goodtypes[] = { + "text", + "text/plain", + "message/rfc822", + "text/richtext", + "text/tab-separated-values", + "application/octet-stream", + nil, +}; + +struct{ + char *type; + char *ext; +} exts[] = { + "image/gif", ".gif", + "image/jpeg", ".jpg", + nil, nil +}; + +char *okheaders[] = +{ + "From:", + "Date:", + "To:", + "CC:", + "Subject:", + nil +}; + +char *extraheaders[] = +{ + "Resent-From:", + "Resent-To:", + "Sort:", + nil, +}; + +char* +line(char *data, char **pp) +{ + char *p, *q; + + for(p=data; *p!='\0' && *p!='\n'; p++) + ; + if(*p == '\n') + *pp = p+1; + else + *pp = p; + q = emalloc(p-data + 1); + memmove(q, data, p-data); + return q; +} + +void +scanheaders(Message *m, char *dir) +{ + char *s, *t, *u, *f; + + s = f = readfile(dir, "header", nil); + if(s != nil) + while(*s){ + t = line(s, &s); + if(strncmp(t, "From: ", 6) == 0){ + m->fromcolon = estrdup(t+6); + /* remove all quotes; they're ugly and irregular */ + for(u=m->fromcolon; *u; u++) + if(*u == '"') + memmove(u, u+1, strlen(u)); + } + if(strncmp(t, "Subject: ", 9) == 0) + m->subject = estrdup(t+9); + free(t); + } + if(m->fromcolon == nil) + m->fromcolon = estrdup(m->from); + free(f); +} + +int +loadinfo(Message *m, char *dir) +{ + int n; + char *data, *p, *s; + + data = readfile(dir, "info", &n); + if(data == nil) + return 0; + m->from = line(data, &p); + scanheaders(m, dir); /* depends on m->from being set */ + m->to = line(p, &p); + m->cc = line(p, &p); + m->replyto = line(p, &p); + m->date = line(p, &p); + s = line(p, &p); + if(m->subject == nil) + m->subject = s; + else + free(s); + m->type = line(p, &p); + m->disposition = line(p, &p); + m->filename = line(p, &p); + m->digest = line(p, &p); + free(data); + return 1; +} + +int +isnumeric(char *s) +{ + while(*s){ + if(!isdigit(*s)) + return 0; + s++; + } + return 1; +} + +Dir* +loaddir(char *name, int *np) +{ + int fd; + Dir *dp; + + fd = open(name, OREAD); + if(fd < 0) + return nil; + *np = dirreadall(fd, &dp); + close(fd); + return dp; +} + +void +readmbox(Message *mbox, char *dir, char *subdir) +{ + char *name; + Dir *d, *dirp; + int i, n; + + name = estrstrdup(dir, subdir); + dirp = loaddir(name, &n); + mbox->recursed = 1; + if(dirp) + for(i=0; iname)) + mesgadd(mbox, name, d, nil); + } + free(dirp); + free(name); +} + +/* add message to box, in increasing numerical order */ +int +mesgadd(Message *mbox, char *dir, Dir *d, char *digest) +{ + Message *m; + char *name; + int loaded; + + m = emalloc(sizeof(Message)); + m->name = estrstrdup(d->name, "/"); + m->next = nil; + m->prev = mbox->tail; + m->level= mbox->level+1; + m->recursed = 0; + name = estrstrdup(dir, m->name); + loaded = loadinfo(m, name); + free(name); + /* if two upas/fs are running, we can get misled, so check digest before accepting message */ + if(loaded==0 || (digest!=nil && m->digest!=nil && strcmp(digest, m->digest)!=0)){ + mesgfreeparts(m); + free(m); + return 0; + } + if(mbox->tail != nil) + mbox->tail->next = m; + mbox->tail = m; + if(mbox->head == nil) + mbox->head = m; + + if (m->level != 1){ + m->recursed = 1; + readmbox(m, dir, m->name); + } + return 1; +} + +int +thisyear(char *year) +{ + static char now[10]; + char *s; + + if(now[0] == '\0'){ + s = ctime(time(nil)); + strcpy(now, s+24); + } + return strncmp(year, now, 4) == 0; +} + +char* +stripdate(char *as) +{ + int n; + char *s, *fld[10]; + + as = estrdup(as); + s = estrdup(as); + n = tokenize(s, fld, 10); + if(n > 5){ + sprint(as, "%.3s ", fld[0]); /* day */ + /* some dates have 19 Apr, some Apr 19 */ + if(strlen(fld[1])<4 && isnumeric(fld[1])) + sprint(as+strlen(as), "%.3s %.3s ", fld[1], fld[2]); /* date, month */ + else + sprint(as+strlen(as), "%.3s %.3s ", fld[2], fld[1]); /* date, month */ + /* do we use time or year? depends on whether year matches this one */ + if(thisyear(fld[5])){ + if(strchr(fld[3], ':') != nil) + sprint(as+strlen(as), "%.5s ", fld[3]); /* time */ + else if(strchr(fld[4], ':') != nil) + sprint(as+strlen(as), "%.5s ", fld[4]); /* time */ + }else + sprint(as+strlen(as), "%.4s ", fld[5]); /* year */ + } + free(s); + return as; +} + +char* +readfile(char *dir, char *name, int *np) +{ + char *file, *data; + int fd, len; + Dir *d; + + if(np != nil) + *np = 0; + file = estrstrdup(dir, name); + fd = open(file, OREAD); + if(fd < 0) + return nil; + d = dirfstat(fd); + free(file); + len = 0; + if(d != nil) + len = d->length; + free(d); + data = emalloc(len+1); + read(fd, data, len); + close(fd); + if(np != nil) + *np = len; + return data; +} + +char* +info(Message *m, int ind, int ogf) +{ + char *i; + int j, len, lens; + char *p; + char fmt[80], s[80]; + + if (ogf) + p=m->to; + else + p=m->fromcolon; + + if(ind==0 && shortmenu){ + len = 30; + lens = 30; + if(shortmenu > 1){ + len = 10; + lens = 25; + } + if(ind==0 && m->subject[0]=='\0'){ + snprint(fmt, sizeof fmt, " %%-%d.%ds", len, len); + snprint(s, sizeof s, fmt, p); + }else{ + snprint(fmt, sizeof fmt, " %%-%d.%ds %%-%d.%ds", len, len, lens, lens); + snprint(s, sizeof s, fmt, p, m->subject); + } + i = estrdup(s); + + return i; + } + + i = estrdup(""); + i = eappend(i, "\t", p); + i = egrow(i, "\t", stripdate(m->date)); + if(ind == 0){ + if(strcmp(m->type, "text")!=0 && strncmp(m->type, "text/", 5)!=0 && + strncmp(m->type, "multipart/", 10)!=0) + i = egrow(i, "\t(", estrstrdup(m->type, ")")); + }else if(strncmp(m->type, "multipart/", 10) != 0) + i = egrow(i, "\t(", estrstrdup(m->type, ")")); + if(m->subject[0] != '\0'){ + i = eappend(i, "\n", nil); + for(j=0; jsubject); + } + return i; +} + +void +mesgmenu0(Window *w, Message *mbox, char *realdir, char *dir, int ind, Biobuf *fd, int onlyone, int dotail) +{ + int i; + Message *m; + char *name, *tmp; + int ogf=0; + + if(strstr(realdir, "outgoing") != nil) + ogf=1; + + /* show mail box in reverse order, pieces in forward order */ + if(ind > 0) + m = mbox->head; + else + m = mbox->tail; + while(m != nil){ + for(i=0; iname); + tmp = info(m, ind, ogf); + Bprint(fd, "%s%s\n", name, tmp); + free(tmp); + if(dotail && m->tail) + mesgmenu0(w, m, realdir, name, ind+1, fd, 0, dotail); + free(name); + if(ind) + m = m->next; + else + m = m->prev; + if(onlyone) + m = nil; + } +} + +void +mesgmenu(Window *w, Message *mbox) +{ + winopenbody(w, OWRITE); + mesgmenu0(w, mbox, mbox->name, "", 0, w->body, 0, !shortmenu); + winclosebody(w); +} + +/* one new message has arrived, as mbox->tail */ +void +mesgmenunew(Window *w, Message *mbox) +{ + Biobuf *b; + + winselect(w, "0", 0); + w->data = winopenfile(w, "data"); + b = emalloc(sizeof(Biobuf)); + Binit(b, w->data, OWRITE); + mesgmenu0(w, mbox, mbox->name, "", 0, b, 1, !shortmenu); + Bterm(b); + free(b); + if(!mbox->dirty) + winclean(w); + /* select tag line plus following indented lines, but not final newline (it's distinctive) */ + winselect(w, "0/.*\\n((\t.*\\n)*\t.*)?/", 1); + close(w->addr); + close(w->data); + w->addr = -1; + w->data = -1; +} + +char* +name2regexp(char *prefix, char *s) +{ + char *buf, *p, *q; + + buf = emalloc(strlen(prefix)+2*strlen(s)+50); /* leave room to append more */ + p = buf; + *p++ = '0'; + *p++ = '/'; + *p++ = '^'; + strcpy(p, prefix); + p += strlen(prefix); + for(q=s; *q!='\0'; q++){ + if(strchr(regexchars, *q) != nil) + *p++ = '\\'; + *p++ = *q; + } + *p++ = '/'; + *p = '\0'; + return buf; +} + +void +mesgmenumarkdel(Window *w, Message *mbox, Message *m, int writeback) +{ + char *buf; + + + if(m->deleted) + return; + m->writebackdel = writeback; + if(w->data < 0) + w->data = winopenfile(w, "data"); + buf = name2regexp("", m->name); + strcat(buf, "-#0"); + if(winselect(w, buf, 1)) + write(w->data, deleted, 10); + free(buf); + close(w->data); + close(w->addr); + w->addr = w->data = -1; + mbox->dirty = 1; + m->deleted = 1; +} + +void +mesgmenumarkundel(Window *w, Message*, Message *m) +{ + char *buf; + + if(m->deleted == 0) + return; + if(w->data < 0) + w->data = winopenfile(w, "data"); + buf = name2regexp(deletedrx, m->name); + if(winselect(w, buf, 1)) + if(winsetaddr(w, deletedaddr, 1)) + write(w->data, "", 0); + free(buf); + close(w->data); + close(w->addr); + w->addr = w->data = -1; + m->deleted = 0; +} + +void +mesgmenudel(Window *w, Message *mbox, Message *m) +{ + char *buf; + + if(w->data < 0) + w->data = winopenfile(w, "data"); + buf = name2regexp(deletedrx, m->name); + if(winsetaddr(w, buf, 1) && winsetaddr(w, ".,./.*\\n(\t.*\\n)*/", 1)) + write(w->data, "", 0); + free(buf); + close(w->data); + close(w->addr); + w->addr = w->data = -1; + mbox->dirty = 1; + m->deleted = 1; +} + +void +mesgmenumark(Window *w, char *which, char *mark) +{ + char *buf; + + if(w->data < 0) + w->data = winopenfile(w, "data"); + buf = name2regexp(deletedrx01, which); + if(winsetaddr(w, buf, 1) && winsetaddr(w, "+0-#1", 1)) /* go to end of line */ + write(w->data, mark, strlen(mark)); + free(buf); + close(w->data); + close(w->addr); + w->addr = w->data = -1; + if(!mbox.dirty) + winclean(w); +} + +void +mesgfreeparts(Message *m) +{ + free(m->name); + free(m->replyname); + free(m->fromcolon); + free(m->from); + free(m->to); + free(m->cc); + free(m->replyto); + free(m->date); + free(m->subject); + free(m->type); + free(m->disposition); + free(m->filename); + free(m->digest); +} + +void +mesgdel(Message *mbox, Message *m) +{ + Message *n, *next; + + if(m->opened) + error("internal error: deleted message still open in mesgdel"); + /* delete subparts */ + for(n=m->head; n!=nil; n=next){ + next = n->next; + mesgdel(m, n); + } + /* remove this message from list */ + if(m->next) + m->next->prev = m->prev; + else + mbox->tail = m->prev; + if(m->prev) + m->prev->next = m->next; + else + mbox->head = m->next; + + mesgfreeparts(m); +} + +int +mesgsave(Message *m, char *s) +{ + int ofd, n, k, ret; + char *t, *raw, *unixheader, *all; + + t = estrstrdup(mbox.name, m->name); + raw = readfile(t, "raw", &n); + unixheader = readfile(t, "unixheader", &k); + if(raw==nil || unixheader==nil){ + fprint(2, "Mail: can't read %s: %r\n", t); + free(t); + return 0; + } + free(t); + + all = emalloc(n+k+1); + memmove(all, unixheader, k); + memmove(all+k, raw, n); + memmove(all+k+n, "\n", 1); + n = k+n+1; + free(unixheader); + free(raw); + ret = 1; + s = estrdup(s); + if(s[0] != '/') + s = egrow(estrdup(mailboxdir), "/", s); + ofd = open(s, OWRITE); + if(ofd < 0){ + fprint(2, "Mail: can't open %s: %r\n", s); + ret = 0; + }else if(seek(ofd, 0LL, 2)<0 || write(ofd, all, n)!=n){ + fprint(2, "Mail: save failed: can't write %s: %r\n", s); + ret = 0; + } + free(all); + close(ofd); + free(s); + return ret; +} + +int +mesgcommand(Message *m, char *cmd) +{ + char *s; + char *args[10]; + int ok, ret, nargs; + + s = cmd; + ret = 1; + nargs = tokenize(s, args, nelem(args)); + if(nargs == 0) + return 0; + if(strcmp(args[0], "Post") == 0){ + mesgsend(m); + goto Return; + } + if(strncmp(args[0], "Save", 4) == 0){ + if(m->isreply) + goto Return; + s = estrdup("\t[saved"); + if(nargs==1 || strcmp(args[1], "")==0){ + ok = mesgsave(m, "stored"); + }else{ + ok = mesgsave(m, args[1]); + s = eappend(s, " ", args[1]); + } + if(ok){ + s = egrow(s, "]", nil); + mesgmenumark(mbox.w, m->name, s); + } + free(s); + goto Return; + } + if(strcmp(args[0], "Reply")==0){ + if(nargs>=2 && strcmp(args[1], "all")==0) + mkreply(m, "Replyall", nil, nil, nil); + else + mkreply(m, "Reply", nil, nil, nil); + goto Return; + } + if(strcmp(args[0], "Q") == 0){ + s = winselection(m->w); /* will be freed by mkreply */ + if(nargs>=3 && strcmp(args[1], "Reply")==0 && strcmp(args[2], "all")==0) + mkreply(m, "QReplyall", nil, nil, s); + else + mkreply(m, "QReply", nil, nil, s); + goto Return; + } + if(strcmp(args[0], "Del") == 0){ + if(windel(m->w, 0)){ + chanfree(m->w->cevent); + free(m->w); + m->w = nil; + if(m->isreply) + delreply(m); + else{ + m->opened = 0; + m->tagposted = 0; + } + free(cmd); + threadexits(nil); + } + goto Return; + } + if(strcmp(args[0], "Delmesg") == 0){ + if(!m->isreply){ + mesgmenumarkdel(wbox, &mbox, m, 1); + free(cmd); /* mesgcommand might not return */ + mesgcommand(m, estrdup("Del")); + return 1; + } + goto Return; + } + if(strcmp(args[0], "UnDelmesg") == 0){ + if(!m->isreply && m->deleted) + mesgmenumarkundel(wbox, &mbox, m); + goto Return; + } +// if(strcmp(args[0], "Headers") == 0){ +// m->showheaders(); +// return True; +// } + + ret = 0; + + Return: + free(cmd); + return ret; +} + +void +mesgtagpost(Message *m) +{ + if(m->tagposted) + return; + wintagwrite(m->w, " Post", 5); + m->tagposted = 1; +} + +/* need to expand selection more than default word */ +#pragma varargck argpos eval 2 + +long +eval(Window *w, char *s, ...) +{ + char buf[64]; + va_list arg; + + va_start(arg, s); + vsnprint(buf, sizeof buf, s, arg); + va_end(arg); + + if(winsetaddr(w, buf, 1)==0) + return -1; + + if(pread(w->addr, buf, 24, 0) != 24) + return -1; + return strtol(buf, 0, 10); +} + +int +isemail(char *s) +{ + int nat; + + nat = 0; + for(; *s; s++) + if(*s == '@') + nat++; + else if(!isalpha(*s) && !isdigit(*s) && !strchr("_.-+/", *s)) + return 0; + return nat==1; +} + +char addrdelim[] = "/[ \t\\n<>()\\[\\]]/"; +char* +expandaddr(Window *w, Event *e) +{ + char *s; + long q0, q1; + + if(e->q0 != e->q1) /* cannot happen */ + return nil; + + q0 = eval(w, "#%d-%s", e->q0, addrdelim); + if(q0 == -1) /* bad char not found */ + q0 = 0; + else /* increment past bad char */ + q0++; + + q1 = eval(w, "#%d+%s", e->q0, addrdelim); + if(q1 < 0){ + q1 = eval(w, "$"); + if(q1 < 0) + return nil; + } + if(q0 >= q1) + return nil; + s = emalloc((q1-q0)*UTFmax+1); + winread(w, q0, q1, s); + return s; +} + +int +replytoaddr(Window *w, Message *m, Event *e, char *s) +{ + int did; + char *buf; + Plumbmsg *pm; + + buf = nil; + did = 0; + if(e->flag & 2){ + /* autoexpanded; use our own bigger expansion */ + buf = expandaddr(w, e); + if(buf == nil) + return 0; + s = buf; + } + if(isemail(s)){ + did = 1; + pm = emalloc(sizeof(Plumbmsg)); + pm->src = estrdup("Mail"); + pm->dst = estrdup("sendmail"); + pm->data = estrdup(s); + pm->ndata = -1; + if(m->subject && m->subject[0]){ + pm->attr = emalloc(sizeof(Plumbattr)); + pm->attr->name = estrdup("Subject"); + if(tolower(m->subject[0]) != 'r' || tolower(m->subject[1]) != 'e' || m->subject[2] != ':') + pm->attr->value = estrstrdup("Re: ", m->subject); + else + pm->attr->value = estrdup(m->subject); + pm->attr->next = nil; + } + if(plumbsend(plumbsendfd, pm) < 0) + fprint(2, "error writing plumb message: %r\n"); + plumbfree(pm); + } + free(buf); + return did; +} + + +void +mesgctl(void *v) +{ + Message *m; + Window *w; + Event *e, *eq, *e2, *ea; + int na, nopen, i, j; + char *os, *s, *t, *buf; + + m = v; + w = m->w; + threadsetname("mesgctl"); + proccreate(wineventproc, w, STACK); + for(;;){ + e = recvp(w->cevent); + switch(e->c1){ + default: + Unk: + 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 */ + case 'M': + switch(e->c2){ + case 'x': /* mouse only */ + case 'X': + ea = nil; + eq = e; + if(e->flag & 2){ + e2 = recvp(w->cevent); + eq = e2; + } + if(e->flag & 8){ + ea = recvp(w->cevent); + recvp(w->cevent); + na = ea->nb; + }else + na = 0; + if(eq->q1>eq->q0 && eq->nb==0){ + s = emalloc((eq->q1-eq->q0)*UTFmax+1); + winread(w, eq->q0, eq->q1, s); + }else + s = estrdup(eq->b); + if(na){ + t = emalloc(strlen(s)+1+na+1); + sprint(t, "%s %s", s, ea->b); + free(s); + s = t; + } + if(!mesgcommand(m, s)) /* send it back */ + winwriteevent(w, e); + break; + + case 'l': /* mouse only */ + 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; + } + os = s; + nopen = 0; + do{ + /* skip mail box name if present */ + if(strncmp(s, mbox.name, strlen(mbox.name)) == 0) + s += strlen(mbox.name); + if(strstr(s, "body") != nil){ + /* strip any known extensions */ + for(i=0; exts[i].ext!=nil; i++){ + j = strlen(exts[i].ext); + if(strlen(s)>j && strcmp(s+strlen(s)-j, exts[i].ext)==0){ + s[strlen(s)-j] = '\0'; + break; + } + } + if(strlen(s)>5 && strcmp(s+strlen(s)-5, "/body")==0) + s[strlen(s)-4] = '\0'; /* leave / in place */ + } + nopen += mesgopen(&mbox, mbox.name, s, m, 0, nil); + while(*s!=0 && *s++!='\n') + ; + }while(*s); + if(nopen == 0 && e->c1 == 'L') + nopen += replytoaddr(w, m, e, os); + if(nopen == 0) + winwriteevent(w, e); + free(buf); + break; + + case 'I': /* modify away; we don't care */ + case 'D': + mesgtagpost(m); + /* fall through */ + case 'd': + case 'i': + break; + + default: + goto Unk; + } + } + } +} + +void +mesgline(Message *m, char *header, char *value) +{ + if(strlen(value) > 0) + Bprint(m->w->body, "%s: %s\n", header, value); +} + +int +isprintable(char *type) +{ + int i; + + for(i=0; goodtypes[i]!=nil; i++) + if(strcmp(type, goodtypes[i])==0) + return 1; + return 0; +} + +char* +ext(char *type) +{ + int i; + + for(i=0; exts[i].type!=nil; i++) + if(strcmp(type, exts[i].type)==0) + return exts[i].ext; + return ""; +} + +void +mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly) +{ + char *dest; + + if(strcmp(m->disposition, "file")==0 || strlen(m->filename)!=0){ + if(strlen(m->filename) == 0){ + dest = estrdup(m->name); + dest[strlen(dest)-1] = '\0'; + }else + dest = estrdup(m->filename); + if(m->filename[0] != '/') + dest = egrow(estrdup(home), "/", dest); + Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), dest); + free(dest); + }else if(!fileonly) + Bprint(w->body, "\tfile is %s%sbody%s\n", rootdir, name, ext(m->type)); +} + +void +printheader(char *dir, Biobuf *b, char **okheaders) +{ + char *s; + char *lines[100]; + int i, j, n; + + s = readfile(dir, "header", nil); + if(s == nil) + return; + n = getfields(s, lines, nelem(lines), 0, "\n"); + for(i=0; itype, "message/rfc822") != 0){ /* suppress headers of envelopes */ + if(strlen(m->from) > 0){ + Bprint(w->body, "From: %s\n", m->from); + mesgline(m, "Date", m->date); + mesgline(m, "To", m->to); + mesgline(m, "CC", m->cc); + mesgline(m, "Subject", m->subject); + printheader(dir, w->body, extraheaders); + }else{ + printheader(dir, w->body, okheaders); + printheader(dir, w->body, extraheaders); + } + Bprint(w->body, "\n"); + } + + if(m->level == 1 && m->recursed == 0){ + m->recursed = 1; + readmbox(m, rootdir, m->name); + } + if(m->head == nil){ /* single part message */ + if(strcmp(m->type, "text")==0 || strncmp(m->type, "text/", 5)==0){ + mimedisplay(m, m->name, rootdir, w, 1); + s = readbody(m->type, dir, &n); + winwritebody(w, s, n); + free(s); + }else + mimedisplay(m, m->name, rootdir, w, 0); + }else{ + /* multi-part message, either multipart/* or message/rfc822 */ + thisone = nil; + if(strcmp(m->type, "multipart/alternative") == 0){ + thisone = m->head; /* in case we can't find a good one */ + for(mp=m->head; mp!=nil; mp=mp->next) + if(isprintable(mp->type)){ + thisone = mp; + break; + } + } + for(mp=m->head; mp!=nil; mp=mp->next){ + if(thisone!=nil && mp!=thisone) + continue; + subdir = estrstrdup(dir, mp->name); + name = estrstrdup(file, mp->name); + /* skip first element in name because it's already in window name */ + if(mp != m->head) + Bprint(w->body, "\n===> %s (%s) [%s]\n", strchr(name, '/')+1, mp->type, mp->disposition); + if(strcmp(mp->type, "text")==0 || strncmp(mp->type, "text/", 5)==0){ + mimedisplay(mp, name, rootdir, w, 1); + printheader(subdir, w->body, okheaders); + printheader(subdir, w->body, extraheaders); + winwritebody(w, "\n", 1); + s = readbody(mp->type, subdir, &n); + winwritebody(w, s, n); + free(s); + }else{ + if(strncmp(mp->type, "multipart/", 10)==0 || strcmp(mp->type, "message/rfc822")==0){ + mp->w = w; + mesgload(mp, rootdir, name, w); + mp->w = nil; + }else + mimedisplay(mp, name, rootdir, w, 0); + } + free(name); + free(subdir); + } + } + free(dir); +} + +int +tokenizec(char *str, char **args, int max, char *splitc) +{ + int na; + int intok = 0; + + if(max <= 0) + return 0; + for(na=0; *str != '\0';str++){ + if(strchr(splitc, *str) == nil){ + if(intok) + continue; + args[na++] = str; + intok = 1; + }else{ + /* it's a separator/skip character */ + *str = '\0'; + if(intok){ + intok = 0; + if(na >= max) + break; + } + } + } + return na; +} + +Message* +mesglookup(Message *mbox, char *name, char *digest) +{ + int n; + Message *m; + char *t; + + if(digest){ + /* can find exactly */ + for(m=mbox->head; m!=nil; m=m->next) + if(strcmp(digest, m->digest) == 0) + break; + return m; + } + + n = strlen(name); + if(n == 0) + return nil; + if(name[n-1] == '/') + t = estrdup(name); + else + t = estrstrdup(name, "/"); + for(m=mbox->head; m!=nil; m=m->next) + if(strcmp(t, m->name) == 0) + break; + free(t); + return m; +} + +/* + * Find plumb port, knowing type is text, given file name (by extension) + */ +int +plumbportbysuffix(char *file) +{ + char *suf; + int i, nsuf, nfile; + + nfile = strlen(file); + for(i=0; ports[i].type!=nil; i++){ + suf = ports[i].suffix; + nsuf = strlen(suf); + if(nfile > nsuf) + if(cistrncmp(file+nfile-nsuf, suf, nsuf) == 0) + return i; + } + return 0; +} + +/* + * Find plumb port using type and file name (by extension) + */ +int +plumbport(char *type, char *file) +{ + int i; + + for(i=0; ports[i].type!=nil; i++) + if(strncmp(type, ports[i].type, strlen(ports[i].type)) == 0) + return i; + /* see if it's a text type */ + for(i=0; goodtypes[i]!=nil; i++) + if(strncmp(type, goodtypes[i], strlen(goodtypes[i])) == 0) + return plumbportbysuffix(file); + return -1; +} + +void +plumb(Message *m, char *dir) +{ + int i; + char *port; + Plumbmsg *pm; + + if(strlen(m->type) == 0) + return; + i = plumbport(m->type, m->filename); + if(i < 0) + fprint(2, "can't find destination for message subpart\n"); + else{ + port = ports[i].port; + pm = emalloc(sizeof(Plumbmsg)); + pm->src = estrdup("Mail"); + if(port) + pm->dst = estrdup(port); + else + pm->dst = nil; + pm->wdir = nil; + pm->type = estrdup("text"); + pm->ndata = -1; + pm->data = estrstrdup(dir, "body"); + pm->data = eappend(pm->data, "", ports[i].suffix); + if(plumbsend(plumbsendfd, pm) < 0) + fprint(2, "error writing plumb message: %r\n"); + plumbfree(pm); + } +} + +int +mesgopen(Message *mbox, char *dir, char *s, Message *mesg, int plumbed, char *digest) +{ + char *t, *u, *v; + Message *m; + char *direlem[10]; + int i, ndirelem, reuse; + + /* find white-space-delimited first word */ + for(t=s; *t!='\0' && !isspace(*t); t++) + ; + u = emalloc(t-s+1); + memmove(u, s, t-s); + /* separate it on slashes */ + ndirelem = tokenizec(u, direlem, nelem(direlem), "/"); + if(ndirelem <= 0){ + Error: + free(u); + return 0; + } + if(plumbed){ + write(wctlfd, "top", 3); + write(wctlfd, "current", 7); + } + /* open window for message */ + m = mesglookup(mbox, direlem[0], digest); + if(m == nil) + goto Error; + if(mesg!=nil && m!=mesg) /* string looked like subpart but isn't part of this message */ + goto Error; + if(m->opened == 0){ + if(m->w == nil){ + reuse = 0; + m->w = newwindow(); + }else{ + reuse = 1; + /* re-use existing window */ + if(winsetaddr(m->w, "0,$", 1)){ + if(m->w->data < 0) + m->w->data = winopenfile(m->w, "data"); + write(m->w->data, "", 0); + } + } + v = estrstrdup(mbox->name, m->name); + winname(m->w, v); + free(v); + if(!reuse){ + if(m->deleted) + wintagwrite(m->w, "Q Reply all UnDelmesg Save ", 2+6+4+10+5); + else + wintagwrite(m->w, "Q Reply all Delmesg Save ", 2+6+4+8+5); + } + threadcreate(mesgctl, m, STACK); + winopenbody(m->w, OWRITE); + mesgload(m, dir, m->name, m->w); + winclosebody(m->w); + winclean(m->w); + m->opened = 1; + if(ndirelem == 1){ + free(u); + return 1; + } + } + if(ndirelem == 1 && plumbport(m->type, m->filename) <= 0){ + /* make sure dot is visible */ + ctlprint(m->w->ctl, "show\n"); + return 0; + } + /* walk to subpart */ + dir = estrstrdup(dir, m->name); + for(i=1; iname, nil); + } + if(m != nil && plumbport(m->type, m->filename) > 0) + plumb(m, dir); + free(dir); + free(u); + return 1; +} + +void +rewritembox(Window *w, Message *mbox) +{ + Message *m, *next; + char *deletestr, *t; + int nopen; + + deletestr = estrstrdup("delete ", fsname); + + nopen = 0; + for(m=mbox->head; m!=nil; m=next){ + next = m->next; + if(m->deleted == 0) + continue; + if(m->opened){ + nopen++; + continue; + } + if(m->writebackdel){ + /* messages deleted by plumb message are not removed again */ + t = estrdup(m->name); + if(strlen(t) > 0) + t[strlen(t)-1] = '\0'; + deletestr = egrow(deletestr, " ", t); + } + mesgmenudel(w, mbox, m); + mesgdel(mbox, m); + } + if(write(mbox->ctlfd, deletestr, strlen(deletestr)) < 0) + fprint(2, "Mail: warning: error removing mail message files: %r\n"); + free(deletestr); + winselect(w, "0", 0); + if(nopen == 0) + winclean(w); + mbox->dirty = 0; +} + +/* name is a full file name, but it might not belong to us */ +Message* +mesglookupfile(Message *mbox, char *name, char *digest) +{ + int k, n; + + k = strlen(name); + n = strlen(mbox->name); + if(k==0 || strncmp(name, mbox->name, n) != 0){ +// fprint(2, "Mail: message %s not in this mailbox\n", name); + return nil; + } + return mesglookup(mbox, name+n, digest); +} -- cgit v1.2.3