new webfs, rc based hget

This commit is contained in:
cinap_lenrek 2012-01-11 16:17:54 +01:00
parent 62fb4f9717
commit 75e1ef0ab6
24 changed files with 2412 additions and 6198 deletions

69
rc/bin/hget Executable file
View file

@ -0,0 +1,69 @@
#!/bin/rc
argv0=$0
fn usage {
echo usage: $argv0 [ -o file ] [ -p body ] [ -r header ] [ -m method ] [ -b baseurl ] url >[2=1]
exit usage
}
s=0
o=()
p=()
r=()
m=()
b=()
while(~ $1 -*){
switch($1){
case -o
o=$2
shift
case -p
p=$2
shift
case -r
r=($r $2)
shift
case -m
m=$2
shift
case -b
b=$2
shift
case *
usage
}
shift
}
if(! ~ $#* 1)
usage
if(! ~ $#o 0){
if(! ~ $#o 1)
usage
if(test -s $o)
s=`{ls -l $o | awk '{print $6}'}
}
if(! ~ $s 0)
r=($r 'Range: bytes='^$s^'-')
<>/mnt/web/clone {
d=/mnt/web/^`{sed 1q}
if(~ $#b 1)
echo -n baseurl $b >[1=0]
echo -n url $1 >[1=0]
for(i in $r)
echo -n headers $i >[1=0]
if(~ $#m 1)
echo -n request $m >[1=0]
if(~ $#p 1)
cat <$p >$d/postbody
<$d/body {
if(~ $#o 1){
l=`{cat $d/contentlength >[2]/dev/null}
x=`{awk 'BEGIN{FS=" |-"}/^bytes ([0-9]+)\-/{print $2}' \
$d/contentrange >[2]/dev/null}
if(~ $s $l && ~ $#x 0)
exit
if(~ $s $x)
exec cat >>$o
exec cat >$o
}
exec cat
}
}

View file

@ -9,9 +9,15 @@ font = /lib/font/bit/pelm/euro.9.font
fn cd { builtin cd $* && awd } # for acme
switch($service){
case terminal
if(! test -w $home/lib/webcookies){
touch /tmp/webcookies
webcookies -f /tmp/webcookies
}
if not {
webcookies
}
webfs
plumber
touch /tmp/webcookies
webfs -c /tmp/webcookies
echo -n accelerated > '#m/mousectl'
echo -n 'res 3' > '#m/mousectl'
prompt=('term% ' ' ')

View file

@ -10,10 +10,11 @@ cd $home
x='$'
mkdir bin bin/rc bin/mips bin/386 bin/power bin/arm
mkdir lib tmp
touch lib/webcookies
chmod 600 lib/webcookies
chmod +t tmp
bind -qc /n/other/usr/$user/tmp $home/tmp
bind -c $home/tmp /tmp
mail -c
auth/cron -c
@ -26,6 +27,8 @@ bind -c $x^home/tmp /tmp
font = /lib/font/bit/pelm/euro.9.font
switch($x^service){
case terminal
webcookies
webfs
plumber
startupasfs
echo -n accelerated > '#m/mousectl'

View file

@ -4,19 +4,20 @@ hget \- retrieve a web page corresponding to a url
.SH SYNOPSIS
.B hget
[
.B -dhv
] [
.B -o
.I ofile
.I file
] [
.B -p
.I body
] [
.B -x
.I netmntpt
] [
.B -r
.I header
] [
.B -m
.I method
] [
.B -b
.I baseurl
]
.I url
.SH DESCRIPTION
@ -26,7 +27,16 @@ retrieves the web page specified by the URL
and writes it, absent the
.B -o
option, to standard output.
The known URL types are: http and ftp.
.PP
The
.I url
can be a relative path like
.B ../index.html
if a absolute
.I baseurl
was specified with the
.B -b
option.
.PP
If
.I url
@ -47,40 +57,22 @@ but incomplete,
will fetch the missing bytes.
.PP
Option
.B -h
causes HTTP headers to be printed to standard output
in addition to the transferred web page.
.PP
Option
.B -r
sends an arbitrary HTTP
.IR header .
.PP
Option
.B -d
turns on debugging written to standard error.
.PP
Normally,
.I hget
uses the IP stack mounted under
.BR /net .
The
.B -x
option can be used to specify the mount point of
a different IP stack to use.
.PP
Option
.B -v
writes progress lines to standard error once a second.
Each line contains two numbers, the bytes transferred so
far and the total length to be transferred.
.PP
If the environment variable
.B httpproxy
is set, it is used as a URL denoting an HTTP proxy server.
All HTTP accesses use this server to get the page instead of
calling the destination server.
.B -m
overrides the HTTP method used for the request.
.SH SOURCE
.B /sys/src/cmd/hget.c
.B /rc/bin/hget
.SH "SEE ALSO"
.IR webfs (4),
.IR ftpfs (4)
.SH DIAGNOSTICS
.I Hget
requires
.IR webfs (4)
service mounted on
.B /mnt/web
to work.

View file

@ -147,20 +147,8 @@ If
.B cookiefs
decides not to accept the cookie (as outlined in
RFC2109, section 4.3.4), no indication is given.
.PP
.IR Hget (1)
uses
.BR /mnt/webcookies/http ,
when it exists, to manage cookie state.
.I Webfs
does not (yet).
.SH SOURCE
.B /sys/src/cmd/webcookies.c
.SH SEE ALSO
.IR webfs (4),
.IR hget (1)
.SH BUGS
It's not clear what the relationship between
.I cookiefs
and something like
.I webfs
should be.

View file

@ -4,10 +4,6 @@ webfs \- world wide web file system
.SH SYNOPSIS
.B webfs
[
.B -c
.I cookiefile
]
[
.B -m
.I mtpt
]
@ -26,10 +22,15 @@ mounts itself at
.BR /mnt/web ),
and, if
.I service
is specified, will post a service file descriptor
in
is specified, will post a service file descriptor in
.BR /srv/\fIservice .
.PP
If the enviroment variable
.B httpproxy
is set, all HTTP request initiated by
.I webfs
will be made thru that proxy url.
.PP
.I Webfs
presents a three-level file system suggestive
of the network protocol hierarchies
@ -37,13 +38,12 @@ of the network protocol hierarchies
and
.IR ether (3).
.PP
The top level contains three files:
The top level contains the files files:
.BR ctl ,
.BR cookies ,
and
.BR clone .
.PP
The
The top level
.B ctl
file is used to maintain parameters global to the instance of
.IR webfs .
@ -53,72 +53,6 @@ file yields the current values of the parameters.
Writing strings of the form
.RB `` attr " " value ''
sets a particular attribute.
Attributes are:
.TP
.B chatty9p
The
.B chatty9p
flag used by the 9P library, discussed in
.IR 9p (2).
.B 0
is no debugging,
.B 1
prints 9P message traces on standard error,
and values above
.B 1
present more debugging, at the whim of the library.
The default for this and the following debug flags is
.BR 0 .
.TP
.B fsdebug
This variable is the level of debugging output about the file system module.
.TP
.B cookiedebug
This variable is the level of debugging output about the cookie module.
.TP
.B urldebug
This variable is the level of debugging output about URL parsing.
.TP
.B acceptcookies
This flag controls whether to accept cookies presented by remote web servers.
(Cookies are described below, in the discussion of the
.B cookies
file.)
The values
.B on
and
.B off
are synonymous with
.B 1
and
.BR 0 .
The default is
.BR on .
.TP
.B sendcookies
This flag controls whether to present stored cookies to remote web servers.
The default is
.BR on .
.TP
.B redirectlimit
Web servers can respond to a request with a message
redirecting to another page.
.I Webfs
makes no effort to determine whether it is in an infinite
redirect loop.
Instead, it gives up after this many redirects.
The default is
.BR 10 .
.TP
.B useragent
.I Webfs
sends the value of this attribute in its
.B User-Agent:
header in its HTTP requests.
The default is
.RB `` "webfs/2.0 (plan 9)" .''
.PD
.PP
The top-level directory also contains
numbered directories corresponding to connections, which
may be used to fetch a single URL.
@ -131,23 +65,10 @@ After opening, the
.B clone
file is equivalent to the file
.IB n /ctl \fR.
A connection is assumed closed once all files in its directory
have been closed, and is then will be reallocated.
A connection is assumed closed once all files in its
directory have been closed, and is then will be reallocated.
.PP
Each connection has its own private set of
.BR acceptcookies ,
.BR sendcookies ,
.BR redirectlimit ,
and
.B useragent
variables, initialized to the defaults set in the
root's
.B ctl
file. The per-connection
.B ctl
file allows editing the variables for this particular connection.
.PP
Each connection also has a URL string variable
Each connection has a URL attribute
.B url
associated with it.
This URL may be an absolute URL such as
@ -156,153 +77,96 @@ or a relative URL such as
.IR ../index.html .
The
.B baseurl
string variable sets the URL against which relative URLs
attribute sets the URL against which relative URLs
are interpreted.
Once the URL has been set,
its pieces can be retrieved via individual files in the
Once the URL has been set by wrting to the
.B ctl
file of the connetcion, its pieces can be retrieved via
individual files in the
.B parsed
directory.
.I Webfs
parses the following URL syntaxes; names in italics are
the names of files in the
.B parsed
directory.
.IP
\fIscheme\f5:\fIschemedata
.br
\f5http://\fIhost\f5/\fIpath\fR[\f5?\fIquery\fR][\f5#\fIfragment\fR]
.br
\f5ftp://\fR[\fIuser\fR[\f5:\fIpassword\fR]\f5@\fR]\fP\f5\fIhost\f5/\fIpath\fR[\f5;type=\fIftptype\fR]
.br
\f5file:\fIpath
.LP
If there is associated data to be
posted with the request, it can be written to
directory:
.de UU
.TP
.B parsed/\fI\\$1
\\$2
..
.UU url http://pete:secret@www.example.com:8000/cgi/search?q=kittens#results
.UU scheme http
.UU user pete
.UU pass secret
.UU host www.example.com
.UU port 8000
.UU path /cgi/search
.UU query q=kittens
.UU fragment results
.PP
If there is associated data to be posted with the request,
it can be written to
.BR postbody .
Finally, opening
Opening
.B postbody
or
.B body
initiates the request.
initiates the request. If the request fails,
then opening the
.B body
or writing to
.B postbody
file will fail and return a error string.
.PP
When the
.B body
file has been opend, response headers appear
as files in the connection directory. For example
reading the
.B contenttype
file yields the MIME content type of the body data.
If the request was redirected, the URL represended
by the
.B parsed
directory will change to the final destination.
.PP
The resulting data may be read from
.B body
as it arrives.
After the request has been executed, the MIME content type
may be read from the
.PP
The following is a list of attributes that can be
set to to a connection prior initiating the request:
.TP
.B url,baseurl
See above.
.TP
.B useragent
Sets a custom useragent string to be used with the request.
.TP
.B contenttype
file.
.PP
The top-level
.B cookies
file contains the internal set of HTTP cookies, which
are used by HTTP servers to associate requests with persistent
state such as user profiles.
It may be edited as an ordinary text file.
Multiple instances of
.I webfs
and
.IR webcookies (4)
share cookies by keeping their internal set
consistent with the
.I cookiefile
(default
.BR $home/lib/webcookies ),
which has the same format.
.PP
These files contain one line per cookie;
each cookie comprises some number of
.IB attr = value
pairs.
Cookie attributes are:
Sets the MIME content type of the postbody.
.TP
.BI name= name
The name of the cookie on the remote server.
.B request
Usualy, the HTTP method used is
.B POST
when
.B postbody
file is opend first or
.B GET
otherwise. This can be overriden with the
.B request
attribute so send arbitrary HTTP requests.
.TP
.BI value= value
The value associated with that name on the remote server.
The actual data included when a cookie is sent back
to the server is
.IB \fR``\fIname = value\fR''
(where, confusingly,
.I name
and
.I value
are the values associated with the
.B name
and
.B value
attributes.
.TP
.BI domain= domain
If
.I domain
is an IP address, the cookie can only be used for URLs
with
.I host
equal to that IP address.
Otherwise,
.I domain
must be a pattern beginning with a dot, and
the cookie can only be used for URLs with a
.I host
having
.I domain
as a suffix.
For example, a cookie with
.B domain=.bell-labs.com
may be used on hosts
.I www.bell-labs.com
and
.IR www.research.bell-labs.com
(but not
.IR www.not-bell-labs.com ).
.TP
.BI path= path
The cookie can only be used for URLs with a path
beginning with
.IR path .
.TP
.BI version= version
The version of the HTTP cookie specification, specified by the server.
.TP
.BI comment= comment
A comment, specified by the server.
.TP
.BI expire= expire
The cookie expires at time
.IR expire ,
which is a decimal number of seconds since the epoch.
.TP
.B secure=1
The cookie may only be used over secure
.RB ( https )
connections.
Secure connections are currently unimplemented.
.TP
.B explicitdomain=1
The domain associated with this cookie was set by
the server (rather than inferred from a URL).
.TP
.B explicitpath=1
The path associated with this cookie was set by the
server (rather than inferred from a URL).
.TP
.B netscapestyle=1
The server presented the cookie in ``Netscape style,'' which
does not conform to the cookie standard, RFC2109.
It is assumed that when presenting the cookie to the server,
it must be sent back in Netscape style as well.
.PD
.B headers
Adds arbitrary HTTP headers to be send with
the request.
.SH EXAMPLE
.B /sys/src/cmd/webfs/webget.c
.B /rc/bin/hget
is a simple client.
.SH SOURCE
.B /sys/src/cmd/webfs
.SH SEE ALSO
.IR hget (1),
.IR webcookies (4)
.SH BUGS
It's not clear what the relationship between
.IR hget ,
.I webcookies
and
.I webfs
should be.
.SH "SEE ALSO"
.IR webcookies (4),
.IR hget (1)
.SH DIAGNOSTICS
For cookies to work,
.IR webcookies (4),
should be running and mounted on
.B /mnt/webcookies
otherwise cookies will be ignored.

File diff suppressed because it is too large Load diff

View file

@ -1,89 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <plumb.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
void
initibuf(Ibuf *b, Ioproc *io, int fd)
{
b->fd = fd;
b->io = io;
b->rp = b->wp = b->buf;
}
int
readibuf(Ibuf *b, char *buf, int len)
{
int n;
n = b->wp - b->rp;
if(n > 0){
if(n > len)
n = len;
memmove(buf, b->rp, n);
b->rp += n;
return n;
}
return ioreadn(b->io, b->fd, buf, len);
}
void
unreadline(Ibuf *b, char *line)
{
int i, n;
i = strlen(line);
n = b->wp - b->rp;
memmove(&b->buf[i+1], b->rp, n);
memmove(b->buf, line, i);
b->buf[i] = '\n';
b->rp = b->buf;
b->wp = b->rp+i+1+n;
}
int
readline(Ibuf *b, char *buf, int len)
{
int n;
char *p;
len--;
for(p = buf;;){
if(b->rp >= b->wp){
n = ioread(b->io, b->fd, b->wp, sizeof(b->buf)/2);
if(n < 0)
return -1;
if(n == 0)
break;
b->wp += n;
}
n = *b->rp++;
if(len > 0){
*p++ = n;
len--;
}
if(n == '\n')
break;
}
/* drop trailing white */
for(;;){
if(p <= buf)
break;
n = *(p-1);
if(n != ' ' && n != '\t' && n != '\r' && n != '\n')
break;
p--;
}
*p = 0;
return p-buf;
}

263
sys/src/cmd/webfs/buq.c Normal file
View file

@ -0,0 +1,263 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
static void
matchreq(Buq *q)
{
Req *r;
Buf *b;
int l;
while(r = q->rh){
if((b = q->bh) == nil){
if(q->closed){
if((q->rh = r->aux) == nil)
q->rt = &q->rh;
if(r->ifcall.type == Tread)
r->ofcall.count = 0;
respond(r, q->error);
continue;
}
break;
}
if((q->rh = r->aux) == nil)
q->rt = &q->rh;
if(r->ifcall.type == Topen){
respond(r, nil);
continue;
}
l = b->ep - b->rp;
if(l > r->ifcall.count)
l = r->ifcall.count;
memmove(r->ofcall.data, b->rp, l);
r->ofcall.count = l;
respond(r, nil);
b->rp += l;
q->size -= l;
if(b->rp >= b->ep){
if((q->bh = b->next) == nil)
q->bt = &q->bh;
if(r = b->wreq){
r->ofcall.count = r->ifcall.count;
respond(r, nil);
}
free(b);
}
}
rwakeupall(&q->rz);
}
int
buread(Buq *q, void *v, int l)
{
Req *r;
Buf *b;
qlock(q);
while((b = q->bh) == nil){
if(q->closed){
l = 0;
if(q->error){
werrstr("%s", q->error);
l = -1;
}
qunlock(q);
return l;
}
rsleep(&q->rz);
}
if(l > (b->ep - b->rp))
l = b->ep - b->rp;
memmove(v, b->rp, l);
b->rp += l;
q->size -= l;
rwakeup(&q->rz);
if(b->rp < b->ep){
qunlock(q);
return l;
}
if((q->bh = b->next) == nil)
q->bt = &q->bh;
qunlock(q);
if(r = b->wreq){
r->ofcall.count = r->ifcall.count;
respond(r, nil);
}
free(b);
return l;
}
int
buwrite(Buq *q, void *v, int l)
{
Buf *b;
b = emalloc(sizeof(*b) + l);
b->wreq = nil;
b->rp = b->end;
b->ep = b->rp + l;
memmove(b->rp, v, l);
b->next = nil;
qlock(q);
if(q->closed){
l = 0;
if(q->error){
werrstr("%s", q->error);
l = -1;
}
qunlock(q);
free(b);
return l;
}
*q->bt = b;
q->bt = &b->next;
q->size += l;
matchreq(q);
while(!q->closed && q->size >= q->limit)
rsleep(&q->rz);
qunlock(q);
return l;
}
void
buclose(Buq *q, char *error)
{
if(q == nil)
return;
qlock(q);
if(!q->closed){
if(error)
q->error = estrdup9p(error);
q->closed = 1;
matchreq(q);
}
qunlock(q);
}
Buq*
bualloc(int limit)
{
Buq *q;
q = emalloc(sizeof(*q));
q->limit = limit;
q->rt = &q->rh;
q->bt = &q->bh;
q->rz.l = q;
incref(q);
return q;
}
void
bufree(Buq *q)
{
Buf *b;
Key *k;
if(q == nil || decref(q))
return;
while(b = q->bh){
q->bh = b->next;
free(b);
}
freeurl(q->url);
while(k = q->hdr){
q->hdr = k->next;
free(k);
}
free(q->error);
free(q);
}
void
bureq(Buq *q, Req *r)
{
Buf *b;
int l;
switch(r->ifcall.type){
default:
respond(r, "bug in bureq");
return;
case Twrite:
l = r->ifcall.count;
if((q->size + l) < q->limit){
r->ofcall.count = buwrite(q, r->ifcall.data, r->ifcall.count);
respond(r, nil);
return;
}
b = emalloc(sizeof(*b));
b->wreq = r;
b->rp = (uchar*)r->ifcall.data;
b->ep = b->rp + l;
b->next = nil;
qlock(q);
*q->bt = b;
q->bt = &b->next;
q->size += l;
break;
case Tread:
case Topen:
r->aux = nil;
qlock(q);
*q->rt = r;
q->rt = (Req**)&r->aux;
break;
}
matchreq(q);
qunlock(q);
}
void
buflushreq(Buq *q, Req *r)
{
Buf **bb, *b;
Req **rr;
int l;
switch(r->ifcall.type){
default:
respond(r, "bug in bufflushreq");
return;
case Twrite:
qlock(q);
for(bb = &q->bh; b = *bb; bb = &b->next){
if(b->wreq != r)
continue;
/* fake successfull write */
l = b->ep - b->rp;
b = realloc(b, sizeof(*b) + l);
memmove(b->end, b->rp, l);
b->rp = b->end;
b->ep = b->rp + l;
b->wreq = nil;
*bb = b;
if(b->next == nil)
q->bt = &b->next;
r->ofcall.count = r->ifcall.count;
respond(r, nil);
break;
}
break;
case Topen:
case Tread:
qlock(q);
for(rr = &q->rh; *rr; rr = (Req**)&((*rr)->aux)){
if(*rr != r)
continue;
if((*rr = r->aux) == nil)
q->rt = rr;
respond(r, "interrupted");
break;
}
break;
}
qunlock(q);
}

View file

@ -1,411 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <plumb.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
int nclient;
Client **client;
static void clientthread(void*);
int
newclient(int plumbed)
{
int i;
Client *c;
for(i=0; i<nclient; i++)
if(client[i]->ref==0)
return i;
c = emalloc(sizeof(Client));
c->plumbed = plumbed;
c->creq = chancreate(sizeof(Req*), 8);
threadcreate(clientthread, c, STACK);
c->io = ioproc();
c->num = nclient;
c->ctl = globalctl;
clonectl(&c->ctl);
if(nclient%16 == 0)
client = erealloc(client, (nclient+16)*sizeof(client[0]));
client[nclient++] = c;
return nclient-1;
}
void
closeclient(Client *c)
{
if(--c->ref == 0){
if(c->bodyopened){
if(c->url && c->url->close)
(*c->url->close)(c);
c->bodyopened = 0;
}
free(c->contenttype);
c->contenttype = nil;
free(c->postbody);
c->postbody = nil;
freeurl(c->url);
c->url = nil;
freeurl(c->baseurl);
c->baseurl = nil;
free(c->redirect);
c->redirect = nil;
free(c->authenticate);
c->authenticate = nil;
c->npostbody = 0;
c->havepostbody = 0;
c->bodyopened = 0;
}
}
void
clonectl(Ctl *c)
{
if(c->useragent)
c->useragent = estrdup(c->useragent);
}
void
clientbodyopen(Client *c, Req *r)
{
char e[ERRMAX], *next, *frag;
int i, nauth;
Url *u;
nauth = 0;
next = nil;
for(i=0; i<=c->ctl.redirectlimit; i++){
if(c->url == nil){
werrstr("nil url");
goto Error;
}
if(c->url->open == nil){
werrstr("unsupported url type");
goto Error;
}
if(fsdebug)
fprint(2, "try %s\n", c->url->url);
if(c->url->open(c, c->url) < 0){
Error:
rerrstr(e, sizeof e);
if(next)
fprint(2, "next %s (but for error)\n", next);
free(next);
c->iobusy = 0;
if(r != nil)
r->fid->omode = -1;
closeclient(c); /* not opening */
if(r != nil)
respond(r, e);
return;
}
free(next);
next = c->redirect;
c->redirect = nil;
if(c->authenticate && nauth++ < 1){
if(c->url->close)
(*c->url->close)(c);
continue;
}
if(next == nil)
break;
if(i==c->ctl.redirectlimit){
werrstr("redirect limit reached");
goto Error;
}
if((u = parseurl(next, c->url)) == nil)
goto Error;
/* if there was a redirect, carry over the fragment */
if((frag = c->url->fragment) && u->fragment == nil){
u->fragment = estrdup(frag);
rewriteurl(u);
}
if(urldebug)
fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme);
if(u->ischeme == USunknown){
werrstr("redirect with unknown URL scheme");
goto Error;
}
if(u->ischeme == UScurrent){
werrstr("redirect to URL relative to current document");
goto Error;
}
if(c->url->close)
(*c->url->close)(c);
freeurl(c->url);
c->url = u;
}
free(next);
c->iobusy = 0;
if(r != nil)
respond(r, nil);
}
void
plumburl(char *url, char *base)
{
int i;
Client *c;
Url *ubase, *uurl;
ubase = nil;
if(base){
ubase = parseurl(base, nil);
if(ubase == nil)
return;
}
uurl = parseurl(url, ubase);
if(uurl == nil){
freeurl(ubase);
return;
}
i = newclient(1);
c = client[i];
c->ref++;
c->baseurl = ubase;
c->url = uurl;
sendp(c->creq, nil);
}
void
clientbodyread(Client *c, Req *r)
{
char e[ERRMAX];
if(c->url->read == nil){
respond(r, "unsupported url type");
return;
}
if(c->url->read(c, r) < 0){
rerrstr(e, sizeof e);
c->iobusy = 0;
respond(r, e);
return;
}
c->iobusy = 0;
respond(r, nil);
}
static void
clientthread(void *a)
{
Client *c;
Req *r;
c = a;
if(c->plumbed) {
recvp(c->creq);
if(c->url == nil){
fprint(2, "bad url got plumbed\n");
return;
}
clientbodyopen(c, nil);
replumb(c);
}
while((r = recvp(c->creq)) != nil){
if(fsdebug)
fprint(2, "clientthread %F\n", &r->ifcall);
switch(r->ifcall.type){
case Topen:
if(c->plumbed) {
c->plumbed = 0;
c->ref--; /* from plumburl() */
respond(r, nil);
}
else
clientbodyopen(c, r);
break;
case Tread:
clientbodyread(c, r);
break;
case Tflush:
respond(r, nil);
}
if(fsdebug)
fprint(2, "clientthread finished req\n");
}
}
enum
{
Bool,
Int,
String,
XRel,
XUrl,
Fn,
};
typedef struct Ctab Ctab;
struct Ctab {
char *name;
int type;
void *offset;
};
Ctab ctltab[] = {
"acceptcookies", Bool, (void*)offsetof(Ctl, acceptcookies),
"sendcookies", Bool, (void*)offsetof(Ctl, sendcookies),
"redirectlimit", Int, (void*)offsetof(Ctl, redirectlimit),
"useragent", String, (void*)offsetof(Ctl, useragent),
};
Ctab globaltab[] = {
"chatty9p", Int, &chatty9p,
"fsdebug", Int, &fsdebug,
"cookiedebug", Int, &cookiedebug,
"urldebug", Int, &urldebug,
"httpdebug", Int, &httpdebug,
};
Ctab clienttab[] = {
"baseurl", XUrl, (void*)offsetof(Client, baseurl),
"url", XRel, (void*)offsetof(Client, url),
};
static Ctab*
findcmd(char *cmd, Ctab *tab, int ntab)
{
int i;
for(i=0; i<ntab; i++)
if(strcmp(tab[i].name, cmd) == 0)
return &tab[i];
return nil;
}
static void
parseas(Req *r, char *arg, int type, void *a)
{
Url *u, *base;
char e[ERRMAX];
base = nil;
switch(type){
case Bool:
if(strcmp(arg, "on")==0 || strcmp(arg, "1")==0)
*(int*)a = 1;
else
*(int*)a = 0;
break;
case String:
free(*(char**)a);
*(char**)a = estrdup(arg);
break;
case XRel:
base = ((Client*)a)->baseurl;
case XUrl:
u = parseurl(arg, base);
if(u == nil){
snprint(e, sizeof e, "parseurl: %r");
respond(r, e);
return;
}
freeurl(*(Url**)a);
*(Url**)a = u;
break;
case Int:
if(strcmp(arg, "on")==0)
*(int*)a = 1;
else
*(int*)a = atoi(arg);
break;
}
respond(r, nil);
}
int
ctlwrite(Req *r, Ctl *ctl, char *cmd, char *arg)
{
void *a;
Ctab *t;
if((t = findcmd(cmd, ctltab, nelem(ctltab))) == nil)
return 0;
a = (void*)((uintptr)ctl+(uintptr)t->offset);
parseas(r, arg, t->type, a);
return 1;
}
int
clientctlwrite(Req *r, Client *c, char *cmd, char *arg)
{
void *a;
Ctab *t;
if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil)
return 0;
a = (void*)((uintptr)c+(uintptr)t->offset);
parseas(r, arg, t->type, a);
return 1;
}
int
globalctlwrite(Req *r, char *cmd, char *arg)
{
void *a;
Ctab *t;
if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil)
return 0;
a = t->offset;
parseas(r, arg, t->type, a);
return 1;
}
static void
ctlfmt(Ctl *c, char *s)
{
int i;
void *a;
char *t;
for(i=0; i<nelem(ctltab); i++){
a = (void*)((uintptr)c+(uintptr)ctltab[i].offset);
switch(ctltab[i].type){
case Bool:
s += sprint(s, "%s %s\n", ctltab[i].name, *(int*)a ? "on" : "off");
break;
case Int:
s += sprint(s, "%s %d\n", ctltab[i].name, *(int*)a);
break;
case String:
t = *(char**)a;
if(t != nil)
s += sprint(s, "%s %.*s%s\n", ctltab[i].name, utfnlen(t, 100), t, strlen(t)>100 ? "..." : "");
break;
}
}
}
void
ctlread(Req *r, Client *c)
{
char buf[1024];
sprint(buf, "%11d \n", c->num);
ctlfmt(&c->ctl, buf+strlen(buf));
readstr(r, buf);
respond(r, nil);
}
void
globalctlread(Req *r)
{
char buf[1024], *s;
int i;
s = buf;
for(i=0; i<nelem(globaltab); i++)
s += sprint(s, "%s %d\n", globaltab[i].name, *(int*)globaltab[i].offset);
ctlfmt(&globalctl, s);
readstr(r, buf);
respond(r, nil);
}

File diff suppressed because it is too large Load diff

View file

@ -1,104 +1,67 @@
typedef struct Client Client;
typedef struct Ctl Ctl;
typedef struct Ibuf Ibuf;
typedef struct Url Url;
typedef struct Buq Buq;
typedef struct Buf Buf;
typedef struct Key Key;
/* simple buffered i/o for network connections; shared by http, ftp */
struct Ibuf
{
int fd;
Ioproc *io;
char buf[4096];
char *rp, *wp;
};
typedef struct {
char *s1;
char *s2;
} Str2;
struct Ctl
{
int acceptcookies;
int sendcookies;
int redirectlimit;
char *useragent;
};
struct Client
{
Url *url;
Url *baseurl;
Ctl ctl;
Channel *creq; /* chan(Req*) */
int num;
int plumbed;
char *contenttype;
char *postbody;
char *redirect;
char *authenticate;
char *ext;
int npostbody;
int havepostbody;
int iobusy;
int bodyopened;
Ioproc *io;
int ref;
void *aux;
};
/*
* If ischeme is USunknown, then the given URL is a relative
* URL which references the "current document" in the context of the base.
* If this is the case, only the "fragment" and "url" members will have
* meaning, and the given URL structure may not be used as a base URL itself.
*/
enum
{
USunknown,
UShttp,
UShttps,
USftp,
USfile,
UScurrent,
};
/* 9p */
typedef struct Req Req;
struct Url
{
int ischeme;
char* url;
char* scheme;
int (*open)(Client*, Url*);
int (*read)(Client*, Req*);
void (*close)(Client*);
char* schemedata;
char* authority;
char* user;
char* passwd;
char* host;
char* port;
char* path;
char* query;
char* fragment;
union {
struct {
char *page_spec;
} http;
struct {
char *path_spec;
char *type;
} ftp;
};
char *scheme;
char *user;
char *pass;
char *host;
char *port;
char *path;
char *query;
char *fragment;
};
enum
struct Buf
{
STACK = 32*1024, /* was 16*1024; there are big arrays on the stack */
Buf *next;
uchar *rp;
uchar *ep;
Req *wreq;
uchar end[];
};
extern Client** client;
extern int cookiedebug;
extern Srv fs;
extern int fsdebug;
extern Ctl globalctl;
extern int nclient;
extern int urldebug;
extern int httpdebug;
extern char* status[];
struct Key
{
Key *next;
char *val;
char key[];
};
struct Buq
{
Ref;
QLock;
Url *url;
Key *hdr;
char *error;
int closed;
int limit;
int size;
/* write buffers */
Buf *bh;
Buf **bt;
/* read requests */
Req *rh;
Req **rt;
Rendez rz;
};
int debug;
Url *proxy;

View file

@ -1,62 +1,35 @@
/* buf.c */
void initibuf(Ibuf*, Ioproc*, int);
int readibuf(Ibuf*, char*, int);
void unreadline(Ibuf*, char*);
int readline(Ibuf*, char*, int);
/* sub */
void* emalloc(int n);
char* estrdup(char *s);
/* client.c */
int newclient(int);
void closeclient(Client*);
void clonectl(Ctl*);
int ctlwrite(Req*, Ctl*, char*, char*);
int clientctlwrite(Req*, Client*, char*, char*);
int globalctlwrite(Req*, char*, char*);
void ctlread(Req*, Client*);
void globalctlread(Req*);
void plumburl(char*, char*);
Key* addkey(Key *h, char *key, char *val);
Key* delkey(Key *h, char *key);
char* lookkey(Key *k, char *key);
Key* parsehdr(char *s);
char* unquote(char *s, char **ps);
/* cookies.c */
void cookieread(Req*);
void cookiewrite(Req*);
void cookieopen(Req*);
void cookieclunk(Fid*);
void initcookies(char*);
void closecookies(void);
void httpsetcookie(char*, char*, char*);
char* httpcookies(char*, char*, int);
/* url */
#pragma varargck type "U" Url*
#pragma varargck type "E" Str2
/* fs.c */
void initfs(void);
int Efmt(Fmt*);
int Ufmt(Fmt*);
char* Upath(Url *);
Url* url(char *s, Url *b);
Url* saneurl(Url *u);
int matchurl(Url *u, Url *s);
void freeurl(Url *u);
/* http.c */
int httpopen(Client*, Url*);
int httpread(Client*, Req*);
void httpclose(Client*);
/* buq */
int buread(Buq *q, void *v, int l);
int buwrite(Buq *q, void *v, int l);
void buclose(Buq *q, char *error);
Buq* bualloc(int limit);
void bufree(Buq *q);
/* io.c */
int iotlsdial(Ioproc*, char*, char*, char*, int*, int);
int ioprint(Ioproc*, int, char*, ...);
#pragma varargck argpos ioprint 3
void bureq(Buq *q, Req *r);
void buflushreq(Buq *q, Req *r);
/* plumb.c */
void plumbinit(void);
void plumbstart(void);
void replumb(Client*);
/* url.c */
Url* parseurl(char*, Url*);
void freeurl(Url*);
void rewriteurl(Url*);
int seturlquery(Url*, char*);
Url* copyurl(Url*);
char* escapeurl(char*, char *);
char* unescapeurl(char*, char *);
void initurl(void);
/* util.c */
char* estrdup(char*);
char* estrmanydup(char*, ...);
char* estredup(char*, char*);
void* emalloc(uint);
void* erealloc(void*, uint);
char* strlower(char*);
/* http */
void flushauth(Url *u, char *t);
void http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,86 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <plumb.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include <mp.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
static long
_iovfprint(va_list *arg)
{
int fd;
char *fmt;
va_list arg2;
fd = va_arg(*arg, int);
fmt = va_arg(*arg, char*);
arg2 = va_arg(*arg, va_list);
return vfprint(fd, fmt, arg2);
}
int
iovfprint(Ioproc *io, int fd, char *fmt, va_list arg)
{
return iocall(io, _iovfprint, fd, fmt, arg);
}
int
ioprint(Ioproc *io, int fd, char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = iovfprint(io, fd, fmt, arg);
va_end(arg);
return n;
}
static long
_iotlsdial(va_list *arg)
{
char *addr, *local, *dir;
int *cfdp, fd, tfd, usetls;
TLSconn conn;
addr = va_arg(*arg, char*);
local = va_arg(*arg, char*);
dir = va_arg(*arg, char*);
cfdp = va_arg(*arg, int*);
usetls = va_arg(*arg, int);
fd = dial(addr, local, dir, cfdp);
if(fd < 0)
return -1;
if(!usetls)
return fd;
memset(&conn, 0, sizeof conn);
/* does no good, so far anyway */
// conn.chain = readcertchain("/sys/lib/ssl/vsignss.pem");
tfd = tlsClient(fd, &conn);
close(fd);
if(tfd < 0)
fprint(2, "%s: tlsClient: %r\n", argv0);
else {
/* BUG: check cert here? */
if(conn.cert)
free(conn.cert);
if(conn.sessionID)
free(conn.sessionID);
}
return tfd;
}
int
iotlsdial(Ioproc *io, char *addr, char *local, char *dir, int *cfdp, int usetls)
{
return iocall(io, _iotlsdial, addr, local, dir, cfdp, usetls);
}

View file

@ -1,67 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <plumb.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
char *cookiefile;
char *mtpt = "/mnt/web";
char *service;
Ctl globalctl =
{
1, /* accept cookies */
1, /* send cookies */
10, /* redirect limit */
"webfs/2.0 (plan 9)" /* user agent */
};
void
usage(void)
{
fprint(2, "usage: webfs [-c cookies] [-m mtpt] [-s service]\n");
threadexitsall("usage");
}
#include <pool.h>
void
threadmain(int argc, char **argv)
{
rfork(RFNOTEG);
ARGBEGIN{
case 'd':
mainmem->flags |= POOL_PARANOIA|POOL_ANTAGONISM;
break;
case 'D':
chatty9p++;
break;
case 'c':
cookiefile = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
service = EARGF(usage());
break;
default:
usage();
}ARGEND
quotefmtinstall();
if(argc != 0)
usage();
plumbinit();
globalctl.useragent = estrdup(globalctl.useragent);
initcookies(cookiefile);
initurl();
initfs();
threadpostmountsrv(&fs, service, mtpt, MREPL);
threadexits(nil);
}

View file

@ -1,35 +1,8 @@
</$objtype/mkfile
BIN=/$objtype/bin
TARG=webfs
SCHEMEOFILES=\
file.$O\
ftp.$O\
http.$O\
OFILES=\
buf.$O\
client.$O\
cookies.$O\
fs.$O\
http.$O\
io.$O\
main.$O\
plumb.$O\
url.$O\
util.$O\
# $SCHEMEOFILES
HFILES=\
dat.h\
fns.h\
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
${TARG:%=/386/bin/%}\
HFILES=fns.h dat.h
OFILES=sub.$O url.$O buq.$O http.$O fs.$O
</sys/src/cmd/mkone

View file

@ -1,165 +0,0 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <plumb.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
static int plumbsendfd;
static int plumbwebfd;
static Channel *plumbchan;
static void plumbwebproc(void*);
static void plumbwebthread(void*);
static void plumbsendproc(void*);
void
plumbinit(void)
{
plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
plumbwebfd = plumbopen("web", OREAD|OCEXEC);
}
void
plumbstart(void)
{
plumbchan = chancreate(sizeof(Plumbmsg*), 0);
proccreate(plumbwebproc, nil, STACK);
threadcreate(plumbwebthread, nil, STACK);
}
static void
plumbwebthread(void*)
{
char *base;
Plumbmsg *m;
for(;;){
m = recvp(plumbchan);
if(m == nil)
threadexits(nil);
base = plumblookup(m->attr, "baseurl");
if(base == nil)
base = m->wdir;
plumburl(m->data, base);
plumbfree(m);
}
}
static void
plumbwebproc(void*)
{
Plumbmsg *m;
for(;;){
m = plumbrecv(plumbwebfd);
sendp(plumbchan, m);
if(m == nil)
threadexits(nil);
}
}
static void
addattr(Plumbmsg *m, char *name, char *value)
{
Plumbattr *a;
a = malloc(sizeof(Plumbattr));
a->name = name;
a->value = value;
a->next = m->attr;
m->attr = a;
}
static void
freeattrs(Plumbmsg *m)
{
Plumbattr *a, *next;
a = m->attr;
while(a != nil) {
next = a->next;
free(a);
a = next;
}
}
static struct
{
char *ctype;
char *ext;
}
ctypes[] =
{
{ "application/msword", "doc" },
{ "application/pdf", "pdf" },
{ "application/postscript", "ps" },
{ "application/rtf", "rtf" },
{ "image/gif", "gif" },
{ "image/jpeg", "jpg" },
{ "image/png", "png" },
{ "image/ppm", "ppm" },
{ "image/tiff", "tiff" },
{ "text/html", "html" },
{ "text/plain", "txt" },
{ "text/xml", "xml" },
};
void
replumb(Client *c)
{
int i;
Plumbmsg *m;
char name[128], *ctype, *ext, *p;
if(!c->plumbed)
return;
m = emalloc(sizeof(Plumbmsg));
m->src = "webfs";
m->dst = nil;
m->wdir = "/";
m->type = "text";
m->attr = nil;
addattr(m, "url", c->url->url);
ctype = c->contenttype;
ext = nil;
if(ctype != nil) {
addattr(m, "content-type", ctype);
for(i = 0; i < nelem(ctypes); i++) {
if(strcmp(ctype, ctypes[i].ctype) == 0) {
ext = ctypes[i].ext;
break;
}
}
}
if(ext == nil) {
p = strrchr(c->url->url, '/');
if(p != nil)
p = strrchr(p+1, '.');
if(p != nil && strlen(p) <= 5)
ext = p+1;
else
ext = "txt"; /* punt */
}
c->ext = ext;
if(0)fprint(2, "content type %s -> extension .%s\n", ctype, ext);
m->ndata = snprint(name, sizeof name, "/mnt/web/%d/body.%s", c->num, ext);
m->data = estrdup(name);
proccreate(plumbsendproc, m, STACK); /* separate proc to avoid a deadlock */
}
static void
plumbsendproc(void *x)
{
Plumbmsg *m;
m = x;
plumbsend(plumbsendfd, m);
freeattrs(m);
free(m->data);
free(m);
}

119
sys/src/cmd/webfs/sub.c Normal file
View file

@ -0,0 +1,119 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
void*
emalloc(int n)
{
void *v;
v = emalloc9p(n);
setmalloctag(v, getcallerpc(&n));
memset(v, 0, n);
return v;
}
char*
estrdup(char *s)
{
s = estrdup9p(s);
setmalloctag(s, getcallerpc(&s));
return s;
}
Key*
addkey(Key *h, char *key, char *val)
{
Key *k;
int n;
if(val == nil)
val = "";
n = strlen(key)+1;
k = emalloc(sizeof(*k) + n + strlen(val)+1);
k->next = h;
k->val = k->key + n;
strcpy(k->key, key);
strcpy(k->val, val);
return k;
}
Key*
delkey(Key *h, char *key)
{
Key *k, *p;
for(p = nil, k = h; k; p = k, k = k->next){
if(!cistrcmp(k->key, key)){
if(p)
p->next = k->next;
else
h = k->next;
memset(k->val, 0, strlen(k->val));
free(k);
break;
}
}
return h;
}
char*
lookkey(Key *k, char *key)
{
while(k){
if(!cistrcmp(k->key, key))
return k->val;
k = k->next;
}
return nil;
}
Key*
parsehdr(char *s)
{
char *v;
v = strchr(s, 0)-1;
while(v >= s && strchr("\n\r\t ", *v))
*v-- = 0;
if(v = strchr(s, ':')){
*v++ = 0;
while(strchr("\t ", *v))
v++;
if(*s && *v)
return addkey(0, s, v);
}
return nil;
}
char*
unquote(char *s, char **ps)
{
char *p;
if(*s != '"'){
p = strpbrk(s, " \t\r\n");
*p++ = 0;
*ps = p;
return s;
}
for(p=s+1; *p; p++){
if(*p == '\"'){
*p++ = 0;
break;
}
if(*p == '\\' && *(p+1)){
p++;
continue;
}
}
memmove(s, s+1, p-(s+1));
s[p-(s+1)] = 0;
*ps = p;
return s;
}

File diff suppressed because it is too large Load diff

View file

@ -1,86 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <ctype.h>
#include "dat.h"
#include "fns.h"
void*
erealloc(void *a, uint n)
{
a = realloc(a, n);
if(a == nil)
sysfatal("realloc %d: out of memory", n);
setrealloctag(a, getcallerpc(&a));
return a;
}
void*
emalloc(uint n)
{
void *a;
a = mallocz(n, 1);
if(a == nil)
sysfatal("malloc %d: out of memory", n);
setmalloctag(a, getcallerpc(&n));
return a;
}
char*
estrdup(char *s)
{
s = strdup(s);
if(s == nil)
sysfatal("strdup: out of memory");
setmalloctag(s, getcallerpc(&s));
return s;
}
char*
estredup(char *s, char *e)
{
char *t;
t = emalloc(e-s+1);
memmove(t, s, e-s);
t[e-s] = '\0';
setmalloctag(t, getcallerpc(&s));
return t;
}
char*
estrmanydup(char *s, ...)
{
char *p, *t;
int len;
va_list arg;
len = strlen(s);
va_start(arg, s);
while((p = va_arg(arg, char*)) != nil)
len += strlen(p);
len++;
t = emalloc(len);
strcpy(t, s);
va_start(arg, s);
while((p = va_arg(arg, char*)) != nil)
strcat(t, p);
return t;
}
char*
strlower(char *s)
{
char *t;
for(t=s; *t; t++)
if('A' <= *t && *t <= 'Z')
*t += 'a'-'A';
return s;
}

View file

@ -1,87 +0,0 @@
/*
* Sample client.
*/
#include <u.h>
#include <libc.h>
void
xfer(int from, int to)
{
char buf[12*1024];
int n;
while((n = read(from, buf, sizeof buf)) > 0)
if(write(to, buf, n) < 0)
sysfatal("write failed: %r");
if(n < 0)
sysfatal("read failed: %r");
}
void
usage(void)
{
fprint(2, "usage: webget [-b baseurl] [-m mtpt] [-p postbody] url\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int conn, ctlfd, fd, n;
char buf[128], *base, *mtpt, *post, *url;
mtpt = "/mnt/web";
post = nil;
base = nil;
ARGBEGIN{
default:
usage();
case 'b':
base = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 'p':
post = EARGF(usage());
break;
}ARGEND;
if (argc != 1)
usage();
url = argv[0];
snprint(buf, sizeof buf, "%s/clone", mtpt);
if((ctlfd = open(buf, ORDWR)) < 0)
sysfatal("couldn't open %s: %r", buf);
if((n = read(ctlfd, buf, sizeof buf-1)) < 0)
sysfatal("reading clone: %r");
if(n == 0)
sysfatal("short read on clone");
buf[n] = '\0';
conn = atoi(buf);
if(base)
if(fprint(ctlfd, "baseurl %s", base) < 0)
sysfatal("baseurl ctl write: %r");
if(fprint(ctlfd, "url %s", url) <= 0)
sysfatal("get ctl write: %r");
if(post){
snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
if((fd = open(buf, OWRITE)) < 0)
sysfatal("open %s: %r", buf);
if(write(fd, post, strlen(post)) < 0)
sysfatal("post write failed: %r");
close(fd);
}
snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
if((fd = open(buf, OREAD)) < 0)
sysfatal("open %s: %r", buf);
xfer(fd, 1);
exits(nil);
}

View file

@ -1,85 +0,0 @@
/* Example of how to use webfs */
#include <u.h>
#include <libc.h>
void
xfer(int from, int to)
{
char buf[12*1024];
int n;
while((n = read(from, buf, sizeof buf)) > 0)
if(write(to, buf, n) < 0)
sysfatal("write failed: %r");
if(n < 0)
sysfatal("read failed: %r");
}
void
usage(void)
{
fprint(2, "usage: webfsget [-b baseurl] [-m mtpt] [-p postbody] url\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int conn, ctlfd, fd, n;
char buf[128], *base, *mtpt, *post, *url;
mtpt = "/mnt/web";
post = nil;
base = nil;
ARGBEGIN{
default:
usage();
case 'b':
base = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 'p':
post = EARGF(usage());
break;
}ARGEND;
if (argc != 1)
usage();
url = argv[0];
snprint(buf, sizeof buf, "%s/clone", mtpt);
if((ctlfd = open(buf, ORDWR)) < 0)
sysfatal("couldn't open %s: %r", buf);
if((n = read(ctlfd, buf, sizeof buf-1)) < 0)
sysfatal("reading clone: %r");
if(n == 0)
sysfatal("short read on clone");
buf[n] = '\0';
conn = atoi(buf);
if(base)
if(fprint(ctlfd, "baseurl %s", base) < 0)
sysfatal("baseurl ctl write: %r");
if(fprint(ctlfd, "url %s", url) <= 0)
sysfatal("get ctl write: %r");
if(post){
snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn);
if((fd = open(buf, OWRITE)) < 0)
sysfatal("open %s: %r", buf);
if(write(fd, post, strlen(post)) < 0)
sysfatal("post write failed: %r");
close(fd);
}
snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn);
if((fd = open(buf, OREAD)) < 0)
sysfatal("open %s: %r", buf);
xfer(fd, 1);
exits(nil);
}