ref: f07f9eee353fe4e8407878fd16e42cc7485cf45b
dir: /mesg.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <thread.h> #include <regexp.h> #include "mail.h" #define Datefmt "?WWW, ?MMM ?DD hh:mm:ss ?Z YYYY" typedef struct Fn Fn; struct Fn { char *name; void (*fn)(Mesg *, char **, int); }; void mesgclear(Mesg *m) { free(m->name); 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->messageid); free(m->filename); free(m->digest); free(m->mflags); free(m->fromcolon); } void mesgfree(Mesg *m) { if(m == nil) return; mesgclear(m); free(m); } static char* line(char *data, char **pp, int z) { char *p, *q; for(p=data; *p!='\0' && *p!='\n'; p++) ; if(*p == '\n') *pp = p+1; else *pp = p; if(z && p == data) return nil; q = emalloc(p-data + 1); memmove(q, data, p-data); return q; } static char* fc(Mesg *m, char *s) { char *r; if(s != nil && m->from != nil){ r = smprint("%s <%s>", s, m->from); free(s); return r; } if(m->from != nil) return estrdup(m->from); if(s != nil) return s; return estrdup("??"); } Mesg* mesgload(char *name) { char *info, *p; int ninfo; Mesg *m; Tm tm; m = emalloc(sizeof(Mesg)); m->name = estrjoin(name, "/", nil); if((info = rslurp(m, "info", &ninfo)) == nil) return nil; p = info; m->from = line(p, &p, 0); m->to = line(p, &p, 0); m->cc = line(p, &p, 0); m->replyto = line(p, &p, 1); m->date = line(p, &p, 0); m->subject = line(p, &p, 0); m->type = line(p, &p, 1); m->disposition = line(p, &p, 1); m->filename = line(p, &p, 1); m->digest = line(p, &p, 1); /* m->bcc = */ free(line(p, &p, 1)); m->inreplyto = line(p, &p, 1); /* m->date = */ free(line(p, &p, 1)); /* m->sender = */ free(line(p, &p, 1)); m->messageid = line(p, &p, 0); /* m->lines = */ free(line(p, &p, 1)); /* m->size = */ free(line(p, &p, 1)); m->mflags = line(p, &p, 0); /* m->fileid = */ free(line(p, &p, 1)); m->fromcolon = fc(m, line(p, &p, 0)); free(info); m->flags = Funseen; if(strchr(m->mflags, 'd')) m->flags |= Fdel; if(strchr(m->mflags, 's')) m->flags &= ~Funseen; if(strchr(m->mflags, 'a')) m->flags |= Fresp; m->time = time(nil); if(tmparse(&tm, Datefmt, m->date, nil, nil) != nil) m->time = tmnorm(&tm); m->hash = 0; if(m->messageid != nil) m->hash = strhash(m->messageid); return m; } static Mesg* readparts(Mesg *m) { char *dpath, *apath; int n, i, dfd; Mesg *a, *best; Dir *d; dpath = estrjoin(mbox.path, m->name, nil); dfd = open(dpath, OREAD); free(dpath); if(dfd == -1) return m; n = dirreadall(dfd, &d); close(dfd); if(n == -1) sysfatal("%s read: %r", mbox.path); best = nil; m->attachments = emalloc(n*sizeof(Mesg*)); m->nattachments = 0; for(i = 0; i < n; i++){ if(d[i].qid.type != QTDIR) continue; apath = estrjoin(m->name, d[i].name, nil); a = mesgload(apath); free(apath); if(a == nil) continue; m->attachments[m->nattachments++] = a; if(a->filename != nil || a->disposition != nil && strcmp(a->disposition, "inline") != 0) continue; if(strcmp(a->type, "text/plain") == 0) best = a; else if(best == nil && strcmp(a->type, "text/html") == 0) best = a; } free(d); if(best == nil) return nil; return best; } static void execfmt(void *pm) { Mesg *m; m = pm; rfork(RFFDG); dup(m->fd[1], 1); close(m->fd[0]); close(m->fd[1]); sendul(m->sync, 0); procexecl(nil, "/bin/htmlfmt", "-a", "-cutf-8", m->path, nil); } static int htmlfmt(Mesg *m, char *path) { if(pipe(m->fd) == -1) sysfatal("pipe: %r"); m->sync = chancreate(sizeof(ulong), 0); m->path = path; proccreate(execfmt, m, Stack); recvul(m->sync); chanfree(m->sync); close(m->fd[1]); return m->fd[0]; } static void copy(int wfd, int rfd) { char *buf; int n; buf = emalloc(Bufsz); while(1){ n = read(rfd, buf, Bufsz); if(n <= 0) break; if(write(wfd, buf, n) != n) break; } free(buf); } static int mesgshow(Mesg *m) { char *path, *home, *name; int i, rfd, wfd; Mesg *a, *b; if((wfd = winopen(m, "body", OWRITE)) == -1) return -1; fprint(wfd, "From: %s\n", m->from); fprint(wfd, "Date: %s\n", m->date); fprint(wfd, "Subject: %s\n\n", m->subject); b = readparts(m); path = estrjoin(mbox.path, b->name, "body", nil); if(strcmp(b->type, "text/html") == 0) rfd = htmlfmt(m, path); else rfd = open(path, OREAD); free(path); if(rfd != -1){ copy(wfd, rfd); close(rfd); } home = getenv("home"); if(m->nattachments != 0) fprint(wfd, "\n\n"); for(i = 0; i < m->nattachments; i++){ a = m->attachments[i]; if(a == b) continue; name = a->name; if(strncmp(a->name, m->name, strlen(m->name)) == 0) name += strlen(m->name); fprint(wfd, "===> %s (%s)\n", name, a->type); if(a->disposition != nil && strcmp(a->disposition, "inline") == 0){ path = estrjoin(mbox.path, b->name, "body", nil); if((rfd = open(path, OREAD)) != -1){ copy(wfd, rfd); close(rfd); } }else{ name = a->filename; if(name == nil) name = "body"; fprint(wfd, "\tcp %s%sbody %s/%s\n", mbox.path, a->name, home, name); continue; } } close(wfd); free(home); return 0; } static void reply(Mesg *m, char **f, int nf) { if(nf >= 2 || nf >= 1 && strcmp(f[0], "All") != 0){ fprint(2, "Q: invaid args\n"); return; } /* FIXME: get all recievers of the message */ compose1(m->from, m, 0); } static void qreply(Mesg *m, char **f, int nf) { if(nf >= 3 || nf >= 2 && strcmp(f[1], "All") != 0 || nf >= 1 && strcmp(f[0], "Reply") != 0 || nf == 0){ fprint(2, "Q: invaid args\n"); return; } /* FIXME: get all recievers of the message */ compose1(m->from, m, 1); } static void delmesg(Mesg *m, char **, int nf) { if(nf != 0){ fprint(2, "Delmesg: too many args\n"); return; } m->flags |= Ftodel; m->quitting = 1; mbredraw(m, 0, 0); } static void mesgquit(Mesg *m, char **, int) { m->quitting = 1; } static Fn mesgfn[] = { {"Q", qreply}, {"Reply", reply}, {"Delmesg", delmesg}, {"Save", nil}, {"Del", mesgquit}, {nil} }; static void mesgmain(void *mp) { char *a, *path, *f[32]; Event ev; Mesg *m; Fn *p; int nf; m = mp; m->quitting = 0; path = estrjoin(mbox.path, m->name, nil); wininit(m, path); free(path); wintagwrite(m, "Q Reply all Delmesg Save "); mesgshow(m); fprint(m->ctl, "clean\n"); mbox.nopen++; while(!m->quitting){ if(winevent(m, &ev) != 'M') continue; if(strcmp(ev.text, "Del") == 0) break; switch(ev.type){ case 'l': case 'L': if((a = matchaddr(m, &ev)) != nil) compose1(a, nil, 0); else if(matchmesg(m, ev.text)) mesgopen(ev.text, nil); else winsendevent(m, &ev); free(a); break; case 'x': case 'X': if((nf = tokenize(ev.text, f, nelem(f))) == 0) continue; for(p = mesgfn; p->name != nil; p++){ if(strcmp(p->name, f[0]) == 0 && p->fn != nil){ p->fn(m, &f[1], nf - 1); break; } } if(p->fn == nil) winsendevent(m, &ev); break; } } mbox.nopen--; m->flags &= ~Fopen; winclose(m); threadexits(nil); } void mesgpath2name(char *buf, int nbuf, char *name) { char *e; int n; n = strlen(mbox.path); if(strncmp(name, mbox.path, n) == 0){ e = strecpy(buf, buf + nbuf - 2, name + n); e[0] = '/'; e[1] = 0; }else strecpy(buf, buf+nbuf, name); } int mesgmatch(Mesg *m, char *name, char *digest) { if(m->flags & Fdummy) return 0; if(strcmp(m->name, name) == 0) return digest == nil || strcmp(m->digest, digest) == 0; return 0; } Mesg* mesglookup(char *name, char *digest) { char buf[32]; int i; mesgpath2name(buf, sizeof(buf), name); for(i = 0; i < mbox.nmesg; i++) if(mesgmatch(mbox.mesg[i], buf, digest)) return mbox.mesg[i]; return nil; } void mesgopen(char *name, char *digest) { Mesg *m; char *path; int fd; m = mesglookup(name, digest); if(m == nil || (m->flags & Fopen)) return; assert(!(m->flags & Fdummy)); m->flags |= Fopen; if(m->flags & Funseen){ m->flags &= ~Funseen; path = estrjoin(mbox.path, "/", m->name, "/flags", nil); if((fd = open(path, OWRITE)) != -1){ fprint(fd, "+s"); close(fd); } mbredraw(m, 0, 0); free(path); } threadcreate(mesgmain, m, Stack); }