-/*
- * Web file system. Conventionally mounted at /mnt/web
- *
- * ctl send control messages (might go away)
- * cookies list of cookies, editable
- * clone open and read to obtain new connection
- * n connection directory
- * ctl control messages (like get url)
- * body retrieved data
- * content-type mime content-type of body
- * postbody data to be posted
- * parsed parsed version of url
- * url entire url
- * scheme http, ftp, etc.
- * host hostname
- * path path on host
- * query query after path
- * fragment #foo anchor reference
- * user user name (ftp)
- * password password (ftp)
- * ftptype transfer mode (ftp)
- */
-
#include <u.h>
#include <libc.h>
-#include <bio.h>
-#include <ip.h>
-#include <plumb.h>
-#include <thread.h>
+#include <ctype.h>
#include <fcall.h>
+#include <thread.h>
#include <9p.h>
+
#include "dat.h"
#include "fns.h"
-int fsdebug;
+typedef struct Webfid Webfid;
+typedef struct Client Client;
-enum
+struct Client
{
- Qroot,
- Qrootctl,
- Qclone,
- Qcookies,
- Qclient,
- Qctl,
- Qbody,
- Qbodyext,
- Qcontenttype,
- Qpostbody,
- Qparsed,
- Qurl,
- Qscheme,
- Qschemedata,
- Quser,
- Qpasswd,
- Qhost,
- Qport,
- Qpath,
- Qquery,
- Qfragment,
- Qftptype,
- Qend,
-};
+ Ref;
-#define PATH(type, n) ((type)|((n)<<8))
-#define TYPE(path) ((int)(path) & 0xFF)
-#define NUM(path) ((uint)(path)>>8)
+ char request[16];
+ Url *baseurl;
+ Url *url;
+ Key *hdr;
-Channel *creq;
-Channel *creqwait;
-Channel *cclunk;
-Channel *cclunkwait;
+ int obody; /* body opend */
+ int cbody; /* body closed */
+ Buq *qbody;
+};
-typedef struct Tab Tab;
-struct Tab
+struct Webfid
{
- char *name;
- ulong mode;
- int offset;
+ int level;
+
+ Client *client;
+ Key *key; /* copy for Qheader */
+ Buq *buq; /* reference for Qbody, Qpost */
};
-Tab tab[] =
-{
- "/", DMDIR|0555, 0,
- "ctl", 0666, 0,
- "clone", 0666, 0,
- "cookies", 0666, 0,
- "XXX", DMDIR|0555, 0,
- "ctl", 0666, 0,
- "body", 0444, 0,
- "XXX", 0444, 0,
- "contenttype", 0444, 0,
- "postbody", 0666, 0,
- "parsed", DMDIR|0555, 0,
- "url", 0444, offsetof(Url, url),
- "scheme", 0444, offsetof(Url, scheme),
- "schemedata", 0444, offsetof(Url, schemedata),
- "user", 0444, offsetof(Url, user),
- "passwd", 0444, offsetof(Url, passwd),
- "host", 0444, offsetof(Url, host),
- "port", 0444, offsetof(Url, port),
- "path", 0444, offsetof(Url, path),
- "query", 0444, offsetof(Url, query),
- "fragment", 0444, offsetof(Url, fragment),
- "ftptype", 0444, offsetof(Url, ftp.type),
+enum {
+ Qroot,
+ Qrctl,
+ Qclone,
+ Qclient,
+ Qctl,
+ Qbody,
+ Qpost,
+ Qparsed,
+ Qurl,
+ Qurlschm,
+ Qurluser,
+ Qurlpass,
+ Qurlhost,
+ Qurlport,
+ Qurlpath,
+ Qurlqwry,
+ Qurlfrag,
+ Qheader,
};
-ulong time0;
+static char *nametab[] = {
+ "/",
+ "ctl",
+ "clone",
+ nil,
+ "ctl",
+ "body",
+ "postbody",
+ "parsed",
+ "url",
+ "scheme",
+ "user",
+ "passwd",
+ "host",
+ "port",
+ "path",
+ "query",
+ "fragment",
+ nil,
+};
-static void
-fillstat(Dir *d, uvlong path, ulong length, char *ext)
+static char *mtpt;
+static char *service;
+static long time0;
+static char *user;
+static char *agent;
+static Client client[64];
+static int nclient;
+
+#define CLIENTID(c) ((int)(((Client*)(c)) - client))
+
+Client*
+newclient(void)
{
- Tab *t;
- int type;
- char buf[32];
+ Client *cl;
+ int i;
- memset(d, 0, sizeof(*d));
- d->uid = estrdup("web");
- d->gid = estrdup("web");
- d->qid.path = path;
- d->atime = d->mtime = time0;
- d->length = length;
- type = TYPE(path);
- t = &tab[type];
- if(type == Qbodyext) {
- snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
- d->name = estrdup(buf);
- }
- else if(t->name)
- d->name = estrdup(t->name);
- else{ /* client directory */
- snprint(buf, sizeof buf, "%ud", NUM(path));
- d->name = estrdup(buf);
+ for(i = 0; i < nclient; i++)
+ if(client[i].ref == 0)
+ break;
+ if(i >= nelem(client))
+ return nil;
+ if(i == nclient)
+ nclient++;
+ cl = &client[i];
+ incref(cl);
+
+ cl->request[0] = 0;
+ cl->baseurl = nil;
+ cl->url = nil;
+ cl->hdr = nil;
+ cl->qbody = nil;
+
+ return cl;
+}
+
+void
+freeclient(Client *cl)
+{
+ Key *k;
+
+ if(cl == nil || decref(cl))
+ return;
+
+ buclose(cl->qbody, 0);
+ bufree(cl->qbody);
+
+ while(k = cl->hdr){
+ cl->hdr = k->next;
+ free(k);
}
- d->qid.type = t->mode>>24;
- d->mode = t->mode;
+
+ freeurl(cl->url);
+ freeurl(cl->baseurl);
+
+ memset(cl, 0, sizeof(*cl));
}
-static void
-fsstat(Req *r)
+static Url*
+clienturl(Client *cl)
{
- fillstat(&r->d, r->fid->qid.path, 0, nil);
- respond(r, nil);
+ static Url nullurl;
+
+ if(cl->qbody && cl->qbody->url)
+ return cl->qbody->url;
+ if(cl->url)
+ return cl->url;
+ return &nullurl;
}
-static int
-rootgen(int i, Dir *d, void*)
+static void*
+wfaux(Webfid *f)
{
- char buf[32];
+ if(f->level < Qclient)
+ return nil;
+ else if(f->level < Qurl)
+ return f->client;
+ else if(f->level < Qheader)
+ return clienturl(f->client);
+ else
+ return f->key;
+}
- i += Qroot+1;
- if(i < Qclient){
- fillstat(d, i, 0, nil);
- return 0;
- }
- i -= Qclient;
- if(i < nclient){
- fillstat(d, PATH(Qclient, i), 0, nil);
- snprint(buf, sizeof buf, "%d", i);
- free(d->name);
- d->name = estrdup(buf);
- return 0;
+static void
+fsmkqid(Qid *q, int level, void *aux)
+{
+ q->type = 0;
+ q->vers = 0;
+ switch(level){
+ case Qroot:
+ case Qparsed:
+ case Qclient:
+ q->type = QTDIR;
+ default:
+ q->path = (level<<24) | (((uintptr)aux ^ time0) & 0x00ffffff);
}
- return -1;
}
-static int
-clientgen(int i, Dir *d, void *aux)
+static char*
+fshdrname(char *s)
{
- Client *c;
+ char *k, *w;
- c = aux;
- i += Qclient+1;
- if(i <= Qparsed){
- fillstat(d, PATH(i, c->num), 0, c->ext);
- return 0;
- }
- return -1;
+ for(k=w=s; *k; k++)
+ if(isalnum(*k))
+ *w++ = tolower(*k);
+ *w = 0;
+ return s;
}
static int
-parsedgen(int i, Dir *d, void *aux)
+urlstr(char *buf, int nbuf, Url *u, int level)
{
- Client *c;
+ char *s;
- c = aux;
- i += Qparsed+1;
- if(i < Qend){
- fillstat(d, PATH(i, c->num), 0, nil);
+ if(level == Qurl)
+ return snprint(buf, nbuf, "%U", u);
+ if(level == Qurlpath)
+ return snprint(buf, nbuf, "%s", Upath(u));
+ if((s = (&u->scheme)[level - Qurlschm]) == nil){
+ buf[0] = 0;
return 0;
}
- return -1;
+ return snprint(buf, nbuf, "%s", s);
}
+
static void
-fsread(Req *r)
+fsmkdir(Dir *d, int level, void *aux)
{
- char *s;
- char e[ERRMAX];
- Client *c;
- ulong path;
+ char buf[1024];
- path = r->fid->qid.path;
- switch(TYPE(path)){
- default:
- snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
- respond(r, e);
- break;
-
- case Qroot:
- dirread9p(r, rootgen, nil);
- respond(r, nil);
- break;
-
- case Qrootctl:
- globalctlread(r);
- break;
-
- case Qcookies:
- cookieread(r);
+ memset(d, 0, sizeof(*d));
+ fsmkqid(&d->qid, level, aux);
+ d->mode = 0444;
+ d->atime = d->mtime = time0;
+ d->uid = estrdup(user);
+ d->gid = estrdup(user);
+ d->muid = estrdup(user);
+ if(d->qid.type & QTDIR)
+ d->mode |= DMDIR | 0111;
+ switch(level){
+ case Qheader:
+ d->name = fshdrname(estrdup(((Key*)aux)->key));
+ d->length = strlen(((Key*)aux)->val);
break;
-
case Qclient:
- dirread9p(r, clientgen, client[NUM(path)]);
- respond(r, nil);
+ snprint(buf, sizeof(buf), "%d", CLIENTID(aux));
+ d->name = estrdup(buf);
break;
-
case Qctl:
- ctlread(r, client[NUM(path)]);
- break;
-
- case Qcontenttype:
- c = client[NUM(path)];
- if(c->contenttype == nil)
- r->ofcall.count = 0;
- else
- readstr(r, c->contenttype);
- respond(r, nil);
- break;
-
- case Qpostbody:
- c = client[NUM(path)];
- readbuf(r, c->postbody, c->npostbody);
- respond(r, nil);
- break;
-
- case Qbody:
- case Qbodyext:
- c = client[NUM(path)];
- if(c->iobusy){
- respond(r, "already have i/o pending");
- break;
+ case Qrctl:
+ case Qclone:
+ d->mode = 0666;
+ if(0){
+ case Qpost:
+ d->mode = 0222;
}
- c->iobusy = 1;
- sendp(c->creq, r);
- break;
+ default:
+ d->name = estrdup(nametab[level]);
+ if(level >= Qurl && level <= Qurlfrag)
+ d->length = urlstr(buf, sizeof(buf), (Url*)aux, level);
+ }
+}
- case Qparsed:
- dirread9p(r, parsedgen, client[NUM(path)]);
- respond(r, nil);
- break;
+static void
+fsattach(Req *r)
+{
+ Webfid *f;
- case Qurl:
- case Qscheme:
- case Qschemedata:
- case Quser:
- case Qpasswd:
- case Qhost:
- case Qport:
- case Qpath:
- case Qquery:
- case Qfragment:
- case Qftptype:
- c = client[NUM(path)];
- r->ofcall.count = 0;
- if(c->url != nil
- && (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil)
- readstr(r, s);
- respond(r, nil);
- break;
+ if(r->ifcall.aname && r->ifcall.aname[0]){
+ respond(r, "invalid attach specifier");
+ return;
}
+ f = emalloc(sizeof(*f));
+ f->level = Qroot;
+ fsmkqid(&r->fid->qid, f->level, wfaux(f));
+ r->ofcall.qid = r->fid->qid;
+ r->fid->aux = f;
+ respond(r, nil);
}
static void
-fswrite(Req *r)
+fsstat(Req *r)
{
- int m;
- ulong path;
- char e[ERRMAX], *buf, *cmd, *arg;
- Client *c;
+ Webfid *f;
- path = r->fid->qid.path;
- switch(TYPE(path)){
- default:
- snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
- respond(r, e);
- break;
+ f = r->fid->aux;
+ fsmkdir(&r->d, f->level, wfaux(f));
+ respond(r, nil);
+}
- case Qcookies:
- cookiewrite(r);
- break;
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+ Webfid *f;
+ int i, j;
- case Qrootctl:
- case Qctl:
- if(r->ifcall.count >= 1024){
- respond(r, "ctl message too long");
- return;
- }
- buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count);
- cmd = buf;
- arg = strpbrk(cmd, "\t ");
- if(arg){
- *arg++ = '\0';
- arg += strspn(arg, "\t ");
- }else
- arg = "";
- r->ofcall.count = r->ifcall.count;
- if(TYPE(path)==Qrootctl){
- if(!ctlwrite(r, &globalctl, cmd, arg)
- && !globalctlwrite(r, cmd, arg))
- respond(r, "unknown control command");
- }else{
- c = client[NUM(path)];
- if(!ctlwrite(r, &c->ctl, cmd, arg)
- && !clientctlwrite(r, c, cmd, arg))
- respond(r, "unknown control command");
- }
- free(buf);
- break;
+ if(!(fid->qid.type&QTDIR))
+ return "walk in non-directory";
- case Qpostbody:
- c = client[NUM(path)];
- if(c->bodyopened){
- respond(r, "cannot write postbody after opening body");
+ f = fid->aux;
+ if(strcmp(name, "..") == 0){
+ switch(f->level){
+ case Qroot:
break;
- }
- if(r->ifcall.offset >= 128*1024*1024){ /* >128MB is probably a mistake */
- respond(r, "offset too large");
+ case Qclient:
+ freeclient(f->client);
+ f->client = nil;
break;
+ default:
+ if(f->level > Qparsed)
+ f->level = Qparsed;
+ else
+ f->level = Qclient;
}
- m = r->ifcall.offset + r->ifcall.count;
- if(c->npostbody < m){
- c->postbody = erealloc(c->postbody, m);
- memset(c->postbody+c->npostbody, 0, m-c->npostbody);
- c->npostbody = m;
+ } else {
+ for(i=f->level+1; i < nelem(nametab); i++){
+ if(nametab[i]){
+ if(strcmp(name, nametab[i]) == 0)
+ break;
+ if(i == Qbody && strncmp(name, "body.", 5) == 0)
+ break;
+ }
+ if(i == Qclient){
+ j = atoi(name);
+ if(j >= 0 && j < nclient){
+ f->client = &client[j];
+ incref(f->client);
+ break;
+ }
+ }
+ if(i == Qheader && f->client && f->client->qbody){
+ char buf[128];
+ Key *k;
+
+ for(k = f->client->qbody->hdr; k; k = k->next){
+ nstrcpy(buf, k->key, sizeof(buf));
+ if(!strcmp(name, fshdrname(buf)))
+ break;
+ }
+ if(k != nil){
+ /* need to copy as key is owned by qbody wich might go away */
+ f->key = addkey(0, k->key, k->val);
+ break;
+ }
+ }
}
- memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
- r->ofcall.count = r->ifcall.count;
- respond(r, nil);
- break;
+ if(i >= nelem(nametab))
+ return "directory entry not found";
+ f->level = i;
}
+ fsmkqid(qid, f->level, wfaux(f));
+ fid->qid = *qid;
+ return nil;
+}
+
+static char*
+fsclone(Fid *oldfid, Fid *newfid)
+{
+ Webfid *f, *o;
+
+ o = oldfid->aux;
+ if(o == nil || o->key || o->buq)
+ return "bad fid";
+ f = emalloc(sizeof(*f));
+ memmove(f, o, sizeof(*f));
+ if(f->client)
+ incref(f->client);
+ newfid->aux = f;
+ return nil;
}
static void
fsopen(Req *r)
{
- static int need[4] = { 4, 2, 6, 1 };
- ulong path;
- int n;
- Client *c;
- Tab *t;
+ Webfid *f;
+ Client *cl;
- /*
- * lib9p already handles the blatantly obvious.
- * we just have to enforce the permissions we have set.
- */
- path = r->fid->qid.path;
- t = &tab[TYPE(path)];
- n = need[r->ifcall.mode&3];
- if((n&t->mode) != n){
- respond(r, "permission denied");
- return;
- }
-
- switch(TYPE(path)){
- case Qcookies:
- cookieopen(r);
- break;
-
- case Qpostbody:
- c = client[NUM(path)];
- c->havepostbody++;
- c->ref++;
- respond(r, nil);
+ f = r->fid->aux;
+ cl = f->client;
+ switch(f->level){
+ case Qclone:
+ if((cl = newclient()) == nil){
+ respond(r, "no more clients");
+ return;
+ }
+ f->level = Qctl;
+ f->client = cl;
+ fsmkqid(&r->fid->qid, f->level, wfaux(f));
+ r->ofcall.qid = r->fid->qid;
break;
-
+ case Qpost:
+ if(cl->qbody && !cl->cbody){
+ Inuse:
+ respond(r, "client in use");
+ return;
+ }
case Qbody:
- case Qbodyext:
- c = client[NUM(path)];
- if(c->url == nil){
- respond(r, "url is not yet set");
- break;
+ if(cl->obody)
+ goto Inuse;
+ if(cl->cbody){
+ bufree(cl->qbody);
+ cl->qbody = nil;
+ cl->cbody = 0;
}
- c->bodyopened = 1;
- c->ref++;
- sendp(c->creq, r);
- break;
+ if(cl->qbody == nil){
+ char *m;
- case Qclone:
- n = newclient(0);
- path = PATH(Qctl, n);
- r->fid->qid.path = path;
- r->ofcall.qid.path = path;
- if(fsdebug)
- fprint(2, "open clone => path=%lux\n", path);
- t = &tab[Qctl];
- /* fall through */
- default:
- if(t-tab >= Qclient)
- client[NUM(path)]->ref++;
- respond(r, nil);
- break;
+ if(cl->url == nil){
+ respond(r, "no url set");
+ return;
+ }
+ cl->qbody = bualloc(16*1024);
+ if(f->level != Qbody){
+ f->buq = bualloc(64*1024);
+ if(!lookkey(cl->hdr, "Content-Type"))
+ cl->hdr = addkey(cl->hdr, "Content-Type",
+ "application/x-www-form-urlencoded");
+ m = "POST";
+ } else
+ m = "GET";
+ if(cl->request[0])
+ m = cl->request;
+
+ /*
+ * some sites give a 403 Forbidden if we dont include
+ * a meaningless Accept header in the request.
+ */
+ if(!lookkey(cl->hdr, "Accept"))
+ cl->hdr = addkey(cl->hdr, "Accept", "*/*");
+
+ if(!lookkey(cl->hdr, "Connection"))
+ cl->hdr = addkey(cl->hdr, "Connection", "keep-alive");
+
+ if(agent && !lookkey(cl->hdr, "User-Agent"))
+ cl->hdr = addkey(cl->hdr, "User-Agent", agent);
+
+ http(m, cl->url, cl->hdr, cl->qbody, f->buq);
+ cl->request[0] = 0;
+ cl->url = nil;
+ cl->hdr = nil;
+ }
+ if(f->buq)
+ break;
+ cl->obody = 1;
+ incref(cl->qbody);
+ bureq(f->buq = cl->qbody, r);
+ return;
}
+ respond(r, nil);
}
-static void
-fsdestroyfid(Fid *fid)
+static int
+rootgen(int i, Dir *d, void *)
{
- sendp(cclunk, fid);
- recvp(cclunkwait);
+ i += Qroot+1;
+ if(i < Qclient){
+ fsmkdir(d, i, 0);
+ return 0;
+ }
+ i -= Qclient;
+ if(i < nclient){
+ fsmkdir(d, Qclient, &client[i]);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+clientgen(int i, Dir *d, void *aux)
+{
+ i += Qclient+1;
+ if(i > Qparsed){
+ Client *cl = aux;
+ Key *k;
+
+ i -= Qparsed+1;
+ if(cl == nil || cl->qbody == nil)
+ return -1;
+ for(k = cl->qbody->hdr; i > 0 && k; i--, k = k->next)
+ ;
+ if(k == nil || i > 0)
+ return -1;
+ i = Qheader;
+ aux = k;
+ }
+ fsmkdir(d, i, aux);
+ return 0;
+}
+
+static int
+parsedgen(int i, Dir *d, void *aux)
+{
+ i += Qparsed+1;
+ if(i > Qurlfrag)
+ return -1;
+ fsmkdir(d, i, aux);
+ return 0;
}
static void
-fsattach(Req *r)
+fsread(Req *r)
{
- if(r->ifcall.aname && r->ifcall.aname[0]){
- respond(r, "invalid attach specifier");
+ char buf[1024];
+ Webfid *f;
+
+ f = r->fid->aux;
+ switch(f->level){
+ case Qroot:
+ dirread9p(r, rootgen, nil);
+ respond(r, nil);
+ return;
+ case Qclient:
+ dirread9p(r, clientgen, f->client);
+ respond(r, nil);
+ return;
+ case Qparsed:
+ dirread9p(r, parsedgen, clienturl(f->client));
+ respond(r, nil);
+ return;
+ case Qrctl:
+ snprint(buf, sizeof(buf), "useragent %s\ntimeout %d\n", agent, timeout);
+ String:
+ readstr(r, buf);
+ respond(r, nil);
+ return;
+ case Qctl:
+ snprint(buf, sizeof(buf), "%d\n", CLIENTID(f->client));
+ goto String;
+ case Qheader:
+ snprint(buf, sizeof(buf), "%s", f->key->val);
+ goto String;
+ case Qurl:
+ case Qurlschm:
+ case Qurluser:
+ case Qurlpass:
+ case Qurlhost:
+ case Qurlport:
+ case Qurlpath:
+ case Qurlqwry:
+ case Qurlfrag:
+ urlstr(buf, sizeof(buf), clienturl(f->client), f->level);
+ goto String;
+ case Qbody:
+ bureq(f->buq, r);
return;
}
- r->fid->qid.path = PATH(Qroot, 0);
- r->fid->qid.type = QTDIR;
- r->fid->qid.vers = 0;
- r->ofcall.qid = r->fid->qid;
- respond(r, nil);
+ respond(r, "not implemented");
}
static char*
-fswalk1(Fid *fid, char *name, Qid *qid)
+rootctl(Srv *fs, char *ctl, char *arg)
{
- int i, n;
- ulong path;
- char buf[32], *ext;
+ Url *u;
- path = fid->qid.path;
- if(!(fid->qid.type&QTDIR))
- return "walk in non-directory";
+ if(debug)
+ fprint(2, "rootctl: %q %q\n", ctl, arg);
- if(strcmp(name, "..") == 0){
- switch(TYPE(path)){
- case Qparsed:
- qid->path = PATH(Qclient, NUM(path));
- qid->type = tab[Qclient].mode>>24;
- return nil;
- case Qclient:
- case Qroot:
- qid->path = PATH(Qroot, 0);
- qid->type = tab[Qroot].mode>>24;
- return nil;
- default:
- return "bug in fswalk1";
- }
+ if(!strcmp(ctl, "useragent")){
+ free(agent);
+ if(arg && *arg)
+ agent = estrdup(arg);
+ else
+ agent = nil;
+ return nil;
+ }
+
+ if(!strcmp(ctl, "flushauth")){
+ u = nil;
+ if(arg && *arg)
+ u = saneurl(url(arg, 0));
+ flushauth(u, 0);
+ freeurl(u);
+ return nil;
+ }
+
+ if(!strcmp(ctl, "timeout")){
+ if(arg && *arg)
+ timeout = atoi(arg);
+ else
+ timeout = 0;
+ if(timeout < 0)
+ timeout = 0;
+ return nil;
+ }
+
+ /* ppreemptive authentication only basic
+ * auth supported, ctl message of the form:
+ * preauth url realm
+ */
+ if(!strcmp(ctl, "preauth")){
+ char *a[3], buf[256];
+ int rc;
+
+ if(tokenize(arg, a, nelem(a)) != 2)
+ return "preauth - bad field count";
+ if((u = saneurl(url(a[0], 0))) == nil)
+ return "preauth - malformed url";
+ snprint(buf, sizeof(buf), "BASIC realm=\"%s\"", a[1]);
+ srvrelease(fs);
+ rc = authenticate(u, u, "GET", buf);
+ srvacquire(fs);
+ freeurl(u);
+ if(rc == -1)
+ return "preauth failed";
+ return nil;
}
- i = TYPE(path)+1;
- for(; i<nelem(tab); i++){
- if(i==Qclient){
- n = atoi(name);
- snprint(buf, sizeof buf, "%d", n);
- if(n < nclient && strcmp(buf, name) == 0){
- qid->path = PATH(i, n);
- qid->type = tab[i].mode>>24;
- return nil;
+ return "bad ctl message";
+}
+
+static char*
+clientctl(Client *cl, char *ctl, char *arg)
+{
+ char *p;
+ Url *u;
+ Key *k;
+
+ if(debug)
+ fprint(2, "clientctl: %q %q\n", ctl, arg);
+
+ if(!strcmp(ctl, "url")){
+ if((u = saneurl(url(arg, cl->baseurl))) == nil)
+ return "bad url";
+ freeurl(cl->url);
+ cl->url = u;
+ }
+ else if(!strcmp(ctl, "baseurl")){
+ if((u = url(arg, 0)) == nil)
+ return "bad baseurl";
+ freeurl(cl->baseurl);
+ cl->baseurl = u;
+ }
+ else if(!strcmp(ctl, "request")){
+ p = cl->request;
+ nstrcpy(p, arg, sizeof(cl->request));
+ for(; *p && isalpha(*p); p++)
+ *p = toupper(*p);
+ *p = 0;
+ }
+ else if(!strcmp(ctl, "headers")){
+ while(arg && *arg){
+ ctl = arg;
+ while(*ctl && strchr(whitespace, *ctl))
+ ctl++;
+ if(arg = strchr(ctl, '\n'))
+ *arg++ = 0;
+ if(k = parsehdr(ctl)){
+ k->next = cl->hdr;
+ cl->hdr = k;
}
- break;
}
- if(i==Qbodyext){
- ext = client[NUM(path)]->ext;
- snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
- if(strcmp(buf, name) == 0){
- qid->path = PATH(i, NUM(path));
- qid->type = tab[i].mode>>24;
- return nil;
+ }
+ else {
+ char buf[128], **t;
+ static char *tab[] = {
+ "User-Agent",
+ "Content-Type",
+ nil,
+ };
+ for(t = tab; *t; t++){
+ nstrcpy(buf, *t, sizeof(buf));
+ if(!strcmp(ctl, fshdrname(buf))){
+ cl->hdr = delkey(cl->hdr, *t);
+ if(arg && *arg)
+ cl->hdr = addkey(cl->hdr, *t, arg);
+ break;
}
}
- else if(strcmp(name, tab[i].name) == 0){
- qid->path = PATH(i, NUM(path));
- qid->type = tab[i].mode>>24;
- return nil;
- }
- if(tab[i].mode&DMDIR)
- break;
+ if(*t == nil)
+ return "bad ctl message";
+ }
+ return nil;
+}
+
+static void
+fswrite(Req *r)
+{
+ int n;
+ Webfid *f;
+ char *s, *t;
+
+ f = r->fid->aux;
+ switch(f->level){
+ case Qrctl:
+ case Qctl:
+ n = r->ofcall.count = r->ifcall.count;
+ s = emalloc(n+1);
+ memmove(s, r->ifcall.data, n);
+ while(n > 0 && strchr("\r\n", s[n-1]))
+ n--;
+ s[n] = 0;
+ t = s;
+ while(*t && strchr(whitespace, *t)==0)
+ t++;
+ while(*t && strchr(whitespace, *t))
+ *t++ = 0;
+ if(f->level == Qctl)
+ t = clientctl(f->client, s, t);
+ else
+ t = rootctl(r->srv, s, t);
+ free(s);
+ respond(r, t);
+ return;
+ case Qpost:
+ bureq(f->buq, r);
+ return;
}
- return "directory entry not found";
+ respond(r, "not implemented");
}
static void
fsflush(Req *r)
{
- Req *or;
- int t;
- Client *c;
- ulong path;
-
- or=r;
- while(or->ifcall.type==Tflush)
- or = or->oldreq;
-
- if(or->ifcall.type != Tread && or->ifcall.type != Topen)
- abort();
-
- path = or->fid->qid.path;
- t = TYPE(path);
- if(t != Qbody && t != Qbodyext)
- abort();
-
- c = client[NUM(path)];
- sendp(c->creq, r);
- iointerrupt(c->io);
+ Webfid *f;
+ Req *o;
+
+ if(o = r->oldreq)
+ if(f = o->fid->aux)
+ buflushreq(f->buq, o);
+ respond(r, nil);
}
static void
-fsthread(void*)
+fsdestroyfid(Fid *fid)
{
- ulong path;
- Alt a[3];
- Fid *fid;
- Req *r;
-
- threadsetname("fsthread");
- plumbstart();
-
- a[0].op = CHANRCV;
- a[0].c = cclunk;
- a[0].v = &fid;
- a[1].op = CHANRCV;
- a[1].c = creq;
- a[1].v = &r;
- a[2].op = CHANEND;
-
- for(;;){
- switch(alt(a)){
- case 0:
- path = fid->qid.path;
- if(TYPE(path)==Qcookies)
- cookieclunk(fid);
- if(fid->omode != -1 && TYPE(path) >= Qclient)
- closeclient(client[NUM(path)]);
- sendp(cclunkwait, nil);
- break;
- case 1:
- switch(r->ifcall.type){
- case Tattach:
- fsattach(r);
- break;
- case Topen:
- fsopen(r);
- break;
- case Tread:
- fsread(r);
- break;
- case Twrite:
- fswrite(r);
- break;
- case Tstat:
- fsstat(r);
- break;
- case Tflush:
- fsflush(r);
- break;
- default:
- respond(r, "bug in fsthread");
- break;
+ Webfid *f;
+
+ if(f = fid->aux){
+ fid->aux = nil;
+ if(f->buq){
+ buclose(f->buq, 0);
+ if(f->client->qbody == f->buq){
+ f->client->obody = 0;
+ f->client->cbody = 1;
}
- sendp(creqwait, 0);
- break;
+ bufree(f->buq);
}
+ if(f->key)
+ free(f->key);
+ freeclient(f->client);
+ free(f);
}
}
static void
-fssend(Req *r)
+fsstart(Srv*)
{
- sendp(creq, r);
- recvp(creqwait); /* avoids need to deal with spurious flushes */
+ /* drop reference to old webfs mount */
+ if(mtpt != nil)
+ unmount(nil, mtpt);
}
-void
-initfs(void)
+static void
+fsend(Srv*)
{
- time0 = time(0);
- creq = chancreate(sizeof(void*), 0);
- creqwait = chancreate(sizeof(void*), 0);
- cclunk = chancreate(sizeof(void*), 0);
- cclunkwait = chancreate(sizeof(void*), 0);
- procrfork(fsthread, nil, STACK, RFNAMEG);
+ postnote(PNGROUP, getpid(), "shutdown");
+ exits(nil);
}
+Srv fs =
+{
+ .start=fsstart,
+ .attach=fsattach,
+ .stat=fsstat,
+ .walk1=fswalk1,
+ .clone=fsclone,
+ .open=fsopen,
+ .read=fsread,
+ .write=fswrite,
+ .flush=fsflush,
+ .destroyfid=fsdestroyfid,
+ .end=fsend,
+};
+
void
-takedown(Srv*)
+usage(void)
{
- closecookies();
- threadexitsall("done");
+ fprint(2, "usage: %s [-Dd] [-A useragent] [-T timeout] [-m mtpt] [-s service]\n", argv0);
+ exits("usage");
}
-Srv fs =
+void
+main(int argc, char *argv[])
{
-.attach= fssend,
-.destroyfid= fsdestroyfid,
-.walk1= fswalk1,
-.open= fssend,
-.read= fssend,
-.write= fssend,
-.stat= fssend,
-.flush= fssend,
-.end= takedown,
-};
+ char *s;
+
+ quotefmtinstall();
+ fmtinstall('U', Ufmt);
+ fmtinstall('N', Nfmt);
+ fmtinstall(']', Mfmt);
+ fmtinstall('E', Efmt);
+ fmtinstall('[', encodefmt);
+ fmtinstall('H', encodefmt);
+
+ mtpt = "/mnt/web";
+ user = getuser();
+ time0 = time(0);
+ timeout = 10000;
+
+ ARGBEGIN {
+ case 'D':
+ chatty9p++;
+ break;
+ case 'A':
+ agent = EARGF(usage());
+ break;
+ case 'T':
+ timeout = atoi(EARGF(usage()));
+ if(timeout < 0)
+ timeout = 0;
+ break;
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ case 's':
+ service = EARGF(usage());
+ break;
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ rfork(RFNOTEG);
+ if(agent == nil)
+ agent = "Mozilla/5.0 (compatible; hjdicks)";
+ agent = estrdup(agent);
+
+ if(s = getenv("httpproxy")){
+ proxy = saneurl(url(s, 0));
+ if(proxy == nil || strcmp(proxy->scheme, "http") && strcmp(proxy->scheme, "https"))
+ sysfatal("invalid httpproxy url: %s", s);
+ free(s);
+ }
+
+ postmountsrv(&fs, service, mtpt, MREPL);
+ exits(nil);
+}