#include #include #include #include #include "util.h" /* * Generate a list of msgs from a plan b mail box. * This can be used either to read mail by running the * program in the mail box directory or to generate * indexes for msgs */ #define dprint if(debug)fprint int showarch; int showspam; char** months; // show only these dirs char* showrunes; int nmonths; Biobuf bout; Biobuf blist; int debug; char* readf(char*f) { static char buf[1024]; char err[ERRMAX]; Dir* d; int fd; long n; fd = open(f, OREAD); if(fd < 0) return nil; d = dirfstat(fd); if(d == nil) goto fail; n = d->length; if(n == 0 || n > sizeof buf - 1) n = sizeof buf - 1; if(n == 0) n = read(fd, buf, n); else n = readn(fd, buf, n); if(n < 0) goto fail; buf[n] = 0; free(d); close(fd); return buf; fail: rerrstr(err, sizeof(err)); free(d); close(fd); werrstr(err); return nil; } char* gethdr(char** hdrs, char* h) { int l; l = strlen(h); while(*hdrs){ if(cistrncmp(*hdrs, h, l) == 0) return *hdrs + l + 1; hdrs++; } return ""; } char* cleanfrom(char* f) { char *c, *e; c = strdup(f); e = strchr(c, '@'); if(!e) return nil; else *e = '\0'; return c; } void hdrline(char*fn, char* buf, char* flags) { char* hdrs[20+1]; int nhdrs; char* f; char* cf; char* s; Dir* de; int n; int fd; int i; int l; s = buf; for(i = 0; s && i < 20; i++){ f = utfrune(s, '\n'); if(f) *f++ = 0; hdrs[i] = s; s = f; } nhdrs = i; hdrs[nhdrs] = nil; f = gethdr(hdrs, "from"); s = gethdr(hdrs, "subject"); cf = cleanfrom(f); l = strlen(s); if(l > 48) l = 48; Bprint(&bout, "%s%-19.19s %-12.12s %-*.*s\n", flags, fn, cf?cf:f, l, l, s); free(cf); fd = open(fn, OREAD); n = dirreadall(fd, &de); close(fd); if(n > 1) Bprint(&bout, "\t"); for(i = 0; i < n; i++) if(strcmp(de[i].name, "text") != 0) Bprint(&bout, "\t%s/%s\n", fn, de[i].name); } int mustshow(char* name) { Rune r; int nc; if(isdigit(name[0])) return 1; if(showarch && name[0] == 'a' && name[1] == '.') return 1; if(showrunes != nil){ nc = chartorune(&r, name); if(utfrune(showrunes, r) != nil && name[nc] == '.') return 1; } return 0; } 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; } static void attachline(char* msg, char* rel) { Dir* d; int nd; int fd; int i; fd = open(msg, OREAD); if(fd < 0) return; nd = dirreadall(fd, &d); close(fd); for(i = 0; i < nd; i++) if(strcmp(d[i].name, "text") && strcmp(d[i].name, "raw") && strcmp(d[i].name, "L.mbox")) if(d[i].qid.type&QTDIR) Bprint(&bout, "\t%s/%s/text\n", rel, d[i].name); else Bprint(&bout, "\t%s/%s\n", rel, d[i].name); if(nd > 0) free(d); } static int listf(char* file, int mayarchive) { Biobuf* bin; char* ln; char* s; char* e; int l; int show; int some; int allarchived; Dir* d; bin = Bopen(file, OREAD); if(bin == nil) return 0; show = some = 0; dprint(2, "%s: listing [file] %s\n", argv0, file); allarchived = 1; while((ln = Brdline(bin, '\n')) != nil){ l = Blinelen(bin); if(l < 2) continue; ln[l-1] = 0; if(ln[0] >= '0' && ln[0] <= '9'){ s = strchr(ln, '/'); if(s != nil){ s++; if(s[0] >= '0' && s[0] <= '9') allarchived = 0; show = mustshow(s); if(show){ e = utfrune(s, ' '); if(e != nil) *e = 0; Bprint(&blist, "%s\n", s); if(e != nil) *e = ' '; } } } if(show != 0){ some++; Bprint(&bout, "%s\n", ln); } } Bterm(bin); if(mayarchive && allarchived){ /* move archive from 200901.l to 200901.la; skip it next time */ dprint(2, "%s: %s -> %sa\n", argv0, file, file); d = dirstat(file); if(d != nil){ s = smprint("%sa", d->name); if(s != nil){ nulldir(d); d->name = s; dirwstat(file, d); free(s); } free(d); } } return some; } /* * list msgs in dir. If a file named `dir.l' or `dir.la' exists and is up to date * it is considered a listing of dir and used as a cache. */ int list(char* dir) { Dir* d; Dir* dd; int n; int fd; int i; char* tf; char* sf; int some; char* suf; char* buf; char* flags; char* list; int archivedlist; fd = open(dir, OREAD); if(fd < 0){ fprint(2, "%s: %r\n", dir); return 0; } list = smprint("%s.l", dir); d = dirstat(list); dd = dirfstat(fd); archivedlist = 0; if(d == nil){ free(list); list = smprint("%s.la", dir); d = dirstat(list); archivedlist = 1; } if(d != nil && dd != nil && d->length > 0 && dd->mtime < d->mtime){ free(d); free(dd); close(fd); if(archivedlist && showarch == 0 && showrunes == nil) some = 0; else some = listf(list, archivedlist == 0); free(list); return some; } free(d); free(dd); dprint(2, "%s: listing %s\n", argv0, dir); n = dirreadall(fd, &d); close(fd); if(n <= 0) return 0; some = 0; qsort(d, n, sizeof(Dir), cmpent); for(i = 0; i < n; i++) if(mustshow(d[i].name)){ dprint(2, "%s: listing %s/%s\n", argv0, dir, d[i].name); flags = ""; some++; tf = smprint("%s/%s/text", dir, d[i].name); buf = readf(tf); sf = tf + strlen(dir) - 1; for(suf = sf; *suf != '/' && suf > tf; suf--) ; suf++; hdrline(suf, buf, flags); free(tf); Bprint(&blist, "%s/%s\n", dir, d[i].name); tf = smprint("%s/%s", dir, d[i].name); sf = tf + strlen(dir) - 1; for(suf = sf; *suf != '/' && suf > tf; suf--) ; suf++; attachline(tf, suf); free(tf); } free(d); return some; } int member(char* s, char** list, int nlist) { int i; for(i = 0; i < nlist; i++) if(strcmp(s, list[i]) == 0) return 1; return 0; } int listmbox(char* mbox) { Dir* d; int fd; int i; int n; int some; char* dir; dprint(2, "%s: reading mbox...", argv0); fd = open(mbox, OREAD); if(fd < 0){ fprint(2, "%s: %r\n", mbox); return -1; } n = dirreadall(fd, &d); close(fd); dprint(2, "done\n"); if(n <= 0) return 0; some = 0; qsort(d, n, sizeof(Dir), cmpent); for(i = 0; i < n; i++) if(d[i].qid.type&QTDIR){ if(nmonths > 0) if(!member(d[i].name, months, nmonths)) continue; dir=smprint("%s/%s", mbox, d[i].name); some |= list(dir); free(dir); } free(d); return some; } void usage(void) { fprint(2, "usage: %s [-aD] [-s runes] [dir] [month...]\n", argv0); exits("usage"); } void main(int argc, char*argv[]) { char* dir; char* top; int fd; char* list; top = smprint("/mail/box/%s", getuser()); ARGBEGIN{ case 'D': debug++; break; case 'a': showarch++; break; case 's': showrunes = EARGF(usage()); break; default: usage(); }ARGEND; if(argc == 1) dir = cleanpath(argv[0], top); else{ months = argv; nmonths= argc; dir = smprint("%s/msgs", top); } list = smprint("/tmp/msgs.%s", getuser()); fd = create(list, OWRITE, 0640); if(fd < 0) sysfatal("%s: %r\n", list); free(list); Binit(&blist, fd, OWRITE); Binit(&bout, 1, OWRITE); if(!listmbox(dir)) Bprint(&bout, "No mail\n"); Bterm(&bout); Bterm(&blist); exits(nil); }