upas/fs imap fixes and improvements

do incremental imap fetches after startup, fixes validity handling,
record flags correctly when we aren't in the process of directly
updating a message, fixes off by one in flag parsing, fixes
mis-indexing messages in sync when we get an unsolicited fetch
response.
This commit is contained in:
Ori Bernstein 2019-12-01 17:12:19 -08:00
parent 08d6b0f043
commit 84c4c81cee

View file

@ -35,6 +35,7 @@ typedef struct {
uvlong uid; uvlong uid;
ulong sizes; ulong sizes;
ulong dates; ulong dates;
ulong flags;
} Fetchi; } Fetchi;
typedef struct Imap Imap; typedef struct Imap Imap;
@ -51,9 +52,23 @@ struct Imap {
ulong tag; ulong tag;
ulong validity; ulong validity;
ulong newvalidity;
int nmsg; int nmsg;
int size; int size;
/*
* These variables are how we keep track
* of what's been added or deleted. They
* keep a count of the number of uids we
* have processed this sync (nuid), and
* the number we processed last sync
* (muid).
*
* We keep the latest imap state in fetchi,
* and imap4read syncs the information in
* it with the messages. That's how we know
* something changed on the server.
*/
Fetchi *f; Fetchi *f;
int nuid; int nuid;
int muid; int muid;
@ -163,20 +178,22 @@ enum {
Fetch, Fetch,
Cap, Cap,
Auth, Auth,
Expunge,
Unknown, Unknown,
}; };
static char *verblist[] = { static char *verblist[] = {
[Ok] "ok", [Ok] "ok",
[No] "no", [No] "no",
[Bad] "bad", [Bad] "bad",
[Bye] "bye", [Bye] "bye",
[Exists] "exists", [Exists] "exists",
[Status] "status", [Status] "status",
[Fetch] "fetch", [Fetch] "fetch",
[Cap] "capability", [Cap] "capability",
[Auth] "authenticate", [Auth] "authenticate",
[Expunge] "expunge",
}; };
static int static int
@ -187,7 +204,7 @@ verbcode(char *verb)
if(q = strchr(verb, ' ')) if(q = strchr(verb, ' '))
*q = '\0'; *q = '\0';
for(i = 0; i < nelem(verblist) - 1; i++) for(i = 0; i < nelem(verblist); i++)
if(strcmp(verblist[i], verb) == 0) if(strcmp(verblist[i], verb) == 0)
break; break;
if(q) if(q)
@ -200,6 +217,7 @@ mkuid(Imap *i, char *id)
{ {
vlong v; vlong v;
idprint(i, "mkuid: validity: %lud, idstr: '%s', val: %lud\n", i->validity, id, strtoul(id, 0, 10));
v = (vlong)i->validity<<32; v = (vlong)i->validity<<32;
return v | strtoul(id, 0, 10); return v | strtoul(id, 0, 10);
} }
@ -230,26 +248,29 @@ static struct{
"\\Stored", Fstored, "\\Stored", Fstored,
}; };
static void static int
parseflags(Message *m, char *s) parseflags(char *s)
{ {
char *f[10]; char *f[10];
int i, j, j0, n; int i, j, j0, n, flg;
n = tokenize(s, f, nelem(f)); n = tokenize(s, f, nelem(f));
qsort(f, n, sizeof *f, (int (*)(void*,void*))strcmp); qsort(f, n, sizeof *f, (int (*)(void*,void*))strcmp);
j = 0; j = 0;
for(i = 0; i < n; i++) flg = 0;
for(i = 0; i < n; i++){
for(j0 = j;; j++){ for(j0 = j;; j++){
if(j == nelem(ftab)){ if(j == nelem(ftab)){
j = j0; /* restart search */ j = j0; /* restart search */
break; break;
} }
if(strcmp(f[i], ftab[j].flag) == 0){ if(cistrcmp(f[i], ftab[j].flag) == 0){
m->flags |= ftab[j].e; flg |= ftab[j].e;
break; break;
} }
} }
}
return flg;
} }
/* "17-Jul-1996 02:44:25 -0700" */ /* "17-Jul-1996 02:44:25 -0700" */
@ -274,7 +295,7 @@ qtoken(char *s, char *sep)
quoting = 0; quoting = 0;
t = s; /* s is output string, t is input string */ t = s; /* s is output string, t is input string */
while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){ while(*t != '\0' && (quoting || utfrune(sep, *t) == nil)){
if(*t != '"' && *t != '(' && *t != ')'){ if(*t != '"' && *t != '(' && *t != ')'){
*s++ = *t++; *s++ = *t++;
continue; continue;
@ -311,7 +332,7 @@ imaptokenize(char *s, char **args, int maxargs)
int nargs; int nargs;
for(nargs=0; nargs < maxargs; nargs++){ for(nargs=0; nargs < maxargs; nargs++){
while(*s!='\0' && utfrune(qsep, *s)!=nil) while(*s != '\0' && utfrune(qsep, *s) != nil)
s++; s++;
if(*s == '\0') if(*s == '\0')
break; break;
@ -323,7 +344,7 @@ imaptokenize(char *s, char **args, int maxargs)
} }
static char* static char*
fetchrsp(Imap *imap, char *p, Mailbox *, Message *m) fetchrsp(Imap *imap, char *p, Mailbox *, Message *m, int idx)
{ {
char *f[15], *s, *q; char *f[15], *s, *q;
int i, n, a; int i, n, a;
@ -332,6 +353,11 @@ fetchrsp(Imap *imap, char *p, Mailbox *, Message *m)
static char error[256]; static char error[256];
extern void msgrealloc(Message*, ulong); extern void msgrealloc(Message*, ulong);
if(idx < 0 || idx >= imap->muid){
snprint(error, sizeof error, "fetchrsp: bad idx %d", idx);
return error;
}
redux: redux:
n = imaptokenize(p, f, nelem(f)); n = imaptokenize(p, f, nelem(f));
if(n%2) if(n%2)
@ -341,23 +367,26 @@ redux:
l = internaltounix(f[i + 1]); l = internaltounix(f[i + 1]);
if(l < 418319360) if(l < 418319360)
abort(); abort();
if(imap->nuid < imap->muid) if(idx < imap->muid)
imap->f[imap->nuid].dates = l; imap->f[idx].dates = l;
}else if(strcmp(f[i], "rfc822.size") == 0){ }else if(strcmp(f[i], "rfc822.size") == 0){
l = strtoul(f[i + 1], 0, 0); l = strtoul(f[i + 1], 0, 0);
if(m) if(m)
m->size = l; m->size = l;
else if(imap->nuid < imap->muid) else if(idx < imap->muid)
imap->f[imap->nuid].sizes = l; imap->f[idx].sizes = l;
}else if(strcmp(f[i], "uid") == 0){ }else if(strcmp(f[i], "uid") == 0){
v = mkuid(imap, f[1]); v = mkuid(imap, f[i + 1]);
if(m) if(m)
m->imapuid = v; m->imapuid = v;
if(imap->nuid < imap->muid) if(idx < imap->muid)
imap->f[imap->nuid].uid = v; imap->f[idx].uid = v;
}else if(strcmp(f[i], "flags") == 0){ }else if(strcmp(f[i], "flags") == 0){
l = parseflags(f[i + 1]);
if(m) if(m)
parseflags(m, f[i + 1]); m->flags = l;
if(idx < imap->muid)
imap->f[idx].flags = l;
}else if(strncmp(f[i], "body[]", 6) == 0){ }else if(strncmp(f[i], "body[]", 6) == 0){
s = f[i]+6; s = f[i]+6;
o = 0; o = 0;
@ -396,7 +425,7 @@ redux:
}else }else
return confused; return confused;
} }
return 0; return nil;
} }
void void
@ -428,7 +457,7 @@ static char*
imap4resp0(Imap *imap, Mailbox *mb, Message *m) imap4resp0(Imap *imap, Mailbox *mb, Message *m)
{ {
char *e, *line, *p, *ep, *op, *q, *verb; char *e, *line, *p, *ep, *op, *q, *verb;
int n, unexp; int n, idx, unexp;
static char error[256]; static char error[256];
unexp = 0; unexp = 0;
@ -484,7 +513,7 @@ imap4resp0(Imap *imap, Mailbox *mb, Message *m)
if(q = strstr(p, "messages")) if(q = strstr(p, "messages"))
imap->nmsg = strtoul(q + 8, 0, 10); imap->nmsg = strtoul(q + 8, 0, 10);
if(q = strstr(p, "uidvalidity")) if(q = strstr(p, "uidvalidity"))
imap->validity = strtoul(q + 11, 0, 10); imap->newvalidity = strtoul(q + 11, 0, 10);
break; break;
case Fetch: case Fetch:
if(*p == '('){ if(*p == '('){
@ -492,9 +521,20 @@ imap4resp0(Imap *imap, Mailbox *mb, Message *m)
if(ep[-1] == ')') if(ep[-1] == ')')
*--ep = 0; *--ep = 0;
} }
if(e = fetchrsp(imap, p, mb, m)) if(e = fetchrsp(imap, p, mb, m, n - 1))
eprint("imap: fetchrsp: %s\n", e); eprint("imap: fetchrsp: %s\n", e);
imap->nuid++; if(n > 0 && n <= imap->muid && n > imap->nuid)
imap->nuid = n;
break;
case Expunge:
if(n < 1 || n > imap->muid){
snprint(error, sizeof(error), "bad expunge %d (nmsg %d)", n, imap->nuid);
return error;
}
idx = n - 1;
memmove(&imap->f[idx], &imap->f[idx + 1], (imap->nmsg - idx - 1)*sizeof(imap->f[0]));
imap->nmsg--;
imap->nuid--;
break; break;
case Auth: case Auth:
break; break;
@ -903,15 +943,30 @@ imap4read(Imap *imap, Mailbox *mb)
Fetchi *f; Fetchi *f;
Message *m, **ll; Message *m, **ll;
again:
imap4cmd(imap, "status %Z (messages uidvalidity)", imap->mbox); imap4cmd(imap, "status %Z (messages uidvalidity)", imap->mbox);
if(!isokay(s = imap4resp(imap))) if(!isokay(s = imap4resp(imap)))
return s; return s;
/* the world shifted: start over */
if(imap->validity != imap->newvalidity){
imap->validity = imap->newvalidity;
imap->nuid = 0;
imap->muid = 0;
imap->nmsg = 0;
goto again;
}
imap->nuid = 0;
imap->muid = imap->nmsg;
imap->f = erealloc(imap->f, imap->nmsg*sizeof imap->f[0]); imap->f = erealloc(imap->f, imap->nmsg*sizeof imap->f[0]);
if(imap->nmsg > imap->muid)
memset(&imap->f[imap->muid], 0, (imap->nmsg - imap->muid)*sizeof(imap->f[0]));
imap->muid = imap->nmsg;
if(imap->nmsg > 0){ if(imap->nmsg > 0){
imap4cmd(imap, "uid fetch 1:* (uid rfc822.size internaldate)"); n = imap->nuid;
if(n == 0)
n = 1;
if(n > imap->nmsg)
n = imap->nmsg;
imap4cmd(imap, "fetch %d:%d (uid flags rfc822.size internaldate)", n, imap->nmsg);
if(!isokay(s = imap4resp(imap))) if(!isokay(s = imap4resp(imap)))
return s; return s;
} }
@ -949,6 +1004,7 @@ imap4read(Imap *imap, Mailbox *mb)
m->imapuid = f[i].uid; m->imapuid = f[i].uid;
m->fileid = datesec(imap, i); m->fileid = datesec(imap, i);
m->size = f[i].sizes; m->size = f[i].sizes;
m->flags = f[i].flags;
m->next = *ll; m->next = *ll;
*ll = m; *ll = m;
ll = &m->next; ll = &m->next;
@ -960,6 +1016,8 @@ imap4read(Imap *imap, Mailbox *mb)
m->deleted = Disappear; m->deleted = Disappear;
ll = &m->next; ll = &m->next;
}else{ }else{
/* TODO: flag this as changed, plumb. */
m->flags = f[i].flags;
ll = &m->next; ll = &m->next;
i++; i++;
} }