#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))){
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;
}
|