ref: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
dir: /sys/src/cmd/upas/imap4d/imp.c/
#include "imap4d.h" static char magic[] = "imap internal mailbox description\n"; /* another appearance of this nasty hack. */ typedef struct{ Avl; Msg *m; }Mtree; static Avltree *mtree; static Bin *mbin; static int mtreecmp(Avl *va, Avl *vb) { Mtree *a, *b; a = (Mtree*)va; b = (Mtree*)vb; return strcmp(a->m->info[Idigest], b->m->info[Idigest]); } static Namedint flagcmap[Nflags] = { {"s", Fseen}, {"a", Fanswered}, {"f", Fflagged}, {"D", Fdeleted}, {"d", Fdraft}, {"r", Frecent}, }; static int parseflags(char *flags) { int i, f; f = 0; for(i = 0; i < Nflags; i++){ if(flags[i] == '-') continue; if(flags[i] != flagcmap[i].name[0]) return 0; f |= flagcmap[i].v; } return f; } static int impflags(Box *box, Msg *m, char *flags) { int f; f = parseflags(flags); /* * recent flags are set until the first time message's box is selected or examined. * it may be stored in the file as a side effect of a status or subscribe command; * if so, clear it out. */ if((f & Frecent) && strcmp(box->fs, "imap") == 0) box->dirtyimp = 1; f |= m->flags & Frecent; /* * all old messages with changed flags should be reported to the client */ if(m->uid && m->flags != f){ box->sendflags = 1; m->sendflags = 1; } m->flags = f; return 1; } /* * considerations: * . messages can be deleted by another agent * . we might still have a Msg for an expunged message, * because we haven't told the client yet. * . we can have a Msg without a .imp entry. * . flag information is added at the end of the .imp by copy & append */ static int rdimp(Biobuf *b, Box *box) { char *s, *f[4]; uint u; Msg *m, m0; Mtree t, *p; memset(&m0, 0, sizeof m0); for(; s = Brdline(b, '\n'); ){ s[Blinelen(b) - 1] = 0; if(tokenize(s, f, nelem(f)) != 3) return -1; u = strtoul(f[1], 0, 10); memset(&t, 0, sizeof t); m0.info[Idigest] = f[0]; t.m = &m0; p = (Mtree*)avllookup(mtree, &t, 0); if(p){ m = p->m; if(m->uid && m->uid != u){ ilog("dup? %ud %ud %s", u, m->uid, f[0]); continue; } if(m->uid >= box->uidnext){ ilog("uid %ud >= %ud\n", m->uid, box->uidnext); box->uidnext = m->uid; } if(m->uid == 0) m->flags = 0; if(impflags(box, m, f[2]) == -1) return -1; m->uid = u; }else{ /* * message has been deleted. */ // ilog("flags, uid dropped on floor [%s, %ud]", m0.info[Idigest], u); } } return 0; } enum{ Rmagic, Rrdstr, Rtok, Rvalidity, Ruidnext, }; static char *rtab[] = { "magic", "rdstr", "tok", "val", "uidnext" }; char* sreason(int r) { if(r >= 0 && r <= nelem(rtab)) return rtab[r]; return "*GOK*"; } static int verscmp(Biobuf *b, Box *box, int *reason) { char *s, *f[3]; int n; uint u, v; n = -1; *reason = Rmagic; if(s = Brdstr(b, '\n', 0)) n = strcmp(s, magic); free(s); if(n == -1) return -1; n = -1; v = box->uidvalidity; if((s = Brdstr(b, '\n', 1)) && ++*reason) if(tokenize(s, f, nelem(f)) == 2 && ++*reason) if((u = strtoul(f[0], 0, 10)) == v || v == 0 && ++*reason) if((v = strtoul(f[1], 0, 10)) >= box->uidnext && ++*reason){ box->uidvalidity = u; box->uidnext = v; n = 0; } free(s); return n; } int parseimp(Biobuf *b, Box *box) { int r, reason; Msg *m; Mtree *p; if(verscmp(b, box, &reason) == -1) return -1; mtree = avlcreate(mtreecmp); r = 0; for(m = box->msgs; m; m = m->next) r++; p = binalloc(&mbin, r*sizeof *p, 1); if(p == nil) bye("no memory"); for(m = box->msgs; m; m = m->next){ p->m = m; avlinsert(mtree, p); p++; } r = rdimp(b, box); binfree(&mbin); free(mtree); return r; } static void wrimpflags(char *buf, int flags, int killrecent) { int i; if(killrecent) flags &= ~Frecent; memset(buf, '-', Nflags); for(i = 0; i < Nflags; i++) if(flags & flagcmap[i].v) buf[i] = flagcmap[i].name[0]; buf[i] = 0; } int wrimp(Biobuf *b, Box *box) { char buf[16]; int i; Msg *m; box->dirtyimp = 0; Bprint(b, "%s", magic); Bprint(b, "%.*ud %.*ud\n", Nuid, box->uidvalidity, Nuid, box->uidnext); i = strcmp(box->fs, "imap") == 0; for(m = box->msgs; m != nil; m = m->next){ if(m->expunged) continue; wrimpflags(buf, m->flags, i); Bprint(b, "%.*s %.*ud %s\n", Ndigest, m->info[Idigest], Nuid, m->uid, buf); } return 0; } static uint scanferdup(Biobuf *b, char *digest, int *flags, vlong *pos) { char *s, *f[4]; uint uid; uid = 0; for(; s = Brdline(b, '\n'); ){ s[Blinelen(b) - 1] = 0; if(tokenize(s, f, nelem(f)) != 3) return ~0; if(strcmp(f[0], digest) == 0){ uid = strtoul(f[1], 0, 10); // fprint(2, "digest %s matches uid %ud\n", f[0], uid); *flags |= parseflags(f[2]); break; } *pos += Blinelen(b); } return uid; } int appendimp(char *bname, char *digest, int flags, Uidplus *u) { char buf[16], *iname; int fd, reason; uint dup; vlong pos; Biobuf b; Box box; dup = 0; pos = 0; memset(&box, 0, sizeof box); iname = impname(bname); fd = cdopen(mboxdir, iname, ORDWR); if(fd == -1){ fd = cdcreate(mboxdir, iname, OWRITE, 0664); if(fd == -1) return -1; box.uidvalidity = time(0); box.uidnext = 1; }else{ dup = ~0; Binit(&b, fd, OREAD); if(verscmp(&b, &box, &reason) == -1) ilog("bad verscmp %s", sreason(reason)); else{ pos = Bseek(&b, 0, 1); dup = scanferdup(&b, digest, &flags, &pos); } Bterm(&b); } if(dup == ~0){ close(fd); return -1; } Binit(&b, fd, OWRITE); if(dup == 0){ Bseek(&b, 0, 0); Bprint(&b, "%s", magic); Bprint(&b, "%.*ud %.*ud\n", Nuid, box.uidvalidity, Nuid, box.uidnext + 1); Bseek(&b, 0, 2); }else Bseek(&b, pos, 0); wrimpflags(buf, flags, 0); Bprint(&b, "%.*s %.*ud %s\n", Ndigest, digest, Nuid, dup? dup: box.uidnext, buf); Bterm(&b); close(fd); u->uidvalidity = box.uidvalidity; u->uid = box.uidnext; return 0; }