ref: 4d69aacea023546a7150d92e147e531c38de822f
dir: /sys/src/cmd/upas/imap4d/list.c/
#include "imap4d.h" enum{ Mfolder = 0, Mbox, Mdir, }; char subscribed[] = "imap.subscribed"; static int ldebug; #define dprint(...) if(ldebug)fprint(2, __VA_ARGS__); else {} static int lmatch(char*, char*, char*); static int mopen(char *box, int mode) { char buf[Pathlen]; if(!strcmp(box, "..") || strstr(box, "/..")) return -1; return cdopen(mboxdir, encfs(buf, sizeof buf, box), mode); } static Dir* mdirstat(char *box) { char buf[Pathlen]; return cddirstat(mboxdir, encfs(buf, sizeof buf, box)); } static long mtime(char *box) { long mtime; Dir *d; mtime = 0; if(d = mdirstat(box)) mtime = d->mtime; free(d); return mtime; } static int mokmbox(char *s) { char *p; if(p = strrchr(s, '/')) s = p + 1; if(!strcmp(s, "mbox")) return 1; return okmbox(s); } /* * paranoid check to prevent accidents */ /* * BOTCH: we're taking it upon ourselves to * identify mailboxes. this is a bad idea. * keep in sync with ../fs/mdir.c */ static int dirskip(Dir *a, uvlong *uv) { char *p; if(a->length == 0) return 1; *uv = strtoul(a->name, &p, 0); if(*uv < 1000000 || *p != '.') return 1; *uv = *uv<<8 | strtoul(p+1, &p, 10); if(*p) return 1; return 0; } static int chkmbox(char *path, int mode) { char buf[32]; int i, r, n, fd, type; uvlong uv; Dir *d; type = Mbox; if(mode & DMDIR) type = Mdir; fd = mopen(path, OREAD); if(fd == -1) return -1; r = -1; if(type == Mdir && (n = dirread(fd, &d)) > 0){ r = Mfolder; for(i = 0; i < n; i++) if(!dirskip(d + i, &uv)){ r = Mdir; break; } free(d); }else if(type == Mdir) r = Mdir; else if(type == Mbox){ if(pread(fd, buf, sizeof buf, 0) == sizeof buf) if(!strncmp(buf, "From ", 5)) r = Mbox; } close(fd); return r; } static int chkmboxpath(char *f) { int r; Dir *d; r = -1; if(d = mdirstat(f)) r = chkmbox(f, d->mode); free(d); return r; } static char* appendwd(char *nwd, int n, char *wd, char *a) { if(wd[0] && a[0] != '/') snprint(nwd, n, "%s/%s", wd, a); else snprint(nwd, n, "%s", a); return nwd; } static int output(char *cmd, char *wd, Dir *d, int term) { char path[Pathlen], dec[Pathlen], *s, *flags; appendwd(path, sizeof path, wd, d->name); dprint("Xoutput %s %s %d\n", wd, d->name, term); switch(chkmbox(path, d->mode)){ default: return 0; case Mfolder: flags = "(\\Noselect)"; break; case Mdir: case Mbox: s = impname(path); if(s != nil && mtime(s) < d->mtime) flags = "(\\Noinferiors \\Marked)"; else flags = "(\\Noinferiors)"; break; } if(!term) return 1; if(s = strmutf7(decfs(dec, sizeof dec, path))) Bprint(&bout, "* %s %s \"/\" %#Z\r\n", cmd, flags, s); return 1; } static int rematch(char *cmd, char *wd, char *pat, Dir *d) { char nwd[Pathlen]; appendwd(nwd, sizeof nwd, wd, d->name); if(d->mode & DMDIR) if(chkmbox(nwd, d->mode) == Mfolder) if(lmatch(cmd, pat, nwd)) return 1; return 0; } static int match(char *cmd, char *wd, char *pat, Dir *d, int i) { char *p, *p1; int m, n; Rune r, r1; m = 0; for(p = pat; ; p = p1){ n = chartorune(&r, p); p1 = p + n; dprint("r = %C [%.2ux]\n", r, r); switch(r){ case '*': case '%': for(r1 = 1; r1;){ if(match(cmd, wd, p1, d, i)) if(output(cmd, wd, d, 0)){ m++; break; } i += chartorune(&r1, d->name + i); } if(r == '*' && rematch(cmd, wd, p, d)) return 1; if(m > 0) return 1; break; case '/': return rematch(cmd, wd, p1, d); default: chartorune(&r1, d->name + i); if(r1 != r) return 0; if(r == 0) return output(cmd, wd, d, 1); dprint(" r %C ~ %C [%.2ux]\n", r, r1, r1); i += n; break; } } } static int lmatch(char *cmd, char *pat, char *wd) { char dec[Pathlen]; int fd, n, m, i; Dir *d; if((fd = mopen(wd[0]? wd: ".", OREAD)) == -1) return -1; if(wd[0]) dprint("wd %s\n", wd); m = 0; for(;;){ n = dirread(fd, &d); if(n <= 0) break; for(i = 0; i < n; i++) if(mokmbox(d[i].name)){ d[i].name = decfs(dec, sizeof dec, d[i].name); m += match(cmd, wd, pat, d + i, 0); } free(d); } close(fd); return m; } int listboxes(char *cmd, char *ref, char *pat) { char buf[Pathlen]; pat = appendwd(buf, sizeof buf, ref, pat); return lmatch(cmd, pat, "") > 0; } static int opensubscribed(void) { int fd; fd = cdopen(mboxdir, subscribed, ORDWR); if(fd >= 0) return 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; } /* * resistance to hand-edits */ static char* trim(char *s, int l) { int c; for(;; l--){ if(l == 0) return 0; c = s[l - 1]; if(c != '\t' && c != ' ') break; } for(s[l] = 0; c = *s; s++) if(c != '\t' && c != ' ') break; if(c == 0 || c == '#') return 0; return s; } static int poutput(char *cmd, char *f, int term) { char *p, *wd; int r; Dir *d; if(!mokmbox(f) || !(d = mdirstat(f))) return 0; wd = ""; if(p = strrchr(f, '/')){ *p = 0; wd = f; } r = output(cmd, wd, d, term); if(p) *p = '/'; free(d); return r; } static int pmatch(char *cmd, char *pat, char *f, int i) { char *p, *p1; int m, n; Rune r, r1; dprint("pmatch pat[%s] f[%s]\n", pat, f + i); m = 0; for(p = pat; ; p = p1){ n = chartorune(&r, p); p1 = p + n; switch(r){ case '*': case '%': for(r1 = 1; r1;){ if(pmatch(cmd, p1, f, i)) if(poutput(cmd, f, 0)){ m++; break; } i += chartorune(&r1, f + i); if(r == '%' && r1 == '/') break; } if(m > 0) return 1; break; default: chartorune(&r1, f + i); if(r1 != r) return 0; if(r == 0) return poutput(cmd, f, 1); i += n; break; } } } int lsubboxes(char *cmd, char *ref, char *pat) { char *s, buf[Pathlen]; int r, fd; Biobuf b; Mblock *l; pat = appendwd(buf, sizeof buf, ref, pat); if((l = mblock()) == nil) return 0; fd = opensubscribed(); r = 0; Binit(&b, fd, OREAD); while(s = Brdline(&b, '\n')) if(s = trim(s, Blinelen(&b) - 1)) r += pmatch(cmd, pat, s, 0); Bterm(&b); close(fd); mbunlock(l); return r; } int subscribe(char *mbox, int how) { char *s, *in, *ein; int fd, tfd, ok, l; Mblock *mb; if(cistrcmp(mbox, "inbox") == 0) mbox = "INBOX"; if((mb = mblock()) == nil) return 0; fd = opensubscribed(); if(fd < 0 || (in = readfile(fd)) == nil){ close(fd); mbunlock(mb); return 0; } l = strlen(mbox); s = strstr(in, mbox); while(s != nil && (s != in && s[-1] != '\n' || s[l] != '\n')) s = strstr(s + 1, mbox); ok = 0; if(how == 's' && s == nil){ if(chkmboxpath(mbox) > 0) if(fprint(fd, "%s\n", mbox) > 0) ok = 1; }else if(how == 'u' && s != nil){ ein = strchr(s, 0); memmove(s, &s[l+1], ein - &s[l+1]); ein -= l + 1; tfd = cdopen(mboxdir, subscribed, OWRITE|OTRUNC); if(tfd >= 0 && pwrite(fd, in, ein - in, 0) == ein - in) ok = 1; close(tfd); }else ok = 1; close(fd); mbunlock(mb); return ok; }