summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
commit963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc (patch)
tree749b74875dbc49bcf6ed0776648b8f0ef9417407 /sys/src/cmd/ip
parent8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff)
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/ip')
-rw-r--r--sys/src/cmd/ip/imap4d/auth.c184
-rw-r--r--sys/src/cmd/ip/imap4d/copy.c259
-rw-r--r--sys/src/cmd/ip/imap4d/csquery.c44
-rw-r--r--sys/src/cmd/ip/imap4d/date.c308
-rw-r--r--sys/src/cmd/ip/imap4d/debug.c108
-rw-r--r--sys/src/cmd/ip/imap4d/fetch.c625
-rw-r--r--sys/src/cmd/ip/imap4d/fns.h125
-rw-r--r--sys/src/cmd/ip/imap4d/folder.c398
-rw-r--r--sys/src/cmd/ip/imap4d/imap4d.c2102
-rw-r--r--sys/src/cmd/ip/imap4d/imap4d.h378
-rw-r--r--sys/src/cmd/ip/imap4d/list.c412
-rw-r--r--sys/src/cmd/ip/imap4d/mbox.c863
-rw-r--r--sys/src/cmd/ip/imap4d/mkfile31
-rw-r--r--sys/src/cmd/ip/imap4d/msg.c1782
-rw-r--r--sys/src/cmd/ip/imap4d/mutf7.c174
-rw-r--r--sys/src/cmd/ip/imap4d/nodes.c213
-rw-r--r--sys/src/cmd/ip/imap4d/search.c244
-rw-r--r--sys/src/cmd/ip/imap4d/store.c127
-rw-r--r--sys/src/cmd/ip/imap4d/utils.c182
-rw-r--r--sys/src/cmd/ip/mkfile2
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