summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/imap4d/list.c
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/upas/imap4d/list.c
parent8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff)
downloadplan9front-963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc.tar.xz
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/upas/imap4d/list.c')
-rw-r--r--sys/src/cmd/upas/imap4d/list.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/sys/src/cmd/upas/imap4d/list.c b/sys/src/cmd/upas/imap4d/list.c
new file mode 100644
index 000000000..1dd3533eb
--- /dev/null
+++ b/sys/src/cmd/upas/imap4d/list.c
@@ -0,0 +1,425 @@
+#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;
+}