1704 lines
28 KiB
C
1704 lines
28 KiB
C
#include "common.h"
|
|
#include <auth.h>
|
|
#include <fcall.h>
|
|
#include <libsec.h>
|
|
#include <9pclient.h> /* jpc */
|
|
#include <thread.h> /* jpc */
|
|
#include "dat.h"
|
|
|
|
enum
|
|
{
|
|
OPERM = 0x3, /* mask of all permission types in open mode */
|
|
};
|
|
|
|
typedef struct Fid Fid;
|
|
|
|
struct Fid
|
|
{
|
|
Qid qid;
|
|
short busy;
|
|
short open;
|
|
int fid;
|
|
Fid *next;
|
|
Mailbox *mb;
|
|
Message *m;
|
|
Message *mtop; /* top level message */
|
|
|
|
/*finger pointers to speed up reads of large directories */
|
|
long foff; /* offset/DIRLEN of finger */
|
|
Message *fptr; /* pointer to message at off */
|
|
int fvers; /* mailbox version when finger was saved */
|
|
};
|
|
|
|
ulong path; /* incremented for each new file */
|
|
Fid *fids;
|
|
int mfd[2];
|
|
char user[Elemlen];
|
|
int messagesize = 4*1024*IOHDRSZ;
|
|
uchar mdata[8*1024*IOHDRSZ];
|
|
uchar mbuf[8*1024*IOHDRSZ];
|
|
Fcall thdr;
|
|
Fcall rhdr;
|
|
int fflg;
|
|
char *mntpt;
|
|
int biffing;
|
|
int plumbing = 1;
|
|
|
|
QLock mbllock;
|
|
Mailbox *mbl;
|
|
|
|
Fid *newfid(int);
|
|
void error(char*);
|
|
void io(void);
|
|
void *erealloc(void*, ulong);
|
|
void *emalloc(ulong);
|
|
void usage(void);
|
|
void run_io(void*);
|
|
void reader(void*);
|
|
int readheader(Message*, char*, int, int);
|
|
int cistrncmp(char*, char*, int);
|
|
int tokenconvert(String*, char*, int);
|
|
String* stringconvert(String*, char*, int);
|
|
void post(char*, char*, int);
|
|
|
|
char *rflush(Fid*), *rauth(Fid*),
|
|
*rattach(Fid*), *rwalk(Fid*),
|
|
*ropen(Fid*), *rcreate(Fid*),
|
|
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
|
|
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
|
|
*rversion(Fid*);
|
|
|
|
char *(*fcalls[])(Fid*) = {
|
|
[Tflush] rflush,
|
|
[Tversion] rversion,
|
|
[Tauth] rauth,
|
|
[Tattach] rattach,
|
|
[Twalk] rwalk,
|
|
[Topen] ropen,
|
|
[Tcreate] rcreate,
|
|
[Tread] rread,
|
|
[Twrite] rwrite,
|
|
[Tclunk] rclunk,
|
|
[Tremove] rremove,
|
|
[Tstat] rstat,
|
|
[Twstat] rwstat
|
|
};
|
|
|
|
char Eperm[] = "permission denied";
|
|
char Enotdir[] = "not a directory";
|
|
char Enoauth[] = "upas/fs: authentication not required";
|
|
char Enotexist[] = "file does not exist";
|
|
char Einuse[] = "file in use";
|
|
char Eexist[] = "file exists";
|
|
char Enotowner[] = "not owner";
|
|
char Eisopen[] = "file already open for I/O";
|
|
char Excl[] = "exclusive use file already open";
|
|
char Ename[] = "illegal name";
|
|
char Ebadctl[] = "unknown control message";
|
|
|
|
char *dirtab[] =
|
|
{
|
|
[Qdir] ".",
|
|
[Qbody] "body",
|
|
[Qbcc] "bcc",
|
|
[Qcc] "cc",
|
|
[Qdate] "date",
|
|
[Qdigest] "digest",
|
|
[Qdisposition] "disposition",
|
|
[Qfilename] "filename",
|
|
[Qfrom] "from",
|
|
[Qheader] "header",
|
|
[Qinfo] "info",
|
|
[Qinreplyto] "inreplyto",
|
|
[Qlines] "lines",
|
|
[Qmimeheader] "mimeheader",
|
|
[Qmessageid] "messageid",
|
|
[Qraw] "raw",
|
|
[Qrawunix] "rawunix",
|
|
[Qrawbody] "rawbody",
|
|
[Qrawheader] "rawheader",
|
|
[Qreplyto] "replyto",
|
|
[Qsender] "sender",
|
|
[Qsubject] "subject",
|
|
[Qto] "to",
|
|
[Qtype] "type",
|
|
[Qunixdate] "unixdate",
|
|
[Qunixheader] "unixheader",
|
|
[Qctl] "ctl",
|
|
[Qmboxctl] "ctl"
|
|
};
|
|
|
|
enum
|
|
{
|
|
Hsize= 1277
|
|
};
|
|
|
|
Hash *htab[Hsize];
|
|
|
|
int debug;
|
|
int fflag;
|
|
int logging;
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: %s [-b -m mountpoint]\n", argv0);
|
|
threadexits("usage");
|
|
}
|
|
|
|
void
|
|
notifyf(void *a, char *s)
|
|
{
|
|
USED(a);
|
|
if(strncmp(s, "interrupt", 9) == 0)
|
|
noted(NCONT);
|
|
noted(NDFLT);
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char *argv[])
|
|
{
|
|
int p[2], std, nodflt;
|
|
char maildir[128];
|
|
char mbox[128];
|
|
char *mboxfile, *err;
|
|
char srvfile[64];
|
|
int srvpost;
|
|
|
|
rfork(RFNOTEG);
|
|
mntpt = nil;
|
|
fflag = 0;
|
|
mboxfile = nil;
|
|
std = 0;
|
|
nodflt = 0;
|
|
srvpost = 0;
|
|
|
|
ARGBEGIN{
|
|
case 'b':
|
|
biffing = 1;
|
|
break;
|
|
case 'f':
|
|
fflag = 1;
|
|
mboxfile = ARGF();
|
|
break;
|
|
case 'm':
|
|
mntpt = ARGF();
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'p':
|
|
plumbing = 0;
|
|
break;
|
|
case 's':
|
|
srvpost = 1;
|
|
break;
|
|
case 'l':
|
|
logging = 1;
|
|
break;
|
|
case 'n':
|
|
nodflt = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
if(pipe(p) < 0)
|
|
error("pipe failed");
|
|
mfd[0] = p[0];
|
|
mfd[1] = p[0];
|
|
|
|
notify(notifyf);
|
|
strcpy(user, getuser());
|
|
if(mntpt == nil){
|
|
snprint(maildir, sizeof(maildir), "/mail/fs");
|
|
mntpt = maildir;
|
|
}
|
|
if(mboxfile == nil && !nodflt){
|
|
snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
|
|
mboxfile = mbox;
|
|
std = 1;
|
|
}
|
|
|
|
if(debug)
|
|
fmtinstall('F', fcallfmt);
|
|
|
|
if(mboxfile != nil){
|
|
err = newmbox(mboxfile, "mbox", std);
|
|
if(err != nil)
|
|
sysfatal("opening mailbox: %s", err);
|
|
}
|
|
|
|
switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ /* jpc removed RFEND */
|
|
case -1:
|
|
error("fork");
|
|
case 0:
|
|
henter(PATH(0, Qtop), dirtab[Qctl],
|
|
(Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
|
|
close(p[1]);
|
|
io();
|
|
postnote(PNGROUP, getpid(), "die yankee pig dog");
|
|
break;
|
|
default:
|
|
close(p[0]); /* don't deadlock if child fails */
|
|
if(srvpost){
|
|
sprint(srvfile, "/srv/upasfs.%s", user);
|
|
/* post(srvfile, "upasfs", p[1]); jpc */
|
|
post9pservice(p[1], "upasfs", nil); /* jpc */
|
|
} else {
|
|
error("tried to mount, fixme"); /* jpc */
|
|
/* if(mount(p[1], -1, mntpt, MREPL, "") < 0)
|
|
error("mount failed"); jpc */
|
|
}
|
|
}
|
|
threadexits(0);
|
|
}
|
|
|
|
void run_io(void *v) {
|
|
int *p;
|
|
|
|
p = v;
|
|
henter(PATH(0, Qtop), dirtab[Qctl],
|
|
(Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
|
|
close(p[1]);
|
|
io();
|
|
postnote(PNGROUP, getpid(), "die yankee pig dog");
|
|
}
|
|
|
|
static int
|
|
fileinfo(Message *m, int t, char **pp)
|
|
{
|
|
char *p;
|
|
int len;
|
|
|
|
p = "";
|
|
len = 0;
|
|
switch(t){
|
|
case Qbody:
|
|
p = m->body;
|
|
len = m->bend - m->body;
|
|
break;
|
|
case Qbcc:
|
|
if(m->bcc822){
|
|
p = s_to_c(m->bcc822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qcc:
|
|
if(m->cc822){
|
|
p = s_to_c(m->cc822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qdisposition:
|
|
switch(m->disposition){
|
|
case Dinline:
|
|
p = "inline";
|
|
break;
|
|
case Dfile:
|
|
p = "file";
|
|
break;
|
|
}
|
|
len = strlen(p);
|
|
break;
|
|
case Qdate:
|
|
if(m->date822){
|
|
p = s_to_c(m->date822);
|
|
len = strlen(p);
|
|
} else if(m->unixdate != nil){
|
|
p = s_to_c(m->unixdate);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qfilename:
|
|
if(m->filename){
|
|
p = s_to_c(m->filename);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qinreplyto:
|
|
if(m->inreplyto822){
|
|
p = s_to_c(m->inreplyto822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qmessageid:
|
|
if(m->messageid822){
|
|
p = s_to_c(m->messageid822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qfrom:
|
|
if(m->from822){
|
|
p = s_to_c(m->from822);
|
|
len = strlen(p);
|
|
} else if(m->unixfrom != nil){
|
|
p = s_to_c(m->unixfrom);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qheader:
|
|
p = m->header;
|
|
len = headerlen(m);
|
|
break;
|
|
case Qlines:
|
|
p = m->lines;
|
|
if(*p == 0)
|
|
countlines(m);
|
|
len = strlen(m->lines);
|
|
break;
|
|
case Qraw:
|
|
p = m->start;
|
|
if(strncmp(m->start, "From ", 5) == 0){
|
|
p = strchr(p, '\n');
|
|
if(p == nil)
|
|
p = m->start;
|
|
else
|
|
p++;
|
|
}
|
|
len = m->end - p;
|
|
break;
|
|
case Qrawunix:
|
|
p = m->start;
|
|
len = m->end - p;
|
|
break;
|
|
case Qrawbody:
|
|
p = m->rbody;
|
|
len = m->rbend - p;
|
|
break;
|
|
case Qrawheader:
|
|
p = m->header;
|
|
len = m->hend - p;
|
|
break;
|
|
case Qmimeheader:
|
|
p = m->mheader;
|
|
len = m->mhend - p;
|
|
break;
|
|
case Qreplyto:
|
|
p = nil;
|
|
if(m->replyto822 != nil){
|
|
p = s_to_c(m->replyto822);
|
|
len = strlen(p);
|
|
} else if(m->from822 != nil){
|
|
p = s_to_c(m->from822);
|
|
len = strlen(p);
|
|
} else if(m->sender822 != nil){
|
|
p = s_to_c(m->sender822);
|
|
len = strlen(p);
|
|
} else if(m->unixfrom != nil){
|
|
p = s_to_c(m->unixfrom);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qsender:
|
|
if(m->sender822){
|
|
p = s_to_c(m->sender822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qsubject:
|
|
p = nil;
|
|
if(m->subject822){
|
|
p = s_to_c(m->subject822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qto:
|
|
if(m->to822){
|
|
p = s_to_c(m->to822);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qtype:
|
|
if(m->type){
|
|
p = s_to_c(m->type);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qunixdate:
|
|
if(m->unixdate){
|
|
p = s_to_c(m->unixdate);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
case Qunixheader:
|
|
if(m->unixheader){
|
|
p = s_to_c(m->unixheader);
|
|
len = s_len(m->unixheader);
|
|
}
|
|
break;
|
|
case Qdigest:
|
|
if(m->sdigest){
|
|
p = s_to_c(m->sdigest);
|
|
len = strlen(p);
|
|
}
|
|
break;
|
|
}
|
|
*pp = p;
|
|
return len;
|
|
}
|
|
|
|
int infofields[] = {
|
|
Qfrom,
|
|
Qto,
|
|
Qcc,
|
|
Qreplyto,
|
|
Qunixdate,
|
|
Qsubject,
|
|
Qtype,
|
|
Qdisposition,
|
|
Qfilename,
|
|
Qdigest,
|
|
Qbcc,
|
|
Qinreplyto,
|
|
Qdate,
|
|
Qsender,
|
|
Qmessageid,
|
|
Qlines,
|
|
-1
|
|
};
|
|
|
|
static int
|
|
readinfo(Message *m, char *buf, long off, int count)
|
|
{
|
|
char *p;
|
|
int len, i, n;
|
|
String *s;
|
|
|
|
s = s_new();
|
|
len = 0;
|
|
for(i = 0; len < count && infofields[i] >= 0; i++){
|
|
n = fileinfo(m, infofields[i], &p);
|
|
s = stringconvert(s, p, n);
|
|
s_append(s, "\n");
|
|
p = s_to_c(s);
|
|
n = strlen(p);
|
|
if(off > 0){
|
|
if(off >= n){
|
|
off -= n;
|
|
continue;
|
|
}
|
|
p += off;
|
|
n -= off;
|
|
off = 0;
|
|
}
|
|
if(n > count - len)
|
|
n = count - len;
|
|
if(buf)
|
|
memmove(buf+len, p, n);
|
|
len += n;
|
|
}
|
|
s_free(s);
|
|
return len;
|
|
}
|
|
|
|
static void
|
|
mkstat(Dir *d, Mailbox *mb, Message *m, int t)
|
|
{
|
|
char *p;
|
|
|
|
d->uid = user;
|
|
d->gid = user;
|
|
d->muid = user;
|
|
d->mode = 0444;
|
|
d->qid.vers = 0;
|
|
d->qid.type = QTFILE;
|
|
d->type = 0;
|
|
d->dev = 0;
|
|
if(mb != nil && mb->d != nil){
|
|
d->atime = mb->d->atime;
|
|
d->mtime = mb->d->mtime;
|
|
} else {
|
|
d->atime = time(0);
|
|
d->mtime = d->atime;
|
|
}
|
|
|
|
switch(t){
|
|
case Qtop:
|
|
d->name = ".";
|
|
d->mode = DMDIR|0555;
|
|
d->atime = d->mtime = time(0);
|
|
d->length = 0;
|
|
d->qid.path = PATH(0, Qtop);
|
|
d->qid.type = QTDIR;
|
|
break;
|
|
case Qmbox:
|
|
d->name = mb->name;
|
|
d->mode = DMDIR|0555;
|
|
d->length = 0;
|
|
d->qid.path = PATH(mb->id, Qmbox);
|
|
d->qid.type = QTDIR;
|
|
d->qid.vers = mb->vers;
|
|
break;
|
|
case Qdir:
|
|
d->name = m->name;
|
|
d->mode = DMDIR|0555;
|
|
d->length = 0;
|
|
d->qid.path = PATH(m->id, Qdir);
|
|
d->qid.type = QTDIR;
|
|
break;
|
|
case Qctl:
|
|
d->name = dirtab[t];
|
|
d->mode = 0666;
|
|
d->atime = d->mtime = time(0);
|
|
d->length = 0;
|
|
d->qid.path = PATH(0, Qctl);
|
|
break;
|
|
case Qmboxctl:
|
|
d->name = dirtab[t];
|
|
d->mode = 0222;
|
|
d->atime = d->mtime = time(0);
|
|
d->length = 0;
|
|
d->qid.path = PATH(mb->id, Qmboxctl);
|
|
break;
|
|
case Qinfo:
|
|
d->name = dirtab[t];
|
|
d->length = readinfo(m, nil, 0, 1<<30);
|
|
d->qid.path = PATH(m->id, t);
|
|
break;
|
|
default:
|
|
d->name = dirtab[t];
|
|
d->length = fileinfo(m, t, &p);
|
|
d->qid.path = PATH(m->id, t);
|
|
break;
|
|
}
|
|
}
|
|
|
|
char*
|
|
rversion(Fid* dummy)
|
|
{
|
|
Fid *f;
|
|
|
|
if(thdr.msize < 256)
|
|
return "max messagesize too small";
|
|
if(thdr.msize < messagesize)
|
|
messagesize = thdr.msize;
|
|
rhdr.msize = messagesize;
|
|
if(strncmp(thdr.version, "9P2000", 6) != 0)
|
|
return "unknown 9P version";
|
|
else
|
|
rhdr.version = "9P2000";
|
|
for(f = fids; f; f = f->next)
|
|
if(f->busy)
|
|
rclunk(f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
rauth(Fid* dummy)
|
|
{
|
|
return Enoauth;
|
|
}
|
|
|
|
char*
|
|
rflush(Fid *f)
|
|
{
|
|
USED(f);
|
|
return 0;
|
|
}
|
|
|
|
char*
|
|
rattach(Fid *f)
|
|
{
|
|
f->busy = 1;
|
|
f->m = nil;
|
|
f->mb = nil;
|
|
f->qid.path = PATH(0, Qtop);
|
|
f->qid.type = QTDIR;
|
|
f->qid.vers = 0;
|
|
rhdr.qid = f->qid;
|
|
if(strcmp(thdr.uname, user) != 0)
|
|
return Eperm;
|
|
return 0;
|
|
}
|
|
|
|
static Fid*
|
|
doclone(Fid *f, int nfid)
|
|
{
|
|
Fid *nf;
|
|
|
|
nf = newfid(nfid);
|
|
if(nf->busy)
|
|
return nil;
|
|
nf->busy = 1;
|
|
nf->open = 0;
|
|
nf->m = f->m;
|
|
nf->mtop = f->mtop;
|
|
nf->mb = f->mb;
|
|
if(f->mb != nil)
|
|
mboxincref(f->mb);
|
|
if(f->mtop != nil){
|
|
qlock(&f->mb->ql);
|
|
msgincref(f->mtop);
|
|
qunlock(&f->mb->ql);
|
|
}
|
|
nf->qid = f->qid;
|
|
return nf;
|
|
}
|
|
|
|
char*
|
|
dowalk(Fid *f, char *name)
|
|
{
|
|
int t;
|
|
Mailbox *omb, *mb;
|
|
char *rv, *p;
|
|
Hash *h;
|
|
|
|
t = FILE(f->qid.path);
|
|
|
|
rv = Enotexist;
|
|
|
|
omb = f->mb;
|
|
if(omb)
|
|
qlock(&omb->ql);
|
|
else
|
|
qlock(&mbllock);
|
|
|
|
/* this must catch everything except . and .. */
|
|
retry:
|
|
h = hlook(f->qid.path, name);
|
|
if(h != nil){
|
|
f->mb = h->mb;
|
|
f->m = h->m;
|
|
switch(t){
|
|
case Qtop:
|
|
if(f->mb != nil)
|
|
mboxincref(f->mb);
|
|
break;
|
|
case Qmbox:
|
|
if(f->m){
|
|
msgincref(f->m);
|
|
f->mtop = f->m;
|
|
}
|
|
break;
|
|
}
|
|
f->qid = h->qid;
|
|
rv = nil;
|
|
} else if((p = strchr(name, '.')) != nil && *name != '.'){
|
|
*p = 0;
|
|
goto retry;
|
|
}
|
|
|
|
if(omb)
|
|
qunlock(&omb->ql);
|
|
else
|
|
qunlock(&mbllock);
|
|
if(rv == nil)
|
|
return rv;
|
|
|
|
if(strcmp(name, ".") == 0)
|
|
return nil;
|
|
|
|
if(f->qid.type != QTDIR)
|
|
return Enotdir;
|
|
|
|
if(strcmp(name, "..") == 0){
|
|
switch(t){
|
|
case Qtop:
|
|
f->qid.path = PATH(0, Qtop);
|
|
f->qid.type = QTDIR;
|
|
f->qid.vers = 0;
|
|
break;
|
|
case Qmbox:
|
|
f->qid.path = PATH(0, Qtop);
|
|
f->qid.type = QTDIR;
|
|
f->qid.vers = 0;
|
|
qlock(&mbllock);
|
|
mb = f->mb;
|
|
f->mb = nil;
|
|
mboxdecref(mb);
|
|
qunlock(&mbllock);
|
|
break;
|
|
case Qdir:
|
|
qlock(&f->mb->ql);
|
|
if(f->m->whole == f->mb->root){
|
|
f->qid.path = PATH(f->mb->id, Qmbox);
|
|
f->qid.type = QTDIR;
|
|
f->qid.vers = f->mb->d->qid.vers;
|
|
msgdecref(f->mb, f->mtop);
|
|
f->m = f->mtop = nil;
|
|
} else {
|
|
f->m = f->m->whole;
|
|
f->qid.path = PATH(f->m->id, Qdir);
|
|
f->qid.type = QTDIR;
|
|
}
|
|
qunlock(&f->mb->ql);
|
|
break;
|
|
}
|
|
rv = nil;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
char*
|
|
rwalk(Fid *f)
|
|
{
|
|
Fid *nf;
|
|
char *rv;
|
|
int i;
|
|
|
|
if(f->open)
|
|
return Eisopen;
|
|
|
|
rhdr.nwqid = 0;
|
|
nf = nil;
|
|
|
|
/* clone if requested */
|
|
if(thdr.newfid != thdr.fid){
|
|
nf = doclone(f, thdr.newfid);
|
|
if(nf == nil)
|
|
return "new fid in use";
|
|
f = nf;
|
|
}
|
|
|
|
/* if it's just a clone, return */
|
|
if(thdr.nwname == 0 && nf != nil)
|
|
return nil;
|
|
|
|
/* walk each element */
|
|
rv = nil;
|
|
for(i = 0; i < thdr.nwname; i++){
|
|
rv = dowalk(f, thdr.wname[i]);
|
|
if(rv != nil){
|
|
if(nf != nil)
|
|
rclunk(nf);
|
|
break;
|
|
}
|
|
rhdr.wqid[i] = f->qid;
|
|
}
|
|
rhdr.nwqid = i;
|
|
|
|
/* we only error out if no walk */
|
|
if(i > 0)
|
|
rv = nil;
|
|
|
|
return rv;
|
|
}
|
|
|
|
char *
|
|
ropen(Fid *f)
|
|
{
|
|
int file;
|
|
|
|
if(f->open)
|
|
return Eisopen;
|
|
|
|
file = FILE(f->qid.path);
|
|
if(thdr.mode != OREAD)
|
|
if(file != Qctl && file != Qmboxctl)
|
|
return Eperm;
|
|
|
|
/* make sure we've decoded */
|
|
if(file == Qbody){
|
|
if(f->m->decoded == 0)
|
|
decode(f->m);
|
|
if(f->m->converted == 0)
|
|
convert(f->m);
|
|
}
|
|
|
|
rhdr.iounit = 0;
|
|
rhdr.qid = f->qid;
|
|
f->open = 1;
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
rcreate(Fid* dummy)
|
|
{
|
|
return Eperm;
|
|
}
|
|
|
|
int
|
|
readtopdir(Fid* dummy, uchar *buf, long off, int cnt, int blen)
|
|
{
|
|
Dir d;
|
|
int m, n;
|
|
long pos;
|
|
Mailbox *mb;
|
|
|
|
n = 0;
|
|
pos = 0;
|
|
mkstat(&d, nil, nil, Qctl);
|
|
m = convD2M(&d, &buf[n], blen);
|
|
if(off <= pos){
|
|
if(m <= BIT16SZ || m > cnt)
|
|
return 0;
|
|
n += m;
|
|
cnt -= m;
|
|
}
|
|
pos += m;
|
|
|
|
for(mb = mbl; mb != nil; mb = mb->next){
|
|
mkstat(&d, mb, nil, Qmbox);
|
|
m = convD2M(&d, &buf[n], blen-n);
|
|
if(off <= pos){
|
|
if(m <= BIT16SZ || m > cnt)
|
|
break;
|
|
n += m;
|
|
cnt -= m;
|
|
}
|
|
pos += m;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
|
|
{
|
|
Dir d;
|
|
int n, m;
|
|
long pos;
|
|
Message *msg;
|
|
|
|
n = 0;
|
|
if(f->mb->ctl){
|
|
mkstat(&d, f->mb, nil, Qmboxctl);
|
|
m = convD2M(&d, &buf[n], blen);
|
|
if(off == 0){
|
|
if(m <= BIT16SZ || m > cnt){
|
|
f->fptr = nil;
|
|
return 0;
|
|
}
|
|
n += m;
|
|
cnt -= m;
|
|
} else
|
|
off -= m;
|
|
}
|
|
|
|
/* to avoid n**2 reads of the directory, use a saved finger pointer */
|
|
if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
|
|
msg = f->fptr;
|
|
pos = f->foff;
|
|
} else {
|
|
msg = f->mb->root->part;
|
|
pos = 0;
|
|
}
|
|
|
|
for(; cnt > 0 && msg != nil; msg = msg->next){
|
|
/* act like deleted files aren't there */
|
|
if(msg->deleted)
|
|
continue;
|
|
|
|
mkstat(&d, f->mb, msg, Qdir);
|
|
m = convD2M(&d, &buf[n], blen-n);
|
|
if(off <= pos){
|
|
if(m <= BIT16SZ || m > cnt)
|
|
break;
|
|
n += m;
|
|
cnt -= m;
|
|
}
|
|
pos += m;
|
|
}
|
|
|
|
/* save a finger pointer for next read of the mbox directory */
|
|
f->foff = pos;
|
|
f->fptr = msg;
|
|
f->fvers = f->mb->vers;
|
|
|
|
return n;
|
|
}
|
|
|
|
int
|
|
readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
|
|
{
|
|
Dir d;
|
|
int i, n, m;
|
|
long pos;
|
|
Message *msg;
|
|
|
|
n = 0;
|
|
pos = 0;
|
|
for(i = 0; i < Qmax; i++){
|
|
mkstat(&d, f->mb, f->m, i);
|
|
m = convD2M(&d, &buf[n], blen-n);
|
|
if(off <= pos){
|
|
if(m <= BIT16SZ || m > cnt)
|
|
return n;
|
|
n += m;
|
|
cnt -= m;
|
|
}
|
|
pos += m;
|
|
}
|
|
for(msg = f->m->part; msg != nil; msg = msg->next){
|
|
mkstat(&d, f->mb, msg, Qdir);
|
|
m = convD2M(&d, &buf[n], blen-n);
|
|
if(off <= pos){
|
|
if(m <= BIT16SZ || m > cnt)
|
|
break;
|
|
n += m;
|
|
cnt -= m;
|
|
}
|
|
pos += m;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
char*
|
|
rread(Fid *f)
|
|
{
|
|
long off;
|
|
int t, i, n, cnt;
|
|
char *p;
|
|
|
|
rhdr.count = 0;
|
|
off = thdr.offset;
|
|
cnt = thdr.count;
|
|
|
|
if(cnt > messagesize - IOHDRSZ)
|
|
cnt = messagesize - IOHDRSZ;
|
|
|
|
rhdr.data = (char*)mbuf;
|
|
|
|
t = FILE(f->qid.path);
|
|
if(f->qid.type & QTDIR){
|
|
if(t == Qtop) {
|
|
qlock(&mbllock);
|
|
n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
|
|
qunlock(&mbllock);
|
|
} else if(t == Qmbox) {
|
|
qlock(&f->mb->ql);
|
|
if(off == 0)
|
|
syncmbox(f->mb, 1);
|
|
n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
|
|
qunlock(&f->mb->ql);
|
|
} else if(t == Qmboxctl) {
|
|
n = 0;
|
|
} else {
|
|
n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
|
|
}
|
|
|
|
rhdr.count = n;
|
|
return nil;
|
|
}
|
|
|
|
if(FILE(f->qid.path) == Qheader){
|
|
rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
|
|
return nil;
|
|
}
|
|
|
|
if(FILE(f->qid.path) == Qinfo){
|
|
rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
|
|
return nil;
|
|
}
|
|
|
|
i = fileinfo(f->m, FILE(f->qid.path), &p);
|
|
if(off < i){
|
|
if((off + cnt) > i)
|
|
cnt = i - off;
|
|
memmove(mbuf, p + off, cnt);
|
|
rhdr.count = cnt;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
rwrite(Fid *f)
|
|
{
|
|
char *err;
|
|
char *token[1024];
|
|
int t, n;
|
|
String *file;
|
|
|
|
t = FILE(f->qid.path);
|
|
rhdr.count = thdr.count;
|
|
switch(t){
|
|
case Qctl:
|
|
if(thdr.count == 0)
|
|
return Ebadctl;
|
|
if(thdr.data[thdr.count-1] == '\n')
|
|
thdr.data[thdr.count-1] = 0;
|
|
else
|
|
thdr.data[thdr.count] = 0;
|
|
n = tokenize(thdr.data, token, nelem(token));
|
|
if(n == 0)
|
|
return Ebadctl;
|
|
if(strcmp(token[0], "open") == 0){
|
|
file = s_new();
|
|
switch(n){
|
|
case 1:
|
|
err = Ebadctl;
|
|
break;
|
|
case 2:
|
|
mboxpath(token[1], getlog(), file, 0);
|
|
err = newmbox(s_to_c(file), nil, 0);
|
|
break;
|
|
default:
|
|
mboxpath(token[1], getlog(), file, 0);
|
|
if(strchr(token[2], '/') != nil)
|
|
err = "/ not allowed in mailbox name";
|
|
else
|
|
err = newmbox(s_to_c(file), token[2], 0);
|
|
break;
|
|
}
|
|
s_free(file);
|
|
return err;
|
|
}
|
|
if(strcmp(token[0], "close") == 0){
|
|
if(n < 2)
|
|
return nil;
|
|
freembox(token[1]);
|
|
return nil;
|
|
}
|
|
if(strcmp(token[0], "delete") == 0){
|
|
if(n < 3)
|
|
return nil;
|
|
delmessages(n-1, &token[1]);
|
|
return nil;
|
|
}
|
|
return Ebadctl;
|
|
case Qmboxctl:
|
|
if(f->mb && f->mb->ctl){
|
|
if(thdr.count == 0)
|
|
return Ebadctl;
|
|
if(thdr.data[thdr.count-1] == '\n')
|
|
thdr.data[thdr.count-1] = 0;
|
|
else
|
|
thdr.data[thdr.count] = 0;
|
|
n = tokenize(thdr.data, token, nelem(token));
|
|
if(n == 0)
|
|
return Ebadctl;
|
|
return (*f->mb->ctl)(f->mb, n, token);
|
|
}
|
|
}
|
|
return Eperm;
|
|
}
|
|
|
|
char *
|
|
rclunk(Fid *f)
|
|
{
|
|
Mailbox *mb;
|
|
|
|
f->busy = 0;
|
|
f->open = 0;
|
|
if(f->mtop != nil){
|
|
qlock(&f->mb->ql);
|
|
msgdecref(f->mb, f->mtop);
|
|
qunlock(&f->mb->ql);
|
|
}
|
|
f->m = f->mtop = nil;
|
|
mb = f->mb;
|
|
if(mb != nil){
|
|
f->mb = nil;
|
|
assert(mb->refs > 0);
|
|
qlock(&mbllock);
|
|
mboxdecref(mb);
|
|
qunlock(&mbllock);
|
|
}
|
|
f->fid = -1;
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
rremove(Fid *f)
|
|
{
|
|
if(f->m != nil){
|
|
if(f->m->deleted == 0)
|
|
mailplumb(f->mb, f->m, 1);
|
|
f->m->deleted = 1;
|
|
}
|
|
return rclunk(f);
|
|
}
|
|
|
|
char *
|
|
rstat(Fid *f)
|
|
{
|
|
Dir d;
|
|
|
|
if(FILE(f->qid.path) == Qmbox){
|
|
qlock(&f->mb->ql);
|
|
syncmbox(f->mb, 1);
|
|
qunlock(&f->mb->ql);
|
|
}
|
|
mkstat(&d, f->mb, f->m, FILE(f->qid.path));
|
|
rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
|
|
rhdr.stat = mbuf;
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
rwstat(Fid* dummy)
|
|
{
|
|
return Eperm;
|
|
}
|
|
|
|
Fid *
|
|
newfid(int fid)
|
|
{
|
|
Fid *f, *ff;
|
|
|
|
ff = 0;
|
|
for(f = fids; f; f = f->next)
|
|
if(f->fid == fid)
|
|
return f;
|
|
else if(!ff && !f->busy)
|
|
ff = f;
|
|
if(ff){
|
|
ff->fid = fid;
|
|
ff->fptr = nil;
|
|
return ff;
|
|
}
|
|
f = emalloc(sizeof *f);
|
|
f->fid = fid;
|
|
f->fptr = nil;
|
|
f->next = fids;
|
|
fids = f;
|
|
return f;
|
|
}
|
|
|
|
int
|
|
fidmboxrefs(Mailbox *mb)
|
|
{
|
|
Fid *f;
|
|
int refs = 0;
|
|
|
|
for(f = fids; f; f = f->next){
|
|
if(f->mb == mb)
|
|
refs++;
|
|
}
|
|
return refs;
|
|
}
|
|
|
|
void
|
|
io(void)
|
|
{
|
|
char *err;
|
|
int n, nw;
|
|
|
|
/* start a process to watch the mailboxes*/
|
|
if(plumbing){
|
|
proccreate(reader, nil, 16000);
|
|
#if 0 /* jpc */
|
|
switch(rfork(RFPROC|RFMEM)){
|
|
case -1:
|
|
/* oh well */
|
|
break;
|
|
case 0:
|
|
reader();
|
|
threadexits(nil);
|
|
default:
|
|
break;
|
|
}
|
|
#endif /* jpc */
|
|
}
|
|
|
|
for(;;){
|
|
/*
|
|
* reading from a pipe or a network device
|
|
* will give an error after a few eof reads
|
|
* however, we cannot tell the difference
|
|
* between a zero-length read and an interrupt
|
|
* on the processes writing to us,
|
|
* so we wait for the error
|
|
*/
|
|
checkmboxrefs();
|
|
n = read9pmsg(mfd[0], mdata, messagesize);
|
|
if(n == 0)
|
|
continue;
|
|
if(n < 0)
|
|
return;
|
|
if(convM2S(mdata, n, &thdr) == 0)
|
|
continue;
|
|
|
|
if(debug)
|
|
fprint(2, "%s:<-%F\n", argv0, &thdr);
|
|
|
|
rhdr.data = (char*)mdata + messagesize;
|
|
if(!fcalls[thdr.type])
|
|
err = "bad fcall type";
|
|
else
|
|
err = (*fcalls[thdr.type])(newfid(thdr.fid));
|
|
if(err){
|
|
rhdr.type = Rerror;
|
|
rhdr.ename = err;
|
|
}else{
|
|
rhdr.type = thdr.type + 1;
|
|
rhdr.fid = thdr.fid;
|
|
}
|
|
rhdr.tag = thdr.tag;
|
|
if(debug)
|
|
fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
|
|
n = convS2M(&rhdr, mdata, messagesize);
|
|
if((nw = write(mfd[1], mdata, n)) != n) {
|
|
fprint(2,"wrote %d bytes\n",nw);
|
|
error("mount write");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
reader(void *dummy)
|
|
{
|
|
ulong t;
|
|
Dir *d;
|
|
Mailbox *mb;
|
|
|
|
sleep(15*1000);
|
|
for(;;){
|
|
t = time(0);
|
|
qlock(&mbllock);
|
|
for(mb = mbl; mb != nil; mb = mb->next){
|
|
assert(mb->refs > 0);
|
|
if(mb->waketime != 0 && t > mb->waketime){
|
|
qlock(&mb->ql);
|
|
mb->waketime = 0;
|
|
break;
|
|
}
|
|
|
|
d = dirstat(mb->path);
|
|
if(d == nil)
|
|
continue;
|
|
|
|
qlock(&mb->ql);
|
|
if(mb->d)
|
|
if(d->qid.path != mb->d->qid.path
|
|
|| d->qid.vers != mb->d->qid.vers){
|
|
free(d);
|
|
break;
|
|
}
|
|
qunlock(&mb->ql);
|
|
free(d);
|
|
}
|
|
qunlock(&mbllock);
|
|
if(mb != nil){
|
|
syncmbox(mb, 1);
|
|
qunlock(&mb->ql);
|
|
} else
|
|
sleep(15*1000);
|
|
}
|
|
}
|
|
|
|
int
|
|
newid(void)
|
|
{
|
|
int rv;
|
|
static int id;
|
|
static Lock idlock;
|
|
|
|
lock(&idlock);
|
|
rv = ++id;
|
|
unlock(&idlock);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
error(char *s)
|
|
{
|
|
postnote(PNGROUP, getpid(), "die yankee pig dog");
|
|
fprint(2, "%s: %s: %r\n", argv0, s);
|
|
threadexits(s);
|
|
}
|
|
|
|
|
|
typedef struct Ignorance Ignorance;
|
|
struct Ignorance
|
|
{
|
|
Ignorance *next;
|
|
char *str; /* string */
|
|
int partial; /* true if not exact match */
|
|
};
|
|
Ignorance *ignorance;
|
|
|
|
/*
|
|
* read the file of headers to ignore
|
|
*/
|
|
void
|
|
readignore(void)
|
|
{
|
|
char *p;
|
|
Ignorance *i;
|
|
Biobuf *b;
|
|
|
|
if(ignorance != nil)
|
|
return;
|
|
|
|
b = Bopen("/mail/lib/ignore", OREAD);
|
|
if(b == 0)
|
|
return;
|
|
while(p = Brdline(b, '\n')){
|
|
p[Blinelen(b)-1] = 0;
|
|
while(*p && (*p == ' ' || *p == '\t'))
|
|
p++;
|
|
if(*p == '#')
|
|
continue;
|
|
i = malloc(sizeof(Ignorance));
|
|
if(i == 0)
|
|
break;
|
|
i->partial = strlen(p);
|
|
i->str = strdup(p);
|
|
if(i->str == 0){
|
|
free(i);
|
|
break;
|
|
}
|
|
i->next = ignorance;
|
|
ignorance = i;
|
|
}
|
|
Bterm(b);
|
|
}
|
|
|
|
int
|
|
ignore(char *p)
|
|
{
|
|
Ignorance *i;
|
|
|
|
readignore();
|
|
for(i = ignorance; i != nil; i = i->next)
|
|
if(cistrncmp(i->str, p, i->partial) == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
hdrlen(char *p, char *e)
|
|
{
|
|
char *ep;
|
|
|
|
ep = p;
|
|
do {
|
|
ep = strchr(ep, '\n');
|
|
if(ep == nil){
|
|
ep = e;
|
|
break;
|
|
}
|
|
ep++;
|
|
if(ep >= e){
|
|
ep = e;
|
|
break;
|
|
}
|
|
} while(*ep == ' ' || *ep == '\t');
|
|
return ep - p;
|
|
}
|
|
|
|
/* rfc2047 non-ascii */
|
|
typedef struct Charset Charset;
|
|
struct Charset {
|
|
char *name;
|
|
int len;
|
|
int convert;
|
|
char *tcsname;
|
|
} charsets[] =
|
|
{
|
|
{ "us-ascii", 8, 1, nil, },
|
|
{ "utf-8", 5, 0, nil, },
|
|
{ "iso-8859-1", 10, 1, nil, },
|
|
{ "iso-8859-2", 10, 2, "8859-2", },
|
|
{ "big5", 4, 2, "big5", },
|
|
{ "iso-2022-jp", 11, 2, "jis", },
|
|
{ "windows-1251", 12, 2, "cp1251"},
|
|
{ "koi8-r", 6, 2, "koi8"}
|
|
};
|
|
|
|
int
|
|
rfc2047convert(String *s, char *token, int len)
|
|
{
|
|
char decoded[1024];
|
|
char utfbuf[2*1024];
|
|
int i;
|
|
char *e, *x;
|
|
|
|
if(len == 0)
|
|
return -1;
|
|
|
|
e = token+len-2;
|
|
token += 2;
|
|
|
|
/* bail if we don't understand the character set */
|
|
for(i = 0; i < nelem(charsets); i++)
|
|
if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
|
|
if(token[charsets[i].len] == '?'){
|
|
token += charsets[i].len + 1;
|
|
break;
|
|
}
|
|
if(i >= nelem(charsets))
|
|
return -1;
|
|
|
|
/* bail if it doesn't fit */
|
|
if(e-token > sizeof(decoded)-1)
|
|
return -1;
|
|
|
|
/* bail if we don't understand the encoding */
|
|
if(cistrncmp(token, "b?", 2) == 0){
|
|
token += 2;
|
|
len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
|
|
decoded[len] = 0;
|
|
} else if(cistrncmp(token, "q?", 2) == 0){
|
|
token += 2;
|
|
len = decquoted(decoded, token, e);
|
|
if(len > 0 && decoded[len-1] == '\n')
|
|
len--;
|
|
decoded[len] = 0;
|
|
} else
|
|
return -1;
|
|
|
|
switch(charsets[i].convert){
|
|
case 0:
|
|
s_append(s, decoded);
|
|
break;
|
|
case 1:
|
|
latin1toutf(utfbuf, decoded, decoded+len);
|
|
s_append(s, utfbuf);
|
|
break;
|
|
case 2:
|
|
if(xtoutf(charsets[i].tcsname, &x, decoded, decoded+len) <= 0){
|
|
s_append(s, decoded);
|
|
} else {
|
|
s_append(s, x);
|
|
free(x);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char*
|
|
rfc2047start(char *start, char *end)
|
|
{
|
|
int quests;
|
|
|
|
if(*--end != '=')
|
|
return nil;
|
|
if(*--end != '?')
|
|
return nil;
|
|
|
|
quests = 0;
|
|
for(end--; end >= start; end--){
|
|
switch(*end){
|
|
case '=':
|
|
if(quests == 3 && *(end+1) == '?')
|
|
return end;
|
|
break;
|
|
case '?':
|
|
++quests;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
/* can't have white space in a token */
|
|
return nil;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/* convert a header line */
|
|
String*
|
|
stringconvert(String *s, char *uneaten, int len)
|
|
{
|
|
char *token;
|
|
char *p;
|
|
int i;
|
|
|
|
s = s_reset(s);
|
|
p = uneaten;
|
|
for(i = 0; i < len; i++){
|
|
if(*p++ == '='){
|
|
token = rfc2047start(uneaten, p);
|
|
if(token != nil){
|
|
s_nappend(s, uneaten, token-uneaten);
|
|
if(rfc2047convert(s, token, p - token) < 0)
|
|
s_nappend(s, token, p - token);
|
|
uneaten = p;
|
|
}
|
|
}
|
|
}
|
|
if(p > uneaten)
|
|
s_nappend(s, uneaten, p-uneaten);
|
|
return s;
|
|
}
|
|
|
|
int
|
|
readheader(Message *m, char *buf, int off, int cnt)
|
|
{
|
|
char *p, *e;
|
|
int n, ns;
|
|
char *to = buf;
|
|
String *s;
|
|
|
|
p = m->header;
|
|
e = m->hend;
|
|
s = nil;
|
|
|
|
/* copy in good headers */
|
|
while(cnt > 0 && p < e){
|
|
n = hdrlen(p, e);
|
|
if(ignore(p)){
|
|
p += n;
|
|
continue;
|
|
}
|
|
|
|
/* rfc2047 processing */
|
|
s = stringconvert(s, p, n);
|
|
ns = s_len(s);
|
|
if(off > 0){
|
|
if(ns <= off){
|
|
off -= ns;
|
|
p += n;
|
|
continue;
|
|
}
|
|
ns -= off;
|
|
}
|
|
if(ns > cnt)
|
|
ns = cnt;
|
|
memmove(to, s_to_c(s)+off, ns);
|
|
to += ns;
|
|
p += n;
|
|
cnt -= ns;
|
|
off = 0;
|
|
}
|
|
|
|
s_free(s);
|
|
return to - buf;
|
|
}
|
|
|
|
int
|
|
headerlen(Message *m)
|
|
{
|
|
char buf[1024];
|
|
int i, n;
|
|
|
|
if(m->hlen >= 0)
|
|
return m->hlen;
|
|
for(n = 0; ; n += i){
|
|
i = readheader(m, buf, n, sizeof(buf));
|
|
if(i <= 0)
|
|
break;
|
|
}
|
|
m->hlen = n;
|
|
return n;
|
|
}
|
|
|
|
QLock hashlock;
|
|
|
|
uint
|
|
hash(ulong ppath, char *name)
|
|
{
|
|
uchar *p;
|
|
uint h;
|
|
|
|
h = 0;
|
|
for(p = (uchar*)name; *p; p++)
|
|
h = h*7 + *p;
|
|
h += ppath;
|
|
|
|
return h % Hsize;
|
|
}
|
|
|
|
Hash*
|
|
hlook(ulong ppath, char *name)
|
|
{
|
|
int h;
|
|
Hash *hp;
|
|
|
|
qlock(&hashlock);
|
|
h = hash(ppath, name);
|
|
for(hp = htab[h]; hp != nil; hp = hp->next)
|
|
if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
|
|
qunlock(&hashlock);
|
|
return hp;
|
|
}
|
|
qunlock(&hashlock);
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
|
|
{
|
|
int h;
|
|
Hash *hp, **l;
|
|
|
|
qlock(&hashlock);
|
|
h = hash(ppath, name);
|
|
for(l = &htab[h]; *l != nil; l = &(*l)->next){
|
|
hp = *l;
|
|
if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
|
|
hp->m = m;
|
|
hp->mb = mb;
|
|
hp->qid = qid;
|
|
qunlock(&hashlock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
*l = hp = emalloc(sizeof(*hp));
|
|
hp->m = m;
|
|
hp->mb = mb;
|
|
hp->qid = qid;
|
|
hp->name = name;
|
|
hp->ppath = ppath;
|
|
qunlock(&hashlock);
|
|
}
|
|
|
|
void
|
|
hfree(ulong ppath, char *name)
|
|
{
|
|
int h;
|
|
Hash *hp, **l;
|
|
|
|
qlock(&hashlock);
|
|
h = hash(ppath, name);
|
|
for(l = &htab[h]; *l != nil; l = &(*l)->next){
|
|
hp = *l;
|
|
if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
|
|
hp->mb = nil;
|
|
*l = hp->next;
|
|
free(hp);
|
|
break;
|
|
}
|
|
}
|
|
qunlock(&hashlock);
|
|
}
|
|
|
|
int
|
|
hashmboxrefs(Mailbox *mb)
|
|
{
|
|
int h;
|
|
Hash *hp;
|
|
int refs = 0;
|
|
|
|
qlock(&hashlock);
|
|
for(h = 0; h < Hsize; h++){
|
|
for(hp = htab[h]; hp != nil; hp = hp->next)
|
|
if(hp->mb == mb)
|
|
refs++;
|
|
}
|
|
qunlock(&hashlock);
|
|
return refs;
|
|
}
|
|
|
|
void
|
|
checkmboxrefs(void)
|
|
{
|
|
int f, refs;
|
|
Mailbox *mb;
|
|
|
|
qlock(&mbllock);
|
|
for(mb=mbl; mb; mb=mb->next){
|
|
qlock(&mb->ql);
|
|
refs = (f=fidmboxrefs(mb))+1;
|
|
if(refs != mb->refs){
|
|
fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs);
|
|
abort();
|
|
}
|
|
qunlock(&mb->ql);
|
|
}
|
|
qunlock(&mbllock);
|
|
}
|
|
|
|
void
|
|
post(char *name, char *envname, int srvfd)
|
|
{
|
|
int fd;
|
|
char buf[32];
|
|
|
|
fd = create(name, OWRITE, 0600);
|
|
if(fd < 0)
|
|
error("post failed");
|
|
sprint(buf, "%d",srvfd);
|
|
if(write(fd, buf, strlen(buf)) != strlen(buf))
|
|
error("srv write");
|
|
close(fd);
|
|
putenv(envname, name);
|
|
}
|