#include #include #include #include #include #include <9pclient.h> #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", /* must be first for plumbport() */ "image/gif", "image", ".gif", "image/jpeg", "image", ".jpg", "image/jpeg", "image", ".jpeg", "image/png", "image", ".png", "application/postscript", "postscript", ".ps", "application/pdf", "postscript", ".pdf", "application/msword", "msword", ".doc", "application/rtf", "msword", ".rtf", nil, nil}; char* goodtypes[] = { "text", "text/plain", "message/rfc822", "text/richtext", "text/tab-separated-values", "application/octet-stream", 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; } static char* mkaddrs(char* t, char** colon) { int i, nf, inquote; char **f, *s; Fmt fmt; inquote = 0; nf = 2; for (s = t; *s; s++) { if (*s == '\'') inquote = !inquote; if (*s == ' ' && !inquote) nf++; } f = emalloc(nf * sizeof f[0]); nf = tokenize(t, f, nf); if (colon) { fmtstrinit(&fmt); for (i = 0; i + 1 < nf; i += 2) { if (i > 0) fmtprint(&fmt, ", "); if (f[i][0] == 0 || strcmp(f[i], f[i + 1]) == 0) fmtprint(&fmt, "%s", f[i + 1]); else fmtprint(&fmt, "%s <%s>", f[i], f[i + 1]); } *colon = fmtstrflush(&fmt); } fmtstrinit(&fmt); for (i = 0; i + 1 < nf; i += 2) { if (i > 0) fmtprint(&fmt, ", "); fmtprint(&fmt, "%s", f[i + 1]); } free(f); return fmtstrflush(&fmt); } int loadinfo(Message* m, char* dir) { int n; char *data, *p, *s, *t; data = readfile(dir, "info", &n); if (data == nil) return 0; p = data; while ((s = line(p, &p)) != nil && *s != 0) { t = strchr(s, ' '); if (t == nil) continue; *t++ = 0; if (strcmp(s, "from") == 0) { free(m->from); m->from = mkaddrs(t, &m->fromcolon); } else if (strcmp(s, "sender") == 0) { free(m->sender); m->sender = mkaddrs(t, nil); } else if (strcmp(s, "to") == 0) { free(m->to); m->to = mkaddrs(t, nil); } else if (strcmp(s, "cc") == 0) { free(m->cc); m->cc = mkaddrs(t, nil); } else if (strcmp(s, "replyto") == 0) { free(m->replyto); m->replyto = mkaddrs(t, nil); } else if (strcmp(s, "subject") == 0) { free(m->subject); m->subject = estrdup(t); } else if (strcmp(s, "type") == 0) { free(m->type); m->type = estrdup(t); } else if (strcmp(s, "unixdate") == 0 && (t = strchr(t, ' ')) != nil) { free(m->date); m->date = estrdup(t + 1); } else if (strcmp(s, "digest") == 0) { free(m->digest); m->digest = estrdup(t); } else if (strcmp(s, "filename") == 0) { free(m->filename); m->filename = estrdup(t); } free(s); } free(s); free(data); if (m->replyto == nil) { if (m->sender) m->replyto = estrdup(m->sender); else if (m->from) m->replyto = estrdup(m->from); else m->replyto = estrdup(""); } if (m->from == nil) m->from = estrdup(""); if (m->to == nil) m->to = estrdup(""); if (m->cc == nil) m->cc = estrdup(""); if (m->subject == nil) m->subject = estrdup(""); if (m->type == nil) m->type = estrdup(""); if (m->date == nil) m->date = estrdup(""); if (m->disposition == nil) m->disposition = estrdup(""); if (m->filename == nil) m->filename = estrdup(""); if (m->digest == nil) m->digest = estrdup(""); return 1; } int isnumeric(char* s) { while (*s) { if (!isdigit(*s)) return 0; s++; } return 1; } CFid* mailopen(char* name, int mode) { if (strncmp(name, "Mail/", 5) != 0) return nil; return fsopen(mailfs, name + 5, mode); } Dir* loaddir(char* name, int* np) { CFid* fid; Dir* dp; fid = mailopen(name, OREAD); if (fid == nil) return nil; *np = fsdirreadall(fid, &dp); fsclose(fid); 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; i < n; i++) { d = &dirp[i]; if (isnumeric(d->name)) 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 len; Dir* d; CFid* fid; char buf[1]; if (np != nil) *np = 0; file = estrstrdup(dir, name); fid = mailopen(file, OREAD); if (fid == nil) return nil; d = fsdirfstat(fid); if (d && d->length == 0) { /* some files, e.g. body, are not loaded until we read them */ fsread(fid, buf, 1); fsseek(fid, 0, 0); free(d); d = fsdirfstat(fid); } free(file); len = 0; if (d != nil) len = d->length; free(d); data = emalloc(len + 1); len = fsreadn(fid, data, len); if (len <= 0) { fsclose(fid); free(data); return nil; } fsclose(fid); 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; j < ind; j++) i = eappend(i, "\t", nil); i = eappend(i, "\t", m->subject); } return i; } void mesgmenu0( Window* w, Message* mbox, char* realdir, char* dir, int ind, CFid* 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; i < ind; i++) fsprint(fd, "\t"); if (ind != 0) fsprint(fd, " "); name = estrstrdup(dir, m->name); tmp = info(m, ind, ogf); fsprint(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)); mesgmenu0(w, mbox, mbox->name, "", 0, w->data, 1, !shortmenu); 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); fsclose(w->addr); fsclose(w->data); w->addr = nil; w->data = nil; } 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 == nil) w->data = winopenfile(w, "data"); buf = name2regexp("", m->name); strcat(buf, "-#0"); if (winselect(w, buf, 1)) fswrite(w->data, deleted, 10); free(buf); fsclose(w->data); fsclose(w->addr); w->addr = nil; w->data = nil; mbox->dirty = 1; m->deleted = 1; } void mesgmenumarkundel(Window* w, Message* v, Message* m) { char* buf; USED(v); if (m->deleted == 0) return; if (w->data == nil) w->data = winopenfile(w, "data"); buf = name2regexp(deletedrx, m->name); if (winselect(w, buf, 1)) if (winsetaddr(w, deletedaddr, 1)) fswrite(w->data, "", 0); free(buf); fsclose(w->data); fsclose(w->addr); w->addr = nil; w->data = nil; m->deleted = 0; } void mesgmenudel(Window* w, Message* mbox, Message* m) { char* buf; if (w->data == nil) w->data = winopenfile(w, "data"); buf = name2regexp(deletedrx01, m->name); if (winsetaddr(w, buf, 1) && winsetaddr(w, ".,./.*\\n(\t.*\\n)*/", 1)) fswrite(w->data, "", 0); free(buf); fsclose(w->data); fsclose(w->addr); w->addr = nil; w->data = nil; /* assume caller knows best mbox->dirty = 1; */ m->deleted = 1; } void mesgmenumark(Window* w, char* which, char* mark) { char* buf; if (w->data == nil) w->data = winopenfile(w, "data"); buf = name2regexp(deletedrx01, which); if (winsetaddr(w, buf, 1) && winsetaddr(w, "+0-#1", 1)) /* go to end of line */ fswrite(w->data, mark, strlen(mark)); free(buf); fsclose(w->data); fsclose(w->addr); w->addr = nil; w->data = nil; if (!mbox.dirty) winclean(w); } void mesgfreeparts(Message* m) { free(m->name); free(m->replyname); 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\n"); /* 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 save) { int ofd, n, k, ret; char *t, *raw, *unixheader, *all; if (save) { if (fsprint(mbox.ctlfd, "save %q %q", s, m->name) < 0) { fprint(2, "Mail: can't save %s to %s: %r\n", m->name, s); return 0; } return 1; } 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 save, 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 || strncmp(args[0], "Write", 5) == 0) { if (m->isreply) goto Return; save = args[0][0] == 'S'; if (save) s = estrdup("\t[saved"); else s = estrdup("\t[wrote"); if (nargs == 1 || strcmp(args[1], "") == 0) { ok = mesgsave(m, "stored", save); } else { ok = mesgsave(m, args[1], save); 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)) { windecref(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 (fspread(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 (plumbsendtofid(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"); winincref(w); 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; ports[i].suffix != nil; i++) { j = strlen(ports[i].suffix); if ( strlen(s) > j && strcmp(s + strlen(s) - j, ports[i].suffix) == 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) fsprint(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; ports[i].type != nil; i++) if (strcmp(type, ports[i].type) == 0) return ports[i].suffix; 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 || !fileonly) { if (strlen(m->filename) == 0) dest = estrstrdup("a", ext(m->type)); else dest = estrdup(m->filename); if (m->filename[0] != '/') dest = egrow(estrdup(home), "/", dest); fsprint( w->body, "\t9p read %s/%s/%sbody > %s\n", srvname, mboxname, name, dest); free(dest); } } void printheader(char* dir, CFid* fid, 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; i < n; i++) for (j = 0; okheaders[j]; j++) if (cistrncmp(lines[i], okheaders[j], strlen(okheaders[j])) == 0) fsprint(fid, "%s\n", lines[i]); free(s); } void mesgload(Message* m, char* rootdir, char* file, Window* w) { char *s, *subdir, *name, *dir; Message *mp, *thisone; int n; dir = estrstrdup(rootdir, file); if (strcmp(m->type, "message/rfc822") != 0) { /* suppress headers of envelopes */ if (strlen(m->from) > 0) { fsprint(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); } fsprint(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) fsprint( 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 i, na; int intok = 0; char* p; if (max <= 0) return 0; /* if(strchr(str, ',') || strchr(str, '"') || strchr(str, '<') || strchr(str, * '(')) */ /* splitc = ","; */ 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; } } } for (i = 0; i < na; i++) { while (*args[i] && strchr(" \t\r\n", *args[i])) args[i]++; p = args[i] + strlen(args[i]); while (p > args[i] && strchr(" \t\r\n", *(p - 1))) *--p = 0; } return na; } Message* mesglookup(Message* mbox, char* name, char* digest) { int n; Message* m; char* t; if (digest && digest[0]) { /* 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 (plumbsendtofid(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; } /*XXX if(plumbed) drawtopwindow(); */ /* 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 == nil) m->w->data = winopenfile(m->w, "data"); fswrite(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); /* sleep(100); */ 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; i < ndirelem; i++) { m = mesglookup(m, direlem[i], digest); if (m == nil) break; dir = egrow(dir, m->name, 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 (fswrite(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); }