summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/imap4d/imp.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
commit963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc (patch)
tree749b74875dbc49bcf6ed0776648b8f0ef9417407 /sys/src/cmd/upas/imap4d/imp.c
parent8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff)
downloadplan9front-963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc.tar.xz
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/upas/imap4d/imp.c')
-rw-r--r--sys/src/cmd/upas/imap4d/imp.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/sys/src/cmd/upas/imap4d/imp.c b/sys/src/cmd/upas/imap4d/imp.c
new file mode 100644
index 000000000..c5542eab7
--- /dev/null
+++ b/sys/src/cmd/upas/imap4d/imp.c
@@ -0,0 +1,315 @@
+#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);
+ 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;
+}