summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/fs
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/fs
parent8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff)
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/upas/fs')
-rw-r--r--sys/src/cmd/upas/fs/bos.c40
-rw-r--r--sys/src/cmd/upas/fs/cache.c552
-rwxr-xr-xsys/src/cmd/upas/fs/chkidxbin0 -> 75510 bytes
-rw-r--r--sys/src/cmd/upas/fs/chkidx.c416
-rw-r--r--sys/src/cmd/upas/fs/dat.h407
-rwxr-xr-xsys/src/cmd/upas/fs/extra/fd2pathbin0 -> 36947 bytes
-rw-r--r--sys/src/cmd/upas/fs/extra/fd2path.c32
-rw-r--r--sys/src/cmd/upas/fs/extra/idxtst.c58
-rw-r--r--sys/src/cmd/upas/fs/extra/infotst.c89
-rwxr-xr-xsys/src/cmd/upas/fs/extra/pawbin0 -> 62365 bytes
-rw-r--r--sys/src/cmd/upas/fs/extra/paw.c23
-rw-r--r--sys/src/cmd/upas/fs/extra/prflags.c37
-rw-r--r--sys/src/cmd/upas/fs/extra/strtotmtst.c17
-rw-r--r--sys/src/cmd/upas/fs/extra/tokens.c62
-rw-r--r--sys/src/cmd/upas/fs/fs.c1293
-rw-r--r--sys/src/cmd/upas/fs/header.c176
-rw-r--r--sys/src/cmd/upas/fs/idx.c535
-rw-r--r--sys/src/cmd/upas/fs/imap.c1271
-rw-r--r--sys/src/cmd/upas/fs/imap4.c878
-rw-r--r--sys/src/cmd/upas/fs/mbox.c1714
-rw-r--r--sys/src/cmd/upas/fs/mdir.c354
-rw-r--r--sys/src/cmd/upas/fs/mkfile22
-rw-r--r--sys/src/cmd/upas/fs/mtree.c80
-rw-r--r--sys/src/cmd/upas/fs/plan9.c475
-rw-r--r--sys/src/cmd/upas/fs/planb.c144
-rw-r--r--sys/src/cmd/upas/fs/pop3.c374
-rw-r--r--sys/src/cmd/upas/fs/readdir.c15
-rw-r--r--sys/src/cmd/upas/fs/ref.c100
-rw-r--r--sys/src/cmd/upas/fs/remove.c141
-rw-r--r--sys/src/cmd/upas/fs/rename.c234
-rw-r--r--sys/src/cmd/upas/fs/rfc2047-test28
-rw-r--r--sys/src/cmd/upas/fs/seg.c164
-rw-r--r--sys/src/cmd/upas/fs/strtotm.c74
-rw-r--r--sys/src/cmd/upas/fs/tester.c81
34 files changed, 6992 insertions, 2894 deletions
diff --git a/sys/src/cmd/upas/fs/bos.c b/sys/src/cmd/upas/fs/bos.c
new file mode 100644
index 000000000..fdbf83f52
--- /dev/null
+++ b/sys/src/cmd/upas/fs/bos.c
@@ -0,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+/*
+ * assume:
+ * - the stack segment can't be resized
+ * - stacks may not be segattached (by name Stack, anyway)
+ * - no thread library
+ */
+uintptr
+absbos(void)
+{
+ char buf[64], *f[10], *s, *r;
+ int n;
+ uintptr p, q;
+ Biobuf *b;
+
+ p = 0xd0000000; /* guess pc kernel */
+ snprint(buf, sizeof buf, "/proc/%ud/segment", getpid());
+ b = Bopen(buf, OREAD);
+ if(b == nil)
+ return p;
+ for(; s = Brdstr(b, '\n', 1); free(s)){
+ if((n = tokenize(s, f, nelem(f))) < 3)
+ continue;
+ if(strcmp(f[0], "Stack") != 0)
+ continue;
+ /*
+ * addressing from end because segment
+ * flags could become discontiguous if
+ * additional flags are added
+ */
+ q = strtoull(f[n - 3], &r, 16);
+ if(*r == 0 && (char*)q > end)
+ p = q;
+ }
+ Bterm(b);
+ return p;
+}
diff --git a/sys/src/cmd/upas/fs/cache.c b/sys/src/cmd/upas/fs/cache.c
new file mode 100644
index 000000000..2d0a6280d
--- /dev/null
+++ b/sys/src/cmd/upas/fs/cache.c
@@ -0,0 +1,552 @@
+#include "common.h"
+#include <libsec.h>
+#include "dat.h"
+
+int
+findcache(Mcache *c, Message *m)
+{
+ int i;
+
+ for(i = 0; i < c->ntab; i++)
+ if(c->ctab[i] == m)
+ return i;
+ return -1;
+}
+
+static void
+prcache(Mcache *c, char *prefix)
+{
+ int j;
+ Message *m;
+
+ if(!debug)
+ return;
+ for(j = 0; j < c->ntab; j++){
+ m = c->ctab[j];
+ dprint("%s%d/%s\t%p\t%d\t%ld\n", prefix, j, m->name, m, m->refs, m->csize);
+ }
+}
+
+/* debugging only */
+static void
+dupchk(Mcache *c)
+{
+ int i, j;
+
+ if(!debug)
+ return;
+ for(i = 0; i < c->ntab; i++)
+ for(j = i + 1; j < c->ntab; j++)
+ if(c->ctab[i] == c->ctab[j])
+ goto lose;
+ return;
+lose:
+ for(j = 0; j < c->ntab; j++)
+ dprint("%d\t%p %d\t%ld\n", j, c->ctab[j], c->ctab[j]->refs, c->ctab[j]->size);
+ abort();
+}
+
+int
+addcache(Mcache *c, Message *m)
+{
+ int i;
+
+ if((i = findcache(c, m)) < 0){
+ if(c->ntab + 1 == nelem(c->ctab))
+ abort();
+ i = c->ntab++;
+ }else{
+ /* rotate */
+ if(i == c->ntab - 1)
+ return i; /* silly shortcut to prevent excessive printage. */
+ dprint("addcache rotate %d %d\n", i, c->ntab);
+ prcache(c, "");
+ memmove(c->ctab + i, c->ctab + i + 1, (c->ntab - i - 1)*sizeof c->ctab[0]);
+ i = c->ntab - 1;
+c->ctab[i] = m;
+dupchk(c);
+ }
+ dprint("addcache %d %d %p\n", i, c->ntab, m);
+ c->ctab[i] = m;
+ return i;
+}
+
+static void
+notecache(Mailbox *mb, Message *m, long sz)
+{
+ assert(Topmsg(mb, m));
+ assert(sz >= 0 && sz < Maxmsg);
+ m->csize += sz;
+ mb->cached += sz;
+ addcache(mb, m);
+}
+
+static long
+cachefree0(Mailbox *mb, Message *m, int force)
+{
+ long sz, i;
+ Message *s;
+
+ if(!force && !mb->fetch)
+ return 0;
+ for(s = m->part; s; s = s->next)
+ cachefree(mb, s, force);
+ dprint("cachefree: %D %p, %p\n", m->fileid, m, m->start);
+ if(m->mallocd){
+ free(m->start);
+ m->mallocd = 0;
+ }
+ if(m->ballocd){
+ free(m->body);
+ m->ballocd = 0;
+ }
+ if(m->hallocd){
+ free(m->header);
+ m->hallocd = 0;
+ }
+ for(i = 0; i < nelem(m->references); i++){
+ free(m->references[i]);
+ m->references[i] = 0;
+ }
+ sz = m->csize;
+ m->csize = 0;
+ m->start = 0;
+ m->end = 0;
+ m->header = 0;
+ m->hend = 0;
+ m->hlen = -1;
+ m->body = 0;
+ m->bend = 0;
+ m->mheader = 0;
+ m->mhend = 0;
+ if(mb->decache)
+ mb->decache(mb, m);
+ m->decoded = 0;
+ m->converted = 0;
+ m->badchars = 0;
+ m->cstate &= ~(Cheader|Cbody);
+ if(Topmsg(mb, m))
+ mb->cached -= sz;
+ return sz;
+}
+
+long
+cachefree(Mailbox *mb, Message *m, int force)
+{
+ long sz, i;
+
+ sz = cachefree0(mb, m, force);
+ for(i = 0; i < mb->ntab; i++)
+ if(m == mb->ctab[i]){
+ mb->ntab--;
+ memmove(mb->ctab + i, mb->ctab + i + 1, sizeof m*mb->ntab - i);
+ dupchk(mb);
+ break;
+ }
+ return sz;
+}
+
+enum{
+ Maxntab = nelem(mbl->ctab) - 10,
+};
+
+vlong
+sumcache(Mcache *c)
+{
+ int i;
+ vlong sz;
+
+ sz = 0;
+ for(i = 0; i < c->ntab; i++)
+ sz += c->ctab[i]->csize;
+ return sz;
+}
+
+int
+scancache(Mcache *c)
+{
+ int i;
+
+ for(i = 0; i < c->ntab; i++)
+ if(c->ctab[i]->csize > Maxmsg)
+ return -1;
+ return 0;
+}
+
+/* debugging only */
+static void
+chkcsize(Mailbox *mb, vlong sz, vlong sz0)
+{
+ int j;
+ Mcache *c;
+ Message *m;
+
+ if(sumcache(mb) == mb->cached)
+ if(scancache(mb) == 0)
+ return;
+ eprint("sz0 %lld sz %lld sum %lld sumca %lld\n", sz0, sz, sumcache(mb), mb->cached);
+ eprint("%lld\n", sumcache(mb));
+ c = mb;
+ for(j = 0; j < c->ntab; j++){
+ m = c->ctab[j];
+ eprint("%d %p %d %ld %ld\n", j, m, m->refs, m->csize, m->size);
+ }
+ abort();
+}
+
+/*
+ * strategy: start with i = 0. while cache exceeds limits,
+ * find j so that all the [i:j] elements have refs == 0.
+ * uncache all the [i:j], reduce ntab by i-j. the tail
+ * [j+1:ntab] is shifted to [i:ntab], and finally i = i+1.
+ * we may safely skip the new i, since the condition
+ * that stopped our scan there still holds.
+ */
+void
+putcache(Mailbox *mb, Message *m)
+{
+ int i, j, k;
+ vlong sz, sz0;
+ Message **p;
+
+ p = mb->ctab;
+ sz0 = mb->cached;
+ dupchk(mb);
+ for(i = 0;; i++){
+ sz = mb->cached;
+ for(j = i;; j++){
+ if(j >= mb->ntab ||
+ sz < cachetarg && mb->ntab - (j - i) < Maxntab){
+ if(j != i)
+ break;
+chkcsize(mb, sz, sz0);
+ return;
+ }
+ if(p[j]->refs > 0)
+ break;
+ sz -= p[j]->csize;
+ }
+ if(sz == mb->cached){
+ if(i >= mb->ntab)
+ break;
+ continue;
+ }
+ for(k = i; k < j; k++)
+ cachefree0(mb, p[k], 0);
+ mb->ntab -= j - i;
+ memmove(p + i, p + j, (mb->ntab - i)*sizeof *p);
+ }
+chkcsize(mb, sz, sz0);
+ k = 0;
+ for(i = 0; i < mb->ntab; i++)
+ k += p[i]->refs > 0;
+ if((mb->ntab > 1 || k != mb->ntab) && Topmsg(mb, m))
+ eprint("cache overflow: %D %llud bytes; %d entries\n",
+ m? m->fileid: 1ll, mb->cached, mb->ntab);
+ if(k == mb->ntab)
+ return;
+ debug = 1; prcache(mb, "");
+ abort();
+}
+
+static int
+squeeze(Message *m, uvlong o, long l, int c)
+{
+ char *p, *q, *e;
+ int n;
+
+ q = memchr(m->start + o, c, l);
+ if(q == nil)
+ return 0;
+ n = 0;
+ e = m->start + o + l;
+ for(p = q; q < e; q++){
+ if(*q == c){
+ n++;
+ continue;
+ }
+ *p++ = *q;
+ }
+ return n;
+}
+
+void
+msgrealloc(Message *m, ulong l)
+{
+ long l0, h0, m0, me, b0;
+
+ l0 = m->end - m->start;
+ m->mallocd = 1;
+ h0 = m->hend - m->start;
+ m0 = m->mheader - m->start;
+ me = m->mhend - m->start;
+ b0 = m->body - m->start;
+ assert(h0 >= 0 && m0 >= 0 && me >= 0 && b0 >= 0);
+ m->start = erealloc(m->start, l + 1);
+ m->rbody = m->start + b0;
+ m->rbend = m->end = m->start + l0;
+ if(!m->hallocd){
+ m->header = m->start;
+ m->hend = m->start + h0;
+ }
+ if(!m->ballocd){
+ m->body = m->start + b0;
+ m->bend = m->start + l0;
+ }
+ m->mheader = m->start + m0;
+ m->mhend = m->start + me;
+}
+
+/*
+ * the way we squeeze out bad characters is exceptionally sneaky.
+ */
+static int
+fetch(Mailbox *mb, Message *m, uvlong o, ulong l)
+{
+ int expand;
+ long l0, n, sz0;
+
+top:
+ l0 = m->end - m->start;
+ assert(l0 >= 0);
+ dprint("fetch %lud sz %lud o %llud l %lud badchars %d\n", l0, m->size, o, l, m->badchars);
+ assert(m->badchars < Maxmsg/10);
+ if(l0 == m->size || o > m->size)
+ return 0;
+ expand = 0;
+ if(o + l > m->size)
+ l = m->size - o;
+ if(o + l == m->size)
+ l += m->ibadchars - m->badchars;
+ if(o + l > l0){
+ expand = 1;
+ msgrealloc(m, o + m->badchars + l);
+ }
+ assert(l0 <= o);
+ sz0 = m->size;
+ if(mb->fetch(mb, m, o + m->badchars, l) == -1){
+ logmsg(m, "can't fetch %D %llud %lud", m->fileid, o, l);
+ m->deleted = Dead;
+ return -1;
+ }
+ if(m->size - sz0)
+ l += m->size - sz0; /* awful botch for gmail */
+ if(expand){
+ /* grumble. poor planning. */
+ if(m->badchars > 0)
+ memmove(m->start + o, m->start + o + m->badchars, l);
+ n = squeeze(m, o, l, 0);
+ n += squeeze(m, o, l - n, '\r');
+ if(n > 0){
+ if(m->ibadchars == 0)
+ dprint(" %ld more badchars\n", n);
+ l -= n;
+ m->badchars += n;
+ msgrealloc(m, o + l);
+ }
+ notecache(mb, m, l);
+ m->bend = m->rbend = m->end = m->start + o + l;
+ if(n)
+ if(o + l + n == m->size && m->cstate&Cidx){
+ dprint(" redux %llud %ld\n", o + l, n);
+ o += l;
+ l = n;
+ goto top;
+ }
+ }else
+ eprint("unhandled case in fetch\n");
+ *m->end = 0;
+ return 0;
+}
+
+void
+cachehash(Mailbox *mb, Message *m)
+{
+// fprint(2, "cachehash %P\n", mpair(mb, m));
+ if(m->whole == m->whole->whole)
+ henter(PATH(mb->id, Qmbox), m->name,
+ (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
+ else
+ henter(PATH(m->whole->id, Qdir), m->name,
+ (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
+ henter(PATH(m->id, Qdir), "xxx",
+ (Qid){PATH(m->id, Qmax), 0, QTFILE}, m, mb); /* sleezy speedup */
+}
+
+void
+newcachehash(Mailbox *mb, Message *m, int doplumb)
+{
+ if(doplumb)
+ mailplumb(mb, m, 0);
+ else
+ if(insurecache(mb, m) == 0)
+ msgdecref(mb, m);
+ /* avoid cachehash on error? */
+ cachehash(mb, m);
+}
+
+static char *itab[] = {
+ "idx",
+ "stale",
+ "header",
+ "body"
+};
+
+char*
+cstate(Message *m)
+{
+ char *p, *e;
+ int i, s;
+ static char buf[64];
+
+ s = m->cstate;
+ p = e = buf;
+ e += sizeof buf;
+ for(i = 0; i < 8; i++)
+ if(s & 1<<i)
+ if(i < nelem(itab))
+ p = seprint(p, e, "%s ", itab[i]);
+ if(p > buf)
+ p--;
+ p[0] = 0;
+ return buf;
+}
+
+
+static int
+middlecache(Mailbox *mb, Message *m)
+{
+ int y;
+
+ y = 0;
+ while(!Topmsg(mb, m)){
+ m = m->whole;
+ if((m->cstate & Cbody) == 0)
+ y = 1;
+ }
+ if(y == 0)
+ return 0;
+ dprint("middlecache %d [%D] %lud %lud\n", m->id, m->fileid, m->end - m->start, m->size);
+ return cachebody(mb, m);
+}
+
+int
+cacheheaders(Mailbox *mb, Message *m)
+{
+ char *p, *e;
+ int r;
+ ulong o;
+
+ if(!mb->fetch || m->cstate&Cheader)
+ return 0;
+ if(!Topmsg(mb, m))
+ return middlecache(mb, m);
+ dprint("cacheheaders %d %D\n", m->id, m->fileid);
+ if(m->size < 10000)
+ r = fetch(mb, m, 0, m->size);
+ else for(r = 0; (o = m->end - m->start) < m->size; ){
+ if((r = fetch(mb, m, o, 4096)) < 0)
+ break;
+ p = m->start + o;
+ if(o)
+ p--;
+ for(e = m->end - 2; p < e; p++){
+ p = memchr(p, '\n', e - p);
+ if(p == nil)
+ break;
+ if(p[1] == '\n' || (p[1] == '\r' && p[2] == '\n'))
+ goto found;
+ }
+ }
+ if(r < 0)
+ return -1;
+found:
+ parseheaders(mb, m, mb->addfrom, 0);
+ return 0;
+}
+
+void
+digestmessage(Mailbox *mb, Message *m)
+{
+ assert(m->digest == 0);
+ m->digest = emalloc(SHA1dlen);
+ sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
+ if(mtreeisdup(mb, m)){
+ logmsg(m, "dup detected");
+ m->deleted = Dup; /* no dups allowed */
+ }else
+ mtreeadd(mb, m);
+ dprint("%d %#A\n", m->id, m->digest);
+}
+
+int
+cachebody(Mailbox *mb, Message *m)
+{
+ ulong o;
+
+ while(!Topmsg(mb, m))
+ m = m->whole;
+ if(!mb->fetch || m->cstate&Cbody)
+ return 0;
+ o = m->end - m->start;
+ dprint("cachebody %d [%D] %lud %lud %s\n", m->id, m->fileid, o, m->size, cstate(m));
+ if(o < m->size)
+ if(fetch(mb, m, o, m->size - o) < 0)
+ return -1;
+ if((m->cstate&Cidx) == 0){
+ assert(m->ibadchars == 0);
+ if(m->badchars > 0)
+ dprint("reducing size %ld %ld\n", m->size, m->size - m->badchars);
+ m->size -= m->badchars; /* sneaky */
+ m->ibadchars = m->badchars;
+ }
+ if(m->digest == 0)
+ digestmessage(mb, m);
+ if(m->lines == 0)
+ m->lines = countlines(m);
+ parse(mb, m, mb->addfrom, 0);
+ dprint(" →%s\n", cstate(m));
+ return 0;
+}
+
+int
+cacheidx(Mailbox *mb, Message *m)
+{
+ if(m->cstate & Cidx)
+ return 0;
+ if(cachebody(mb, m) == -1)
+ return -1;
+ m->cstate |= Cidxstale|Cidx;
+ return 0;
+}
+
+static int
+countparts(Message *m)
+{
+ Message *p;
+
+ if(m->nparts == 0)
+ for(p = m->part; p; p = p->next){
+ countparts(p);
+ m->nparts++;
+ }
+ return m->nparts;
+}
+
+int
+insurecache(Mailbox *mb, Message *m)
+{
+ if(m->deleted || !m->inmbox)
+ return -1;
+ msgincref(m);
+ cacheidx(mb, m);
+ if((m->cstate & Cidx) == 0){
+ logmsg(m, "%s: can't cache: %s: %r", mb->path, m->name);
+ msgdecref(mb, m);
+ return -1;
+ }
+ if(m->digest == 0)
+ sysfatal("digest?");
+ countparts(m);
+ return 0;
+}
diff --git a/sys/src/cmd/upas/fs/chkidx b/sys/src/cmd/upas/fs/chkidx
new file mode 100755
index 000000000..bac911139
--- /dev/null
+++ b/sys/src/cmd/upas/fs/chkidx
Binary files differ
diff --git a/sys/src/cmd/upas/fs/chkidx.c b/sys/src/cmd/upas/fs/chkidx.c
new file mode 100644
index 000000000..8b0a1545b
--- /dev/null
+++ b/sys/src/cmd/upas/fs/chkidx.c
@@ -0,0 +1,416 @@
+#include "common.h"
+#include <auth.h>
+#include <libsec.h>
+#include <bin.h>
+#include "dat.h"
+
+#define idprint(...) if(1) fprint(2, __VA_ARGS__); else {}
+enum{
+ Maxver = 10,
+};
+static char *magictab[Maxver] = {
+[4] "idx magic v4\n",
+[7] "idx magic v7\n",
+};
+static int fieldstab[Maxver] = {
+[4] 19,
+[7] 21,
+};
+
+static char *magic;
+static int Idxfields;
+static int lineno;
+static int idxver;
+
+int
+newid(void)
+{
+ static int id;
+
+ return ++id;
+}
+
+void*
+emalloc(ulong n)
+{
+ void *p;
+
+ p = mallocz(n, 1);
+ if(!p)
+ sysfatal("malloc %lud: %r", n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+static int
+Afmt(Fmt *f)
+{
+ char buf[SHA1dlen*2 + 1];
+ uchar *u, i;
+
+ u = va_arg(f->args, uchar*);
+ if(u == 0 && f->flags & FmtSharp)
+ return fmtstrcpy(f, "-");
+ if(u == 0)
+ return fmtstrcpy(f, "<nildigest>");
+ for(i = 0; i < SHA1dlen; i++)
+ sprint(buf + 2*i, "%2.2ux", u[i]);
+ return fmtstrcpy(f, buf);
+}
+
+static int
+Dfmt(Fmt *f)
+{
+ char buf[32];
+ int seq;
+ uvlong v;
+
+ v = va_arg(f->args, uvlong);
+ seq = v & 0xff;
+ if(seq > 99)
+ seq = 99;
+ snprint(buf, sizeof buf, "%llud.%.2d", v>>8, seq);
+ return fmtstrcpy(f, buf);
+}
+
+static Mailbox*
+shellmailbox(char *path)
+{
+ Mailbox *mb;
+
+ mb = malloc(sizeof *mb);
+ if(mb == 0)
+ sysfatal("malloc");
+ memset(mb, 0, sizeof *mb);
+ snprint(mb->path, sizeof mb->path, "%s", path);
+ mb->id = newid();
+ mb->root = newmessage(nil);
+ mb->mtree = mkavltree(mtreecmp);
+ return mb;
+}
+
+void
+shellmailboxfree(Mailbox*)
+{
+}
+
+Message*
+newmessage(Message *parent)
+{
+ static int id;
+ Message *m;
+
+// msgallocd++;
+
+ m = mallocz(sizeof *m, 1);
+ if(m == 0)
+ sysfatal("malloc");
+ m->disposition = Dnone;
+// m->type = newrefs("text/plain");
+// m->charset = newrefs("iso-8859-1");
+ m->cstate = Cidxstale;
+ m->flags = Frecent;
+ m->id = newid();
+ if(parent)
+ snprint(m->name, sizeof m->name, "%d", ++(parent->subname));
+ if(parent == nil)
+ parent = m;
+ m->whole = parent;
+ m->hlen = -1;
+ return m;
+}
+
+void
+unnewmessage(Mailbox *mb, Message *parent, Message *m)
+{
+ assert(parent->subname > 0);
+// delmessage(mb, m);
+ USED(mb, m);
+ parent->subname -= 1;
+}
+
+
+static int
+validmessage(Mailbox *mb, Message *m, int level)
+{
+ if(level){
+ if(m->digest != 0)
+ goto lose;
+ if(m->fileid <= 1000000ull<<8)
+ if(m->fileid != 0)
+ goto lose;
+ }else{
+ if(m->digest == 0)
+ goto lose;
+ if(m->size == 0)
+ goto lose;
+ if(m->fileid <= 1000000ull<<8)
+ goto lose;
+ if(mtreefind(mb, m->digest))
+ goto lose;
+ }
+ return 1;
+lose:
+ fprint(2, "invalid cache[%d] %#A size %ld %D\n", level, m->digest, m->size, m->fileid);
+ return 0;
+}
+
+static char*
+∫(char *x)
+{
+ if(x && *x)
+ return x;
+ return nil;
+}
+
+static char*
+brdstr(Biobuf *b, int c, int eat)
+{
+ char *s;
+
+ s = Brdstr(b, c, eat);
+ if(s)
+ lineno++;
+ return s;
+}
+
+static int
+nibble(int c)
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c < 0x20)
+ c += 0x20;
+ if(c >= 'a' && c <= 'f')
+ return c - 'a'+10;
+ return 0xff;
+}
+
+static uchar*
+hackdigest(char *s)
+{
+ uchar t[SHA1dlen];
+ int i;
+
+ if(strcmp(s, "-") == 0)
+ return 0;
+ if(strlen(s) != 2*SHA1dlen){
+ fprint(2, "bad digest %s\n", s);
+ return 0;
+ }
+ for(i = 0; i < SHA1dlen; i++)
+ t[i] = nibble(s[2*i])<<4 | nibble(s[2*i + 1]);
+ memmove(s, t, SHA1dlen);
+ return (uchar*)s;
+}
+
+static Message*
+findmessage(Mailbox *, Message *parent, int n)
+{
+ Message *m;
+
+ for(m = parent->part; m; m = m->next)
+ if(!m->digest && n-- == 0)
+ return m;
+ return 0;
+}
+
+static uvlong
+rdfileid(char *s, int level)
+{
+ char *p;
+ uvlong uv;
+
+ uv = strtoul(s, &p, 0);
+ if((level == 0 && uv < 1000000) || *p != '.')
+ return 0;
+ return uv<<8 | strtoul(p + 1, 0, 10);
+}
+
+static int
+rdidx(Biobuf *b, Mailbox *mb, Message *parent, int npart, int level)
+{
+ char *f[50 + 1], *s;
+ uchar *digest;
+ int n, nparts, good, bad, redux;
+ Message *m, **ll, *l;
+
+ bad = good = redux = 0;
+ ll = &parent->part;
+ nparts = npart;
+ for(; npart != 0 && (s = brdstr(b, '\n', 1)); npart--){
+//if(lineno>18&&lineno<25)idprint("%d: %d [%s]\n", lineno, level, s);
+ n = tokenize(s, f, nelem(f));
+ if(n != Idxfields){
+ print("%d: bad line\n", lineno);
+ bad++;
+ free(s);
+ continue;
+ }
+ digest = hackdigest(f[0]);
+ if(level == 0){
+ if(digest == 0)
+ idprint("%d: no digest\n", lineno);
+ m = mtreefind(mb, digest);
+ }else{
+ m = findmessage(mb, parent, nparts - npart);
+ if(m == 0){
+ // idprint("can't find message\n");
+ }
+ }
+ if(m){
+ /*
+ * read in mutable information.
+ * currently this is only flags
+ */
+ idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
+ redux++;
+ m->flags |= strtoul(f[1], 0, 16);
+ m->cstate &= ~Cidxstale;
+ m->cstate |= Cidx;
+ idprint("→%.2ux\n", m->cstate);
+ free(s);
+
+ if(m->nparts)
+ rdidx(b, mb, m, m->nparts, level + 1);
+ ll = &m->next;
+ continue;
+ }
+ m = newmessage(parent);
+//if(lineno>18&&lineno<25)idprint("%d: %d %d %A\n", lineno, level, m->id, digest);
+// idprint("%d new %d %#A \n", level, m->id, digest);
+ m->digest = digest;
+ m->flags = strtoul(f[1], 0, 16);
+ m->fileid = rdfileid(f[2], level);
+ m->lines = atoi(f[3]);
+ m->ffrom = ∫(f[4]);
+ m->from = ∫(f[5]);
+ m->to = ∫(f[6]);
+ m->cc = ∫(f[7]);
+ m->bcc = ∫(f[8]);
+ m->replyto = ∫(f[9]);
+ m->messageid = ∫(f[10]);
+ m->subject = ∫(f[11]);
+ m->sender = ∫(f[12]);
+ m->inreplyto = ∫(f[13]);
+// m->type = newrefs(f[14]);
+ m->disposition = atoi(f[15]);
+ m->size = strtoul(f[16], 0, 0);
+ m->rawbsize = strtoul(f[17], 0, 0);
+ switch(idxver){
+ case 4:
+ m->nparts = strtoul(f[18], 0, 0);
+ case 7:
+ m->ibadchars = strtoul(f[18], 0, 0);
+ m->idxaux = ∫(f[19]);
+ m->nparts = strtoul(f[20], 0, 0);
+ }
+ m->cstate &= ~Cidxstale;
+ m->cstate |= Cidx;
+ m->str = s;
+// free(s);
+ if(!validmessage(mb, m, level)){
+ /*
+ * if this was an okay message, and somebody
+ * wrote garbage to the index file, we lose the msg.
+ */
+ print("%d: !validmessage\n", lineno);
+ bad++;
+ unnewmessage(mb, parent, m);
+ continue;
+ }
+ if(level == 0)
+ m->inmbox = 1;
+// cachehash(mb, m); /* hokey */
+ l = *ll;
+ *ll = m;
+ ll = &m->next;
+ *ll = l;
+ good++;
+
+ if(m->nparts){
+// fprint(2, "%d: %d parts [%s]\n", lineno, m->nparts, f[18]);
+ rdidx(b, mb, m, m->nparts, level + 1);
+ }
+ }
+ if(level == 0)
+ print("idx: %d %d %d\n", good, bad, redux);
+ return 0;
+}
+
+static int
+verscmp(Biobuf *b)
+{
+ char *s;
+ int i;
+
+ if((s = brdstr(b, '\n', 0)) == 0)
+ return -1;
+ for(i = 0; i < Maxver; i++)
+ if(magictab[i])
+ if(strcmp(s, magictab[i]) == 0)
+ break;
+ free(s);
+ if(i == Maxver)
+ return -1;
+ idxver = i;
+ magic = magictab[i];
+ Idxfields = fieldstab[i];
+ fprint(2, "version %d\n", i);
+ return 0;
+}
+
+int
+mbvers(Biobuf *b)
+{
+ char *s;
+
+ if(s = brdstr(b, '\n', 1)){
+ free(s);
+ return 0;
+ }
+ return -1;
+}
+
+int
+ckidxfile(Mailbox *mb)
+{
+ char buf[Pathlen + 4];
+ int r;
+ Biobuf *b;
+
+ snprint(buf, sizeof buf, "%s", mb->path);
+ b = Bopen(buf, OREAD);
+ if(b == nil)
+ return -1;
+ if(verscmp(b) == -1)
+ return -1;
+ if(idxver >= 7)
+ mbvers(b);
+ r = rdidx(b, mb, mb->root, -1, 0);
+ Bterm(b);
+ return r;
+}
+
+static char *bargv[] = {"/fd/0", 0};
+
+void
+main(int argc, char **argv)
+{
+ Mailbox *mb;
+
+ fmtinstall('A', Afmt);
+ fmtinstall('D', Dfmt);
+ ARGBEGIN{
+ }ARGEND
+ if(*argv == 0)
+ argv = bargv;
+ for(; *argv; argv++){
+ mb = shellmailbox(*argv);
+ lineno = 0;
+ if(ckidxfile(mb) == -1)
+ fprint(2, "%s: %r\n", *argv);
+ shellmailboxfree(mb);
+ }
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/dat.h b/sys/src/cmd/upas/fs/dat.h
index d21de3ed5..585af992a 100644
--- a/sys/src/cmd/upas/fs/dat.h
+++ b/sys/src/cmd/upas/fs/dat.h
@@ -1,191 +1,313 @@
-typedef struct Message Message;
-struct Message
-{
- int id;
- int refs;
- int subname;
- char name[Elemlen];
+#include <avl.h>
- // pointers into message
- char *start; // start of message
- char *end; // end of message
- char *header; // start of header
- char *hend; // end of header
- int hlen; // length of header minus ignored fields
- char *mheader; // start of mime header
- char *mhend; // end of mime header
- char *body; // start of body
- char *bend; // end of body
- char *rbody; // raw (unprocessed) body
- char *rbend; // end of raw (unprocessed) body
- char *lim;
- char deleted;
- char inmbox;
- char mallocd; // message is malloc'd
- char ballocd; // body is malloc'd
- char hallocd; // header is malloce'd
-
- // mail info
- String *unixheader;
- String *unixfrom;
- String *unixdate;
- String *from822;
- String *sender822;
- String *to822;
- String *bcc822;
- String *cc822;
- String *replyto822;
- String *date822;
- String *inreplyto822;
- String *subject822;
- String *messageid822;
- String *addrs;
- String *mimeversion;
- String *sdigest;
-
- // mime info
- String *boundary;
- String *type;
- int encoding;
- int disposition;
- String *charset;
- String *filename;
- int converted;
- int decoded;
- char lines[10]; // number of lines in rawbody
-
- Message *next; // same level
- Message *part; // down a level
- Message *whole; // up a level
-
- uchar digest[SHA1dlen];
-
- vlong imapuid; // used by imap4
-
- char uidl[80]; // used by pop3
- int mesgno;
-};
+enum {
+ /* cache states */
+ Cidx = 1<<0,
+ Cidxstale = 1<<1,
+ Cheader = 1<<2,
+ Cbody = 1<<3,
-enum
-{
- // encodings
+ /* encodings */
Enone= 0,
Ebase64,
Equoted,
- // disposition possibilities
+ /* dispositions */
Dnone= 0,
Dinline,
Dfile,
Dignore,
- PAD64= '=',
+ /* mb create flags */
+ DMcreate = 0x02000000,
+
+ /* rm flags */
+ Rrecur = 1<<0,
+ Rtrunc = 1<<1,
+
+ /* m->deleted flags */
+ Deleted = 1<<0,
+ Dup = 1<<1,
+ Dead = 1<<2,
+ Disappear = 1<<3,
+ Dmark = 1<<4, /* temporary mark for idx scan */
+
+ /* mime flags */
+ Mtrunc = 1<<0, /* message had no boundary */
+
+ Maxmsg = 75*1024*1024, /* maxmessage size; debugging */
+ Maxcache = 512*1024, /* target cache size; set low for debugging */
+ Nctab = 15, /* max # of cached messages >10 */
+ Nref = 10,
+};
+
+typedef struct Idx Idx;
+struct Idx {
+ char *str; /* as read from idx file */
+ uchar *digest;
+ uchar flags;
+ uvlong fileid;
+ ulong lines;
+ ulong size;
+ ulong rawbsize; /* nasty imap4d */
+ ulong ibadchars;
+
+ char *ffrom;
+ char *from;
+ char *to;
+ char *cc;
+ char *bcc;
+ char *replyto;
+ char *messageid;
+ char *subject;
+ char *sender;
+ char *inreplyto;
+ char *idxaux; /* mailbox specific */
+
+ int type; /* very few types: refstring */
+ int disposition; /* very few types: refstring */
+ int nparts;
+};
+
+typedef struct Message Message;
+struct Message {
+ int id;
+ int refs;
+ int subname;
+ char name[12];
+
+ /* top-level indexed information */
+ Idx;
+
+ /* caching help */
+ uchar cstate;
+ ulong infolen;
+ ulong csize;
+
+ /*
+ * a plethoria of pointers into message
+ * and some status. not valid unless cached
+ */
+ char *start; /* start of message */
+ char *end; /* end of message */
+ char *header; /* start of header */
+ char *hend; /* end of header */
+ int hlen; /* length of header minus ignored fields */
+ char *mheader; /* start of mime header */
+ char *mhend; /* end of mime header */
+ char *body; /* start of body */
+ char *bend; /* end of body */
+ char *rbody; /* raw (unprocessed) body */
+ char *rbend; /* end of raw (unprocessed) body */
+ char mallocd; /* message is malloc'd */
+ char ballocd; /* body is malloc'd */
+ char hallocd; /* header is malloc'd */
+ int badchars; /* running count of bad chars. */
+
+ char deleted;
+ char inmbox;
+
+ /* mail info */
+ char *unixheader;
+ char *unixfrom;
+ char *date822;
+ char *references[Nref];
+
+ /* mime info */
+ char *boundary;
+ int charset;
+ char *filename;
+ char encoding;
+ char converted;
+ char decoded;
+ char mimeflag;
+
+ Message *next;
+ Message *part;
+ Message *whole;
+
+ union{
+ char *lim; /* used by plan9; not compatable with cache */
+ vlong imapuid; /* used by imap4 */
+ void *aux;
+ };
+};
+
+typedef struct {
+ Avl;
+ Message *m;
+} Mtree;
+
+typedef struct Mcache Mcache;
+struct Mcache {
+ uvlong cached;
+ int ntab;
+ Message *ctab[Nctab];
};
typedef struct Mailbox Mailbox;
-struct Mailbox
-{
+struct Mailbox {
QLock;
+ long idxsem; /* abort on concurrent index access */
+ long syncsem; /* abort on concurrent syncs */
int refs;
Mailbox *next;
int id;
- int dolock; // lock when syncing?
- int std;
+ int flags;
+ char rmflags;
+ char dolock; /* lock when syncing? */
+ char addfrom;
char name[Elemlen];
char path[Pathlen];
Dir *d;
Message *root;
- int vers; // goes up each time mailbox is read
+ Avltree *mtree;
+ ulong vers; /* goes up each time mailbox is changed */
- ulong waketime;
- char *(*sync)(Mailbox*, int);
+ /* cache tracking */
+ Mcache;
+
+ /* index tracking */
+ Qid qid;
+
+ ulong waketime;
void (*close)(Mailbox*);
- char *(*fetch)(Mailbox*, Message*);
+ void (*decache)(Mailbox*, Message*);
+ int (*fetch)(Mailbox*, Message*, uvlong, ulong);
+ void (*delete)(Mailbox*, Message*);
char *(*ctl)(Mailbox*, int, char**);
- void *aux; // private to Mailbox implementation
+ char *(*remove)(Mailbox *, int);
+ char *(*rename)(Mailbox*, char*, int);
+ char *(*sync)(Mailbox*, int, int*);
+ void (*modflags)(Mailbox*, Message*, int);
+ void (*idxwrite)(Biobuf*, Mailbox*);
+ int (*idxread)(char*, Mailbox*);
+ void (*idxinvalid)(Mailbox*);
+ void *aux; /* private to Mailbox implementation */
};
+/* print argument tango; can't varargck 2 types. should fix compiler */
+typedef struct Mpair Mpair;
+struct Mpair {
+ Mailbox *mb;
+ Message *m;
+};
+Mpair mpair(Mailbox*, Message*);
+
typedef char *Mailboxinit(Mailbox*, char*);
-extern Message *root;
-extern Mailboxinit plan9mbox;
-extern Mailboxinit pop3mbox;
-extern Mailboxinit imap4mbox;
-extern Mailboxinit planbmbox;
-extern Mailboxinit planbvmbox;
+Mailboxinit plan9mbox;
+Mailboxinit planbmbox;
+Mailboxinit pop3mbox;
+Mailboxinit imap4mbox;
+Mailboxinit mdirmbox;
+
+void genericidxwrite(Biobuf*, Mailbox*);
+int genericidxread(char*, Mailbox*);
+void genericidxinvalid(Mailbox*);
+
+void cachehash(Mailbox*, Message*);
+void newcachehash(Mailbox*, Message*, int);
+int cacheheaders(Mailbox*, Message*); /* "getcache" */
+int cachebody(Mailbox*, Message*);
+int cacheidx(Mailbox*, Message*);
+int insurecache(Mailbox*, Message*);
+/**/
+void putcache(Mailbox*, Message*); /* asymmetricial */
+long cachefree(Mailbox*, Message*, int);
+
+Message* gettopmsg(Mailbox*, Message*);
char* syncmbox(Mailbox*, int);
-char* geterrstr(void);
void* emalloc(ulong);
void* erealloc(void*, ulong);
Message* newmessage(Message*);
+void unnewmessage(Mailbox*, Message*, Message*);
void delmessage(Mailbox*, Message*);
-void delmessages(int, char**);
+char* delmessages(int, char**);
+char *flagmessages(int, char**);
+void digestmessage(Mailbox*, Message*);
+
+uintptr absbos(void);
+void eprint(char*, ...);
+void iprint(char *, ...);
int newid(void);
void mailplumb(Mailbox*, Message*, int);
-char* newmbox(char*, char*, int);
+char* newmbox(char*, char*, int, Mailbox**);
void freembox(char*);
-void logmsg(char*, Message*);
+char* removembox(char*, int);
+void syncallmboxes(void);
+void logmsg(Message*, char*, ...);
void msgincref(Message*);
void msgdecref(Mailbox*, Message*);
void mboxincref(Mailbox*);
void mboxdecref(Mailbox*);
+char *mboxrename(char*, char*, int);
void convert(Message*);
void decode(Message*);
-int cistrncmp(char*, char*, int);
-int cistrcmp(char*, char*);
int decquoted(char*, char*, char*, int);
int xtoutf(char*, char**, char*, char*);
-void countlines(Message*);
-int headerlen(Message*);
-void parse(Message*, int, Mailbox*, int);
-void parseheaders(Message*, int, Mailbox*, int);
+ulong countlines(Message*);
+void parse(Mailbox*, Message*, int, int);
+void parseheaders(Mailbox*, Message*, int, int);
void parsebody(Message*, Mailbox*);
-void parseunix(Message*);
-String* date822tounix(char*);
+char* date822tounix(Message*, char*);
int fidmboxrefs(Mailbox*);
int hashmboxrefs(Mailbox*);
+void checkmboxrefs(void);
+int strtotm(char*, Tm*);
+char* lowercase(char*);
-extern int debug;
-extern int fflag;
-extern int logging;
-extern char user[Elemlen];
-extern char stdmbox[Pathlen];
-extern QLock mbllock;
-extern Mailbox *mbl;
-extern char *mntpt;
-extern int biffing;
-extern int plumbing;
-extern char* Enotme;
+char* sputc(char*, char*, int);
+char* seappend(char*, char*, char*, int);
-enum
-{
- /* mail subobjects */
- Qbody,
+int hdrlen(char*, char*);
+char* rfc2047(char*, char*, char*, int, int);
+
+char* localremove(Mailbox*, int);
+char* localrename(Mailbox*, char*, int);
+void rmidx(char*, int);
+int vremove(char*);
+int rename(char *, char*, int);
+
+int mtreecmp(Avl*, Avl*);
+int mtreeisdup(Mailbox *, Message *);
+Message* mtreefind(Mailbox*, uchar*);
+void mtreeadd(Mailbox*, Message*);
+void mtreedelete(Mailbox*, Message*);
+
+enum {
+ /* mail sub-objects; must be sorted */
Qbcc,
+ Qbody,
Qcc,
Qdate,
Qdigest,
Qdisposition,
+ Qffrom,
+ Qfileid,
Qfilename,
+ Qflags,
Qfrom,
Qheader,
+ Qinfo,
Qinreplyto,
Qlines,
- Qmimeheader,
Qmessageid,
+ Qmimeheader,
Qraw,
Qrawbody,
Qrawheader,
Qrawunix,
+ Qreferences,
Qreplyto,
Qsender,
+ Qsize,
Qsubject,
Qto,
Qtype,
- Qunixheader,
- Qinfo,
Qunixdate,
+ Qunixheader,
Qmax,
/* other files */
@@ -196,12 +318,10 @@ enum
Qmboxctl,
};
-#define PATH(id, f) ((((id)&0xfffff)<<10) | (f))
+#define PATH(id, f) ((((id) & 0xfffff)<<10) | (f))
#define FILE(p) ((p) & 0x3ff)
-char *dirtab[];
-
-// hash table to aid in name lookup, all files have an entry
+/* hash table to aid in name lookup, all files have an entry */
typedef struct Hash Hash;
struct Hash {
Hash *next;
@@ -216,5 +336,50 @@ Hash *hlook(ulong, char*);
void henter(ulong, char*, Qid, Message*, Mailbox*);
void hfree(ulong, char*);
-ulong msgallocd, msgfreed;
+typedef struct {
+ char *s;
+ int l;
+ ulong ref;
+} Refs;
+
+int newrefs(char*);
+void delrefs(int);
+void refsinit(void);
+int prrefs(Biobuf*);
+int rdrefs(Biobuf*);
+
+void idxfree(Idx*);
+int rdidxfile(Mailbox*, int);
+int wridxfile(Mailbox*);
+
+char *modflags(Mailbox*, Message*, char*);
+int getmtokens(char *, char**, int, int);
+
+extern char Enotme[];
+extern char *mntpt;
+extern char user[Elemlen];
+extern char *dirtab[];
+extern int Sflag;
+extern int iflag;
+extern int biffing;
+extern ulong cachetarg;
+extern int debug;
+extern int lflag;
+extern int plumbing;
+extern ulong msgallocd;
+extern ulong msgfreed;
+extern Mailbox *mbl;
+extern Message *root;
+extern QLock mbllock;
+extern Refs *rtab;
+
+#define dprint(...) if(debug) fprint(2, __VA_ARGS__); else {}
+#define Topmsg(mb, m) (m->whole == mb->root)
+#pragma varargck type "A" uchar*
+#pragma varargck type "D" uvlong
+#pragma varargck type "P" Mpair
+#pragma varargck type "Δ" uvlong
+#pragma varargck argpos eprint 1
+#pragma varargck argpos iprint 1
+#pragma varargck argpos logmsg 2
diff --git a/sys/src/cmd/upas/fs/extra/fd2path b/sys/src/cmd/upas/fs/extra/fd2path
new file mode 100755
index 000000000..d73815007
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/fd2path
Binary files differ
diff --git a/sys/src/cmd/upas/fs/extra/fd2path.c b/sys/src/cmd/upas/fs/extra/fd2path.c
new file mode 100644
index 000000000..5fe3ce708
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/fd2path.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+
+void
+usage(void)
+{
+ fprint(2, "usage: fd2path path ...\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char buf[1024];
+ int fd;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0){
+ if(fd2path(0, buf, sizeof buf) != -1)
+ fprint(2, "%s\n", buf);
+ }else for(; *argv; argv++){
+ fd = open(*argv, OREAD);
+ if(fd != -1 && fd2path(fd, buf, sizeof buf) != -1)
+ fprint(2, "%s\n", buf);
+ close(fd);
+ }
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/extra/idxtst.c b/sys/src/cmd/upas/fs/extra/idxtst.c
new file mode 100644
index 000000000..919d25476
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/idxtst.c
@@ -0,0 +1,58 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+static char *etab[] = {
+ "not found",
+ "does not exist",
+ "file is locked",
+ "exclusive lock",
+};
+
+static int
+bad(int idx)
+{
+ char buf[ERRMAX];
+ int i;
+
+ rerrstr(buf, sizeof buf);
+ for(i = idx; i < nelem(etab); i++)
+ if(strstr(buf, etab[i]))
+ return 0;
+ return 1;
+}
+
+static int
+exopen(char *s)
+{
+ int i, fd;
+
+ for(i = 0; i < 30; i++){
+ if((fd = open(s, OWRITE|OTRUNC)) >= 0 || bad(0))
+ return fd;
+ if((fd = create(s, OWRITE|OEXCL, DMEXCL|0600)) >= 0 || bad(2))
+ return fd;
+ sleep(1000);
+ }
+ werrstr("lock timeout");
+ return -1;
+}
+
+void
+main(void)
+{
+ int fd;
+ Biobuf *b;
+
+ fd = exopen("testingex");
+ if(fd == -1)
+ sysfatal("exopen: %r");
+ b = Bopen("testingex", OREAD);
+ if(b){
+ free(b);
+ fprint(2, "print both opened at once\n");
+ }else
+ fprint(2, "bopen: %r\n");
+ close(fd);
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/extra/infotst.c b/sys/src/cmd/upas/fs/extra/infotst.c
new file mode 100644
index 000000000..de7537a94
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/infotst.c
@@ -0,0 +1,89 @@
+/*
+ * simulate the read patterns of external programs for testing
+ * info file. "infotest 511 512" simulates what ned does today.
+ *
+ * here's how the new info scheme was verified:
+ *
+ ramfs
+ s=/sys/src/cmd/upas
+ unmount /mail/fs
+ $s/fs/8.out -p
+ for(f in /mail/fs/mbox/*/info){
+ for(i in `{seq 1 1026})
+ $s/fs/infotst $i `{echo $i + 1 | hoc} > /tmp/$i < $f
+ for(i in /tmp/*)
+ cmp $i /tmp/1
+ rm /tmp/*
+ }
+
+ # now test for differences with old scheme under
+ # ideal reading conditions
+ for(f in /mail/fs/mbox/*/info){
+ i = `{echo $f | sed 's:/mail/fs/mbox/([^/]+)/info:\1:g'}
+ $s/fs/infotst 2048 > /tmp/$i < $f
+ }
+ unmount /mail/fs
+ upas/fs -p
+ for(f in /mail/fs/mbox/*/info){
+ i = `{echo $f | sed 's:/mail/fs/mbox/([^/]+)/info:\1:g'}
+ $s/fs/infotst 2048 > /tmp/$i.o < $f
+ }
+ for(i in /tmp/*.o)
+ cmp $i `{echo $i | sed 's:\.o$::g'}
+ rm /tmp/*
+ */
+#include <u.h>
+#include <libc.h>
+
+enum{
+ Ntab = 100,
+};
+
+int tab[Ntab];
+int ntab;
+int largest;
+
+void
+usage(void)
+{
+ fprint(2, "usage: infotest n1 n2 ... nm\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *buf;
+ int i, n;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+ if(argc == 0)
+ usage();
+ for(; *argv; argv++){
+ if(ntab == nelem(tab))
+ break;
+ i = atoi(*argv);
+ if(i > largest)
+ largest = i;
+ tab[ntab++] = i;
+ }
+ buf = malloc(largest);
+ if(!buf)
+ sysfatal("malloc: %r");
+ for(i = 0;; ){
+ switch(n = read(0, buf, tab[i])){
+ case -1:
+ sysfatal("read: %r");
+ case 0:
+ exits("");
+ default:
+ write(1, buf, n);
+ break;
+ }
+ if(i < ntab-1)
+ i++;
+ }
+}
diff --git a/sys/src/cmd/upas/fs/extra/paw b/sys/src/cmd/upas/fs/extra/paw
new file mode 100755
index 000000000..538f648f3
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/paw
Binary files differ
diff --git a/sys/src/cmd/upas/fs/extra/paw.c b/sys/src/cmd/upas/fs/extra/paw.c
new file mode 100644
index 000000000..6d3a5e484
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/paw.c
@@ -0,0 +1,23 @@
+#include<u.h>
+#include<libc.h>
+#include<bio.h>
+
+void
+main(void)
+{
+ char *f[10], *s;
+ vlong sum;
+ Biobuf b;
+
+ sum = 0;
+ Binit(&b, 0, OREAD);
+
+ while(s = Brdstr(&b, '\n', 1)){
+ if(getfields(s, f, nelem(f), 1, " ") > 2)
+ sum += strtoul(f[2], 0, 0);
+ free(s);
+ }
+ Bterm(&b);
+ print("%lld\n", sum);
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/extra/prflags.c b/sys/src/cmd/upas/fs/extra/prflags.c
new file mode 100644
index 000000000..6a42045d1
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/prflags.c
@@ -0,0 +1,37 @@
+#include "common.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: prflags\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *f[Fields+1], buf[20], *s;
+ int n;
+ Biobuf b, o;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+ if(argc)
+ usage();
+ Binit(&b, 0, OREAD);
+ Binit(&o, 1, OWRITE);
+
+ for(; s = Brdstr(&b, '\n', 1); free(s)){
+ n = gettokens(s, f, nelem(f), " ");
+ if(n != Fields)
+ continue;
+ if(!strcmp(f[0], "-"))
+ continue;
+ Bprint(&o, "%s\n", flagbuf(buf, strtoul(f[1], 0, 16)));
+ }
+ Bterm(&b);
+ Bterm(&o);
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/extra/strtotmtst.c b/sys/src/cmd/upas/fs/extra/strtotmtst.c
new file mode 100644
index 000000000..074e10e2c
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/strtotmtst.c
@@ -0,0 +1,17 @@
+#include "strtotm.c"
+
+void
+main(int argc, char **argv)
+{
+ Tm tm;
+
+ ARGBEGIN{
+ }ARGEND
+
+ for(; *argv; argv++)
+ if(strtotm(*argv, &tm) >= 0)
+ print("%s", asctime(&tm));
+ else
+ print("bad\n");
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/extra/tokens.c b/sys/src/cmd/upas/fs/extra/tokens.c
new file mode 100644
index 000000000..6192f2b93
--- /dev/null
+++ b/sys/src/cmd/upas/fs/extra/tokens.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+
+/* unfortunately, tokenize insists on collapsing multiple seperators */
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s, char *sep)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+int
+getmtokens(char *s, char **args, int maxargs, int multiflag)
+{
+ int i;
+
+ for(i = 0; i < maxargs; i++){
+ if(multiflag)
+ while(*s && utfrune(qsep, *s))
+ s++;
+ else if(*s && utfrune(qsep, *s))
+ s++;
+ if(*s == 0)
+ break;
+ args[i] = s;
+ s = qtoken(s, qsep);
+ }
+ return i;
+}
diff --git a/sys/src/cmd/upas/fs/fs.c b/sys/src/cmd/upas/fs/fs.c
index fffb290aa..acfca8838 100644
--- a/sys/src/cmd/upas/fs/fs.c
+++ b/sys/src/cmd/upas/fs/fs.c
@@ -1,17 +1,10 @@
#include "common.h"
-#include <auth.h>
#include <fcall.h>
#include <libsec.h>
-#include <ctype.h>
+#include <pool.h>
#include "dat.h"
-enum
-{
- OPERM = 0x3, // mask of all permission types in open mode
-};
-
typedef struct Fid Fid;
-
struct Fid
{
Qid qid;
@@ -21,31 +14,13 @@ struct Fid
Fid *next;
Mailbox *mb;
Message *m;
- Message *mtop; // top level message
+ Message *mtop; /* top level message */
- //finger pointers to speed up reads of large directories
- long foff; // offset/DIRLEN of finger
- Message *fptr; // pointer to message at off
- int fvers; // mailbox version when finger was saved
+ long foff; /* offset/DIRLEN of finger */
+ Message *fptr; /* pointer to message at off */
+ int fvers; /* mailbox version when finger was saved */
};
-ulong path; // incremented for each new file
-Fid *fids;
-int mfd[2];
-char user[Elemlen];
-int messagesize = 4*1024+IOHDRSZ;
-uchar mdata[8*1024+IOHDRSZ];
-uchar mbuf[8*1024+IOHDRSZ];
-Fcall thdr;
-Fcall rhdr;
-int fflg;
-char *mntpt;
-int biffing;
-int plumbing = 1;
-
-QLock mbllock;
-Mailbox *mbl;
-
Fid *newfid(int);
void error(char*);
void io(void);
@@ -54,9 +29,6 @@ void *emalloc(ulong);
void usage(void);
void reader(void);
int readheader(Message*, char*, int, int);
-int cistrncmp(char*, char*, int);
-int tokenconvert(String*, char*, int);
-String* stringconvert(String*, char*, int);
void post(char*, char*, int);
char *rflush(Fid*), *rauth(Fid*),
@@ -93,30 +65,36 @@ char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
char Ebadctl[] = "unknown control message";
+char Ebadargs[] = "invalid arguments";
+char Enotme[] = "path not served by this file server";
-char *dirtab[] =
-{
+char *dirtab[] = {
[Qdir] ".",
-[Qbody] "body",
[Qbcc] "bcc",
+[Qbody] "body",
[Qcc] "cc",
[Qdate] "date",
[Qdigest] "digest",
[Qdisposition] "disposition",
+[Qffrom] "ffrom",
+[Qfileid] "fileid",
[Qfilename] "filename",
+[Qflags] "flags",
[Qfrom] "from",
[Qheader] "header",
[Qinfo] "info",
[Qinreplyto] "inreplyto",
-[Qlines] "lines",
-[Qmimeheader] "mimeheader",
+[Qlines] "lines",
[Qmessageid] "messageid",
+[Qmimeheader] "mimeheader",
[Qraw] "raw",
-[Qrawunix] "rawunix",
[Qrawbody] "rawbody",
[Qrawheader] "rawheader",
+[Qrawunix] "rawunix",
+[Qreferences] "references",
[Qreplyto] "replyto",
[Qsender] "sender",
+[Qsize] "size",
[Qsubject] "subject",
[Qto] "to",
[Qtype] "type",
@@ -128,61 +106,290 @@ char *dirtab[] =
enum
{
- Hsize= 1277,
+ Hsize= 1999,
};
-Hash *htab[Hsize];
+char *mntpt;
+char user[Elemlen];
+int Dflag;
+int Sflag;
+int iflag;
+int lflag;
+int biffing;
int debug;
-int fflag;
-int logging;
+int plumbing = 1;
+ulong cachetarg = Maxcache;
+Mailbox *mbl;
+QLock mbllock;
+
+static int messagesize = 8*1024 + IOHDRSZ;
+static int mfd[2];
+static char hbuf[32*1024];
+static uchar mbuf[16*1024 + IOHDRSZ];
+static uchar mdata[16*1024 + IOHDRSZ];
+static ulong path; /* incremented for each new file */
+static Hash *htab[Hsize];
+static Fcall rhdr;
+static Fcall thdr;
+static Fid *fids;
+static uintptr bos = 0xd0000000; /* pc kernel specific */
+
+#define onstack(x) ((uintptr)(x) >= bos)
+#define intext(x) ((char*)(x) <= end)
+#define validgptr(x) assert(!onstack(x) && !intext(x))
+void
+sanemsg(Message *m)
+{
+ if(bos == 0)
+ bos = absbos();
+ assert(m->refs < 100);
+ validgptr(m->whole);
+ if(debug)
+ poolcheck(mainmem);
+ validgptr(m);
+ assert(m->next != m);
+ if(m->end < m->start)
+ abort();
+ if(m->ballocd && (m->start <= m->body && m->end >= m->body))
+ abort();
+ if(m->end - m->start > Maxmsg)
+ abort();
+ if(m->size > Maxmsg)
+ abort();
+ if(m->fileid != 0 && m->fileid <= 1000000ull<<8)
+ abort();
+}
+
+void
+sanembmsg(Mailbox *mb, Message *m)
+{
+ sanemsg(m);
+ if(Topmsg(mb, m)){
+ if(m->start > end && m->size == 0)
+ abort();
+ if(m->fileid <= 1000000ull<<8)
+ abort();
+ }
+}
+
+static void
+sanefid(Fid *f)
+{
+ if(f->m == 0)
+ return;
+ if(f->mtop){
+ sanemsg(f->mtop);
+ assert(f->mtop->refs > 0);
+ }
+ sanemsg(f->m);
+ if(f->m)
+ if(Topmsg(f->mb, f->m))
+ assert(f->m->refs > 0);
+}
+
+void
+sanefids(void)
+{
+ Fid *f;
+
+ for(f = fids; f; f = f->next)
+ if(f->busy)
+ sanefid(f);
+}
+
+static int
+Afmt(Fmt *f)
+{
+ char buf[SHA1dlen*2 + 1];
+ uchar *u, i;
+
+ u = va_arg(f->args, uchar*);
+ if(u == 0 && f->flags & FmtSharp)
+ return fmtstrcpy(f, "-");
+ if(u == 0)
+ return fmtstrcpy(f, "<nildigest>");
+ for(i = 0; i < SHA1dlen; i++)
+ sprint(buf + 2*i, "%2.2ux", u[i]);
+ return fmtstrcpy(f, buf);
+}
+
+static int
+Δfmt(Fmt *f)
+{
+ char buf[32];
+ uvlong v;
+
+ v = va_arg(f->args, uvlong);
+ if(f->flags & FmtSharp)
+ if((v>>8) == 0)
+ return fmtstrcpy(f, "");
+ strcpy(buf, ctime(v>>8));
+ buf[28] = 0;
+ return fmtstrcpy(f, buf);
+}
+
+static int
+Dfmt(Fmt *f)
+{
+ char buf[32];
+ int seq;
+ uvlong v;
+
+ v = va_arg(f->args, uvlong);
+ seq = v & 0xff;
+ if(seq > 99)
+ seq = 99;
+ snprint(buf, sizeof buf, "%llud.%.2d", v>>8, seq);
+ return fmtstrcpy(f, buf);
+}
+
+Mpair
+mpair(Mailbox *mb, Message *m)
+{
+ Mpair mp;
+
+ mp.mb = mb;
+ mp.m = m;
+ return mp;
+}
+
+static int
+Pfmt(Fmt *f)
+{
+ char buf[128], *p, *e;
+ int i, dots;
+ Mailbox *mb;
+ Message *t[32], *m;
+ Mpair mp;
+
+ mp = va_arg(f->args, Mpair);
+ mb = mp.mb;
+ m = mp.m;
+ if(m == nil || mb == nil)
+ return fmtstrcpy(f, "<P nil>");
+ i = 0;
+ for(; !Topmsg(mb, m); m = m->whole){
+ t[i++] = m;
+ if(i == nelem(t)-1)
+ break;
+ }
+ t[i++] = m;
+ dots = 0;
+ if(i == nelem(t))
+ dots = 1;
+ e = buf + sizeof buf;
+ p = buf;
+ if(dots)
+ p = seprint(p, e, ".../");
+ while(--i >= 1)
+ p = seprint(p, e, "%s/", t[i]->name);
+ if(i == 0)
+ seprint(p, e, "%s", t[0]->name);
+ return fmtstrcpy(f, buf);
+}
void
usage(void)
{
- fprint(2, "usage: upas/fs [-bdlnps] [-f mboxfile] [-m mountpoint]\n");
+ fprint(2, "usage: upas/fs [-DSbdlmnps] [-c cachetarg] [-f mboxfile] [-m mountpoint]\n");
exits("usage");
}
void
notifyf(void *, char *s)
{
- if(strstr(s, "alarm") || strstr(s, "interrupt"))
+ if(strncmp(s, "interrupt", 9) == 0)
noted(NCONT);
+ if(strncmp(s, "die: yankee pig dog", 19) != 0)
+ /* don't want to call syslog from notify handler */
+ fprint(2, "upas/fs: user: %s; note: %s\n", getuser(), s);
noted(NDFLT);
}
void
+setname(char **v)
+{
+ char buf[128], buf2[32], *p, *e;
+ int fd, i;
+
+ e = buf + sizeof buf;
+ p = seprint(buf, e, "%s", v[0]);
+ for(i = 0; v[++i]; )
+ p = seprint(p, e, " %s", v[i]);
+ snprint(buf2, sizeof buf2, "#p/%d/args", getpid());
+ if((fd = open(buf2, OWRITE)) >= 0){
+ write(fd, buf, p - buf);
+ close(fd);
+ }
+}
+
+ulong
+ntoi(char *s)
+{
+ ulong n;
+
+ n = strtoul(s, &s, 0);
+ for(;;)
+ switch(*s++){
+ default:
+ usage();
+ case 'g':
+ n *= 1024;
+ case 'm':
+ n *= 1024;
+ case 'k':
+ n *= 1024;
+ break;
+ case 0:
+ return n;
+ }
+}
+
+void
main(int argc, char *argv[])
{
- int p[2], std, nodflt;
- char maildir[128];
- char mbox[128];
+ char maildir[Pathlen], mbox[Pathlen], srvfile[64], **v;
char *mboxfile, *err;
- char srvfile[64];
- int srvpost;
+ int p[2], nodflt, srvpost;
rfork(RFNOTEG);
- mntpt = nil;
- fflag = 0;
mboxfile = nil;
- std = 0;
nodflt = 0;
srvpost = 0;
+ v = argv;
ARGBEGIN{
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'S':
+ Sflag = 1;
+ break;
case 'b':
biffing = 1;
break;
+ case 'c':
+ cachetarg = ntoi(EARGF(usage()));
+ break;
+ case 'd':
+ debug = 1;
+ mainmem->flags |= POOL_PARANOIA;
+ break;
case 'f':
- fflag = 1;
mboxfile = EARGF(usage());
break;
+ case 'i':
+ iflag++;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
case 'm':
mntpt = EARGF(usage());
break;
- case 'd':
- debug = 1;
+ case 'n':
+ nodflt = 1;
break;
case 'p':
plumbing = 0;
@@ -190,18 +397,19 @@ main(int argc, char *argv[])
case 's':
srvpost = 1;
break;
- case 'l':
- logging = 1;
- break;
- case 'n':
- nodflt = 1;
- break;
default:
usage();
}ARGEND
if(argc)
usage();
+ fmtinstall('A', Afmt);
+ fmtinstall('D', Dfmt);
+ fmtinstall(L'Δ', Δfmt);
+ fmtinstall('F', fcallfmt);
+ fmtinstall('H', encodefmt); /* forces tls stuff */
+ fmtinstall('P', Pfmt);
+ quotefmtinstall();
if(pipe(p) < 0)
error("pipe failed");
mfd[0] = p[0];
@@ -214,19 +422,13 @@ main(int argc, char *argv[])
mntpt = maildir;
}
if(mboxfile == nil && !nodflt){
- snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
+ snprint(mbox, sizeof mbox, "/mail/box/%s/mbox", user);
mboxfile = mbox;
- std = 1;
}
- if(debug)
- fmtinstall('F', fcallfmt);
-
- if(mboxfile != nil){
- err = newmbox(mboxfile, "mbox", std);
- if(err != nil)
+ if(mboxfile != nil)
+ if(err = newmbox(mboxfile, "mbox", 0, 0))
sysfatal("opening %s: %s", mboxfile, err);
- }
switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG|RFREND)){
case -1:
@@ -235,46 +437,67 @@ main(int argc, char *argv[])
henter(PATH(0, Qtop), dirtab[Qctl],
(Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
close(p[1]);
+ setname(v);
io();
- postnote(PNGROUP, getpid(), "die yankee pig dog");
+ syncallmboxes();
+ syskillpg(getpid());
break;
default:
close(p[0]); /* don't deadlock if child fails */
if(srvpost){
- sprint(srvfile, "/srv/upasfs.%s", user);
+ snprint(srvfile, sizeof srvfile, "/srv/upasfs.%s", user);
post(srvfile, "upasfs", p[1]);
- } else {
+ }else
if(mount(p[1], -1, mntpt, MREPL, "") < 0)
error("mount failed");
- }
}
- exits(0);
+ exits("");
}
-static int
-fileinfo(Message *m, int t, char **pp)
+char*
+sputc(char *p, char *e, int c)
{
- char *p;
- int len;
+ if(p < e - 1)
+ *p++ = c;
+ return p;
+}
+
+char*
+seappend(char *s, char *e, char *a, int n)
+{
+ int l;
- p = "";
- len = 0;
+ l = e - s - 1;
+ if(l < n)
+ n = l;
+ memcpy(s, a, n);
+ s += n;
+ *s = 0;
+ return s;
+}
+
+static int
+fileinfo(Mailbox *mb, Message *m, int t, char **pp)
+{
+ char *s, *e, *p;
+ int len, i;
+ static char buf[64 + 512];
+
+ cacheidx(mb, m);
+ sanembmsg(mb, m);
+ p = nil;
+ len = -1;
switch(t){
case Qbody:
+ cachebody(mb, m);
p = m->body;
- len = m->bend - m->body;
+ len = m->bend - p;
break;
case Qbcc:
- if(m->bcc822){
- p = s_to_c(m->bcc822);
- len = strlen(p);
- }
+ p = m->bcc;
break;
case Qcc:
- if(m->cc822){
- p = s_to_c(m->cc822);
- len = strlen(p);
- }
+ p = m->cc;
break;
case Qdisposition:
switch(m->disposition){
@@ -285,142 +508,131 @@ fileinfo(Message *m, int t, char **pp)
p = "file";
break;
}
- len = strlen(p);
break;
case Qdate:
- if(m->date822){
- p = s_to_c(m->date822);
- len = strlen(p);
- } else if(m->unixdate != nil){
- p = s_to_c(m->unixdate);
- len = strlen(p);
+ p = m->date822;
+ if(!p){
+ p = buf;
+ len = snprint(buf, sizeof buf, "%#Δ", m->fileid);
}
break;
case Qfilename:
- if(m->filename){
- p = s_to_c(m->filename);
- len = strlen(p);
- }
+ p = m->filename;
+ break;
+ case Qflags:
+ p = flagbuf(buf, m->flags);
break;
case Qinreplyto:
- if(m->inreplyto822){
- p = s_to_c(m->inreplyto822);
- len = strlen(p);
- }
+ p = m->inreplyto;
break;
case Qmessageid:
- if(m->messageid822){
- p = s_to_c(m->messageid822);
- len = strlen(p);
- }
+ p = m->messageid;
break;
case Qfrom:
- if(m->from822){
- p = s_to_c(m->from822);
- len = strlen(p);
- } else if(m->unixfrom != nil){
- p = s_to_c(m->unixfrom);
- len = strlen(p);
- }
+ if(m->from)
+ p = m->from;
+ else
+ p = m->unixfrom;
break;
- case Qheader:
- p = m->header;
- len = headerlen(m);
+ case Qffrom:
+ if(m->ffrom)
+ p = m->ffrom;
break;
case Qlines:
- p = m->lines;
- if(*p == 0)
- countlines(m);
- len = strlen(m->lines);
+ len = snprint(buf, sizeof buf, "%lud", m->lines);
+ p = buf;
break;
case Qraw:
+ cachebody(mb, m);
p = m->start;
- if(strncmp(m->start, "From ", 5) == 0){
- p = strchr(p, '\n');
- if(p == nil)
- p = m->start;
- else
- p++;
- }
- len = m->end - p;
+ if(strncmp(m->start, "From ", 5) == 0)
+ if(e = strchr(p, '\n'))
+ p = e + 1;
+ len = m->rbend - p;
break;
case Qrawunix:
+ cachebody(mb, m);
p = m->start;
len = m->end - p;
break;
case Qrawbody:
+ cachebody(mb, m);
p = m->rbody;
len = m->rbend - p;
break;
case Qrawheader:
+ cacheheaders(mb, m);
p = m->header;
len = m->hend - p;
break;
case Qmimeheader:
+ cacheheaders(mb, m);
p = m->mheader;
len = m->mhend - p;
break;
- case Qreplyto:
- p = nil;
- if(m->replyto822 != nil){
- p = s_to_c(m->replyto822);
- len = strlen(p);
- } else if(m->from822 != nil){
- p = s_to_c(m->from822);
- len = strlen(p);
- } else if(m->sender822 != nil){
- p = s_to_c(m->sender822);
- len = strlen(p);
- } else if(m->unixfrom != nil){
- p = s_to_c(m->unixfrom);
- len = strlen(p);
+ case Qreferences:
+ cacheheaders(mb, m);
+ e = buf + sizeof buf;
+ s = buf;
+ for(i = 0; i < nelem(m->references); i++){
+ if(m->references[i] == 0)
+ break;
+ s = seprint(s, e, "%s\n", m->references[i]);
}
+ *s = 0;
+ p = buf;
+ len = s - buf;
+ break;
+ case Qreplyto:
+ if(m->replyto != nil)
+ p = m->replyto;
+ else if(m->from != nil)
+ p = m->from;
+ else if(m->sender != nil)
+ p = m->sender;
+ else if(m->unixfrom != nil)
+ p = m->unixfrom;
break;
case Qsender:
- if(m->sender822){
- p = s_to_c(m->sender822);
- len = strlen(p);
- }
+ p = m->sender;
break;
case Qsubject:
- p = nil;
- if(m->subject822){
- p = s_to_c(m->subject822);
- len = strlen(p);
- }
+ p = m->subject;
+ break;
+ case Qsize:
+ len = snprint(buf, sizeof buf, "%lud", m->size);
+ p = buf;
break;
case Qto:
- if(m->to822){
- p = s_to_c(m->to822);
- len = strlen(p);
- }
+ p = m->to;
break;
case Qtype:
- if(m->type){
- p = s_to_c(m->type);
- len = strlen(p);
- }
+ p = rtab[m->type].s;
+ len = rtab[m->type].l;
break;
case Qunixdate:
- if(m->unixdate){
- p = s_to_c(m->unixdate);
- len = strlen(p);
- }
+ p = buf;
+ len = snprint(buf, sizeof buf, "%#Δ", m->fileid);
+ break;
+ case Qfileid:
+ p = buf;
+ len = snprint(buf, sizeof buf, "%D", m->fileid);
break;
case Qunixheader:
- if(m->unixheader){
- p = s_to_c(m->unixheader);
- len = s_len(m->unixheader);
- }
+ cacheheaders(mb, m);
+ p = m->unixheader;
break;
case Qdigest:
- if(m->sdigest){
- p = s_to_c(m->sdigest);
- len = strlen(p);
- }
+ p = buf;
+ len = snprint(buf, sizeof buf, "%A", m->digest);
break;
}
+ if(p == nil)
+ p = "";
+ if(len == -1)
+ len = strlen(p);
*pp = p;
+ putcache(mb, m);
return len;
}
@@ -441,47 +653,53 @@ int infofields[] = {
Qsender,
Qmessageid,
Qlines,
- -1,
+ Qsize,
+ Qflags,
+ Qfileid,
+ Qffrom,
};
-static int
-readinfo(Message *m, char *buf, long off, int count)
+int
+readinfo(Mailbox *mb, Message *m, char *buf, long off, int count)
{
- char *p;
- int len, i, n;
- String *s;
-
- s = s_new();
- len = 0;
- for(i = 0; len < count && infofields[i] >= 0; i++){
- n = fileinfo(m, infofields[i], &p);
- s = stringconvert(s, p, n);
- s_append(s, "\n");
- p = s_to_c(s);
- n = strlen(p);
- if(off > 0){
- if(off >= n){
- off -= n;
- continue;
- }
- p += off;
+ char *s, *p, *e;
+ int i, n;
+ long off0;
+
+ if(m->infolen > 0 && off >= m->infolen)
+ return 0;
+ off0 = off;
+ s = buf;
+ e = s + count;
+ for(i = 0; s < e; i++){
+ if(i == nelem(infofields)){
+ m->infolen = s - buf + off0;
+ break;
+ }
+ n = fileinfo(mb, m, infofields[i], &p);
+ if(off > n){
+ off -= n + 1;
+ continue;
+ }
+ if(off){
n -= off;
+ p += off;
off = 0;
}
- if(n > count - len)
- n = count - len;
- if(buf)
- memmove(buf+len, p, n);
- len += n;
+ if(s + n > e)
+ n = e - s;
+ memcpy(s, p, n);
+ s += n;
+ if(s < e)
+ *s++ = '\n';
}
- s_free(s);
- return len;
+ return s - buf;
}
static void
mkstat(Dir *d, Mailbox *mb, Message *m, int t)
{
- char *p;
+ char *p, *e;
d->uid = user;
d->gid = user;
@@ -491,13 +709,13 @@ mkstat(Dir *d, Mailbox *mb, Message *m, int t)
d->qid.type = QTFILE;
d->type = 0;
d->dev = 0;
- if(mb != nil && mb->d != nil){
- d->atime = mb->d->atime;
- d->mtime = mb->d->mtime;
- } else {
+ if(m && m->fileid > 1000000ull)
+ d->atime = m->fileid >> 8;
+ else if(mb && mb->d)
+ d->atime = mb->d->mtime;
+ else
d->atime = time(0);
- d->mtime = d->atime;
- }
+ d->mtime = d->atime;
switch(t){
case Qtop:
@@ -530,6 +748,12 @@ mkstat(Dir *d, Mailbox *mb, Message *m, int t)
d->length = 0;
d->qid.path = PATH(0, Qctl);
break;
+ case Qheader:
+ d->name = dirtab[t];
+ cacheheaders(mb, m);
+ d->length = readheader(m, hbuf, 0, sizeof hbuf);
+ putcache(mb, m);
+ break;
case Qmboxctl:
d->name = dirtab[t];
d->mode = 0222;
@@ -539,12 +763,37 @@ mkstat(Dir *d, Mailbox *mb, Message *m, int t)
break;
case Qinfo:
d->name = dirtab[t];
- d->length = readinfo(m, nil, 0, 1<<30);
+ d->length = readinfo(mb, m, hbuf, 0, sizeof hbuf);
d->qid.path = PATH(m->id, t);
break;
+ case Qraw:
+ cacheheaders(mb, m);
+ p = m->start;
+ if(strncmp(m->start, "From ", 5) == 0)
+ if(e = strchr(p, '\n'))
+ p = e + 1;
+ d->name = dirtab[t];
+ d->length = m->size - (p - m->start);
+ putcache(mb, m);
+ break;
+ case Qrawbody:
+ d->name = dirtab[t];
+ d->length = m->rawbsize;
+ break;
+ case Qrawunix:
+ d->name = dirtab[t];
+ d->length = m->size;
+ if(mb->addfrom && Topmsg(mb, m)){
+ cacheheaders(mb, m);
+ d->length += strlen(m->unixheader);
+ putcache(mb, m);
+ }
+ break;
+ case Qflags:
+ d->mode = 0666;
default:
d->name = dirtab[t];
- d->length = fileinfo(m, t, &p);
+ d->length = fileinfo(mb, m, t, &p);
d->qid.path = PATH(m->id, t);
break;
}
@@ -577,9 +826,8 @@ rauth(Fid*)
}
char*
-rflush(Fid *f)
+rflush(Fid*)
{
- USED(f);
return 0;
}
@@ -608,30 +856,43 @@ doclone(Fid *f, int nfid)
return nil;
nf->busy = 1;
nf->open = 0;
- nf->m = f->m;
- nf->mtop = f->mtop;
- nf->mb = f->mb;
- if(f->mb != nil)
- mboxincref(f->mb);
- if(f->mtop != nil){
- qlock(f->mb);
- msgincref(f->mtop);
- qunlock(f->mb);
+ if(nf->mb = f->mb)
+ mboxincref(nf->mb);
+ if(nf->m = f->m)
+ msgincref(gettopmsg(nf->mb, nf->m));
+ if(nf->mtop = f->mtop){
+ qlock(nf->mb);
+ msgincref(nf->mtop);
+ qunlock(nf->mb);
}
nf->qid = f->qid;
+sanefid(nf);
+sanefid(f);
return nf;
}
+/* slow? binary search? */
+static int
+dindex(char *name)
+{
+ int i;
+
+ for(i = 0; i < Qmax; i++)
+ if(dirtab[i] != nil)
+ if(strcmp(dirtab[i], name) == 0)
+ return i;
+ return -1;
+}
+
char*
dowalk(Fid *f, char *name)
{
- int t;
- Mailbox *omb, *mb;
char *rv, *p;
+ int t, t1;
+ Mailbox *omb, *mb;
Hash *h;
t = FILE(f->qid.path);
-
rv = Enotexist;
omb = f->mb;
@@ -640,12 +901,26 @@ dowalk(Fid *f, char *name)
else
qlock(&mbllock);
- // this must catch everything except . and ..
+ /* this must catch everything except . and .. */
retry:
- h = hlook(f->qid.path, name);
+sanefid(f);
+ t1 = FILE(f->qid.path);
+ if((t1 == Qmbox || t1 == Qdir) && *name >= 'a' && *name <= 'z'){
+ h = hlook(f->qid.path, "xxx"); /* sleezy speedup */
+ t1 = dindex(name);
+ if(t1 == -1)
+ h = nil;
+ }else
+ h = hlook(f->qid.path, name);
if(h != nil){
+ if(f->m)
+ msgdecref(f->mb, gettopmsg(f->mb, f->m));
+ if(f->mb && f->mb != h->mb)
+ mboxdecref(f->mb);
f->mb = h->mb;
f->m = h->m;
+ if(f->m)
+ msgincref(gettopmsg(f->mb, f->m));
switch(t){
case Qtop:
if(f->mb != nil)
@@ -659,8 +934,11 @@ retry:
break;
}
f->qid = h->qid;
+ if(t1 < Qmax)
+ f->qid.path = PATH(f->m->id, t1); /* sleezy speedup */
+sanefid(f);
rv = nil;
- } else if((p = strchr(name, '.')) != nil && *name != '.'){
+ }else if((p = strchr(name, '.')) != nil && *name != '.'){
*p = 0;
goto retry;
}
@@ -697,13 +975,15 @@ retry:
break;
case Qdir:
qlock(f->mb);
- if(f->m->whole == f->mb->root){
+ if(Topmsg(f->mb, f->m)){
f->qid.path = PATH(f->mb->id, Qmbox);
f->qid.type = QTDIR;
f->qid.vers = f->mb->d->qid.vers;
msgdecref(f->mb, f->mtop);
+ msgdecref(f->mb, f->m);
f->m = f->mtop = nil;
} else {
+ /* refs don't change; still the same message */
f->m = f->m->whole;
f->qid.path = PATH(f->m->id, Qdir);
f->qid.type = QTDIR;
@@ -723,6 +1003,7 @@ rwalk(Fid *f)
char *rv;
int i;
+sanefid(f);
if(f->open)
return Eisopen;
@@ -754,32 +1035,31 @@ rwalk(Fid *f)
}
rhdr.nwqid = i;
- /* we only error out if no walk */
+ /* we only error out if no walk */
if(i > 0)
rv = nil;
-
+sanefid(f);
return rv;
}
-char *
+char*
ropen(Fid *f)
{
int file;
if(f->open)
return Eisopen;
-
file = FILE(f->qid.path);
if(thdr.mode != OREAD)
- if(file != Qctl && file != Qmboxctl)
+ if(file != Qctl && file != Qmboxctl && file != Qflags)
return Eperm;
- // make sure we've decoded
+ /* make sure we've decoded */
if(file == Qbody){
- if(f->m->decoded == 0)
- decode(f->m);
- if(f->m->converted == 0)
- convert(f->m);
+ cachebody(f->mb, f->m);
+ decode(f->m);
+ convert(f->m);
+ putcache(f->mb, f->m);
}
rhdr.iounit = 0;
@@ -788,7 +1068,7 @@ ropen(Fid *f)
return 0;
}
-char *
+char*
rcreate(Fid*)
{
return Eperm;
@@ -816,7 +1096,7 @@ readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
for(mb = mbl; mb != nil; mb = mb->next){
mkstat(&d, mb, nil, Qmbox);
- m = convD2M(&d, &buf[n], blen-n);
+ m = convD2M(&d, &buf[n], blen - n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
break;
@@ -851,22 +1131,22 @@ readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
off -= m;
}
- // to avoid n**2 reads of the directory, use a saved finger pointer
+ /* to avoid n**2 reads of the directory, use a saved finger pointer */
if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
msg = f->fptr;
pos = f->foff;
} else {
msg = f->mb->root->part;
pos = 0;
- }
+ }
for(; cnt > 0 && msg != nil; msg = msg->next){
- // act like deleted files aren't there
+ /* act like deleted files aren't there */
if(msg->deleted)
continue;
mkstat(&d, f->mb, msg, Qdir);
- m = convD2M(&d, &buf[n], blen-n);
+ m = convD2M(&d, &buf[n], blen - n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
break;
@@ -876,7 +1156,7 @@ readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
pos += m;
}
- // save a finger pointer for next read of the mbox directory
+ /* save a finger pointer for next read of the mbox directory */
f->foff = pos;
f->fptr = msg;
f->fvers = f->mb->vers;
@@ -896,7 +1176,7 @@ readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
pos = 0;
for(i = 0; i < Qmax; i++){
mkstat(&d, f->mb, f->m, i);
- m = convD2M(&d, &buf[n], blen-n);
+ m = convD2M(&d, &buf[n], blen - n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
return n;
@@ -907,7 +1187,7 @@ readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
}
for(msg = f->m->part; msg != nil; msg = msg->next){
mkstat(&d, f->mb, msg, Qdir);
- m = convD2M(&d, &buf[n], blen-n);
+ m = convD2M(&d, &buf[n], blen - n);
if(off <= pos){
if(m <= BIT16SZ || m > cnt)
break;
@@ -920,60 +1200,110 @@ readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
return n;
}
+static int
+mboxctlread(Mailbox *mb, char **p)
+{
+ static char buf[128];
+
+ *p = buf;
+ return snprint(*p, sizeof buf, "%s\n%ld\n", mb->path, mb->vers);
+}
+
char*
rread(Fid *f)
{
- long off;
- int t, i, n, cnt;
char *p;
+ int t, i, n, cnt;
+ long off;
rhdr.count = 0;
off = thdr.offset;
cnt = thdr.count;
-
if(cnt > messagesize - IOHDRSZ)
cnt = messagesize - IOHDRSZ;
-
rhdr.data = (char*)mbuf;
+sanefid(f);
t = FILE(f->qid.path);
if(f->qid.type & QTDIR){
- if(t == Qtop) {
+ if(t == Qtop){
qlock(&mbllock);
n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
qunlock(&mbllock);
- } else if(t == Qmbox) {
+ }else if(t == Qmbox) {
qlock(f->mb);
if(off == 0)
syncmbox(f->mb, 1);
n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
qunlock(f->mb);
- } else if(t == Qmboxctl) {
+ }else if(t == Qmboxctl)
n = 0;
- } else {
+ else
n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
- }
-
rhdr.count = n;
return nil;
}
- if(FILE(f->qid.path) == Qheader){
+ switch(t){
+ case Qctl:
+ rhdr.count = 0;
+ break;
+ case Qmboxctl:
+ i = mboxctlread(f->mb, &p);
+ goto output;
+ break;
+ case Qheader:
+ cacheheaders(f->mb, f->m);
rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
- return nil;
- }
-
- if(FILE(f->qid.path) == Qinfo){
- rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
- return nil;
+ putcache(f->mb, f->m);
+ break;
+ case Qinfo:
+ if(cnt > sizeof mbuf)
+ cnt = sizeof mbuf;
+ rhdr.count = readinfo(f->mb, f->m, (char*)mbuf, off, cnt);
+ break;
+ case Qrawunix:
+ if(f->mb->addfrom && Topmsg(f->mb, f->m)){
+ cacheheaders(f->mb, f->m);
+ p = f->m->unixheader;
+ if(off < strlen(p)){
+ rhdr.count = strlen(p + off);
+ memmove(mbuf, p + off, rhdr.count);
+ break;
+ }
+ off -= strlen(p);
+ putcache(f->mb, f->m);
+ }
+ default:
+ i = fileinfo(f->mb, f->m, t, &p);
+ output:
+ if(off < i){
+ if(off + cnt > i)
+ cnt = i - off;
+ if(cnt > sizeof mbuf)
+ cnt = sizeof mbuf;
+ memmove(mbuf, p + off, cnt);
+ rhdr.count = cnt;
+ }
+ break;
}
+ return nil;
+}
- i = fileinfo(f->m, FILE(f->qid.path), &p);
- if(off < i){
- if((off + cnt) > i)
- cnt = i - off;
- memmove(mbuf, p + off, cnt);
- rhdr.count = cnt;
+char*
+modflags(Mailbox *mb, Message *m, char *p)
+{
+ char *err;
+ uchar f;
+
+ f = m->flags;
+ if(err = txflags(p, &f))
+ return err;
+ if(f != m->flags){
+ if(mb->modflags != nil)
+ mb->modflags(mb, m, f);
+ m->flags = f;
+ m->cstate |= Cidxstale;
}
return nil;
}
@@ -981,87 +1311,141 @@ rread(Fid *f)
char*
rwrite(Fid *f)
{
- char *err;
- char *token[1024];
- int t, n;
- String *file;
+ char *argvbuf[1024], **argv, file[Pathlen], *err, *v0;
+ int i, t, argc, flags;
+ Message *m;
t = FILE(f->qid.path);
rhdr.count = thdr.count;
+ sanefid(f);
+ if(thdr.count == 0)
+ return Ebadctl;
+ if(thdr.data[thdr.count - 1] == '\n')
+ thdr.data[thdr.count - 1] = 0;
+ else
+ thdr.data[thdr.count] = 0;
+ argv = argvbuf;
switch(t){
case Qctl:
- if(thdr.count == 0)
- return Ebadctl;
- if(thdr.data[thdr.count-1] == '\n')
- thdr.data[thdr.count-1] = 0;
- else
- thdr.data[thdr.count] = 0;
- n = tokenize(thdr.data, token, nelem(token));
- if(n == 0)
+ memset(argvbuf, 0, sizeof argvbuf);
+ argc = tokenize(thdr.data, argv, nelem(argvbuf) - 1);
+ if(argc == 0)
return Ebadctl;
- if(strcmp(token[0], "open") == 0){
- file = s_new();
- switch(n){
- case 1:
- err = Ebadctl;
- break;
- case 2:
- mboxpath(token[1], getlog(), file, 0);
- err = newmbox(s_to_c(file), nil, 0);
- break;
- default:
- mboxpath(token[1], getlog(), file, 0);
- if(strchr(token[2], '/') != nil)
- err = "/ not allowed in mailbox name";
- else
- err = newmbox(s_to_c(file), token[2], 0);
- break;
- }
- s_free(file);
- return err;
+ if(strcmp(argv[0], "open") == 0 || strcmp(argv[0], "create") == 0){
+ if(argc == 1 || argc > 3)
+ return Ebadargs;
+ mboxpathbuf(file, sizeof file, getlog(), argv[1]);
+ if(argc == 3){
+ if(strchr(argv[2], '/') != nil)
+ return "/ not allowed in mailbox name";
+ }else
+ argv[2] = nil;
+ flags = 0;
+ if(strcmp(argv[0], "create") == 0)
+ flags |= DMcreate;
+ return newmbox(file, argv[2], flags, 0);
}
- if(strcmp(token[0], "close") == 0){
- if(n < 2)
+ if(strcmp(argv[0], "close") == 0){
+ if(argc < 2)
return nil;
- freembox(token[1]);
+ for(i = 1; i < argc; i++)
+ freembox(argv[i]);
return nil;
}
- if(strcmp(token[0], "delete") == 0){
- if(n < 3)
+ if(strcmp(argv[0], "delete") == 0){
+ if(argc < 3)
return nil;
- delmessages(n-1, &token[1]);
+ delmessages(argc - 1, argv + 1);
return nil;
}
+ if(strcmp(argv[0], "flag") == 0){
+ if(argc < 3)
+ return nil;
+ return flagmessages(argc - 1, argv + 1);
+ }
+ if(strcmp(argv[0], "remove") == 0){
+ v0 = argv0;
+ flags = 0;
+ ARGBEGIN{
+ default:
+ argv0 = v0;
+ return Ebadargs;
+ case 'r':
+ flags |= Rrecur;
+ break;
+ case 't':
+ flags |= Rtrunc;
+ break;
+ }ARGEND
+ argv0 = v0;
+ if(argc == 0)
+ return Ebadargs;
+ for(; *argv; argv++){
+ mboxpathbuf(file, sizeof file, getlog(), *argv);
+ if(err = newmbox(file, nil, 0, 0))
+ return err;
+// if(!mb->remove)
+// return "remove not implemented";
+ if(err = removembox(file, flags))
+ return err;
+ }
+ return 0;
+ }
+ if(strcmp(argv[0], "rename") == 0){
+ v0 = argv0;
+ flags = 0;
+ ARGBEGIN{
+ case 't':
+ flags |= Rtrunc;
+ break;
+ }ARGEND
+ argv0 = v0;
+ if(argc != 2)
+ return Ebadargs;
+ return mboxrename(argv[0], argv[1], flags);
+ }
return Ebadctl;
case Qmboxctl:
if(f->mb && f->mb->ctl){
- if(thdr.count == 0)
- return Ebadctl;
- if(thdr.data[thdr.count-1] == '\n')
- thdr.data[thdr.count-1] = 0;
- else
- thdr.data[thdr.count] = 0;
- n = tokenize(thdr.data, token, nelem(token));
- if(n == 0)
+ argc = tokenize(thdr.data, argv, nelem(argvbuf));
+ if(argc == 0)
return Ebadctl;
- return (*f->mb->ctl)(f->mb, n, token);
+ return f->mb->ctl(f->mb, argc, argv);
}
+ break;
+ case Qflags:
+ /*
+ * modifying flags on subparts is a little strange.
+ */
+ if(!f->mb || !f->m)
+ break;
+ m = gettopmsg(f->mb, f->m);
+ err = modflags(f->mb, m, thdr.data);
+// premature optimization? flags not written immediately.
+// if(err == nil && f->m->cstate&Cidxstale)
+// wridxfile(f->mb); /* syncmbox(f->mb, 1); */
+ return err;
}
return Eperm;
}
-char *
+char*
rclunk(Fid *f)
{
Mailbox *mb;
- f->busy = 0;
+sanefid(f);
+ f->busy = 1;
+ /* coherence(); */
+ f->fid = -1;
f->open = 0;
- if(f->mtop != nil){
+ if(f->mtop){
qlock(f->mb);
msgdecref(f->mb, f->mtop);
qunlock(f->mb);
}
+ if(f->m)
+ msgdecref(f->mb, gettopmsg(f->mb, f->m));
f->m = f->mtop = nil;
mb = f->mb;
if(mb != nil){
@@ -1071,17 +1455,18 @@ rclunk(Fid *f)
mboxdecref(mb);
qunlock(&mbllock);
}
- f->fid = -1;
+ f->busy = 0;
return 0;
}
char *
rremove(Fid *f)
{
+sanefid(f);
if(f->m != nil){
if(f->m->deleted == 0)
mailplumb(f->mb, f->m, 1);
- f->m->deleted = 1;
+ f->m->deleted = Deleted;
}
return rclunk(f);
}
@@ -1091,6 +1476,7 @@ rstat(Fid *f)
{
Dir d;
+sanefid(f);
if(FILE(f->qid.path) == Qmbox){
qlock(f->mb);
syncmbox(f->mb, 1);
@@ -1102,13 +1488,13 @@ rstat(Fid *f)
return 0;
}
-char *
+char*
rwstat(Fid*)
{
return Eperm;
}
-Fid *
+Fid*
newfid(int fid)
{
Fid *f, *ff;
@@ -1152,33 +1538,44 @@ io(void)
int n;
/* start a process to watch the mailboxes*/
- if(plumbing){
+ if(plumbing || biffing)
switch(rfork(RFPROC|RFMEM)){
case -1:
/* oh well */
break;
case 0:
reader();
- exits(nil);
+ exits("");
default:
break;
}
- }
- while((n = read9pmsg(mfd[0], mdata, messagesize)) != 0){
+ for(;;){
+ /*
+ * reading from a pipe or a network device
+ * will give an error after a few eof reads
+ * however, we cannot tell the difference
+ * between a zero-length read and an interrupt
+ * on the processes writing to us,
+ * so we wait for the error
+ */
+ checkmboxrefs();
+ n = read9pmsg(mfd[0], mdata, messagesize);
+ if(n == 0)
+ continue;
if(n < 0)
- error("mount read");
- if(convM2S(mdata, n, &thdr) != n)
- error("convM2S format error");
+ return;
+ if(convM2S(mdata, n, &thdr) == 0)
+ continue;
- if(debug)
+ if(Dflag)
fprint(2, "%s:<-%F\n", argv0, &thdr);
rhdr.data = (char*)mdata + messagesize;
if(!fcalls[thdr.type])
err = "bad fcall type";
else
- err = (*fcalls[thdr.type])(newfid(thdr.fid));
+ err = fcalls[thdr.type](newfid(thdr.fid));
if(err){
rhdr.type = Rerror;
rhdr.ename = err;
@@ -1187,14 +1584,16 @@ io(void)
rhdr.fid = thdr.fid;
}
rhdr.tag = thdr.tag;
- if(debug)
- fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
+ if(Dflag)
+ fprint(2, "%s:->%F\n", argv0, &rhdr);
n = convS2M(&rhdr, mdata, messagesize);
if(write(mfd[1], mdata, n) != n)
error("mount write");
}
}
+static char *readerargv[] = {"upas/fs", "plumbing", 0};
+
void
reader(void)
{
@@ -1202,13 +1601,14 @@ reader(void)
Dir *d;
Mailbox *mb;
+ setname(readerargv);
sleep(15*1000);
for(;;){
t = time(0);
qlock(&mbllock);
for(mb = mbl; mb != nil; mb = mb->next){
assert(mb->refs > 0);
- if(mb->waketime != 0 && t > mb->waketime){
+ if(mb->waketime != 0 && t >= mb->waketime){
qlock(mb);
mb->waketime = 0;
break;
@@ -1256,8 +1656,8 @@ newid(void)
void
error(char *s)
{
- postnote(PNGROUP, getpid(), "die yankee pig dog");
- fprint(2, "%s: %s: %r\n", argv0, s);
+ syskillpg(getpid());
+ eprint("upas/fs: fatal error: %s: %r\n", s);
exits(s);
}
@@ -1266,13 +1666,13 @@ typedef struct Ignorance Ignorance;
struct Ignorance
{
Ignorance *next;
- char *str; /* string */
- int partial; /* true if not exact match */
+ char *str;
+ int len;
};
Ignorance *ignorance;
/*
- * read the file of headers to ignore
+ * read the file of headers to ignore
*/
void
readignore(void)
@@ -1288,15 +1688,13 @@ readignore(void)
if(b == 0)
return;
while(p = Brdline(b, '\n')){
- p[Blinelen(b)-1] = 0;
+ p[Blinelen(b) - 1] = 0;
while(*p && (*p == ' ' || *p == '\t'))
p++;
if(*p == '#')
continue;
- i = malloc(sizeof(Ignorance));
- if(i == 0)
- break;
- i->partial = strlen(p);
+ i = emalloc(sizeof *i);
+ i->len = strlen(p);
i->str = strdup(p);
if(i->str == 0){
free(i);
@@ -1315,159 +1713,37 @@ ignore(char *p)
readignore();
for(i = ignorance; i != nil; i = i->next)
- if(cistrncmp(i->str, p, i->partial) == 0)
+ if(cistrncmp(i->str, p, i->len) == 0)
return 1;
return 0;
}
int
-hdrlen(char *p, char *e)
-{
- char *ep;
-
- ep = p;
- do {
- ep = strchr(ep, '\n');
- if(ep == nil){
- ep = e;
- break;
- }
- ep++;
- if(ep >= e){
- ep = e;
- break;
- }
- } while(*ep == ' ' || *ep == '\t');
- return ep - p;
-}
-
-// rfc2047 non-ascii: =?charset?q?encoded-text?=
-int
-rfc2047convert(String *s, char *token, int len)
-{
- char charset[100], decoded[1024], *e, *x;
- int l;
-
- if(len == 0)
- return -1;
-
- e = token+len-2;
- token += 2;
-
- x = memchr(token, '?', e-token);
- if(x == nil || (l=x-token) >= sizeof charset)
- return -1;
- memmove(charset, token, l);
- charset[l] = 0;
-
- token = x+1;
-
- // bail if it doesn't fit
- if(e-token > sizeof(decoded)-1)
- return -1;
-
- // bail if we don't understand the encoding
- if(cistrncmp(token, "b?", 2) == 0){
- token += 2;
- len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
- decoded[len] = 0;
- } else if(cistrncmp(token, "q?", 2) == 0){
- token += 2;
- len = decquoted(decoded, token, e, 1);
- if(len > 0 && decoded[len-1] == '\n')
- len--;
- decoded[len] = 0;
- } else
- return -1;
-
- if(xtoutf(charset, &x, decoded, decoded+len) <= 0)
- s_append(s, decoded);
- else {
- s_append(s, x);
- free(x);
- }
- return 0;
-}
-
-char*
-rfc2047start(char *start, char *end)
-{
- int quests;
-
- if(*--end != '=')
- return nil;
- if(*--end != '?')
- return nil;
-
- quests = 0;
- for(end--; end >= start; end--){
- switch(*end){
- case '=':
- if(quests == 3 && *(end+1) == '?')
- return end;
- break;
- case '?':
- ++quests;
- break;
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- /* can't have white space in a token */
- return nil;
- }
- }
- return nil;
-}
-
-// convert a header line
-String*
-stringconvert(String *s, char *uneaten, int len)
-{
- char *token, *p, *e;
-
- s = s_reset(s);
- p = uneaten;
- for(e = p+len; p < e; ){
- while(*p++ == '=' && (token = rfc2047start(uneaten, p))){
- s_nappend(s, uneaten, token-uneaten);
- if(rfc2047convert(s, token, p - token) < 0)
- s_nappend(s, token, p - token);
- uneaten = p;
- for(; p<e && isspace(*p);)
- p++;
- if(p+2 < e && p[0] == '=' && p[1] == '?')
- uneaten = p; // paste
- }
- }
- if(p > uneaten)
- s_nappend(s, uneaten, p-uneaten);
- return s;
-}
-
-int
readheader(Message *m, char *buf, int off, int cnt)
{
- char *p, *e;
- int n, ns;
- char *to = buf;
- String *s;
+ char *s, *end, *se, *p, *e, *to;
+ int n, ns, salloc;
+ to = buf;
p = m->header;
e = m->hend;
- s = nil;
+ s = emalloc(salloc = 2048);
+ end = s + salloc;
- // copy in good headers
+ /* copy in good headers */
while(cnt > 0 && p < e){
n = hdrlen(p, e);
+ assert(n > 0);
if(ignore(p)){
p += n;
continue;
}
-
- // rfc2047 processing
- s = stringconvert(s, p, n);
- ns = s_len(s);
+ if(n + 1 > salloc){
+ s = erealloc(s, salloc = n + 1);
+ end = s + salloc;
+ }
+ se = rfc2047(s, end, p, n, 0);
+ ns = se - s;
if(off > 0){
if(ns <= off){
off -= ns;
@@ -1478,34 +1754,16 @@ readheader(Message *m, char *buf, int off, int cnt)
}
if(ns > cnt)
ns = cnt;
- memmove(to, s_to_c(s)+off, ns);
+ memmove(to, s + off, ns);
to += ns;
p += n;
cnt -= ns;
off = 0;
}
-
- s_free(s);
+ free(s);
return to - buf;
}
-int
-headerlen(Message *m)
-{
- char buf[1024];
- int i, n;
-
- if(m->hlen >= 0)
- return m->hlen;
- for(n = 0; ; n += i){
- i = readheader(m, buf, n, sizeof(buf));
- if(i <= 0)
- break;
- }
- m->hlen = n;
- return n;
-}
-
QLock hashlock;
uint
@@ -1545,6 +1803,7 @@ henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
int h;
Hash *hp, **l;
+//if(m)sanemsg(m);
qlock(&hashlock);
h = hash(ppath, name);
for(l = &htab[h]; *l != nil; l = &(*l)->next){
@@ -1595,25 +1854,43 @@ hashmboxrefs(Mailbox *mb)
int refs = 0;
qlock(&hashlock);
- for(h = 0; h < Hsize; h++){
+ for(h = 0; h < Hsize; h++)
for(hp = htab[h]; hp != nil; hp = hp->next)
if(hp->mb == mb)
refs++;
- }
qunlock(&hashlock);
return refs;
}
void
+checkmboxrefs(void)
+{
+ int refs;
+ Mailbox *mb;
+
+// qlock(&mbllock);
+ for(mb = mbl; mb; mb = mb->next){
+ qlock(mb);
+ refs = fidmboxrefs(mb) + 1;
+ if(refs != mb->refs){
+ eprint("%s:%s ref mismatch got %d expected %d\n", mb->name, mb->path, refs, mb->refs);
+ abort();
+ }
+ qunlock(mb);
+ }
+// qunlock(&mbllock);
+}
+
+void
post(char *name, char *envname, int srvfd)
{
- int fd;
char buf[32];
+ int fd;
fd = create(name, OWRITE, 0600);
if(fd < 0)
error("post failed");
- sprint(buf, "%d",srvfd);
+ snprint(buf, sizeof buf, "%d", srvfd);
if(write(fd, buf, strlen(buf)) != strlen(buf))
error("srv write");
close(fd);
diff --git a/sys/src/cmd/upas/fs/header.c b/sys/src/cmd/upas/fs/header.c
new file mode 100644
index 000000000..c6ffe8216
--- /dev/null
+++ b/sys/src/cmd/upas/fs/header.c
@@ -0,0 +1,176 @@
+#include "common.h"
+#include <ctype.h>
+#include <libsec.h>
+#include "dat.h"
+
+int
+hdrlen(char *p, char *e)
+{
+ char *ep;
+
+ ep = p;
+ do {
+ ep = strchr(ep, '\n');
+ if(ep == nil){
+ ep = e;
+ break;
+ }
+ if(ep == p)
+ break;
+ if(ep - p == 1 && ep[-1] == '\r')
+ break;
+ ep++;
+ if(ep >= e){
+ ep = e;
+ break;
+ }
+ } while(*ep == ' ' || *ep == '\t');
+ return ep - p;
+}
+
+/* rfc2047 non-ascii: =?charset?q?encoded-text?= */
+static int
+tok(char **sp, char *se, char *token, int len)
+{
+ char charset[100], *s, *e, *x;
+ int l;
+
+ if(len == 0)
+ return -1;
+ s = *sp;
+ e = token + len - 2;
+ token += 2;
+
+ x = memchr(token, '?', e - token);
+ if(x == nil || (l = x - token) >= sizeof charset)
+ return -1;
+ memmove(charset, token, l);
+ charset[l] = 0;
+
+ /* bail if it doesn't fit */
+ token = x + 1;
+ if(e - token > se - s - 1)
+ return -1;
+
+ if(cistrncmp(token, "b?", 2) == 0){
+ token += 2;
+ len = dec64((uchar*)s, se - s - 1, token, e - token);
+ if(len == -1)
+ return -1;
+ s[len] = 0;
+ }else if(cistrncmp(token, "q?", 2) == 0){
+ token += 2;
+ len = decquoted(s, token, e, 1);
+ if(len > 0 && s[len - 1] == '\n')
+ len--;
+ s[len] = 0;
+ }else
+ return -1;
+
+ if(xtoutf(charset, &x, s, s + len) <= 0)
+ s += len;
+ else {
+ s = seprint(s, se, "%s", x);
+ free(x);
+ }
+ *sp = s;
+ return 0;
+}
+
+char*
+tokbegin(char *start, char *end)
+{
+ int quests;
+
+ if(*--end != '=')
+ return nil;
+ if(*--end != '?')
+ return nil;
+
+ quests = 0;
+ for(end--; end >= start; end--){
+ switch(*end){
+ case '=':
+ if(quests == 3 && *(end + 1) == '?')
+ return end;
+ break;
+ case '?':
+ ++quests;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ /* can't have white space in a token */
+ return nil;
+ }
+ }
+ return nil;
+}
+
+static char*
+seappend822f(char *s, char *e, char *a, int n)
+{
+ int skip, c;
+
+ skip = 0;
+ for(; n--; a++){
+ c = *a;
+ if(skip && isspace(c))
+ continue;
+ if(c == '\n'){
+ c = ' ';
+ skip = 1;
+ }else{
+ if(c < 0x20)
+ continue;
+ skip = 0;
+ }
+ s = sputc(s, e, c);
+ }
+ return s;
+}
+
+static char*
+seappend822(char *s, char *e, char *a, int n)
+{
+ int c;
+
+ for(; n--; a++){
+ c = *a;
+ if(c < 0x20 && c != '\n' && c != '\t')
+ continue;
+ s = sputc(s, e, c);
+ }
+ return s;
+}
+
+/* convert a header line */
+char*
+rfc2047(char *s, char *se, char *uneaten, int len, int fold)
+{
+ char *sp, *token, *p, *e;
+ char *(*f)(char*, char*, char*, int);
+
+ f = seappend822;
+ if(fold)
+ f = seappend822f;
+ sp = s;
+ p = uneaten;
+ for(e = p + len; p < e; ){
+ while(*p++ == '=' && (token = tokbegin(uneaten, p))){
+ sp = f(sp, se, uneaten, token - uneaten);
+ if(tok(&sp, se, token, p - token) < 0)
+ sp = f(sp, se, token, p - token);
+ uneaten = p;
+ for(; p < e && isspace(*p);)
+ p++;
+ if(p + 2 < e && p[0] == '=' && p[1] == '?')
+ uneaten = p; /* paste */
+ }
+ }
+ if(p > uneaten)
+ sp = f(sp, se, uneaten, e - uneaten);
+ *sp = 0;
+ return sp;
+}
diff --git a/sys/src/cmd/upas/fs/idx.c b/sys/src/cmd/upas/fs/idx.c
new file mode 100644
index 000000000..eebe191c7
--- /dev/null
+++ b/sys/src/cmd/upas/fs/idx.c
@@ -0,0 +1,535 @@
+#include "common.h"
+#include <libsec.h>
+#include "dat.h"
+
+#define idprint(...) if(iflag > 1) fprint(2, __VA_ARGS__); else {}
+#define iprint(...) if(iflag) fprint(2, __VA_ARGS__); else {}
+
+static char *magic = "idx magic v7\n";
+static char *mbmagic = "genericv1";
+enum {
+ Idxfields = 21,
+
+ Idxto = 30000, /* index timeout in ms */
+ Idxstep = 300, /* sleep between tries */
+};
+
+void
+idxfree(Idx *i)
+{
+ if(i->str)
+ free(i->str);
+ else{
+ free(i->digest);
+ free(i->ffrom);
+ free(i->from);
+ free(i->to);
+ free(i->cc);
+ free(i->bcc);
+ free(i->replyto);
+ free(i->messageid);
+ free(i->subject);
+ free(i->sender);
+ free(i->inreplyto);
+ free(i->idxaux);
+ }
+ memset(i, 0, sizeof *i);
+}
+
+static char*
+∂(char *x)
+{
+ if(x)
+ return x;
+ return "";
+}
+
+static int
+pridxmsg(Biobuf *b, Idx *x)
+{
+ Bprint(b, "%#A %ux %D %lud ", x->digest, x->flags&~Frecent, x->fileid, x->lines);
+ Bprint(b, "%q %q %q %q %q ", ∂(x->ffrom), ∂(x->from), ∂(x->to), ∂(x->cc), ∂(x->bcc));
+ Bprint(b, "%q %q %q %q %q ", ∂(x->replyto), ∂(x->messageid), ∂(x->subject), ∂(x->sender), ∂(x->inreplyto));
+ Bprint(b, "%s %d %lud %lud ", rtab[x->type].s, x->disposition, x->size, x->rawbsize);
+ Bprint(b, "%lud %q %d\n", x->ibadchars, ∂(x->idxaux), x->nparts);
+ return 0;
+}
+
+static int
+pridx0(Biobuf *b, Mailbox *mb, Message *m, int l)
+{
+ for(; m; m = m->next){
+ if(l == 0)
+ if(insurecache(mb, m) == -1)
+ continue;
+ if(pridxmsg(b, m))
+ return -1;
+ if(m->part)
+ pridx0(b, mb, m->part, l + 1);
+ m->cstate &= ~Cidxstale;
+ m->cstate |= Cidx;
+ if(l == 0)
+ msgdecref(mb, m);
+ }
+ return 0;
+}
+
+void
+genericidxwrite(Biobuf *b, Mailbox*)
+{
+ Bprint(b, "%s\n", mbmagic);
+}
+
+static int
+pridx(Biobuf *b, Mailbox *mb)
+{
+ int i;
+
+ Bprint(b, magic);
+ mb->idxwrite(b, mb);
+// prrefs(b);
+ i = pridx0(b, mb, mb->root->part, 0);
+ return i;
+}
+
+static char *eopen[] = {
+ "not found",
+ "does not exist",
+ "file is locked",
+ "file locked",
+ "exclusive lock",
+ 0,
+};
+
+static char *ecreate[] = {
+ "already exists",
+ "file is locked",
+ "file locked",
+ "exclusive lock",
+ 0,
+};
+
+static int
+bad(char **t)
+{
+ char buf[ERRMAX];
+ int i;
+
+ rerrstr(buf, sizeof buf);
+ for(i = 0; t[i]; i++)
+ if(strstr(buf, t[i]))
+ return 0;
+ return 1;
+}
+
+static int
+forceexcl(int fd)
+{
+ int r;
+ Dir *d;
+
+ d = dirfstat(fd);
+ if(d == nil)
+ return 0; /* ignore: assume file removed */
+ if(d->mode & DMEXCL){
+ free(d);
+ return 0;
+ }
+ d->mode |= DMEXCL;
+ d->qid.type |= QTEXCL;
+ r = dirfwstat(fd, d);
+ free(d);
+ if(r == -1)
+ return 0; /* ignore unwritable (e.g dump) */
+ close(fd);
+ return -1;
+}
+
+static int
+exopen(char *s)
+{
+ int i, fd;
+
+ for(i = 0; i < Idxto/Idxstep; i++){
+ if((fd = open(s, OWRITE|OTRUNC)) >= 0 || bad(eopen)){
+ if(fd != -1 && forceexcl(fd) == -1)
+ continue;
+ return fd;
+ }
+ if((fd = create(s, OWRITE|OEXCL, DMTMP|DMEXCL|0600)) >= 0 || bad(ecreate))
+ return fd;
+ sleep(Idxstep);
+ }
+ werrstr("lock timeout");
+ return -1;
+}
+
+static Message*
+findmessage(Mailbox *, Message *parent, int n)
+{
+ Message *m;
+
+ for(m = parent->part; m; m = m->next)
+ if(!m->digest && n-- == 0)
+ return m;
+ return 0;
+}
+
+static int
+validmessage(Mailbox *mb, Message *m, int level)
+{
+ if(level){
+ if(m->digest != 0)
+ goto lose;
+ if(m->fileid <= 1000000ull<<8)
+ if(m->fileid != 0)
+ goto lose;
+ }else{
+ if(m->digest == 0)
+ goto lose;
+ if(m->size == 0)
+ goto lose;
+ if(m->fileid <= 1000000ull<<8)
+ goto lose;
+ if(mtreefind(mb, m->digest))
+ goto lose;
+ }
+ return 1;
+lose:
+ eprint("invalid cache[%d] %#A size %ld %D\n", level, m->digest, m->size, m->fileid);
+ return 0;
+}
+
+/*
+ * n.b.: we don't insure this is the index version we last read.
+ *
+ * we may overwrite changes. dualing deletes should sync eventually.
+ * mboxsync should complain about missing messages but
+ * mutable information (which is not in the email itself)
+ * may be lost.
+ */
+int
+wridxfile(Mailbox *mb)
+{
+ char buf[Pathlen + 4];
+ int r, fd;
+ Biobuf b;
+ Dir *d;
+
+ assert(semacquire(&mb->idxsem, 0) != -1);
+ snprint(buf, sizeof buf, "%s.idx", mb->path);
+ iprint("wridxfile %s\n", buf);
+ if((fd = exopen(buf)) == -1){
+ rerrstr(buf, sizeof buf);
+ if(strcmp(buf, "no creates") != 0)
+ if(strstr(buf, "file system read only") == 0)
+ eprint("wridxfile: %r\n");
+ semrelease(&mb->idxsem, 1);
+ return -1;
+ }
+ seek(fd, 0, 0);
+ Binit(&b, fd, OWRITE);
+ r = pridx(&b, mb);
+ Bterm(&b);
+ d = dirfstat(fd);
+ if(d == 0)
+ sysfatal("dirfstat: %r");
+ mb->qid = d->qid;
+ free(d);
+ close(fd);
+ semrelease(&mb->idxsem, 1);
+ return r;
+}
+
+static int
+nibble(int c)
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c < 0x20)
+ c += 0x20;
+ if(c >= 'a' && c <= 'f')
+ return c - 'a'+10;
+ return 0xff;
+}
+
+static uchar*
+hackdigest(char *s)
+{
+ uchar t[SHA1dlen];
+ int i;
+
+ if(strcmp(s, "-") == 0)
+ return 0;
+ if(strlen(s) != 2*SHA1dlen){
+ eprint("bad digest %s\n", s);
+ return 0;
+ }
+ for(i = 0; i < SHA1dlen; i++)
+ t[i] = nibble(s[2*i])<<4 | nibble(s[2*i + 1]);
+ memmove(s, t, SHA1dlen);
+ return (uchar*)s;
+}
+
+static uvlong
+rdfileid(char *s, int level)
+{
+ char *p;
+ uvlong uv;
+
+ uv = strtoul(s, &p, 0);
+ if((level == 0 && uv < 1000000) || *p != '.')
+ return 0;
+ return uv<<8 | strtoul(p + 1, 0, 10);
+}
+
+static char*
+∫(char *x)
+{
+ if(x && *x)
+ return x;
+ return nil;
+}
+
+/*
+ * strategy: use top-level avl tree to merge index with
+ * our ideas about the mailbox. new or old messages
+ * with corrupt index entries are marked Dead. they
+ * will be cleared out of the mailbox and are kept out
+ * of the index. when messages are marked Dead, a
+ * reread of the mailbox is forced.
+ *
+ * side note. if we get a new message while we are
+ * running it is added to the list in order but m->id
+ * looks out-of-order. this is because m->id must
+ * increase monotonicly. a new instance of the fs
+ * will result in a different ordering.
+ */
+
+static int
+rdidx(Biobuf *b, Mailbox *mb, Message *parent, int npart, int level, int doplumb)
+{
+ char *f[Idxfields + 1], *s;
+ uchar *digest;
+ int n, flags, nparts, good, bad, redux;
+ Message *m, **ll, *l;
+
+ bad = good = redux = 0;
+ ll = &parent->part;
+ nparts = npart;
+ for(; npart != 0 && (s = Brdstr(b, '\n', 1)); npart--){
+ m = 0;
+ digest = 0;
+ n = tokenize(s, f, nelem(f));
+ if(n != Idxfields){
+dead:
+ eprint("bad index %#A %d %d n=%d\n", digest, level, npart, n);
+ bad++;
+ free(s);
+ if(level)
+ return -1;
+ if(m)
+ m->deleted = Dead;
+ continue;
+ }
+ digest = hackdigest(f[0]);
+ if(digest == 0 ^ level != 0)
+ goto dead;
+ if(level == 0)
+ m = mtreefind(mb, digest);
+ else
+ m = findmessage(mb, parent, nparts - npart);
+ if(m){
+ /*
+ * read in mutable information.
+ * currently this is only flags
+ */
+ redux++;
+ if(level == 0)
+ m->deleted &= ~Dmark;
+ if(m->nparts)
+ if(rdidx(b, mb, m, m->nparts, level + 1, 0) == -1)
+ goto dead;
+ ll = &m->next;
+ idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
+ flags = m->flags;
+ m->flags |= strtoul(f[1], 0, 16);
+ if(flags != m->flags)
+ m->cstate |= Cidxstale;
+ m->cstate |= Cidx;
+ idprint("→%.2ux\n", m->cstate);
+ free(s);
+ // s = 0;
+ continue;
+ }
+ m = newmessage(parent);
+ idprint("%d new %d %#A\n", level, m->id, digest);
+ m->digest = digest;
+ m->flags = strtoul(f[1], 0, 16);
+ m->fileid = rdfileid(f[2], level);
+ m->lines = atoi(f[3]);
+ m->ffrom = ∫(f[4]);
+ m->from = ∫(f[5]);
+ m->to = ∫(f[6]);
+ m->cc = ∫(f[7]);
+ m->bcc = ∫(f[8]);
+ m->replyto = ∫(f[9]);
+ m->messageid = ∫(f[10]);
+ m->subject = ∫(f[11]);
+ m->sender = ∫(f[12]);
+ m->inreplyto = ∫(f[13]);
+ m->type = newrefs(f[14]);
+ m->disposition = atoi(f[15]);
+ m->size = strtoul(f[16], 0, 0);
+ m->rawbsize = strtoul(f[17], 0, 0);
+ m->ibadchars = strtoul(f[18], 0, 0);
+ m->idxaux = ∫(f[19]);
+ m->nparts = strtoul(f[20], 0, 0);
+ m->cstate &= ~Cidxstale;
+ m->cstate |= Cidx;
+ m->str = s;
+ s = 0;
+ if(!validmessage(mb, m, level))
+ goto dead;
+ if(level == 0){
+ mtreeadd(mb, m);
+ m->inmbox = 1;
+ }
+ cachehash(mb, m); /* hokey */
+ l = *ll;
+ *ll = m;
+ ll = &m->next;
+ *ll = l;
+ good++;
+
+ if(m->nparts)
+ if(rdidx(b, mb, m, m->nparts, level + 1, 0) == -1)
+ goto dead;
+ if(doplumb && level == 0)
+ mailplumb(mb, m, 0);
+ }
+ if(level == 0 && bad + redux > 0)
+ iprint("idx: %d %d %d\n", good, bad, redux);
+ if(bad)
+ return -1;
+ return 0;
+}
+
+/* bug: should check time. */
+static int
+qidcmp(int fd, Qid *q)
+{
+ int r;
+ Dir *d;
+ Qid q0;
+
+ d = dirfstat(fd);
+ if(!d)
+ sysfatal("dirfstat: %r");
+ r = 1;
+ if(d->qid.path == q->path)
+ if(d->qid.vers == q->vers)
+ r = 0;
+ q0 = *q;
+ *q = d->qid;
+ free(d);
+ if(q0.path != 0 && r)
+ iprint("qidcmp ... index changed [%ld .. %ld]\n", q0.vers, q->vers);
+ return r;
+}
+
+static int
+verscmp(Biobuf *b, Mailbox *mb)
+{
+ char *s;
+ int n;
+
+ n = -1;
+ if(s = Brdstr(b, '\n', 0))
+ n = strcmp(s, magic);
+ free(s);
+ if(n)
+ return -1;
+ n = -1;
+ if(s = Brdstr(b, '\n', 0))
+ n = mb->idxread(s, mb);
+ free(s);
+ return n;
+}
+
+int
+genericidxread(char *s, Mailbox*)
+{
+ return strcmp(s, mbmagic);
+}
+
+void
+genericidxinvalid(Mailbox *mb)
+{
+ if(mb->d)
+ memset(&mb->d->qid, 0, sizeof mb->d->qid);
+ mb->waketime = time(0);
+}
+
+void
+mark(Mailbox *mb)
+{
+ Message *m;
+
+ for(m = mb->root->part; m != nil; m = m->next)
+ m->deleted |= Dmark;
+}
+
+int
+unmark(Mailbox *mb)
+{
+ int i;
+ Message *m;
+
+ i = 0;
+ for(m = mb->root->part; m != nil; m = m->next)
+ if(m->deleted & Dmark){
+ i++;
+ m->deleted &= ~Dmark; /* let mailbox scan figure this out. BOTCH?? */
+ }
+ return i;
+}
+
+int
+rdidxfile0(Mailbox *mb, int doplumb)
+{
+ char buf[Pathlen + 4];
+ int r, v;
+ Biobuf *b;
+
+ snprint(buf, sizeof buf, "%s.idx", mb->path);
+ b = Bopen(buf, OREAD);
+ if(b == nil)
+ return -2;
+ if(qidcmp(Bfildes(b), &mb->qid) == 0)
+ r = 0;
+ else if(verscmp(b, mb) == -1)
+ r = -1;
+ else{
+ mark(mb);
+ r = rdidx(b, mb, mb->root, -1, 0, doplumb);
+ v = unmark(mb);
+ if(r == 0 && v > 0)
+ r = -1;
+ }
+ Bterm(b);
+ return r;
+}
+
+int
+rdidxfile(Mailbox *mb, int doplumb)
+{
+ int r;
+
+ assert(semacquire(&mb->idxsem, 0) > 0);
+ r = rdidxfile0(mb, doplumb);
+ if(r == -1 && mb->idxinvalid)
+ mb->idxinvalid(mb);
+ semrelease(&mb->idxsem, 1);
+ return r;
+}
diff --git a/sys/src/cmd/upas/fs/imap.c b/sys/src/cmd/upas/fs/imap.c
new file mode 100644
index 000000000..4a3f74467
--- /dev/null
+++ b/sys/src/cmd/upas/fs/imap.c
@@ -0,0 +1,1271 @@
+/*
+ * todo:
+ * 1. sync with imap server's flags
+ * 2. better algorithm for avoiding downloading message list.
+ * 3. get sender — eating envelope is lots of work!
+ */
+#include "common.h"
+#include <libsec.h>
+#include <auth.h>
+#include "dat.h"
+
+#define idprint(i, ...) if(i->flags & Fdebug) fprint(2, __VA_ARGS__); else {}
+#pragma varargck argpos imap4cmd 2
+#pragma varargck type "Z" char*
+#pragma varargck type "U" uvlong
+#pragma varargck type "U" vlong
+
+static char confused[] = "confused about fetch response";
+static char qsep[] = " \t\r\n";
+static char Eimap4ctl[] = "bad imap4 control message";
+
+enum{
+ /* cap */
+ Cnolog = 1<<0,
+ Ccram = 1<<1,
+ Cntlm = 1<<2,
+
+ /* flags */
+ Fssl = 1<<0,
+ Fdebug = 1<<1,
+ Fgmail = 1<<2,
+};
+
+typedef struct {
+ uvlong uid;
+ ulong sizes;
+ ulong dates;
+} Fetchi;
+
+typedef struct Imap Imap;
+struct Imap {
+ long lastread;
+
+ char *mbox;
+ /* free this to free the strings below */
+ char *freep;
+ char *host;
+ char *user;
+
+ int refreshtime;
+ uchar cap;
+ uchar flags;
+
+ ulong tag;
+ ulong validity;
+ int nmsg;
+ int size;
+
+ Fetchi *f;
+ int nuid;
+ int muid;
+
+ Thumbprint *thumb;
+
+ /* open network connection */
+ Biobuf bin;
+ Biobuf bout;
+ int binit;
+ int fd;
+};
+
+enum
+{
+ Qok = 0,
+ Qquote,
+ Qbackslash,
+};
+
+static int
+needtoquote(Rune r)
+{
+ if(r >= Runeself)
+ return Qquote;
+ if(r <= ' ')
+ return Qquote;
+ if(r == '\\' || r == '"')
+ return Qbackslash;
+ return Qok;
+}
+
+static int
+Zfmt(Fmt *f)
+{
+ char *s, *t;
+ int w, quotes;
+ Rune r;
+
+ s = va_arg(f->args, char*);
+ if(s == 0 || *s == 0)
+ return fmtstrcpy(f, "\"\"");
+
+ quotes = 0;
+ for(t = s; *t; t += w){
+ w = chartorune(&r, t);
+ quotes |= needtoquote(r);
+ }
+ if(quotes == 0)
+ return fmtstrcpy(f, s);
+
+ fmtrune(f, '"');
+ for(t = s; *t; t += w){
+ w = chartorune(&r, t);
+ if(needtoquote(r) == Qbackslash)
+ fmtrune(f, '\\');
+ fmtrune(f, r);
+ }
+ return fmtrune(f, '"');
+}
+
+static int
+Ufmt(Fmt *f)
+{
+ char buf[20*2 + 2];
+ ulong a, b;
+ uvlong u;
+
+ u = va_arg(f->args, uvlong);
+ if(u == 1)
+ return fmtstrcpy(f, "nil");
+ if(u == 0)
+ return fmtstrcpy(f, "-");
+ a = u>>32;
+ b = u;
+ snprint(buf, sizeof buf, "%lud:%lud", a, b);
+ return fmtstrcpy(f, buf);
+}
+
+static void
+imap4cmd(Imap *imap, char *fmt, ...)
+{
+ char buf[256], *p;
+ va_list va;
+
+ va_start(va, fmt);
+ p = buf + sprint(buf, "9x%lud ", imap->tag);
+ vseprint(p, buf + sizeof buf, fmt, va);
+ va_end(va);
+
+ p = buf + strlen(buf);
+ if(p > buf + sizeof buf - 3)
+ sysfatal("imap4 command too long");
+ idprint(imap, "-> %s\n", buf);
+ strcpy(p, "\r\n");
+ Bwrite(&imap->bout, buf, strlen(buf));
+ Bflush(&imap->bout);
+}
+
+enum {
+ Ok,
+ No,
+ Bad,
+ Bye,
+ Exists,
+ Status,
+ Fetch,
+ Cap,
+ Auth,
+
+ Unknown,
+};
+
+static char *verblist[] = {
+[Ok] "ok",
+[No] "no",
+[Bad] "bad",
+[Bye] "bye",
+[Exists] "exists",
+[Status] "status",
+[Fetch] "fetch",
+[Cap] "capability",
+[Auth] "authenticate",
+};
+
+static int
+verbcode(char *verb)
+{
+ int i;
+ char *q;
+
+ if(q = strchr(verb, ' '))
+ *q = '\0';
+ for(i = 0; i < nelem(verblist) - 1; i++)
+ if(strcmp(verblist[i], verb) == 0)
+ break;
+ if(q)
+ *q = ' ';
+ return i;
+}
+
+static vlong
+mkuid(Imap *i, char *id)
+{
+ vlong v;
+
+ v = (vlong)i->validity<<32;
+ return v | strtoul(id, 0, 10);
+}
+
+static vlong
+xnum(char *s, int a, int b)
+{
+ vlong v;
+
+ if(*s != a)
+ return -1;
+ v = strtoull(s + 1, &s, 10);
+ if(*s != b)
+ return -1;
+ return v;
+}
+
+static struct{
+ char *flag;
+ int e;
+} ftab[] = {
+ "Answered", Fanswered,
+ "\\Deleted", Fdeleted,
+ "\\Draft", Fdraft,
+ "\\Flagged", Fflagged,
+ "\\Recent", Frecent,
+ "\\Seen", Fseen,
+ "\\Stored", Fstored,
+};
+
+static void
+parseflags(Message *m, char *s)
+{
+ char *f[10];
+ int i, j, j0, n;
+
+ n = tokenize(s, f, nelem(f));
+ qsort(f, n, sizeof *f, (int (*)(void*,void*))strcmp);
+ j = 0;
+ for(i = 0; i < n; i++)
+ for(j0 = j;; j++){
+ if(j == nelem(ftab)){
+ j = j0; /* restart search */
+ break;
+ }
+ if(strcmp(f[i], ftab[j].flag) == 0){
+ m->flags |= ftab[j].e;
+ break;
+ }
+ }
+}
+
+/* "17-Jul-1996 02:44:25 -0700" */
+long
+internaltounix(char *s)
+{
+ Tm tm;
+ if(strlen(s) < 20 || s[2] != '-' || s[6] != '-')
+ return -1;
+ s[2] = ' ';
+ s[6] = ' ';
+ if(strtotm(s, &tm) == -1)
+ return -1;
+ return tm2sec(&tm);
+}
+
+static char*
+qtoken(char *s, char *sep)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '"' && *t != '(' && *t != ')'){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting || *t == '('){
+ quoting++;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '"'){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ if(quoting > 0)
+ quoting--;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+int
+imaptokenize(char *s, char **args, int maxargs)
+{
+ int nargs;
+
+ for(nargs=0; nargs < maxargs; nargs++){
+ while(*s!='\0' && utfrune(qsep, *s)!=nil)
+ s++;
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = qtoken(s, qsep);
+ }
+
+ return nargs;
+}
+
+static char*
+fetchrsp(Imap *imap, char *p, Mailbox *, Message *m)
+{
+ char *f[15], *s, *q;
+ int i, n, a;
+ ulong o, l;
+ uvlong v;
+ static char error[256];
+ extern void msgrealloc(Message*, ulong);
+
+redux:
+ n = imaptokenize(p, f, nelem(f));
+ if(n%2)
+ return confused;
+ for(i = 0; i < n; i += 2){
+ if(strcmp(f[i], "internaldate") == 0){
+ l = internaltounix(f[i + 1]);
+ if(l < 418319360)
+ abort();
+ if(imap->nuid < imap->muid)
+ imap->f[imap->nuid].dates = l;
+ }else if(strcmp(f[i], "rfc822.size") == 0){
+ l = strtoul(f[i + 1], 0, 0);
+ if(m)
+ m->size = l;
+ else if(imap->nuid < imap->muid)
+ imap->f[imap->nuid].sizes = l;
+ }else if(strcmp(f[i], "uid") == 0){
+ v = mkuid(imap, f[1]);
+ if(m)
+ m->imapuid = v;
+ if(imap->nuid < imap->muid)
+ imap->f[imap->nuid].uid = v;
+ }else if(strcmp(f[i], "flags") == 0)
+ parseflags(m, f[i + 1]);
+ else if(strncmp(f[i], "body[]", 6) == 0){
+ s = f[i]+6;
+ o = 0;
+ if(*s == '<')
+ o = xnum(s, '<', '>');
+ if(o == -1)
+ return confused;
+ l = xnum(f[i + 1], '{', '}');
+ a = o + l - m->ibadchars - m->size;
+ if(a > 0){
+ assert(imap->flags & Fgmail);
+ m->size = o + l;
+ msgrealloc(m, m->size);
+ m->size -= m->ibadchars;
+ }
+ if(Bread(&imap->bin, m->start + o, l) != l){
+ snprint(error, sizeof error, "read: %r");
+ return error;
+ }
+ if(Bgetc(&imap->bin) == ')'){
+ while(Bgetc(&imap->bin) != '\n')
+ ;
+ return 0;
+ }
+ /* evil */
+ if(!(p = Brdline(&imap->bin, '\n')))
+ return 0;
+ q = p + Blinelen(&imap->bin);
+ while(q > p && (q[-1] == '\n' || q[-1] == '\r'))
+ q--;
+ *q = 0;
+ lowercase(p);
+ idprint(imap, "<- %s\n", p);
+
+ goto redux;
+ }else
+ return confused;
+ }
+ return 0;
+}
+
+void
+parsecap(Imap *imap, char *s)
+{
+ char *t[32], *p;
+ int n, i;
+
+ s = strdup(s);
+ n = getfields(s, t, nelem(t), 0, " ");
+ for(i = 0; i < n; i++){
+ if(strncmp(t[i], "auth=", 5) == 0){
+ p = t[i] + 5;
+ if(strcmp(p, "cram-md5") == 0)
+ imap->cap |= Ccram;
+ if(strcmp(p, "ntlm") == 0)
+ imap->cap |= Cntlm;
+ }else if(strcmp(t[i], "logindisabled") == 0)
+ imap->cap |= Cnolog;
+ }
+ free(s);
+}
+
+/*
+ * get imap4 response line. there might be various
+ * data or other informational lines mixed in.
+ */
+static char*
+imap4resp0(Imap *imap, Mailbox *mb, Message *m)
+{
+ char *e, *line, *p, *ep, *op, *q, *verb;
+ int n, unexp;
+ static char error[256];
+
+ unexp = 0;
+ while(p = Brdline(&imap->bin, '\n')){
+ ep = p + Blinelen(&imap->bin);
+ while(ep > p && (ep[-1] == '\n' || ep[-1] == '\r'))
+ *--ep = '\0';
+ idprint(imap, "<- %s\n", p);
+ if(unexp && p[0] != '9' && p[1] != 'x')
+ if(strtoul(p + 2, &p, 10) != imap->tag)
+ continue;
+ if(p[0] != '+')
+ lowercase(p); /* botch */
+
+ switch(p[0]){
+ case '+': /* cram challenge */
+ if(ep - p > 2)
+ return p + 2;
+ break;
+ case '*':
+ if(p[1] != ' ')
+ continue;
+ p += 2;
+ line = p;
+ n = strtol(p, &p, 10);
+ if(*p == ' ')
+ p++;
+ verb = p;
+
+ if(p = strchr(verb, ' '))
+ p++;
+ else
+ p = verb + strlen(verb);
+
+ switch(verbcode(verb)){
+ case Bye:
+ /* early disconnect */
+ snprint(error, sizeof error, "%s", p);
+ return error;
+ case Ok:
+ case No:
+ case Bad:
+ /* human readable text at p; */
+ break;
+ case Exists:
+ imap->nmsg = n;
+ break;
+ case Cap:
+ parsecap(imap, p);
+ break;
+ case Status:
+ /* * status inbox (messages 2 uidvalidity 960164964) */
+ if(q = strstr(p, "messages"))
+ imap->nmsg = strtoul(q + 8, 0, 10);
+ if(q = strstr(p, "uidvalidity"))
+ imap->validity = strtoul(q + 11, 0, 10);
+ break;
+ case Fetch:
+ if(*p == '('){
+ p++;
+ if(ep[-1] == ')')
+ *--ep = 0;
+ }
+ if(e = fetchrsp(imap, p, mb, m))
+ eprint("imap: fetchrsp: %s\n", e);
+ imap->nuid++;
+ break;
+ case Auth:
+ break;
+ }
+ if(imap->tag == 0)
+ return line;
+ break;
+ case '9': /* response to our message */
+ op = p;
+ if(p[1] == 'x' && strtoul(p + 2, &p, 10) == imap->tag){
+ while(*p == ' ')
+ p++;
+ imap->tag++;
+ return p;
+ }
+ eprint("imap: expected %lud; got %s\n", imap->tag, op);
+ break;
+ default:
+ if(imap->flags&Fdebug || *p){
+ eprint("imap: unexpected line: %s\n", p);
+ unexp = 1;
+ }
+ }
+ }
+ snprint(error, sizeof error, "i/o error: %r\n");
+ return error;
+}
+
+static char*
+imap4resp(Imap *i)
+{
+ return imap4resp0(i, 0, 0);
+}
+
+static int
+isokay(char *resp)
+{
+ return cistrncmp(resp, "OK", 2) == 0;
+}
+
+static char*
+findflag(int idx)
+{
+ int i;
+
+ for(i = 0; i < nelem(ftab); i++)
+ if(ftab[i].e == 1<<idx)
+ return ftab[i].flag;
+ return nil;
+}
+
+static void
+imap4modflags(Mailbox *mb, Message *m, int flags)
+{
+ char buf[128], *p, *e, *fs;
+ int i, f;
+ Imap *imap;
+
+ imap = mb->aux;
+ e = buf + sizeof buf;
+ p = buf;
+ f = flags & ~Frecent;
+ for(i = 0; i < Nflags; i++)
+ if(f & 1<<i && (fs = findflag(i)))
+ p = seprint(p, e, "%s ", fs);
+ if(p > buf){
+ p[-1] = 0;
+ imap4cmd(imap, "uid store %lud flags (%s)", (ulong)m->imapuid, buf);
+ imap4resp(imap);
+ }
+}
+
+static char*
+imap4cram(Imap *imap)
+{
+ char *s, *p, ch[128], usr[64], rbuf[128], ubuf[128], ebuf[192];
+ int i, n, l;
+
+ fmtinstall('[', encodefmt);
+
+ imap4cmd(imap, "authenticate cram-md5");
+ p = imap4resp(imap);
+ if(p == nil)
+ return "no challenge";
+ l = dec64((uchar*)ch, sizeof ch, p, strlen(p));
+ if(l == -1)
+ return "bad base64";
+ ch[l] = 0;
+ idprint(imap, "challenge [%s]\n", ch);
+
+ if(imap->user == nil)
+ imap->user = getlog();
+ n = auth_respond(ch, l, usr, sizeof usr, rbuf, sizeof rbuf, auth_getkey,
+ "proto=cram role=client server=%q user=%s", imap->host, imap->user);
+ if(n == -1)
+ return "cannot find IMAP password";
+ for(i = 0; i < n; i++)
+ if(rbuf[i] >= 'A' && rbuf[i] <= 'Z')
+ rbuf[i] += 'a' - 'A';
+ l = snprint(ubuf, sizeof ubuf, "%s %.*s", usr, n, rbuf);
+ idprint(imap, "raw cram [%s]\n", ubuf);
+ snprint(ebuf, sizeof ebuf, "%.*[", l, ubuf);
+
+ imap->tag = 1;
+ idprint(imap, "-> %s\n", ebuf);
+ Bprint(&imap->bout, "%s\r\n", ebuf);
+ Bflush(&imap->bout);
+
+ if(!isokay(s = imap4resp(imap)))
+ return s;
+ return nil;
+}
+
+/*
+ * authenticate to IMAP4 server using NTLM (untested)
+ *
+ * http://davenport.sourceforge.net/ntlm.html#ntlmImapAuthentication
+ * http://msdn.microsoft.com/en-us/library/cc236621%28PROT.13%29.aspx
+ */
+static uchar*
+psecb(uchar *p, uint o, int n)
+{
+ p[0] = n;
+ p[1] = n>>8;
+ p[2] = n;
+ p[3] = n>>8;
+ p[4] = o;
+ p[5] = o>>8;
+ p[6] = o>>16;
+ p[7] = o>>24;
+ return p+8;
+}
+
+static uchar*
+psecq(uchar *q, char *s, int n)
+{
+ memcpy(q, s, n);
+ return q+n;
+}
+
+static char*
+imap4ntlm(Imap *imap)
+{
+ char *s, ruser[64], enc[256];
+ uchar buf[128], *p, *ep, *q, *eq, *chal;
+ int n;
+ MSchapreply mcr;
+
+ imap4cmd(imap, "authenticate ntlm");
+ imap4resp(imap);
+
+ /* simple NtLmNegotiate blob with NTLM+OEM flags */
+ imap4cmd(imap, "TlRMTVNTUAABAAAAAgIAAA==");
+ s = imap4resp(imap);
+ n = dec64(buf, sizeof buf, s, strlen(s));
+ if(n < 32 || memcmp(buf, "NTLMSSP", 8) != 0)
+ return "bad NtLmChallenge";
+ chal = buf+24;
+
+ if(auth_respond(chal, 8, ruser, sizeof ruser,
+ &mcr, sizeof mcr, auth_getkey,
+ "proto=mschap role=client service=imap server=%q user?",
+ imap->host) < 0)
+ return "auth_respond failed";
+
+ /* prepare NtLmAuthenticate blob */
+
+ memset(buf, sizeof buf, 0);
+ p = buf;
+ ep = p + 8 + 6*8 + 2*4;
+ q = ep;
+ eq = buf + sizeof buf;
+
+
+ memcpy(p, "NTLMSSP", 8); /* magic */
+ p += 8;
+
+ *p++ = 3;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+
+ p = psecb(p, q-buf, 24); /* LMresp */
+ q = psecq(q, mcr.LMresp, 24);
+
+ p = psecb(p, q-buf, 24); /* NTresp */
+ q = psecq(q, mcr.NTresp, 24);
+
+ p = psecb(p, q-buf, 0); /* realm */
+
+ n = strlen(ruser);
+ p = psecb(p, q-buf, n); /* user name */
+ q = psecq(q, ruser, n);
+
+ p = psecb(p, q-buf, 0); /* workstation name */
+ p = psecb(p, q-buf, 0); /* session key */
+
+ *p++ = 0x02; /* flags: oem(2)|ntlm(0x200) */
+ *p++ = 0x02;
+ *p++ = 0;
+ *p++ = 0;
+
+ if(p > ep || q > eq)
+ return "error creating NtLmAuthenticate";
+ enc64(enc, sizeof enc, buf, q-buf);
+
+ imap4cmd(imap, enc);
+ if(!isokay(s = imap4resp(imap)))
+ return s;
+ return nil;
+}
+
+static char*
+imap4passwd(Imap *imap)
+{
+ char *s;
+ UserPasswd *up;
+
+ if(imap->user != nil)
+ up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
+ else
+ up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
+ if(up == nil)
+ return "cannot find IMAP password";
+
+ imap->tag = 1;
+ imap4cmd(imap, "login %Z %Z", up->user, up->passwd);
+ free(up);
+ if(!isokay(s = imap4resp(imap)))
+ return s;
+ return nil;
+}
+
+static char*
+imap4login(Imap *imap)
+{
+ char *e;
+
+ if(imap->cap & Ccram)
+ e = imap4cram(imap);
+ else if(imap->cap & Cntlm)
+ e = imap4ntlm(imap);
+ else
+ e = imap4passwd(imap);
+ if(e)
+ return e;
+ imap4cmd(imap, "select %Z", imap->mbox);
+ if(!isokay(e = imap4resp(imap)))
+ return e;
+ return nil;
+}
+
+static char*
+imaperrstr(char *host, char *port)
+{
+ char err[ERRMAX];
+ static char buf[256];
+
+ err[0] = 0;
+ errstr(err, sizeof err);
+ snprint(buf, sizeof buf, "%s/%s:%s", host, port, err);
+ return buf;
+}
+
+static int
+starttls(Imap *imap, TLSconn *tls)
+{
+ char buf[Pathlen];
+ uchar digest[SHA1dlen];
+ int sfd, fd;
+
+ memset(tls, 0, sizeof *tls);
+ sfd = tlsClient(imap->fd, tls);
+ if(sfd < 0){
+ werrstr("tlsClient: %r");
+ return -1;
+ }
+ if(tls->cert == nil || tls->certlen <= 0){
+ close(sfd);
+ werrstr("server did not provide TLS certificate");
+ return -1;
+ }
+ sha1(tls->cert, tls->certlen, digest, nil);
+ if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
+ close(sfd);
+ werrstr("server certificate %.*H not recognized",
+ SHA1dlen, digest);
+ return -1;
+ }
+ close(imap->fd);
+ imap->fd = sfd;
+
+ if(imap->flags & Fdebug){
+ snprint(buf, sizeof buf, "%s/ctl", tls->dir);
+ fd = open(buf, OWRITE);
+ fprint(fd, "debug");
+ close(fd);
+ }
+
+ return 1;
+}
+
+static void
+imap4disconnect(Imap *imap)
+{
+ if(imap->binit){
+ Bterm(&imap->bin);
+ Bterm(&imap->bout);
+ imap->binit = 0;
+ }
+ close(imap->fd);
+ imap->fd = -1;
+}
+
+char*
+capabilties(Imap *imap)
+{
+ char * err;
+
+ imap4cmd(imap, "capability");
+ imap4resp(imap);
+ err = imap4resp(imap);
+ if(isokay(err))
+ err = 0;
+ return err;
+}
+
+static char*
+imap4dial(Imap *imap)
+{
+ char *err, *port;
+ TLSconn conn;
+
+ if(imap->fd >= 0){
+ imap4cmd(imap, "noop");
+ if(isokay(imap4resp(imap)))
+ return nil;
+ imap4disconnect(imap);
+ }
+ if(imap->flags & Fssl)
+ port = "imaps";
+ else
+ port = "imap";
+ if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
+ return imaperrstr(imap->host, port);
+ if(imap->flags & Fssl && starttls(imap, &conn) == -1){
+ err = imaperrstr(imap->host, port);
+ free(conn.cert);
+ imap4disconnect(imap);
+ return err;
+ }
+ assert(imap->binit == 0);
+ Binit(&imap->bin, imap->fd, OREAD);
+ Binit(&imap->bout, imap->fd, OWRITE);
+ imap->binit = 1;
+
+ imap->tag = 0;
+ err = imap4resp(imap);
+ if(!isokay(err))
+ return "error in initial IMAP handshake";
+
+ if((err = capabilties(imap)) || (err = imap4login(imap))){
+ eprint("imap: err is %s\n", err);
+ imap4disconnect(imap);
+ return err;
+ }
+ return nil;
+}
+
+static void
+imap4hangup(Imap *imap)
+{
+ imap4cmd(imap, "logout");
+ imap4resp(imap);
+ imap4disconnect(imap);
+}
+
+/* gmail lies about message sizes */
+static ulong
+gmaildiscount(Message *m, uvlong o, ulong l)
+{
+ if((m->cstate&Cidx) == 0)
+ if(o + l == m->size)
+ return l + 100 + (o + l)/5;
+ return l;
+}
+
+static int
+imap4fetch(Mailbox *mb, Message *m, uvlong o, ulong l)
+{
+ Imap *imap;
+
+ imap = mb->aux;
+ if(imap->flags & Fgmail)
+ l = gmaildiscount(m, o, l);
+ idprint(imap, "uid fetch %lud (body.peek[]<%llud.%lud>)\n", (ulong)m->imapuid, o, l);
+ imap4cmd(imap, "uid fetch %lud (body.peek[]<%llud.%lud>)", (ulong)m->imapuid, o, l);
+ if(!isokay(imap4resp0(imap, mb, m))){
+ eprint("imap: imap fetch failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static uvlong
+datesec(Imap *imap, int i)
+{
+ int j;
+ uvlong v;
+ Fetchi *f;
+
+ f = imap->f;
+ v = (uvlong)f[i].dates << 8;
+
+ /* shifty; these sequences should be stable. */
+ for(j = i; j-- > 0; )
+ if(f[i].dates != f[j].dates)
+ break;
+ v |= i - (j + 1);
+ return v;
+}
+
+static void
+markdel(Mailbox *mb, Message *m, int doplumb)
+{
+ if(doplumb)
+ mailplumb(mb, m, 1);
+ m->inmbox = 0;
+ m->deleted = Disappear;
+}
+
+static int
+vcmp(vlong a, vlong b)
+{
+ a -= b;
+ if(a > 0)
+ return 1;
+ if(a < 0)
+ return -1;
+ return 0;
+}
+
+static int
+fetchicmp(Fetchi *f1, Fetchi *f2)
+{
+ return vcmp(f1->uid, f2->uid);
+}
+
+static int
+setsize(Mailbox *, Message *m, Fetchi *f)
+{
+ if(f->sizes >= Maxmsg)
+ return -1;
+// if(!gmailmbox(mb))
+ return m->size = f->sizes;
+}
+
+static char*
+imap4read(Imap *imap, Mailbox *mb, int doplumb, int *new)
+{
+ char *s;
+ int i, n, c, nnew, ndel;
+ Fetchi *f;
+ Message *m, **ll;
+
+ *new = 0;
+ if(time(0) - imap->lastread < 10)
+ return nil;
+ imap->lastread = time(0);
+ imap4cmd(imap, "status %Z (messages uidvalidity)", imap->mbox);
+ if(!isokay(s = imap4resp(imap)))
+ return s;
+
+ imap->nuid = 0;
+ imap->muid = imap->nmsg;
+ imap->f = erealloc(imap->f, imap->nmsg*sizeof imap->f[0]);
+ f = imap->f;
+ n = imap->nmsg;
+
+ if(imap->nmsg > 0){
+ imap4cmd(imap, "uid fetch 1:* (uid rfc822.size internaldate)");
+ if(!isokay(s = imap4resp(imap)))
+ return s;
+ }
+
+ qsort(f, n, sizeof f[0], (int(*)(void*, void*))fetchicmp);
+ nnew = ndel = 0;
+ ll = &mb->root->part;
+ for(i = 0; *ll || i < n; ){
+ c = -1;
+ if(i >= n)
+ c = 1;
+ else if(*ll){
+ if((*ll)->imapuid == 0)
+ (*ll)->imapuid = strtoull((*ll)->idxaux, 0, 0);
+ c = vcmp(f[i].uid, (*ll)->imapuid);
+ }
+ idprint(imap, "consider %U and %U -> %d\n", i<n? f[i].uid: 0, *ll? (*ll)->imapuid: 1, c);
+ if(c < 0){
+ /* new message */
+ idprint(imap, "new: %U (%U)\n", f[i].uid, *ll? (*ll)->imapuid: 0);
+ m = newmessage(mb->root);
+ m->inmbox = 1;
+ m->idxaux = smprint("%llud", f[i].uid);
+ m->imapuid = f[i].uid;
+ m->fileid = datesec(imap, i);
+ if(setsize(mb, m, f + i) < 0 || m->size >= Maxmsg){
+ /* message disappeared? unchain */
+ idprint(imap, "deleted → %r (%U)\n", m->imapuid);
+ logmsg(m, "disappeared");
+ if(doplumb)
+ mailplumb(mb, m, 1); /* redundant */
+ unnewmessage(mb, mb->root, m);
+ /* we're out of sync; here's were to signal that */
+ break;
+ }
+ nnew++;
+ logmsg(m, "new %s", m->idxaux);
+ m->next = *ll;
+ *ll = m;
+ ll = &m->next;
+ i++;
+ newcachehash(mb, m, doplumb);
+ putcache(mb, m);
+ }else if(c > 0){
+ /* deleted message; */
+ idprint(imap, "deleted: %U (%U)\n", i<n? f[i].uid: 0, *ll? (*ll)->imapuid: 0);
+ ndel++;
+ logmsg(*ll, "deleted");
+ markdel(mb, *ll, doplumb);
+ ll = &(*ll)->next;
+ }else{
+ //logmsg(*ll, "duplicate %s", d[i].name);
+ i++;
+ ll = &(*ll)->next;
+ }
+ }
+
+ *new = nnew;
+ return nil;
+}
+
+static void
+imap4delete(Mailbox *mb, Message *m)
+{
+ Imap *imap;
+
+ imap = mb->aux;
+ if((ulong)(m->imapuid>>32) == imap->validity){
+ imap4cmd(imap, "uid store %lud +flags (\\Deleted)", (ulong)m->imapuid);
+ imap4resp(imap);
+ imap4cmd(imap, "expunge");
+ imap4resp(imap);
+// if(!isokay(imap4resp(imap))
+// return -1;
+ }
+ m->inmbox = 0;
+}
+
+static char*
+imap4sync(Mailbox *mb, int doplumb, int *new)
+{
+ char *err;
+ Imap *imap;
+
+ imap = mb->aux;
+ if(err = imap4dial(imap))
+ goto out;
+ if((err = imap4read(imap, mb, doplumb, new)) == nil)
+ mb->d->atime = mb->d->mtime = time(0);
+out:
+ mb->waketime = time(0) + imap->refreshtime;
+ return err;
+}
+
+static char*
+imap4ctl(Mailbox *mb, int argc, char **argv)
+{
+ char *a, *b;
+ Imap *imap;
+
+ imap = mb->aux;
+ if(argc < 1)
+ return Eimap4ctl;
+
+ if(argc == 1 && strcmp(argv[0], "debug") == 0){
+ imap->flags ^= Fdebug;
+ return nil;
+ }
+ if(strcmp(argv[0], "thumbprint") == 0){
+ if(imap->thumb){
+ freeThumbprints(imap->thumb);
+ imap->thumb = 0;
+ }
+ a = "/sys/lib/tls/mail";
+ b = "/sys/lib/tls/mail.exclude";
+ switch(argc){
+ default:
+ return Eimap4ctl;
+ case 4:
+ b = argv[2];
+ case 3:
+ a = argv[1];
+ case 2:
+ break;
+ }
+ imap->thumb = initThumbprints(a, b);
+ return nil;
+ }
+ if(argc == 2 && strcmp(argv[0], "uid") == 0){
+ uvlong l;
+ Message *m;
+
+ for(m = mb->root->part; m; m = m->next)
+ if(strcmp(argv[1], m->name) == 0){
+ l = strtoull(m->idxaux, 0, 0);
+ fprint(2, "uid %s %lud %lud %lud %lud\n", m->name, (ulong)(l>>32), (ulong)l,
+ (ulong)(m->imapuid>>32), (ulong)m->imapuid);
+ }
+ return nil;
+ }
+ if(strcmp(argv[0], "refresh") == 0)
+ switch(argc){
+ case 1:
+ imap->refreshtime = 60;
+ return nil;
+ case 2:
+ imap->refreshtime = atoi(argv[1]);
+ return nil;
+ }
+
+ return Eimap4ctl;
+}
+
+static void
+imap4close(Mailbox *mb)
+{
+ Imap *imap;
+
+ imap = mb->aux;
+ imap4disconnect(imap);
+ free(imap->f);
+ free(imap);
+}
+
+static char*
+mkmbox(Imap *imap, char *p, char *e)
+{
+ p = seprint(p, e, "%s/box/%s/imap.%s", MAILROOT, getlog(), imap->host);
+ if(imap->user && strcmp(imap->user, getlog()))
+ p = seprint(p, e, ".%s", imap->user);
+ if(cistrcmp(imap->mbox, "inbox"))
+ p = seprint(p, e, ".%s", imap->mbox);
+ return p;
+}
+
+static char*
+findmbox(char *p)
+{
+ char *f[10], path[Pathlen];
+ int nf;
+
+ snprint(path, sizeof path, "%s", p);
+ nf = getfields(path, f, 5, 0, "/");
+ if(nf < 3)
+ return nil;
+ return f[nf - 1];
+}
+
+static char*
+imap4rename(Mailbox *mb, char *p2, int)
+{
+ char *r, *new;
+ Imap *imap;
+
+ imap = mb->aux;
+ new = findmbox(p2);
+ idprint(imap, "rename %s %s\n", imap->mbox, new);
+ imap4cmd(imap, "rename %s %s", imap->mbox, new);
+ r = imap4resp(imap);
+ if(!isokay(r))
+ return r;
+ free(imap->mbox);
+ imap->mbox = smprint("%s", new);
+ mkmbox(imap, mb->path, mb->path + sizeof mb->path);
+ return 0;
+}
+
+/*
+ * incomplete; when we say remove we want to get subfolders, too.
+ * so we need to to a list, and recursivly nuke folders.
+ */
+static char*
+imap4remove(Mailbox *mb, int flags)
+{
+ char *r;
+ Imap *imap;
+
+ imap = mb->aux;
+ idprint(imap, "remove %s\n", imap->mbox);
+ imap4cmd(imap, "delete %s", imap->mbox);
+ r = imap4resp(imap);
+ if(!isokay(r))
+ return r;
+ if(flags & Rtrunc){
+ imap4cmd(imap, "create %s", imap->mbox);
+ r = imap4resp(imap);
+ if(!isokay(r))
+ return r;
+ }
+ return 0;
+}
+
+char*
+imap4mbox(Mailbox *mb, char *path)
+{
+ char *f[10];
+ uchar flags;
+ int nf;
+ Imap *imap;
+
+ fmtinstall('Z', Zfmt);
+ fmtinstall('U', Ufmt);
+ if(strncmp(path, "/imap/", 6) == 0)
+ flags = 0;
+ else if(strncmp(path, "/imaps/", 7) == 0)
+ flags = Fssl;
+ else
+ return Enotme;
+
+ path = strdup(path);
+ if(path == nil)
+ return "out of memory";
+
+ nf = getfields(path, f, 5, 0, "/");
+ if(nf < 3){
+ free(path);
+ return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
+ }
+
+ imap = emalloc(sizeof *imap);
+ imap->fd = -1;
+ imap->freep = path;
+ imap->flags = flags;
+ imap->host = f[2];
+ if(strstr(imap->host, "gmail.com"))
+ imap->flags |= Fgmail;
+ imap->refreshtime = 60;
+ if(nf < 4)
+ imap->user = nil;
+ else
+ imap->user = f[3];
+ if(nf < 5)
+ imap->mbox = strdup("inbox");
+ else
+ imap->mbox = strdup(f[4]);
+ mkmbox(imap, mb->path, mb->path + sizeof mb->path);
+ if(imap->flags & Fssl)
+ imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
+
+ mb->aux = imap;
+ mb->sync = imap4sync;
+ mb->close = imap4close;
+ mb->ctl = imap4ctl;
+ mb->fetch = imap4fetch;
+ mb->delete = imap4delete;
+ mb->rename = imap4rename;
+// mb->remove = imap4remove;
+ mb->modflags = imap4modflags;
+ mb->d = emalloc(sizeof *mb->d);
+ mb->addfrom = 1;
+ return nil;
+}
diff --git a/sys/src/cmd/upas/fs/imap4.c b/sys/src/cmd/upas/fs/imap4.c
deleted file mode 100644
index 06e8fdd84..000000000
--- a/sys/src/cmd/upas/fs/imap4.c
+++ /dev/null
@@ -1,878 +0,0 @@
-#include "common.h"
-#include <ctype.h>
-#include <plumb.h>
-#include <libsec.h>
-#include <auth.h>
-#include "dat.h"
-
-#pragma varargck argpos imap4cmd 2
-#pragma varargck type "Z" char*
-
-int doublequote(Fmt*);
-
-// if pipeline == 1 and upas/fs is used with dovecot,
-// 9Xn OK responses sometimes come much later after FETCH responses, i.e.
-// <- * 1 FETCH ...
-// <- * 2 FETCH ...
-// <- * 3 FETCH ...
-// <- 9X5 OK Fetch completed.
-// <- 9X6 OK Fetch completed.
-// download 40: did not get message body
-// <- 9X7 OK Fetch completed.
-// causing multiple messages to turn into one in imap4.c:/^imap4resp.
-int pipeline = 0;
-
-static char Eio[] = "i/o error";
-
-typedef struct Imap Imap;
-struct Imap {
- char *freep; // free this to free the strings below
-
- char *host;
- char *user;
- char *mbox;
-
- int mustssl;
- int refreshtime;
- int debug;
-
- ulong tag;
- ulong validity;
- int nmsg;
- int size;
- char *base;
- char *data;
-
- vlong *uid;
- int nuid;
- int muid;
-
- Thumbprint *thumb;
-
- // open network connection
- Biobuf bin;
- Biobuf bout;
- int fd;
-};
-
-static char*
-removecr(char *s)
-{
- char *r, *w;
-
- for(r=w=s; *r; r++)
- if(*r != '\r')
- *w++ = *r;
- *w = '\0';
- return s;
-}
-
-//
-// send imap4 command
-//
-static void
-imap4cmd(Imap *imap, char *fmt, ...)
-{
- char buf[128], *p;
- va_list va;
-
- va_start(va, fmt);
- p = buf+sprint(buf, "9X%lud ", imap->tag);
- vseprint(p, buf+sizeof(buf), fmt, va);
- va_end(va);
-
- p = buf+strlen(buf);
- if(p > (buf+sizeof(buf)-3))
- sysfatal("imap4 command too long");
-
- if(imap->debug)
- fprint(2, "-> %s\n", buf);
- strcpy(p, "\r\n");
- Bwrite(&imap->bout, buf, strlen(buf));
- Bflush(&imap->bout);
-}
-
-enum {
- OK,
- NO,
- BAD,
- BYE,
- EXISTS,
- STATUS,
- FETCH,
- UNKNOWN,
-};
-
-static char *verblist[] = {
-[OK] "OK",
-[NO] "NO",
-[BAD] "BAD",
-[BYE] "BYE",
-[EXISTS] "EXISTS",
-[STATUS] "STATUS",
-[FETCH] "FETCH",
-};
-
-static int
-verbcode(char *verb)
-{
- int i;
- char *q;
-
- if(q = strchr(verb, ' '))
- *q = '\0';
-
- for(i=0; i<nelem(verblist); i++)
- if(verblist[i] && strcmp(verblist[i], verb)==0){
- if(q)
- *q = ' ';
- return i;
- }
- if(q)
- *q = ' ';
- return UNKNOWN;
-}
-
-static void
-strupr(char *s)
-{
- for(; *s; s++)
- if('a' <= *s && *s <= 'z')
- *s += 'A'-'a';
-}
-
-static void
-imapgrow(Imap *imap, int n)
-{
- int i;
-
- if(imap->data == nil){
- imap->base = emalloc(n+1);
- imap->data = imap->base;
- imap->size = n+1;
- }
- if(n >= imap->size){
- // friggin microsoft - reallocate
- i = imap->data - imap->base;
- imap->base = erealloc(imap->base, i+n+1);
- imap->data = imap->base + i;
- imap->size = n+1;
- }
-}
-
-
-//
-// get imap4 response line. there might be various
-// data or other informational lines mixed in.
-//
-static char*
-imap4resp(Imap *imap)
-{
- char *line, *p, *ep, *op, *q, *r, *en, *verb;
- int i, n;
- static char error[256];
-
- while(p = Brdline(&imap->bin, '\n')){
- ep = p+Blinelen(&imap->bin);
- while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
- *--ep = '\0';
-
- if(imap->debug)
- fprint(2, "<- %s\n", p);
- strupr(p);
-
- switch(p[0]){
- case '+':
- if(imap->tag == 0)
- fprint(2, "unexpected: %s\n", p);
- break;
-
- // ``unsolicited'' information; everything happens here.
- case '*':
- if(p[1]!=' ')
- continue;
- p += 2;
- line = p;
- n = strtol(p, &p, 10);
- if(*p==' ')
- p++;
- verb = p;
-
- if(p = strchr(verb, ' '))
- p++;
- else
- p = verb+strlen(verb);
-
- switch(verbcode(verb)){
- case OK:
- case NO:
- case BAD:
- // human readable text at p;
- break;
- case BYE:
- // early disconnect
- // human readable text at p;
- break;
-
- // * 32 EXISTS
- case EXISTS:
- imap->nmsg = n;
- break;
-
- // * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
- case STATUS:
- if(q = strstr(p, "MESSAGES"))
- imap->nmsg = atoi(q+8);
- if(q = strstr(p, "UIDVALIDITY"))
- imap->validity = strtoul(q+11, 0, 10);
- break;
-
- case FETCH:
- // * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
- // <3031 bytes of data>
- // )
- if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
- if((q = strchr(p, '{'))
- && (n=strtol(q+1, &en, 0), *en=='}')){
- if(imap->data == nil || n >= imap->size)
- imapgrow(imap, n);
- if((i = Bread(&imap->bin, imap->data, n)) != n){
- snprint(error, sizeof error,
- "short read %d != %d: %r\n",
- i, n);
- return error;
- }
- if(imap->debug)
- fprint(2, "<- read %d bytes\n", n);
- imap->data[n] = '\0';
- if(imap->debug)
- fprint(2, "<- %s\n", imap->data);
- imap->data += n;
- imap->size -= n;
- p = Brdline(&imap->bin, '\n');
- if(imap->debug)
- fprint(2, "<- ignoring %.*s\n",
- Blinelen(&imap->bin), p);
- }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
- *r = '\0';
- q++;
- n = r-q;
- if(imap->data == nil || n >= imap->size)
- imapgrow(imap, n);
- memmove(imap->data, q, n);
- imap->data[n] = '\0';
- imap->data += n;
- imap->size -= n;
- }else
- return "confused about FETCH response";
- break;
- }
-
- // * 1 FETCH (UID 1 RFC822.SIZE 511)
- if(q=strstr(p, "RFC822.SIZE")){
- imap->size = atoi(q+11);
- break;
- }
-
- // * 1 FETCH (UID 1 RFC822.HEADER {496}
- // <496 bytes of data>
- // )
- // * 1 FETCH (UID 1 RFC822.HEADER "data")
- if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
- if((q = strchr(p, '{'))
- && (n=strtol(q+1, &en, 0), *en=='}')){
- if(imap->data == nil || n >= imap->size)
- imapgrow(imap, n);
- if((i = Bread(&imap->bin, imap->data, n)) != n){
- snprint(error, sizeof error,
- "short read %d != %d: %r\n",
- i, n);
- return error;
- }
- if(imap->debug)
- fprint(2, "<- read %d bytes\n", n);
- imap->data[n] = '\0';
- if(imap->debug)
- fprint(2, "<- %s\n", imap->data);
- imap->data += n;
- imap->size -= n;
- p = Brdline(&imap->bin, '\n');
- if(imap->debug)
- fprint(2, "<- ignoring %.*s\n",
- Blinelen(&imap->bin), p);
- }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
- *r = '\0';
- q++;
- n = r-q;
- if(imap->data == nil || n >= imap->size)
- imapgrow(imap, n);
- memmove(imap->data, q, n);
- imap->data[n] = '\0';
- imap->data += n;
- imap->size -= n;
- }else
- return "confused about FETCH response";
- break;
- }
-
- // * 1 FETCH (UID 1)
- // * 2 FETCH (UID 6)
- if(q = strstr(p, "UID")){
- if(imap->nuid < imap->muid)
- imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
- break;
- }
- }
-
- if(imap->tag == 0)
- return line;
- break;
-
- case '9': // response to our message
- op = p;
- if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
- while(*p==' ')
- p++;
- imap->tag++;
- return p;
- }
- fprint(2, "expected %lud; got %s\n", imap->tag, op);
- break;
-
- default:
- if(imap->debug || *p)
- fprint(2, "unexpected line: %s\n", p);
- }
- }
- snprint(error, sizeof error, "i/o error: %r\n");
- return error;
-}
-
-static int
-isokay(char *resp)
-{
- return strncmp(resp, "OK", 2)==0;
-}
-
-//
-// log in to IMAP4 server, select mailbox, no SSL at the moment
-//
-static char*
-imap4login(Imap *imap)
-{
- char *s;
- UserPasswd *up;
-
- imap->tag = 0;
- s = imap4resp(imap);
- if(!isokay(s))
- return "error in initial IMAP handshake";
-
- if(imap->user != nil)
- up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
- else
- up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
- if(up == nil)
- return "cannot find IMAP password";
-
- imap->tag = 1;
- imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
- free(up);
- if(!isokay(s = imap4resp(imap)))
- return s;
-
- imap4cmd(imap, "SELECT %Z", imap->mbox);
- if(!isokay(s = imap4resp(imap)))
- return s;
-
- return nil;
-}
-
-static char*
-imaperrstr(char *host, char *port)
-{
- /*
- * make mess big enough to hold a TLS certificate fingerprint
- * plus quite a bit of slop.
- */
- static char mess[3 * Errlen];
- char err[Errlen];
-
- err[0] = '\0';
- errstr(err, sizeof(err));
- snprint(mess, sizeof(mess), "%s/%s:%s", host, port, err);
- return mess;
-}
-
-static int
-starttls(Imap *imap)
-{
- int sfd;
- uchar digest[SHA1dlen];
- TLSconn conn;
-
- memset(&conn, 0, sizeof(conn));
- sfd = tlsClient(imap->fd, &conn);
- if(sfd < 0) {
- werrstr("tlsClient: %r");
- return -1;
- }
- imap->fd = sfd;
- free(conn.sessionID);
- if(conn.cert==nil || conn.certlen <= 0) {
- werrstr("server did not provide TLS certificate");
- return -1;
- }
- sha1(conn.cert, conn.certlen, digest, nil);
- free(conn.cert);
- if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
- fmtinstall('H', encodefmt);
- werrstr("server certificate %.*H not recognized",
- SHA1dlen, digest);
- return -1;
- }
- return sfd;
-}
-
-//
-// dial and handshake with the imap server
-//
-static char*
-imap4dial(Imap *imap)
-{
- char *err, *port;
-
- if(imap->fd >= 0){
- imap4cmd(imap, "noop");
- if(isokay(imap4resp(imap)))
- return nil;
- close(imap->fd);
- imap->fd = -1;
- }
-
- if(imap->mustssl)
- port = "imaps";
- else
- port = "imap";
-
- if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
- return imaperrstr(imap->host, port);
-
- if(imap->mustssl){
- if(starttls(imap) < 0){
- err = imaperrstr(imap->host, port);
- goto Out;
- }
- }
- Binit(&imap->bin, imap->fd, OREAD);
- Binit(&imap->bout, imap->fd, OWRITE);
- err = imap4login(imap);
-Out:
- if(err != nil){
- if(imap->fd >= 0){
- close(imap->fd);
- imap->fd = -1;
- }
- }
- return err;
-}
-
-//
-// close connection
-//
-static void
-imap4hangup(Imap *imap)
-{
- if(imap->fd < 0)
- return;
- imap4cmd(imap, "LOGOUT");
- imap4resp(imap);
- close(imap->fd);
- imap->fd = -1;
-}
-
-//
-// download a single message
-//
-static char*
-imap4fetch(Mailbox *mb, Message *m)
-{
- int i;
- char *p, *s, sdigest[2*SHA1dlen+1];
- Imap *imap;
-
- imap = mb->aux;
-
- imap->size = 0;
-
- if(!isokay(s = imap4resp(imap)))
- return s;
-
- p = imap->base;
- if(p == nil)
- return "did not get message body";
-
- removecr(p);
- free(m->start);
- m->start = p;
- m->end = p+strlen(p);
- m->bend = m->rbend = m->end;
- m->header = m->start;
-
- imap->base = nil;
- imap->data = nil;
-
- parse(m, 0, mb, 1);
-
- // digest headers
- sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
- for(i = 0; i < SHA1dlen; i++)
- sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
- m->sdigest = s_copy(sdigest);
-
- return nil;
-}
-
-//
-// check for new messages on imap4 server
-// download new messages, mark deleted messages
-//
-static char*
-imap4read(Imap *imap, Mailbox *mb, int doplumb)
-{
- char *s;
- int i, ignore, nnew, t;
- Message *m, *next, **l;
-
- imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
- if(!isokay(s = imap4resp(imap)))
- return s;
-
- imap->nuid = 0;
- imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
- imap->muid = imap->nmsg;
-
- if(imap->nmsg > 0){
- imap4cmd(imap, "UID FETCH 1:* UID");
- if(!isokay(s = imap4resp(imap)))
- return s;
- }
-
- l = &mb->root->part;
- for(i=0; i<imap->nuid; i++){
- ignore = 0;
- while(*l != nil){
- if((*l)->imapuid == imap->uid[i]){
- ignore = 1;
- l = &(*l)->next;
- break;
- }else{
- // old mail, we don't have it anymore
- if(doplumb)
- mailplumb(mb, *l, 1);
- (*l)->inmbox = 0;
- (*l)->deleted = 1;
- l = &(*l)->next;
- }
- }
- if(ignore)
- continue;
-
- // new message
- m = newmessage(mb->root);
- m->mallocd = 1;
- m->inmbox = 1;
- m->imapuid = imap->uid[i];
-
- // add to chain, will download soon
- *l = m;
- l = &m->next;
- }
-
- // whatever is left at the end of the chain is gone
- while(*l != nil){
- if(doplumb)
- mailplumb(mb, *l, 1);
- (*l)->inmbox = 0;
- (*l)->deleted = 1;
- l = &(*l)->next;
- }
-
- // download new messages
- t = imap->tag;
- if(pipeline)
- switch(rfork(RFPROC|RFMEM)){
- case -1:
- sysfatal("rfork: %r");
- default:
- break;
- case 0:
- for(m = mb->root->part; m != nil; m = m->next){
- if(m->start != nil)
- continue;
- if(imap->debug)
- fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
- t, (ulong)m->imapuid);
- Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
- t++, (ulong)m->imapuid);
- }
- Bflush(&imap->bout);
- _exits(nil);
- }
-
- nnew = 0;
- for(m=mb->root->part; m!=nil; m=next){
- next = m->next;
- if(m->start != nil)
- continue;
-
- if(!pipeline){
- Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
- (ulong)imap->tag, (ulong)m->imapuid);
- Bflush(&imap->bout);
- }
-
- if(s = imap4fetch(mb, m)){
- // message disappeared? unchain
- fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
- delmessage(mb, m);
- mb->root->subname--;
- continue;
- }
- nnew++;
- if(doplumb)
- mailplumb(mb, m, 0);
- }
- if(pipeline)
- waitpid();
-
- if(nnew || mb->vers == 0){
- mb->vers++;
- henter(PATH(0, Qtop), mb->name,
- (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
- }
- return nil;
-}
-
-//
-// sync mailbox
-//
-static void
-imap4purge(Imap *imap, Mailbox *mb)
-{
- int ndel;
- Message *m, *next;
-
- ndel = 0;
- for(m=mb->root->part; m!=nil; m=next){
- next = m->next;
- if(m->deleted && m->refs==0){
- if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
- imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
- if(isokay(imap4resp(imap))){
- ndel++;
- delmessage(mb, m);
- }
- }else
- delmessage(mb, m);
- }
- }
-
- if(ndel){
- imap4cmd(imap, "EXPUNGE");
- imap4resp(imap);
- }
-}
-
-//
-// connect to imap4 server, sync mailbox
-//
-static char*
-imap4sync(Mailbox *mb, int doplumb)
-{
- char *err;
- Imap *imap;
-
- imap = mb->aux;
-
- if(err = imap4dial(imap)){
- mb->waketime = time(0) + imap->refreshtime;
- return err;
- }
-
- if((err = imap4read(imap, mb, doplumb)) == nil){
- imap4purge(imap, mb);
- mb->d->atime = mb->d->mtime = time(0);
- }
- /*
- * don't hang up; leave connection open for next time.
- */
- // imap4hangup(imap);
- mb->waketime = time(0) + imap->refreshtime;
- return err;
-}
-
-static char Eimap4ctl[] = "bad imap4 control message";
-
-static char*
-imap4ctl(Mailbox *mb, int argc, char **argv)
-{
- int n;
- Imap *imap;
-
- imap = mb->aux;
- if(argc < 1)
- return Eimap4ctl;
-
- if(argc==1 && strcmp(argv[0], "debug")==0){
- imap->debug = 1;
- return nil;
- }
-
- if(argc==1 && strcmp(argv[0], "nodebug")==0){
- imap->debug = 0;
- return nil;
- }
-
- if(argc==1 && strcmp(argv[0], "thumbprint")==0){
- if(imap->thumb)
- freeThumbprints(imap->thumb);
- imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
- }
- if(strcmp(argv[0], "refresh")==0){
- if(argc==1){
- imap->refreshtime = 60;
- return nil;
- }
- if(argc==2){
- n = atoi(argv[1]);
- if(n < 15)
- return Eimap4ctl;
- imap->refreshtime = n;
- return nil;
- }
- }
-
- return Eimap4ctl;
-}
-
-//
-// free extra memory associated with mb
-//
-static void
-imap4close(Mailbox *mb)
-{
- Imap *imap;
-
- imap = mb->aux;
- free(imap->freep);
- free(imap->base);
- free(imap->uid);
- if(imap->fd >= 0)
- close(imap->fd);
- free(imap);
-}
-
-//
-// open mailboxes of the form /imap/host/user
-//
-char*
-imap4mbox(Mailbox *mb, char *path)
-{
- char *f[10];
- int mustssl, nf;
- Imap *imap;
-
- quotefmtinstall();
- fmtinstall('Z', doublequote);
- if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
- return Enotme;
- mustssl = (strncmp(path, "/imaps/", 7) == 0);
-
- path = strdup(path);
- if(path == nil)
- return "out of memory";
-
- nf = getfields(path, f, 5, 0, "/");
- if(nf < 3){
- free(path);
- return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
- }
-
- imap = emalloc(sizeof(*imap));
- imap->fd = -1;
- imap->debug = debug;
- imap->freep = path;
- imap->mustssl = mustssl;
- imap->host = f[2];
- if(nf < 4)
- imap->user = nil;
- else
- imap->user = f[3];
- if(nf < 5)
- imap->mbox = "Inbox";
- else
- imap->mbox = f[4];
- imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
-
- mb->aux = imap;
- mb->sync = imap4sync;
- mb->close = imap4close;
- mb->ctl = imap4ctl;
- mb->d = emalloc(sizeof(*mb->d));
- //mb->fetch = imap4fetch;
-
- return nil;
-}
-
-//
-// Formatter for %"
-// Use double quotes to protect white space, frogs, \ and "
-//
-enum
-{
- Qok = 0,
- Qquote,
- Qbackslash,
-};
-
-static int
-needtoquote(Rune r)
-{
- if(r >= Runeself)
- return Qquote;
- if(r <= ' ')
- return Qquote;
- if(r=='\\' || r=='"')
- return Qbackslash;
- return Qok;
-}
-
-int
-doublequote(Fmt *f)
-{
- char *s, *t;
- int w, quotes;
- Rune r;
-
- s = va_arg(f->args, char*);
- if(s == nil || *s == '\0')
- return fmtstrcpy(f, "\"\"");
-
- quotes = 0;
- for(t=s; *t; t+=w){
- w = chartorune(&r, t);
- quotes |= needtoquote(r);
- }
- if(quotes == 0)
- return fmtstrcpy(f, s);
-
- fmtrune(f, '"');
- for(t=s; *t; t+=w){
- w = chartorune(&r, t);
- if(needtoquote(r) == Qbackslash)
- fmtrune(f, '\\');
- fmtrune(f, r);
- }
- return fmtrune(f, '"');
-}
diff --git a/sys/src/cmd/upas/fs/mbox.c b/sys/src/cmd/upas/fs/mbox.c
index 3cd14e6cd..3c2c1fb53 100644
--- a/sys/src/cmd/upas/fs/mbox.c
+++ b/sys/src/cmd/upas/fs/mbox.c
@@ -5,104 +5,188 @@
#include "dat.h"
typedef struct Header Header;
-
struct Header {
- char *type;
- void (*f)(Message*, Header*, char*);
- int len;
+ char *type;
+ uintptr offset;
+ char *(*f)(Message*, Header*, char*, char*);
+ int len;
+ int str;
};
/* headers */
-static void ctype(Message*, Header*, char*);
-static void cencoding(Message*, Header*, char*);
-static void cdisposition(Message*, Header*, char*);
-static void date822(Message*, Header*, char*);
-static void from822(Message*, Header*, char*);
-static void to822(Message*, Header*, char*);
-static void sender822(Message*, Header*, char*);
-static void replyto822(Message*, Header*, char*);
-static void subject822(Message*, Header*, char*);
-static void inreplyto822(Message*, Header*, char*);
-static void cc822(Message*, Header*, char*);
-static void bcc822(Message*, Header*, char*);
-static void messageid822(Message*, Header*, char*);
-static void mimeversion(Message*, Header*, char*);
-static void nullsqueeze(Message*);
-enum
-{
- Mhead= 11, /* offset of first mime header */
-};
+static char *ctype(Message*, Header*, char*, char*);
+static char *cencoding(Message*, Header*, char*, char*);
+static char *cdisposition(Message*, Header*, char*, char*);
+static char *from822(Message*, Header*, char*, char*);
+static char *replace822(Message*, Header*, char*, char*);
+static char *concat822(Message*, Header*, char*, char*);
+static char *copy822(Message*, Header*, char*, char*);
+static char *ref822(Message*, Header*, char*, char*);
-Header head[] =
+enum
{
- { "date:", date822, },
- { "from:", from822, },
- { "to:", to822, },
- { "sender:", sender822, },
- { "reply-to:", replyto822, },
- { "subject:", subject822, },
- { "cc:", cc822, },
- { "bcc:", bcc822, },
- { "in-reply-to:", inreplyto822, },
- { "mime-version:", mimeversion, },
- { "message-id:", messageid822, },
-
-[Mhead] { "content-type:", ctype, },
- { "content-transfer-encoding:", cencoding, },
- { "content-disposition:", cdisposition, },
- { 0, },
+ Mhead = 11, /* offset of first mime header */
};
-static void fatal(char *fmt, ...);
-static void initquoted(void);
-static void startheader(Message*);
-static void startbody(Message*);
-static char* skipwhite(char*);
-static char* skiptosemi(char*);
-static char* getstring(char*, String*, int);
-static void setfilename(Message*, char*);
-static char* lowercase(char*);
-static int is8bit(Message*);
-static int headerline(char**, String*);
-static void initheaders(void);
-static void parseattachments(Message*, Mailbox*);
-
-int debug;
-
-char *Enotme = "path not served by this file server";
-
-enum
+#define O(x) offsetof(Message, x)
+static Header head[] =
{
- Chunksize = 1024,
+ "date:", O(date822), copy822, 0, 0,
+ "from:", O(from), from822, 0, 1,
+ "to:", O(to), concat822, 0, 1,
+ "sender:", O(sender), replace822, 0, 1,
+ "reply-to:", O(replyto), replace822, 0, 1,
+ "subject:", O(subject), copy822, 0, 1,
+ "cc:", O(cc), concat822, 0, 1,
+ "bcc:", O(bcc), concat822, 0, 1,
+ "in-reply-to:", O(inreplyto), replace822, 0, 1,
+ "message-id:", O(messageid), replace822, 0, 1,
+ "references:", ~0, ref822, 0, 0,
+
+[Mhead] "content-type:", ~0, ctype, 0, 0,
+ "content-transfer-encoding:", ~0, cencoding, 0, 0,
+ "content-disposition:", ~0, cdisposition, 0, 0,
};
-Mailboxinit *boxinit[] = {
+static Mailboxinit *boxinit[] = {
imap4mbox,
pop3mbox,
- planbmbox,
- planbvmbox,
+ mdirmbox,
+// planbmbox,
plan9mbox,
};
+/*
+ * do we want to plumb flag changes?
+ */
char*
syncmbox(Mailbox *mb, int doplumb)
{
- return (*mb->sync)(mb, doplumb);
+ char *s;
+ int n, d, y, a;
+ Message *m, *next;
+
+ if(semacquire(&mb->syncsem, 0) <= 0)
+ return nil;
+ a = mb->root->subname;
+ if(rdidxfile(mb, doplumb) == -2)
+ wridxfile(mb);
+ if(s = mb->sync(mb, doplumb, &n)){
+ semrelease(&mb->syncsem, 1);
+ return s;
+ }
+ d = 0;
+ y = 0;
+ for(m = mb->root->part; m; m = next){
+ next = m->next;
+ if(m->cstate & Cidxstale)
+ y++;
+ if(m->deleted == 0 || m->refs > 0)
+ continue;
+ if(mb->delete && m->inmbox && m->deleted & Deleted)
+ mb->delete(mb, m);
+ if(!m->inmbox){
+ delmessage(mb, m);
+ d++;
+ }
+ }
+ a = mb->root->subname - a;
+ assert(a >= 0);
+ if(n + d + y + a){
+ iprint("deleted: %d; new %d; stale %d\n", d, n, y);
+ logmsg(nil, "deleted: %d; new %d; stale %d", d, n, y);
+ wridxfile(mb);
+ }
+ if(n + d + y + a){
+ mb->vers++;
+ henter(PATH(0, Qtop), mb->name,
+ (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
+ }
+ semrelease(&mb->syncsem, 1);
+ return nil;
}
-/* create a new mailbox */
+/*
+ * not entirely clear where the locking should take place, if
+ * it is required.
+ */
char*
-newmbox(char *path, char *name, int std)
+mboxrename(char *a, char *b, int flags)
+{
+ char f0[Pathlen + 4], f1[Pathlen + 4], *err, *p0, *p1;
+ Mailbox *mb;
+
+ snprint(f0, sizeof f0, "%s", a);
+ snprint(f1, sizeof f1, "%s", b);
+ err = newmbox(f0, nil, 0, &mb);
+ dprint("mboxrename %s %s -> %s\n", f0, f1, err);
+ if(!err && !mb->rename)
+ err = "rename not supported";
+ if(err)
+ goto done;
+ err = mb->rename(mb, f1, flags);
+ if(err)
+ goto done;
+ if(flags & Rtrunc)
+ /* we're comitted, so forget bailing */
+ err = newmbox(f0, nil, DMcreate, 0);
+ p0 = f0 + strlen(f0);
+ p1 = f1 + strlen(f1);
+
+ strcat(f0, ".idx");
+ strcat(f1, ".idx");
+ rename(f0, f1, 0);
+
+ *p0 = *p1 = 0;
+ strcat(f0, ".imp");
+ strcat(f1, ".imp");
+ rename(f0, f1, 0);
+
+ snprint(mb->path, sizeof mb->path, "%s", b);
+ hfree(PATH(0, Qtop), mb->name);
+ p0 = strrchr(mb->path, '/') + 1;
+ if(p0 == (char*)1)
+ p0 = mb->path;
+ snprint(mb->name, sizeof mb->name, "%s", p0);
+ henter(PATH(0, Qtop), mb->name,
+ (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
+done:
+ if(!mb)
+ return err;
+ qunlock(mb);
+// if(err)
+// mboxdecref(mb);
+ return err;
+}
+
+static void
+initheaders(void)
+{
+ int i;
+ static int already;
+
+ if(already)
+ return;
+ already = 1;
+ for(i = 0; i < nelem(head); i++)
+ head[i].len = strlen(head[i].type);
+}
+
+char*
+newmbox(char *path, char *name, int flags, Mailbox **r)
{
- Mailbox *mb, **l;
char *p, *rv;
int i;
+ Mailbox *mb, **l;
initheaders();
-
- mb = emalloc(sizeof(*mb));
- strncpy(mb->path, path, sizeof(mb->path)-1);
- if(name == nil){
+ mb = emalloc(sizeof *mb);
+ mb->idxsem = 1;
+ mb->syncsem = 1;
+ mb->flags = flags;
+ strncpy(mb->path, path, sizeof mb->path - 1);
+ p = name;
+ if(p == nil){
p = strrchr(path, '/');
if(p == nil)
p = path;
@@ -112,401 +196,539 @@ newmbox(char *path, char *name, int std)
free(mb);
return "bad mbox name";
}
- strncpy(mb->name, p, sizeof(mb->name)-1);
- } else {
- strncpy(mb->name, name, sizeof(mb->name)-1);
}
-
- rv = nil;
- // check for a mailbox type
- for(i=0; i<nelem(boxinit); i++)
- if((rv = (*boxinit[i])(mb, path)) != Enotme)
+ strncpy(mb->name, p, sizeof mb->name - 1);
+ mb->idxread = genericidxread;
+ mb->idxwrite = genericidxwrite;
+ mb->idxinvalid = genericidxinvalid;
+
+ /* check for a mailbox type */
+ rv = Enotme; /* can't happen; shut compiler up */
+ for(i = 0; i < nelem(boxinit); i++)
+ if((rv = boxinit[i](mb, path)) != Enotme)
break;
- if(i == nelem(boxinit)){
- free(mb);
- return "bad path";
- }
-
- // on error, give up
if(rv){
free(mb);
return rv;
}
- // make sure name isn't taken
+ /* make sure name isn't taken */
qlock(&mbllock);
- for(l = &mbl; *l != nil; l = &(*l)->next){
+ for(l = &mbl; *l != nil; l = &(*l)->next)
if(strcmp((*l)->name, mb->name) == 0){
if(strcmp(path, (*l)->path) == 0)
rv = nil;
else
rv = "mbox name in use";
if(mb->close)
- (*mb->close)(mb);
+ mb->close(mb);
free(mb);
qunlock(&mbllock);
return rv;
}
- }
- // always try locking
+ /* always try locking */
mb->dolock = 1;
-
mb->refs = 1;
mb->next = nil;
mb->id = newid();
mb->root = newmessage(nil);
- mb->std = std;
+ mb->mtree = avlcreate(mtreecmp);
+
*l = mb;
qunlock(&mbllock);
qlock(mb);
- if(mb->ctl){
+ henter(PATH(0, Qtop), mb->name,
+ (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
+ if(mb->ctl)
henter(PATH(mb->id, Qmbox), "ctl",
(Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
- }
rv = syncmbox(mb, 0);
- qunlock(mb);
+ if(r)
+ *r = mb;
+ else
+ qunlock(mb);
return rv;
}
-// close the named mailbox
+/* close the named mailbox */
void
freembox(char *name)
{
Mailbox **l, *mb;
qlock(&mbllock);
- for(l=&mbl; *l != nil; l=&(*l)->next){
+ for(l=&mbl; *l != nil; l=&(*l)->next)
if(strcmp(name, (*l)->name) == 0){
mb = *l;
*l = mb->next;
mboxdecref(mb);
break;
}
- }
hfree(PATH(0, Qtop), name);
qunlock(&mbllock);
}
-static void
-initheaders(void)
+void
+syncallmboxes(void)
{
- Header *h;
- static int already;
+ char *err;
+ Mailbox *m;
- if(already)
- return;
- already = 1;
+ qlock(&mbllock);
+ for(m = mbl; m != nil; m = m->next)
+ if(err = syncmbox(m, 1))
+ eprint("syncmbox: %s\n", err);
+ qunlock(&mbllock);
+}
+
+
+char*
+removembox(char *name, int flags)
+{
+ int found;
+ Mailbox **l, *mb;
- for(h = head; h->type != nil; h++)
- h->len = strlen(h->type);
+ found = 0;
+ qlock(&mbllock);
+ for(l=&mbl; *l != nil; l=&(*l)->next)
+ if(strcmp(name, (*l)->path) == 0){
+ mb = *l;
+ *l = mb->next;
+ mb->flags |= ORCLOSE;
+ mb->rmflags = flags;
+ mboxdecref(mb);
+ found = 1;
+ break;
+ }
+ hfree(PATH(0, Qtop), name);
+ qunlock(&mbllock);
+
+ if(found == 0)
+ return "maibox not found";
+ return 0;
}
/*
- * parse a Unix style header
+ * look for the date in the first Received: line.
+ * it's likely to be the right time zone (it's
+ * the local system) and in a convenient format.
*/
-void
-parseunix(Message *m)
+static int
+rxtotm(Message *m, Tm *tm)
{
- char *p;
- String *h;
+ char *p, *q;
+ int r;
+
+ if(cistrncmp(m->header, "received:", 9))
+ return -1;
+ q = strchr(m->header, ';');
+ if(!q)
+ return -1;
+ p = q;
+ while((p = strchr(p, '\n')) != nil){
+ if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
+ break;
+ p++;
+ }
+ if(!p)
+ return -1;
+ *p = '\0';
+ r = strtotm(q + 1, tm);
+ *p = '\n';
+ return r;
+}
- h = s_new();
- for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
- s_putc(h, *p);
- s_terminate(h);
- s_restart(h);
+Message*
+gettopmsg(Mailbox *mb, Message *m)
+{
+ while(!Topmsg(mb, m))
+ m = m->whole;
+ return m;
+}
- m->unixfrom = s_parse(h, s_reset(m->unixfrom));
- m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
+void
+datesec(Mailbox *mb, Message *m)
+{
+ char *s;
+ vlong v;
+ Tm tm;
- s_free(h);
+ if(m->fileid > 1000000ull<<8)
+ return;
+ if(m->unixfrom && strtotm(m->unixfrom, &tm) >= 0)
+ v = tm2sec(&tm);
+ else if(m->date822 && strtotm(m->date822, &tm) >= 0)
+ v = tm2sec(&tm);
+ else if(rxtotm(m, &tm) >= 0)
+ v = tm2sec(&tm);
+ else{
+ s = rtab[m->type].s;
+ logmsg(gettopmsg(mb, m), "%s:%s: datasec %s %s\n", mb->path,
+ m->whole? m->whole->name: "?",
+ m->name, s);
+ if(Topmsg(mb, m) || strcmp(s, "message/rfc822") == 0)
+ abort();
+ v = 0;
+ }
+ m->fileid = v<<8;
}
/*
* parse a message
*/
-void
-parseheaders(Message *m, int justmime, Mailbox *mb, int addfrom)
+extern void sanemsg(Message*);
+extern void sanembmsg(Mailbox*, Message*);
+
+static Message*
+haschild(Message *m, int i)
{
- String *hl;
- Header *h;
- char *p, *q;
+ for(m = m->part; m && i; i--)
+ m = m->next;
+ if(m)
+ m->mimeflag = 0;
+ return m;
+}
+
+static void
+parseattachments(Message *m, Mailbox *mb)
+{
+ char *p, *x;
int i;
+ Message *nm, **l;
- if(m->whole == m->whole->whole){
- henter(PATH(mb->id, Qmbox), m->name,
- (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
- } else {
- henter(PATH(m->whole->id, Qdir), m->name,
- (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
- }
- for(i = 0; i < Qmax; i++)
- henter(PATH(m->id, Qdir), dirtab[i],
- (Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
+ /* if there's a boundary, recurse... */
+sanemsg(m);
+// dprint("parseattachments %p %ld\n", m->start, m->end - m->start);
+ if(m->boundary != nil){
+ p = m->body;
+ nm = nil;
+ l = &m->part;
+ for(i = 0;;){
+sanemsg(m);
+ x = strstr(p, m->boundary);
+ /* two sequential boundaries; ignore nil message */
+ if(nm && x == p){
+ p = strchr(x, '\n');
+ if(p == nil){
+ nm->rbend = nm->bend = nm->end = x;
+ sanemsg(nm);
+ break;
+ }
+ p = p + 1;
+ continue;
+ }
+ /* no boundary, we're done */
+ if(x == nil){
+ if(nm != nil){
+ nm->rbend = nm->bend = nm->end = m->bend;
+sanemsg(nm);
+ if(nm->end == nm->start)
+ nm->mimeflag |= Mtrunc;
+ }
+ break;
+ }
+ /* boundary must be at the start of a line */
+ if(x != m->body && x[-1] != '\n'){
+ p = x + 1;
+ continue;
+ }
- // parse mime headers
- p = m->header;
- hl = s_new();
- while(headerline(&p, hl)){
- if(justmime)
- h = &head[Mhead];
- else
- h = head;
- for(; h->type; h++){
- if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
- (*h->f)(m, h, s_to_c(hl));
+ if(nm != nil)
+{
+ nm->rbend = nm->bend = nm->end = x;
+sanemsg(nm);}
+ x += strlen(m->boundary);
+
+ /* is this the last part? ignore anything after it */
+ if(strncmp(x, "--", 2) == 0)
+ break;
+ p = strchr(x, '\n');
+ if(p == nil)
break;
+ if((nm = haschild(m, i++)) == nil){
+ nm = newmessage(m);
+ *l = nm;
+ l = &nm->next;
}
+ nm->start = ++p;
+ assert(nm->ballocd == 0);
+ nm->mheader = nm->header = nm->body = nm->rbody = nm->start;
+ }
+ for(nm = m->part; nm != nil; nm = nm->next){
+ parse(mb, nm, 0, 1);
+ cachehash(mb, nm); /* botchy place for this */
}
- s_reset(hl);
+ return;
}
- s_free(hl);
- // the blank line isn't really part of the body or header
+ /* if we've got an rfc822 message, recurse... */
+ if(strcmp(rtab[m->type].s, "message/rfc822") == 0){
+ if((nm = haschild(m, 0)) == nil){
+ nm = newmessage(m);
+ m->part = nm;
+ }
+ assert(nm->ballocd == 0);
+ nm->start = nm->header = nm->body = nm->rbody = m->body;
+ nm->end = nm->bend = nm->rbend = m->bend;
+ if(nm->end == nm->start)
+ nm->mimeflag |= Mtrunc;
+ parse(mb, nm, 0, 0);
+ cachehash(mb, nm); /* botchy place for this */
+ }
+}
+
+void
+parseheaders(Mailbox *mb, Message *m, int addfrom, int justmime)
+{
+ char *p, *e, *o, *t, *s;
+ int i, i0, n;
+ uintptr a;
+
+/*sanembmsg(mb, m); /* fails with pop but i want this debugging for now */
+
+ /* parse mime headers */
+ p = m->header;
+ i0 = 0;
+ if(justmime)
+ i0 = Mhead;
+ s = emalloc(2048);
+ e = s + 2048 - 1;
+ while((n = hdrlen(p, m->end)) != 0){
+ if(n > e - s){
+ s = erealloc(s, n);
+ e = s + n - 1;
+ }
+ rfc2047(s, e, p, n, 1);
+ p += n;
+
+ for(i = i0; i < nelem(head); i++)
+ if(!cistrncmp(s, head[i].type, head[i].len)){
+ a = head[i].offset;
+ if(a != ~0){
+ if(o = *(char**)((char*)m + a))
+ continue;
+ t = head[i].f(m, head + i, o, s);
+ *(char**)((char*)m + a) = t;
+ }else
+ head[i].f(m, head + i, 0, s);
+ break;
+ }
+ }
+ free(s);
+/*sanembmsg(mb, m); /* fails with pop but i want this debugging for now */
+ /* the blank line isn't really part of the body or header */
if(justmime){
m->mhend = p;
m->hend = m->header;
- } else {
+ } else{
m->hend = p;
+ m->mhend = m->header;
}
+ /*
+ * not all attachments have mime headers themselves.
+ */
+ if(!m->mheader)
+ m->mhend = 0;
if(*p == '\n')
p++;
m->rbody = m->body = p;
- // if type is text, get any nulls out of the body. This is
- // for the two seans and imap clients that get confused.
- if(strncmp(s_to_c(m->type), "text/", 5) == 0)
- nullsqueeze(m);
-
- //
- // cobble together Unix-style from line
- // for local mailbox messages, we end up recreating the
- // original header.
- // for pop3 messages, the best we can do is
- // use the From: information and the RFC822 date.
- //
- if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0
- || strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){
- if(m->unixdate){
- s_free(m->unixdate);
- m->unixdate = nil;
- }
- // look for the date in the first Received: line.
- // it's likely to be the right time zone (it's
- // the local system) and in a convenient format.
- if(cistrncmp(m->header, "received:", 9)==0){
- if((q = strchr(m->header, ';')) != nil){
- p = q;
- while((p = strchr(p, '\n')) != nil){
- if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
- break;
- p++;
- }
- if(p){
- *p = '\0';
- m->unixdate = date822tounix(q+1);
- *p = '\n';
- }
- }
- }
-
- // fall back on the rfc822 date
- if(m->unixdate==nil && m->date822)
- m->unixdate = date822tounix(s_to_c(m->date822));
- }
-
- if(m->unixheader != nil)
- s_free(m->unixheader);
-
- // only fake header for top-level messages for pop3 and imap4
- // clients (those protocols don't include the unix header).
- // adding the unix header all the time screws up mime-attached
- // rfc822 messages.
- if(!addfrom && !m->unixfrom){
+ if(!justmime)
+ datesec(mb, m);
+
+ /*
+ * only fake header for top-level messages for pop3 and imap4
+ * clients (those protocols don't include the unix header).
+ * adding the unix header all the time screws up mime-attached
+ * rfc822 messages.
+ */
+/*sanembmsg(mb, m); /* fails with pop but i want this debugging for now */
+ if(!addfrom && !m->unixfrom)
m->unixheader = nil;
- return;
+ else if(m->unixheader == nil){
+ if(m->unixfrom && strcmp(m->unixfrom, "???") != 0)
+ p = m->unixfrom;
+ else if(m->from)
+ p = m->from;
+ else
+ p = "???";
+ m->unixheader = smprint("From %s %Δ\n", p, m->fileid);
}
-
- m->unixheader = s_copy("From ");
- if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0)
- s_append(m->unixheader, s_to_c(m->unixfrom));
- else if(m->from822)
- s_append(m->unixheader, s_to_c(m->from822));
- else
- s_append(m->unixheader, "???");
-
- s_append(m->unixheader, " ");
- if(m->unixdate)
- s_append(m->unixheader, s_to_c(m->unixdate));
- else
- s_append(m->unixheader, "Thu Jan 1 00:00:00 GMT 1970");
-
- s_append(m->unixheader, "\n");
+ m->cstate |= Cheader;
+sanembmsg(mb, m);
}
-String*
-promote(String **sp)
+char*
+promote(char *s)
{
- String *s;
-
- if(*sp != nil)
- s = s_clone(*sp);
- else
- s = nil;
- return s;
+ return s? strdup(s): nil;
}
void
parsebody(Message *m, Mailbox *mb)
{
+ char *s;
+ int l;
Message *nm;
- // recurse
- if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
+ /* recurse */
+ s = rtab[m->type].s;
+ l = rtab[m->type].l;
+ if(l >= 10 && strncmp(s, "multipart/", 10) == 0)
parseattachments(m, mb);
- } else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
+ else if(l == 14 && strcmp(s, "message/rfc822") == 0){
decode(m);
parseattachments(m, mb);
nm = m->part;
- // promote headers
- if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
- m->from822 = promote(&nm->from822);
- m->to822 = promote(&nm->to822);
- m->date822 = promote(&nm->date822);
- m->sender822 = promote(&nm->sender822);
- m->replyto822 = promote(&nm->replyto822);
- m->subject822 = promote(&nm->subject822);
- m->unixdate = promote(&nm->unixdate);
+ /* promote headers */
+ if(m->replyto == nil && m->from == nil && m->sender == nil){
+ m->from = promote(nm->from);
+ m->to = promote(nm->to);
+ m->date822 = promote(nm->date822);
+ m->sender = promote(nm->sender);
+ m->replyto = promote(nm->replyto);
+ m->subject = promote(nm->subject);
}
- }
+ }else if(strncmp(rtab[m->type].s, "text/", 5) == 0)
+ sanemsg(m);
+ m->rawbsize = m->rbend - m->rbody;
+ m->cstate |= Cbody;
}
void
-parse(Message *m, int justmime, Mailbox *mb, int addfrom)
+parse(Mailbox *mb, Message *m, int addfrom, int justmime)
{
- parseheaders(m, justmime, mb, addfrom);
+ sanemsg(m);
+ assert(m->end - m->start > 0 || m->mimeflag&Mtrunc && m->end - m->start == 0);
+ if((m->cstate & Cheader) == 0)
+ parseheaders(mb, m, addfrom, justmime);
parsebody(m, mb);
+ sanemsg(m);
}
-static void
-parseattachments(Message *m, Mailbox *mb)
+static char*
+skipwhite(char *p)
{
- Message *nm, **l;
- char *p, *x;
-
- // if there's a boundary, recurse...
- if(m->boundary != nil){
- p = m->body;
- nm = nil;
- l = &m->part;
- for(;;){
- x = strstr(p, s_to_c(m->boundary));
-
- /* no boundary, we're done */
- if(x == nil){
- if(nm != nil)
- nm->rbend = nm->bend = nm->end = m->bend;
- break;
- }
+ while(isascii(*p) && isspace(*p))
+ p++;
+ return p;
+}
- /* boundary must be at the start of a line */
- if(x != m->body && *(x-1) != '\n'){
- p = x+1;
- continue;
- }
+static char*
+skiptosemi(char *p)
+{
+ while(*p && *p != ';')
+ p++;
+ while(*p == ';' || (isascii(*p) && isspace(*p)))
+ p++;
+ return p;
+}
- if(nm != nil)
- nm->rbend = nm->bend = nm->end = x;
- x += strlen(s_to_c(m->boundary));
+static char*
+getstring(char *p, char *s, char *e, int dolower)
+{
+ int c;
- /* is this the last part? ignore anything after it */
- if(strncmp(x, "--", 2) == 0)
+ p = skipwhite(p);
+ if(*p == '"'){
+ for(p++; (c = *p) != '"'; p++){
+ if(c == '\\')
+ c = *++p;
+ /*
+ * 821 says <x> after \ can be anything at all.
+ * we just don't care.
+ */
+ if(c == 0)
break;
-
- p = strchr(x, '\n');
- if(p == nil)
+ if(c < ' ')
+ continue;
+ if(dolower && c >= 'A' && c <= 'Z')
+ c += 0x20;
+ s = sputc(s, e, c);
+ }
+ if(*p == '"')
+ p++;
+ }else{
+ for(; (c = *p) && !isspace(c) && c != ';'; p++){
+ if(c == '\\')
+ c = *++p;
+ /*
+ * 821 says <x> after \ can be anything at all.
+ * we just don't care.
+ */
+ if(c == 0)
break;
- nm = newmessage(m);
- nm->start = nm->header = nm->body = nm->rbody = ++p;
- nm->mheader = nm->header;
- *l = nm;
- l = &nm->next;
+ if(c < ' ')
+ continue;
+ if(dolower && c >= 'A' && c <= 'Z')
+ c += 0x20;
+ s = sputc(s, e, c);
}
- for(nm = m->part; nm != nil; nm = nm->next)
- parse(nm, 1, mb, 0);
- return;
- }
-
- // if we've got an rfc822 message, recurse...
- if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
- nm = newmessage(m);
- m->part = nm;
- nm->start = nm->header = nm->body = nm->rbody = m->body;
- nm->end = nm->bend = nm->rbend = m->bend;
- parse(nm, 0, mb, 0);
}
+ *s = 0;
+ return p;
}
-/*
- * pick up a header line
- */
-static int
-headerline(char **pp, String *hl)
+static void
+setfilename(Message *m, char *p)
{
- char *p, *x;
+ char buf[Pathlen];
- s_reset(hl);
- p = *pp;
- x = strpbrk(p, ":\n");
- if(x == nil || *x == '\n')
- return 0;
- for(;;){
- x = strchr(p, '\n');
- if(x == nil)
- x = p + strlen(p);
- s_nappend(hl, p, x-p);
- p = x;
- if(*p != '\n' || *++p != ' ' && *p != '\t')
- break;
- while(*p == ' ' || *p == '\t')
- p++;
- s_putc(hl, ' ');
- }
- *pp = p;
- return 1;
+ free(m->filename);
+ getstring(p, buf, buf + sizeof buf - 1, 0);
+ m->filename = smprint("%s", buf);
+ for(p = m->filename; *p; p++)
+ if(*p == ' ' || *p == '\t' || *p == ';')
+ *p = '_';
}
-/* returns nil iff there are no addressees */
-static String*
-addr822(char *p)
+static char*
+rtrim(char *p)
{
- String *s, *list;
- int incomment, addrdone, inanticomment, quoted;
- int n;
- int c;
+ char *e;
- list = s_new();
- s = s_new();
- quoted = incomment = addrdone = inanticomment = 0;
- n = 0;
- for(; *p; p++){
- c = *p;
+ if(p == 0)
+ return p;
+ e = p + strlen(p) - 1;
+ while(e > p && isascii(*e) && isspace(*e))
+ *e-- = 0;
+ return p;
+}
- // whitespace is ignored
+static char*
+addr822(char *p, char **ac)
+{
+ int n, c, space, incomment, addrdone, inanticomment, quoted;
+ char s[128+1], *ps, *e, *x, *list;
+
+ list = 0;
+ s[0] = 0;
+ ps = s;
+ e = s + sizeof s;
+ space = quoted = incomment = addrdone = inanticomment = 0;
+ n = 0;
+ for(; c = *p; p++){
+ if(!inanticomment && !quoted && !space && ps != s && c == ' '){
+ ps = sputc(ps, e, c);
+ space = 1;
+ continue;
+ }
+ space = 0;
if(!quoted && isspace(c) || c == '\r')
continue;
-
- // strings are always treated as atoms
+ /* strings are always treated as atoms */
if(!quoted && c == '"'){
- if(!addrdone && !incomment)
- s_putc(s, c);
- for(p++; *p; p++){
+ if(!addrdone && !incomment && !ac)
+ ps = sputc(ps, e, c);
+ for(p++; c = *p; p++){
+ if(ac && c == '"')
+ break;
if(!addrdone && !incomment)
- s_putc(s, *p);
+ ps = sputc(ps, e, c);
if(!quoted && *p == '"')
break;
if(*p == '\\')
@@ -514,13 +736,13 @@ addr822(char *p)
else
quoted = 0;
}
- if(*p == 0)
+ if(c == 0)
break;
quoted = 0;
continue;
}
- // ignore everything in an expicit comment
+ /* ignore everything in an expicit comment */
if(!quoted && c == '('){
incomment = 1;
continue;
@@ -532,10 +754,21 @@ addr822(char *p)
continue;
}
- // anticomments makes everything outside of them comments
+ /* anticomments makes everything outside of them comments */
if(!quoted && c == '<' && !inanticomment){
+ if(ac){
+ *ps-- = 0;
+ if(ps > s && *ps == ' ')
+ *ps = 0;
+ if(*ac){
+ *ac = smprint("%s, %s", x=*ac, s);
+ free(x);
+ }else
+ *ac = smprint("%s", s);
+ }
+
inanticomment = 1;
- s = s_reset(s);
+ ps = s;
continue;
}
if(!quoted && c == '>' && inanticomment){
@@ -544,21 +777,23 @@ addr822(char *p)
continue;
}
- // commas separate addresses
+ /* commas separate addresses */
if(!quoted && c == ',' && !inanticomment){
- s_terminate(s);
+ *ps = 0;
addrdone = 0;
- if(n++ != 0)
- s_append(list, " ");
- s_append(list, s_to_c(s));
- s = s_reset(s);
+ if(n++ != 0){
+ list = smprint("%s %s", x=list, s);
+ free(x);
+ }else
+ list = smprint("%s", s);
+ ps = s;
continue;
}
- // what's left is part of the address
- s_putc(s, c);
+ /* what's left is part of the address */
+ ps = sputc(ps, e, c);
- // quoted characters are recognized only as characters
+ /* quoted characters are recognized only as characters */
if(c == '\\')
quoted = 1;
else
@@ -566,19 +801,15 @@ addr822(char *p)
}
- if(*s_to_c(s) != 0){
- s_terminate(s);
- if(n++ != 0)
- s_append(list, " ");
- s_append(list, s_to_c(s));
+ if(ps > s){
+ *ps = 0;
+ if(n != 0){
+ list = smprint("%s %s", x=list, s);
+ free(x);
+ }else
+ list = smprint("%s", s);
}
- s_free(s);
-
- if(n == 0){ /* no addressees given, just the keyword */
- s_free(list);
- return nil;
- }
- return list;
+ return rtrim(list);
}
/*
@@ -586,138 +817,95 @@ addr822(char *p)
* concatenating their values.
*/
-static void
-to822(Message *m, Header *h, char *p)
-{
- String *s;
-
- p += strlen(h->type);
- s = addr822(p);
- if (m->to822 == nil)
- m->to822 = s;
- else if (s != nil) {
- s_append(m->to822, " ");
- s_append(m->to822, s_to_c(s));
- s_free(s);
- }
-}
-
-static void
-cc822(Message *m, Header *h, char *p)
-{
- String *s;
-
- p += strlen(h->type);
- s = addr822(p);
- if (m->cc822 == nil)
- m->cc822 = s;
- else if (s != nil) {
- s_append(m->cc822, " ");
- s_append(m->cc822, s_to_c(s));
- s_free(s);
- }
-}
-
-static void
-bcc822(Message *m, Header *h, char *p)
-{
- String *s;
-
- p += strlen(h->type);
- s = addr822(p);
- if (m->bcc822 == nil)
- m->bcc822 = s;
- else if (s != nil) {
- s_append(m->bcc822, " ");
- s_append(m->bcc822, s_to_c(s));
- s_free(s);
- }
-}
-
-static void
-from822(Message *m, Header *h, char *p)
+static char*
+concat822(Message*, Header *h, char *o, char *p)
{
- p += strlen(h->type);
- s_free(m->from822);
- m->from822 = addr822(p);
-}
+ char *s, *n;
-static void
-sender822(Message *m, Header *h, char *p)
-{
p += strlen(h->type);
- s_free(m->sender822);
- m->sender822 = addr822(p);
+ s = addr822(p, 0);
+ if(o){
+ n = smprint("%s %s", o, s);
+ free(s);
+ }else
+ n = s;
+ return n;
}
-static void
-replyto822(Message *m, Header *h, char *p)
-{
- p += strlen(h->type);
- s_free(m->replyto822);
- m->replyto822 = addr822(p);
-}
-
-static void
-mimeversion(Message *m, Header *h, char *p)
-{
- p += strlen(h->type);
- s_free(m->mimeversion);
- m->mimeversion = addr822(p);
-}
-
-static void
-killtrailingwhite(char *p)
+static char*
+from822(Message *m, Header *h, char*, char *p)
{
- char *e;
-
- e = p + strlen(p) - 1;
- while(e > p && isspace(*e))
- *e-- = 0;
+ if(m->ffrom)
+ free(m->ffrom);
+ m->from = 0;
+ return addr822(p + h->len, &m->ffrom);
}
-static void
-date822(Message *m, Header *h, char *p)
+static char*
+replace822(Message *, Header *h, char*, char *p)
{
- p += strlen(h->type);
- p = skipwhite(p);
- s_free(m->date822);
- m->date822 = s_copy(p);
- p = s_to_c(m->date822);
- killtrailingwhite(p);
+ return addr822(p + h->len, 0);
}
-static void
-subject822(Message *m, Header *h, char *p)
+static char*
+copy822(Message*, Header *h, char*, char *p)
{
- p += strlen(h->type);
- p = skipwhite(p);
- s_free(m->subject822);
- m->subject822 = s_copy(p);
- p = s_to_c(m->subject822);
- killtrailingwhite(p);
+ return rtrim(strdup(skipwhite(p + h->len)));
}
-static void
-inreplyto822(Message *m, Header *h, char *p)
+/*
+ * firefox, e.g. doesn't keep references unique
+ */
+static int
+uniqarray(char **a, int n, int allocd)
{
- p += strlen(h->type);
- p = skipwhite(p);
- s_free(m->inreplyto822);
- m->inreplyto822 = s_copy(p);
- p = s_to_c(m->inreplyto822);
- killtrailingwhite(p);
+ int i, j;
+
+ for(i = 0; i < n; i++)
+ for(j = i + 1; j < n; j++)
+ if(strcmp(a[i], a[j]) == 0){
+ if(allocd)
+ free(a[j]);
+ memmove(a + j, a + j + 1, sizeof *a*(n - (j + 1)));
+ a[--n] = 0;
+ }
+ return n;
}
-static void
-messageid822(Message *m, Header *h, char *p)
+static char*
+ref822(Message *m, Header *h, char*, char *p)
{
- p += strlen(h->type);
- p = skipwhite(p);
- s_free(m->messageid822);
- m->messageid822 = s_copy(p);
- p = s_to_c(m->messageid822);
- killtrailingwhite(p);
+ char **a, *s, *f[Nref + 1];
+ int i, j, k, n;
+
+ s = strdup(skipwhite(p + h->len));
+ n = getfields(s, f, nelem(f), 1, "<> \n\t\r,");
+ if(n > Nref)
+ n = Nref;
+ n = uniqarray(f, n, 0);
+ a = m->references;
+ for(i = 0; i < Nref; i++)
+ if(a[i] == 0)
+ break;
+ /*
+ * if there are too many references, drop from the beginning
+ * of the list.
+ */
+ j = i + n - Nref;
+ if(j > 0){
+ if(j > Nref)
+ j = Nref;
+ for(k = 0; k < j; k++)
+ free(a[k]);
+ memmove(a, a + j, sizeof a[0]*(Nref - j));
+ memset(a + j, 0, Nref - j);
+ i -= j;
+ }
+ for(j = 0; j < n;)
+ a[i++] = strdup(f[j++]);
+ free(s);
+ uniqarray(a, i, 1);
+ return (char*)~0;
}
static int
@@ -741,24 +929,20 @@ isattribute(char **pp, char *attr)
return 1;
}
-static void
-ctype(Message *m, Header *h, char *p)
+static char*
+ctype(Message *m, Header *h, char*, char *p)
{
- String *s;
+ char buf[128], *e;
- p += h->len;
- p = skipwhite(p);
+ e = buf + sizeof buf - 1;
+ p = getstring(skipwhite(p + h->len), buf, e, 1);
+ m->type = newrefs(buf);
- p = getstring(p, m->type, 1);
-
- while(*p){
+ for(; *p; p = skiptosemi(p))
if(isattribute(&p, "boundary")){
- s = s_new();
- p = getstring(p, s, 0);
- m->boundary = s_reset(m->boundary);
- s_append(m->boundary, "--");
- s_append(m->boundary, s_to_c(s));
- s_free(s);
+ p = getstring(p, buf, e, 0);
+ free(m->boundary);
+ m->boundary = smprint("--%s", buf);
} else if(cistrncmp(p, "multipart", 9) == 0){
/*
* the first unbounded part of a multipart message,
@@ -768,44 +952,41 @@ ctype(Message *m, Header *h, char *p)
if(m->filename == nil)
setfilename(m, p);
} else if(isattribute(&p, "charset")){
- p = getstring(p, s_reset(m->charset), 0);
+ p = getstring(p, buf, e, 0);
+ lowercase(buf);
+ m->charset = newrefs(buf);
}
-
- p = skiptosemi(p);
- }
+ return (char*)~0;
}
-static void
-cencoding(Message *m, Header *h, char *p)
+static char*
+cencoding(Message *m, Header *h, char*, char *p)
{
- p += h->len;
- p = skipwhite(p);
+ p = skipwhite(p + h->len);
if(cistrncmp(p, "base64", 6) == 0)
m->encoding = Ebase64;
else if(cistrncmp(p, "quoted-printable", 16) == 0)
m->encoding = Equoted;
+ return (char*)~0;
}
-static void
-cdisposition(Message *m, Header *h, char *p)
+static char*
+cdisposition(Message *m, Header *h, char*, char *p)
{
- p += h->len;
- p = skipwhite(p);
- while(*p){
- if(cistrncmp(p, "inline", 6) == 0){
+ for(p = skipwhite(p + h->len); *p; p = skiptosemi(p))
+ if(cistrncmp(p, "inline", 6) == 0)
m->disposition = Dinline;
- } else if(cistrncmp(p, "attachment", 10) == 0){
+ else if(cistrncmp(p, "attachment", 10) == 0)
m->disposition = Dfile;
- } else if(cistrncmp(p, "filename=", 9) == 0){
+ else if(cistrncmp(p, "filename=", 9) == 0){
p += 9;
setfilename(m, p);
}
- p = skiptosemi(p);
- }
-
+ return (char*)~0;
}
-ulong msgallocd, msgfreed;
+ulong msgallocd;
+ulong msgfreed;
Message*
newmessage(Message *parent)
@@ -815,14 +996,16 @@ newmessage(Message *parent)
msgallocd++;
- m = emalloc(sizeof(*m));
- memset(m, 0, sizeof(*m));
+ m = emalloc(sizeof *m);
+ dprint("newmessage %ld %p %p\n", msgallocd, parent, m);
m->disposition = Dnone;
- m->type = s_copy("text/plain");
- m->charset = s_copy("iso-8859-1");
+ m->type = newrefs("text/plain");
+ m->charset = newrefs("iso-8859-1");
+ m->cstate = Cidxstale;
+ m->flags = Frecent;
m->id = newid();
if(parent)
- sprint(m->name, "%d", ++(parent->subname));
+ snprint(m->name, sizeof m->name, "%d", ++(parent->subname));
if(parent == nil)
parent = m;
m->whole = parent;
@@ -830,74 +1013,65 @@ newmessage(Message *parent)
return m;
}
-// delete a message from a mailbox
+/* delete a message from a mailbox */
void
delmessage(Mailbox *mb, Message *m)
{
Message **l;
- int i;
mb->vers++;
msgfreed++;
if(m->whole != m){
- // unchain from parent
+ /* unchain from parent */
for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
;
if(*l != nil)
*l = m->next;
- // clear out of name lookup hash table
+ /* clear out of name lookup hash table */
if(m->whole->whole == m->whole)
hfree(PATH(mb->id, Qmbox), m->name);
else
hfree(PATH(m->whole->id, Qdir), m->name);
- for(i = 0; i < Qmax; i++)
- hfree(PATH(m->id, Qdir), dirtab[i]);
+ hfree(PATH(m->id, Qdir), "xxx"); /* sleezy speedup */
}
-
- /* recurse through sub-parts */
+ if(Topmsg(mb, m)){
+ if(m != mb->root)
+ mtreedelete(mb, m);
+ cachefree(mb, m, 1);
+ }
+ delrefs(m->type);
+ delrefs(m->charset);
+ idxfree(m);
while(m->part)
delmessage(mb, m->part);
-
- /* free memory */
- if(m->mallocd)
- free(m->start);
- if(m->hallocd)
- free(m->header);
- if(m->ballocd)
- free(m->body);
- s_free(m->unixfrom);
- s_free(m->unixdate);
- s_free(m->unixheader);
- s_free(m->from822);
- s_free(m->sender822);
- s_free(m->to822);
- s_free(m->bcc822);
- s_free(m->cc822);
- s_free(m->replyto822);
- s_free(m->date822);
- s_free(m->inreplyto822);
- s_free(m->subject822);
- s_free(m->messageid822);
- s_free(m->addrs);
- s_free(m->mimeversion);
- s_free(m->sdigest);
- s_free(m->boundary);
- s_free(m->type);
- s_free(m->charset);
- s_free(m->filename);
+ free(m->unixfrom);
+ free(m->unixheader);
+ free(m->date822);
+ free(m->inreplyto);
+ free(m->boundary);
+ free(m->filename);
free(m);
}
-// mark messages (identified by path) for deletion
void
+unnewmessage(Mailbox *mb, Message *parent, Message *m)
+{
+ assert(parent->subname > 0);
+ m->deleted = Dup;
+ delmessage(mb, m);
+ parent->subname -= 1;
+}
+
+/* mark messages (identified by path) for deletion */
+char*
delmessages(int ac, char **av)
{
+ int i, needwrite;
Mailbox *mb;
Message *m;
- int i, needwrite;
qlock(&mbllock);
for(mb = mbl; mb != nil; mb = mb->next)
@@ -907,24 +1081,59 @@ delmessages(int ac, char **av)
}
qunlock(&mbllock);
if(mb == nil)
- return;
+ return "no such mailbox";
needwrite = 0;
- for(i = 1; i < ac; i++){
+ for(i = 1; i < ac; i++)
for(m = mb->root->part; m != nil; m = m->next)
if(strcmp(m->name, av[i]) == 0){
if(!m->deleted){
mailplumb(mb, m, 1);
needwrite = 1;
- m->deleted = 1;
- logmsg("deleting", m);
+ m->deleted = Deleted;
+ logmsg(m, "deleting");
}
break;
}
- }
if(needwrite)
syncmbox(mb, 1);
qunlock(mb);
+ return 0;
+}
+
+char*
+flagmessages(int argc, char **argv)
+{
+ char *err, *rerr;
+ int i, needwrite;
+ Mailbox *mb;
+ Message *m;
+
+ if(argc%2)
+ return "bad flags";
+ qlock(&mbllock);
+ for(mb = mbl; mb; mb = mb->next)
+ if(strcmp(*argv, mb->name) == 0){
+ qlock(mb);
+ break;
+ }
+ qunlock(&mbllock);
+ if(mb == nil)
+ return "no such mailbox";
+ needwrite = 0;
+ rerr = 0;
+ for(i = 1; i < argc; i += 2)
+ for(m = mb->root->part; m; m = m->next)
+ if(strcmp(m->name, argv[i]) == 0){
+ if(err = modflags(mb, m, argv[i + 1]))
+ rerr = err;
+ else
+ needwrite = 1;
+ }
+ if(needwrite)
+ syncmbox(mb, 1);
+ qunlock(mb);
+ return rerr;
}
/*
@@ -935,12 +1144,18 @@ msgincref(Message *m)
{
m->refs++;
}
+
void
msgdecref(Mailbox *mb, Message *m)
{
+ assert(m->refs > 0);
m->refs--;
- if(m->refs == 0 && m->deleted)
- syncmbox(mb, 1);
+ if(m->refs == 0){
+ if(m->deleted)
+ syncmbox(mb, 1);
+ else
+ putcache(mb, m);
+ }
}
/*
@@ -952,6 +1167,20 @@ mboxincref(Mailbox *mb)
assert(mb->refs > 0);
mb->refs++;
}
+
+static void
+mbrmidx(char *path, int flags)
+{
+ char buf[Pathlen];
+
+ snprint(buf, sizeof buf, "%s.idx", path);
+ vremove(buf);
+ if((flags & Rtrunc) == 0){
+ snprint(buf, sizeof buf, "%s.imp", path);
+ vremove(buf);
+ }
+}
+
void
mboxdecref(Mailbox *mb)
{
@@ -959,98 +1188,41 @@ mboxdecref(Mailbox *mb)
qlock(mb);
mb->refs--;
if(mb->refs == 0){
+ syncmbox(mb, 1);
delmessage(mb, mb->root);
if(mb->ctl)
hfree(PATH(mb->id, Qmbox), "ctl");
if(mb->close)
- (*mb->close)(mb);
+ mb->close(mb);
+ if(mb->flags & ORCLOSE && mb->remove)
+ if(mb->remove(mb, mb->rmflags))
+ rmidx(mb->path, mb->rmflags);
+ free(mb->mtree);
+ free(mb->d);
free(mb);
} else
qunlock(mb);
}
+/* just space over \r. sleezy but necessary for ms email. */
int
-cistrncmp(char *a, char *b, int n)
+deccr(char *x, int len)
{
- while(n-- > 0){
- if(tolower(*a++) != tolower(*b++))
- return -1;
- }
- return 0;
-}
+ char *e;
-int
-cistrcmp(char *a, char *b)
-{
+ e = x + len;
for(;;){
- if(tolower(*a) != tolower(*b++))
- return -1;
- if(*a++ == 0)
+ x = memchr(x, '\r', e - x);
+ if(x == nil)
break;
+ *x = ' ';
}
- return 0;
-}
-
-static char*
-skipwhite(char *p)
-{
- while(isspace(*p))
- p++;
- return p;
+ return len;
}
-static char*
-skiptosemi(char *p)
-{
- while(*p && *p != ';')
- p++;
- while(*p == ';' || isspace(*p))
- p++;
- return p;
-}
-
-static char*
-getstring(char *p, String *s, int dolower)
-{
- s = s_reset(s);
- p = skipwhite(p);
- if(*p == '"'){
- p++;
- for(;*p && *p != '"'; p++)
- if(dolower)
- s_putc(s, tolower(*p));
- else
- s_putc(s, *p);
- if(*p == '"')
- p++;
- s_terminate(s);
-
- return p;
- }
-
- for(; *p && !isspace(*p) && *p != ';'; p++)
- if(dolower)
- s_putc(s, tolower(*p));
- else
- s_putc(s, *p);
- s_terminate(s);
-
- return p;
-}
-
-static void
-setfilename(Message *m, char *p)
-{
- m->filename = s_reset(m->filename);
- getstring(p, m->filename, 0);
- for(p = s_to_c(m->filename); *p; p++)
- if(*p == ' ' || *p == '\t' || *p == ';')
- *p = '_';
-}
-
-//
-// undecode message body
-//
+/*
+ * undecode message body
+ */
void
decode(Message *m)
{
@@ -1062,9 +1234,15 @@ decode(Message *m)
switch(m->encoding){
case Ebase64:
len = m->bend - m->body;
- i = (len*3)/4+1; // room for max chars + null
+ i = (len*3)/4 + 1; /* room for max chars + null */
x = emalloc(i);
len = dec64((uchar*)x, i, m->body, len);
+ if(len == -1){
+ free(x);
+ break;
+ }
+ if(strncmp(rtab[m->type].s, "text/", 5) == 0)
+ len = deccr(x, len);
if(m->ballocd)
free(m->body);
m->body = x;
@@ -1073,7 +1251,7 @@ decode(Message *m)
break;
case Equoted:
len = m->bend - m->body;
- x = emalloc(len+2); // room for null and possible extra nl
+ x = emalloc(len + 2); /* room for null and possible extra nl */
len = decquoted(x, m->body, m->bend, 0);
if(m->ballocd)
free(m->body);
@@ -1087,22 +1265,20 @@ decode(Message *m)
m->decoded = 1;
}
-// convert latin1 to utf
+/* convert x to utf8 */
void
convert(Message *m)
{
int len;
char *x;
- // don't convert if we're not a leaf, not text, or already converted
+ /* don't convert if we're not a leaf, not text, or already converted */
if(m->converted)
return;
- if(m->part != nil)
- return;
- if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
+ m->converted = 1;
+ if(m->part != nil || cistrncmp(rtab[m->type].s, "text", 4) != 0)
return;
-
- len = xtoutf(s_to_c(m->charset), &x, m->body, m->bend);
+ len = xtoutf(rtab[m->charset].s, &x, m->body, m->bend);
if(len > 0){
if(m->ballocd)
free(m->body);
@@ -1110,7 +1286,6 @@ convert(Message *m)
m->bend = x + len;
m->ballocd = 1;
}
- m->converted = 1;
}
static int
@@ -1119,18 +1294,20 @@ hex2int(int x)
if(x >= '0' && x <= '9')
return x - '0';
if(x >= 'A' && x <= 'F')
- return (x - 'A') + 10;
+ return x - 'A' + 10;
if(x >= 'a' && x <= 'f')
- return (x - 'a') + 10;
+ return x - 'a' + 10;
return -1;
}
-// underscores are translated in 2047 headers (uscores=1)
-// but not in the body (uscores=0)
+/*
+ * underscores are translated in 2047 headers (uscores=1)
+ * but not in the body (uscores=0)
+ */
static char*
decquotedline(char *out, char *in, char *e, int uscores)
{
- int c, c2, soft;
+ int c, soft;
/* dump trailing white space */
while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
@@ -1155,11 +1332,11 @@ decquotedline(char *out, char *in, char *e, int uscores)
*out++ = c;
break;
case '=':
- c = hex2int(*in++);
- c2 = hex2int(*in++);
- if (c != -1 && c2 != -1)
- *out++ = c<<4 | c2;
- else {
+ c = hex2int(*in++)<<4;
+ c |= hex2int(*in++);
+ if(c != -1)
+ *out++ = c;
+ else{
*out++ = '=';
in -= 2;
}
@@ -1184,10 +1361,10 @@ decquoted(char *out, char *in, char *e, int uscores)
in = nl + 1;
}
if(in < e)
- p = decquotedline(p, in, e-1, uscores);
+ p = decquotedline(p, in, e - 1, uscores);
- // make sure we end with a new line
- if(*(p-1) != '\n'){
+ /* make sure we end with a new line */
+ if(*(p - 1) != '\n'){
*p++ = '\n';
*p = 0;
}
@@ -1195,7 +1372,7 @@ decquoted(char *out, char *in, char *e, int uscores)
return p - out;
}
-static char*
+char*
lowercase(char *p)
{
char *op;
@@ -1207,7 +1384,7 @@ lowercase(char *p)
return op;
}
-// translate latin1 directly since it fits neatly in utf
+/* translate latin1 directly since it fits neatly in utf */
static int
latin1toutf(char **out, char *in, char *e)
{
@@ -1222,38 +1399,35 @@ latin1toutf(char **out, char *in, char *e)
if(n == 0)
return 0;
- n += e-in;
- *out = p = malloc(UTFmax*n+1);
+ n += e - in;
+ *out = p = malloc(n + 1);
if(p == nil)
return 0;
for(; in < e; in++){
- r = (*in) & 0xff;
+ r = (uchar)*in;
p += runetochar(p, &r);
}
*p = 0;
return p - *out;
}
-// translate any thing using the tcs program
+/* translate any thing using the tcs program */
int
xtoutf(char *charset, char **out, char *in, char *e)
{
- char *av[4];
- int totcs[2];
- int fromtcs[2];
- int n, len, sofar;
- char *p;
+ char *av[4], *p;
+ int totcs[2], fromtcs[2], n, len, sofar;
- // might not need to convert
+ /* might not need to convert */
if(cistrcmp(charset, "us-ascii") == 0 || cistrcmp(charset, "utf-8") == 0)
return 0;
if(cistrcmp(charset, "iso-8859-1") == 0)
return latin1toutf(out, in, e);
- len = e-in+1;
+ len = e - in + 1;
sofar = 0;
- *out = p = malloc(len+1);
+ *out = p = malloc(len + 1);
if(p == nil)
return 0;
@@ -1279,7 +1453,7 @@ xtoutf(char *charset, char **out, char *in, char *e)
close(fromtcs[1]); close(totcs[0]);
dup(open("/dev/null", OWRITE), 2);
exec("/bin/tcs", av);
- _exits(0);
+ _exits("");
default:
close(fromtcs[1]); close(totcs[0]);
switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
@@ -1289,24 +1463,24 @@ xtoutf(char *charset, char **out, char *in, char *e)
case 0:
close(fromtcs[0]);
while(in < e){
- n = write(totcs[1], in, e-in);
+ n = write(totcs[1], in, e - in);
if(n <= 0)
break;
in += n;
}
close(totcs[1]);
- _exits(0);
+ _exits("");
default:
close(totcs[1]);
for(;;){
- n = read(fromtcs[0], &p[sofar], len-sofar);
+ n = read(fromtcs[0], &p[sofar], len - sofar);
if(n <= 0)
break;
sofar += n;
p[sofar] = 0;
if(sofar == len){
len += 1024;
- p = realloc(p, len+1);
+ p = realloc(p, len + 1);
if(p == nil)
goto error;
*out = p;
@@ -1333,10 +1507,8 @@ emalloc(ulong n)
void *p;
p = mallocz(n, 1);
- if(!p){
- fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
- exits("out of memory");
- }
+ if(!p)
+ sysfatal("malloc %lud: %r", n);
setmalloctag(p, getcallerpc(&n));
return p;
}
@@ -1347,53 +1519,57 @@ erealloc(void *p, ulong n)
if(n == 0)
n = 1;
p = realloc(p, n);
- if(!p){
- fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
- exits("out of memory");
- }
+ if(!p)
+ sysfatal("realloc %lud: %r", n);
setrealloctag(p, getcallerpc(&p));
return p;
}
+int
+myplumbsend(int fd, Plumbmsg *m)
+{
+ char *buf;
+ int n;
+
+ buf = plumbpack(m, &n);
+ if(buf == nil)
+ return -1;
+ n = write(fd, buf, n);
+ free(buf);
+ return n;
+}
+
void
mailplumb(Mailbox *mb, Message *m, int delete)
{
+ char buf[256], dbuf[SHA1dlen*2 + 1], len[10], date[30], *from, *subject;
+ int ai, cache;
Plumbmsg p;
Plumbattr a[7];
- char buf[256];
- int ai;
- char lenstr[10], *from, *subject, *date;
static int fd = -1;
- if(m->subject822 == nil)
+ cache = insurecache(mb, m) == 0; /* living dangerously if deleted */
+ subject = m->subject;
+ if(subject == nil)
subject = "";
- else
- subject = s_to_c(m->subject822);
- if(m->from822 != nil)
- from = s_to_c(m->from822);
+ if(m->from != nil)
+ from = m->from;
else if(m->unixfrom != nil)
- from = s_to_c(m->unixfrom);
+ from = m->unixfrom;
else
from = "";
- if(m->unixdate != nil)
- date = s_to_c(m->unixdate);
- else
- date = "";
-
- sprint(lenstr, "%zd", m->end-m->start);
-
+ sprint(len, "%lud", m->size);
if(biffing && !delete)
- print("[ %s / %s / %s ]\n", from, subject, lenstr);
-
+ fprint(2, "[ %s / %s / %s ]\n", from, subject, len);
if(!plumbing)
- return;
+ goto out;
if(fd < 0)
fd = plumbopen("send", OWRITE);
if(fd < 0)
- return;
+ goto out;
p.src = "mailfs";
p.dst = "seemail";
@@ -1409,102 +1585,123 @@ mailplumb(Mailbox *mb, Message *m, int delete)
a[ai-1].next = &a[ai];
a[++ai].name = "length";
- a[ai].value = lenstr;
+ a[ai].value = len;
a[ai-1].next = &a[ai];
a[++ai].name = "mailtype";
- a[ai].value = delete?"delete":"new";
+ a[ai].value = delete? "delete": "new";
a[ai-1].next = &a[ai];
+ snprint(date, sizeof date, "%Δ", m->fileid);
a[++ai].name = "date";
a[ai].value = date;
a[ai-1].next = &a[ai];
- if(m->sdigest){
+ if(m->digest){
+ snprint(dbuf, sizeof dbuf, "%A", m->digest);
a[++ai].name = "digest";
- a[ai].value = s_to_c(m->sdigest);
+ a[ai].value = dbuf;
a[ai-1].next = &a[ai];
}
-
a[ai].next = nil;
-
p.attr = a;
- snprint(buf, sizeof(buf), "%s/%s/%s",
+ snprint(buf, sizeof buf, "%s/%s/%s",
mntpt, mb->name, m->name);
p.ndata = strlen(buf);
p.data = buf;
- plumbsend(fd, &p);
+ myplumbsend(fd, &p);
+out:
+ if(cache)
+ msgdecref(mb, m);
}
-//
-// count the number of lines in the body (for imap4)
-//
-void
+/*
+ * count the number of lines in the body (for imap4)
+ */
+ulong
countlines(Message *m)
{
- int i;
char *p;
+ ulong i;
i = 0;
- for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
+ for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p + 1, '\n'))
i++;
- sprint(m->lines, "%d", i);
+ return i;
}
-char *LOG = "fs";
+static char *logf = "fs";
void
-logmsg(char *s, Message *m)
+logmsg(Message *m, char *fmt, ...)
{
- int pid;
+ char buf[256], *p, *e;
+ va_list args;
- if(!logging)
+ if(!lflag)
return;
- pid = getpid();
- if(m == nil)
- syslog(0, LOG, "%s.%d: %s", user, pid, s);
- else
- syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
- user, pid, s,
- m->from822 ? s_to_c(m->from822) : "?",
- s_to_c(m->sdigest));
+ e = buf + sizeof buf;
+ p = seprint(buf, e, "%s.%d: ", user, getpid());
+ if(m)
+ p = seprint(p, e, "from %s digest %A ",
+ m->from, m->digest);
+ va_start(args, fmt);
+ vseprint(p, e, fmt, args);
+ va_end(args);
+
+ if(Sflag)
+ fprint(2, "%s\n", buf);
+ syslog(Sflag, logf, "%s", buf);
}
-/*
- * squeeze nulls out of the body
- */
-static void
-nullsqueeze(Message *m)
+void
+iprint(char *fmt, ...)
{
- char *p, *q;
+ char buf[256], *p, *e;
+ va_list args;
- q = memchr(m->body, 0, m->end-m->body);
- if(q == nil)
+ if(!iflag)
return;
-
- for(p = m->body; q < m->end; q++){
- if(*q == 0)
- continue;
- *p++ = *q;
- }
- m->bend = m->rbend = m->end = p;
+ e = buf + sizeof buf;
+ p = seprint(buf, e, "%s.%d: ", user, getpid());
+ va_start(args, fmt);
+ vseprint(p, e, fmt, args);
+ vfprint(2, fmt, args);
+ va_end(args);
+ syslog(Sflag, logf, "%s", buf);
}
+void
+eprint(char *fmt, ...)
+{
+ char buf[256], buf2[256], *p, *e;
+ va_list args;
+
+ e = buf + sizeof buf;
+ p = seprint(buf, e, "%s.%d: ", user, getpid());
+ va_start(args, fmt);
+ vseprint(p, e, fmt, args);
+ e = buf2 + sizeof buf2;
+ p = seprint(buf2, e, "upas/fs: ");
+ vseprint(p, e, fmt, args);
+ va_end(args);
+ syslog(Sflag, logf, "%s", buf);
+ fprint(2, "%s", buf2);
+}
-//
-// convert an RFC822 date into a Unix style date
-// for when the Unix From line isn't there (e.g. POP3).
-// enough client programs depend on having a Unix date
-// that it's easiest to write this conversion code once, right here.
-//
-// people don't follow RFC822 particularly closely,
-// so we use strtotm, which is a bunch of heuristics.
-//
+/*
+ * convert an RFC822 date into a Unix style date
+ * for when the Unix From line isn't there (e.g. POP3).
+ * enough client programs depend on having a Unix date
+ * that it's easiest to write this conversion code once, right here.
+ *
+ * people don't follow RFC822 particularly closely,
+ * so we use strtotm, which is a bunch of heuristics.
+ */
-extern int strtotm(char*, Tm*);
-String*
-date822tounix(char *s)
+char*
+date822tounix(Message *, char *s)
{
char *p, *q;
Tm tm;
@@ -1515,6 +1712,5 @@ date822tounix(char *s)
p = asctime(&tm);
if(q = strchr(p, '\n'))
*q = '\0';
- return s_copy(p);
+ return strdup(p);
}
-
diff --git a/sys/src/cmd/upas/fs/mdir.c b/sys/src/cmd/upas/fs/mdir.c
new file mode 100644
index 000000000..69d53e465
--- /dev/null
+++ b/sys/src/cmd/upas/fs/mdir.c
@@ -0,0 +1,354 @@
+#include "common.h"
+#include "dat.h"
+
+typedef struct {
+ int debug;
+} Mdir;
+
+#define mdprint(mdir, ...) if(mdir->debug) fprint(2, __VA_ARGS__)
+
+static int
+slurp(char *f, char *b, uvlong o, long l)
+{
+ int fd, r;
+
+ if((fd = open(f, OREAD)) == -1)
+ return -1;
+
+ seek(fd, o, 0);
+ r = readn(fd, b, l) != l;
+ close(fd);
+ return r? -1: 0;
+}
+
+static void
+parseunix(Message *m)
+{
+ char *s, *p;
+ int l;
+
+ l = m->header - m->start;
+ m->unixheader = smprint("%.*s", l, m->start);
+ s = m->start + 5;
+ if((p = strchr(s, ' ')) == nil)
+ abort();
+ *p = 0;
+ m->unixfrom = strdup(s);
+ *p = ' ';
+}
+
+static int
+mdirfetch(Mailbox *mb, Message *m, uvlong o, ulong l)
+{
+ char buf[Pathlen], *x;
+ Mdir *mdir;
+
+ mdir = mb->aux;
+ mdprint(mdir, "mdirfetch(%D) ...", m->fileid);
+
+ snprint(buf, sizeof buf, "%s/%D", mb->path, m->fileid);
+ if(slurp(buf, m->start + o, o, l) == -1){
+ logmsg(m, "fetch failed: %r");
+ mdprint(mdir, "%r\n");
+ return -1;
+ }
+ if(m->header == nil)
+ m->header = m->start;
+ if(m->header == m->start)
+ if(o + l >= 36)
+ if(strncmp(m->start, "From ", 5) == 0)
+ if(x = strchr(m->start, '\n')){
+ m->header = x + 1;
+ if(m->unixfrom == nil)
+ parseunix(m);
+ }
+ m->mheader = m->mhend = m->header;
+ mdprint(mdir, "fetched [%llud, %llud]\n", o, o + l);
+ return 0;
+}
+
+static int
+setsize(Mailbox *mb, Message *m)
+{
+ char buf[Pathlen];
+ Dir *d;
+
+ snprint(buf, sizeof buf, "%s/%D", mb->path, m->fileid);
+ if((d = dirstat(buf)) == nil)
+ return -1;
+ m->size = d->length;
+ free(d);
+ return 0;
+}
+
+/* must be [0-9]+(\..*)? */
+int
+dirskip(Dir *a, uvlong *uv)
+{
+ char *p;
+
+ if(a->length == 0)
+ return 1;
+ *uv = strtoul(a->name, &p, 0);
+ if(*uv < 1000000 || *p != '.')
+ return 1;
+ *uv = *uv<<8 | strtoul(p + 1, &p, 10);
+ if(*p)
+ return 1;
+ return 0;
+}
+
+static int
+vcmp(vlong a, vlong b)
+{
+ a -= b;
+ if(a > 0)
+ return 1;
+ if(a < 0)
+ return -1;
+ return 0;
+}
+
+static int
+dircmp(Dir *a, Dir *b)
+{
+ uvlong x, y;
+
+ dirskip(a, &x);
+ dirskip(b, &y);
+ return vcmp(x, y);
+}
+
+static char*
+mdirread(Mdir* mdir, Mailbox* mb, int doplumb, int *new)
+{
+ int i, nnew, ndel, fd, n, c;
+ uvlong uv;
+ Dir *d;
+ Message *m, **ll;
+ static char err[ERRMAX];
+
+ mdprint(mdir, "mdirread()\n");
+ if((fd = open(mb->path, OREAD)) == -1){
+ errstr(err, sizeof err);
+ return err;
+ }
+ if((d = dirfstat(fd)) == nil){
+ errstr(err, sizeof err);
+ close(fd);
+ return err;
+ }
+ *new = nnew = 0;
+ if(mb->d){
+ if(d->qid.path == mb->d->qid.path)
+ if(d->qid.vers == mb->d->qid.vers){
+ mdprint(mdir, "\tqids match\n");
+ close(fd);
+ free(d);
+ goto finished;
+ }
+ free(mb->d);
+ }
+ logmsg(nil, "reading %s (mdir)", mb->path);
+ mb->d = d;
+
+ n = dirreadall(fd, &d);
+ close(fd);
+ if(n == -1){
+ errstr(err, sizeof err);
+ return err;
+ }
+
+ qsort(d, n, sizeof *d, (int(*)(void*, void*))dircmp);
+ ndel = 0;
+ ll = &mb->root->part;
+ for(i = 0; *ll || i < n; ){
+ if(i < n && dirskip(d + i, &uv)){
+ i++;
+ continue;
+ }
+ c = -1;
+ if(i >= n)
+ c = 1;
+ else if(*ll)
+ c = vcmp(uv, (*ll)->fileid);
+ mdprint(mdir, "consider %s and %D -> %d\n", i<n? d[i].name: 0, *ll? (*ll)->fileid: 1ull, c);
+ if(c < 0){
+ /* new message */
+ mdprint(mdir, "new: %s (%D)\n", d[i].name, *ll? (*ll)->fileid: 0);
+ m = newmessage(mb->root);
+ m->fileid = uv;
+ if(setsize(mb, m) < 0 || m->size >= Maxmsg){
+ /* message disappeared? unchain */
+ mdprint(mdir, "deleted → %r (%D)\n", m->fileid);
+ logmsg(m, "disappeared");
+ if(doplumb)
+ mailplumb(mb, m, 1); /* redundant */
+ unnewmessage(mb, mb->root, m);
+ /* we're out of sync; note this by dropping cached qid */
+ mb->d->qid.path = 0;
+ break;
+ }
+ m->inmbox = 1;
+ nnew++;
+ m->next = *ll;
+ *ll = m;
+ ll = &m->next;
+ logmsg(m, "new %s", d[i].name);
+ i++;
+ newcachehash(mb, m, doplumb);
+ putcache(mb, m);
+ }else if(c > 0){
+ /* deleted message; */
+ mdprint(mdir, "deleted: %s (%D)\n", i<n? d[i].name: 0, *ll? (*ll)->fileid: 0);
+ ndel++;
+ logmsg(*ll, "deleted (refs=%d)", *ll? (*ll)->refs: -42);
+ if(doplumb)
+ mailplumb(mb, *ll, 1);
+ (*ll)->inmbox = 0;
+ (*ll)->deleted = Disappear;
+ ll = &(*ll)->next;
+ }else{
+ //logmsg(*ll, "duplicate %s", d[i].name);
+ i++;
+ ll = &(*ll)->next;
+ }
+ }
+
+ free(d);
+ logmsg(nil, "mbox read: %d new %d deleted", nnew, ndel);
+finished:
+ *new = nnew;
+ return nil;
+}
+
+static void
+mdirdelete(Mailbox *mb, Message *m)
+{
+ char mpath[Pathlen];
+ Mdir* mdir;
+
+ mdir = mb->aux;
+ snprint(mpath, sizeof mpath, "%s/%D", mb->path, m->fileid);
+ mdprint(mdir, "remove: %s\n", mpath);
+ /* may have been removed by other fs. just log the error. */
+ if(remove(mpath) == -1)
+ logmsg(m, "remove: %s: %r", mpath);
+ m->inmbox = 0;
+}
+
+static char*
+mdirsync(Mailbox* mb, int doplumb, int *new)
+{
+ Mdir *mdir;
+
+ mdir = mb->aux;
+ mdprint(mdir, "mdirsync()\n");
+ return mdirread(mdir, mb, doplumb, new);
+}
+
+static char*
+mdirctl(Mailbox *mb, int c, char **v)
+{
+ Mdir *mdir;
+
+ mdir = mb->aux;
+ if(c == 1 && strcmp(*v, "debug") == 0)
+ mdir->debug = 1;
+ else if(c == 1 && strcmp(*v, "nodebug") == 0)
+ mdir->debug = 0;
+ else
+ return "bad mdir control message";
+ return nil;
+}
+
+static void
+mdirclose(Mailbox *mb)
+{
+ free(mb->aux);
+}
+
+static int
+qidcmp(Qid *a, Qid *b)
+{
+ if(a->path != b->path)
+ return 1;
+ return a->vers - b->vers;
+}
+
+/*
+ * .idx strategy. we save the qid.path and .vers
+ * of the mdir directory and the date to the index.
+ * we accept the work done by the other upas/fs if
+ * the index is based on the same (or a newer)
+ * qid. in any event, we recheck the directory after
+ * the directory is four hours old.
+ */
+static int
+idxr(char *s, Mailbox *mb)
+{
+ char *f[5];
+ long t, δt, n;
+ Dir d;
+
+ n = tokenize(s, f, nelem(f));
+ if(n != 4 || strcmp(f[0], "mdirv1") != 0)
+ return -1;
+ t = strtoul(f[1], 0, 0);
+ δt = time(0) - t;
+ if(δt < 0 || δt > 4*3600)
+ return 0;
+ memset(&d, 0, sizeof d);
+ d.qid.path = strtoull(f[2], 0, 0);
+ d.qid.vers = strtoull(f[3], 0, 0);
+ if(mb->d && qidcmp(&mb->d->qid, &d.qid) >= 0)
+ return 0;
+ if(mb->d == 0)
+ mb->d = emalloc(sizeof d);
+ mb->d->qid = d.qid;
+ mb->d->mtime = t;
+ return 0;
+}
+
+static void
+idxw(Biobuf *b, Mailbox *mb)
+{
+ Qid q;
+
+ memset(&q, 0, sizeof q);
+ if(mb->d)
+ q = mb->d->qid;
+ Bprint(b, "mdirv1 %lud %llud %lud\n", time(0), q.path, q.vers);
+}
+
+char*
+mdirmbox(Mailbox *mb, char *path)
+{
+ int m;
+ Dir *d;
+ Mdir *mdir;
+
+ d = dirstat(path);
+ if(!d && mb->flags & DMcreate){
+ createfolder(getuser(), path);
+ d = dirstat(path);
+ }
+ m = d && (d->mode & DMDIR);
+ free(d);
+ if(!m)
+ return Enotme;
+ snprint(mb->path, sizeof mb->path, "%s", path);
+ mdir = emalloc(sizeof *mdir);
+ mdir->debug = 0;
+ mb->aux = mdir;
+ mb->sync = mdirsync;
+ mb->close = mdirclose;
+ mb->fetch = mdirfetch;
+ mb->delete = mdirdelete;
+ mb->remove = localremove;
+ mb->rename = localrename;
+ mb->idxread = idxr;
+ mb->idxwrite = idxw;
+ mb->ctl = mdirctl;
+ return nil;
+}
diff --git a/sys/src/cmd/upas/fs/mkfile b/sys/src/cmd/upas/fs/mkfile
index 4a7132b86..814787a07 100644
--- a/sys/src/cmd/upas/fs/mkfile
+++ b/sys/src/cmd/upas/fs/mkfile
@@ -1,14 +1,24 @@
</$objtype/mkfile
+<../mkupas
TARG= fs\
OFILES=\
+ bos.$O\
+ cache.$O\
fs.$O\
- imap4.$O\
+ header.$O\
+ idx.$O\
+ imap.$O\
mbox.$O\
+ mdir.$O\
+ mtree.$O\
plan9.$O\
planb.$O\
pop3.$O\
+ ref.$O\
+ remove.$O\
+ rename.$O\
strtotm.$O\
LIB=../common/libcommon.a$O\
@@ -16,8 +26,6 @@ LIB=../common/libcommon.a$O\
HFILES= ../common/common.h\
dat.h
-BIN=/$objtype/bin/upas
-
UPDATE=\
mkfile\
$HFILES\
@@ -25,4 +33,10 @@ UPDATE=\
${OFILES:%.$O=%.c}\
</sys/src/cmd/mkone
-CFLAGS=$CFLAGS -I/sys/include -I../common
+CFLAGS=$CFLAGS -I../common
+
+acid:V:
+ $CC -a $CFLAGS fs.c>a$O
+
+chkidx: mtree.$O chkidx.$O
+ $LD $LDFLAGS -o $target $prereq
diff --git a/sys/src/cmd/upas/fs/mtree.c b/sys/src/cmd/upas/fs/mtree.c
new file mode 100644
index 000000000..4f2af5655
--- /dev/null
+++ b/sys/src/cmd/upas/fs/mtree.c
@@ -0,0 +1,80 @@
+#include "common.h"
+#include <libsec.h>
+#include "dat.h"
+
+int
+mtreecmp(Avl *va, Avl *vb)
+{
+ Mtree *a, *b;
+
+ a = (Mtree*)va;
+ b = (Mtree*)vb;
+ return memcmp(a->m->digest, b->m->digest, SHA1dlen);
+}
+
+int
+mtreeisdup(Mailbox *mb, Message *m)
+{
+ Mtree t;
+
+ assert(Topmsg(mb, m) && m->digest);
+ if(!m->digest)
+ return 0;
+ memset(&t, 0, sizeof t);
+ t.m = m;
+ if(avllookup(mb->mtree, &t))
+ return 1;
+ return 0;
+}
+
+Message*
+mtreefind(Mailbox *mb, uchar *digest)
+{
+ Message m0;
+ Mtree t, *p;
+
+ m0.digest = digest;
+ memset(&t, 0, sizeof t);
+ t.m = &m0;
+ if(p = (Mtree*)avllookup(mb->mtree, &t))
+ return p->m;
+ return nil;
+}
+
+void
+mtreeadd(Mailbox *mb, Message *m)
+{
+ Avl *old;
+ Mtree *p;
+
+ assert(Topmsg(mb, m) && m->digest);
+ p = emalloc(sizeof *p);
+ p->m = m;
+ old = avlinsert(mb->mtree, p);
+ assert(old == 0);
+}
+
+void
+mtreedelete(Mailbox *mb, Message *m)
+{
+ Mtree t, *p;
+
+ assert(Topmsg(mb, m));
+ memset(&t, 0, sizeof t);
+ t.m = m;
+ if(m->deleted & ~Deleted){
+ if(m->digest == nil)
+ return;
+ p = (Mtree*)avllookup(mb->mtree, &t);
+ if(p == nil || p->m != m)
+ return;
+ p = (Mtree*)avldelete(mb->mtree, &t);
+ free(p);
+ return;
+ }
+ assert(m->digest);
+ p = (Mtree*)avldelete(mb->mtree, &t);
+ if(p == nil)
+ _assert("mtree delete fails");
+ free(p);
+}
diff --git a/sys/src/cmd/upas/fs/plan9.c b/sys/src/cmd/upas/fs/plan9.c
index 171b15617..1d65a62e0 100644
--- a/sys/src/cmd/upas/fs/plan9.c
+++ b/sys/src/cmd/upas/fs/plan9.c
@@ -1,151 +1,188 @@
#include "common.h"
-#include <ctype.h>
-#include <plumb.h>
#include <libsec.h>
#include "dat.h"
-enum {
- Buffersize = 64*1024,
-};
+typedef struct {
+ Biobuf *in;
+ char *shift;
+} Inbuf;
-typedef struct Inbuf Inbuf;
-struct Inbuf
+/*
+ * parse a Unix style header
+ */
+static int
+memtotm(char *p, int n, Tm *t)
+{
+ char buf[128];
+
+ if(n > sizeof buf - 1)
+ n = sizeof buf -1;
+ memcpy(buf, p, n);
+ buf[n] = 0;
+ return strtotm(buf, t);
+}
+
+static int
+chkunix0(char *s, int n)
+{
+ char *p;
+ Tm tm;
+
+ if(n > 256)
+ return -1;
+ if((p = memchr(s, ' ', n)) == nil)
+ return -1;
+ if(memtotm(p, n - (p - s), &tm) < 0)
+ return -1;
+ if(tm2sec(&tm) < 1000000)
+ return -1;
+ return 0;
+}
+
+static int
+chkunix(char *s, int n)
{
- int fd;
- uchar *lim;
- uchar *rptr;
- uchar *wptr;
- uchar data[Buffersize+7];
-};
+ int r;
+
+ r = chkunix0(s, n);
+ if(r == -1)
+ eprint("plan9: warning naked from [%.*s]\n", n, s);
+ return r;
+}
+
+static char*
+parseunix(Message *m)
+{
+ char *s, *p, *q;
+ int l;
+ Tm tm;
+
+ l = m->header - m->start;
+ m->unixheader = smprint("%.*s", l, m->start);
+ s = m->start + 5;
+ if((p = strchr(s, ' ')) == nil)
+ return s;
+ *p = 0;
+ m->unixfrom = strdup(s);
+ *p++ = ' ';
+ if(q = strchr(p, '\n'))
+ *q = 0;
+ if(strtotm(p, &tm) < 0)
+ return p;
+ if(q)
+ *q = '\n';
+ m->fileid = (uvlong)tm2sec(&tm) << 8;
+ return 0;
+}
static void
-addtomessage(Message *m, uchar *p, int n, int done)
+addtomessage(Message *m, char *p, int n)
{
int i, len;
- // add to message (+1 in malloc is for a trailing NUL)
+ if(n == 0)
+ return;
+ /* add to message (+1 in malloc is for a trailing NUL) */
if(m->lim - m->end < n){
if(m->start != nil){
- i = m->end-m->start;
- if(done)
- len = i + n;
- else
- len = (4*(i+n))/3;
+ i = m->end - m->start;
+ len = (4*(i + n))/3;
m->start = erealloc(m->start, len + 1);
m->end = m->start + i;
} else {
- if(done)
- len = n;
- else
- len = 2*n;
+ len = 2*n;
m->start = emalloc(len + 1);
m->end = m->start;
}
m->lim = m->start + len;
- *m->lim = '\0';
+ *m->lim = 0;
}
memmove(m->end, p, n);
m->end += n;
- *m->end = '\0';
+ *m->end = 0;
}
-//
-// read in a single message
-//
+/*
+ * read in a single message
+ */
static int
-readmessage(Message *m, Inbuf *inb)
+okmsg(Mailbox *mb, Message *m, Inbuf *b)
{
- int i, n, done;
- uchar *p, *np;
- char sdigest[SHA1dlen*2+1];
- char tmp[64];
-
- for(done = 0; !done;){
- n = inb->wptr - inb->rptr;
- if(n < 6){
- if(n)
- memmove(inb->data, inb->rptr, n);
- inb->rptr = inb->data;
- inb->wptr = inb->rptr + n;
- i = read(inb->fd, inb->wptr, Buffersize);
- if(i < 0){
- if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
- strcpy(tmp, "unknown mailbox");
- fprint(2, "error reading '%s': %r\n", tmp);
- return -1;
- }
- if(i == 0){
- if(n != 0)
- addtomessage(m, inb->rptr, n, 1);
- if(m->end == m->start)
- return -1;
- break;
- }
- inb->wptr += i;
- }
-
- // look for end of message
- for(p = inb->rptr; p < inb->wptr; p = np+1){
- // first part of search for '\nFrom '
- np = memchr(p, '\n', inb->wptr - p);
- if(np == nil){
- p = inb->wptr;
- break;
- }
-
- /*
- * if we've found a \n but there's
- * not enough room for '\nFrom ', don't do
- * the comparison till we've read in more.
- */
- if(inb->wptr - np < 6){
- p = np;
- break;
- }
-
- if(strncmp((char*)np, "\nFrom ", 6) == 0){
- done = 1;
- p = np+1;
- break;
- }
- }
+ char e[ERRMAX], buf[128];
- // add to message (+ 1 in malloc is for a trailing null)
- n = p - inb->rptr;
- addtomessage(m, inb->rptr, n, done);
- inb->rptr += n;
+ rerrstr(e, sizeof e);
+ if(strlen(e)){
+ if(fd2path(Bfildes(b->in), buf, sizeof buf) < 0)
+ strcpy(buf, "unknown mailbox");
+ eprint("plan9: error reading %s: %r\n", buf);
+ return -1;
}
-
- // if it doesn't start with a 'From ', this ain't a mailbox
- if(strncmp(m->start, "From ", 5) != 0)
+ if(m->end == m->start)
return -1;
-
- // dump trailing newline, make sure there's a trailing null
- // (helps in body searches)
- if(*(m->end-1) == '\n')
+ if(m->end[-1] == '\n')
m->end--;
*m->end = 0;
+ m->size = m->end - m->start;
+ if(m->size >= Maxmsg)
+ return -1;
m->bend = m->rbend = m->end;
+ if(m->digest == 0)
+ digestmessage(mb, m);
+ return 0;
+}
- // digest message
- sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
- for(i = 0; i < SHA1dlen; i++)
- sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
- m->sdigest = s_copy(sdigest);
+static char*
+inbread(Inbuf *b)
+{
+ if(b->shift)
+ return b->shift;
+ return b->shift = Brdline(b->in, '\n');
+}
- return 0;
+void
+inbconsume(Inbuf *b)
+{
+ b->shift = 0;
}
+/*
+ * bug: very long line with From at the buffer break.
+ */
+static int
+readmessage(Mailbox *mb, Message *m, Inbuf *b)
+{
+ char *s, *n;
+ long l, state;
+
+ werrstr("");
+ state = 0;
+ for(;;){
+ s = inbread(b);
+ if(s == 0)
+ break;
+ n = s + (l = Blinelen(b->in)) - 1;
+ if(l >= 28 + 7 && n[0] == '\n')
+ if(strncmp(s, "From ", 5) == 0)
+ if(!chkunix(s + 5, l - 5))
+ if(++state == 2)
+ break;
+ if(state == 0)
+ return -1;
+ addtomessage(m, s, l);
+ inbconsume(b);
+ }
+ return okmsg(mb, m, b);
+}
-// throw out deleted messages. return number of freshly deleted messages
+/* throw out deleted messages. return number of freshly deleted messages */
int
purgedeleted(Mailbox *mb)
{
Message *m, *next;
int newdels;
- // forget about what's no longer in the mailbox
+ /* forget about what's no longer in the mailbox */
newdels = 0;
for(m = mb->root->part; m != nil; m = next){
next = m->next;
@@ -158,44 +195,51 @@ purgedeleted(Mailbox *mb)
return newdels;
}
-//
-// read in the mailbox and parse into messages.
-//
+static void
+mergemsg(Message *m, Message *x)
+{
+ assert(m->start == 0);
+ m->mallocd = 1;
+ m->inmbox = 1;
+ m->lim = x->lim;
+ m->start = x->start;
+ m->end = x->end;
+ m->bend = x->bend;
+ m->rbend = x->rbend;
+ x->lim = 0;
+ x->start = 0;
+ x->end = 0;
+ x->bend = 0;
+ x->rbend = 0;
+}
+
+/*
+ * read in the mailbox and parse into messages.
+ */
static char*
-_readmbox(Mailbox *mb, int doplumb, Mlock *lk)
+readmbox(Mailbox *mb, int doplumb, int *new, Mlock *lk)
{
- int fd, n;
- String *tmp;
+ char *p, *x, buf[Pathlen];
+ int nnew;
+ Biobuf *in;
Dir *d;
- static char err[Errlen];
+ Inbuf b;
Message *m, **l;
- Inbuf *inb;
- char *x;
+ static char err[ERRMAX];
l = &mb->root->part;
/*
* open the mailbox. If it doesn't exist, try the temporary one.
*/
- n = 0;
retry:
- fd = open(mb->path, OREAD);
- if(fd < 0){
- rerrstr(err, sizeof(err));
- if(strstr(err, "locked") != nil
- || strstr(err, "exclusive lock") != nil)
- if(n++ < 20){
- sleep(500); /* wait for lock to go away */
- goto retry;
- }
- if(strstr(err, "exist") != nil){
- tmp = s_copy(mb->path);
- s_append(tmp, ".tmp");
- if(sysrename(s_to_c(tmp), mb->path) == 0){
- s_free(tmp);
+ in = Bopen(mb->path, OREAD);
+ if(in == nil){
+ errstr(err, sizeof(err));
+ if(strstr(err, "exist") != 0){
+ snprint(buf, sizeof buf, "%s.tmp", mb->path);
+ if(sysrename(buf, mb->path) == 0)
goto retry;
- }
- s_free(tmp);
}
return err;
}
@@ -204,173 +248,170 @@ retry:
* a new qid.path means reread the mailbox, while
* a new qid.vers means read any new messages
*/
- d = dirfstat(fd);
+ d = dirfstat(Bfildes(in));
if(d == nil){
- close(fd);
- errstr(err, sizeof(err));
+ Bterm(in);
+ errstr(err, sizeof err);
return err;
}
if(mb->d != nil){
if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
- close(fd);
+ *new = 0;
+ Bterm(in);
free(d);
return nil;
}
if(d->qid.path == mb->d->qid.path){
while(*l != nil)
l = &(*l)->next;
- seek(fd, mb->d->length, 0);
+ Bseek(in, mb->d->length, 0);
}
free(mb->d);
}
mb->d = d;
- mb->vers++;
- henter(PATH(0, Qtop), mb->name,
- (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
- inb = emalloc(sizeof(Inbuf));
- inb->rptr = inb->wptr = inb->data;
- inb->fd = fd;
+ memset(&b, 0, sizeof b);
+ b.in = in;
+ b.shift = 0;
- // read new messages
- snprint(err, sizeof err, "reading '%s'", mb->path);
- logmsg(err, nil);
+ /* read new messages */
+ logmsg(nil, "reading %s", mb->path);
+ nnew = 0;
for(;;){
if(lk != nil)
syslockrefresh(lk);
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
- if(readmessage(m, inb) < 0){
- delmessage(mb, m);
- mb->root->subname--;
+ if(readmessage(mb, m, &b) < 0){
+ unnewmessage(mb, mb->root, m);
break;
}
-
- // merge mailbox versions
+ /* merge mailbox versions */
while(*l != nil){
if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
- // matches mail we already read, discard
- logmsg("duplicate", *l);
- delmessage(mb, m);
- mb->root->subname--;
- m = nil;
- l = &(*l)->next;
+ if((*l)->start == nil){
+ logmsg(*l, "read indexed");
+ mergemsg(*l, m);
+ unnewmessage(mb, mb->root, m);
+ m = *l;
+ }else{
+ logmsg(*l, "duplicate");
+ m->inmbox = 1; /* remove it */
+ unnewmessage(mb, mb->root, m);
+ m = nil;
+ l = &(*l)->next;
+ }
break;
} else {
- // old mail no longer in box, mark deleted
- logmsg("disappeared", *l);
+ /* old mail no longer in box, mark deleted */
+ logmsg(*l, "disappeared");
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
- (*l)->deleted = 1;
+ (*l)->deleted = Disappear;
l = &(*l)->next;
}
}
if(m == nil)
continue;
-
- x = strchr(m->start, '\n');
- if(x == nil)
- m->header = m->end;
- else
+ m->header = m->end;
+ if(x = strchr(m->start, '\n'))
m->header = x + 1;
+ if(p = parseunix(m))
+ sysfatal("%s:%lld naked From in body? [%s]", mb->path, seek(Bfildes(in), 0, 1), p);
m->mheader = m->mhend = m->header;
- parseunix(m);
- parse(m, 0, mb, 0);
- logmsg("new", m);
-
+ parse(mb, m, 0, 0);
+ if(m != *l && m->deleted != Dup){
+ logmsg(m, "new");
+ newcachehash(mb, m, doplumb);
+ putcache(mb, m);
+ nnew++;
+ }
/* chain in */
*l = m;
l = &m->next;
- if(doplumb)
- mailplumb(mb, m, 0);
-
}
- logmsg("mbox read", nil);
+ logmsg(nil, "mbox read");
- // whatever is left has been removed from the mbox, mark deleted
+ /* whatever is left has been removed from the mbox, mark deleted */
while(*l != nil){
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
- (*l)->deleted = 1;
+ (*l)->deleted = Deleted;
l = &(*l)->next;
}
- close(fd);
- free(inb);
+ Bterm(in);
+ *new = nnew;
return nil;
}
static void
-_writembox(Mailbox *mb, Mlock *lk)
+writembox(Mailbox *mb, Mlock *lk)
{
- Dir *d;
- Message *m;
- String *tmp;
+ char buf[Pathlen];
int mode, errs;
Biobuf *b;
+ Dir *d;
+ Message *m;
- tmp = s_copy(mb->path);
- s_append(tmp, ".tmp");
+ snprint(buf, sizeof buf, "%s.tmp", mb->path);
/*
* preserve old files permissions, if possible
*/
- d = dirstat(mb->path);
- if(d != nil){
- mode = d->mode&0777;
+ mode = Mboxmode;
+ if(d = dirstat(mb->path)){
+ mode = d->mode & 0777;
free(d);
- } else
- mode = MBOXMODE;
+ }
- sysremove(s_to_c(tmp));
- b = sysopen(s_to_c(tmp), "alc", mode);
+ remove(buf);
+ b = sysopen(buf, "alc", mode);
if(b == 0){
- fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
+ eprint("plan9: can't write temporary mailbox %s: %r\n", buf);
return;
}
- logmsg("writing new mbox", nil);
+ logmsg(nil, "writing new mbox");
errs = 0;
for(m = mb->root->part; m != nil; m = m->next){
if(lk != nil)
syslockrefresh(lk);
if(m->deleted)
continue;
- logmsg("writing", m);
+ logmsg(m, "writing");
if(Bwrite(b, m->start, m->end - m->start) < 0)
errs = 1;
if(Bwrite(b, "\n", 1) < 0)
errs = 1;
}
- logmsg("wrote new mbox", nil);
+ logmsg(nil, "wrote new mbox");
if(sysclose(b) < 0)
errs = 1;
if(errs){
- fprint(2, "error writing temporary mail file\n");
- s_free(tmp);
+ eprint("plan9: error writing temporary mail file\n");
return;
}
- sysremove(mb->path);
- if(sysrename(s_to_c(tmp), mb->path) < 0)
- fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
- s_to_c(tmp), mb->path);
- s_free(tmp);
+ remove(mb->path);
+ if(sysrename(buf, mb->path) < 0)
+ eprint("plan9: can't rename %s to %s: %r\n",
+ buf, mb->path);
if(mb->d != nil)
free(mb->d);
mb->d = dirstat(mb->path);
}
char*
-plan9syncmbox(Mailbox *mb, int doplumb)
+plan9syncmbox(Mailbox *mb, int doplumb, int *new)
{
- Mlock *lk;
char *rv;
+ Mlock *lk;
lk = nil;
if(mb->dolock){
@@ -379,9 +420,9 @@ plan9syncmbox(Mailbox *mb, int doplumb)
return "can't lock mailbox";
}
- rv = _readmbox(mb, doplumb, lk); /* interpolate */
+ rv = readmbox(mb, doplumb, new, lk); /* interpolate */
if(purgedeleted(mb) > 0)
- _writembox(mb, lk);
+ writembox(mb, lk);
if(lk != nil)
sysunlock(lk);
@@ -389,26 +430,30 @@ plan9syncmbox(Mailbox *mb, int doplumb)
return rv;
}
-//
-// look to see if we can open this mail box
-//
+void
+plan9decache(Mailbox*, Message *m)
+{
+ m->lim = 0;
+}
+
+/*
+ * look to see if we can open this mail box
+ */
char*
plan9mbox(Mailbox *mb, char *path)
{
- static char err[Errlen];
- String *tmp;
+ char buf[Pathlen];
+ static char err[Pathlen];
if(access(path, AEXIST) < 0){
- errstr(err, sizeof(err));
- tmp = s_copy(path);
- s_append(tmp, ".tmp");
- if(access(s_to_c(tmp), AEXIST) < 0){
- s_free(tmp);
+ errstr(err, sizeof err);
+ snprint(buf, sizeof buf, "%s.tmp", path);
+ if(access(buf, AEXIST) < 0)
return err;
- }
- s_free(tmp);
}
-
mb->sync = plan9syncmbox;
+ mb->remove = localremove;
+ mb->rename = localrename;
+ mb->decache = plan9decache;
return nil;
}
diff --git a/sys/src/cmd/upas/fs/planb.c b/sys/src/cmd/upas/fs/planb.c
index 58dfe1622..fbe2ac251 100644
--- a/sys/src/cmd/upas/fs/planb.c
+++ b/sys/src/cmd/upas/fs/planb.c
@@ -15,12 +15,37 @@
#include <libsec.h>
#include "dat.h"
+static char*
+parseunix(Message *m)
+{
+ char *s, *p, *q;
+ int l;
+ Tm tm;
+
+ l = m->header - m->start;
+ m->unixheader = smprint("%.*s", l, m->start);
+ s = m->start + 5;
+ if((p = strchr(s, ' ')) == nil)
+ return s;
+ *p = 0;
+ m->unixfrom = strdup(s);
+ *p++ = ' ';
+ if(q = strchr(p, '\n'))
+ *q = 0;
+ if(strtotm(p, &tm) < 0)
+ return p;
+ if(q)
+ *q = '\n';
+ m->fileid = (uvlong)tm2sec(&tm) << 8;
+ return 0;
+}
+
static int
readmessage(Message *m, char *msg)
{
- int fd, i, n;
+ int fd, n;
char *buf, *name, *p;
- char hdr[128], sdigest[SHA1dlen*2+1];
+ char hdr[128];
Dir *d;
buf = nil;
@@ -29,8 +54,10 @@ readmessage(Message *m, char *msg)
if(name == nil)
return -1;
if(m->filename != nil)
- s_free(m->filename);
- m->filename = s_copy(name);
+ free(m->filename);
+ m->filename = strdup(name);
+ if(m->filename == nil)
+ sysfatal("malloc: %r");
fd = open(name, OREAD);
if(fd < 0)
goto Fail;
@@ -75,10 +102,7 @@ readmessage(Message *m, char *msg)
m->end--;
*m->end = 0;
m->bend = m->rbend = m->end;
- sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
- for(i = 0; i < SHA1dlen; i++)
- sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
- m->sdigest = s_copy(sdigest);
+
return 0;
Fail:
if(fd >= 0)
@@ -98,7 +122,7 @@ archive(Message *m)
char *dir, *p, *nname;
Dir d;
- dir = strdup(s_to_c(m->filename));
+ dir = strdup(m->filename);
nname = nil;
if(dir == nil)
return;
@@ -162,21 +186,20 @@ mustshow(char* name)
}
static int
-readpbmessage(Mailbox *mb, char *msg, int doplumb)
+readpbmessage(Mailbox *mb, char *msg, int doplumb, int *nnew)
{
Message *m, **l;
- char *x;
+ char *x, *p;
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
if(readmessage(m, msg) < 0){
- delmessage(mb, m);
- mb->root->subname--;
+ unnewmessage(mb, mb->root, m);
return -1;
}
for(l = &mb->root->part; *l != nil; l = &(*l)->next)
- if(strcmp(s_to_c((*l)->filename), s_to_c(m->filename)) == 0 &&
+ if(strcmp((*l)->filename, m->filename) == 0 &&
*l != m){
if((*l)->deleted < 0)
(*l)->deleted = 0;
@@ -184,15 +207,19 @@ readpbmessage(Mailbox *mb, char *msg, int doplumb)
mb->root->subname--;
return -1;
}
- x = strchr(m->start, '\n');
- if(x == nil)
- m->header = m->end;
- else
+ m->header = m->end;
+ if(x = strchr(m->start, '\n'))
m->header = x + 1;
+ if(p = parseunix(m))
+ sysfatal("%s:%s naked From in body? [%s]", mb->path, (*l)->filename, p);
m->mheader = m->mhend = m->header;
- parseunix(m);
- parse(m, 0, mb, 0);
- logmsg("new", m);
+ parse(mb, m, 0, 0);
+ if(m != *l && m->deleted != Dup){
+ logmsg(m, "new");
+ newcachehash(mb, m, doplumb);
+ putcache(mb, m);
+ nnew[0]++;
+ }
/* chain in */
*l = m;
@@ -215,17 +242,18 @@ dcmp(Dir *a, Dir *b)
return strcmp(an, bn);
}
-static void
-readpbmbox(Mailbox *mb, int doplumb)
+static char*
+readpbmbox(Mailbox *mb, int doplumb, int *new)
{
- int fd, i, j, nd, nmd;
char *month, *msg;
+ int fd, i, j, nd, nmd;
Dir *d, *md;
+ static char err[ERRMAX];
fd = open(mb->path, OREAD);
if(fd < 0){
- fprint(2, "%s: %s: %r\n", argv0, mb->path);
- return;
+ errstr(err, sizeof err);
+ return err;
}
nd = dirreadall(fd, &d);
close(fd);
@@ -249,7 +277,7 @@ readpbmbox(Mailbox *mb, int doplumb)
for(j = 0; j < nmd; j++)
if(mustshow(md[j].name)){
msg = smprint("%s/%s", month, md[j].name);
- readpbmessage(mb, msg, doplumb);
+ readpbmessage(mb, msg, doplumb, new);
free(msg);
}
}
@@ -259,45 +287,45 @@ readpbmbox(Mailbox *mb, int doplumb)
md = nil;
}
free(d);
+ return nil;
}
-static void
-readpbvmbox(Mailbox *mb, int doplumb)
+static char*
+readpbvmbox(Mailbox *mb, int doplumb, int *new)
{
+ char *data, *ln, *p, *nln, *msg;
int fd, nr;
long sz;
- char *data, *ln, *p, *nln, *msg;
Dir *d;
+ static char err[ERRMAX];
fd = open(mb->path, OREAD);
if(fd < 0){
- fprint(2, "%s: %s: %r\n", argv0, mb->path);
- return;
+ errstr(err, sizeof err);
+ return err;
}
d = dirfstat(fd);
if(d == nil){
- fprint(2, "%s: %s: %r\n", argv0, mb->path);
- close(fd);
- return;
+ errstr(err, sizeof err);
+ return err;
}
sz = d->length;
free(d);
if(sz > 2 * 1024 * 1024){
sz = 2 * 1024 * 1024;
- fprint(2, "%s: %s: bug: folder too big\n", argv0, mb->path);
+ fprint(2, "upas/fs: %s: bug: folder too big\n", mb->path);
}
data = malloc(sz+1);
if(data == nil){
- close(fd);
- fprint(2, "%s: no memory\n", argv0);
- return;
+ errstr(err, sizeof err);
+ return err;
}
nr = readn(fd, data, sz);
close(fd);
if(nr < 0){
- fprint(2, "%s: %s: %r\n", argv0, mb->path);
+ errstr(err, sizeof err);
free(data);
- return;
+ return err;
}
data[nr] = 0;
@@ -318,22 +346,24 @@ readpbvmbox(Mailbox *mb, int doplumb)
*p = 0;
msg = smprint("/mail/box/%s/msgs/%s", user, ln);
if(msg == nil){
- fprint(2, "%s: no memory\n", argv0);
+ fprint(2, "upas/fs: malloc: %r\n");
continue;
}
- readpbmessage(mb, msg, doplumb);
+ readpbmessage(mb, msg, doplumb, new);
free(msg);
}
free(data);
+ return nil;
}
static char*
-readmbox(Mailbox *mb, int doplumb, int virt)
+readmbox(Mailbox *mb, int doplumb, int virt, int *new)
{
+ char *mberr;
int fd;
Dir *d;
Message *m;
- static char err[Errlen];
+ static char err[128];
if(debug)
fprint(2, "read mbox %s\n", mb->path);
@@ -364,45 +394,45 @@ readmbox(Mailbox *mb, int doplumb, int virt)
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
snprint(err, sizeof err, "reading '%s'", mb->path);
- logmsg(err, nil);
+ logmsg(nil, err, nil);
for(m = mb->root->part; m != nil; m = m->next)
if(m->deleted == 0)
m->deleted = -1;
if(virt == 0)
- readpbmbox(mb, doplumb);
+ mberr = readpbmbox(mb, doplumb, new);
else
- readpbvmbox(mb, doplumb);
+ mberr = readpbvmbox(mb, doplumb, new);
/*
* messages removed from the mbox; flag them to go.
*/
for(m = mb->root->part; m != nil; m = m->next)
if(m->deleted < 0 && doplumb){
- m->inmbox = 0;
- m->deleted = 1;
- mailplumb(mb, m, 1);
+ delmessage(mb, m);
+ if(doplumb)
+ mailplumb(mb, m, 1);
}
- logmsg("mbox read", nil);
- return nil;
+ logmsg(nil, "mbox read");
+ return mberr;
}
static char*
-mbsync(Mailbox *mb, int doplumb)
+mbsync(Mailbox *mb, int doplumb, int *new)
{
char *rv;
- rv = readmbox(mb, doplumb, 0);
+ rv = readmbox(mb, doplumb, 0, new);
purgembox(mb, 0);
return rv;
}
static char*
-mbvsync(Mailbox *mb, int doplumb)
+mbvsync(Mailbox *mb, int doplumb, int *new)
{
char *rv;
- rv = readmbox(mb, doplumb, 1);
+ rv = readmbox(mb, doplumb, 1, new);
purgembox(mb, 1);
return rv;
}
diff --git a/sys/src/cmd/upas/fs/pop3.c b/sys/src/cmd/upas/fs/pop3.c
index ac8cb3cd0..750646d9b 100644
--- a/sys/src/cmd/upas/fs/pop3.c
+++ b/sys/src/cmd/upas/fs/pop3.c
@@ -1,54 +1,64 @@
#include "common.h"
-#include <ctype.h>
-#include <plumb.h>
#include <libsec.h>
#include <auth.h>
#include "dat.h"
#pragma varargck type "M" uchar*
#pragma varargck argpos pop3cmd 2
+#define pdprint(p, ...) if((p)->debug) fprint(2, __VA_ARGS__); else{}
+
+typedef struct Popm Popm;
+struct Popm{
+ int mesgno;
+};
typedef struct Pop Pop;
struct Pop {
- char *freep; // free this to free the strings below
-
- char *host;
- char *user;
- char *port;
-
- int ppop;
- int refreshtime;
- int debug;
- int pipeline;
- int encrypted;
- int needtls;
- int notls;
- int needssl;
-
- // open network connection
- Biobuf bin;
- Biobuf bout;
- int fd;
- char *lastline; // from Brdstr
-
+ char *freep; /* free this to free the strings below */
+ char *host;
+ char *user;
+ char *port;
+
+ int ppop;
+ int refreshtime;
+ int debug;
+ int pipeline;
+ int encrypted;
+ int needtls;
+ int notls;
+ int needssl;
+
+ Biobuf bin; /* open network connection */
+ Biobuf bout;
+ int fd;
+ char *lastline; /* from Brdstr */
Thumbprint *thumb;
};
-char*
+static int
+mesgno(Message *m)
+{
+ Popm *a;
+
+ a = m->aux;
+ return a->mesgno;
+}
+
+static char*
geterrstr(void)
{
- static char err[Errlen];
+ static char err[64];
err[0] = '\0';
errstr(err, sizeof(err));
return err;
}
-//
-// get pop3 response line , without worrying
-// about multiline responses; the clients
-// will deal with that.
-//
+/*
+ * get pop3 response line , without worrying
+ * about multiline responses; the clients
+ * will deal with that.
+ */
static int
isokay(char *s)
{
@@ -62,15 +72,13 @@ pop3cmd(Pop *pop, char *fmt, ...)
va_list va;
va_start(va, fmt);
- vseprint(buf, buf+sizeof(buf), fmt, va);
+ vseprint(buf, buf + sizeof buf, fmt, va);
va_end(va);
- p = buf+strlen(buf);
- if(p > (buf+sizeof(buf)-3))
+ p = buf + strlen(buf);
+ if(p > buf + sizeof buf - 3)
sysfatal("pop3 command too long");
-
- if(pop->debug)
- fprint(2, "<- %s\n", buf);
+ pdprint(pop, "<- %s\n", buf);
strcpy(p, "\r\n");
Bwrite(&pop->bout, buf, strlen(buf));
Bflush(&pop->bout);
@@ -83,20 +91,19 @@ pop3resp(Pop *pop)
char *p;
alarm(60*1000);
- s = Brdstr(&pop->bin, '\n', 0);
- alarm(0);
- if(s == nil){
+ if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
close(pop->fd);
pop->fd = -1;
+ alarm(0);
return "unexpected eof";
}
+ alarm(0);
- p = s+strlen(s)-1;
+ p = s + strlen(s) - 1;
while(p >= s && (*p == '\r' || *p == '\n'))
*p-- = '\0';
- if(pop->debug)
- fprint(2, "-> %s\n", s);
+ pdprint(pop, "-> %s\n", s);
free(pop->lastline);
pop->lastline = s;
return s;
@@ -119,40 +126,35 @@ pop3pushtls(Pop *pop)
int fd;
uchar digest[SHA1dlen];
TLSconn conn;
- char *err;
- err = nil;
memset(&conn, 0, sizeof conn);
// conn.trace = pop3log;
fd = tlsClient(pop->fd, &conn);
- if(fd < 0){
- err = "tls error";
- goto out;
- }
- pop->fd = fd;
- Binit(&pop->bin, pop->fd, OREAD);
- Binit(&pop->bout, pop->fd, OWRITE);
+ if(fd < 0)
+ return "tls error";
if(conn.cert==nil || conn.certlen <= 0){
- err = "server did not provide TLS certificate";
- goto out;
+ close(fd);
+ return "server did not provide TLS certificate";
}
sha1(conn.cert, conn.certlen, digest, nil);
if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
- fmtinstall('H', encodefmt);
- fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
- err = "bad server certificate";
- goto out;
+ close(fd);
+ free(conn.cert);
+ eprint("pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
+ return "bad server certificate";
}
- pop->encrypted = 1;
-out:
- free(conn.sessionID);
free(conn.cert);
- return err;
+ close(pop->fd);
+ pop->fd = fd;
+ pop->encrypted = 1;
+ Binit(&pop->bin, pop->fd, OREAD);
+ Binit(&pop->bout, pop->fd, OWRITE);
+ return nil;
}
-//
-// get capability list, possibly start tls
-//
+/*
+ * get capability list, possibly start tls
+ */
static char*
pop3capa(Pop *pop)
{
@@ -186,9 +188,9 @@ pop3capa(Pop *pop)
return nil;
}
-//
-// log in using APOP if possible, password if allowed by user
-//
+/*
+ * log in using APOP if possible, password if allowed by user
+ */
static char*
pop3login(Pop *pop)
{
@@ -207,10 +209,10 @@ pop3login(Pop *pop)
else
ubuf[0] = '\0';
- // look for apop banner
- if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
+ /* look for apop banner */
+ if(pop->ppop == 0 && (p = strchr(s, '<')) && (q = strchr(p + 1, '>'))) {
*++q = '\0';
- if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
+ if((n=auth_respond(p, q - p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
pop->host, ubuf)) < 0)
return "factotum failed";
if(user[0]=='\0')
@@ -253,83 +255,75 @@ pop3login(Pop *pop)
}
}
-//
-// dial and handshake with pop server
-//
+/*
+ * dial and handshake with pop server
+ */
static char*
pop3dial(Pop *pop)
{
char *err;
- if(pop->fd >= 0){
- close(pop->fd);
- pop->fd = -1;
- }
if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
return geterrstr();
if(pop->needssl){
if((err = pop3pushtls(pop)) != nil)
- goto Out;
+ return err;
}else{
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
}
- err = pop3login(pop);
-Out:
- if(err != nil){
- if(pop->fd >= 0){
- close(pop->fd);
- pop->fd = -1;
- }
+
+ if(err = pop3login(pop)) {
+ close(pop->fd);
+ return err;
}
- return err;
+
+ return nil;
}
-//
-// close connection
-//
+/*
+ * close connection
+ */
static void
pop3hangup(Pop *pop)
{
- if(pop->fd < 0)
- return;
pop3cmd(pop, "QUIT");
pop3resp(pop);
close(pop->fd);
- pop->fd = -1;
}
-//
-// download a single message
-//
+/*
+ * download a single message
+ */
static char*
-pop3download(Pop *pop, Message *m)
+pop3download(Mailbox *mb, Pop *pop, Message *m)
{
char *s, *f[3], *wp, *ep;
- char sdigest[SHA1dlen*2+1];
- int i, l, sz;
+ int l, sz, pos, n;
+ Popm *a;
+ a = m->aux;
if(!pop->pipeline)
- pop3cmd(pop, "LIST %d", m->mesgno);
+ pop3cmd(pop, "LIST %d", a->mesgno);
if(!isokay(s = pop3resp(pop)))
return s;
if(tokenize(s, f, 3) != 3)
return "syntax error in LIST response";
- if(atoi(f[1]) != m->mesgno)
+ if(atoi(f[1]) != a->mesgno)
return "out of sync with pop3 server";
- sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
+ sz = atoi(f[2]) + 200; /* 200 because the plan9 pop3 server lies */
if(sz == 0)
return "invalid size in LIST response";
- m->start = wp = emalloc(sz+1);
- ep = wp+sz;
+ m->start = wp = emalloc(sz + 1);
+ ep = wp + sz;
if(!pop->pipeline)
- pop3cmd(pop, "RETR %d", m->mesgno);
+ pop3cmd(pop, "RETR %d", a->mesgno);
if(!isokay(s = pop3resp(pop))) {
m->start = nil;
free(wp);
@@ -347,7 +341,7 @@ pop3download(Pop *pop, Message *m)
if(strcmp(s, ".") == 0)
break;
- l = strlen(s)+1;
+ l = strlen(s) + 1;
if(s[0] == '.') {
s++;
l--;
@@ -356,14 +350,17 @@ pop3download(Pop *pop, Message *m)
* grow by 10%/200bytes - some servers
* lie about message sizes
*/
- if(wp+l > ep) {
- int pos = wp - m->start;
- sz += ((sz / 10) < 200)? 200: sz/10;
- m->start = erealloc(m->start, sz+1);
- wp = m->start+pos;
- ep = m->start+sz;
+ if(wp + l > ep) {
+ pos = wp - m->start;
+ n = sz/10;
+ if(n < 200)
+ n = 200;
+ sz += n;
+ m->start = erealloc(m->start, sz + 1);
+ wp = m->start + pos;
+ ep = m->start + sz;
}
- memmove(wp, s, l-1);
+ memmove(wp, s, l - 1);
wp[l-1] = '\n';
wp += l;
}
@@ -373,40 +370,41 @@ pop3download(Pop *pop, Message *m)
m->end = wp;
- // make sure there's a trailing null
- // (helps in body searches)
+ /*
+ * make sure there's a trailing null
+ * (helps in body searches)
+ */
*m->end = 0;
m->bend = m->rbend = m->end;
m->header = m->start;
-
- // digest message
- sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
- for(i = 0; i < SHA1dlen; i++)
- sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
- m->sdigest = s_copy(sdigest);
+ m->size = m->end - m->start;
+ if(m->digest == nil)
+ digestmessage(mb, m);
return nil;
}
-//
-// check for new messages on pop server
-// UIDL is not required by RFC 1939, but
-// netscape requires it, so almost every server supports it.
-// we'll use it to make our lives easier.
-//
+/*
+ * check for new messages on pop server
+ * UIDL is not required by RFC 1939, but
+ * netscape requires it, so almost every server supports it.
+ * we'll use it to make our lives easier.
+ */
static char*
-pop3read(Pop *pop, Mailbox *mb, int doplumb)
+pop3read(Pop *pop, Mailbox *mb, int doplumb, int *new)
{
char *s, *p, *uidl, *f[2];
- int mesgno, ignore, nnew;
+ int mno, ignore, nnew;
Message *m, *next, **l;
+ Popm *a;
- // Some POP servers disallow UIDL if the maildrop is empty.
+ *new = 0;
+ /* Some POP servers disallow UIDL if the maildrop is empty. */
pop3cmd(pop, "STAT");
if(!isokay(s = pop3resp(pop)))
return s;
- // fetch message listing; note messages to grab
+ /* fetch message listing; note messages to grab */
l = &mb->root->part;
if(strncmp(s, "+OK 0 ", 6) != 0) {
pop3cmd(pop, "UIDL");
@@ -421,25 +419,32 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
if(tokenize(p, f, 2) != 2)
continue;
- mesgno = atoi(f[0]);
+ mno = atoi(f[0]);
uidl = f[1];
- if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
+ if(strlen(uidl) > 75) /* RFC 1939 says 70 characters max */
continue;
ignore = 0;
while(*l != nil) {
- if(strcmp((*l)->uidl, uidl) == 0) {
- // matches mail we already have, note mesgno for deletion
- (*l)->mesgno = mesgno;
+ a = (*l)->aux;
+ if(strcmp((*l)->idxaux, uidl) == 0){
+ if(a == 0){
+ m = *l;
+ m->mallocd = 1;
+ m->inmbox = 1;
+ m->aux = a = emalloc(sizeof *a);
+ }
+ /* matches mail we already have, note mesgno for deletion */
+ a->mesgno = mno;
ignore = 1;
l = &(*l)->next;
break;
- } else {
- // old mail no longer in box mark deleted
+ }else{
+ /* old mail no longer in box mark deleted */
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
- (*l)->deleted = 1;
+ (*l)->deleted = Deleted;
l = &(*l)->next;
}
}
@@ -449,30 +454,31 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
- m->mesgno = mesgno;
- strcpy(m->uidl, uidl);
+ m->idxaux = strdup(uidl);
+ m->aux = a = emalloc(sizeof *a);
+ a->mesgno = mno;
- // chain in; will fill in message later
+ /* chain in; will fill in message later */
*l = m;
l = &m->next;
}
}
- // whatever is left has been removed from the mbox, mark as deleted
+ /* whatever is left has been removed from the mbox, mark as deleted */
while(*l != nil) {
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
- (*l)->deleted = 1;
+ (*l)->deleted = Disappear;
l = &(*l)->next;
}
- // download new messages
+ /* download new messages */
nnew = 0;
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
- fprint(2, "rfork: %r\n");
+ eprint("pop3: rfork: %r\n");
pop->pipeline = 0;
default:
@@ -480,49 +486,41 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
case 0:
for(m = mb->root->part; m != nil; m = m->next){
- if(m->start != nil)
+ if(m->start != nil || m->deleted)
continue;
- Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
+ Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", mesgno(m), mesgno(m));
}
Bflush(&pop->bout);
- _exits(nil);
+ _exits("");
}
}
for(m = mb->root->part; m != nil; m = next) {
next = m->next;
- if(m->start != nil)
+ if(m->start != nil || m->deleted)
continue;
-
- if(s = pop3download(pop, m)) {
- // message disappeared? unchain
- fprint(2, "download %d: %s\n", m->mesgno, s);
+ if(s = pop3download(mb, pop, m)) {
+ /* message disappeared? unchain */
+ eprint("pop3: download %d: %s\n", mesgno(m), s);
delmessage(mb, m);
mb->root->subname--;
continue;
}
nnew++;
- parse(m, 0, mb, 1);
-
- if(doplumb)
- mailplumb(mb, m, 0);
+ parse(mb, m, 1, 0);
+ newcachehash(mb, m, doplumb);
+ putcache(mb, m);
}
if(pop->pipeline)
waitpid();
-
- if(nnew || mb->vers == 0) {
- mb->vers++;
- henter(PATH(0, Qtop), mb->name,
- (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
- }
-
+ *new = nnew;
return nil;
}
-//
-// delete marked messages
-//
+/*
+ * delete marked messages
+ */
static void
pop3purge(Pop *pop, Mailbox *mb)
{
@@ -531,7 +529,7 @@ pop3purge(Pop *pop, Mailbox *mb)
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
- fprint(2, "rfork: %r\n");
+ eprint("pop3: rfork: %r\n");
pop->pipeline = 0;
default:
@@ -542,11 +540,11 @@ pop3purge(Pop *pop, Mailbox *mb)
next = m->next;
if(m->deleted && m->refs == 0){
if(m->inmbox)
- Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
+ Bprint(&pop->bout, "DELE %d\r\n", mesgno(m));
}
}
Bflush(&pop->bout);
- _exits(nil);
+ _exits("");
}
}
for(m = mb->root->part; m != nil; m = next) {
@@ -554,7 +552,7 @@ pop3purge(Pop *pop, Mailbox *mb)
if(m->deleted && m->refs == 0) {
if(m->inmbox) {
if(!pop->pipeline)
- pop3cmd(pop, "DELE %d", m->mesgno);
+ pop3cmd(pop, "DELE %d", mesgno(m));
if(isokay(pop3resp(pop)))
delmessage(mb, m);
} else
@@ -564,19 +562,21 @@ pop3purge(Pop *pop, Mailbox *mb)
}
-// connect to pop3 server, sync mailbox
+/* connect to pop3 server, sync mailbox */
static char*
-pop3sync(Mailbox *mb, int doplumb)
+pop3sync(Mailbox *mb, int doplumb, int *new)
{
char *err;
Pop *pop;
pop = mb->aux;
+
if(err = pop3dial(pop)) {
mb->waketime = time(0) + pop->refreshtime;
return err;
}
- if((err = pop3read(pop, mb, doplumb)) == nil){
+
+ if((err = pop3read(pop, mb, doplumb, new)) == nil){
pop3purge(pop, mb);
mb->d->atime = mb->d->mtime = time(0);
}
@@ -629,7 +629,7 @@ pop3ctl(Mailbox *mb, int argc, char **argv)
return Epop3ctl;
}
-// free extra memory associated with mb
+/* free extra memory associated with mb */
static void
pop3close(Mailbox *mb)
{
@@ -640,9 +640,18 @@ pop3close(Mailbox *mb)
free(pop);
}
-//
-// open mailboxes of the form /pop/host/user or /apop/host/user
-//
+static char*
+mkmbox(Pop *pop, char *p, char *e)
+{
+ p = seprint(p, e, "%s/box/%s/pop.%s", MAILROOT, getlog(), pop->host);
+ if(pop->user && strcmp(pop->user, getlog()))
+ p = seprint(p, e, ".%s", pop->user);
+ return p;
+}
+
+/*
+ * open mailboxes of the form /pop/host/user or /apop/host/user
+ */
char*
pop3mbox(Mailbox *mb, char *path)
{
@@ -650,7 +659,6 @@ pop3mbox(Mailbox *mb, char *path)
int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
Pop *pop;
- quotefmtinstall();
popssl = strncmp(path, "/pops/", 6) == 0;
apopssl = strncmp(path, "/apops/", 7) == 0;
poptls = strncmp(path, "/poptls/", 8) == 0;
@@ -673,8 +681,7 @@ pop3mbox(Mailbox *mb, char *path)
return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
}
- pop = emalloc(sizeof(*pop));
- pop->fd = -1;
+ pop = emalloc(sizeof *pop);
pop->freep = path;
pop->host = f[2];
if(nf < 4)
@@ -688,12 +695,13 @@ pop3mbox(Mailbox *mb, char *path)
pop->notls = popnotls || apopnotls;
pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
+ mkmbox(pop, mb->path, mb->path + sizeof mb->path);
mb->aux = pop;
mb->sync = pop3sync;
mb->close = pop3close;
mb->ctl = pop3ctl;
- mb->d = emalloc(sizeof(*mb->d));
-
+ mb->d = emalloc(sizeof *mb->d);
+ mb->addfrom = 1;
return nil;
}
diff --git a/sys/src/cmd/upas/fs/readdir.c b/sys/src/cmd/upas/fs/readdir.c
deleted file mode 100644
index 42028d4c1..000000000
--- a/sys/src/cmd/upas/fs/readdir.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <u.h>
-#include <libc.h>
-
-void
-main(void)
-{
- Dir d;
- int fd, n;
-
- fd = open("/mail/fs", OREAD);
- while((n = dirread(fd, &d, sizeof(d))) > 0){
- print("%s\n", d.name);
- }
- print("n = %d\n", n);
-}
diff --git a/sys/src/cmd/upas/fs/ref.c b/sys/src/cmd/upas/fs/ref.c
new file mode 100644
index 000000000..f3cf078b3
--- /dev/null
+++ b/sys/src/cmd/upas/fs/ref.c
@@ -0,0 +1,100 @@
+#include "common.h"
+#include <libsec.h>
+#include "dat.h"
+
+/* all the data that's fit to cache */
+
+typedef struct{
+ char *s;
+ int l;
+ ulong ref;
+}Refs;
+
+Refs *rtab;
+int nrtab;
+int nralloc;
+
+int
+newrefs(char *s)
+{
+ int l, i;
+ Refs *r;
+
+ l = strlen(s);
+ for(i = 0; i < nrtab; i++){
+ r = rtab + i;
+ if(r->ref == 0)
+ goto enter;
+ if(l == r->l && strcmp(r->s, s) == 0){
+ r->ref++;
+ return i;
+ }
+ }
+ if(nrtab == nralloc)
+ rtab = erealloc(rtab, sizeof *rtab*(nralloc += 50));
+ nrtab = i + 1;
+enter:
+ r = rtab + i;
+ r->s = strdup(s);
+ r->l = l;
+ r->ref = 1;
+ return i;
+}
+
+void
+delrefs(int i)
+{
+ Refs *r;
+
+ r = rtab + i;
+ if(--r->ref > 0)
+ return;
+ free(r->s);
+ memset(r, 0, sizeof *r);
+}
+
+void
+refsinit(void)
+{
+ newrefs("");
+}
+
+static char *sep = "--------\n";
+
+int
+prrefs(Biobuf *b)
+{
+ int i, n;
+
+ n = 0;
+ for(i = 1; i < nrtab; i++){
+ if(rtab[i].ref == 0)
+ continue;
+ Bprint(b, "%s ", rtab[i].s);
+ if(n++%8 == 7)
+ Bprint(b, "\n");
+ }
+ if(n%8 != 7)
+ Bprint(b, "\n");
+ Bprint(b, sep);
+ return 0;
+}
+
+int
+rdrefs(Biobuf *b)
+{
+ char *f[10], *s;
+ int i, n;
+
+ while(s = Brdstr(b, '\n', 1)){
+ if(strcmp(s, sep) == 0){
+ free(s);
+ return 0;
+ }
+ n = tokenize(s, f, nelem(f));
+ for(i = 0; i < n; i++)
+ newrefs(f[i]);
+ free(s);
+ }
+ return -1;
+}
diff --git a/sys/src/cmd/upas/fs/remove.c b/sys/src/cmd/upas/fs/remove.c
new file mode 100644
index 000000000..5bafe8a70
--- /dev/null
+++ b/sys/src/cmd/upas/fs/remove.c
@@ -0,0 +1,141 @@
+#include "common.h"
+#include "dat.h"
+
+#define deprint(...) /* eprint(__VA_ARGS__) */
+
+extern int dirskip(Dir*, uvlong*);
+
+static int
+ismbox(char *path)
+{
+ char buf[512];
+ int fd, r;
+
+ fd = open(path, OREAD);
+ if(fd == -1)
+ return 0;
+ r = 1;
+ if(read(fd, buf, sizeof buf) < 28 + 5)
+ r = 0;
+ else if(strncmp(buf, "From ", 5))
+ r = 0;
+ close(fd);
+ return r;
+}
+
+static int
+isindex(Dir *d)
+{
+ char *p;
+
+ p = strrchr(d->name, '.');
+ if(!p)
+ return -1;
+ if(strcmp(p, ".idx") || strcmp(p, ".imp"))
+ return 1;
+ return 0;
+}
+
+static int
+idiotcheck(char *path, Dir *d, int getindex)
+{
+ uvlong v;
+
+ if(d->mode & DMDIR)
+ return 0;
+ if(strncmp(d->name, "L.", 2) == 0)
+ return 0;
+ if(getindex && isindex(d))
+ return 0;
+ if(!dirskip(d, &v) || ismbox(path))
+ return 0;
+ return -1;
+}
+
+int
+vremove(char *buf)
+{
+ deprint("rm %s\n", buf);
+ return remove(buf);
+}
+
+static int
+rm(char *dir, int flags, int level)
+{
+ char buf[Pathlen];
+ int i, n, r, fd, isdir, rflag;
+ Dir *d;
+
+ d = dirstat(dir);
+ isdir = d->mode & DMDIR;
+ free(d);
+ if(!isdir)
+ return 0;
+ fd = open(dir, OREAD);
+ if(fd == -1)
+ return -1;
+ n = dirreadall(fd, &d);
+ close(fd);
+ r = 0;
+ rflag = flags & Rrecur;
+ for(i = 0; i < n; i++){
+ snprint(buf, sizeof buf, "%s/%s", dir, d[i].name);
+ if(rflag)
+ r |= rm(buf, flags, level + 1);
+ if(idiotcheck(buf, d + i, level + rflag) == -1)
+ continue;
+ if(vremove(buf) != 0)
+ r = -1;
+ }
+ free(d);
+ return r;
+}
+
+void
+rmidx(char *buf, int flags)
+{
+ char buf2[Pathlen];
+
+ snprint(buf2, sizeof buf2, "%s.idx", buf);
+ vremove(buf2);
+ if((flags & Rtrunc) == 0){
+ snprint(buf2, sizeof buf2, "%s.imp", buf);
+ vremove(buf2);
+ }
+}
+
+char*
+localremove(Mailbox *mb, int flags)
+{
+ char *msg, *path;
+ int r, isdir;
+ Dir *d;
+ static char err[2*Pathlen];
+
+ path = mb->path;
+ if((d = dirstat(path)) == 0){
+ snprint(err, sizeof err, "%s: doesn't exist\n", path);
+ return 0;
+ }
+ isdir = d->mode & DMDIR;
+ free(d);
+ msg = "deleting";
+ if(flags & Rtrunc)
+ msg = "truncating";
+ deprint("%s: %s\n", msg, path);
+
+ /* must match folder.c:/^openfolder */
+ r = rm(path, flags, 0);
+ if((flags & Rtrunc) == 0)
+ r = vremove(path);
+ else if(!isdir)
+ close(r = open(path, OWRITE|OTRUNC));
+
+ rmidx(path, flags);
+
+ if(r == -1){
+ snprint(err, sizeof err, "%s: can't %s\n", path, msg);
+ return err;
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/upas/fs/rename.c b/sys/src/cmd/upas/fs/rename.c
new file mode 100644
index 000000000..6d5337643
--- /dev/null
+++ b/sys/src/cmd/upas/fs/rename.c
@@ -0,0 +1,234 @@
+#include "common.h"
+#include "dat.h"
+
+#define deprint(...) /* eprint(__VA_ARGS__) */
+
+static int
+delivery(char *s)
+{
+ if(strncmp(s, "/mail/fs/", 9) == 0)
+ if((s = strrchr(s, '/')) && strcmp(s + 1, "mbox") == 0)
+ return 1;
+ return 0;
+}
+
+static int
+isdir(char *s)
+{
+ int isdir;
+ Dir *d;
+
+ d = dirstat(s);
+ isdir = d && d->mode & DMDIR;
+ free(d);
+ return isdir;
+}
+
+static int
+docreate(char *file, int perm)
+{
+ int fd;
+ Dir ndir;
+ Dir *d;
+
+ fd = create(file, OREAD, perm);
+ if(fd < 0)
+ return -1;
+ d = dirfstat(fd);
+ if(d == nil)
+ return -1;
+ nulldir(&ndir);
+ ndir.mode = perm;
+ ndir.gid = d->uid;
+ dirfwstat(fd, &ndir);
+ close(fd);
+ return 0;
+}
+
+static int
+rollup(char *s)
+{
+ char *p;
+ int mode;
+
+ if(access(s, 0) == 0)
+ return -1;
+
+ /*
+ * if we can deliver to this mbox, it needs
+ * to be read/execable all the way down
+ */
+ mode = 0711;
+ if(delivery(s))
+ mode = 0755;
+
+ for(p = s; p; p++) {
+ if(*p == '/')
+ continue;
+ p = strchr(p, '/');
+ if(p == 0)
+ break;
+ *p = 0;
+ if(access(s, 0) != 0)
+ if(docreate(s, DMDIR|mode) < 0)
+ return -1;
+ *p = '/';
+ }
+ return 0;
+}
+
+static int
+copyfile(char *a, char *b, int flags)
+{
+ char *s;
+ int fd, fd1, mode, i, m, n, r;
+ Dir *d;
+
+ mode = 0600;
+ if(delivery(b))
+ mode = 0622;
+ fd = open(a, OREAD);
+ fd1 = create(b, OWRITE|OEXCL, DMEXCL|mode);
+ if(fd == -1 || fd1 == -1){
+ close(fd);
+ close(fd1);
+ return -1;
+ }
+ s = malloc(64*1024);
+ i = m = 0;
+ while((n = read(fd, s, sizeof s)) > 0)
+ for(i = 0; i != n; i += m)
+ if((m = write(fd1, s + i, n - i)) == -1)
+ goto lose;
+lose:
+ free(s);
+ close(fd);
+ close(fd1);
+ if(i != m || n != 0)
+ return -1;
+
+ if((flags & Rtrunc) == 0)
+ return vremove(a);
+
+ fd = open(a, ORDWR);
+ if(fd == -1)
+ return -1;
+ r = -1;
+ if(d = dirfstat(fd)){
+ d->length = 0;
+ r = dirfwstat(fd, d);
+ free(d);
+ }
+ return r;
+}
+
+static int
+copydir(char *a, char *b, int flags)
+{
+ char *p, buf[Pathlen], ns[Pathlen], owd[Pathlen];
+ int fd, fd1, len, i, n, r;
+ Dir *d;
+
+ fd = open(a, OREAD);
+ fd1 = create(b, OWRITE|OEXCL, DMEXCL|0777);
+ close(fd1);
+ if(fd == -1 || fd1 == -1){
+ close(fd);
+ return -1;
+ }
+
+ /* fixup mode */
+ if(delivery(b))
+ if(d = dirfstat(fd)){
+ d->mode |= 0777;
+ dirfwstat(fd, d);
+ free(d);
+ }
+
+ getwd(owd, sizeof owd);
+ if(chdir(a) == -1)
+ return -1;
+
+ p = seprint(buf, buf + sizeof buf, "%s/", b);
+ len = buf + sizeof buf - p;
+ n = dirreadall(fd, &d);
+ r = 0;
+ for(i = 0; i < n; i++){
+ snprint(p, len, "%s", d[i].name);
+ if(d->mode & DMDIR){
+ snprint(ns, sizeof ns, "%s/%s", a, d[i].name);
+ r |= copydir(ns, buf, 0);
+ chdir(a);
+ }else
+ r |= copyfile(d[i].name, buf, 0);
+ if(r)
+ break;
+ }
+ free(d);
+
+ if((flags & Rtrunc) == 0)
+ r |= vremove(a);
+
+ chdir(owd);
+ return r;
+}
+
+int
+rename(char *a, char *b, int flags)
+{
+ char *e0, *e1;
+ int fd, r;
+ Dir *d;
+
+ e0 = strrchr(a, '/');
+ e1 = strrchr(b, '/');
+ if(!e0 || !e1 || !e1[1])
+ return -1;
+
+ if(e0 - a == e1 - b)
+ if(strncmp(a, b, e0 - a) == 0)
+ if(!delivery(a) || isdir(a)){
+ fd = open(a, OREAD);
+ if(!(d = dirfstat(fd))){
+ close(fd);
+ return -1;
+ }
+ d->name = e1 + 1;
+ r = dirfwstat(fd, d);
+ deprint("rename %s %s -> %d\n", a, b, r);
+ if(r != -1 && flags & Rtrunc)
+ close(create(a, OWRITE, d->mode));
+ free(d);
+ close(fd);
+ return r;
+ }
+
+ if(rollup(b) == -1)
+ return -1;
+ if(isdir(a))
+ return copydir(a, b, flags);
+ return copyfile(a, b, flags);
+}
+
+char*
+localrename(Mailbox *mb, char *p2, int flags)
+{
+ char *path, *msg;
+ int r;
+ static char err[2*Pathlen];
+
+ path = mb->path;
+ msg = "rename";
+ if(flags & Rtrunc)
+ msg = "move";
+ deprint("localrename %s: %s %s\n", msg, path, p2);
+
+ r = rename(path, p2, flags);
+ if(r == -1){
+ snprint(err, sizeof err, "%s: can't %s\n", path, msg);
+ deprint("localrename %s\n", err);
+ return err;
+ }
+ close(r);
+ return 0;
+}
diff --git a/sys/src/cmd/upas/fs/rfc2047-test b/sys/src/cmd/upas/fs/rfc2047-test
deleted file mode 100644
index b536c4b3e..000000000
--- a/sys/src/cmd/upas/fs/rfc2047-test
+++ /dev/null
@@ -1,28 +0,0 @@
-From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
-From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
-To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
-CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
-Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
- =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
-
-From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
-From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
-To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
-Subject: Time for ISO 10646?
-
-From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
-To: Dave Crocker <dcrocker@mordor.stanford.edu>
-Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
-From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
-Subject: Re: RFC-HDR care and feeding
-
-From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
-From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
- (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
-To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
- <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
-Subject: Test of new header generator
-MIME-Version: 1.0
-Content-type: text/plain; charset=ISO-8859-1
-
-
diff --git a/sys/src/cmd/upas/fs/seg.c b/sys/src/cmd/upas/fs/seg.c
new file mode 100644
index 000000000..394b66ad3
--- /dev/null
+++ b/sys/src/cmd/upas/fs/seg.c
@@ -0,0 +1,164 @@
+#include "common.h"
+#include <libsec.h>
+#include "dat.h"
+
+/*
+ * unnatural acts with virtual memory
+ */
+
+typedef struct{
+ int ref;
+ char *va;
+ long sz;
+}S;
+
+static S s[15]; /* 386 only gives 4 */
+static int nstab = nelem(s);
+static long ssem = 1;
+//static ulong thresh = 10*1024*1024;
+static ulong thresh = 1024;
+
+void*
+segmalloc(ulong sz)
+{
+ int i, j;
+ void *va;
+
+ if(sz < thresh)
+ return emalloc(sz);
+ semacquire(&ssem, 1);
+ for(i = 0; i < nstab; i++)
+ if(s[i].ref == 0)
+ goto found;
+notfound:
+ /* errstr not informative; assume we hit seg limit */
+ for(j = nstab - 1; j >= i; j--)
+ if(s[j].ref)
+ break;
+ nstab = j;
+ semrelease(&ssem, 1);
+ return emalloc(sz);
+found:
+ /*
+ * the system doesn't leave any room for expansion
+ */
+ va = segattach(SG_CEXEC, "memory", 0, sz + sz/10 + 4096);
+ if(va == 0)
+ goto notfound;
+ s[i].ref++;
+ s[i].va = va;
+ s[i].sz = sz;
+ semrelease(&ssem, 1);
+ memset(va, 0, sz);
+ return va;
+}
+
+void
+segmfree(void *va)
+{
+ char *a;
+ int i;
+
+ a = va;
+ for(i = 0; i < nstab; i++)
+ if(s[i].va == a)
+ goto found;
+ free(va);
+ return;
+found:
+ semacquire(&ssem, 1);
+ s[i].ref--;
+ s[i].va = 0;
+ s[i].sz = 0;
+ semrelease(&ssem, 1);
+}
+
+void*
+segreallocfixup(int i, ulong sz)
+{
+ char buf[ERRMAX];
+ void *va, *ova;
+
+ rerrstr(buf, sizeof buf);
+ if(strstr(buf, "segments overlap") == 0)
+ sysfatal("segibrk: %r");
+ va = segattach(SG_CEXEC, "memory", 0, sz);
+ if(va == 0)
+ sysfatal("segattach: %r");
+ ova = s[i].va;
+fprint(2, "fix memcpy(%p, %p, %lud)\n", va, ova, s[i].sz);
+ memcpy(va, ova, s[i].sz);
+ s[i].va = va;
+ s[i].sz = sz;
+ segdetach(ova);
+ return va;
+}
+
+void*
+segrealloc(void *va, ulong sz)
+{
+ char *a;
+ int i;
+ ulong sz0;
+
+fprint(2, "segrealloc %p %lud\n", va, sz);
+ if(va == 0)
+ return segmalloc(sz);
+ a = va;
+ for(i = 0; i < nstab; i++)
+ if(s[i].va == a)
+ goto found;
+ if(sz >= thresh)
+ if(a = segmalloc(sz)){
+ sz0 = msize(va);
+ memcpy(a, va, sz0);
+fprint(2, "memset(%p, 0, %lud)\n", a + sz0, sz - sz0);
+ memset(a + sz0, 0, sz - sz0);
+ return a;
+ }
+ return realloc(va, sz);
+found:
+ sz0 = s[i].sz;
+fprint(2, "segbrk(%p, %p)\n", s[i].va, s[i].va + sz);
+ va = segbrk(s[i].va, s[i].va + sz);
+ if(va == (void*)-1 || va < end)
+ return segreallocfixup(i, sz);
+ a = va;
+ if(sz > sz0)
+{
+fprint(2, "memset(%p, 0, %lud)\n", a + sz0, sz - sz0);
+ memset(a + sz0, 0, sz - sz0);
+}
+ s[i].va = va;
+ s[i].sz = sz;
+ return va;
+}
+
+void*
+emalloc(ulong n)
+{
+ void *p;
+fprint(2, "emalloc %lud\n", n);
+ p = mallocz(n, 1);
+ if(!p)
+ sysfatal("malloc %lud: %r", n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+void
+main(void)
+{
+ char *p;
+ int i;
+ ulong sz;
+
+ p = 0;
+ for(i = 0; i < 6; i++){
+ sz = i*512;
+ p = segrealloc(p, sz);
+ memset(p, 0, sz);
+ }
+ segmfree(p);
+ exits("");
+}
diff --git a/sys/src/cmd/upas/fs/strtotm.c b/sys/src/cmd/upas/fs/strtotm.c
index bcf0bcee0..6c9f7d3b6 100644
--- a/sys/src/cmd/upas/fs/strtotm.c
+++ b/sys/src/cmd/upas/fs/strtotm.c
@@ -1,11 +1,10 @@
#include <u.h>
#include <libc.h>
-#include <ctype.h>
static char*
skiptext(char *q)
{
- while(*q!='\0' && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
+ while(*q != '\0' && *q != ' ' && *q != '\t' && *q != '\r' && *q != '\n')
q++;
return q;
}
@@ -13,36 +12,19 @@ skiptext(char *q)
static char*
skipwhite(char *q)
{
- while(*q==' ' || *q=='\t' || *q=='\r' || *q=='\n')
+ while(*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n')
q++;
return q;
}
static char* months[] = {
"jan", "feb", "mar", "apr",
- "may", "jun", "jul", "aug",
+ "may", "jun", "jul", "aug",
"sep", "oct", "nov", "dec"
};
-static int
-strcmplwr(char *a, char *b, int n)
-{
- char *eb;
-
- eb = b+n;
- while(*a && *b && b<eb){
- if(tolower(*a) != tolower(*b))
- return 1;
- a++;
- b++;
- }
- if(b==eb)
- return 0;
- return *a != *b;
-}
-
int
-strtotm(char *p, Tm *tmp)
+strtotm(char *p, Tm *t)
{
char *q, *r;
int j;
@@ -56,58 +38,60 @@ strtotm(char *p, Tm *tmp)
tm.min = -1;
tm.year = -1;
tm.mday = -1;
- for(p=skipwhite(p); *p; p=skipwhite(q)){
+ for(p = skipwhite(p); *p; p = skipwhite(q)){
q = skiptext(p);
/* look for time in hh:mm[:ss] */
- if(r = memchr(p, ':', q-p)){
+ if(r = memchr(p, ':', q - p)){
tm.hour = strtol(p, 0, 10);
- tm.min = strtol(r+1, 0, 10);
- if(r = memchr(r+1, ':', q-(r+1)))
- tm.sec = strtol(r+1, 0, 10);
+ tm.min = strtol(r + 1, 0, 10);
+ if(r = memchr(r + 1, ':', q - (r + 1)))
+ tm.sec = strtol(r + 1, 0, 10);
else
tm.sec = 0;
continue;
}
/* look for month */
- for(j=0; j<12; j++)
- if(strcmplwr(p, months[j], 3)==0){
+ for(j = 0; j < 12; j++)
+ if(cistrncmp(p, months[j], 3) == 0){
tm.mon = j;
break;
}
-
- if(j!=12)
+ if(j != 12)
continue;
/* look for time zone [A-Z][A-Z]T */
- if(q-p==3 && 'A' <= p[0] && p[0] <= 'Z'
- && 'A' <= p[1] && p[1] <= 'Z' && p[2] == 'T'){
- strecpy(tm.zone, tm.zone+4, p);
+ if(q - p == 3)
+ if(p[0] >= 'A' && p[0] <= 'Z')
+ if(p[1] >= 'A' && p[1] <= 'Z')
+ if(p[2] == 'T'){
+ strecpy(tm.zone, tm.zone + 4, p);
continue;
}
- if(p[0]=='+'||p[0]=='-')
- if(q-p==5 && strspn(p+1, "0123456789") == 4){
- delta = (((p[1]-'0')*10+p[2]-'0')*60+(p[3]-'0')*10+p[4]-'0')*60;
+ if(p[0] == '+'||p[0] == '-')
+ if(q - p == 5 && strspn(p + 1, "0123456789") == 4){
+ delta = (((p[1] - '0')*10 + p[2] - '0')*60 + (p[3] - '0')*10 + p[4] - '0')*60;
if(p[0] == '-')
delta = -delta;
continue;
}
- if(strspn(p, "0123456789") == q-p){
+ if(strspn(p, "0123456789") == q - p){
j = strtol(p, nil, 10);
- if(1 <= j && j <= 31)
+ if(j >= 1 && j <= 31)
tm.mday = j;
if(j >= 1900)
- tm.year = j-1900;
+ tm.year = j - 1900;
+ continue;
}
+ //eprint("strtotm: garbage %.*s\n", q - p, p);
}
-
- if(tm.mon<0 || tm.year<0
- || tm.hour<0 || tm.min<0
- || tm.mday<0)
+ if(tm.mon < 0 || tm.year < 0
+ || tm.hour < 0 || tm.min < 0
+ || tm.mday < 0)
return -1;
- *tmp = *localtime(tm2sec(&tm)-delta);
+ *t = *localtime(tm2sec(&tm) - delta);
return 0;
}
diff --git a/sys/src/cmd/upas/fs/tester.c b/sys/src/cmd/upas/fs/tester.c
deleted file mode 100644
index 3d24012ef..000000000
--- a/sys/src/cmd/upas/fs/tester.c
+++ /dev/null
@@ -1,81 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <ctype.h>
-#include <String.h>
-#include "message.h"
-
-Message *root;
-
-void
-prindent(int i)
-{
- for(; i > 0; i--)
- print(" ");
-}
-
-void
-prstring(int indent, char *tag, String *s)
-{
- if(s == nil)
- return;
- prindent(indent+1);
- print("%s %s\n", tag, s_to_c(s));
-}
-
-void
-info(int indent, int mno, Message *m)
-{
- int i;
- Message *nm;
-
- prindent(indent);
- print("%d%c %d ", mno, m->allocated?'*':' ', m->end - m->start);
- if(m->unixfrom != nil)
- print("uf %s ", s_to_c(m->unixfrom));
- if(m->unixdate != nil)
- print("ud %s ", s_to_c(m->unixdate));
- print("\n");
- prstring(indent, "from:", m->from822);
- prstring(indent, "sender:", m->sender822);
- prstring(indent, "to:", m->to822);
- prstring(indent, "cc:", m->cc822);
- prstring(indent, "reply-to:", m->replyto822);
- prstring(indent, "subject:", m->subject822);
- prstring(indent, "date:", m->date822);
- prstring(indent, "filename:", m->filename);
- prstring(indent, "type:", m->type);
- prstring(indent, "charset:", m->charset);
-
- i = 1;
- for(nm = m->part; nm != nil; nm = nm->next){
- info(indent+1, i++, nm);
- }
-}
-
-
-void
-main(int argc, char **argv)
-{
- char *err;
- char *mboxfile;
-
- ARGBEGIN{
- }ARGEND;
-
- if(argc > 0)
- mboxfile = argv[0];
- else
- mboxfile = "./mbox";
-
- root = newmessage(nil);
-
- err = readmbox(mboxfile, &root->part);
- if(err != nil){
- fprint(2, "boom: %s\n", err);
- exits(0);
- }
-
- info(0, 1, root);
-
- exits(0);
-}