diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-03-12 17:15:03 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-03-12 17:15:03 +0100 |
commit | 963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc (patch) | |
tree | 749b74875dbc49bcf6ed0776648b8f0ef9417407 /sys/src/cmd/ip | |
parent | 8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff) |
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/ip')
-rw-r--r-- | sys/src/cmd/ip/imap4d/auth.c | 184 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/copy.c | 259 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/csquery.c | 44 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/date.c | 308 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/debug.c | 108 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/fetch.c | 625 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/fns.h | 125 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/folder.c | 398 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/imap4d.c | 2102 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/imap4d.h | 378 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/list.c | 412 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/mbox.c | 863 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/mkfile | 31 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/msg.c | 1782 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/mutf7.c | 174 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/nodes.c | 213 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/search.c | 244 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/store.c | 127 | ||||
-rw-r--r-- | sys/src/cmd/ip/imap4d/utils.c | 182 | ||||
-rw-r--r-- | sys/src/cmd/ip/mkfile | 2 |
20 files changed, 1 insertions, 8560 deletions
diff --git a/sys/src/cmd/ip/imap4d/auth.c b/sys/src/cmd/ip/imap4d/auth.c deleted file mode 100644 index b8dcd7581..000000000 --- a/sys/src/cmd/ip/imap4d/auth.c +++ /dev/null @@ -1,184 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <auth.h> -#include <libsec.h> -#include <bio.h> -#include "imap4d.h" - -/* - * hack to allow smtp forwarding. - * hide the peer IP address under a rock in the ratifier FS. - */ -void -enableForwarding(void) -{ - char buf[64], peer[64], *p; - static ulong last; - ulong now; - int fd; - - if(remote == nil) - return; - - now = time(0); - if(now < last + 5*60) - return; - last = now; - - fd = open("/srv/ratify", ORDWR); - if(fd < 0) - return; - if(!mount(fd, -1, "/mail/ratify", MBEFORE, "")){ - close(fd); - return; - } - close(fd); - - strncpy(peer, remote, sizeof(peer)); - peer[sizeof(peer) - 1] = '\0'; - p = strchr(peer, '!'); - if(p != nil) - *p = '\0'; - - snprint(buf, sizeof(buf), "/mail/ratify/trusted/%s#32", peer); - - /* - * if the address is already there and the user owns it, - * remove it and recreate it to give him a new time quanta. - */ - if(access(buf, 0) >= 0 && remove(buf) < 0) - return; - - fd = create(buf, OREAD, 0666); - if(fd >= 0) - close(fd); -} - -void -setupuser(AuthInfo *ai) -{ - Waitmsg *w; - int pid; - - if(ai){ - strecpy(username, username+sizeof username, ai->cuid); - - if(auth_chuid(ai, nil) < 0) - bye("user auth failed: %r"); - auth_freeAI(ai); - }else - strecpy(username, username+sizeof username, getuser()); - - if(newns(username, 0) < 0) - bye("user login failed: %r"); - - /* - * hack to allow access to outgoing smtp forwarding - */ - enableForwarding(); - - snprint(mboxDir, MboxNameLen, "/mail/box/%s", username); - if(myChdir(mboxDir) < 0) - bye("can't open user's mailbox"); - - switch(pid = fork()){ - case -1: - bye("can't initialize mail system"); - break; - case 0: - execl("/bin/upas/fs", "upas/fs", "-np", nil); -_exits("rob1"); - _exits(0); - break; - default: - break; - } - if((w=wait()) == nil || w->pid != pid || w->msg[0] != '\0') - bye("can't initialize mail system"); - free(w); -} - -static char* -authresp(void) -{ - char *s, *t; - int n; - - t = Brdline(&bin, '\n'); - n = Blinelen(&bin); - if(n < 2) - return nil; - n--; - if(t[n-1] == '\r') - n--; - t[n] = '\0'; - if(n == 0 || strcmp(t, "*") == 0) - return nil; - - s = binalloc(&parseBin, n + 1, 0); - n = dec64((uchar*)s, n, t, n); - s[n] = '\0'; - return s; -} - -/* - * rfc 2195 cram-md5 authentication - */ -char* -cramauth(void) -{ - AuthInfo *ai; - Chalstate *cs; - char *s, *t; - - if((cs = auth_challenge("proto=cram role=server")) == nil) - return "couldn't get cram challenge"; - - Bprint(&bout, "+ %.*[\r\n", cs->nchal, cs->chal); - if(Bflush(&bout) < 0) - writeErr(); - - s = authresp(); - if(s == nil) - return "client cancelled authentication"; - - t = strchr(s, ' '); - if(t == nil) - bye("bad auth response"); - *t++ = '\0'; - strncpy(username, s, UserNameLen); - username[UserNameLen-1] = '\0'; - - cs->user = username; - cs->resp = t; - cs->nresp = strlen(t); - if((ai = auth_response(cs)) == nil) - return "login failed"; - auth_freechal(cs); - setupuser(ai); - return nil; -} - -AuthInfo* -passLogin(char *user, char *secret) -{ - AuthInfo *ai; - Chalstate *cs; - uchar digest[MD5dlen]; - char response[2*MD5dlen+1]; - - if((cs = auth_challenge("proto=cram role=server")) == nil) - return nil; - - hmac_md5((uchar*)cs->chal, strlen(cs->chal), - (uchar*)secret, strlen(secret), digest, - nil); - snprint(response, sizeof(response), "%.*H", MD5dlen, digest); - - cs->user = user; - cs->resp = response; - cs->nresp = strlen(response); - ai = auth_response(cs); - auth_freechal(cs); - return ai; -} diff --git a/sys/src/cmd/ip/imap4d/copy.c b/sys/src/cmd/ip/imap4d/copy.c deleted file mode 100644 index a1b3f6a5d..000000000 --- a/sys/src/cmd/ip/imap4d/copy.c +++ /dev/null @@ -1,259 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include <libsec.h> -#include "imap4d.h" - -static int saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n); -static int saveb(int fd, DigestState *dstate, char *buf, int nr, int nw); -static long appSpool(Biobuf *bout, Biobuf *bin, long n); - -/* - * check if the message exists - */ -int -copyCheck(Box *box, Msg *m, int uids, void *v) -{ - int fd; - - USED(box); - USED(uids); - USED(v); - - if(m->expunged) - return 0; - fd = msgFile(m, "raw"); - if(fd < 0){ - msgDead(m); - return 0; - } - close(fd); - return 1; -} - -int -copySave(Box *box, Msg *m, int uids, void *vs) -{ - Dir *d; - Biobuf b; - vlong length; - char *head; - int ok, hfd, bfd, nhead; - - USED(box); - USED(uids); - - if(m->expunged) - return 0; - - hfd = msgFile(m, "unixheader"); - if(hfd < 0){ - msgDead(m); - return 0; - } - head = readFile(hfd); - if(head == nil){ - close(hfd); - return 0; - } - - /* - * clear out the header if it doesn't end in a newline, - * since it is a runt and the "header" will show up in the raw file. - */ - nhead = strlen(head); - if(nhead > 0 && head[nhead-1] != '\n') - nhead = 0; - - bfd = msgFile(m, "raw"); - close(hfd); - if(bfd < 0){ - msgDead(m); - return 0; - } - - d = dirfstat(bfd); - if(d == nil){ - close(bfd); - return 0; - } - length = d->length; - free(d); - - Binit(&b, bfd, OREAD); - ok = saveMsg(vs, m->info[IDigest], m->flags, head, nhead, &b, length); - Bterm(&b); - close(bfd); - return ok; -} - -/* - * first spool the input into a temorary file, - * and massage the input in the process. - * then save to real box. - */ -int -appendSave(char *mbox, int flags, char *head, Biobuf *b, long n) -{ - Biobuf btmp; - int fd, ok; - - fd = imapTmp(); - if(fd < 0) - return 0; - Bprint(&bout, "+ Ready for literal data\r\n"); - if(Bflush(&bout) < 0) - writeErr(); - Binit(&btmp, fd, OWRITE); - n = appSpool(&btmp, b, n); - Bterm(&btmp); - if(n < 0){ - close(fd); - return 0; - } - - seek(fd, 0, 0); - Binit(&btmp, fd, OREAD); - ok = saveMsg(mbox, nil, flags, head, strlen(head), &btmp, n); - Bterm(&btmp); - close(fd); - return ok; -} - -/* - * copy from bin to bout, - * mapping "\r\n" to "\n" and "\nFrom " to "\n From " - * return the number of bytes in the mapped file. - * - * exactly n bytes must be read from the input, - * unless an input error occurs. - */ -static long -appSpool(Biobuf *bout, Biobuf *bin, long n) -{ - int i, c; - - c = '\n'; - while(n > 0){ - if(c == '\n' && n >= STRLEN("From ")){ - for(i = 0; i < STRLEN("From "); i++){ - c = Bgetc(bin); - if(c != "From "[i]){ - if(c < 0) - return -1; - Bungetc(bin); - break; - } - n--; - } - if(i == STRLEN("From ")) - Bputc(bout, ' '); - Bwrite(bout, "From ", i); - } - c = Bgetc(bin); - n--; - if(c == '\r' && n-- > 0){ - c = Bgetc(bin); - if(c != '\n') - Bputc(bout, '\r'); - } - if(c < 0) - return -1; - if(Bputc(bout, c) < 0) - return -1; - } - if(c != '\n') - Bputc(bout, '\n'); - if(Bflush(bout) < 0) - return -1; - return Boffset(bout); -} - -static int -saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n) -{ - DigestState *dstate; - MbLock *ml; - uchar shadig[SHA1dlen]; - char buf[BufSize + 1], digbuf[NDigest + 1]; - int i, fd, nr, nw, ok; - - ml = mbLock(); - if(ml == nil) - return 0; - fd = openLocked(mboxDir, dst, OWRITE); - if(fd < 0){ - mbUnlock(ml); - return 0; - } - seek(fd, 0, 2); - - dstate = nil; - if(digest == nil) - dstate = sha1(nil, 0, nil, nil); - if(!saveb(fd, dstate, head, nhead, nhead)){ - if(dstate != nil) - sha1(nil, 0, shadig, dstate); - mbUnlock(ml); - close(fd); - return 0; - } - ok = 1; - if(n == 0) - ok = saveb(fd, dstate, "\n", 0, 1); - while(n > 0){ - nr = n; - if(nr > BufSize) - nr = BufSize; - nr = Bread(b, buf, nr); - if(nr <= 0){ - saveb(fd, dstate, "\n\n", 0, 2); - ok = 0; - break; - } - n -= nr; - nw = nr; - if(n == 0){ - if(buf[nw - 1] != '\n') - buf[nw++] = '\n'; - buf[nw++] = '\n'; - } - if(!saveb(fd, dstate, buf, nr, nw)){ - ok = 0; - break; - } - mbLockRefresh(ml); - } - close(fd); - - if(dstate != nil){ - digest = digbuf; - sha1(nil, 0, shadig, dstate); - for(i = 0; i < SHA1dlen; i++) - snprint(digest+2*i, NDigest+1-2*i, "%2.2ux", shadig[i]); - } - if(ok){ - fd = cdOpen(mboxDir, impName(dst), OWRITE); - if(fd < 0) - fd = emptyImp(dst); - if(fd >= 0){ - seek(fd, 0, 2); - wrImpFlags(buf, flags, 1); - fprint(fd, "%.*s %.*lud %s\n", NDigest, digest, NUid, 0UL, buf); - close(fd); - } - } - mbUnlock(ml); - return 1; -} - -static int -saveb(int fd, DigestState *dstate, char *buf, int nr, int nw) -{ - if(dstate != nil) - sha1((uchar*)buf, nr, nil, dstate); - if(write(fd, buf, nw) != nw) - return 0; - return 1; -} diff --git a/sys/src/cmd/ip/imap4d/csquery.c b/sys/src/cmd/ip/imap4d/csquery.c deleted file mode 100644 index d5736160b..000000000 --- a/sys/src/cmd/ip/imap4d/csquery.c +++ /dev/null @@ -1,44 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -/* - * query the connection server - */ -char* -csquery(char *attr, char *val, char *rattr) -{ - char token[64+4]; - char buf[256], *p, *sp; - int fd, n; - - if(val == nil || val[0] == 0) - return nil; - fd = open("/net/cs", ORDWR); - if(fd < 0) - return nil; - fprint(fd, "!%s=%s", attr, val); - seek(fd, 0, 0); - snprint(token, sizeof(token), "%s=", rattr); - for(;;){ - n = read(fd, buf, sizeof(buf)-1); - if(n <= 0) - break; - buf[n] = 0; - p = strstr(buf, token); - if(p != nil && (p == buf || *(p-1) == 0)){ - close(fd); - sp = strchr(p, ' '); - if(sp) - *sp = 0; - p = strchr(p, '='); - if(p == nil) - return nil; - return strdup(p+1); - } - } - close(fd); - return nil; -} diff --git a/sys/src/cmd/ip/imap4d/date.c b/sys/src/cmd/ip/imap4d/date.c deleted file mode 100644 index 9c9c4664a..000000000 --- a/sys/src/cmd/ip/imap4d/date.c +++ /dev/null @@ -1,308 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -char * -wdayname[7] = -{ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -char * -monname[12] = -{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static void time2tm(Tm *tm, char *s); -static void zone2tm(Tm *tm, char *s); -static int dateindex(char *d, char **tab, int n); - -int -rfc822date(char *s, int n, Tm *tm) -{ - char *plus; - int m; - - plus = "+"; - if(tm->tzoff < 0) - plus = ""; - m = 0; - if(0 <= tm->wday && tm->wday < 7){ - m = snprint(s, n, "%s, ", wdayname[tm->wday]); - if(m < 0) - return m; - } - return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d", - tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, - plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60); -} - -int -imap4date(char *s, int n, Tm *tm) -{ - char *plus; - - plus = "+"; - if(tm->tzoff < 0) - plus = ""; - return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d", - tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60); -} - -int -imap4Date(Tm *tm, char *date) -{ - char *flds[4]; - - if(getfields(date, flds, 3, 0, "-") != 3) - return 0; - - tm->mday = strtol(flds[0], nil, 10); - tm->mon = dateindex(flds[1], monname, 12); - tm->year = strtol(flds[2], nil, 10) - 1900; - return 1; -} - -/* - * parse imap4 dates - */ -ulong -imap4DateTime(char *date) -{ - Tm tm; - char *flds[4], *sflds[4]; - ulong t; - - if(getfields(date, flds, 4, 0, " ") != 3) - return ~0; - - if(!imap4Date(&tm, flds[0])) - return ~0; - - if(getfields(flds[1], sflds, 3, 0, ":") != 3) - return ~0; - - tm.hour = strtol(sflds[0], nil, 10); - tm.min = strtol(sflds[1], nil, 10); - tm.sec = strtol(sflds[2], nil, 10); - - strcpy(tm.zone, "GMT"); - tm.yday = 0; - t = tm2sec(&tm); - zone2tm(&tm, flds[2]); - t -= tm.tzoff; - return t; -} - -/* - * parse dates of formats - * [Wkd[,]] DD Mon YYYY HH:MM:SS zone - * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY - * plus anything similar - * return nil for a failure - */ -Tm* -date2tm(Tm *tm, char *date) -{ - Tm gmt, *atm; - char *flds[7], *s, dstr[64]; - int n; - - /* - * default date is Thu Jan 1 00:00:00 GMT 1970 - */ - tm->wday = 4; - tm->mday = 1; - tm->mon = 1; - tm->hour = 0; - tm->min = 0; - tm->sec = 0; - tm->year = 70; - strcpy(tm->zone, "GMT"); - tm->tzoff = 0; - - strncpy(dstr, date, sizeof(dstr)); - dstr[sizeof(dstr)-1] = '\0'; - n = tokenize(dstr, flds, 7); - if(n != 6 && n != 5) - return nil; - - if(n == 5){ - for(n = 5; n >= 1; n--) - flds[n] = flds[n - 1]; - n = 5; - }else{ - /* - * Wday[,] - */ - s = strchr(flds[0], ','); - if(s != nil) - *s = '\0'; - tm->wday = dateindex(flds[0], wdayname, 7); - if(tm->wday < 0) - return nil; - } - - /* - * check for the two major formats: - * Month first or day first - */ - tm->mon = dateindex(flds[1], monname, 12); - if(tm->mon >= 0){ - tm->mday = strtoul(flds[2], nil, 10); - time2tm(tm, flds[3]); - zone2tm(tm, flds[4]); - tm->year = strtoul(flds[5], nil, 10); - if(strlen(flds[5]) > 2) - tm->year -= 1900; - }else{ - tm->mday = strtoul(flds[1], nil, 10); - tm->mon = dateindex(flds[2], monname, 12); - tm->year = strtoul(flds[3], nil, 10); - if(strlen(flds[3]) > 2) - tm->year -= 1900; - time2tm(tm, flds[4]); - zone2tm(tm, flds[5]); - } - - if(n == 5){ - gmt = *tm; - strncpy(gmt.zone, "", 4); - gmt.tzoff = 0; - atm = gmtime(tm2sec(&gmt)); - tm->wday = atm->wday; - }else{ - /* - * Wday[,] - */ - s = strchr(flds[0], ','); - if(s != nil) - *s = '\0'; - tm->wday = dateindex(flds[0], wdayname, 7); - if(tm->wday < 0) - return nil; - } - return tm; -} - -/* - * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names - * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong. - * | "UT" universal time - * | [+-][0-9][0-9][0-9][0-9] - * zones is the rfc-822 list of time zone names - */ -static NamedInt zones[] = -{ - {"A", -1 * 3600}, - {"B", -2 * 3600}, - {"C", -3 * 3600}, - {"CDT", -5 * 3600}, - {"CST", -6 * 3600}, - {"D", -4 * 3600}, - {"E", -5 * 3600}, - {"EDT", -4 * 3600}, - {"EST", -5 * 3600}, - {"F", -6 * 3600}, - {"G", -7 * 3600}, - {"GMT", 0}, - {"H", -8 * 3600}, - {"I", -9 * 3600}, - {"K", -10 * 3600}, - {"L", -11 * 3600}, - {"M", -12 * 3600}, - {"MDT", -6 * 3600}, - {"MST", -7 * 3600}, - {"N", +1 * 3600}, - {"O", +2 * 3600}, - {"P", +3 * 3600}, - {"PDT", -7 * 3600}, - {"PST", -8 * 3600}, - {"Q", +4 * 3600}, - {"R", +5 * 3600}, - {"S", +6 * 3600}, - {"T", +7 * 3600}, - {"U", +8 * 3600}, - {"UT", 0}, - {"V", +9 * 3600}, - {"W", +10 * 3600}, - {"X", +11 * 3600}, - {"Y", +12 * 3600}, - {"Z", 0}, - {nil, 0} -}; - -static void -zone2tm(Tm *tm, char *s) -{ - Tm aux, *atm; - int i; - - if(*s == '+' || *s == '-'){ - i = strtol(s, &s, 10); - tm->tzoff = (i / 100) * 3600 + i % 100; - strncpy(tm->zone, "", 4); - return; - } - - /* - * look it up in the standard rfc822 table - */ - strncpy(tm->zone, s, 3); - tm->zone[3] = '\0'; - tm->tzoff = 0; - for(i = 0; zones[i].name != nil; i++){ - if(cistrcmp(zones[i].name, s) == 0){ - tm->tzoff = zones[i].v; - return; - } - } - - /* - * one last try: look it up in the current local timezone - * probe a couple of times to get daylight/standard time change. - */ - aux = *tm; - memset(aux.zone, 0, 4); - aux.hour--; - for(i = 0; i < 2; i++){ - atm = localtime(tm2sec(&aux)); - if(cistrcmp(tm->zone, atm->zone) == 0){ - tm->tzoff = atm->tzoff; - return; - } - aux.hour++; - } - - strncpy(tm->zone, "GMT", 4); - tm->tzoff = 0; -} - -/* - * hh[:mm[:ss]] - */ -static void -time2tm(Tm *tm, char *s) -{ - tm->hour = strtoul(s, &s, 10); - if(*s++ != ':') - return; - tm->min = strtoul(s, &s, 10); - if(*s++ != ':') - return; - tm->sec = strtoul(s, &s, 10); -} - -static int -dateindex(char *d, char **tab, int n) -{ - int i; - - for(i = 0; i < n; i++) - if(cistrcmp(d, tab[i]) == 0) - return i; - return -1; -} diff --git a/sys/src/cmd/ip/imap4d/debug.c b/sys/src/cmd/ip/imap4d/debug.c deleted file mode 100644 index 9dbfcb204..000000000 --- a/sys/src/cmd/ip/imap4d/debug.c +++ /dev/null @@ -1,108 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <auth.h> -#include <bio.h> -#include "imap4d.h" - -void -debuglog(char *fmt, ...) -{ - va_list arg; - static int logfd; - - if(debug == 0) - return; - if(logfd == 0) - logfd = open("/sys/log/imap4d", OWRITE); - if(logfd > 0){ - va_start(arg, fmt); - fprint(logfd, "%s: ", username); - vfprint(logfd, fmt, arg); - va_end(arg); - } -} - -void -boxVerify(Box *box) -{ - Msg *m; - ulong seq, uid, recent; - - if(box == nil) - return; - recent = 0; - seq = 0; - uid = 0; - for(m = box->msgs; m != nil; m = m->next){ - if(m->seq == 0) - fprint(2, "m->seq == 0: m->seq=%lud\n", m->seq); - else if(m->seq <= seq) - fprint(2, "m->seq=%lud out of order: last=%lud\n", m->seq, seq); - seq = m->seq; - - if(m->uid == 0) - fprint(2, "m->uid == 0: m->seq=%lud\n", m->seq); - else if(m->uid <= uid) - fprint(2, "m->uid=%lud out of order: last=%lud\n", m->uid, uid); - uid = m->uid; - - if(m->flags & MRecent) - recent++; - } - if(seq != box->max) - fprint(2, "max=%lud, should be %lud\n", box->max, seq); - if(uid >= box->uidnext) - fprint(2, "uidnext=%lud, maxuid=%lud\n", box->uidnext, uid); - if(recent != box->recent) - fprint(2, "recent=%lud, should be %lud\n", box->recent, recent); -} - -void -openfiles(void) -{ - Dir *d; - int i; - - for(i = 0; i < 20; i++){ - d = dirfstat(i); - if(d != nil){ - fprint(2, "fd[%d]='%s' type=%c dev=%d user='%s group='%s'\n", i, d->name, d->type, d->dev, d->uid, d->gid); - free(d); - } - } -} - -void -ls(char *file) -{ - Dir *d; - int fd, i, nd; - - fd = open(file, OREAD); - if(fd < 0) - return; - - /* - * read box to find all messages - * each one has a directory, and is in numerical order - */ - d = dirfstat(fd); - if(d == nil){ - close(fd); - return; - } - if(!(d->mode & DMDIR)){ - fprint(2, "file %s\n", file); - free(d); - close(fd); - return; - } - free(d); - while((nd = dirread(fd, &d)) > 0){ - for(i = 0; i < nd; i++){ - fprint(2, "%s/%s %c\n", file, d[i].name, "-d"[(d[i].mode & DMDIR) == DMDIR]); - } - free(d); - } - close(fd); -} diff --git a/sys/src/cmd/ip/imap4d/fetch.c b/sys/src/cmd/ip/imap4d/fetch.c deleted file mode 100644 index 30fc0f4b7..000000000 --- a/sys/src/cmd/ip/imap4d/fetch.c +++ /dev/null @@ -1,625 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -char *fetchPartNames[FPMax] = -{ - "", - "HEADER", - "HEADER.FIELDS", - "HEADER.FIELDS.NOT", - "MIME", - "TEXT", -}; - -/* - * implicitly set the \seen flag. done in a separate pass - * so the .imp file doesn't need to be open while the - * messages are sent to the client. - */ -int -fetchSeen(Box *box, Msg *m, int uids, void *vf) -{ - Fetch *f; - - USED(uids); - - if(m->expunged) - return uids; - for(f = vf; f != nil; f = f->next){ - switch(f->op){ - case FRfc822: - case FRfc822Text: - case FBodySect: - msgSeen(box, m); - goto breakout; - } - } -breakout: - - return 1; -} - -/* - * fetch messages - * - * imap4 body[] requestes get translated to upas/fs files as follows - * body[id.header] == id/rawheader file + extra \r\n - * body[id.text] == id/rawbody - * body[id.mime] == id/mimeheader + extra \r\n - * body[id] === body[id.header] + body[id.text] -*/ -int -fetchMsg(Box *, Msg *m, int uids, void *vf) -{ - Tm tm; - Fetch *f; - char *sep; - int todo; - - if(m->expunged) - return uids; - - todo = 0; - for(f = vf; f != nil; f = f->next){ - switch(f->op){ - case FFlags: - todo = 1; - break; - case FUid: - todo = 1; - break; - case FInternalDate: - case FEnvelope: - case FRfc822: - case FRfc822Head: - case FRfc822Size: - case FRfc822Text: - case FBodySect: - case FBodyPeek: - case FBody: - case FBodyStruct: - todo = 1; - if(!msgStruct(m, 1)){ - msgDead(m); - return uids; - } - break; - default: - bye("bad implementation of fetch"); - return 0; - } - } - - if(m->expunged) - return uids; - if(!todo) - return 1; - - /* - * note: it is allowed to send back the responses one at a time - * rather than all together. this is exploited to send flags elsewhere. - */ - Bprint(&bout, "* %lud FETCH (", m->seq); - sep = ""; - if(uids){ - Bprint(&bout, "UID %lud", m->uid); - sep = " "; - } - for(f = vf; f != nil; f = f->next){ - switch(f->op){ - default: - bye("bad implementation of fetch"); - break; - case FFlags: - Bprint(&bout, "%sFLAGS (", sep); - writeFlags(&bout, m, 1); - Bprint(&bout, ")"); - break; - case FUid: - if(uids) - continue; - Bprint(&bout, "%sUID %lud", sep, m->uid); - break; - case FEnvelope: - Bprint(&bout, "%sENVELOPE ", sep); - fetchEnvelope(m); - break; - case FInternalDate: - Bprint(&bout, "%sINTERNALDATE ", sep); - Bimapdate(&bout, date2tm(&tm, m->unixDate)); - break; - case FBody: - Bprint(&bout, "%sBODY ", sep); - fetchBodyStruct(m, &m->head, 0); - break; - case FBodyStruct: - Bprint(&bout, "%sBODYSTRUCTURE ", sep); - fetchBodyStruct(m, &m->head, 1); - break; - case FRfc822Size: - Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m)); - break; - case FRfc822: - f->part = FPAll; - Bprint(&bout, "%sRFC822", sep); - fetchBody(m, f); - break; - case FRfc822Head: - f->part = FPHead; - Bprint(&bout, "%sRFC822.HEADER", sep); - fetchBody(m, f); - break; - case FRfc822Text: - f->part = FPText; - Bprint(&bout, "%sRFC822.TEXT", sep); - fetchBody(m, f); - break; - case FBodySect: - case FBodyPeek: - Bprint(&bout, "%sBODY", sep); - fetchBody(fetchSect(m, f), f); - break; - } - sep = " "; - } - Bprint(&bout, ")\r\n"); - - return 1; -} - -/* - * print out section, part, headers; - * find and return message section - */ -Msg * -fetchSect(Msg *m, Fetch *f) -{ - Bputc(&bout, '['); - BNList(&bout, f->sect, "."); - if(f->part != FPAll){ - if(f->sect != nil) - Bputc(&bout, '.'); - Bprint(&bout, "%s", fetchPartNames[f->part]); - if(f->hdrs != nil){ - Bprint(&bout, " ("); - BSList(&bout, f->hdrs, " "); - Bputc(&bout, ')'); - } - } - Bprint(&bout, "]"); - return findMsgSect(m, f->sect); -} - -/* - * actually return the body pieces - */ -void -fetchBody(Msg *m, Fetch *f) -{ - Pair p; - char *s, *t, *e, buf[BufSize + 2]; - ulong n, start, stop, pos; - int fd, nn; - - if(m == nil){ - fetchBodyStr(f, "", 0); - return; - } - switch(f->part){ - case FPHeadFields: - case FPHeadFieldsNot: - n = m->head.size + 3; - s = emalloc(n); - n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields); - fetchBodyStr(f, s, n); - free(s); - return; - case FPHead: - fetchBodyStr(f, m->head.buf, m->head.size); - return; - case FPMime: - fetchBodyStr(f, m->mime.buf, m->mime.size); - return; - case FPAll: - fd = msgFile(m, "rawbody"); - if(fd < 0){ - msgDead(m); - fetchBodyStr(f, "", 0); - return; - } - p = fetchBodyPart(f, msgSize(m)); - start = p.start; - if(start < m->head.size){ - stop = p.stop; - if(stop > m->head.size) - stop = m->head.size; - Bwrite(&bout, &m->head.buf[start], stop - start); - start = 0; - stop = p.stop; - if(stop <= m->head.size){ - close(fd); - return; - } - }else - start -= m->head.size; - stop = p.stop - m->head.size; - break; - case FPText: - fd = msgFile(m, "rawbody"); - if(fd < 0){ - msgDead(m); - fetchBodyStr(f, "", 0); - return; - } - p = fetchBodyPart(f, m->size); - start = p.start; - stop = p.stop; - break; - default: - fetchBodyStr(f, "", 0); - return; - } - - /* - * read in each block, convert \n without \r to \r\n. - * this means partial fetch requires fetching everything - * through stop, since we don't know how many \r's will be added - */ - buf[0] = ' '; - for(pos = 0; pos < stop; ){ - n = BufSize; - if(n > stop - pos) - n = stop - pos; - n = read(fd, &buf[1], n); - if(n <= 0){ - fetchBodyFill(stop - pos); - break; - } - e = &buf[n + 1]; - *e = '\0'; - for(s = &buf[1]; s < e && pos < stop; s = t + 1){ - t = memchr(s, '\n', e - s); - if(t == nil) - t = e; - n = t - s; - if(pos < start){ - if(pos + n <= start){ - s = t; - pos += n; - }else{ - s += start - pos; - pos = start; - } - n = t - s; - } - nn = n; - if(pos + nn > stop) - nn = stop - pos; - if(Bwrite(&bout, s, nn) != nn) - writeErr(); - pos += n; - if(*t == '\n'){ - if(t[-1] != '\r'){ - if(pos >= start && pos < stop) - Bputc(&bout, '\r'); - pos++; - } - if(pos >= start && pos < stop) - Bputc(&bout, '\n'); - pos++; - } - } - buf[0] = e[-1]; - } - close(fd); -} - -/* - * resolve the actual bounds of any partial fetch, - * and print out the bounds & size of string returned - */ -Pair -fetchBodyPart(Fetch *f, ulong size) -{ - Pair p; - ulong start, stop; - - start = 0; - stop = size; - if(f->partial){ - start = f->start; - if(start > size) - start = size; - stop = start + f->size; - if(stop > size) - stop = size; - Bprint(&bout, "<%lud>", start); - } - Bprint(&bout, " {%lud}\r\n", stop - start); - p.start = start; - p.stop = stop; - return p; -} - -/* - * something went wrong fetching data - * produce fill bytes for what we've committed to produce - */ -void -fetchBodyFill(ulong n) -{ - while(n-- > 0) - if(Bputc(&bout, ' ') < 0) - writeErr(); -} - -/* - * return a simple string - */ -void -fetchBodyStr(Fetch *f, char *buf, ulong size) -{ - Pair p; - - p = fetchBodyPart(f, size); - Bwrite(&bout, &buf[p.start], p.stop-p.start); -} - -char* -printnlist(NList *sect) -{ - static char buf[100]; - char *p; - - for(p= buf; sect; sect=sect->next){ - p += sprint(p, "%ld", sect->n); - if(sect->next) - *p++ = '.'; - } - *p = '\0'; - return buf; -} - -/* - * find the numbered sub-part of the message - */ -Msg* -findMsgSect(Msg *m, NList *sect) -{ - ulong id; - - for(; sect != nil; sect = sect->next){ - id = sect->n; -#ifdef HACK - /* HACK to solve extra level of structure not visible from upas/fs */ - if(m->kids == 0 && id == 1 && sect->next == nil){ - if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0) - if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0) - if(m->head.type->s && strcmp(m->head.type->s, "text")==0) - if(m->head.type->t && strcmp(m->head.type->t, "plain")==0) - break; - } - /* end of HACK */ -#endif HACK - for(m = m->kids; m != nil; m = m->next) - if(m->id == id) - break; - if(m == nil) - return nil; - } - return m; -} - -void -fetchEnvelope(Msg *m) -{ - Tm tm; - - Bputc(&bout, '('); - Brfc822date(&bout, date2tm(&tm, m->info[IDate])); - Bputc(&bout, ' '); - Bimapstr(&bout, m->info[ISubject]); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->from); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->sender); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->replyTo); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->to); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->cc); - Bputc(&bout, ' '); - Bimapaddr(&bout, m->bcc); - Bputc(&bout, ' '); - Bimapstr(&bout, m->info[IInReplyTo]); - Bputc(&bout, ' '); - Bimapstr(&bout, m->info[IMessageId]); - Bputc(&bout, ')'); -} - -void -fetchBodyStruct(Msg *m, Header *h, int extensions) -{ - Msg *k; - ulong len; - - if(msgIsMulti(h)){ - Bputc(&bout, '('); - for(k = m->kids; k != nil; k = k->next) - fetchBodyStruct(k, &k->mime, extensions); - - Bputc(&bout, ' '); - Bimapstr(&bout, h->type->t); - - if(extensions){ - Bputc(&bout, ' '); - BimapMimeParams(&bout, h->type->next); - fetchStructExt(h); - } - - Bputc(&bout, ')'); - return; - } - - Bputc(&bout, '('); - if(h->type != nil){ - Bimapstr(&bout, h->type->s); - Bputc(&bout, ' '); - Bimapstr(&bout, h->type->t); - Bputc(&bout, ' '); - BimapMimeParams(&bout, h->type->next); - }else - Bprint(&bout, "\"text\" \"plain\" NIL"); - - Bputc(&bout, ' '); - if(h->id != nil) - Bimapstr(&bout, h->id->s); - else - Bprint(&bout, "NIL"); - - Bputc(&bout, ' '); - if(h->description != nil) - Bimapstr(&bout, h->description->s); - else - Bprint(&bout, "NIL"); - - Bputc(&bout, ' '); - if(h->encoding != nil) - Bimapstr(&bout, h->encoding->s); - else - Bprint(&bout, "NIL"); - - /* - * this is so strange: return lengths for a body[text] response, - * except in the case of a multipart message, when return lengths for a body[] response - */ - len = m->size; - if(h == &m->mime) - len += m->head.size; - Bprint(&bout, " %lud", len); - - len = m->lines; - if(h == &m->mime) - len += m->head.lines; - - if(h->type == nil || cistrcmp(h->type->s, "text") == 0){ - Bprint(&bout, " %lud", len); - }else if(msgIsRfc822(h)){ - Bputc(&bout, ' '); - k = m; - if(h != &m->mime) - k = m->kids; - if(k == nil) - Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0"); - else{ - fetchEnvelope(k); - Bputc(&bout, ' '); - fetchBodyStruct(k, &k->head, extensions); - Bprint(&bout, " %lud", len); - } - } - - if(extensions){ - Bputc(&bout, ' '); - - /* - * don't have the md5 laying around, - * since the header & body have added newlines. - */ - Bprint(&bout, "NIL"); - - fetchStructExt(h); - } - Bputc(&bout, ')'); -} - -/* - * common part of bodystructure extensions - */ -void -fetchStructExt(Header *h) -{ - Bputc(&bout, ' '); - if(h->disposition != nil){ - Bputc(&bout, '('); - Bimapstr(&bout, h->disposition->s); - Bputc(&bout, ' '); - BimapMimeParams(&bout, h->disposition->next); - Bputc(&bout, ')'); - }else - Bprint(&bout, "NIL"); - Bputc(&bout, ' '); - if(h->language != nil){ - if(h->language->next != nil) - BimapMimeParams(&bout, h->language->next); - else - Bimapstr(&bout, h->language->s); - }else - Bprint(&bout, "NIL"); -} - -int -BimapMimeParams(Biobuf *b, MimeHdr *mh) -{ - char *sep; - int n; - - if(mh == nil) - return Bprint(b, "NIL"); - - n = Bputc(b, '('); - - sep = ""; - for(; mh != nil; mh = mh->next){ - n += Bprint(b, sep); - n += Bimapstr(b, mh->s); - n += Bputc(b, ' '); - n += Bimapstr(b, mh->t); - sep = " "; - } - - n += Bputc(b, ')'); - return n; -} - -/* - * print a list of addresses; - * each address is printed as '(' personalName AtDomainList mboxName hostName ')' - * the AtDomainList is always NIL - */ -int -Bimapaddr(Biobuf *b, MAddr *a) -{ - char *host, *sep; - int n; - - if(a == nil) - return Bprint(b, "NIL"); - - n = Bputc(b, '('); - sep = ""; - for(; a != nil; a = a->next){ - n += Bprint(b, "%s(", sep); - n += Bimapstr(b, a->personal); - n += Bprint(b," NIL "); - n += Bimapstr(b, a->box); - n += Bputc(b, ' '); - - /* - * can't send NIL as hostName, since that is code for a group - */ - host = a->host; - if(host == nil) - host = ""; - n += Bimapstr(b, host); - - n += Bputc(b, ')'); - sep = " "; - } - n += Bputc(b, ')'); - return n; -} diff --git a/sys/src/cmd/ip/imap4d/fns.h b/sys/src/cmd/ip/imap4d/fns.h deleted file mode 100644 index 4f8694689..000000000 --- a/sys/src/cmd/ip/imap4d/fns.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * sorted by 4,/^$/|sort -bd +1 - */ -int fqid(int fd, Qid *qid); -int BNList(Biobuf *b, NList *nl, char *sep); -int BSList(Biobuf *b, SList *sl, char *sep); -int BimapMimeParams(Biobuf *b, MimeHdr *mh); -int Bimapaddr(Biobuf *b, MAddr *a); -int Bimapdate(Biobuf *b, Tm *tm); -int Bimapstr(Biobuf *b, char *s); -int Brfc822date(Biobuf *b, Tm *tm); -int appendSave(char *mbox, int flags, char *head, Biobuf *b, long n); -void bye(char *fmt, ...); -int cdCreate(char *dir, char *file, int mode, ulong perm); -int cdExists(char *dir, char *file); -Dir *cdDirstat(char *dir, char *file); -int cdDirwstat(char *dir, char *file, Dir *d); -int cdOpen(char *dir, char *file, int mode); -int cdRemove(char *dir, char *file); -MbLock *checkBox(Box *box, int imped); -int ciisprefix(char *pre, char *s); -int cistrcmp(char*, char*); -int cistrncmp(char*, char*, int); -char *cistrstr(char *s, char *sub); -void closeBox(Box *box, int opened); -void closeImp(Box *box, MbLock *ml); -int copyBox(char *from, char *to, int doremove); -int copyCheck(Box *box, Msg *m, int uids, void *v); -int copySave(Box *box, Msg *m, int uids, void *vs); -char *cramauth(void); -int createBox(char *mbox, int dir); -Tm *date2tm(Tm *tm, char *date); -int decmutf7(char *out, int nout, char *in); -int deleteMsgs(Box *box); -void debuglog(char *fmt, ...); -void *emalloc(ulong); -int emptyImp(char *mbox); -void enableForwarding(void); -int encmutf7(char *out, int nout, char *in); -void *erealloc(void*, ulong); -char *estrdup(char*); -int expungeMsgs(Box *box, int send); -void *ezmalloc(ulong); -void fetchBodyFill(ulong n); -void fetchBody(Msg *m, Fetch *f); -Pair fetchBodyPart(Fetch *f, ulong size); -void fetchBodyStr(Fetch *f, char *buf, ulong size); -void fetchBodyStruct(Msg *m, Header *h, int extensions); -void fetchEnvelope(Msg *m); -int fetchMsg(Box *box, Msg *m, int uids, void *fetch); -Msg *fetchSect(Msg *m, Fetch *f); -int fetchSeen(Box *box, Msg *m, int uids, void *vf); -void fetchStructExt(Header *h); -Msg *findMsgSect(Msg *m, NList *sect); -int forMsgs(Box *box, MsgSet *ms, ulong max, int uids, int (*f)(Box*, Msg*, int, void*), void *rock); -void freeMsg(Msg *m); -ulong imap4DateTime(char *date); -int imap4Date(Tm *tm, char *date); -int imap4date(char *s, int n, Tm *tm); -int imapTmp(void); -char *impName(char *name); -int infoIsNil(char *s); -int isdotdot(char*); -int isprefix(char *pre, char *s); -int issuffix(char *suf, char *s); -int listBoxes(char *cmd, char *ref, char *pat); -char *loginauth(void); -int lsubBoxes(char *cmd, char *ref, char *pat); -char *maddrStr(MAddr *a); -ulong mapFlag(char *name); -ulong mapInt(NamedInt *map, char *name); -void mbLockRefresh(MbLock *ml); -int mbLocked(void); -MbLock *mbLock(void); -void mbUnlock(MbLock *ml); -char *mboxName(char*); -Fetch *mkFetch(int op, Fetch *next); -NList *mkNList(ulong n, NList *next); -SList *mkSList(char *s, SList *next); -Store *mkStore(int sign, int op, int flags); -int moveBox(char *from, char *to); -void msgDead(Msg *m); -int msgFile(Msg *m, char *f); -int msgInfo(Msg *m); -int msgIsMulti(Header *h); -int msgIsRfc822(Header *h); -int msgSeen(Box *box, Msg *m); -ulong msgSize(Msg *m); -int msgStruct(Msg *m, int top); -char *mutf7str(char*); -int myChdir(char *dir); -int okMbox(char *mbox); -Box *openBox(char *name, char *fsname, int writable); -int openLocked(char *dir, char *file, int mode); -void parseErr(char *msg); -AuthInfo *passLogin(char*, char*); -char *readFile(int fd); -void resetCurDir(void); -Fetch *revFetch(Fetch *f); -NList *revNList(NList *s); -SList *revSList(SList *s); -int rfc822date(char *s, int n, Tm *tm); -int searchMsg(Msg *m, Search *s); -long selectFields(char *dst, long n, char *hdr, SList *fields, int matches); -void sendFlags(Box *box, int uids); -void setFlags(Box *box, Msg *m, int f); -void setupuser(AuthInfo*); -int storeMsg(Box *box, Msg *m, int uids, void *fetch); -char *strmutf7(char*); -void strrev(char *s, char *e); -int subscribe(char *mbox, int how); -void wrImpFlags(char *buf, int flags, int killRecent); -void writeErr(void); -void writeFlags(Biobuf *b, Msg *m, int recentOk); - -#pragma varargck argpos bye 1 -#pragma varargck argpos debuglog 1 - -#define MK(t) ((t*)emalloc(sizeof(t))) -#define MKZ(t) ((t*)ezmalloc(sizeof(t))) -#define MKN(t,n) ((t*)emalloc((n)*sizeof(t))) -#define MKNZ(t,n) ((t*)ezmalloc((n)*sizeof(t))) -#define MKNA(t,at,n) ((t*)emalloc(sizeof(t) + (n)*sizeof(at))) - -#define STRLEN(cs) (sizeof(cs)-1) diff --git a/sys/src/cmd/ip/imap4d/folder.c b/sys/src/cmd/ip/imap4d/folder.c deleted file mode 100644 index 8a74889e6..000000000 --- a/sys/src/cmd/ip/imap4d/folder.c +++ /dev/null @@ -1,398 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -static int copyData(int ffd, int tfd, MbLock *ml); -static MbLock mLock = -{ - .fd = -1 -}; - -static char curDir[MboxNameLen]; - -void -resetCurDir(void) -{ - curDir[0] = '\0'; -} - -int -myChdir(char *dir) -{ - if(strcmp(dir, curDir) == 0) - return 0; - if(dir[0] != '/' || strlen(dir) > MboxNameLen) - return -1; - strcpy(curDir, dir); - if(chdir(dir) < 0){ - werrstr("mychdir failed: %r"); - return -1; - } - return 0; -} - -int -cdCreate(char *dir, char *file, int mode, ulong perm) -{ - if(myChdir(dir) < 0) - return -1; - return create(file, mode, perm); -} - -Dir* -cdDirstat(char *dir, char *file) -{ - if(myChdir(dir) < 0) - return nil; - return dirstat(file); -} - -int -cdExists(char *dir, char *file) -{ - Dir *d; - - d = cdDirstat(dir, file); - if(d == nil) - return 0; - free(d); - return 1; -} - -int -cdDirwstat(char *dir, char *file, Dir *d) -{ - if(myChdir(dir) < 0) - return -1; - return dirwstat(file, d); -} - -int -cdOpen(char *dir, char *file, int mode) -{ - if(myChdir(dir) < 0) - return -1; - return open(file, mode); -} - -int -cdRemove(char *dir, char *file) -{ - if(myChdir(dir) < 0) - return -1; - return remove(file); -} - -/* - * open the one true mail lock file - */ -MbLock* -mbLock(void) -{ - int i; - - if(mLock.fd >= 0) - bye("mail lock deadlock"); - for(i = 0; i < 5; i++){ - mLock.fd = openLocked(mboxDir, "L.mbox", OREAD); - if(mLock.fd >= 0) - return &mLock; - sleep(1000); - } - return nil; -} - -void -mbUnlock(MbLock *ml) -{ - if(ml != &mLock) - bye("bad mail unlock"); - if(ml->fd < 0) - bye("mail unlock when not locked"); - close(ml->fd); - ml->fd = -1; -} - -void -mbLockRefresh(MbLock *ml) -{ - char buf[1]; - - seek(ml->fd, 0, 0); - read(ml->fd, buf, 1); -} - -int -mbLocked(void) -{ - return mLock.fd >= 0; -} - -char* -impName(char *name) -{ - char *s; - int n; - - if(cistrcmp(name, "inbox") == 0) - if(access("msgs", AEXIST) == 0) - name = "msgs"; - else - name = "mbox"; - n = strlen(name) + STRLEN(".imp") + 1; - s = binalloc(&parseBin, n, 0); - if(s == nil) - return nil; - snprint(s, n, "%s.imp", name); - return s; -} - -/* - * massage the mailbox name into something valid - * eliminates all .', and ..',s, redundatant and trailing /'s. - */ -char * -mboxName(char *s) -{ - char *ss; - - ss = mutf7str(s); - if(ss == nil) - return nil; - cleanname(ss); - return ss; -} - -char * -strmutf7(char *s) -{ - char *m; - int n; - - n = strlen(s) * MUtf7Max + 1; - m = binalloc(&parseBin, n, 0); - if(m == nil) - return nil; - if(encmutf7(m, n, s) < 0) - return nil; - return m; -} - -char * -mutf7str(char *s) -{ - char *m; - int n; - - /* - * n = strlen(s) * UTFmax / (2.67) + 1 - * UTFMax / 2.67 == 3 / (8/3) == 9 / 8 - */ - n = strlen(s); - n = (n * 9 + 7) / 8 + 1; - m = binalloc(&parseBin, n, 0); - if(m == nil) - return nil; - if(decmutf7(m, n, s) < 0) - return nil; - return m; -} - -void -splitr(char *s, int c, char **left, char **right) -{ - char *d; - int n; - - n = strlen(s); - d = binalloc(&parseBin, n + 1, 0); - if(d == nil) - parseErr("out of memory"); - strcpy(d, s); - s = strrchr(d, c); - if(s != nil){ - *left = d; - *s++ = '\0'; - *right = s; - }else{ - *right = d; - *left = d + n; - } -} - -/* - * create the mailbox and all intermediate components - * a trailing / implies the new mailbox is a directory; - * otherwise, it's a file. - * - * return with the file open for write, or directory open for read. - */ -int -createBox(char *mbox, int dir) -{ - char *m; - int fd; - - fd = -1; - for(m = mbox; *m; m++){ - if(*m == '/'){ - *m = '\0'; - if(access(mbox, AEXIST) < 0){ - if(fd >= 0) - close(fd); - fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); - if(fd < 0) - return -1; - } - *m = '/'; - } - } - if(dir) - fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); - else - fd = cdCreate(mboxDir, mbox, OWRITE, 0664); - return fd; -} - -/* - * move one mail folder to another - * destination mailbox doesn't exist. - * the source folder may be a directory or a mailbox, - * and may be in the same directory as the destination, - * or a completely different directory. - */ -int -moveBox(char *from, char *to) -{ - Dir *d; - char *fd, *fe, *td, *te, *fimp; - - splitr(from, '/', &fd, &fe); - splitr(to, '/', &td, &te); - - /* - * in the same directory: try rename - */ - d = cdDirstat(mboxDir, from); - if(d == nil) - return 0; - if(strcmp(fd, td) == 0){ - nulldir(d); - d->name = te; - if(cdDirwstat(mboxDir, from, d) >= 0){ - fimp = impName(from); - d->name = impName(te); - cdDirwstat(mboxDir, fimp, d); - free(d); - return 1; - } - } - - /* - * directory copy is too hard for now - */ - if(d->mode & DMDIR) - return 0; - free(d); - - return copyBox(from, to, 1); -} - -/* - * copy the contents of one mailbox to another - * either truncates or removes the source box if it succeeds. - */ -int -copyBox(char *from, char *to, int doremove) -{ - MbLock *ml; - char *fimp, *timp; - int ffd, tfd, ok; - - if(cistrcmp(from, "inbox") == 0) - if(access("msgs", AEXIST) == 0) - from = "msgs"; - else - from = "mbox"; - - ml = mbLock(); - if(ml == nil) - return 0; - ffd = openLocked(mboxDir, from, OREAD); - if(ffd < 0){ - mbUnlock(ml); - return 0; - } - tfd = createBox(to, 0); - if(tfd < 0){ - mbUnlock(ml); - close(ffd); - return 0; - } - - ok = copyData(ffd, tfd, ml); - close(ffd); - close(tfd); - if(!ok){ - mbUnlock(ml); - return 0; - } - - fimp = impName(from); - timp = impName(to); - if(fimp != nil && timp != nil){ - ffd = cdOpen(mboxDir, fimp, OREAD); - if(ffd >= 0){ - tfd = cdCreate(mboxDir, timp, OWRITE, 0664); - if(tfd >= 0){ - copyData(ffd, tfd, ml); - close(tfd); - } - close(ffd); - } - } - cdRemove(mboxDir, fimp); - if(doremove) - cdRemove(mboxDir, from); - else - close(cdOpen(mboxDir, from, OWRITE|OTRUNC)); - mbUnlock(ml); - return 1; -} - -/* - * copies while holding the mail lock, - * then tries to copy permissions and group ownership - */ -static int -copyData(int ffd, int tfd, MbLock *ml) -{ - Dir *fd, td; - char buf[BufSize]; - int n; - - for(;;){ - n = read(ffd, buf, BufSize); - if(n <= 0){ - if(n < 0) - return 0; - break; - } - if(write(tfd, buf, n) != n) - return 0; - mbLockRefresh(ml); - } - fd = dirfstat(ffd); - if(fd != nil){ - nulldir(&td); - td.mode = fd->mode; - if(dirfwstat(tfd, &td) >= 0){ - nulldir(&td); - td.gid = fd->gid; - dirfwstat(tfd, &td); - } - } - return 1; -} diff --git a/sys/src/cmd/ip/imap4d/imap4d.c b/sys/src/cmd/ip/imap4d/imap4d.c deleted file mode 100644 index 6eafe481e..000000000 --- a/sys/src/cmd/ip/imap4d/imap4d.c +++ /dev/null @@ -1,2102 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <auth.h> -#include <bio.h> -#include "imap4d.h" - -/* - * these should be in libraries - */ -char *csquery(char *attr, char *val, char *rattr); - -/* - * /lib/rfc/rfc2060 imap4rev1 - * /lib/rfc/rfc2683 is implementation advice - * /lib/rfc/rfc2342 is namespace capability - * /lib/rfc/rfc2222 is security protocols - * /lib/rfc/rfc1731 is security protocols - * /lib/rfc/rfc2221 is LOGIN-REFERRALS - * /lib/rfc/rfc2193 is MAILBOX-REFERRALS - * /lib/rfc/rfc2177 is IDLE capability - * /lib/rfc/rfc2195 is CRAM-MD5 authentication - * /lib/rfc/rfc2088 is LITERAL+ capability - * /lib/rfc/rfc1760 is S/Key authentication - * - * outlook uses "Secure Password Authentication" aka ntlm authentication - * - * capabilities from nslocum - * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT - */ - -typedef struct ParseCmd ParseCmd; - -enum -{ - UlongMax = 4294967295, -}; - -struct ParseCmd -{ - char *name; - void (*f)(char *tg, char *cmd); -}; - -static void appendCmd(char *tg, char *cmd); -static void authenticateCmd(char *tg, char *cmd); -static void capabilityCmd(char *tg, char *cmd); -static void closeCmd(char *tg, char *cmd); -static void copyCmd(char *tg, char *cmd); -static void createCmd(char *tg, char *cmd); -static void deleteCmd(char *tg, char *cmd); -static void expungeCmd(char *tg, char *cmd); -static void fetchCmd(char *tg, char *cmd); -static void idleCmd(char *tg, char *cmd); -static void listCmd(char *tg, char *cmd); -static void loginCmd(char *tg, char *cmd); -static void logoutCmd(char *tg, char *cmd); -static void namespaceCmd(char *tg, char *cmd); -static void noopCmd(char *tg, char *cmd); -static void renameCmd(char *tg, char *cmd); -static void searchCmd(char *tg, char *cmd); -static void selectCmd(char *tg, char *cmd); -static void statusCmd(char *tg, char *cmd); -static void storeCmd(char *tg, char *cmd); -static void subscribeCmd(char *tg, char *cmd); -static void uidCmd(char *tg, char *cmd); -static void unsubscribeCmd(char *tg, char *cmd); - -static void copyUCmd(char *tg, char *cmd, int uids); -static void fetchUCmd(char *tg, char *cmd, int uids); -static void searchUCmd(char *tg, char *cmd, int uids); -static void storeUCmd(char *tg, char *cmd, int uids); - -static void imap4(int); -static void status(int expungeable, int uids); -static void cleaner(void); -static void check(void); -static int catcher(void*, char*); - -static Search *searchKey(int first); -static Search *searchKeys(int first, Search *tail); -static char *astring(void); -static char *atomString(char *disallowed, char *initial); -static char *atom(void); -static void badsyn(void); -static void clearcmd(void); -static char *command(void); -static void crnl(void); -static Fetch *fetchAtt(char *s, Fetch *f); -static Fetch *fetchWhat(void); -static int flagList(void); -static int flags(void); -static int getc(void); -static char *listmbox(void); -static char *literal(void); -static ulong litlen(void); -static MsgSet *msgSet(int); -static void mustBe(int c); -static ulong number(int nonzero); -static int peekc(void); -static char *quoted(void); -static void sectText(Fetch *f, int mimeOk); -static ulong seqNo(void); -static Store *storeWhat(void); -static char *tag(void); -static ulong uidNo(void); -static void ungetc(void); - -static ParseCmd SNonAuthed[] = -{ - {"capability", capabilityCmd}, - {"logout", logoutCmd}, - {"x-exit", logoutCmd}, - {"noop", noopCmd}, - - {"login", loginCmd}, - {"authenticate", authenticateCmd}, - - nil -}; - -static ParseCmd SAuthed[] = -{ - {"capability", capabilityCmd}, - {"logout", logoutCmd}, - {"x-exit", logoutCmd}, - {"noop", noopCmd}, - - {"append", appendCmd}, - {"create", createCmd}, - {"delete", deleteCmd}, - {"examine", selectCmd}, - {"select", selectCmd}, - {"idle", idleCmd}, - {"list", listCmd}, - {"lsub", listCmd}, - {"namespace", namespaceCmd}, - {"rename", renameCmd}, - {"status", statusCmd}, - {"subscribe", subscribeCmd}, - {"unsubscribe", unsubscribeCmd}, - - nil -}; - -static ParseCmd SSelected[] = -{ - {"capability", capabilityCmd}, - {"logout", logoutCmd}, - {"x-exit", logoutCmd}, - {"noop", noopCmd}, - - {"append", appendCmd}, - {"create", createCmd}, - {"delete", deleteCmd}, - {"examine", selectCmd}, - {"select", selectCmd}, - {"idle", idleCmd}, - {"list", listCmd}, - {"lsub", listCmd}, - {"namespace", namespaceCmd}, - {"rename", renameCmd}, - {"status", statusCmd}, - {"subscribe", subscribeCmd}, - {"unsubscribe", unsubscribeCmd}, - - {"check", noopCmd}, - {"close", closeCmd}, - {"copy", copyCmd}, - {"expunge", expungeCmd}, - {"fetch", fetchCmd}, - {"search", searchCmd}, - {"store", storeCmd}, - {"uid", uidCmd}, - - nil -}; - -static char *atomStop = "(){%*\"\\"; -static Chalstate *chal; -static int chaled; -static ParseCmd *imapState; -static jmp_buf parseJmp; -static char *parseMsg; -static int allowPass; -static int allowCR; -static int exiting; -static QLock imaplock; -static int idlepid = -1; - -Biobuf bout; -Biobuf bin; -char username[UserNameLen]; -char mboxDir[MboxNameLen]; -char *servername; -char *site; -char *remote; -Box *selected; -Bin *parseBin; -int debug; - -void -main(int argc, char *argv[]) -{ - char *s, *t; - int preauth, n; - - Binit(&bin, 0, OREAD); - Binit(&bout, 1, OWRITE); - - /* for auth */ - fmtinstall('H', encodefmt); - fmtinstall('[', encodefmt); - - preauth = 0; - allowPass = 0; - allowCR = 0; - ARGBEGIN{ - case 'a': - preauth = 1; - break; - case 'd': - site = ARGF(); - break; - case 'c': - allowCR = 1; - break; - case 'p': - allowPass = 1; - break; - case 'r': - remote = ARGF(); - break; - case 's': - servername = ARGF(); - break; - case 'v': - debug = 1; - debuglog("imap4d debugging enabled\n"); - break; - default: - fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n"); - bye("usage"); - break; - }ARGEND - - if(allowPass && allowCR){ - fprint(2, "%s: -c and -p are mutually exclusive\n", argv0); - bye("usage"); - } - - if(preauth) - setupuser(nil); - - if(servername == nil){ - servername = csquery("sys", sysname(), "dom"); - if(servername == nil) - servername = sysname(); - if(servername == nil){ - fprint(2, "ip/imap4d can't find server name: %r\n"); - bye("can't find system name"); - } - } - if(site == nil){ - t = getenv("site"); - if(t == nil) - site = servername; - else{ - n = strlen(t); - s = strchr(servername, '.'); - if(s == nil) - s = servername; - else - s++; - n += strlen(s) + 2; - site = emalloc(n); - snprint(site, n, "%s.%s", t, s); - } - } - - rfork(RFNOTEG|RFREND); - - atnotify(catcher, 1); - qlock(&imaplock); - atexit(cleaner); - imap4(preauth); -} - -static void -imap4(int preauth) -{ - char *volatile tg; - char *volatile cmd; - ParseCmd *st; - - if(preauth){ - Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username); - imapState = SAuthed; - }else{ - Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername); - imapState = SNonAuthed; - } - if(Bflush(&bout) < 0) - writeErr(); - - chaled = 0; - - tg = nil; - cmd = nil; - if(setjmp(parseJmp)){ - if(tg == nil) - Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg); - else if(cmd == nil) - Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg); - else - Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg); - clearcmd(); - if(Bflush(&bout) < 0) - writeErr(); - binfree(&parseBin); - } - for(;;){ - if(mbLocked()) - bye("internal error: mailbox lock held"); - tg = nil; - cmd = nil; - tg = tag(); - mustBe(' '); - cmd = atom(); - - /* - * note: outlook express is broken: it requires echoing the - * command as part of matching response - */ - for(st = imapState; st->name != nil; st++){ - if(cistrcmp(cmd, st->name) == 0){ - (*st->f)(tg, cmd); - break; - } - } - if(st->name == nil){ - clearcmd(); - Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd); - } - - if(Bflush(&bout) < 0) - writeErr(); - binfree(&parseBin); - } -} - -void -bye(char *fmt, ...) -{ - va_list arg; - - va_start(arg, fmt); - Bprint(&bout, "* bye "); - Bvprint(&bout, fmt, arg); - Bprint(&bout, "\r\n"); - Bflush(&bout); -exits("rob2"); - exits(0); -} - -void -parseErr(char *msg) -{ - parseMsg = msg; - longjmp(parseJmp, 1); -} - -/* - * an error occured while writing to the client - */ -void -writeErr(void) -{ - cleaner(); - _exits("connection closed"); -} - -static int -catcher(void *v, char *msg) -{ - USED(v); - if(strstr(msg, "closed pipe") != nil) - return 1; - return 0; -} - -/* - * wipes out the idleCmd backgroung process if it is around. - * this can only be called if the current proc has qlocked imaplock. - * it must be the last piece of imap4d code executed. - */ -static void -cleaner(void) -{ - int i; - - if(idlepid < 0) - return; - exiting = 1; - close(0); - close(1); - close(2); - - /* - * the other proc is either stuck in a read, a sleep, - * or is trying to lock imap4lock. - * get him out of it so he can exit cleanly - */ - qunlock(&imaplock); - for(i = 0; i < 4; i++) - postnote(PNGROUP, getpid(), "die"); -} - -/* - * send any pending status updates to the client - * careful: shouldn't exit, because called by idle polling proc - * - * can't always send pending info - * in particular, can't send expunge info - * in response to a fetch, store, or search command. - * - * rfc2060 5.2: server must send mailbox size updates - * rfc2060 5.2: server may send flag updates - * rfc2060 5.5: servers prohibited from sending expunge while fetch, store, search in progress - * rfc2060 7: in selected state, server checks mailbox for new messages as part of every command - * sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox - * should also send appropriate untagged FETCH and EXPUNGE messages if another agent - * changes the state of any message flags or expunges any messages - * rfc2060 7.4.1 expunge server response must not be sent when no command is in progress, - * nor while responding to a fetch, stort, or search command (uid versions are ok) - * command only "in progress" after entirely parsed. - * - * strategy for third party deletion of messages or of a mailbox - * - * deletion of a selected mailbox => act like all message are expunged - * not strictly allowed by rfc2180, but close to method 3.2. - * - * renaming same as deletion - * - * copy - * reject iff a deleted message is in the request - * - * search, store, fetch operations on expunged messages - * ignore the expunged messages - * return tagged no if referenced - */ -static void -status(int expungeable, int uids) -{ - int tell; - - if(!selected) - return; - tell = 0; - if(expungeable) - tell = expungeMsgs(selected, 1); - if(selected->sendFlags) - sendFlags(selected, uids); - if(tell || selected->toldMax != selected->max){ - Bprint(&bout, "* %lud EXISTS\r\n", selected->max); - selected->toldMax = selected->max; - } - if(tell || selected->toldRecent != selected->recent){ - Bprint(&bout, "* %lud RECENT\r\n", selected->recent); - selected->toldRecent = selected->recent; - } - if(tell) - closeImp(selected, checkBox(selected, 1)); -} - -/* - * careful: can't exit, because called by idle polling proc - */ -static void -check(void) -{ - if(!selected) - return; - checkBox(selected, 0); - status(1, 0); -} - -static void -appendCmd(char *tg, char *cmd) -{ - char *mbox, head[128]; - ulong t, n, now; - int flags, ok; - - mustBe(' '); - mbox = astring(); - mustBe(' '); - flags = 0; - if(peekc() == '('){ - flags = flagList(); - mustBe(' '); - } - now = time(nil); - if(peekc() == '"'){ - t = imap4DateTime(quoted()); - if(t == ~0) - parseErr("illegal date format"); - mustBe(' '); - if(t > now) - t = now; - }else - t = now; - n = litlen(); - - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - check(); - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - return; - } - if(!cdExists(mboxDir, mbox)){ - check(); - Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd); - return; - } - - snprint(head, sizeof(head), "From %s %s", username, ctime(t)); - ok = appendSave(mbox, flags, head, &bin, n); - crnl(); - check(); - if(ok) - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); - else - Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd); -} - -static void -authenticateCmd(char *tg, char *cmd) -{ - char *s, *t; - - mustBe(' '); - s = atom(); - crnl(); - auth_freechal(chal); - chal = nil; - if(cistrcmp(s, "cram-md5") == 0){ - t = cramauth(); - if(t == nil){ - Bprint(&bout, "%s OK %s\r\n", tg, cmd); - imapState = SAuthed; - }else - Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t); - }else - Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd); -} - -static void -capabilityCmd(char *tg, char *cmd) -{ - crnl(); - check(); -// nslocum's capabilities -// Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n"); - Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n"); - Bprint(&bout, "%s OK %s\r\n", tg, cmd); -} - -static void -closeCmd(char *tg, char *cmd) -{ - crnl(); - imapState = SAuthed; - closeBox(selected, 1); - selected = nil; - Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd); -} - -/* - * note: message id's are before any pending expunges - */ -static void -copyCmd(char *tg, char *cmd) -{ - copyUCmd(tg, cmd, 0); -} - -static void -copyUCmd(char *tg, char *cmd, int uids) -{ - MsgSet *ms; - char *uid, *mbox; - ulong max; - int ok; - - mustBe(' '); - ms = msgSet(uids); - mustBe(' '); - mbox = astring(); - crnl(); - - uid = ""; - if(uids) - uid = "uid "; - - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - status(1, uids); - Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd); - return; - } - if(!cdExists(mboxDir, mbox)){ - check(); - Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd); - return; - } - - max = selected->max; - checkBox(selected, 0); - ok = forMsgs(selected, ms, max, uids, copyCheck, nil); - if(ok) - ok = forMsgs(selected, ms, max, uids, copySave, mbox); - - status(1, uids); - if(ok) - Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd); - else - Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd); -} - -static void -createCmd(char *tg, char *cmd) -{ - char *mbox, *m; - int fd, slash; - - mustBe(' '); - mbox = astring(); - crnl(); - check(); - - m = strchr(mbox, '\0'); - slash = m != mbox && m[-1] == '/'; - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - return; - } - if(cistrcmp(mbox, "inbox") == 0){ - Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd); - return; - } - if(access(mbox, AEXIST) >= 0){ - Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd); - return; - } - - fd = createBox(mbox, slash); - close(fd); - if(fd < 0) - Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox); - else - Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd); -} - -static void -deleteCmd(char *tg, char *cmd) -{ - char *mbox, *imp; - - mustBe(' '); - mbox = astring(); - crnl(); - check(); - - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - return; - } - - imp = impName(mbox); - if(cistrcmp(mbox, "inbox") == 0 - || imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp) - || cdRemove(mboxDir, mbox) < 0) - Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox); - else - Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd); -} - -static void -expungeCmd(char *tg, char *cmd) -{ - int ok; - - crnl(); - ok = deleteMsgs(selected); - check(); - if(ok) - Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd); - else - Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd); -} - -static void -fetchCmd(char *tg, char *cmd) -{ - fetchUCmd(tg, cmd, 0); -} - -static void -fetchUCmd(char *tg, char *cmd, int uids) -{ - Fetch *f; - MsgSet *ms; - MbLock *ml; - char *uid; - ulong max; - int ok; - - mustBe(' '); - ms = msgSet(uids); - mustBe(' '); - f = fetchWhat(); - crnl(); - uid = ""; - if(uids) - uid = "uid "; - max = selected->max; - ml = checkBox(selected, 1); - if(ml != nil) - forMsgs(selected, ms, max, uids, fetchSeen, f); - closeImp(selected, ml); - ok = ml != nil && forMsgs(selected, ms, max, uids, fetchMsg, f); - status(uids, uids); - if(ok) - Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd); - else - Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd); -} - -static void -idleCmd(char *tg, char *cmd) -{ - int c, pid; - - crnl(); - Bprint(&bout, "+ idling, waiting for done\r\n"); - if(Bflush(&bout) < 0) - writeErr(); - - if(idlepid < 0){ - pid = rfork(RFPROC|RFMEM|RFNOWAIT); - if(pid == 0){ - for(;;){ - qlock(&imaplock); - if(exiting) - break; - - /* - * parent may have changed curDir, but it doesn't change our . - */ - resetCurDir(); - - check(); - if(Bflush(&bout) < 0) - writeErr(); - qunlock(&imaplock); - sleep(15*1000); - enableForwarding(); - } -_exits("rob3"); - _exits(0); - } - idlepid = pid; - } - - qunlock(&imaplock); - - /* - * clear out the next line, which is supposed to contain (case-insensitive) - * done\n - * this is special code since it has to dance with the idle polling proc - * and handle exiting correctly. - */ - for(;;){ - c = getc(); - if(c < 0){ - qlock(&imaplock); - if(!exiting) - cleaner(); -_exits("rob4"); - _exits(0); - } - if(c == '\n') - break; - } - - qlock(&imaplock); - if(exiting) -{_exits("rob5"); - _exits(0); -} - - /* - * child may have changed curDir, but it doesn't change our . - */ - resetCurDir(); - - check(); - Bprint(&bout, "%s OK %s terminated\r\n", tg, cmd); -} - -static void -listCmd(char *tg, char *cmd) -{ - char *s, *t, *ss, *ref, *mbox; - int n; - - mustBe(' '); - s = astring(); - mustBe(' '); - t = listmbox(); - crnl(); - check(); - ref = mutf7str(s); - mbox = mutf7str(t); - if(ref == nil || mbox == nil){ - Bprint(&bout, "%s BAD %s mailbox name not in modified utf-7\r\n", tg, cmd); - return; - } - - /* - * special request for hierarchy delimiter and root name - * root name appears to be name up to and including any delimiter, - * or the empty string, if there is no delimiter. - * - * this must change if the # namespace convention is supported. - */ - if(*mbox == '\0'){ - s = strchr(ref, '/'); - if(s == nil) - ref = ""; - else - s[1] = '\0'; - Bprint(&bout, "* %s (\\Noselect) \"/\" \"%s\"\r\n", cmd, ref); - Bprint(&bout, "%s OK %s\r\n", tg, cmd); - return; - } - - - /* - * massage the listing name: - * clean up the components individually, - * then rip off componenets from the ref to - * take care of leading ..'s in the mbox. - * - * the cleanup can wipe out * followed by a .. - * tough luck if such a stupid pattern is given. - */ - cleanname(mbox); - if(strcmp(mbox, ".") == 0) - *mbox = '\0'; - if(mbox[0] == '/') - *ref = '\0'; - else if(*ref != '\0'){ - cleanname(ref); - if(strcmp(ref, ".") == 0) - *ref = '\0'; - }else - *ref = '\0'; - while(*ref && isdotdot(mbox)){ - s = strrchr(ref, '/'); - if(s == nil) - s = ref; - if(isdotdot(s)) - break; - *s = '\0'; - mbox += 2; - if(*mbox == '/') - mbox++; - } - if(*ref == '\0'){ - s = mbox; - ss = s; - }else{ - n = strlen(ref) + strlen(mbox) + 2; - t = binalloc(&parseBin, n, 0); - if(t == nil) - parseErr("out of memory"); - snprint(t, n, "%s/%s", ref, mbox); - s = t; - ss = s + strlen(ref); - } - - /* - * only allow activity in /mail/box - */ - if(s[0] == '/' || isdotdot(s)){ - Bprint(&bout, "%s NO illegal mailbox pattern\r\n", tg); - return; - } - - if(cistrcmp(cmd, "lsub") == 0) - lsubBoxes(cmd, s, ss); - else - listBoxes(cmd, s, ss); - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -} - -static char* -passCR(char*u, char*p) -{ - static char Ebadch[] = "can't get challenge"; - static char nchall[64]; - static char response[64]; - static Chalstate *ch = nil; - AuthInfo *ai; - -again: - if (ch == nil){ - if(!(ch = auth_challenge("proto=p9cr role=server user=%q", u))) - return Ebadch; - snprint(nchall, 64, " encrypt challenge: %s", ch->chal); - return nchall; - } else { - strncpy(response, p, 64); - ch->resp = response; - ch->nresp = strlen(response); - ai = auth_response(ch); - auth_freechal(ch); - ch = nil; - if (ai == nil) - goto again; - setupuser(ai); - return nil; - } - -} - -static void -loginCmd(char *tg, char *cmd) -{ - char *s, *t; - AuthInfo *ai; - char*r; - mustBe(' '); - s = astring(); /* uid */ - mustBe(' '); - t = astring(); /* password */ - crnl(); - if(allowCR){ - if ((r = passCR(s, t)) == nil){ - Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd); - imapState = SAuthed; - } else { - Bprint(&bout, "* NO [ALERT] %s\r\n", r); - Bprint(&bout, "%s NO %s succeeded\r\n", tg, cmd); - } - return; - } - else if(allowPass){ - if(ai = passLogin(s, t)){ - setupuser(ai); - Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd); - imapState = SAuthed; - }else - Bprint(&bout, "%s NO %s failed check\r\n", tg, cmd); - return; - } - Bprint(&bout, "%s NO %s plaintext passwords disallowed\r\n", tg, cmd); -} - -/* - * logout or x-exit, which doesn't expunge the mailbox - */ -static void -logoutCmd(char *tg, char *cmd) -{ - crnl(); - - if(cmd[0] != 'x' && selected){ - closeBox(selected, 1); - selected = nil; - } - Bprint(&bout, "* bye\r\n"); - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -exits("rob6"); - exits(0); -} - -static void -namespaceCmd(char *tg, char *cmd) -{ - crnl(); - check(); - - /* - * personal, other users, shared namespaces - * send back nil or descriptions of (prefix heirarchy-delim) for each case - */ - Bprint(&bout, "* NAMESPACE ((\"\" \"/\")) nil nil\r\n"); - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -} - -static void -noopCmd(char *tg, char *cmd) -{ - crnl(); - check(); - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); - enableForwarding(); -} - -/* - * this is only a partial implementation - * should copy files to other directories, - * and copy & truncate inbox - */ -static void -renameCmd(char *tg, char *cmd) -{ - char *from, *to; - int ok; - - mustBe(' '); - from = astring(); - mustBe(' '); - to = astring(); - crnl(); - check(); - - to = mboxName(to); - if(to == nil || !okMbox(to) || cistrcmp(to, "inbox") == 0){ - Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd); - return; - } - if(access(to, AEXIST) >= 0){ - Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd); - return; - } - from = mboxName(from); - if(from == nil || !okMbox(from)){ - Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd); - return; - } - if(cistrcmp(from, "inbox") == 0) - ok = copyBox(from, to, 0); - else - ok = moveBox(from, to); - - if(ok) - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); - else - Bprint(&bout, "%s NO %s failed\r\n", tg, cmd); -} - -static void -searchCmd(char *tg, char *cmd) -{ - searchUCmd(tg, cmd, 0); -} - -static void -searchUCmd(char *tg, char *cmd, int uids) -{ - Search rock; - Msg *m; - char *uid; - ulong id; - - mustBe(' '); - rock.next = nil; - searchKeys(1, &rock); - crnl(); - uid = ""; - if(uids) - uid = "uid "; - if(rock.next != nil && rock.next->key == SKCharset){ - if(cistrcmp(rock.next->s, "utf-8") != 0 - && cistrcmp(rock.next->s, "us-ascii") != 0){ - Bprint(&bout, "%s NO [BADCHARSET] (\"US-ASCII\" \"UTF-8\") %s%s failed\r\n", tg, uid, cmd); - checkBox(selected, 0); - status(uids, uids); - return; - } - rock.next = rock.next->next; - } - Bprint(&bout, "* search"); - for(m = selected->msgs; m != nil; m = m->next) - m->matched = searchMsg(m, rock.next); - for(m = selected->msgs; m != nil; m = m->next){ - if(m->matched){ - if(uids) - id = m->uid; - else - id = m->seq; - Bprint(&bout, " %lud", id); - } - } - Bprint(&bout, "\r\n"); - checkBox(selected, 0); - status(uids, uids); - Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd); -} - -static void -selectCmd(char *tg, char *cmd) -{ - Msg *m; - char *s, *mbox; - - mustBe(' '); - mbox = astring(); - crnl(); - - if(selected){ - imapState = SAuthed; - closeBox(selected, 1); - selected = nil; - } - - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - return; - } - - selected = openBox(mbox, "imap", cistrcmp(cmd, "select") == 0); - if(selected == nil){ - Bprint(&bout, "%s NO %s can't open mailbox %s: %r\r\n", tg, cmd, mbox); - return; - } - - imapState = SSelected; - - Bprint(&bout, "* FLAGS (\\Seen \\Answered \\Flagged \\Deleted \\Draft)\r\n"); - Bprint(&bout, "* %lud EXISTS\r\n", selected->max); - selected->toldMax = selected->max; - Bprint(&bout, "* %lud RECENT\r\n", selected->recent); - selected->toldRecent = selected->recent; - for(m = selected->msgs; m != nil; m = m->next){ - if(!m->expunged && (m->flags & MSeen) != MSeen){ - Bprint(&bout, "* OK [UNSEEN %ld]\r\n", m->seq); - break; - } - } - Bprint(&bout, "* OK [PERMANENTFLAGS (\\Seen \\Answered \\Flagged \\Draft \\Deleted)]\r\n"); - Bprint(&bout, "* OK [UIDNEXT %ld]\r\n", selected->uidnext); - Bprint(&bout, "* OK [UIDVALIDITY %ld]\r\n", selected->uidvalidity); - s = "READ-ONLY"; - if(selected->writable) - s = "READ-WRITE"; - Bprint(&bout, "%s OK [%s] %s %s completed\r\n", tg, s, cmd, mbox); -} - -static NamedInt statusItems[] = -{ - {"MESSAGES", SMessages}, - {"RECENT", SRecent}, - {"UIDNEXT", SUidNext}, - {"UIDVALIDITY", SUidValidity}, - {"UNSEEN", SUnseen}, - {nil, 0} -}; - -static void -statusCmd(char *tg, char *cmd) -{ - Box *box; - Msg *m; - char *s, *mbox; - ulong v; - int si, i; - - mustBe(' '); - mbox = astring(); - mustBe(' '); - mustBe('('); - si = 0; - for(;;){ - s = atom(); - i = mapInt(statusItems, s); - if(i == 0) - parseErr("illegal status item"); - si |= i; - if(peekc() == ')') - break; - mustBe(' '); - } - mustBe(')'); - crnl(); - - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox)){ - check(); - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - return; - } - - box = openBox(mbox, "status", 1); - if(box == nil){ - check(); - Bprint(&bout, "%s NO [TRYCREATE] %s can't open mailbox %s: %r\r\n", tg, cmd, mbox); - return; - } - - Bprint(&bout, "* STATUS %s (", mbox); - s = ""; - for(i = 0; statusItems[i].name != nil; i++){ - if(si & statusItems[i].v){ - v = 0; - switch(statusItems[i].v){ - case SMessages: - v = box->max; - break; - case SRecent: - v = box->recent; - break; - case SUidNext: - v = box->uidnext; - break; - case SUidValidity: - v = box->uidvalidity; - break; - case SUnseen: - v = 0; - for(m = box->msgs; m != nil; m = m->next) - if((m->flags & MSeen) != MSeen) - v++; - break; - default: - Bprint(&bout, ")"); - bye("internal error: status item not implemented"); - break; - } - Bprint(&bout, "%s%s %lud", s, statusItems[i].name, v); - s = " "; - } - } - Bprint(&bout, ")\r\n"); - closeBox(box, 1); - - check(); - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -} - -static void -storeCmd(char *tg, char *cmd) -{ - storeUCmd(tg, cmd, 0); -} - -static void -storeUCmd(char *tg, char *cmd, int uids) -{ - Store *st; - MsgSet *ms; - MbLock *ml; - char *uid; - ulong max; - int ok; - - mustBe(' '); - ms = msgSet(uids); - mustBe(' '); - st = storeWhat(); - crnl(); - uid = ""; - if(uids) - uid = "uid "; - max = selected->max; - ml = checkBox(selected, 1); - ok = ml != nil && forMsgs(selected, ms, max, uids, storeMsg, st); - closeImp(selected, ml); - status(uids, uids); - if(ok) - Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd); - else - Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd); -} - -/* - * minimal implementation of subscribe - * all folders are automatically subscribed, - * and can't be unsubscribed - */ -static void -subscribeCmd(char *tg, char *cmd) -{ - Box *box; - char *mbox; - int ok; - - mustBe(' '); - mbox = astring(); - crnl(); - check(); - mbox = mboxName(mbox); - ok = 0; - if(mbox != nil && okMbox(mbox)){ - box = openBox(mbox, "subscribe", 0); - if(box != nil){ - ok = subscribe(mbox, 's'); - closeBox(box, 1); - } - } - if(!ok) - Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd); - else - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -} - -static void -uidCmd(char *tg, char *cmd) -{ - char *sub; - - mustBe(' '); - sub = atom(); - if(cistrcmp(sub, "copy") == 0) - copyUCmd(tg, sub, 1); - else if(cistrcmp(sub, "fetch") == 0) - fetchUCmd(tg, sub, 1); - else if(cistrcmp(sub, "search") == 0) - searchUCmd(tg, sub, 1); - else if(cistrcmp(sub, "store") == 0) - storeUCmd(tg, sub, 1); - else{ - clearcmd(); - Bprint(&bout, "%s BAD %s illegal uid command %s\r\n", tg, cmd, sub); - } -} - -static void -unsubscribeCmd(char *tg, char *cmd) -{ - char *mbox; - - mustBe(' '); - mbox = astring(); - crnl(); - check(); - mbox = mboxName(mbox); - if(mbox == nil || !okMbox(mbox) || !subscribe(mbox, 'u')) - Bprint(&bout, "%s NO %s can't unsubscribe\r\n", tg, cmd); - else - Bprint(&bout, "%s OK %s completed\r\n", tg, cmd); -} - -static void -badsyn(void) -{ - parseErr("bad syntax"); -} - -static void -clearcmd(void) -{ - int c; - - for(;;){ - c = getc(); - if(c < 0) - bye("end of input"); - if(c == '\n') - return; - } -} - -static void -crnl(void) -{ - int c; - - c = getc(); - if(c == '\n') - return; - if(c != '\r' || getc() != '\n') - badsyn(); -} - -static void -mustBe(int c) -{ - if(getc() != c){ - ungetc(); - badsyn(); - } -} - -/* - * flaglist : '(' ')' | '(' flags ')' - */ -static int -flagList(void) -{ - int f; - - mustBe('('); - f = 0; - if(peekc() != ')') - f = flags(); - - mustBe(')'); - return f; -} - -/* - * flags : flag | flags ' ' flag - * flag : '\' atom | atom - */ -static int -flags(void) -{ - int ff, flags; - char *s; - int c; - - flags = 0; - for(;;){ - c = peekc(); - if(c == '\\'){ - mustBe('\\'); - s = atomString(atomStop, "\\"); - }else if(strchr(atomStop, c) != nil) - s = atom(); - else - break; - ff = mapFlag(s); - if(ff == 0) - parseErr("flag not supported"); - flags |= ff; - if(peekc() != ' ') - break; - mustBe(' '); - } - if(flags == 0) - parseErr("no flags given"); - return flags; -} - -/* - * storeWhat : osign 'FLAGS' ' ' storeflags - * | osign 'FLAGS.SILENT' ' ' storeflags - * osign : - * | '+' | '-' - * storeflags : flagList | flags - */ -static Store* -storeWhat(void) -{ - int f; - char *s; - int c, w; - - c = peekc(); - if(c == '+' || c == '-') - mustBe(c); - else - c = 0; - s = atom(); - w = 0; - if(cistrcmp(s, "flags") == 0) - w = STFlags; - else if(cistrcmp(s, "flags.silent") == 0) - w = STFlagsSilent; - else - parseErr("illegal store attribute"); - mustBe(' '); - if(peekc() == '(') - f = flagList(); - else - f = flags(); - return mkStore(c, w, f); -} - -/* - * fetchWhat : "ALL" | "FULL" | "FAST" | fetchAtt | '(' fetchAtts ')' - * fetchAtts : fetchAtt | fetchAtts ' ' fetchAtt - */ -static char *fetchAtom = "(){}%*\"\\[]"; -static Fetch* -fetchWhat(void) -{ - Fetch *f; - char *s; - - if(peekc() == '('){ - getc(); - f = nil; - for(;;){ - s = atomString(fetchAtom, ""); - f = fetchAtt(s, f); - if(peekc() == ')') - break; - mustBe(' '); - } - getc(); - return revFetch(f); - } - - s = atomString(fetchAtom, ""); - if(cistrcmp(s, "all") == 0) - f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, nil)))); - else if(cistrcmp(s, "fast") == 0) - f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, nil))); - else if(cistrcmp(s, "full") == 0) - f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, mkFetch(FBody, nil))))); - else - f = fetchAtt(s, nil); - return f; -} - -/* - * fetchAtt : "ENVELOPE" | "FLAGS" | "INTERNALDATE" - * | "RFC822" | "RFC822.HEADER" | "RFC822.SIZE" | "RFC822.TEXT" - * | "BODYSTRUCTURE" - * | "UID" - * | "BODY" - * | "BODY" bodysubs - * | "BODY.PEEK" bodysubs - * bodysubs : sect - * | sect '<' number '.' nz-number '>' - * sect : '[' sectSpec ']' - * sectSpec : sectMsgText - * | sectPart - * | sectPart '.' sectText - * sectPart : nz-number - * | sectPart '.' nz-number - */ -static Fetch* -fetchAtt(char *s, Fetch *f) -{ - NList *sect; - int c; - - if(cistrcmp(s, "envelope") == 0) - return mkFetch(FEnvelope, f); - if(cistrcmp(s, "flags") == 0) - return mkFetch(FFlags, f); - if(cistrcmp(s, "internaldate") == 0) - return mkFetch(FInternalDate, f); - if(cistrcmp(s, "RFC822") == 0) - return mkFetch(FRfc822, f); - if(cistrcmp(s, "RFC822.header") == 0) - return mkFetch(FRfc822Head, f); - if(cistrcmp(s, "RFC822.size") == 0) - return mkFetch(FRfc822Size, f); - if(cistrcmp(s, "RFC822.text") == 0) - return mkFetch(FRfc822Text, f); - if(cistrcmp(s, "bodystructure") == 0) - return mkFetch(FBodyStruct, f); - if(cistrcmp(s, "uid") == 0) - return mkFetch(FUid, f); - - if(cistrcmp(s, "body") == 0){ - if(peekc() != '[') - return mkFetch(FBody, f); - f = mkFetch(FBodySect, f); - }else if(cistrcmp(s, "body.peek") == 0) - f = mkFetch(FBodyPeek, f); - else - parseErr("illegal fetch attribute"); - - mustBe('['); - c = peekc(); - if(c >= '1' && c <= '9'){ - sect = mkNList(number(1), nil); - while(peekc() == '.'){ - getc(); - c = peekc(); - if(c >= '1' && c <= '9'){ - sect = mkNList(number(1), sect); - }else{ - break; - } - } - f->sect = revNList(sect); - } - if(peekc() != ']') - sectText(f, f->sect != nil); - mustBe(']'); - - if(peekc() != '<') - return f; - - f->partial = 1; - mustBe('<'); - f->start = number(0); - mustBe('.'); - f->size = number(1); - mustBe('>'); - return f; -} - -/* - * sectText : sectMsgText | "MIME" - * sectMsgText : "HEADER" - * | "TEXT" - * | "HEADER.FIELDS" ' ' hdrList - * | "HEADER.FIELDS.NOT" ' ' hdrList - * hdrList : '(' hdrs ')' - * hdrs: : astring - * | hdrs ' ' astring - */ -static void -sectText(Fetch *f, int mimeOk) -{ - SList *h; - char *s; - - s = atomString(fetchAtom, ""); - if(cistrcmp(s, "header") == 0){ - f->part = FPHead; - return; - } - if(cistrcmp(s, "text") == 0){ - f->part = FPText; - return; - } - if(mimeOk && cistrcmp(s, "mime") == 0){ - f->part = FPMime; - return; - } - if(cistrcmp(s, "header.fields") == 0) - f->part = FPHeadFields; - else if(cistrcmp(s, "header.fields.not") == 0) - f->part = FPHeadFieldsNot; - else - parseErr("illegal fetch section text"); - mustBe(' '); - mustBe('('); - h = nil; - for(;;){ - h = mkSList(astring(), h); - if(peekc() == ')') - break; - mustBe(' '); - } - mustBe(')'); - f->hdrs = revSList(h); -} - -/* - * searchWhat : "CHARSET" ' ' astring searchkeys | searchkeys - * searchkeys : searchkey | searchkeys ' ' searchkey - * searchkey : "ALL" | "ANSWERED" | "DELETED" | "FLAGGED" | "NEW" | "OLD" | "RECENT" - * | "SEEN" | "UNANSWERED" | "UNDELETED" | "UNFLAGGED" | "DRAFT" | "UNDRAFT" - * | astrkey ' ' astring - * | datekey ' ' date - * | "KEYWORD" ' ' flag | "UNKEYWORD" flag - * | "LARGER" ' ' number | "SMALLER" ' ' number - * | "HEADER" astring ' ' astring - * | set | "UID" ' ' set - * | "NOT" ' ' searchkey - * | "OR" ' ' searchkey ' ' searchkey - * | '(' searchkeys ')' - * astrkey : "BCC" | "BODY" | "CC" | "FROM" | "SUBJECT" | "TEXT" | "TO" - * datekey : "BEFORE" | "ON" | "SINCE" | "SENTBEFORE" | "SENTON" | "SENTSINCE" - */ -static NamedInt searchMap[] = -{ - {"ALL", SKAll}, - {"ANSWERED", SKAnswered}, - {"DELETED", SKDeleted}, - {"FLAGGED", SKFlagged}, - {"NEW", SKNew}, - {"OLD", SKOld}, - {"RECENT", SKRecent}, - {"SEEN", SKSeen}, - {"UNANSWERED", SKUnanswered}, - {"UNDELETED", SKUndeleted}, - {"UNFLAGGED", SKUnflagged}, - {"DRAFT", SKDraft}, - {"UNDRAFT", SKUndraft}, - {"UNSEEN", SKUnseen}, - {nil, 0} -}; - -static NamedInt searchMapStr[] = -{ - {"CHARSET", SKCharset}, - {"BCC", SKBcc}, - {"BODY", SKBody}, - {"CC", SKCc}, - {"FROM", SKFrom}, - {"SUBJECT", SKSubject}, - {"TEXT", SKText}, - {"TO", SKTo}, - {nil, 0} -}; - -static NamedInt searchMapDate[] = -{ - {"BEFORE", SKBefore}, - {"ON", SKOn}, - {"SINCE", SKSince}, - {"SENTBEFORE", SKSentBefore}, - {"SENTON", SKSentOn}, - {"SENTSINCE", SKSentSince}, - {nil, 0} -}; - -static NamedInt searchMapFlag[] = -{ - {"KEYWORD", SKKeyword}, - {"UNKEYWORD", SKUnkeyword}, - {nil, 0} -}; - -static NamedInt searchMapNum[] = -{ - {"SMALLER", SKSmaller}, - {"LARGER", SKLarger}, - {nil, 0} -}; - -static Search* -searchKeys(int first, Search *tail) -{ - Search *s; - - for(;;){ - if(peekc() == '('){ - getc(); - tail = searchKeys(0, tail); - mustBe(')'); - }else{ - s = searchKey(first); - tail->next = s; - tail = s; - } - first = 0; - if(peekc() != ' ') - break; - getc(); - } - return tail; -} - -static Search* -searchKey(int first) -{ - Search *sr, rock; - Tm tm; - char *a; - int i, c; - - sr = binalloc(&parseBin, sizeof(Search), 1); - if(sr == nil) - parseErr("out of memory"); - - c = peekc(); - if(c >= '0' && c <= '9'){ - sr->key = SKSet; - sr->set = msgSet(0); - return sr; - } - - a = atom(); - if(i = mapInt(searchMap, a)) - sr->key = i; - else if(i = mapInt(searchMapStr, a)){ - if(!first && i == SKCharset) - parseErr("illegal search key"); - sr->key = i; - mustBe(' '); - sr->s = astring(); - }else if(i = mapInt(searchMapDate, a)){ - sr->key = i; - mustBe(' '); - c = peekc(); - if(c == '"') - getc(); - a = atom(); - if(!imap4Date(&tm, a)) - parseErr("bad date format"); - sr->year = tm.year; - sr->mon = tm.mon; - sr->mday = tm.mday; - if(c == '"') - mustBe('"'); - }else if(i = mapInt(searchMapFlag, a)){ - sr->key = i; - mustBe(' '); - c = peekc(); - if(c == '\\'){ - mustBe('\\'); - a = atomString(atomStop, "\\"); - }else - a = atom(); - i = mapFlag(a); - if(i == 0) - parseErr("flag not supported"); - sr->num = i; - }else if(i = mapInt(searchMapNum, a)){ - sr->key = i; - mustBe(' '); - sr->num = number(0); - }else if(cistrcmp(a, "HEADER") == 0){ - sr->key = SKHeader; - mustBe(' '); - sr->hdr = astring(); - mustBe(' '); - sr->s = astring(); - }else if(cistrcmp(a, "UID") == 0){ - sr->key = SKUid; - mustBe(' '); - sr->set = msgSet(0); - }else if(cistrcmp(a, "NOT") == 0){ - sr->key = SKNot; - mustBe(' '); - rock.next = nil; - searchKeys(0, &rock); - sr->left = rock.next; - }else if(cistrcmp(a, "OR") == 0){ - sr->key = SKOr; - mustBe(' '); - rock.next = nil; - searchKeys(0, &rock); - sr->left = rock.next; - mustBe(' '); - rock.next = nil; - searchKeys(0, &rock); - sr->right = rock.next; - }else - parseErr("illegal search key"); - return sr; -} - -/* - * set : seqno - * | seqno ':' seqno - * | set ',' set - * seqno: nz-number - * | '*' - * - */ -static MsgSet* -msgSet(int uids) -{ - MsgSet head, *last, *ms; - ulong from, to; - - last = &head; - head.next = nil; - for(;;){ - from = uids ? uidNo() : seqNo(); - to = from; - if(peekc() == ':'){ - getc(); - to = uids ? uidNo() : seqNo(); - } - ms = binalloc(&parseBin, sizeof(MsgSet), 0); - if(ms == nil) - parseErr("out of memory"); - ms->from = from; - ms->to = to; - ms->next = nil; - last->next = ms; - last = ms; - if(peekc() != ',') - break; - getc(); - } - return head.next; -} - -static ulong -seqNo(void) -{ - if(peekc() == '*'){ - getc(); - return ~0UL; - } - return number(1); -} - -static ulong -uidNo(void) -{ - if(peekc() == '*'){ - getc(); - return ~0UL; - } - return number(0); -} - -/* - * 7 bit, non-ctl chars, no (){%*"\ - * NIL is special case for nstring or parenlist - */ -static char * -atom(void) -{ - return atomString(atomStop, ""); -} - -/* - * like an atom, but no + - */ -static char * -tag(void) -{ - return atomString("+(){%*\"\\", ""); -} - -/* - * string or atom allowing %* - */ -static char * -listmbox(void) -{ - int c; - - c = peekc(); - if(c == '{') - return literal(); - if(c == '"') - return quoted(); - return atomString("(){\"\\", ""); -} - -/* - * string or atom - */ -static char * -astring(void) -{ - int c; - - c = peekc(); - if(c == '{') - return literal(); - if(c == '"') - return quoted(); - return atom(); -} - -/* - * 7 bit, non-ctl chars, none from exception list - */ -static char * -atomString(char *disallowed, char *initial) -{ - char *s; - int c, ns, as; - - ns = strlen(initial); - s = binalloc(&parseBin, ns + StrAlloc, 0); - if(s == nil) - parseErr("out of memory"); - strcpy(s, initial); - as = ns + StrAlloc; - for(;;){ - c = getc(); - if(c <= ' ' || c >= 0x7f || strchr(disallowed, c) != nil){ - ungetc(); - break; - } - s[ns++] = c; - if(ns >= as){ - s = bingrow(&parseBin, s, as, as + StrAlloc, 0); - if(s == nil) - parseErr("out of memory"); - as += StrAlloc; - } - } - if(ns == 0) - badsyn(); - s[ns] = '\0'; - return s; -} - -/* - * quoted: '"' chars* '"' - * chars: 1-128 except \r and \n - */ -static char * -quoted(void) -{ - char *s; - int c, ns, as; - - mustBe('"'); - s = binalloc(&parseBin, StrAlloc, 0); - if(s == nil) - parseErr("out of memory"); - as = StrAlloc; - ns = 0; - for(;;){ - c = getc(); - if(c == '"') - break; - if(c < 1 || c > 0x7f || c == '\r' || c == '\n') - badsyn(); - if(c == '\\'){ - c = getc(); - if(c != '\\' && c != '"') - badsyn(); - } - s[ns++] = c; - if(ns >= as){ - s = bingrow(&parseBin, s, as, as + StrAlloc, 0); - if(s == nil) - parseErr("out of memory"); - as += StrAlloc; - } - } - s[ns] = '\0'; - return s; -} - -/* - * litlen: {number}\r\n - */ -static ulong -litlen(void) -{ - ulong v; - - mustBe('{'); - v = number(0); - mustBe('}'); - crnl(); - return v; -} - -/* - * literal: litlen data<0:litlen> - */ -static char * -literal(void) -{ - char *s; - ulong v; - - v = litlen(); - s = binalloc(&parseBin, v+1, 0); - if(s == nil) - parseErr("out of memory"); - Bprint(&bout, "+ Ready for literal data\r\n"); - if(Bflush(&bout) < 0) - writeErr(); - if(v != 0 && Bread(&bin, s, v) != v) - badsyn(); - s[v] = '\0'; - return s; -} - -/* - * digits; number is 32 bits - */ -static ulong -number(int nonzero) -{ - ulong v; - int c, first; - - v = 0; - first = 1; - for(;;){ - c = getc(); - if(c < '0' || c > '9'){ - ungetc(); - if(first) - badsyn(); - break; - } - if(nonzero && first && c == '0') - badsyn(); - c -= '0'; - first = 0; - if(v > UlongMax/10 || v == UlongMax/10 && c > UlongMax%10) - parseErr("number out of range\r\n"); - v = v * 10 + c; - } - return v; -} - -static int -getc(void) -{ - return Bgetc(&bin); -} - -static void -ungetc(void) -{ - Bungetc(&bin); -} - -static int -peekc(void) -{ - int c; - - c = Bgetc(&bin); - Bungetc(&bin); - return c; -} - diff --git a/sys/src/cmd/ip/imap4d/imap4d.h b/sys/src/cmd/ip/imap4d/imap4d.h deleted file mode 100644 index bc2723fea..000000000 --- a/sys/src/cmd/ip/imap4d/imap4d.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - * mailbox and message representations - * - * these structures are allocated with emalloc and must be explicitly freed - */ -typedef struct Box Box; -typedef struct Header Header; -typedef struct MAddr MAddr; -typedef struct MbLock MbLock; -typedef struct MimeHdr MimeHdr; -typedef struct Msg Msg; -typedef struct NamedInt NamedInt; -typedef struct Pair Pair; - -enum -{ - StrAlloc = 32, /* characters allocated at a time */ - BufSize = 8*1024, /* size of transfer block */ - NDigest = 40, /* length of digest string */ - NUid = 10, /* length of .imp uid string */ - NFlags = 8, /* length of .imp flag string */ - LockSecs = 5 * 60, /* seconds to wait for acquiring a locked file */ - MboxNameLen = 256, /* max. length of upas/fs mbox name */ - MsgNameLen = 32, /* max. length of a file in a upas/fs mbox */ - UserNameLen = 64, /* max. length of user's name */ - - MUtf7Max = 6, /* max length for a modified utf7 character: &bbbb- */ - - /* - * message flags - */ - MSeen = 1 << 0, - MAnswered = 1 << 1, - MFlagged = 1 << 2, - MDeleted = 1 << 3, - MDraft = 1 << 4, - MRecent = 1 << 5, - - /* - * message bogus flags - */ - NotBogus = 0, /* the message is displayable */ - BogusHeader = 1, /* the header had bad characters */ - BogusBody = 2, /* the body had bad characters */ - BogusTried = 4, /* attempted to open the fake message */ -}; - -struct Box -{ - char *name; /* path name of mailbox */ - char *fs; /* fs name of mailbox */ - char *fsDir; /* /mail/fs/box->fs */ - char *imp; /* path name of .imp file */ - uchar writable; /* can write back messages? */ - uchar dirtyImp; /* .imp file needs to be written? */ - uchar sendFlags; /* need flags update */ - Qid qid; /* qid of fs mailbox */ - Qid impQid; /* qid of .imp when last synched */ - long mtime; /* file mtime when last read */ - ulong max; /* maximum msgs->seq, same as number of messages */ - ulong toldMax; /* last value sent to client */ - ulong recent; /* number of recently received messaged */ - ulong toldRecent; /* last value sent to client */ - ulong uidnext; /* next uid value assigned to a message */ - ulong uidvalidity; /* uid of mailbox */ - Msg *msgs; -}; - -/* - * fields of Msg->info - */ -enum -{ - /* - * read from upasfs - */ - IFrom, - ITo, - ICc, - IReplyTo, - IUnixDate, - ISubject, - IType, - IDisposition, - IFilename, - IDigest, - IBcc, - IInReplyTo, /* aka internal date */ - IDate, - ISender, - IMessageId, - ILines, /* number of lines of raw body */ - - IMax -}; - -struct Header -{ - char *buf; /* header, including terminating \r\n */ - ulong size; /* strlen(buf) */ - ulong lines; /* number of \n characters in buf */ - - /* - * pre-parsed mime headers - */ - MimeHdr *type; /* content-type */ - MimeHdr *id; /* content-id */ - MimeHdr *description; /* content-description */ - MimeHdr *encoding; /* content-transfer-encoding */ - MimeHdr *md5; /* content-md5 */ - MimeHdr *disposition; /* content-disposition */ - MimeHdr *language; /* content-language */ -}; - -struct Msg -{ - Msg *next; - Msg *prev; - Msg *kids; - Msg *parent; - char *fsDir; /* box->fsDir of enclosing message */ - Header head; /* message header */ - Header mime; /* mime header from enclosing multipart spec */ - int flags; - uchar sendFlags; /* flags value needs to be sent to client */ - uchar expunged; /* message actually expunged, but not yet reported to client */ - uchar matched; /* search succeeded? */ - uchar bogus; /* implies the message is invalid, ie contains nulls; see flags above */ - ulong uid; /* imap unique identifier */ - ulong seq; /* position in box; 1 is oldest */ - ulong id; /* number of message directory in upas/fs */ - char *fs; /* name of message directory */ - char *efs; /* pointer after / in fs; enough space for file name */ - - ulong size; /* size of fs/rawbody, in bytes, with \r added before \n */ - ulong lines; /* number of lines in rawbody */ - - char *iBuf; - char *info[IMax]; /* all info about message */ - - char *unixDate; - MAddr *unixFrom; - - MAddr *to; /* parsed out address lines */ - MAddr *from; - MAddr *replyTo; - MAddr *sender; - MAddr *cc; - MAddr *bcc; -}; - -/* - * pre-parsed header lines - */ -struct MAddr -{ - char *personal; - char *box; - char *host; - MAddr *next; -}; - -struct MimeHdr -{ - char *s; - char *t; - MimeHdr *next; -}; - -/* - * mapping of integer & names - */ -struct NamedInt -{ - char *name; - int v; -}; - -/* - * lock for all mail file operations - */ -struct MbLock -{ - int fd; -}; - -/* - * parse nodes for imap4rev1 protocol - * - * important: all of these items are allocated - * in one can, so they can be tossed out at the same time. - * this allows leakless parse error recovery by simply tossing the can away. - * however, it means these structures cannot be mixed with the mailbox structures - */ - -typedef struct Fetch Fetch; -typedef struct NList NList; -typedef struct SList SList; -typedef struct MsgSet MsgSet; -typedef struct Store Store; -typedef struct Search Search; - -/* - * parse tree for fetch command - */ -enum -{ - FEnvelope, - FFlags, - FInternalDate, - FRfc822, - FRfc822Head, - FRfc822Size, - FRfc822Text, - FBodyStruct, - FUid, - FBody, /* BODY */ - FBodySect, /* BODY [...] */ - FBodyPeek, - - FMax -}; - -enum -{ - FPAll, - FPHead, - FPHeadFields, - FPHeadFieldsNot, - FPMime, - FPText, - - FPMax -}; - -struct Fetch -{ - uchar op; /* F.* operator */ - uchar part; /* FP.* subpart for body[] & body.peek[]*/ - uchar partial; /* partial fetch? */ - long start; /* partial fetch amounts */ - long size; - NList *sect; - SList *hdrs; - Fetch *next; -}; - -/* - * status items - */ -enum{ - SMessages = 1 << 0, - SRecent = 1 << 1, - SUidNext = 1 << 2, - SUidValidity = 1 << 3, - SUnseen = 1 << 4, -}; - -/* - * parse tree for store command - */ -enum -{ - STFlags, - STFlagsSilent, - - STMax -}; - -struct Store -{ - uchar sign; - uchar op; - int flags; -}; - -/* - * parse tree for search command - */ -enum -{ - SKNone, - - SKCharset, - - SKAll, - SKAnswered, - SKBcc, - SKBefore, - SKBody, - SKCc, - SKDeleted, - SKDraft, - SKFlagged, - SKFrom, - SKHeader, - SKKeyword, - SKLarger, - SKNew, - SKNot, - SKOld, - SKOn, - SKOr, - SKRecent, - SKSeen, - SKSentBefore, - SKSentOn, - SKSentSince, - SKSet, - SKSince, - SKSmaller, - SKSubject, - SKText, - SKTo, - SKUid, - SKUnanswered, - SKUndeleted, - SKUndraft, - SKUnflagged, - SKUnkeyword, - SKUnseen, - - SKMax -}; - -struct Search -{ - int key; - char *s; - char *hdr; - ulong num; - int year; - int mon; - int mday; - MsgSet *set; - Search *left; - Search *right; - Search *next; -}; - -struct NList -{ - ulong n; - NList *next; -}; - -struct SList -{ - char *s; - SList *next; -}; - -struct MsgSet -{ - ulong from; - ulong to; - MsgSet *next; -}; - -struct Pair -{ - ulong start; - ulong stop; -}; - -#include "bin.h" - -extern Bin *parseBin; -extern Biobuf bout; -extern Biobuf bin; -extern char username[UserNameLen]; -extern char mboxDir[MboxNameLen]; -extern char *fetchPartNames[FPMax]; -extern char *site; -extern char *remote; -extern int debug; - -#include "fns.h" diff --git a/sys/src/cmd/ip/imap4d/list.c b/sys/src/cmd/ip/imap4d/list.c deleted file mode 100644 index 139e0f240..000000000 --- a/sys/src/cmd/ip/imap4d/list.c +++ /dev/null @@ -1,412 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -#define SUBSCRIBED "imap.subscribed" - -static int matches(char *ref, char *pat, char *name); -static int mayMatch(char *pat, char *name, int star); -static int checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir); -static int listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime); -static int listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm); -static int mkSubscribed(void); - -static long -listMtime(char *file) -{ - Dir *d; - long mtime; - - d = cdDirstat(mboxDir, file); - if(d == nil) - return 0; - mtime = d->mtime; - free(d); - return mtime; -} - -/* - * check for subscribed mailboxes - * each line is either a comment starting with # - * or is a subscribed mailbox name - */ -int -lsubBoxes(char *cmd, char *ref, char *pat) -{ - MbLock *mb; - Dir *d; - Biobuf bin; - char *s; - long mtime; - int fd, ok, isdir; - - mb = mbLock(); - if(mb == nil) - return 0; - fd = cdOpen(mboxDir, SUBSCRIBED, OREAD); - if(fd < 0) - fd = mkSubscribed(); - if(fd < 0){ - mbUnlock(mb); - return 0; - } - ok = 0; - Binit(&bin, fd, OREAD); - while(s = Brdline(&bin, '\n')){ - s[Blinelen(&bin) - 1] = '\0'; - if(s[0] == '#') - continue; - isdir = 1; - if(cistrcmp(s, "INBOX") == 0){ - if(access("msgs", AEXIST) == 0) - mtime = listMtime("msgs"); - else - mtime = listMtime("mbox"); - isdir = 0; - }else{ - d = cdDirstat(mboxDir, s); - if(d != nil){ - mtime = d->mtime; - if(!(d->mode & DMDIR)) - isdir = 0; - free(d); - }else - mtime = 0; - } - ok |= checkMatch(cmd, ref, pat, s, mtime, isdir); - } - Bterm(&bin); - close(fd); - mbUnlock(mb); - return ok; -} - -static int -mkSubscribed(void) -{ - int fd; - - fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664); - if(fd < 0) - return -1; - fprint(fd, "#imap4 subscription list\nINBOX\n"); - seek(fd, 0, 0); - return fd; -} - -/* - * either subscribe or unsubscribe to a mailbox - */ -int -subscribe(char *mbox, int how) -{ - MbLock *mb; - char *s, *in, *ein; - int fd, tfd, ok, nmbox; - - if(cistrcmp(mbox, "inbox") == 0) - mbox = "INBOX"; - mb = mbLock(); - if(mb == nil) - return 0; - fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR); - if(fd < 0) - fd = mkSubscribed(); - if(fd < 0){ - mbUnlock(mb); - return 0; - } - in = readFile(fd); - if(in == nil){ - mbUnlock(mb); - return 0; - } - nmbox = strlen(mbox); - s = strstr(in, mbox); - while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n')) - s = strstr(s+1, mbox); - ok = 0; - if(how == 's' && s == nil){ - if(fprint(fd, "%s\n", mbox) > 0) - ok = 1; - }else if(how == 'u' && s != nil){ - ein = strchr(s, '\0'); - memmove(s, &s[nmbox+1], ein - &s[nmbox+1]); - ein -= nmbox+1; - tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC); - if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in) - ok = 1; - if(tfd > 0) - close(tfd); - }else - ok = 1; - close(fd); - mbUnlock(mb); - return ok; -} - -/* - * stupidly complicated so that % doesn't read entire directory structure - * yet * works - * note: in most places, inbox is case-insensitive, - * but here INBOX is checked for a case-sensitve match. - */ -int -listBoxes(char *cmd, char *ref, char *pat) -{ - int ok; - - ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0); - return ok | listMatch(cmd, ref, pat, ref, pat); -} - -/* - * look for all messages which may match the pattern - * punt when a * is reached - */ -static int -listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm) -{ - Dir *dir, *dirs; - char *mdir, *m, *mb, *wc; - long mode; - int c, i, nmb, nmdir, nd, ok, fd; - - mdir = nil; - for(m = mm; c = *m; m++){ - if(c == '%' || c == '*'){ - if(mdir == nil){ - fd = cdOpen(mboxDir, ".", OREAD); - if(fd < 0) - return 0; - mbox = ""; - nmdir = 0; - }else{ - *mdir = '\0'; - fd = cdOpen(mboxDir, mbox, OREAD); - *mdir = '/'; - nmdir = mdir - mbox + 1; - if(fd < 0) - return 0; - dir = dirfstat(fd); - if(dir == nil){ - close(fd); - return 0; - } - mode = dir->mode; - free(dir); - if(!(mode & DMDIR)) - break; - } - wc = m; - for(; c = *m; m++) - if(c == '/') - break; - nmb = nmdir + strlen(m) + MboxNameLen + 3; - mb = emalloc(nmb); - strncpy(mb, mbox, nmdir); - ok = 0; - while((nd = dirread(fd, &dirs)) > 0){ - for(i = 0; i < nd; i++){ - if(strcmp(mbox, "") == 0 && - !okMbox(dirs[i].name)) - continue; - /* Safety: ignore message dirs */ - if(strstr(dirs[i].name, "mails") != 0 || - strcmp(dirs[i].name, "out") == 0 || - strcmp(dirs[i].name, "obox") == 0 || - strcmp(dirs[i].name, "ombox") == 0) - continue; - if(strcmp(dirs[i].name, "msgs") == 0) - dirs[i].mode &= ~DMDIR; - if(*wc == '*' && dirs[i].mode & DMDIR && - mayMatch(mm, dirs[i].name, 1)){ - snprint(mb+nmdir, nmb-nmdir, - "%s", dirs[i].name); - ok |= listAll(cmd, ref, pat, mb, - dirs[i].mtime); - }else if(mayMatch(mm, dirs[i].name, 0)){ - snprint(mb+nmdir, nmb-nmdir, - "%s%s", dirs[i].name, m); - if(*m == '\0') - ok |= checkMatch(cmd, - ref, pat, mb, - dirs[i].mtime, - dirs[i].mode & - DMDIR); - else if(dirs[i].mode & DMDIR) - ok |= listMatch(cmd, - ref, pat, mb, mb - + nmdir + strlen( - dirs[i].name)); - } - } - free(dirs); - } - close(fd); - free(mb); - return ok; - } - if(c == '/'){ - mdir = m; - mm = m + 1; - } - } - m = mbox; - if(*mbox == '\0') - m = "."; - dir = cdDirstat(mboxDir, m); - if(dir == nil) - return 0; - ok = checkMatch(cmd, ref, pat, mbox, dir->mtime, (dir->mode & DMDIR) == DMDIR); - free(dir); - return ok; -} - -/* - * too hard: recursively list all files rooted at mbox, - * and list checkMatch figure it out - */ -static int -listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime) -{ - Dir *dirs; - char *mb; - int i, nmb, nd, ok, fd; - - ok = checkMatch(cmd, ref, pat, mbox, mtime, 1); - fd = cdOpen(mboxDir, mbox, OREAD); - if(fd < 0) - return ok; - - nmb = strlen(mbox) + MboxNameLen + 2; - mb = emalloc(nmb); - while((nd = dirread(fd, &dirs)) > 0){ - for(i = 0; i < nd; i++){ - snprint(mb, nmb, "%s/%s", mbox, dirs[i].name); - /* safety: do not recurr */ - if(0 && dirs[i].mode & DMDIR) - ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime); - else - ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0); - } - free(dirs); - } - close(fd); - free(mb); - return ok; -} - -static int -mayMatch(char *pat, char *name, int star) -{ - Rune r; - int i, n; - - for(; *pat && *pat != '/'; pat += n){ - r = *(uchar*)pat; - if(r < Runeself) - n = 1; - else - n = chartorune(&r, pat); - - if(r == '*' || r == '%'){ - pat += n; - if(r == '*' && star || *pat == '\0' || *pat == '/') - return 1; - while(*name){ - if(mayMatch(pat, name, star)) - return 1; - name += chartorune(&r, name); - } - return 0; - } - for(i = 0; i < n; i++) - if(name[i] != pat[i]) - return 0; - name += n; - } - if(*name == '\0') - return 1; - return 0; -} - -/* - * mbox is a mailbox name which might match pat. - * verify the match - * generates response - */ -static int -checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir) -{ - char *s, *flags; - - if(!matches(ref, pat, mbox) || !okMbox(mbox)) - return 0; - if(strcmp(mbox, ".") == 0) - mbox = ""; - - if(isdir) - flags = "(\\Noselect)"; - else{ - s = impName(mbox); - if(s != nil && listMtime(s) < mtime) - flags = "(\\Noinferiors \\Marked)"; - else - flags = "(\\Noinferiors)"; - } - - s = strmutf7(mbox); - if(s != nil) - Bprint(&bout, "* %s %s \"/\" \"%s\"\r\n", cmd, flags, s); - return 1; -} - -static int -matches(char *ref, char *pat, char *name) -{ - Rune r; - int i, n; - - while(ref != pat) - if(*name++ != *ref++) - return 0; - for(; *pat; pat += n){ - r = *(uchar*)pat; - if(r < Runeself) - n = 1; - else - n = chartorune(&r, pat); - - if(r == '*'){ - pat += n; - if(*pat == '\0') - return 1; - while(*name){ - if(matches(pat, pat, name)) - return 1; - name += chartorune(&r, name); - } - return 0; - } - if(r == '%'){ - pat += n; - while(*name && *name != '/'){ - if(matches(pat, pat, name)) - return 1; - name += chartorune(&r, name); - } - pat -= n; - continue; - } - for(i = 0; i < n; i++) - if(name[i] != pat[i]) - return 0; - name += n; - } - if(*name == '\0') - return 1; - return 0; -} diff --git a/sys/src/cmd/ip/imap4d/mbox.c b/sys/src/cmd/ip/imap4d/mbox.c deleted file mode 100644 index 8eb57dfde..000000000 --- a/sys/src/cmd/ip/imap4d/mbox.c +++ /dev/null @@ -1,863 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -static NamedInt flagChars[NFlags] = -{ - {"s", MSeen}, - {"a", MAnswered}, - {"f", MFlagged}, - {"D", MDeleted}, - {"d", MDraft}, - {"r", MRecent}, -}; - -static int fsCtl = -1; - -static void boxFlags(Box *box); -static int createImp(Box *box, Qid *qid); -static void fsInit(void); -static void mboxGone(Box *box); -static MbLock *openImp(Box *box, int new); -static int parseImp(Biobuf *b, Box *box); -static int readBox(Box *box); -static ulong uidRenumber(Msg *m, ulong uid, int force); -static int impFlags(Box *box, Msg *m, char *flags); - -/* - * strategy: - * every mailbox file has an associated .imp file - * which maps upas/fs message digests to uids & message flags. - * - * the .imp files are locked by /mail/fs/usename/L.mbox. - * whenever the flags can be modified, the lock file - * should be opened, thereby locking the uid & flag state. - * for example, whenever new uids are assigned to messages, - * and whenever flags are changed internally, the lock file - * should be open and locked. this means the file must be - * opened during store command, and when changing the \seen - * flag for the fetch command. - * - * if no .imp file exists, a null one must be created before - * assigning uids. - * - * the .imp file has the following format - * imp : "imap internal mailbox description\n" - * uidvalidity " " uidnext "\n" - * messageLines - * - * messageLines : - * | messageLines digest " " uid " " flags "\n" - * - * uid, uidnext, and uidvalidity are 32 bit decimal numbers - * printed right justified in a field NUid characters long. - * the 0 uid implies that no uid has been assigned to the message, - * but the flags are valid. note that message lines are in mailbox - * order, except possibly for 0 uid messages. - * - * digest is an ascii hex string NDigest characters long. - * - * flags has a character for each of NFlag flag fields. - * if the flag is clear, it is represented by a "-". - * set flags are represented as a unique single ascii character. - * the currently assigned flags are, in order: - * MSeen s - * MAnswered a - * MFlagged f - * MDeleted D - * MDraft d - */ -Box* -openBox(char *name, char *fsname, int writable) -{ - Box *box; - MbLock *ml; - int n, new; - - if(cistrcmp(name, "inbox") == 0) - if(access("msgs", AEXIST) == 0) - name = "msgs"; - else - name = "mbox"; - fsInit(); - debuglog("imap4d open %s %s\n", name, fsname); - - if(fprint(fsCtl, "open '/mail/box/%s/%s' %s", username, name, fsname) < 0){ -//ZZZ - char err[ERRMAX]; - - rerrstr(err, sizeof err); - if(strstr(err, "file does not exist") == nil) - fprint(2, - "imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s", - time(nil), username, name, fsname, err, - ctime(time(nil))); /* NB: ctime result ends with \n */ - fprint(fsCtl, "close %s", fsname); - return nil; - } - - /* - * read box to find all messages - * each one has a directory, and is in numerical order - */ - box = MKZ(Box); - box->writable = writable; - - n = strlen(name) + 1; - box->name = emalloc(n); - strcpy(box->name, name); - - n += STRLEN(".imp"); - box->imp = emalloc(n); - snprint(box->imp, n, "%s.imp", name); - - n = strlen(fsname) + 1; - box->fs = emalloc(n); - strcpy(box->fs, fsname); - - n = STRLEN("/mail/fs/") + strlen(fsname) + 1; - box->fsDir = emalloc(n); - snprint(box->fsDir, n, "/mail/fs/%s", fsname); - - box->uidnext = 1; - new = readBox(box); - if(new >= 0){ - ml = openImp(box, new); - if(ml != nil){ - closeImp(box, ml); - return box; - } - } - closeBox(box, 0); - return nil; -} - -/* - * check mailbox - * returns fd of open .imp file if imped. - * otherwise, return value is insignificant - * - * careful: called by idle polling proc - */ -MbLock* -checkBox(Box *box, int imped) -{ - MbLock *ml; - Dir *d; - int new; - - if(box == nil) - return nil; - - /* - * if stat fails, mailbox must be gone - */ - d = cdDirstat(box->fsDir, "."); - if(d == nil){ - mboxGone(box); - return nil; - } - new = 0; - if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers - || box->mtime != d->mtime){ - new = readBox(box); - if(new < 0){ - free(d); - return nil; - } - } - free(d); - ml = openImp(box, new); - if(ml == nil) - box->writable = 0; - else if(!imped){ - closeImp(box, ml); - ml = nil; - } - return ml; -} - -/* - * mailbox is unreachable, so mark all messages expunged - * clean up .imp files as well. - */ -static void -mboxGone(Box *box) -{ - Msg *m; - - if(cdExists(mboxDir, box->name) < 0) - cdRemove(mboxDir, box->imp); - for(m = box->msgs; m != nil; m = m->next) - m->expunged = 1; - box->writable = 0; -} - -/* - * read messages in the mailbox - * mark message that no longer exist as expunged - * returns -1 for failure, 0 if no new messages, 1 if new messages. - */ -static int -readBox(Box *box) -{ - Msg *msgs, *m, *last; - Dir *d; - char *s; - long max, id; - int i, nd, fd, new; - - fd = cdOpen(box->fsDir, ".", OREAD); - if(fd < 0){ - syslog(0, "mail", - "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r", - time(nil), username, box->name, box->fsDir); - mboxGone(box); - return -1; - } - - /* - * read box to find all messages - * each one has a directory, and is in numerical order - */ - d = dirfstat(fd); - if(d == nil){ - close(fd); - return -1; - } - box->mtime = d->mtime; - box->qid = d->qid; - last = nil; - msgs = box->msgs; - max = 0; - new = 0; - free(d); - while((nd = dirread(fd, &d)) > 0){ - for(i = 0; i < nd; i++){ - s = d[i].name; - id = strtol(s, &s, 10); - if(id <= max || *s != '\0' - || (d[i].mode & DMDIR) != DMDIR) - continue; - - max = id; - - while(msgs != nil){ - last = msgs; - msgs = msgs->next; - if(last->id == id) - goto continueDir; - last->expunged = 1; - } - - new = 1; - m = MKZ(Msg); - m->id = id; - m->fsDir = box->fsDir; - m->fs = emalloc(2 * (MsgNameLen + 1)); - m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id); - m->size = ~0UL; - m->lines = ~0UL; - m->prev = last; - m->flags = MRecent; - if(!msgInfo(m)) - freeMsg(m); - else{ - if(last == nil) - box->msgs = m; - else - last->next = m; - last = m; - } - continueDir:; - } - free(d); - } - close(fd); - for(; msgs != nil; msgs = msgs->next) - msgs->expunged = 1; - - /* - * make up the imap message sequence numbers - */ - id = 1; - for(m = box->msgs; m != nil; m = m->next){ - if(m->seq && m->seq != id) - bye("internal error assigning message numbers"); - m->seq = id++; - } - box->max = id - 1; - - return new; -} - -/* - * read in the .imp file, or make one if it doesn't exist. - * make sure all flags and uids are consistent. - * return the mailbox lock. - */ -#define IMPMAGIC "imap internal mailbox description\n" -static MbLock* -openImp(Box *box, int new) -{ - Qid qid; - Biobuf b; - MbLock *ml; - int fd; -//ZZZZ - int once; - - ml = mbLock(); - if(ml == nil) - return nil; - fd = cdOpen(mboxDir, box->imp, OREAD); - once = 0; -ZZZhack: - if(fd < 0 || fqid(fd, &qid) < 0){ - if(fd < 0){ - char buf[ERRMAX]; - - errstr(buf, sizeof buf); - if(cistrstr(buf, "does not exist") == nil) - fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf); - if(!once && cistrstr(buf, "locked") != nil){ - once = 1; - fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp); - fd = openLocked(mboxDir, box->imp, OREAD); - goto ZZZhack; - } - } - if(fd >= 0) - close(fd); - fd = createImp(box, &qid); - if(fd < 0){ - mbUnlock(ml); - return nil; - } - box->dirtyImp = 1; - if(box->uidvalidity == 0) - box->uidvalidity = box->mtime; - box->impQid = qid; - new = 1; - }else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){ - Binit(&b, fd, OREAD); - if(!parseImp(&b, box)){ - box->dirtyImp = 1; - if(box->uidvalidity == 0) - box->uidvalidity = box->mtime; - } - Bterm(&b); - box->impQid = qid; - new = 1; - } - if(new) - boxFlags(box); - close(fd); - return ml; -} - -/* - * close the .imp file, after writing out any changes - */ -void -closeImp(Box *box, MbLock *ml) -{ - Msg *m; - Qid qid; - Biobuf b; - char buf[NFlags+1]; - int fd; - - if(ml == nil) - return; - if(!box->dirtyImp){ - mbUnlock(ml); - return; - } - - fd = cdCreate(mboxDir, box->imp, OWRITE, 0664); - if(fd < 0){ - mbUnlock(ml); - return; - } - Binit(&b, fd, OWRITE); - - box->dirtyImp = 0; - Bprint(&b, "%s", IMPMAGIC); - Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext); - for(m = box->msgs; m != nil; m = m->next){ - if(m->expunged) - continue; - wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0); - Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf); - } - Bterm(&b); - - if(fqid(fd, &qid) >= 0) - box->impQid = qid; - close(fd); - mbUnlock(ml); -} - -void -wrImpFlags(char *buf, int flags, int killRecent) -{ - int i; - - for(i = 0; i < NFlags; i++){ - if((flags & flagChars[i].v) - && (flagChars[i].v != MRecent || !killRecent)) - buf[i] = flagChars[i].name[0]; - else - buf[i] = '-'; - } - buf[i] = '\0'; -} - -int -emptyImp(char *mbox) -{ - Dir *d; - long mode; - int fd; - - fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664); - if(fd < 0) - return -1; - d = cdDirstat(mboxDir, mbox); - if(d == nil){ - close(fd); - return -1; - } - fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL); - mode = d->mode & 0777; - nulldir(d); - d->mode = mode; - dirfwstat(fd, d); - free(d); - return fd; -} - -/* - * try to match permissions with mbox - */ -static int -createImp(Box *box, Qid *qid) -{ - Dir *d; - long mode; - int fd; - - fd = cdCreate(mboxDir, box->imp, OREAD, 0664); - if(fd < 0) - return -1; - d = cdDirstat(mboxDir, box->name); - if(d != nil){ - mode = d->mode & 0777; - nulldir(d); - d->mode = mode; - dirfwstat(fd, d); - free(d); - } - if(fqid(fd, qid) < 0){ - close(fd); - return -1; - } - - return fd; -} - -/* - * read or re-read a .imp file. - * this is tricky: - * 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 - * there can be duplicate messages (same digests). - * - * look up existing messages based on uid. - * look up new messages based on in order digest matching. - * - * note: in the face of duplicate messages, one of which is deleted, - * two active servers may decide different ones are valid, and so return - * different uids for the messages. this situation will stablize when the servers exit. - */ -static int -parseImp(Biobuf *b, Box *box) -{ - Msg *m, *mm; - char *s, *t, *toks[3]; - ulong uid, u; - int match, n; - - m = box->msgs; - s = Brdline(b, '\n'); - if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC) - || strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0) - return 0; - - s = Brdline(b, '\n'); - if(s == nil || Blinelen(b) != 2*NUid + 2) - return 0; - s[2*NUid + 1] = '\0'; - u = strtoul(s, &t, 10); - if(u != box->uidvalidity && box->uidvalidity != 0) - return 0; - box->uidvalidity = u; - if(*t != ' ' || t != s + NUid) - return 0; - t++; - u = strtoul(t, &t, 10); - if(box->uidnext > u) - return 0; - box->uidnext = u; - if(t != s + 2*NUid+1 || box->uidnext == 0) - return 0; - - uid = ~0; - while(m != nil){ - s = Brdline(b, '\n'); - if(s == nil) - break; - n = Blinelen(b) - 1; - if(n != NDigest + NUid + NFlags + 2 - || s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ') - return 0; - toks[0] = s; - s[NDigest] = '\0'; - toks[1] = s + NDigest + 1; - s[NDigest + NUid + 1] = '\0'; - toks[2] = s + NDigest + NUid + 2; - s[n] = '\0'; - t = toks[1]; - u = strtoul(t, &t, 10); - if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid)) - return 0; - uid = u; - - /* - * zero uid => added by append or copy, only flags valid - * can only match messages without uids, but this message - * may not be the next one, and may have been deleted. - */ - if(!uid){ - for(; m != nil && m->uid; m = m->next) - ; - for(mm = m; mm != nil; mm = mm->next){ - if(mm->info[IDigest] != nil && - strcmp(mm->info[IDigest], toks[0]) == 0){ - if(!mm->uid) - mm->flags = 0; - if(!impFlags(box, mm, toks[2])) - return 0; - m = mm->next; - break; - } - } - continue; - } - - /* - * ignore expunged messages, - * and messages already assigned uids which don't match this uid. - * such messages must have been deleted by another imap server, - * which updated the mailbox and .imp file since we read the mailbox, - * or because upas/fs got confused by consecutive duplicate messages, - * the first of which was deleted by another imap server. - */ - for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next) - ; - if(m == nil) - break; - - /* - * only check for digest match on the next message, - * since it comes before all other messages, and therefore - * must be in the .imp file if they should be. - */ - match = m->info[IDigest] != nil && - strcmp(m->info[IDigest], toks[0]) == 0; - if(uid && (m->uid == uid || !m->uid && match)){ - if(!match) - bye("inconsistent uid"); - - /* - * wipe out recent flag if some other server saw this new message. - * it will be read from the .imp file if is really should be set, - * ie the message was only seen by a status command. - */ - if(!m->uid) - m->flags = 0; - - if(!impFlags(box, m, toks[2])) - return 0; - m->uid = uid; - m = m->next; - } - } - return 1; -} - -/* - * parse .imp flags - */ -static int -impFlags(Box *box, Msg *m, char *flags) -{ - int i, f; - - f = 0; - for(i = 0; i < NFlags; i++){ - if(flags[i] == '-') - continue; - if(flags[i] != flagChars[i].name[0]) - return 0; - f |= flagChars[i].v; - } - - /* - * 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 & MRecent) && strcmp(box->fs, "imap") == 0) - box->dirtyImp = 1; - f |= m->flags & MRecent; - - /* - * 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; -} - -/* - * assign uids to any new messages - * which aren't already in the .imp file. - * sum up totals for flag values. - */ -static void -boxFlags(Box *box) -{ - Msg *m; - - box->recent = 0; - for(m = box->msgs; m != nil; m = m->next){ - if(m->uid == 0){ - box->dirtyImp = 1; - box->uidnext = uidRenumber(m, box->uidnext, 0); - } - if(m->flags & MRecent) - box->recent++; - } -} - -static ulong -uidRenumber(Msg *m, ulong uid, int force) -{ - for(; m != nil; m = m->next){ - if(!force && m->uid != 0) - bye("uid renumbering with a valid uid"); - m->uid = uid++; - } - return uid; -} - -void -closeBox(Box *box, int opened) -{ - Msg *m, *next; - - /* - * make sure to leave the mailbox directory so upas/fs can close the mailbox - */ - myChdir(mboxDir); - - if(box->writable){ - deleteMsgs(box); - if(expungeMsgs(box, 0)) - closeImp(box, checkBox(box, 1)); - } - - if(fprint(fsCtl, "close %s", box->fs) < 0 && opened) - bye("can't talk to mail server"); - for(m = box->msgs; m != nil; m = next){ - next = m->next; - freeMsg(m); - } - free(box->name); - free(box->fs); - free(box->fsDir); - free(box->imp); - free(box); -} - -int -deleteMsgs(Box *box) -{ - Msg *m; - char buf[BufSize], *p, *start; - int ok; - - if(!box->writable) - return 0; - - /* - * first pass: delete messages; gang the writes together for speed. - */ - ok = 1; - start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs); - p = start; - for(m = box->msgs; m != nil; m = m->next){ - if((m->flags & MDeleted) && !m->expunged){ - m->expunged = 1; - p = seprint(p, buf + sizeof(buf), " %lud", m->id); - if(p + 32 >= buf + sizeof(buf)){ - if(write(fsCtl, buf, p - buf) < 0) - bye("can't talk to mail server"); - p = start; - } - } - } - if(p != start && write(fsCtl, buf, p - buf) < 0) - bye("can't talk to mail server"); - - return ok; -} - -/* - * second pass: remove the message structure, - * and renumber message sequence numbers. - * update messages counts in mailbox. - * returns true if anything changed. - */ -int -expungeMsgs(Box *box, int send) -{ - Msg *m, *next, *last; - ulong n; - - n = 0; - last = nil; - for(m = box->msgs; m != nil; m = next){ - m->seq -= n; - next = m->next; - if(m->expunged){ - if(send) - Bprint(&bout, "* %lud expunge\r\n", m->seq); - if(m->flags & MRecent) - box->recent--; - n++; - if(last == nil) - box->msgs = next; - else - last->next = next; - freeMsg(m); - }else - last = m; - } - if(n){ - box->max -= n; - box->dirtyImp = 1; - } - return n; -} - -static void -fsInit(void) -{ - if(fsCtl >= 0) - return; - fsCtl = open("/mail/fs/ctl", ORDWR); - if(fsCtl < 0) - bye("can't open mail file system"); - if(fprint(fsCtl, "close mbox") < 0) - bye("can't initialize mail file system"); -} - -static char *stoplist[] = -{ - "mbox", - "pipeto", - "forward", - "names", - "pipefrom", - "headers", - "imap.ok", - 0 -}; - -enum { - Maxokbytes = 4096, - Maxfolders = Maxokbytes / 4, -}; - -static char *folders[Maxfolders]; -static char *folderbuff; - -static void -readokfolders(void) -{ - int fd, nr; - - fd = open("imap.ok", OREAD); - if(fd < 0) - return; - folderbuff = malloc(Maxokbytes); - if(folderbuff == nil) { - close(fd); - return; - } - nr = read(fd, folderbuff, Maxokbytes-1); /* once is ok */ - close(fd); - if(nr < 0){ - free(folderbuff); - folderbuff = nil; - return; - } - folderbuff[nr] = 0; - tokenize(folderbuff, folders, nelem(folders)); -} - -/* - * reject bad mailboxes based on mailbox name - */ -int -okMbox(char *path) -{ - char *name; - int i; - - if(folderbuff == nil && access("imap.ok", AREAD) == 0) - readokfolders(); - name = strrchr(path, '/'); - if(name == nil) - name = path; - else - name++; - if(folderbuff != nil){ - for(i = 0; i < nelem(folders) && folders[i] != nil; i++) - if(cistrcmp(folders[i], name) == 0) - return 1; - return 0; - } - if(strlen(name) + STRLEN(".imp") >= MboxNameLen) - return 0; - for(i = 0; stoplist[i]; i++) - if(strcmp(name, stoplist[i]) == 0) - return 0; - if(isprefix("L.", name) || isprefix("imap-tmp.", name) - || issuffix(".imp", name) - || strcmp("imap.subscribed", name) == 0 - || isdotdot(name) || name[0] == '/') - return 0; - return 1; -} diff --git a/sys/src/cmd/ip/imap4d/mkfile b/sys/src/cmd/ip/imap4d/mkfile deleted file mode 100644 index afadea93e..000000000 --- a/sys/src/cmd/ip/imap4d/mkfile +++ /dev/null @@ -1,31 +0,0 @@ -</$objtype/mkfile - -OFILES=\ - auth.$O\ - copy.$O\ - csquery.$O\ - date.$O\ - fetch.$O\ - imap4d.$O\ - list.$O\ - mbox.$O\ - msg.$O\ - mutf7.$O\ - nodes.$O\ - folder.$O\ - search.$O\ - store.$O\ - utils.$O\ - debug.$O\ - -HFILES=imap4d.h\ - fns.h\ - -TARG=imap4d -BIN=/$objtype/bin/ip -UPDATE=\ - mkfile\ - $HFILES\ - ${OFILES:%.$O=%.c}\ - -</sys/src/cmd/mkone diff --git a/sys/src/cmd/ip/imap4d/msg.c b/sys/src/cmd/ip/imap4d/msg.c deleted file mode 100644 index 46ccea2e5..000000000 --- a/sys/src/cmd/ip/imap4d/msg.c +++ /dev/null @@ -1,1782 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <libsec.h> -#include <auth.h> -#include <fcall.h> -#include "imap4d.h" - -static void body64(int in, int out); -static void bodystrip(int in, int out); -static void cleanupHeader(Header *h); -static char *domBang(char *s); -static void freeMAddr(MAddr *a); -static void freeMimeHdr(MimeHdr *mh); -static char *headAddrSpec(char *e, char *w); -static MAddr *headAddresses(void); -static MAddr *headAddress(void); -static char *headAtom(char *disallowed); -static int headChar(int eat); -static char *headDomain(char *e); -static MAddr *headMAddr(MAddr *old); -static char *headPhrase(char *e, char *w); -static char *headQuoted(int start, int stop); -static char *headSkipWhite(int); -static void headSkip(void); -static char *headSubDomain(void); -static char *headText(void); -static void headToEnd(void); -static char *headWord(void); -static void mimeDescription(Header *h); -static void mimeDisposition(Header *h); -static void mimeEncoding(Header *h); -static void mimeId(Header *h); -static void mimeLanguage(Header *h); -static void mimeMd5(Header *h); -static MimeHdr *mimeParams(void); -static void mimeType(Header *h); -static MimeHdr *mkMimeHdr(char *s, char *t, MimeHdr *next); -static void msgAddDate(Msg *m); -static void msgAddHead(Msg *m, char *head, char *body); -static int msgBodySize(Msg *m); -static int msgHeader(Msg *m, Header *h, char *file); -static long msgReadFile(Msg *m, char *file, char **ss); -static int msgUnix(Msg *m, int top); -static void stripQuotes(char *q); -static MAddr *unixFrom(char *s); - - -static char bogusBody[] = - "This message contains null characters, so it cannot be displayed correctly.\r\n" - "Most likely you were sent a bogus message or a binary file.\r\n" - "\r\n" - "Each of the following attachments has a different version of the message.\r\n" - "The first is inlined with all non-printable characters stripped.\r\n" - "The second contains the message as it was stored in your mailbox.\r\n" - "The third has the initial header stripped.\r\n"; - -static char bogusMimeText[] = - "Content-Disposition: inline\r\n" - "Content-Type: text/plain; charset=\"US-ASCII\"\r\n" - "Content-Transfer-Encoding: 7bit\r\n"; - -static char bogusMimeBinary[] = - "Content-Disposition: attachment\r\n" - "Content-Type: application/octet-stream\r\n" - "Content-Transfer-Encoding: base64\r\n"; - -/* - * stop list for header fields - */ -static char *headFieldStop = ":"; -static char *mimeTokenStop = "()<>@,;:\\\"/[]?="; -static char *headAtomStop = "()<>@,;:\\\".[]"; -static uchar *headStr; -static uchar *lastWhite; - -long -selectFields(char *dst, long n, char *hdr, SList *fields, int matches) -{ - SList *f; - uchar *start; - char *s; - long m, nf; - - headStr = (uchar*)hdr; - m = 0; - for(;;){ - start = headStr; - s = headAtom(headFieldStop); - if(s == nil) - break; - headSkip(); - for(f = fields; f != nil; f = f->next){ - if(cistrcmp(s, f->s) == !matches){ - nf = headStr - start; - if(m + nf > n) - return 0; - memmove(&dst[m], start, nf); - m += nf; - } - } - free(s); - } - if(m + 3 > n) - return 0; - dst[m++] = '\r'; - dst[m++] = '\n'; - dst[m] = '\0'; - return m; -} - -void -freeMsg(Msg *m) -{ - Msg *k, *last; - - free(m->iBuf); - freeMAddr(m->to); - if(m->replyTo != m->from) - freeMAddr(m->replyTo); - if(m->sender != m->from) - freeMAddr(m->sender); - if(m->from != m->unixFrom) - freeMAddr(m->from); - freeMAddr(m->unixFrom); - freeMAddr(m->cc); - freeMAddr(m->bcc); - free(m->unixDate); - cleanupHeader(&m->head); - cleanupHeader(&m->mime); - for(k = m->kids; k != nil; ){ - last = k; - k = k->next; - freeMsg(last); - } - free(m->fs); - free(m); -} - -ulong -msgSize(Msg *m) -{ - return m->head.size + m->size; -} - -int -infoIsNil(char *s) -{ - return s == nil || s[0] == '\0'; -} - -char* -maddrStr(MAddr *a) -{ - char *host, *addr; - int n; - - host = a->host; - if(host == nil) - host = ""; - n = strlen(a->box) + strlen(host) + 2; - if(a->personal != nil) - n += strlen(a->personal) + 3; - addr = emalloc(n); - if(a->personal != nil) - snprint(addr, n, "%s <%s@%s>", a->personal, a->box, host); - else - snprint(addr, n, "%s@%s", a->box, host); - return addr; -} - -/* - * return actual name of f in m's fs directory - * this is special cased when opening m/rawbody, m/mimeheader, or m/rawheader, - * if the message was corrupted. in that case, - * a temporary file is made to hold the base64 encoding of m/raw. - */ -int -msgFile(Msg *m, char *f) -{ - Msg *parent, *p; - Dir d; - Tm tm; - char buf[64], nbuf[2]; - uchar dbuf[64]; - int i, n, fd, fd1, fd2; - - if(!m->bogus - || strcmp(f, "") != 0 && strcmp(f, "rawbody") != 0 - && strcmp(f, "rawheader") != 0 && strcmp(f, "mimeheader") != 0 - && strcmp(f, "info") != 0 && strcmp(f, "unixheader") != 0){ - if(strlen(f) > MsgNameLen) - bye("internal error: msgFile name too long"); - strcpy(m->efs, f); - return cdOpen(m->fsDir, m->fs, OREAD); - } - - /* - * walk up the stupid runt message parts for non-multipart messages - */ - parent = m->parent; - if(parent != nil && parent->parent != nil){ - m = parent; - parent = m->parent; - } - p = m; - if(parent != nil) - p = parent; - - if(strcmp(f, "info") == 0 || strcmp(f, "unixheader") == 0){ - strcpy(p->efs, f); - return cdOpen(p->fsDir, p->fs, OREAD); - } - - fd = imapTmp(); - if(fd < 0) - return -1; - - /* - * craft the message parts for bogus messages - */ - if(strcmp(f, "") == 0){ - /* - * make a fake directory for each kid - * all we care about is the name - */ - if(parent == nil){ - nulldir(&d); - d.mode = DMDIR|0600; - d.qid.type = QTDIR; - d.name = nbuf; - nbuf[1] = '\0'; - for(i = '1'; i <= '4'; i++){ - nbuf[0] = i; - n = convD2M(&d, dbuf, sizeof(dbuf)); - if(n <= BIT16SZ) - fprint(2, "bad convD2M %d\n", n); - write(fd, dbuf, n); - } - } - }else if(strcmp(f, "mimeheader") == 0){ - if(parent != nil){ - switch(m->id){ - case 1: - case 2: - fprint(fd, "%s", bogusMimeText); - break; - case 3: - case 4: - fprint(fd, "%s", bogusMimeBinary); - break; - } - } - }else if(strcmp(f, "rawheader") == 0){ - if(parent == nil){ - date2tm(&tm, m->unixDate); - rfc822date(buf, sizeof(buf), &tm); - fprint(fd, - "Date: %s\r\n" - "From: imap4 daemon <%s@%s>\r\n" - "To: <%s@%s>\r\n" - "Subject: This message was illegal or corrupted\r\n" - "MIME-Version: 1.0\r\n" - "Content-Type: multipart/mixed;\r\n\tboundary=\"upas-%s\"\r\n", - buf, username, site, username, site, m->info[IDigest]); - } - }else if(strcmp(f, "rawbody") == 0){ - fd1 = msgFile(p, "raw"); - strcpy(p->efs, "rawbody"); - fd2 = cdOpen(p->fsDir, p->fs, OREAD); - if(fd1 < 0 || fd2 < 0){ - close(fd); - close(fd1); - close(fd2); - return -1; - } - if(parent == nil){ - fprint(fd, - "This is a multi-part message in MIME format.\r\n" - "--upas-%s\r\n" - "%s" - "\r\n" - "%s" - "\r\n", - m->info[IDigest], bogusMimeText, bogusBody); - - fprint(fd, - "--upas-%s\r\n" - "%s" - "\r\n", - m->info[IDigest], bogusMimeText); - bodystrip(fd1, fd); - - fprint(fd, - "--upas-%s\r\n" - "%s" - "\r\n", - m->info[IDigest], bogusMimeBinary); - seek(fd1, 0, 0); - body64(fd1, fd); - - fprint(fd, - "--upas-%s\r\n" - "%s" - "\r\n", - m->info[IDigest], bogusMimeBinary); - body64(fd2, fd); - - fprint(fd, "--upas-%s--\r\n", m->info[IDigest]); - }else{ - switch(m->id){ - case 1: - fprint(fd, "%s", bogusBody); - break; - case 2: - bodystrip(fd1, fd); - break; - case 3: - body64(fd1, fd); - break; - case 4: - body64(fd2, fd); - break; - } - } - close(fd1); - close(fd2); - } - seek(fd, 0, 0); - return fd; -} - -int -msgIsMulti(Header *h) -{ - return h->type != nil && cistrcmp("multipart", h->type->s) == 0; -} - -int -msgIsRfc822(Header *h) -{ - return h->type != nil && cistrcmp("message", h->type->s) == 0 && cistrcmp("rfc822", h->type->t) == 0; -} - -/* - * check if a message has been deleted by someone else - */ -void -msgDead(Msg *m) -{ - if(m->expunged) - return; - *m->efs = '\0'; - if(!cdExists(m->fsDir, m->fs)) - m->expunged = 1; -} - -/* - * make sure the message has valid associated info - * used for ISubject, IDigest, IInReplyTo, IMessageId. - */ -int -msgInfo(Msg *m) -{ - char *s; - int i; - - if(m->info[0] != nil) - return 1; - - i = msgReadFile(m, "info", &m->iBuf); - if(i < 0) - return 0; - - s = m->iBuf; - for(i = 0; i < IMax; i++){ - m->info[i] = s; - s = strchr(s, '\n'); - if(s == nil) - break; - *s++ = '\0'; - } - for(; i < IMax; i++) - m->info[i] = nil; - - for(i = 0; i < IMax; i++) - if(infoIsNil(m->info[i])) - m->info[i] = nil; - - return 1; -} - -/* - * make sure the message has valid mime structure - * and sub-messages - */ -int -msgStruct(Msg *m, int top) -{ - Msg *k, head, *last; - Dir *d; - char *s; - ulong max, id; - int i, nd, fd, ns; - - if(m->kids != nil) - return 1; - - if(m->expunged - || !msgInfo(m) - || !msgUnix(m, top) - || !msgBodySize(m) - || !msgHeader(m, &m->mime, "mimeheader") - || (top || msgIsRfc822(&m->mime) || msgIsMulti(&m->mime)) && !msgHeader(m, &m->head, "rawheader")){ - if(top && m->bogus && !(m->bogus & BogusTried)){ - m->bogus |= BogusTried; - return msgStruct(m, top); - } - msgDead(m); - return 0; - } - - /* - * if a message has no kids, it has a kid which is just the body of the real message - */ - if(!msgIsMulti(&m->head) && !msgIsMulti(&m->mime) && !msgIsRfc822(&m->head) && !msgIsRfc822(&m->mime)){ - k = MKZ(Msg); - k->id = 1; - k->fsDir = m->fsDir; - k->bogus = m->bogus; - k->parent = m->parent; - ns = m->efs - m->fs; - k->fs = emalloc(ns + (MsgNameLen + 1)); - memmove(k->fs, m->fs, ns); - k->efs = k->fs + ns; - *k->efs = '\0'; - k->size = m->size; - m->kids = k; - return 1; - } - - /* - * read in all child messages messages - */ - fd = msgFile(m, ""); - if(fd < 0){ - msgDead(m); - return 0; - } - - max = 0; - head.next = nil; - last = &head; - while((nd = dirread(fd, &d)) > 0){ - for(i = 0; i < nd; i++){ - s = d[i].name; - id = strtol(s, &s, 10); - if(id <= max || *s != '\0' - || (d[i].mode & DMDIR) != DMDIR) - continue; - - max = id; - - k = MKZ(Msg); - k->id = id; - k->fsDir = m->fsDir; - k->bogus = m->bogus; - k->parent = m; - ns = strlen(m->fs); - k->fs = emalloc(ns + 2 * (MsgNameLen + 1)); - k->efs = seprint(k->fs, k->fs + ns + (MsgNameLen + 1), "%s%lud/", m->fs, id); - k->prev = last; - k->size = ~0UL; - k->lines = ~0UL; - last->next = k; - last = k; - } - } - close(fd); - m->kids = head.next; - - /* - * if kids fail, just whack them - */ - top = top && (msgIsRfc822(&m->head) || msgIsMulti(&m->head)); - for(k = m->kids; k != nil; k = k->next){ - if(!msgStruct(k, top)){ - for(k = m->kids; k != nil; ){ - last = k; - k = k->next; - freeMsg(last); - } - m->kids = nil; - break; - } - } - return 1; -} - -static long -msgReadFile(Msg *m, char *file, char **ss) -{ - Dir *d; - char *s, buf[BufSize]; - vlong length; - long n, nn; - int fd; - - fd = msgFile(m, file); - if(fd < 0){ - msgDead(m); - return -1; - } - - n = read(fd, buf, BufSize); - if(n < BufSize){ - close(fd); - if(n < 0){ - *ss = nil; - return -1; - } - s = emalloc(n + 1); - memmove(s, buf, n); - s[n] = '\0'; - *ss = s; - return n; - } - - d = dirfstat(fd); - if(d == nil){ - close(fd); - return -1; - } - length = d->length; - free(d); - nn = length; - s = emalloc(nn + 1); - memmove(s, buf, n); - if(nn > n) - nn = readn(fd, s+n, nn-n) + n; - close(fd); - if(nn != length){ - free(s); - return -1; - } - s[nn] = '\0'; - *ss = s; - return nn; -} - -static void -freeMAddr(MAddr *a) -{ - MAddr *p; - - while(a != nil){ - p = a; - a = a->next; - free(p->personal); - free(p->box); - free(p->host); - free(p); - } -} - -/* - * the message is corrupted or illegal. - * reset message fields. msgStruct will reparse the message, - * relying on msgFile to make up corrected body parts. - */ -static int -msgBogus(Msg *m, int flags) -{ - if(!(m->bogus & flags)) - m->bogus |= flags; - m->lines = ~0; - free(m->head.buf); - free(m->mime.buf); - memset(&m->head, 0, sizeof(Header)); - memset(&m->mime, 0, sizeof(Header)); - return 0; -} - -/* - * stolen from upas/marshal; base64 encodes from one fd to another. - * - * the size of buf is very important to enc64. Anything other than - * a multiple of 3 will cause enc64 to output a termination sequence. - * To ensure that a full buf corresponds to a multiple of complete lines, - * we make buf a multiple of 3*18 since that's how many enc64 sticks on - * a single line. This avoids short lines in the output which is pleasing - * but not necessary. - */ -static int -enc64x18(char *out, int lim, uchar *in, int n) -{ - int m, mm, nn; - - for(nn = 0; n > 0; n -= m, nn += mm){ - m = 18 * 3; - if(m > n) - m = n; - nn += 2; /* \r\n */ - assert(nn < lim); - mm = enc64(out, lim - nn, in, m); - assert(mm > 0); - in += m; - out += mm; - *out++ = '\r'; - *out++ = '\n'; - } - return nn; -} - -static void -body64(int in, int out) -{ - uchar buf[3*18*54]; - char obuf[3*18*54*2]; - int m, n; - - for(;;){ - n = readn(in, buf, sizeof(buf)); - if(n < 0) - return; - if(n == 0) - break; - m = enc64x18(obuf, sizeof(obuf), buf, n); - if(write(out, obuf, m) < 0) - return; - } -} - -/* - * strip all non-printable characters from a file - */ -static void -bodystrip(int in, int out) -{ - uchar buf[3*18*54]; - int m, n, i, c; - - for(;;){ - n = read(in, buf, sizeof(buf)); - if(n < 0) - return; - if(n == 0) - break; - m = 0; - for(i = 0; i < n; i++){ - c = buf[i]; - if(c > 0x1f && c < 0x7f /* normal characters */ - || c >= 0x9 && c <= 0xd) /* \t, \n, vertical tab, form feed, \r */ - buf[m++] = c; - } - - if(m && write(out, buf, m) < 0) - return; - } -} - -/* - * read in the message body to count \n without a preceding \r - */ -static int -msgBodySize(Msg *m) -{ - Dir *d; - char buf[BufSize + 2], *s, *se; - vlong length; - ulong size, lines, bad; - int n, fd, c; - - if(m->lines != ~0UL) - return 1; - fd = msgFile(m, "rawbody"); - if(fd < 0) - return 0; - d = dirfstat(fd); - if(d == nil){ - close(fd); - return 0; - } - length = d->length; - free(d); - - size = 0; - lines = 0; - bad = 0; - buf[0] = ' '; - for(;;){ - n = read(fd, &buf[1], BufSize); - if(n <= 0) - break; - size += n; - se = &buf[n + 1]; - for(s = &buf[1]; s < se; s++){ - c = *s; - if(c == '\0'){ - close(fd); - return msgBogus(m, BogusBody); - } - if(c != '\n') - continue; - if(s[-1] != '\r') - bad++; - lines++; - } - buf[0] = buf[n]; - } - if(size != length) - bye("bad length reading rawbody"); - size += bad; - m->size = size; - m->lines = lines; - close(fd); - return 1; -} - -/* - * retrieve information from the unixheader file - */ -static int -msgUnix(Msg *m, int top) -{ - Tm tm; - char *s, *ss; - - if(m->unixDate != nil) - return 1; - - if(!top){ -bogus: - m->unixDate = estrdup(""); - m->unixFrom = unixFrom(nil); - return 1; - } - - if(msgReadFile(m, "unixheader", &ss) < 0) - return 0; - s = ss; - s = strchr(s, ' '); - if(s == nil){ - free(ss); - goto bogus; - } - s++; - m->unixFrom = unixFrom(s); - s = (char*)headStr; - if(date2tm(&tm, s) == nil) - s = m->info[IUnixDate]; - if(s == nil){ - free(ss); - goto bogus; - } - m->unixDate = estrdup(s); - free(ss); - return 1; -} - -/* - * parse the address in the unix header - * last line of defence, so must return something - */ -static MAddr * -unixFrom(char *s) -{ - MAddr *a; - char *e, *t; - - if(s == nil) - return nil; - headStr = (uchar*)s; - t = emalloc(strlen(s) + 2); - e = headAddrSpec(t, nil); - if(e == nil) - a = nil; - else{ - if(*e != '\0') - *e++ = '\0'; - else - e = site; - a = MKZ(MAddr); - - a->box = estrdup(t); - a->host = estrdup(e); - } - free(t); - return a; -} - -/* - * read in the entire header, - * and parse out any existing mime headers - */ -static int -msgHeader(Msg *m, Header *h, char *file) -{ - char *s, *ss, *t, *te; - ulong lines, n, nn; - long ns; - int dated, c; - - if(h->buf != nil) - return 1; - - ns = msgReadFile(m, file, &ss); - if(ns < 0) - return 0; - s = ss; - n = ns; - - /* - * count lines ending with \n and \r\n - * add an extra line at the end, since upas/fs headers - * don't have a terminating \r\n - */ - lines = 1; - te = s + ns; - for(t = s; t < te; t++){ - c = *t; - if(c == '\0') - return msgBogus(m, BogusHeader); - if(c != '\n') - continue; - if(t == s || t[-1] != '\r') - n++; - lines++; - } - if(t > s && t[-1] != '\n'){ - if(t[-1] != '\r') - n++; - n++; - } - n += 2; - h->buf = emalloc(n + 1); - h->size = n; - h->lines = lines; - - /* - * make sure all headers end in \r\n - */ - nn = 0; - for(t = s; t < te; t++){ - c = *t; - if(c == '\n'){ - if(!nn || h->buf[nn - 1] != '\r') - h->buf[nn++] = '\r'; - lines++; - } - h->buf[nn++] = c; - } - if(nn && h->buf[nn-1] != '\n'){ - if(h->buf[nn-1] != '\r') - h->buf[nn++] = '\r'; - h->buf[nn++] = '\n'; - } - h->buf[nn++] = '\r'; - h->buf[nn++] = '\n'; - h->buf[nn] = '\0'; - if(nn != n) - bye("misconverted header %ld %ld", nn, n); - free(s); - - /* - * and parse some mime headers - */ - headStr = (uchar*)h->buf; - dated = 0; - while(s = headAtom(headFieldStop)){ - if(cistrcmp(s, "content-type") == 0) - mimeType(h); - else if(cistrcmp(s, "content-transfer-encoding") == 0) - mimeEncoding(h); - else if(cistrcmp(s, "content-id") == 0) - mimeId(h); - else if(cistrcmp(s, "content-description") == 0) - mimeDescription(h); - else if(cistrcmp(s, "content-disposition") == 0) - mimeDisposition(h); - else if(cistrcmp(s, "content-md5") == 0) - mimeMd5(h); - else if(cistrcmp(s, "content-language") == 0) - mimeLanguage(h); - else if(h == &m->head && cistrcmp(s, "from") == 0) - m->from = headMAddr(m->from); - else if(h == &m->head && cistrcmp(s, "to") == 0) - m->to = headMAddr(m->to); - else if(h == &m->head && cistrcmp(s, "reply-to") == 0) - m->replyTo = headMAddr(m->replyTo); - else if(h == &m->head && cistrcmp(s, "sender") == 0) - m->sender = headMAddr(m->sender); - else if(h == &m->head && cistrcmp(s, "cc") == 0) - m->cc = headMAddr(m->cc); - else if(h == &m->head && cistrcmp(s, "bcc") == 0) - m->bcc = headMAddr(m->bcc); - else if(h == &m->head && cistrcmp(s, "date") == 0) - dated = 1; - headSkip(); - free(s); - } - - if(h == &m->head){ - if(m->from == nil){ - m->from = m->unixFrom; - if(m->from != nil){ - s = maddrStr(m->from); - msgAddHead(m, "From", s); - free(s); - } - } - if(m->sender == nil) - m->sender = m->from; - if(m->replyTo == nil) - m->replyTo = m->from; - - if(infoIsNil(m->info[IDate])) - m->info[IDate] = m->unixDate; - if(!dated && m->from != nil) - msgAddDate(m); - } - return 1; -} - -/* - * prepend head: body to the cached header - */ -static void -msgAddHead(Msg *m, char *head, char *body) -{ - char *s; - long size, n; - - n = strlen(head) + strlen(body) + 4; - size = m->head.size + n; - s = emalloc(size + 1); - snprint(s, size + 1, "%s: %s\r\n%s", head, body, m->head.buf); - free(m->head.buf); - m->head.buf = s; - m->head.size = size; - m->head.lines++; -} - -static void -msgAddDate(Msg *m) -{ - Tm tm; - char buf[64]; - - /* don't bother if we don't have a date */ - if(infoIsNil(m->info[IDate])) - return; - - date2tm(&tm, m->info[IDate]); - rfc822date(buf, sizeof(buf), &tm); - msgAddHead(m, "Date", buf); -} - -static MimeHdr* -mkMimeHdr(char *s, char *t, MimeHdr *next) -{ - MimeHdr *mh; - - mh = MK(MimeHdr); - mh->s = s; - mh->t = t; - mh->next = next; - return mh; -} - -static void -freeMimeHdr(MimeHdr *mh) -{ - MimeHdr *last; - - while(mh != nil){ - last = mh; - mh = mh->next; - free(last->s); - free(last->t); - free(last); - } -} - -static void -cleanupHeader(Header *h) -{ - freeMimeHdr(h->type); - freeMimeHdr(h->id); - freeMimeHdr(h->description); - freeMimeHdr(h->encoding); - freeMimeHdr(h->md5); - freeMimeHdr(h->disposition); - freeMimeHdr(h->language); -} - -/* - * parser for rfc822 & mime header fields - */ - -/* - * type : 'content-type' ':' token '/' token params - */ -static void -mimeType(Header *h) -{ - char *s, *t; - - if(headChar(1) != ':') - return; - s = headAtom(mimeTokenStop); - if(s == nil || headChar(1) != '/'){ - free(s); - return; - } - t = headAtom(mimeTokenStop); - if(t == nil){ - free(s); - return; - } - h->type = mkMimeHdr(s, t, mimeParams()); -} - -/* - * params : - * | params ';' token '=' token - * | params ';' token '=' quoted-str - */ -static MimeHdr* -mimeParams(void) -{ - MimeHdr head, *last; - char *s, *t; - - head.next = nil; - last = &head; - for(;;){ - if(headChar(1) != ';') - break; - s = headAtom(mimeTokenStop); - if(s == nil || headChar(1) != '='){ - free(s); - break; - } - if(headChar(0) == '"'){ - t = headQuoted('"', '"'); - stripQuotes(t); - }else - t = headAtom(mimeTokenStop); - if(t == nil){ - free(s); - break; - } - last->next = mkMimeHdr(s, t, nil); - last = last->next; - } - return head.next; -} - -/* - * encoding : 'content-transfer-encoding' ':' token - */ -static void -mimeEncoding(Header *h) -{ - char *s; - - if(headChar(1) != ':') - return; - s = headAtom(mimeTokenStop); - if(s == nil) - return; - h->encoding = mkMimeHdr(s, nil, nil); -} - -/* - * mailaddr : ':' addresses - */ -static MAddr* -headMAddr(MAddr *old) -{ - MAddr *a; - - if(headChar(1) != ':') - return old; - - if(headChar(0) == '\n') - return old; - - a = headAddresses(); - if(a == nil) - return old; - - freeMAddr(old); - return a; -} - -/* - * addresses : address | addresses ',' address - */ -static MAddr* -headAddresses(void) -{ - MAddr *addr, *tail, *a; - - addr = headAddress(); - if(addr == nil) - return nil; - tail = addr; - while(headChar(0) == ','){ - headChar(1); - a = headAddress(); - if(a == nil){ - freeMAddr(addr); - return nil; - } - tail->next = a; - tail = a; - } - return addr; -} - -/* - * address : mailbox | group - * group : phrase ':' mboxes ';' | phrase ':' ';' - * mailbox : addr-spec - * | optphrase '<' addr-spec '>' - * | optphrase '<' route ':' addr-spec '>' - * optphrase : | phrase - * route : '@' domain - * | route ',' '@' domain - * personal names are the phrase before '<', - * or a comment before or after a simple addr-spec - */ -static MAddr* -headAddress(void) -{ - MAddr *addr; - uchar *hs; - char *s, *e, *w, *personal; - int c; - - s = emalloc(strlen((char*)headStr) + 2); - e = s; - personal = headSkipWhite(1); - c = headChar(0); - if(c == '<') - w = nil; - else{ - w = headWord(); - c = headChar(0); - } - if(c == '.' || c == '@' || c == ',' || c == '\n' || c == '\0'){ - lastWhite = headStr; - e = headAddrSpec(s, w); - if(personal == nil){ - hs = headStr; - headStr = lastWhite; - personal = headSkipWhite(1); - headStr = hs; - } - }else{ - if(c != '<' || w != nil){ - free(personal); - if(!headPhrase(e, w)){ - free(s); - return nil; - } - - /* - * ignore addresses with groups, - * so the only thing left if < - */ - c = headChar(1); - if(c != '<'){ - free(s); - return nil; - } - personal = estrdup(s); - }else - headChar(1); - - /* - * after this point, we need to free personal before returning. - * set e to nil to everything afterwards fails. - * - * ignore routes, they are useless, and heavily discouraged in rfc1123. - * imap4 reports them up to, but not including, the terminating : - */ - e = s; - c = headChar(0); - if(c == '@'){ - for(;;){ - c = headChar(1); - if(c != '@'){ - e = nil; - break; - } - headDomain(e); - c = headChar(1); - if(c != ','){ - e = s; - break; - } - } - if(c != ':') - e = nil; - } - - if(e != nil) - e = headAddrSpec(s, nil); - if(headChar(1) != '>') - e = nil; - } - - /* - * e points to @host, or nil if an error occured - */ - if(e == nil){ - free(personal); - addr = nil; - }else{ - if(*e != '\0') - *e++ = '\0'; - else - e = site; - addr = MKZ(MAddr); - - addr->personal = personal; - addr->box = estrdup(s); - addr->host = estrdup(e); - } - free(s); - return addr; -} - -/* - * phrase : word - * | phrase word - * w is the optional initial word of the phrase - * returns the end of the phrase, or nil if a failure occured - */ -static char* -headPhrase(char *e, char *w) -{ - int c; - - for(;;){ - if(w == nil){ - w = headWord(); - if(w == nil) - return nil; - } - if(w[0] == '"') - stripQuotes(w); - strcpy(e, w); - free(w); - w = nil; - e = strchr(e, '\0'); - c = headChar(0); - if(c <= ' ' || strchr(headAtomStop, c) != nil && c != '"') - break; - *e++ = ' '; - *e = '\0'; - } - return e; -} - -/* - * addr-spec : local-part '@' domain - * | local-part extension to allow ! and local names - * local-part : word - * | local-part '.' word - * - * if no '@' is present, rewrite d!e!f!u as @d,@e:u@f, - * where d, e, f are valid domain components. - * the @d,@e: is ignored, since routes are ignored. - * perhaps they should be rewritten as e!f!u@d, but that is inconsistent with upas. - * - * returns a pointer to '@', the end if none, or nil if there was an error - */ -static char* -headAddrSpec(char *e, char *w) -{ - char *s, *at, *b, *bang, *dom; - int c; - - s = e; - for(;;){ - if(w == nil){ - w = headWord(); - if(w == nil) - return nil; - } - strcpy(e, w); - free(w); - w = nil; - e = strchr(e, '\0'); - lastWhite = headStr; - c = headChar(0); - if(c != '.') - break; - headChar(1); - *e++ = '.'; - *e = '\0'; - } - - if(c != '@'){ - /* - * extenstion: allow name without domain - * check for domain!xxx - */ - bang = domBang(s); - if(bang == nil) - return e; - - /* - * if dom1!dom2!xxx, ignore dom1! - */ - dom = s; - for(; b = domBang(bang + 1); bang = b) - dom = bang + 1; - - /* - * convert dom!mbox into mbox@dom - */ - *bang = '@'; - strrev(dom, bang); - strrev(bang+1, e); - strrev(dom, e); - bang = &dom[e - bang - 1]; - if(dom > s){ - bang -= dom - s; - for(e = s; *e = *dom; e++) - dom++; - } - - /* - * eliminate a trailing '.' - */ - if(e[-1] == '.') - e[-1] = '\0'; - return bang; - } - headChar(1); - - at = e; - *e++ = '@'; - *e = '\0'; - if(!headDomain(e)) - return nil; - return at; -} - -/* - * find the ! in domain!rest, where domain must have at least - * one internal '.' - */ -static char* -domBang(char *s) -{ - int dot, c; - - dot = 0; - for(; c = *s; s++){ - if(c == '!'){ - if(!dot || dot == 1 && s[-1] == '.' || s[1] == '\0') - return nil; - return s; - } - if(c == '"') - break; - if(c == '.') - dot++; - } - return nil; -} - -/* - * domain : sub-domain - * | domain '.' sub-domain - * returns the end of the domain, or nil if a failure occured - */ -static char* -headDomain(char *e) -{ - char *w; - - for(;;){ - w = headSubDomain(); - if(w == nil) - return nil; - strcpy(e, w); - free(w); - e = strchr(e, '\0'); - lastWhite = headStr; - if(headChar(0) != '.') - break; - headChar(1); - *e++ = '.'; - *e = '\0'; - } - return e; -} - -/* - * id : 'content-id' ':' msg-id - * msg-id : '<' addr-spec '>' - */ -static void -mimeId(Header *h) -{ - char *s, *e, *w; - - if(headChar(1) != ':') - return; - if(headChar(1) != '<') - return; - - s = emalloc(strlen((char*)headStr) + 3); - e = s; - *e++ = '<'; - e = headAddrSpec(e, nil); - if(e == nil || headChar(1) != '>'){ - free(s); - return; - } - e = strchr(e, '\0'); - *e++ = '>'; - e[0] = '\0'; - w = strdup(s); - free(s); - h->id = mkMimeHdr(w, nil, nil); -} - -/* - * description : 'content-description' ':' *text - */ -static void -mimeDescription(Header *h) -{ - if(headChar(1) != ':') - return; - headSkipWhite(0); - h->description = mkMimeHdr(headText(), nil, nil); -} - -/* - * disposition : 'content-disposition' ':' token params - */ -static void -mimeDisposition(Header *h) -{ - char *s; - - if(headChar(1) != ':') - return; - s = headAtom(mimeTokenStop); - if(s == nil) - return; - h->disposition = mkMimeHdr(s, nil, mimeParams()); -} - -/* - * md5 : 'content-md5' ':' token - */ -static void -mimeMd5(Header *h) -{ - char *s; - - if(headChar(1) != ':') - return; - s = headAtom(mimeTokenStop); - if(s == nil) - return; - h->md5 = mkMimeHdr(s, nil, nil); -} - -/* - * language : 'content-language' ':' langs - * langs : token - * | langs commas token - * commas : ',' - * | commas ',' - */ -static void -mimeLanguage(Header *h) -{ - MimeHdr head, *last; - char *s; - - head.next = nil; - last = &head; - for(;;){ - s = headAtom(mimeTokenStop); - if(s == nil) - break; - last->next = mkMimeHdr(s, nil, nil); - last = last->next; - while(headChar(0) != ',') - headChar(1); - } - h->language = head.next; -} - -/* - * token : 1*<char 33-255, except "()<>@,;:\\\"/[]?=" aka mimeTokenStop> - * atom : 1*<chars 33-255, except "()<>@,;:\\\".[]" aka headAtomStop> - * note this allows 8 bit characters, which occur in utf. - */ -static char* -headAtom(char *disallowed) -{ - char *s; - int c, ns, as; - - headSkipWhite(0); - - s = emalloc(StrAlloc); - as = StrAlloc; - ns = 0; - for(;;){ - c = *headStr++; - if(c <= ' ' || strchr(disallowed, c) != nil){ - headStr--; - break; - } - s[ns++] = c; - if(ns >= as){ - as += StrAlloc; - s = erealloc(s, as); - } - } - if(ns == 0){ - free(s); - return 0; - } - s[ns] = '\0'; - return s; -} - -/* - * sub-domain : atom | domain-lit - */ -static char * -headSubDomain(void) -{ - if(headChar(0) == '[') - return headQuoted('[', ']'); - return headAtom(headAtomStop); -} - -/* - * word : atom | quoted-str - */ -static char * -headWord(void) -{ - if(headChar(0) == '"') - return headQuoted('"', '"'); - return headAtom(headAtomStop); -} - -/* - * q is a quoted string. remove enclosing " and and \ escapes - */ -static void -stripQuotes(char *q) -{ - char *s; - int c; - - if(q == nil) - return; - s = q++; - while(c = *q++){ - if(c == '\\'){ - c = *q++; - if(!c) - return; - } - *s++ = c; - } - s[-1] = '\0'; -} - -/* - * quoted-str : '"' *(any char but '"\\\r', or '\' any char, or linear-white-space) '"' - * domain-lit : '[' *(any char but '[]\\\r', or '\' any char, or linear-white-space) ']' - */ -static char * -headQuoted(int start, int stop) -{ - char *s; - int c, ns, as; - - if(headChar(1) != start) - return nil; - s = emalloc(StrAlloc); - as = StrAlloc; - ns = 0; - s[ns++] = start; - for(;;){ - c = *headStr; - if(c == stop){ - headStr++; - break; - } - if(c == '\0'){ - free(s); - return nil; - } - if(c == '\r'){ - headStr++; - continue; - } - if(c == '\n'){ - headStr++; - while(*headStr == ' ' || *headStr == '\t' || *headStr == '\r' || *headStr == '\n') - headStr++; - c = ' '; - }else if(c == '\\'){ - headStr++; - s[ns++] = c; - c = *headStr; - if(c == '\0'){ - free(s); - return nil; - } - headStr++; - }else - headStr++; - s[ns++] = c; - if(ns + 1 >= as){ /* leave room for \c or "0 */ - as += StrAlloc; - s = erealloc(s, as); - } - } - s[ns++] = stop; - s[ns] = '\0'; - return s; -} - -/* - * headText : contents of rest of header line - */ -static char * -headText(void) -{ - uchar *v; - char *s; - - v = headStr; - headToEnd(); - s = emalloc(headStr - v + 1); - memmove(s, v, headStr - v); - s[headStr - v] = '\0'; - return s; -} - -/* - * white space is ' ' '\t' or nested comments. - * skip white space. - * if com and a comment is seen, - * return it's contents and stop processing white space. - */ -static char* -headSkipWhite(int com) -{ - char *s; - int c, incom, as, ns; - - s = nil; - as = StrAlloc; - ns = 0; - if(com) - s = emalloc(StrAlloc); - incom = 0; - for(; c = *headStr; headStr++){ - switch(c){ - case ' ': - case '\t': - case '\r': - c = ' '; - break; - case '\n': - c = headStr[1]; - if(c != ' ' && c != '\t') - goto breakout; - c = ' '; - break; - case '\\': - if(com && incom) - s[ns++] = c; - c = headStr[1]; - if(c == '\0') - goto breakout; - headStr++; - break; - case '(': - incom++; - if(incom == 1) - continue; - break; - case ')': - incom--; - if(com && !incom){ - s[ns] = '\0'; - return s; - } - break; - default: - if(!incom) - goto breakout; - break; - } - if(com && incom && (c != ' ' || ns > 0 && s[ns-1] != ' ')){ - s[ns++] = c; - if(ns + 1 >= as){ /* leave room for \c or 0 */ - as += StrAlloc; - s = erealloc(s, as); - } - } - } -breakout:; - free(s); - return nil; -} - -/* - * return the next non-white character - */ -static int -headChar(int eat) -{ - int c; - - headSkipWhite(0); - c = *headStr; - if(eat && c != '\0' && c != '\n') - headStr++; - return c; -} - -static void -headToEnd(void) -{ - uchar *s; - int c; - - for(;;){ - s = headStr; - c = *s++; - while(c == '\r') - c = *s++; - if(c == '\n'){ - c = *s++; - if(c != ' ' && c != '\t') - return; - } - if(c == '\0') - return; - headStr = s; - } -} - -static void -headSkip(void) -{ - int c; - - while(c = *headStr){ - headStr++; - if(c == '\n'){ - c = *headStr; - if(c == ' ' || c == '\t') - continue; - return; - } - } -} diff --git a/sys/src/cmd/ip/imap4d/mutf7.c b/sys/src/cmd/ip/imap4d/mutf7.c deleted file mode 100644 index 6d84b4efc..000000000 --- a/sys/src/cmd/ip/imap4d/mutf7.c +++ /dev/null @@ -1,174 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -/* - * modified utf-7, as per imap4 spec - * like utf-7, but substitues , for / in base 64, - * does not allow escaped ascii characters. - * - * /lib/rfc/rfc2152 is utf-7 - * /lib/rfc/rfc1642 is obsolete utf-7 - * - * test sequences from rfc1642 - * 'A≢Α.' 'A&ImIDkQ-.' - * 'Hi Mom ☺!" 'Hi Mom &Jjo-!' - * '日本語' '&ZeVnLIqe-' - */ - -static uchar mt64d[256]; -static char mt64e[64]; - -static void -initm64(void) -{ - int c, i; - - memset(mt64d, 255, 256); - memset(mt64e, '=', 64); - i = 0; - for(c = 'A'; c <= 'Z'; c++){ - mt64e[i] = c; - mt64d[c] = i++; - } - for(c = 'a'; c <= 'z'; c++){ - mt64e[i] = c; - mt64d[c] = i++; - } - for(c = '0'; c <= '9'; c++){ - mt64e[i] = c; - mt64d[c] = i++; - } - mt64e[i] = '+'; - mt64d['+'] = i++; - mt64e[i] = ','; - mt64d[','] = i; -} - -int -encmutf7(char *out, int lim, char *in) -{ - Rune rr; - ulong r, b; - char *start = out; - char *e = out + lim; - int nb; - - if(mt64e[0] == 0) - initm64(); - for(;;){ - r = *(uchar*)in; - - if(r < ' ' || r >= Runeself){ - if(r == '\0') - break; - if(out + 1 >= e) - return -1; - *out++ = '&'; - b = 0; - nb = 0; - for(;;){ - in += chartorune(&rr, in); - r = rr; - if(r == '\0' || r >= ' ' && r < Runeself) - break; - b = (b << 16) | r; - for(nb += 16; nb >= 6; nb -= 6){ - if(out + 1 >= e) - return -1; - *out++ = mt64e[(b>>(nb-6))&0x3f]; - } - } - for(; nb >= 6; nb -= 6){ - if(out + 1 >= e) - return -1; - *out++ = mt64e[(b>>(nb-6))&0x3f]; - } - if(nb){ - if(out + 1 >= e) - return -1; - *out++ = mt64e[(b<<(6-nb))&0x3f]; - } - - if(out + 1 >= e) - return -1; - *out++ = '-'; - if(r == '\0') - break; - }else - in++; - if(out + 1 >= e) - return -1; - *out = r; - out++; - if(r == '&') - *out++ = '-'; - } - - if(out >= e) - return -1; - *out = '\0'; - return out - start; -} - -int -decmutf7(char *out, int lim, char *in) -{ - Rune rr; - char *start = out; - char *e = out + lim; - int c, b, nb; - - if(mt64e[0] == 0) - initm64(); - for(;;){ - c = *in; - - if(c < ' ' || c >= Runeself){ - if(c == '\0') - break; - return -1; - } - if(c != '&'){ - if(out + 1 >= e) - return -1; - *out++ = c; - in++; - continue; - } - in++; - if(*in == '-'){ - if(out + 1 >= e) - return -1; - *out++ = '&'; - in++; - continue; - } - - b = 0; - nb = 0; - while((c = *in++) != '-'){ - c = mt64d[c]; - if(c >= 64) - return -1; - b = (b << 6) | c; - nb += 6; - if(nb >= 16){ - rr = b >> (nb - 16); - nb -= 16; - if(out + UTFmax + 1 >= e && out + runelen(rr) + 1 >= e) - return -1; - out += runetochar(out, &rr); - } - } - if(b & ((1 << nb) - 1)) - return -1; - } - - if(out >= e) - return -1; - *out = '\0'; - return out - start; -} diff --git a/sys/src/cmd/ip/imap4d/nodes.c b/sys/src/cmd/ip/imap4d/nodes.c deleted file mode 100644 index 9fe848e88..000000000 --- a/sys/src/cmd/ip/imap4d/nodes.c +++ /dev/null @@ -1,213 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -/* - * iterated over all of the items in the message set. - * errors are accumulated, but processing continues. - * if uids, then ignore non-existent messages. - * otherwise, that's an error - */ -int -forMsgs(Box *box, MsgSet *ms, ulong max, int uids, int (*f)(Box*, Msg*, int, void*), void *rock) -{ - Msg *m; - ulong id; - int ok, rok; - - ok = 1; - for(; ms != nil; ms = ms->next){ - id = ms->from; - rok = 0; - for(m = box->msgs; m != nil && m->seq <= max; m = m->next){ - if(!uids && m->seq > id - || uids && m->uid > ms->to) - break; - if(!uids && m->seq == id - || uids && m->uid >= id){ - if(!(*f)(box, m, uids, rock)) - ok = 0; - if(uids) - id = m->uid; - if(id >= ms->to){ - rok = 1; - break; - } - if(ms->to == ~0UL) - rok = 1; - id++; - } - } - if(!uids && !rok) - ok = 0; - } - return ok; -} - -Store * -mkStore(int sign, int op, int flags) -{ - Store *st; - - st = binalloc(&parseBin, sizeof(Store), 1); - if(st == nil) - parseErr("out of memory"); - st->sign = sign; - st->op = op; - st->flags = flags; - return st; -} - -Fetch * -mkFetch(int op, Fetch *next) -{ - Fetch *f; - - f = binalloc(&parseBin, sizeof(Fetch), 1); - if(f == nil) - parseErr("out of memory"); - f->op = op; - f->next = next; - return f; -} - -Fetch* -revFetch(Fetch *f) -{ - Fetch *last, *next; - - last = nil; - for(; f != nil; f = next){ - next = f->next; - f->next = last; - last = f; - } - return last; -} - -NList* -mkNList(ulong n, NList *next) -{ - NList *nl; - - nl = binalloc(&parseBin, sizeof(NList), 0); - if(nl == nil) - parseErr("out of memory"); - nl->n = n; - nl->next = next; - return nl; -} - -NList* -revNList(NList *nl) -{ - NList *last, *next; - - last = nil; - for(; nl != nil; nl = next){ - next = nl->next; - nl->next = last; - last = nl; - } - return last; -} - -SList* -mkSList(char *s, SList *next) -{ - SList *sl; - - sl = binalloc(&parseBin, sizeof(SList), 0); - if(sl == nil) - parseErr("out of memory"); - sl->s = s; - sl->next = next; - return sl; -} - -SList* -revSList(SList *sl) -{ - SList *last, *next; - - last = nil; - for(; sl != nil; sl = next){ - next = sl->next; - sl->next = last; - last = sl; - } - return last; -} - -int -BNList(Biobuf *b, NList *nl, char *sep) -{ - char *s; - int n; - - s = ""; - n = 0; - for(; nl != nil; nl = nl->next){ - n += Bprint(b, "%s%lud", s, nl->n); - s = sep; - } - return n; -} - -int -BSList(Biobuf *b, SList *sl, char *sep) -{ - char *s; - int n; - - s = ""; - n = 0; - for(; sl != nil; sl = sl->next){ - n += Bprint(b, "%s", s); - n += Bimapstr(b, sl->s); - s = sep; - } - return n; -} - -int -Bimapdate(Biobuf *b, Tm *tm) -{ - char buf[64]; - - if(tm == nil) - tm = localtime(time(nil)); - imap4date(buf, sizeof(buf), tm); - return Bimapstr(b, buf); -} - -int -Brfc822date(Biobuf *b, Tm *tm) -{ - char buf[64]; - - if(tm == nil) - tm = localtime(time(nil)); - rfc822date(buf, sizeof(buf), tm); - return Bimapstr(b, buf); -} - -int -Bimapstr(Biobuf *b, char *s) -{ - char *t; - int c; - - if(s == nil) - return Bprint(b, "NIL"); - for(t = s; ; t++){ - c = *t; - if(c == '\0') - return Bprint(b, "\"%s\"", s); - if(t - s > 64 || c >= 0x7f || strchr("\"\\\r\n", c) != nil) - break; - } - return Bprint(b, "{%lud}\r\n%s", strlen(s), s); -} diff --git a/sys/src/cmd/ip/imap4d/search.c b/sys/src/cmd/ip/imap4d/search.c deleted file mode 100644 index 12f462b91..000000000 --- a/sys/src/cmd/ip/imap4d/search.c +++ /dev/null @@ -1,244 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -static int dateCmp(char *date, Search *s); -static int addrSearch(MAddr *a, char *s); -static int fileSearch(Msg *m, char *file, char *pat); -static int headerSearch(Msg *m, char *hdr, char *pat); - -/* - * free to exit, parseErr, since called before starting any client reply - * - * the header and envelope searches should convert mime character set escapes. - */ -int -searchMsg(Msg *m, Search *s) -{ - MsgSet *ms; - int ok; - - if(!msgStruct(m, 1) || m->expunged) - return 0; - for(ok = 1; ok && s != nil; s = s->next){ - switch(s->key){ - default: - ok = 0; - break; - case SKNot: - ok = !searchMsg(m, s->left); - break; - case SKOr: - ok = searchMsg(m, s->left) || searchMsg(m, s->right); - break; - case SKAll: - ok = 1; - break; - case SKAnswered: - ok = (m->flags & MAnswered) == MAnswered; - break; - case SKDeleted: - ok = (m->flags & MDeleted) == MDeleted; - break; - case SKDraft: - ok = (m->flags & MDraft) == MDraft; - break; - case SKFlagged: - ok = (m->flags & MFlagged) == MFlagged; - break; - case SKKeyword: - ok = (m->flags & s->num) == s->num; - break; - case SKNew: - ok = (m->flags & (MRecent|MSeen)) == MRecent; - break; - case SKOld: - ok = (m->flags & MRecent) != MRecent; - break; - case SKRecent: - ok = (m->flags & MRecent) == MRecent; - break; - case SKSeen: - ok = (m->flags & MSeen) == MSeen; - break; - case SKUnanswered: - ok = (m->flags & MAnswered) != MAnswered; - break; - case SKUndeleted: - ok = (m->flags & MDeleted) != MDeleted; - break; - case SKUndraft: - ok = (m->flags & MDraft) != MDraft; - break; - case SKUnflagged: - ok = (m->flags & MFlagged) != MFlagged; - break; - case SKUnkeyword: - ok = (m->flags & s->num) != s->num; - break; - case SKUnseen: - ok = (m->flags & MSeen) != MSeen; - break; - - case SKLarger: - ok = msgSize(m) > s->num; - break; - case SKSmaller: - ok = msgSize(m) < s->num; - break; - - case SKBcc: - ok = addrSearch(m->bcc, s->s); - break; - case SKCc: - ok = addrSearch(m->cc, s->s); - break; - case SKFrom: - ok = addrSearch(m->from, s->s); - break; - case SKTo: - ok = addrSearch(m->to, s->s); - break; - case SKSubject: - ok = 0; - if(m->info[ISubject]) - ok = cistrstr(m->info[ISubject], s->s) != nil; - break; - - case SKBefore: - ok = dateCmp(m->unixDate, s) < 0; - break; - case SKOn: - ok = dateCmp(m->unixDate, s) == 0; - break; - case SKSince: - ok = dateCmp(m->unixDate, s) > 0; - break; - case SKSentBefore: - ok = dateCmp(m->info[IDate], s) < 0; - break; - case SKSentOn: - ok = dateCmp(m->info[IDate], s) == 0; - break; - case SKSentSince: - ok = dateCmp(m->info[IDate], s) > 0; - break; - - case SKUid: - case SKSet: - for(ms = s->set; ms != nil; ms = ms->next) - if(s->key == SKUid && m->uid >= ms->from && m->uid <= ms->to - || s->key == SKSet && m->seq >= ms->from && m->seq <= ms->to) - break; - ok = ms != nil; - break; - - case SKHeader: - ok = headerSearch(m, s->hdr, s->s); - break; - - case SKBody: - case SKText: - if(s->key == SKText && cistrstr(m->head.buf, s->s)){ - ok = 1; - break; - } - ok = fileSearch(m, "body", s->s); - break; - } - } - return ok; -} - -static int -fileSearch(Msg *m, char *file, char *pat) -{ - char buf[BufSize + 1]; - int n, nbuf, npat, fd, ok; - - npat = strlen(pat); - if(npat >= BufSize / 2) - return 0; - - fd = msgFile(m, file); - if(fd < 0) - return 0; - ok = 0; - nbuf = 0; - for(;;){ - n = read(fd, &buf[nbuf], BufSize - nbuf); - if(n <= 0) - break; - nbuf += n; - buf[nbuf] = '\0'; - if(cistrstr(buf, pat) != nil){ - ok = 1; - break; - } - if(nbuf > npat){ - memmove(buf, &buf[nbuf - npat], npat); - nbuf = npat; - } - } - close(fd); - return ok; -} - -static int -headerSearch(Msg *m, char *hdr, char *pat) -{ - SList hdrs; - char *s, *t; - int ok, n; - - n = m->head.size + 3; - s = emalloc(n); - hdrs.next = nil; - hdrs.s = hdr; - ok = 0; - if(selectFields(s, n, m->head.buf, &hdrs, 1) > 0){ - t = strchr(s, ':'); - if(t != nil && cistrstr(t+1, pat) != nil) - ok = 1; - } - free(s); - return ok; -} - -static int -addrSearch(MAddr *a, char *s) -{ - char *ok, *addr; - - for(; a != nil; a = a->next){ - addr = maddrStr(a); - ok = cistrstr(addr, s); - free(addr); - if(ok != nil) - return 1; - } - return 0; -} - -static int -dateCmp(char *date, Search *s) -{ - Tm tm; - - date2tm(&tm, date); - if(tm.year < s->year) - return -1; - if(tm.year > s->year) - return 1; - if(tm.mon < s->mon) - return -1; - if(tm.mon > s->mon) - return 1; - if(tm.mday < s->mday) - return -1; - if(tm.mday > s->mday) - return 1; - return 0; -} diff --git a/sys/src/cmd/ip/imap4d/store.c b/sys/src/cmd/ip/imap4d/store.c deleted file mode 100644 index b13c3bb72..000000000 --- a/sys/src/cmd/ip/imap4d/store.c +++ /dev/null @@ -1,127 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -static NamedInt flagMap[] = -{ - {"\\Seen", MSeen}, - {"\\Answered", MAnswered}, - {"\\Flagged", MFlagged}, - {"\\Deleted", MDeleted}, - {"\\Draft", MDraft}, - {"\\Recent", MRecent}, - {nil, 0} -}; - -int -storeMsg(Box *box, Msg *m, int uids, void *vst) -{ - Store *st; - int f, flags; - - USED(uids); - - if(m->expunged) - return uids; - - st = vst; - flags = st->flags; - - f = m->flags; - if(st->sign == '+') - f |= flags; - else if(st->sign == '-') - f &= ~flags; - else - f = flags; - - /* - * not allowed to change the recent flag - */ - f = (f & ~MRecent) | (m->flags & MRecent); - setFlags(box, m, f); - - if(st->op != STFlagsSilent){ - m->sendFlags = 1; - box->sendFlags = 1; - } - - return 1; -} - -/* - * update flags & global flag counts in box - */ -void -setFlags(Box *box, Msg *m, int f) -{ - if(f == m->flags) - return; - - box->dirtyImp = 1; - if((f & MRecent) != (m->flags & MRecent)){ - if(f & MRecent) - box->recent++; - else - box->recent--; - } - m->flags = f; -} - -void -sendFlags(Box *box, int uids) -{ - Msg *m; - - if(!box->sendFlags) - return; - - box->sendFlags = 0; - for(m = box->msgs; m != nil; m = m->next){ - if(!m->expunged && m->sendFlags){ - Bprint(&bout, "* %lud FETCH (", m->seq); - if(uids) - Bprint(&bout, "uid %lud ", m->uid); - Bprint(&bout, "FLAGS ("); - writeFlags(&bout, m, 1); - Bprint(&bout, "))\r\n"); - m->sendFlags = 0; - } - } -} - -void -writeFlags(Biobuf *b, Msg *m, int recentOk) -{ - char *sep; - int f; - - sep = ""; - for(f = 0; flagMap[f].name != nil; f++){ - if((m->flags & flagMap[f].v) - && (flagMap[f].v != MRecent || recentOk)){ - Bprint(b, "%s%s", sep, flagMap[f].name); - sep = " "; - } - } -} - -int -msgSeen(Box *box, Msg *m) -{ - if(m->flags & MSeen) - return 0; - m->flags |= MSeen; - box->sendFlags = 1; - m->sendFlags = 1; - box->dirtyImp = 1; - return 1; -} - -ulong -mapFlag(char *name) -{ - return mapInt(flagMap, name); -} diff --git a/sys/src/cmd/ip/imap4d/utils.c b/sys/src/cmd/ip/imap4d/utils.c deleted file mode 100644 index 6e081a62e..000000000 --- a/sys/src/cmd/ip/imap4d/utils.c +++ /dev/null @@ -1,182 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include <auth.h> -#include "imap4d.h" - -/* - * reverse string [s:e) in place - */ -void -strrev(char *s, char *e) -{ - int c; - - while(--e > s){ - c = *s; - *s++ = *e; - *e = c; - } -} - -int -isdotdot(char *s) -{ - return s[0] == '.' && s[1] == '.' && (s[2] == '/' || s[2] == '\0'); -} - -int -issuffix(char *suf, char *s) -{ - int n; - - n = strlen(s) - strlen(suf); - if(n < 0) - return 0; - return strcmp(s + n, suf) == 0; -} - -int -isprefix(char *pre, char *s) -{ - return strncmp(pre, s, strlen(pre)) == 0; -} - -int -ciisprefix(char *pre, char *s) -{ - return cistrncmp(pre, s, strlen(pre)) == 0; -} - -char* -readFile(int fd) -{ - Dir *d; - long length; - char *s; - - d = dirfstat(fd); - if(d == nil) - return nil; - length = d->length; - free(d); - s = binalloc(&parseBin, length + 1, 0); - if(s == nil || read(fd, s, length) != length) - return nil; - s[length] = '\0'; - return s; -} - -/* - * create the imap tmp file. - * it just happens that we don't need multiple temporary files. - */ -int -imapTmp(void) -{ - char buf[ERRMAX], name[MboxNameLen]; - int tries, fd; - - snprint(name, sizeof(name), "/mail/box/%s/mbox.tmp.imp", username); - for(tries = 0; tries < LockSecs*2; tries++){ - fd = create(name, ORDWR|ORCLOSE|OCEXEC, DMEXCL|0600); - if(fd >= 0) - return fd; - errstr(buf, sizeof buf); - if(cistrstr(buf, "locked") == nil) - break; - sleep(500); - } - return -1; -} - -/* - * open a file which might be locked. - * if it is, spin until available - */ -int -openLocked(char *dir, char *file, int mode) -{ - char buf[ERRMAX]; - int tries, fd; - - for(tries = 0; tries < LockSecs*2; tries++){ - fd = cdOpen(dir, file, mode); - if(fd >= 0) - return fd; - errstr(buf, sizeof buf); - if(cistrstr(buf, "locked") == nil) - break; - sleep(500); - } - return -1; -} - -int -fqid(int fd, Qid *qid) -{ - Dir *d; - - d = dirfstat(fd); - if(d == nil) - return -1; - *qid = d->qid; - free(d); - return 0; -} - -ulong -mapInt(NamedInt *map, char *name) -{ - int i; - - for(i = 0; map[i].name != nil; i++) - if(cistrcmp(map[i].name, name) == 0) - break; - return map[i].v; -} - -char* -estrdup(char *s) -{ - char *t; - - t = emalloc(strlen(s) + 1); - strcpy(t, s); - return t; -} - -void* -emalloc(ulong n) -{ - void *p; - - p = malloc(n); - if(p == nil) - bye("server out of memory"); - setmalloctag(p, getcallerpc(&n)); - return p; -} - -void* -ezmalloc(ulong n) -{ - void *p; - - p = malloc(n); - if(p == nil) - bye("server out of memory"); - setmalloctag(p, getcallerpc(&n)); - memset(p, 0, n); - return p; -} - -void* -erealloc(void *p, ulong n) -{ - p = realloc(p, n); - if(p == nil) - bye("server out of memory"); - setrealloctag(p, getcallerpc(&p)); - return p; -} diff --git a/sys/src/cmd/ip/mkfile b/sys/src/cmd/ip/mkfile index 71c4c6fbc..900bb17f7 100644 --- a/sys/src/cmd/ip/mkfile +++ b/sys/src/cmd/ip/mkfile @@ -27,7 +27,7 @@ TARG = 6in4\ socksd\ wol\ -DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp imap4d snoopy +DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy BIN=/$objtype/bin/ip HFILES=dhcp.h arp.h glob.h icmp.h telnet.h |