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