17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
359cc4ca5SDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <bio.h>
559cc4ca5SDavid du Colombier #include <ip.h>
6106486e8SDavid du Colombier #include <libsec.h>
7fd597ed8SDavid du Colombier #include <auth.h>
87dd7cddfSDavid du Colombier
97dd7cddfSDavid du Colombier typedef struct URL URL;
107dd7cddfSDavid du Colombier struct URL
117dd7cddfSDavid du Colombier {
127dd7cddfSDavid du Colombier int method;
137dd7cddfSDavid du Colombier char *host;
147dd7cddfSDavid du Colombier char *port;
157dd7cddfSDavid du Colombier char *page;
167dd7cddfSDavid du Colombier char *etag;
177dd7cddfSDavid du Colombier char *redirect;
1859cc4ca5SDavid du Colombier char *postbody;
19fd597ed8SDavid du Colombier char *cred;
20*c54d4d90SDavid du Colombier char *rhead;
217dd7cddfSDavid du Colombier long mtime;
227dd7cddfSDavid du Colombier };
237dd7cddfSDavid du Colombier
247dd7cddfSDavid du Colombier typedef struct Range Range;
257dd7cddfSDavid du Colombier struct Range
267dd7cddfSDavid du Colombier {
277dd7cddfSDavid du Colombier long start; /* only 2 gig supported, tdb */
287dd7cddfSDavid du Colombier long end;
297dd7cddfSDavid du Colombier };
307dd7cddfSDavid du Colombier
31e0d6d19cSDavid du Colombier typedef struct Out Out;
32e0d6d19cSDavid du Colombier struct Out
33e0d6d19cSDavid du Colombier {
34e0d6d19cSDavid du Colombier int fd;
35e0d6d19cSDavid du Colombier int offset; /* notional current offset in output */
36e0d6d19cSDavid du Colombier int written; /* number of bytes successfully transferred to output */
37e0d6d19cSDavid du Colombier DigestState *curr; /* digest state up to offset (if known) */
38e0d6d19cSDavid du Colombier DigestState *hiwat; /* digest state of all bytes written */
39e0d6d19cSDavid du Colombier };
40e0d6d19cSDavid du Colombier
417dd7cddfSDavid du Colombier enum
427dd7cddfSDavid du Colombier {
43cd42b314SDavid du Colombier Other,
447dd7cddfSDavid du Colombier Http,
45106486e8SDavid du Colombier Https,
467dd7cddfSDavid du Colombier Ftp,
477dd7cddfSDavid du Colombier };
487dd7cddfSDavid du Colombier
497dd7cddfSDavid du Colombier enum
507dd7cddfSDavid du Colombier {
517dd7cddfSDavid du Colombier Eof = 0,
527dd7cddfSDavid du Colombier Error = -1,
537dd7cddfSDavid du Colombier Server = -2,
547dd7cddfSDavid du Colombier Changed = -3,
557dd7cddfSDavid du Colombier };
567dd7cddfSDavid du Colombier
577dd7cddfSDavid du Colombier int debug;
587dd7cddfSDavid du Colombier char *ofile;
597dd7cddfSDavid du Colombier
60e0d6d19cSDavid du Colombier
61e0d6d19cSDavid du Colombier int doftp(URL*, URL*, Range*, Out*, long);
62e0d6d19cSDavid du Colombier int dohttp(URL*, URL*, Range*, Out*, long);
637dd7cddfSDavid du Colombier int crackurl(URL*, char*);
647dd7cddfSDavid du Colombier Range* crackrange(char*);
657dd7cddfSDavid du Colombier int getheader(int, char*, int);
669a747e4fSDavid du Colombier int httpheaders(int, int, URL*, Range*);
677dd7cddfSDavid du Colombier int httprcode(int);
687dd7cddfSDavid du Colombier int cistrncmp(char*, char*, int);
697dd7cddfSDavid du Colombier int cistrcmp(char*, char*);
707dd7cddfSDavid du Colombier void initibuf(void);
717dd7cddfSDavid du Colombier int readline(int, char*, int);
727dd7cddfSDavid du Colombier int readibuf(int, char*, int);
737dd7cddfSDavid du Colombier int dfprint(int, char*, ...);
747dd7cddfSDavid du Colombier void unreadline(char*);
7560726053SDavid du Colombier int output(Out*, char*, int);
76e0d6d19cSDavid du Colombier void setoffset(Out*, int);
7759cc4ca5SDavid du Colombier
787dd7cddfSDavid du Colombier int verbose;
7959cc4ca5SDavid du Colombier char *net;
80e0d6d19cSDavid du Colombier char tcpdir[NETPATHLEN];
8119d19a2eSDavid du Colombier int headerprint;
827dd7cddfSDavid du Colombier
837dd7cddfSDavid du Colombier struct {
847dd7cddfSDavid du Colombier char *name;
85e0d6d19cSDavid du Colombier int (*f)(URL*, URL*, Range*, Out*, long);
867dd7cddfSDavid du Colombier } method[] = {
877dd7cddfSDavid du Colombier [Http] { "http", dohttp },
88106486e8SDavid du Colombier [Https] { "https", dohttp },
897dd7cddfSDavid du Colombier [Ftp] { "ftp", doftp },
907dd7cddfSDavid du Colombier [Other] { "_______", nil },
917dd7cddfSDavid du Colombier };
927dd7cddfSDavid du Colombier
937dd7cddfSDavid du Colombier void
usage(void)947dd7cddfSDavid du Colombier usage(void)
957dd7cddfSDavid du Colombier {
96*c54d4d90SDavid du Colombier fprint(2, "usage: %s [-dhv] [-o outfile] [-p body] [-x netmtpt] [-r header] url\n", argv0);
977dd7cddfSDavid du Colombier exits("usage");
987dd7cddfSDavid du Colombier }
997dd7cddfSDavid du Colombier
1007dd7cddfSDavid du Colombier void
main(int argc,char ** argv)1017dd7cddfSDavid du Colombier main(int argc, char **argv)
1027dd7cddfSDavid du Colombier {
1037dd7cddfSDavid du Colombier URL u;
1047dd7cddfSDavid du Colombier Range r;
105e0d6d19cSDavid du Colombier int errs, n;
1069a747e4fSDavid du Colombier ulong mtime;
1079a747e4fSDavid du Colombier Dir *d;
108e288d156SDavid du Colombier char postbody[4096], *p, *e, *t, *hpx;
109e288d156SDavid du Colombier URL px; // Proxy
110e0d6d19cSDavid du Colombier Out out;
1117dd7cddfSDavid du Colombier
1127dd7cddfSDavid du Colombier ofile = nil;
11359cc4ca5SDavid du Colombier p = postbody;
11459cc4ca5SDavid du Colombier e = p + sizeof(postbody);
1157dd7cddfSDavid du Colombier r.start = 0;
1167dd7cddfSDavid du Colombier r.end = -1;
1179a747e4fSDavid du Colombier mtime = 0;
11859cc4ca5SDavid du Colombier memset(&u, 0, sizeof(u));
119e288d156SDavid du Colombier memset(&px, 0, sizeof(px));
120e288d156SDavid du Colombier hpx = getenv("httpproxy");
1217dd7cddfSDavid du Colombier
1227dd7cddfSDavid du Colombier ARGBEGIN {
1237dd7cddfSDavid du Colombier case 'o':
124de8abbc9SDavid du Colombier ofile = EARGF(usage());
1257dd7cddfSDavid du Colombier break;
1267dd7cddfSDavid du Colombier case 'd':
1277dd7cddfSDavid du Colombier debug = 1;
1287dd7cddfSDavid du Colombier break;
12919d19a2eSDavid du Colombier case 'h':
13019d19a2eSDavid du Colombier headerprint = 1;
13119d19a2eSDavid du Colombier break;
1327dd7cddfSDavid du Colombier case 'v':
1337dd7cddfSDavid du Colombier verbose = 1;
1347dd7cddfSDavid du Colombier break;
13559cc4ca5SDavid du Colombier case 'x':
136de8abbc9SDavid du Colombier net = EARGF(usage());
13759cc4ca5SDavid du Colombier break;
138*c54d4d90SDavid du Colombier case 'r':
139*c54d4d90SDavid du Colombier u.rhead = EARGF(usage());
140*c54d4d90SDavid du Colombier break;
14159cc4ca5SDavid du Colombier case 'p':
142de8abbc9SDavid du Colombier t = EARGF(usage());
14359cc4ca5SDavid du Colombier if(p != postbody)
14459cc4ca5SDavid du Colombier p = seprint(p, e, "&%s", t);
14559cc4ca5SDavid du Colombier else
14659cc4ca5SDavid du Colombier p = seprint(p, e, "%s", t);
14759cc4ca5SDavid du Colombier u.postbody = postbody;
14859cc4ca5SDavid du Colombier
14959cc4ca5SDavid du Colombier break;
1507dd7cddfSDavid du Colombier default:
1517dd7cddfSDavid du Colombier usage();
1527dd7cddfSDavid du Colombier } ARGEND;
1537dd7cddfSDavid du Colombier
15459cc4ca5SDavid du Colombier if(net != nil){
15559cc4ca5SDavid du Colombier if(strlen(net) > sizeof(tcpdir)-5)
15659cc4ca5SDavid du Colombier sysfatal("network mount point too long");
15759cc4ca5SDavid du Colombier snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net);
15859cc4ca5SDavid du Colombier } else
15959cc4ca5SDavid du Colombier snprint(tcpdir, sizeof(tcpdir), "tcp");
16059cc4ca5SDavid du Colombier
1617dd7cddfSDavid du Colombier if(argc != 1)
1627dd7cddfSDavid du Colombier usage();
1637dd7cddfSDavid du Colombier
164e0d6d19cSDavid du Colombier
165e0d6d19cSDavid du Colombier out.fd = 1;
166e0d6d19cSDavid du Colombier out.written = 0;
167e0d6d19cSDavid du Colombier out.offset = 0;
168e0d6d19cSDavid du Colombier out.curr = nil;
169e0d6d19cSDavid du Colombier out.hiwat = nil;
1707dd7cddfSDavid du Colombier if(ofile != nil){
1719a747e4fSDavid du Colombier d = dirstat(ofile);
1729a747e4fSDavid du Colombier if(d == nil){
173e0d6d19cSDavid du Colombier out.fd = create(ofile, OWRITE, 0664);
174e0d6d19cSDavid du Colombier if(out.fd < 0)
1757dd7cddfSDavid du Colombier sysfatal("creating %s: %r", ofile);
1767dd7cddfSDavid du Colombier } else {
177e0d6d19cSDavid du Colombier out.fd = open(ofile, OWRITE);
178e0d6d19cSDavid du Colombier if(out.fd < 0)
1797dd7cddfSDavid du Colombier sysfatal("can't open %s: %r", ofile);
1809a747e4fSDavid du Colombier r.start = d->length;
1819a747e4fSDavid du Colombier mtime = d->mtime;
1829a747e4fSDavid du Colombier free(d);
1837dd7cddfSDavid du Colombier }
1847dd7cddfSDavid du Colombier }
1857dd7cddfSDavid du Colombier
1867dd7cddfSDavid du Colombier errs = 0;
1877dd7cddfSDavid du Colombier
1887dd7cddfSDavid du Colombier if(crackurl(&u, argv[0]) < 0)
1897dd7cddfSDavid du Colombier sysfatal("%r");
190e288d156SDavid du Colombier if(hpx && crackurl(&px, hpx) < 0)
191e288d156SDavid du Colombier sysfatal("%r");
1927dd7cddfSDavid du Colombier
1937dd7cddfSDavid du Colombier for(;;){
194e0d6d19cSDavid du Colombier setoffset(&out, 0);
1957dd7cddfSDavid du Colombier /* transfer data */
1967dd7cddfSDavid du Colombier werrstr("");
197e0d6d19cSDavid du Colombier n = (*method[u.method].f)(&u, &px, &r, &out, mtime);
1987dd7cddfSDavid du Colombier
1997dd7cddfSDavid du Colombier switch(n){
2007dd7cddfSDavid du Colombier case Eof:
2017dd7cddfSDavid du Colombier exits(0);
2027dd7cddfSDavid du Colombier break;
2037dd7cddfSDavid du Colombier case Error:
2047dd7cddfSDavid du Colombier if(errs++ < 10)
2057dd7cddfSDavid du Colombier continue;
2067dd7cddfSDavid du Colombier sysfatal("too many errors with no progress %r");
2077dd7cddfSDavid du Colombier break;
2087dd7cddfSDavid du Colombier case Server:
2097dd7cddfSDavid du Colombier sysfatal("server returned: %r");
2107dd7cddfSDavid du Colombier break;
2117dd7cddfSDavid du Colombier }
2127dd7cddfSDavid du Colombier
2137dd7cddfSDavid du Colombier /* forward progress */
2147dd7cddfSDavid du Colombier errs = 0;
2157dd7cddfSDavid du Colombier r.start += n;
2167dd7cddfSDavid du Colombier if(r.start >= r.end)
2177dd7cddfSDavid du Colombier break;
2187dd7cddfSDavid du Colombier }
2197dd7cddfSDavid du Colombier
2207dd7cddfSDavid du Colombier exits(0);
2217dd7cddfSDavid du Colombier }
2227dd7cddfSDavid du Colombier
2237dd7cddfSDavid du Colombier int
crackurl(URL * u,char * s)2247dd7cddfSDavid du Colombier crackurl(URL *u, char *s)
2257dd7cddfSDavid du Colombier {
2267dd7cddfSDavid du Colombier char *p;
2277dd7cddfSDavid du Colombier int i;
2287dd7cddfSDavid du Colombier
2297dd7cddfSDavid du Colombier if(u->page != nil){
2307dd7cddfSDavid du Colombier free(u->page);
2317dd7cddfSDavid du Colombier u->page = nil;
2327dd7cddfSDavid du Colombier }
2337dd7cddfSDavid du Colombier
2347dd7cddfSDavid du Colombier /* get type */
2357dd7cddfSDavid du Colombier for(p = s; *p; p++){
2367dd7cddfSDavid du Colombier if(*p == '/'){
2377dd7cddfSDavid du Colombier p = s;
238cd42b314SDavid du Colombier if(u->method == Other){
239cd42b314SDavid du Colombier werrstr("missing method");
240cd42b314SDavid du Colombier return -1;
241cd42b314SDavid du Colombier }
242cd42b314SDavid du Colombier if(u->host == nil){
243cd42b314SDavid du Colombier werrstr("missing host");
244cd42b314SDavid du Colombier return -1;
245cd42b314SDavid du Colombier }
246cd42b314SDavid du Colombier u->page = strdup(p);
247cd42b314SDavid du Colombier return 0;
2487dd7cddfSDavid du Colombier }
2497dd7cddfSDavid du Colombier if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){
2507dd7cddfSDavid du Colombier *p = 0;
2517dd7cddfSDavid du Colombier p += 3;
2527dd7cddfSDavid du Colombier for(i = 0; i < nelem(method); i++){
2537dd7cddfSDavid du Colombier if(cistrcmp(s, method[i].name) == 0){
2547dd7cddfSDavid du Colombier u->method = i;
2557dd7cddfSDavid du Colombier break;
2567dd7cddfSDavid du Colombier }
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier break;
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier
2627dd7cddfSDavid du Colombier if(u->method == Other){
2637dd7cddfSDavid du Colombier werrstr("unsupported URL type %s", s);
2647dd7cddfSDavid du Colombier return -1;
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier
2677dd7cddfSDavid du Colombier /* get system */
268cd42b314SDavid du Colombier free(u->host);
2697dd7cddfSDavid du Colombier s = p;
2707dd7cddfSDavid du Colombier p = strchr(s, '/');
2717dd7cddfSDavid du Colombier if(p == nil){
2727dd7cddfSDavid du Colombier u->host = strdup(s);
2737dd7cddfSDavid du Colombier u->page = strdup("/");
2747dd7cddfSDavid du Colombier } else {
2757dd7cddfSDavid du Colombier u->page = strdup(p);
2767dd7cddfSDavid du Colombier *p = 0;
2777dd7cddfSDavid du Colombier u->host = strdup(s);
2787dd7cddfSDavid du Colombier *p = '/';
2797dd7cddfSDavid du Colombier }
2807dd7cddfSDavid du Colombier
2817dd7cddfSDavid du Colombier if(p = strchr(u->host, ':')) {
2827dd7cddfSDavid du Colombier *p++ = 0;
2837dd7cddfSDavid du Colombier u->port = p;
2847dd7cddfSDavid du Colombier } else
28559cc4ca5SDavid du Colombier u->port = method[u->method].name;
2867dd7cddfSDavid du Colombier
2877dd7cddfSDavid du Colombier if(*(u->host) == 0){
2887dd7cddfSDavid du Colombier werrstr("bad url, null host");
2897dd7cddfSDavid du Colombier return -1;
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier
2927dd7cddfSDavid du Colombier return 0;
2937dd7cddfSDavid du Colombier }
2947dd7cddfSDavid du Colombier
2957dd7cddfSDavid du Colombier char *day[] = {
2967dd7cddfSDavid du Colombier "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
2977dd7cddfSDavid du Colombier };
2987dd7cddfSDavid du Colombier
2997dd7cddfSDavid du Colombier char *month[] = {
3007dd7cddfSDavid du Colombier "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
3017dd7cddfSDavid du Colombier };
3027dd7cddfSDavid du Colombier
3037dd7cddfSDavid du Colombier struct
3047dd7cddfSDavid du Colombier {
3057dd7cddfSDavid du Colombier int fd;
3067dd7cddfSDavid du Colombier long mtime;
3077dd7cddfSDavid du Colombier } note;
3087dd7cddfSDavid du Colombier
3097dd7cddfSDavid du Colombier void
catch(void *,char *)3107dd7cddfSDavid du Colombier catch(void*, char*)
3117dd7cddfSDavid du Colombier {
3127dd7cddfSDavid du Colombier Dir d;
3137dd7cddfSDavid du Colombier
3149a747e4fSDavid du Colombier nulldir(&d);
3157dd7cddfSDavid du Colombier d.mtime = note.mtime;
3167dd7cddfSDavid du Colombier if(dirfwstat(note.fd, &d) < 0)
3177dd7cddfSDavid du Colombier sysfatal("catch: can't dirfwstat: %r");
3187dd7cddfSDavid du Colombier noted(NDFLT);
3197dd7cddfSDavid du Colombier }
3207dd7cddfSDavid du Colombier
3217dd7cddfSDavid du Colombier int
dohttp(URL * u,URL * px,Range * r,Out * out,long mtime)322e0d6d19cSDavid du Colombier dohttp(URL *u, URL *px, Range *r, Out *out, long mtime)
3237dd7cddfSDavid du Colombier {
3249a747e4fSDavid du Colombier int fd, cfd;
325fd597ed8SDavid du Colombier int redirect, auth, loop;
3267dd7cddfSDavid du Colombier int n, rv, code;
3277dd7cddfSDavid du Colombier long tot, vtime;
3287dd7cddfSDavid du Colombier Tm *tm;
3297dd7cddfSDavid du Colombier char buf[1024];
3309a747e4fSDavid du Colombier char err[ERRMAX];
3317dd7cddfSDavid du Colombier
33259cc4ca5SDavid du Colombier
33359cc4ca5SDavid du Colombier /* always move back to a previous 512 byte bound because some
33459cc4ca5SDavid du Colombier * servers can't seem to deal with requests that start at the
33559cc4ca5SDavid du Colombier * end of the file
33659cc4ca5SDavid du Colombier */
33759cc4ca5SDavid du Colombier if(r->start)
33859cc4ca5SDavid du Colombier r->start = ((r->start-1)/512)*512;
33959cc4ca5SDavid du Colombier
3407dd7cddfSDavid du Colombier /* loop for redirects, requires reading both response code and headers */
3417dd7cddfSDavid du Colombier fd = -1;
3427dd7cddfSDavid du Colombier for(loop = 0; loop < 32; loop++){
343e288d156SDavid du Colombier if(px->host == nil){
34459cc4ca5SDavid du Colombier fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0);
345e288d156SDavid du Colombier } else {
346e288d156SDavid du Colombier fd = dial(netmkaddr(px->host, tcpdir, px->port), 0, 0, 0);
347e288d156SDavid du Colombier }
3487dd7cddfSDavid du Colombier if(fd < 0)
3497dd7cddfSDavid du Colombier return Error;
3507dd7cddfSDavid du Colombier
351106486e8SDavid du Colombier if(u->method == Https){
352106486e8SDavid du Colombier int tfd;
353106486e8SDavid du Colombier TLSconn conn;
354106486e8SDavid du Colombier
355106486e8SDavid du Colombier memset(&conn, 0, sizeof conn);
356106486e8SDavid du Colombier tfd = tlsClient(fd, &conn);
357106486e8SDavid du Colombier if(tfd < 0){
358106486e8SDavid du Colombier fprint(2, "tlsClient: %r\n");
359106486e8SDavid du Colombier close(fd);
360106486e8SDavid du Colombier return Error;
361106486e8SDavid du Colombier }
362106486e8SDavid du Colombier /* BUG: check cert here? */
363106486e8SDavid du Colombier if(conn.cert)
364106486e8SDavid du Colombier free(conn.cert);
365106486e8SDavid du Colombier close(fd);
366106486e8SDavid du Colombier fd = tfd;
367106486e8SDavid du Colombier }
368106486e8SDavid du Colombier
3697dd7cddfSDavid du Colombier /* write request, use range if not start of file */
37059cc4ca5SDavid du Colombier if(u->postbody == nil){
371e288d156SDavid du Colombier if(px->host == nil){
37259cc4ca5SDavid du Colombier dfprint(fd, "GET %s HTTP/1.0\r\n"
37359cc4ca5SDavid du Colombier "Host: %s\r\n"
374dc5a79c1SDavid du Colombier "User-agent: Plan9/hget\r\n"
375dc5a79c1SDavid du Colombier "Cache-Control: no-cache\r\n"
376dc5a79c1SDavid du Colombier "Pragma: no-cache\r\n",
37759cc4ca5SDavid du Colombier u->page, u->host);
37859cc4ca5SDavid du Colombier } else {
379e288d156SDavid du Colombier dfprint(fd, "GET http://%s%s HTTP/1.0\r\n"
380e288d156SDavid du Colombier "Host: %s\r\n"
381e288d156SDavid du Colombier "User-agent: Plan9/hget\r\n"
382e288d156SDavid du Colombier "Cache-Control: no-cache\r\n"
383e288d156SDavid du Colombier "Pragma: no-cache\r\n",
384e288d156SDavid du Colombier u->host, u->page, u->host);
385e288d156SDavid du Colombier }
386e288d156SDavid du Colombier } else {
38759cc4ca5SDavid du Colombier dfprint(fd, "POST %s HTTP/1.0\r\n"
38859cc4ca5SDavid du Colombier "Host: %s\r\n"
38959cc4ca5SDavid du Colombier "Content-type: application/x-www-form-urlencoded\r\n"
39059cc4ca5SDavid du Colombier "Content-length: %d\r\n"
39105406af2SDavid du Colombier "User-agent: Plan9/hget\r\n",
39259cc4ca5SDavid du Colombier u->page, u->host, strlen(u->postbody));
393*c54d4d90SDavid du Colombier }
3945e91980fSDavid du Colombier if(u->cred)
3955e91980fSDavid du Colombier dfprint(fd, "Authorization: Basic %s\r\n", u->cred);
396*c54d4d90SDavid du Colombier if(u->rhead)
397*c54d4d90SDavid du Colombier dfprint(fd, "%s\r\n", u->rhead);
3987dd7cddfSDavid du Colombier if(r->start != 0){
3997dd7cddfSDavid du Colombier dfprint(fd, "Range: bytes=%d-\n", r->start);
4007dd7cddfSDavid du Colombier if(u->etag != nil){
4017dd7cddfSDavid du Colombier dfprint(fd, "If-range: %s\n", u->etag);
4027dd7cddfSDavid du Colombier } else {
4037dd7cddfSDavid du Colombier tm = gmtime(mtime);
4047dd7cddfSDavid du Colombier dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n",
4057dd7cddfSDavid du Colombier day[tm->wday], tm->mday, month[tm->mon],
4067dd7cddfSDavid du Colombier tm->year+1900, tm->hour, tm->min, tm->sec);
4077dd7cddfSDavid du Colombier }
4087dd7cddfSDavid du Colombier }
4099a747e4fSDavid du Colombier if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
4109a747e4fSDavid du Colombier if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){
4119a747e4fSDavid du Colombier while((n = read(cfd, buf, sizeof buf)) > 0){
4129a747e4fSDavid du Colombier if(debug)
4139a747e4fSDavid du Colombier write(2, buf, n);
4149a747e4fSDavid du Colombier write(fd, buf, n);
4159a747e4fSDavid du Colombier }
4169a747e4fSDavid du Colombier }else{
4179a747e4fSDavid du Colombier close(cfd);
4189a747e4fSDavid du Colombier cfd = -1;
4199a747e4fSDavid du Colombier }
4209a747e4fSDavid du Colombier }
421fd597ed8SDavid du Colombier
4227dd7cddfSDavid du Colombier dfprint(fd, "\r\n", u->host);
42305406af2SDavid du Colombier if(u->postbody)
42405406af2SDavid du Colombier dfprint(fd, "%s", u->postbody);
4257dd7cddfSDavid du Colombier
426fd597ed8SDavid du Colombier auth = 0;
4277dd7cddfSDavid du Colombier redirect = 0;
4287dd7cddfSDavid du Colombier initibuf();
4297dd7cddfSDavid du Colombier code = httprcode(fd);
4307dd7cddfSDavid du Colombier switch(code){
4317dd7cddfSDavid du Colombier case Error: /* connection timed out */
4327dd7cddfSDavid du Colombier case Eof:
43359cc4ca5SDavid du Colombier close(fd);
4349a747e4fSDavid du Colombier close(cfd);
4357dd7cddfSDavid du Colombier return code;
4367dd7cddfSDavid du Colombier
4377dd7cddfSDavid du Colombier case 200: /* OK */
4387dd7cddfSDavid du Colombier case 201: /* Created */
4397dd7cddfSDavid du Colombier case 202: /* Accepted */
4407dd7cddfSDavid du Colombier if(ofile == nil && r->start != 0)
4417dd7cddfSDavid du Colombier sysfatal("page changed underfoot");
4427dd7cddfSDavid du Colombier break;
4437dd7cddfSDavid du Colombier
4447dd7cddfSDavid du Colombier case 204: /* No Content */
4457dd7cddfSDavid du Colombier sysfatal("No Content");
4467dd7cddfSDavid du Colombier
4477dd7cddfSDavid du Colombier case 206: /* Partial Content */
448e0d6d19cSDavid du Colombier setoffset(out, r->start);
4497dd7cddfSDavid du Colombier break;
4507dd7cddfSDavid du Colombier
4517dd7cddfSDavid du Colombier case 301: /* Moved Permanently */
4526bf192d6SDavid du Colombier case 302: /* Moved Temporarily (actually Found) */
45342dedc50SDavid du Colombier case 303: /* See Other */
4546bf192d6SDavid du Colombier case 307: /* Temporary Redirect (HTTP/1.1) */
4557dd7cddfSDavid du Colombier redirect = 1;
4569a747e4fSDavid du Colombier u->postbody = nil;
4577dd7cddfSDavid du Colombier break;
4587dd7cddfSDavid du Colombier
4597dd7cddfSDavid du Colombier case 304: /* Not Modified */
4607dd7cddfSDavid du Colombier break;
4617dd7cddfSDavid du Colombier
4627dd7cddfSDavid du Colombier case 400: /* Bad Request */
4637dd7cddfSDavid du Colombier sysfatal("Bad Request");
4647dd7cddfSDavid du Colombier
4657dd7cddfSDavid du Colombier case 401: /* Unauthorized */
466fd597ed8SDavid du Colombier if (auth)
467fd597ed8SDavid du Colombier sysfatal("Authentication failed");
468fd597ed8SDavid du Colombier auth = 1;
469fd597ed8SDavid du Colombier break;
470fd597ed8SDavid du Colombier
4717dd7cddfSDavid du Colombier case 402: /* ??? */
4727dd7cddfSDavid du Colombier sysfatal("Unauthorized");
4737dd7cddfSDavid du Colombier
4747dd7cddfSDavid du Colombier case 403: /* Forbidden */
4757dd7cddfSDavid du Colombier sysfatal("Forbidden by server");
4767dd7cddfSDavid du Colombier
4777dd7cddfSDavid du Colombier case 404: /* Not Found */
4787dd7cddfSDavid du Colombier sysfatal("Not found on server");
4797dd7cddfSDavid du Colombier
480fd597ed8SDavid du Colombier case 407: /* Proxy Authentication */
481fd597ed8SDavid du Colombier sysfatal("Proxy authentication required");
482fd597ed8SDavid du Colombier
4837dd7cddfSDavid du Colombier case 500: /* Internal server error */
4847dd7cddfSDavid du Colombier sysfatal("Server choked");
4857dd7cddfSDavid du Colombier
4867dd7cddfSDavid du Colombier case 501: /* Not implemented */
4877dd7cddfSDavid du Colombier sysfatal("Server can't do it!");
4887dd7cddfSDavid du Colombier
4897dd7cddfSDavid du Colombier case 502: /* Bad gateway */
4907dd7cddfSDavid du Colombier sysfatal("Bad gateway");
4917dd7cddfSDavid du Colombier
4927dd7cddfSDavid du Colombier case 503: /* Service unavailable */
4937dd7cddfSDavid du Colombier sysfatal("Service unavailable");
4947dd7cddfSDavid du Colombier
4957dd7cddfSDavid du Colombier default:
496d9306527SDavid du Colombier sysfatal("Unknown response code %d", code);
4977dd7cddfSDavid du Colombier }
4987dd7cddfSDavid du Colombier
4997dd7cddfSDavid du Colombier if(u->redirect != nil){
5007dd7cddfSDavid du Colombier free(u->redirect);
5017dd7cddfSDavid du Colombier u->redirect = nil;
5027dd7cddfSDavid du Colombier }
5037dd7cddfSDavid du Colombier
5049a747e4fSDavid du Colombier rv = httpheaders(fd, cfd, u, r);
5059a747e4fSDavid du Colombier close(cfd);
50659cc4ca5SDavid du Colombier if(rv != 0){
50759cc4ca5SDavid du Colombier close(fd);
5087dd7cddfSDavid du Colombier return rv;
50959cc4ca5SDavid du Colombier }
5107dd7cddfSDavid du Colombier
511fd597ed8SDavid du Colombier if(!redirect && !auth)
5127dd7cddfSDavid du Colombier break;
5137dd7cddfSDavid du Colombier
514fd597ed8SDavid du Colombier if (redirect){
5157dd7cddfSDavid du Colombier if(u->redirect == nil)
5167dd7cddfSDavid du Colombier sysfatal("redirect: no URL");
5177dd7cddfSDavid du Colombier if(crackurl(u, u->redirect) < 0)
5187dd7cddfSDavid du Colombier sysfatal("redirect: %r");
5197dd7cddfSDavid du Colombier }
520fd597ed8SDavid du Colombier }
5217dd7cddfSDavid du Colombier
5227dd7cddfSDavid du Colombier /* transfer whatever you get */
5237dd7cddfSDavid du Colombier if(ofile != nil && u->mtime != 0){
524e0d6d19cSDavid du Colombier note.fd = out->fd;
5257dd7cddfSDavid du Colombier note.mtime = u->mtime;
5267dd7cddfSDavid du Colombier notify(catch);
5277dd7cddfSDavid du Colombier }
5287dd7cddfSDavid du Colombier
5297dd7cddfSDavid du Colombier tot = 0;
5307dd7cddfSDavid du Colombier vtime = 0;
5317dd7cddfSDavid du Colombier for(;;){
5327dd7cddfSDavid du Colombier n = readibuf(fd, buf, sizeof(buf));
5337dd7cddfSDavid du Colombier if(n <= 0)
5347dd7cddfSDavid du Colombier break;
535e0d6d19cSDavid du Colombier if(output(out, buf, n) != n)
5367dd7cddfSDavid du Colombier break;
5377dd7cddfSDavid du Colombier tot += n;
5382cba34c7SDavid du Colombier if(verbose && (vtime != time(0) || r->start == r->end)) {
5397dd7cddfSDavid du Colombier vtime = time(0);
5407dd7cddfSDavid du Colombier fprint(2, "%ld %ld\n", r->start+tot, r->end);
5417dd7cddfSDavid du Colombier }
5427dd7cddfSDavid du Colombier }
5437dd7cddfSDavid du Colombier notify(nil);
54459cc4ca5SDavid du Colombier close(fd);
5457dd7cddfSDavid du Colombier
5467dd7cddfSDavid du Colombier if(ofile != nil && u->mtime != 0){
5477dd7cddfSDavid du Colombier Dir d;
5487dd7cddfSDavid du Colombier
5499a747e4fSDavid du Colombier rerrstr(err, sizeof err);
5509a747e4fSDavid du Colombier nulldir(&d);
5517dd7cddfSDavid du Colombier d.mtime = u->mtime;
552e0d6d19cSDavid du Colombier if(dirfwstat(out->fd, &d) < 0)
5537dd7cddfSDavid du Colombier fprint(2, "couldn't set mtime: %r\n");
5549a747e4fSDavid du Colombier errstr(err, sizeof err);
5557dd7cddfSDavid du Colombier }
5567dd7cddfSDavid du Colombier
5577dd7cddfSDavid du Colombier return tot;
5587dd7cddfSDavid du Colombier }
5597dd7cddfSDavid du Colombier
5607dd7cddfSDavid du Colombier /* get the http response code */
5617dd7cddfSDavid du Colombier int
httprcode(int fd)5627dd7cddfSDavid du Colombier httprcode(int fd)
5637dd7cddfSDavid du Colombier {
5647dd7cddfSDavid du Colombier int n;
5657dd7cddfSDavid du Colombier char *p;
5667dd7cddfSDavid du Colombier char buf[256];
5677dd7cddfSDavid du Colombier
5687dd7cddfSDavid du Colombier n = readline(fd, buf, sizeof(buf)-1);
5697dd7cddfSDavid du Colombier if(n <= 0)
5707dd7cddfSDavid du Colombier return n;
5717dd7cddfSDavid du Colombier if(debug)
5727dd7cddfSDavid du Colombier fprint(2, "%d <- %s\n", fd, buf);
5737dd7cddfSDavid du Colombier p = strchr(buf, ' ');
5747dd7cddfSDavid du Colombier if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){
5757dd7cddfSDavid du Colombier werrstr("bad response from server");
5767dd7cddfSDavid du Colombier return -1;
5777dd7cddfSDavid du Colombier }
5787dd7cddfSDavid du Colombier buf[n] = 0;
5797dd7cddfSDavid du Colombier return atoi(p+1);
5807dd7cddfSDavid du Colombier }
5817dd7cddfSDavid du Colombier
5827dd7cddfSDavid du Colombier /* read in and crack the http headers, update u and r */
5837dd7cddfSDavid du Colombier void hhetag(char*, URL*, Range*);
5847dd7cddfSDavid du Colombier void hhmtime(char*, URL*, Range*);
5857dd7cddfSDavid du Colombier void hhclen(char*, URL*, Range*);
5867dd7cddfSDavid du Colombier void hhcrange(char*, URL*, Range*);
5877dd7cddfSDavid du Colombier void hhuri(char*, URL*, Range*);
5887dd7cddfSDavid du Colombier void hhlocation(char*, URL*, Range*);
589fd597ed8SDavid du Colombier void hhauth(char*, URL*, Range*);
5907dd7cddfSDavid du Colombier
5917dd7cddfSDavid du Colombier struct {
5927dd7cddfSDavid du Colombier char *name;
5937dd7cddfSDavid du Colombier void (*f)(char*, URL*, Range*);
5947dd7cddfSDavid du Colombier } headers[] = {
5957dd7cddfSDavid du Colombier { "etag:", hhetag },
5967dd7cddfSDavid du Colombier { "last-modified:", hhmtime },
5977dd7cddfSDavid du Colombier { "content-length:", hhclen },
5987dd7cddfSDavid du Colombier { "content-range:", hhcrange },
5997dd7cddfSDavid du Colombier { "uri:", hhuri },
6007dd7cddfSDavid du Colombier { "location:", hhlocation },
601fd597ed8SDavid du Colombier { "WWW-Authenticate:", hhauth },
6027dd7cddfSDavid du Colombier };
6037dd7cddfSDavid du Colombier int
httpheaders(int fd,int cfd,URL * u,Range * r)6049a747e4fSDavid du Colombier httpheaders(int fd, int cfd, URL *u, Range *r)
6057dd7cddfSDavid du Colombier {
6067dd7cddfSDavid du Colombier char buf[2048];
6077dd7cddfSDavid du Colombier char *p;
6087dd7cddfSDavid du Colombier int i, n;
6097dd7cddfSDavid du Colombier
6107dd7cddfSDavid du Colombier for(;;){
6117dd7cddfSDavid du Colombier n = getheader(fd, buf, sizeof(buf));
6127dd7cddfSDavid du Colombier if(n <= 0)
6137dd7cddfSDavid du Colombier break;
6149a747e4fSDavid du Colombier if(cfd >= 0)
6159a747e4fSDavid du Colombier fprint(cfd, "%s\n", buf);
6167dd7cddfSDavid du Colombier for(i = 0; i < nelem(headers); i++){
6177dd7cddfSDavid du Colombier n = strlen(headers[i].name);
6187dd7cddfSDavid du Colombier if(cistrncmp(buf, headers[i].name, n) == 0){
6197dd7cddfSDavid du Colombier /* skip field name and leading white */
6207dd7cddfSDavid du Colombier p = buf + n;
6217dd7cddfSDavid du Colombier while(*p == ' ' || *p == '\t')
6227dd7cddfSDavid du Colombier p++;
6237dd7cddfSDavid du Colombier
6247dd7cddfSDavid du Colombier (*headers[i].f)(p, u, r);
6257dd7cddfSDavid du Colombier break;
6267dd7cddfSDavid du Colombier }
6277dd7cddfSDavid du Colombier }
6287dd7cddfSDavid du Colombier }
6297dd7cddfSDavid du Colombier return n;
6307dd7cddfSDavid du Colombier }
6317dd7cddfSDavid du Colombier
6327dd7cddfSDavid du Colombier /*
6337dd7cddfSDavid du Colombier * read a single mime header, collect continuations.
6347dd7cddfSDavid du Colombier *
6357dd7cddfSDavid du Colombier * this routine assumes that there is a blank line twixt
6367dd7cddfSDavid du Colombier * the header and the message body, otherwise bytes will
6377dd7cddfSDavid du Colombier * be lost.
6387dd7cddfSDavid du Colombier */
6397dd7cddfSDavid du Colombier int
getheader(int fd,char * buf,int n)6407dd7cddfSDavid du Colombier getheader(int fd, char *buf, int n)
6417dd7cddfSDavid du Colombier {
6427dd7cddfSDavid du Colombier char *p, *e;
6437dd7cddfSDavid du Colombier int i;
6447dd7cddfSDavid du Colombier
6457dd7cddfSDavid du Colombier n--;
6467dd7cddfSDavid du Colombier p = buf;
6477dd7cddfSDavid du Colombier for(e = p + n; ; p += i){
6487dd7cddfSDavid du Colombier i = readline(fd, p, e-p);
6497dd7cddfSDavid du Colombier if(i < 0)
6507dd7cddfSDavid du Colombier return i;
6517dd7cddfSDavid du Colombier
6527dd7cddfSDavid du Colombier if(p == buf){
6537dd7cddfSDavid du Colombier /* first line */
6547dd7cddfSDavid du Colombier if(strchr(buf, ':') == nil)
6557dd7cddfSDavid du Colombier break; /* end of headers */
6567dd7cddfSDavid du Colombier } else {
6577dd7cddfSDavid du Colombier /* continuation line */
6587dd7cddfSDavid du Colombier if(*p != ' ' && *p != '\t'){
6597dd7cddfSDavid du Colombier unreadline(p);
6607dd7cddfSDavid du Colombier *p = 0;
6617dd7cddfSDavid du Colombier break; /* end of this header */
6627dd7cddfSDavid du Colombier }
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier }
66519d19a2eSDavid du Colombier if(headerprint)
66619d19a2eSDavid du Colombier print("%s\n", buf);
6677dd7cddfSDavid du Colombier
6687dd7cddfSDavid du Colombier if(debug)
6697dd7cddfSDavid du Colombier fprint(2, "%d <- %s\n", fd, buf);
6707dd7cddfSDavid du Colombier return p-buf;
6717dd7cddfSDavid du Colombier }
6727dd7cddfSDavid du Colombier
6737dd7cddfSDavid du Colombier void
hhetag(char * p,URL * u,Range *)6747dd7cddfSDavid du Colombier hhetag(char *p, URL *u, Range*)
6757dd7cddfSDavid du Colombier {
6767dd7cddfSDavid du Colombier if(u->etag != nil){
6777dd7cddfSDavid du Colombier if(strcmp(u->etag, p) != 0)
6787dd7cddfSDavid du Colombier sysfatal("file changed underfoot");
6797dd7cddfSDavid du Colombier } else
6807dd7cddfSDavid du Colombier u->etag = strdup(p);
6817dd7cddfSDavid du Colombier }
6827dd7cddfSDavid du Colombier
6837dd7cddfSDavid du Colombier char* monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
6847dd7cddfSDavid du Colombier
6857dd7cddfSDavid du Colombier void
hhmtime(char * p,URL * u,Range *)6867dd7cddfSDavid du Colombier hhmtime(char *p, URL *u, Range*)
6877dd7cddfSDavid du Colombier {
6887dd7cddfSDavid du Colombier char *month, *day, *yr, *hms;
6897dd7cddfSDavid du Colombier char *fields[6];
6907dd7cddfSDavid du Colombier Tm tm, now;
6917dd7cddfSDavid du Colombier int i;
6927dd7cddfSDavid du Colombier
6937dd7cddfSDavid du Colombier i = getfields(p, fields, 6, 1, " \t");
6947dd7cddfSDavid du Colombier if(i < 5)
6957dd7cddfSDavid du Colombier return;
6967dd7cddfSDavid du Colombier
6977dd7cddfSDavid du Colombier day = fields[1];
6987dd7cddfSDavid du Colombier month = fields[2];
6997dd7cddfSDavid du Colombier yr = fields[3];
7007dd7cddfSDavid du Colombier hms = fields[4];
7017dd7cddfSDavid du Colombier
7027dd7cddfSDavid du Colombier /* default time */
7037dd7cddfSDavid du Colombier now = *gmtime(time(0));
7047dd7cddfSDavid du Colombier tm = now;
705b8023fd7SDavid du Colombier tm.yday = 0;
7067dd7cddfSDavid du Colombier
7077dd7cddfSDavid du Colombier /* convert ascii month to a number twixt 1 and 12 */
7087dd7cddfSDavid du Colombier if(*month >= '0' && *month <= '9'){
7097dd7cddfSDavid du Colombier tm.mon = atoi(month) - 1;
7107dd7cddfSDavid du Colombier if(tm.mon < 0 || tm.mon > 11)
7117dd7cddfSDavid du Colombier tm.mon = 5;
7127dd7cddfSDavid du Colombier } else {
7137dd7cddfSDavid du Colombier for(p = month; *p; p++)
7147dd7cddfSDavid du Colombier *p = tolower(*p);
7157dd7cddfSDavid du Colombier for(i = 0; i < 12; i++)
7167dd7cddfSDavid du Colombier if(strncmp(&monthchars[i*3], month, 3) == 0){
7177dd7cddfSDavid du Colombier tm.mon = i;
7187dd7cddfSDavid du Colombier break;
7197dd7cddfSDavid du Colombier }
7207dd7cddfSDavid du Colombier }
7217dd7cddfSDavid du Colombier
7227dd7cddfSDavid du Colombier tm.mday = atoi(day);
7237dd7cddfSDavid du Colombier
7247dd7cddfSDavid du Colombier if(hms) {
7257dd7cddfSDavid du Colombier tm.hour = strtoul(hms, &p, 10);
7267dd7cddfSDavid du Colombier if(*p == ':') {
7277dd7cddfSDavid du Colombier p++;
7287dd7cddfSDavid du Colombier tm.min = strtoul(p, &p, 10);
7297dd7cddfSDavid du Colombier if(*p == ':') {
7307dd7cddfSDavid du Colombier p++;
7317dd7cddfSDavid du Colombier tm.sec = strtoul(p, &p, 10);
7327dd7cddfSDavid du Colombier }
7337dd7cddfSDavid du Colombier }
7347dd7cddfSDavid du Colombier if(tolower(*p) == 'p')
7357dd7cddfSDavid du Colombier tm.hour += 12;
7367dd7cddfSDavid du Colombier }
7377dd7cddfSDavid du Colombier
7387dd7cddfSDavid du Colombier if(yr) {
7397dd7cddfSDavid du Colombier tm.year = atoi(yr);
7407dd7cddfSDavid du Colombier if(tm.year >= 1900)
7417dd7cddfSDavid du Colombier tm.year -= 1900;
7427dd7cddfSDavid du Colombier } else {
7437dd7cddfSDavid du Colombier if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
7447dd7cddfSDavid du Colombier tm.year--;
7457dd7cddfSDavid du Colombier }
7467dd7cddfSDavid du Colombier
7477dd7cddfSDavid du Colombier strcpy(tm.zone, "GMT");
7487dd7cddfSDavid du Colombier /* convert to epoch seconds */
7497dd7cddfSDavid du Colombier u->mtime = tm2sec(&tm);
7507dd7cddfSDavid du Colombier }
7517dd7cddfSDavid du Colombier
7527dd7cddfSDavid du Colombier void
hhclen(char * p,URL *,Range * r)7537dd7cddfSDavid du Colombier hhclen(char *p, URL*, Range *r)
7547dd7cddfSDavid du Colombier {
7557dd7cddfSDavid du Colombier r->end = atoi(p);
7567dd7cddfSDavid du Colombier }
7577dd7cddfSDavid du Colombier
7587dd7cddfSDavid du Colombier void
hhcrange(char * p,URL *,Range * r)7597dd7cddfSDavid du Colombier hhcrange(char *p, URL*, Range *r)
7607dd7cddfSDavid du Colombier {
7617dd7cddfSDavid du Colombier char *x;
7627dd7cddfSDavid du Colombier vlong l;
7637dd7cddfSDavid du Colombier
7647dd7cddfSDavid du Colombier l = 0;
7657dd7cddfSDavid du Colombier x = strchr(p, '/');
7667dd7cddfSDavid du Colombier if(x)
7677dd7cddfSDavid du Colombier l = atoll(x+1);
76805406af2SDavid du Colombier if(l == 0) {
7697dd7cddfSDavid du Colombier x = strchr(p, '-');
7707dd7cddfSDavid du Colombier if(x)
7717dd7cddfSDavid du Colombier l = atoll(x+1);
77205406af2SDavid du Colombier }
7737dd7cddfSDavid du Colombier if(l)
7747dd7cddfSDavid du Colombier r->end = l;
7757dd7cddfSDavid du Colombier }
7767dd7cddfSDavid du Colombier
7777dd7cddfSDavid du Colombier void
hhuri(char * p,URL * u,Range *)7787dd7cddfSDavid du Colombier hhuri(char *p, URL *u, Range*)
7797dd7cddfSDavid du Colombier {
7807dd7cddfSDavid du Colombier if(*p != '<')
7817dd7cddfSDavid du Colombier return;
7827dd7cddfSDavid du Colombier u->redirect = strdup(p+1);
7837dd7cddfSDavid du Colombier p = strchr(u->redirect, '>');
7847dd7cddfSDavid du Colombier if(p != nil)
7857dd7cddfSDavid du Colombier *p = 0;
7867dd7cddfSDavid du Colombier }
7877dd7cddfSDavid du Colombier
7887dd7cddfSDavid du Colombier void
hhlocation(char * p,URL * u,Range *)7897dd7cddfSDavid du Colombier hhlocation(char *p, URL *u, Range*)
7907dd7cddfSDavid du Colombier {
7917dd7cddfSDavid du Colombier u->redirect = strdup(p);
7927dd7cddfSDavid du Colombier }
7937dd7cddfSDavid du Colombier
794fd597ed8SDavid du Colombier void
hhauth(char * p,URL * u,Range *)795fd597ed8SDavid du Colombier hhauth(char *p, URL *u, Range*)
796fd597ed8SDavid du Colombier {
797fd597ed8SDavid du Colombier char *f[4];
798fd597ed8SDavid du Colombier UserPasswd *up;
799fd597ed8SDavid du Colombier char *s, cred[64];
800fd597ed8SDavid du Colombier
801fd597ed8SDavid du Colombier if (cistrncmp(p, "basic ", 6) != 0)
802fd597ed8SDavid du Colombier sysfatal("only Basic authentication supported");
803fd597ed8SDavid du Colombier
804fd597ed8SDavid du Colombier if (gettokens(p, f, nelem(f), "\"") < 2)
805fd597ed8SDavid du Colombier sysfatal("garbled auth data");
806fd597ed8SDavid du Colombier
80774f16c81SDavid du Colombier if ((up = auth_getuserpasswd(auth_getkey, "proto=pass service=http server=%q realm=%q",
808fd597ed8SDavid du Colombier u->host, f[1])) == nil)
809fd597ed8SDavid du Colombier sysfatal("cannot authenticate");
810fd597ed8SDavid du Colombier
811fd597ed8SDavid du Colombier s = smprint("%s:%s", up->user, up->passwd);
812fd597ed8SDavid du Colombier if(enc64(cred, sizeof(cred), (uchar *)s, strlen(s)) == -1)
813fd597ed8SDavid du Colombier sysfatal("enc64");
814fd597ed8SDavid du Colombier free(s);
815fd597ed8SDavid du Colombier
816fd597ed8SDavid du Colombier assert(u->cred = strdup(cred));
817fd597ed8SDavid du Colombier }
818fd597ed8SDavid du Colombier
81959cc4ca5SDavid du Colombier enum
82059cc4ca5SDavid du Colombier {
82159cc4ca5SDavid du Colombier /* ftp return codes */
82259cc4ca5SDavid du Colombier Extra= 1,
82359cc4ca5SDavid du Colombier Success= 2,
82459cc4ca5SDavid du Colombier Incomplete= 3,
82559cc4ca5SDavid du Colombier TempFail= 4,
82659cc4ca5SDavid du Colombier PermFail= 5,
82759cc4ca5SDavid du Colombier
82859cc4ca5SDavid du Colombier Nnetdir= 64, /* max length of network directory paths */
82959cc4ca5SDavid du Colombier Ndialstr= 64, /* max length of dial strings */
83059cc4ca5SDavid du Colombier };
83159cc4ca5SDavid du Colombier
83259cc4ca5SDavid du Colombier int ftpcmd(int, char*, ...);
83359cc4ca5SDavid du Colombier int ftprcode(int, char*, int);
83459cc4ca5SDavid du Colombier int hello(int);
83559cc4ca5SDavid du Colombier int logon(int);
83659cc4ca5SDavid du Colombier int xfertype(int, char*);
83759cc4ca5SDavid du Colombier int passive(int, URL*);
83859cc4ca5SDavid du Colombier int active(int, URL*);
839e0d6d19cSDavid du Colombier int ftpxfer(int, Out*, Range*);
84059cc4ca5SDavid du Colombier int terminateftp(int, int);
84159cc4ca5SDavid du Colombier int getaddrport(char*, uchar*, uchar*);
842e0d6d19cSDavid du Colombier int ftprestart(int, Out*, URL*, Range*, long);
84359cc4ca5SDavid du Colombier
84459cc4ca5SDavid du Colombier int
doftp(URL * u,URL * px,Range * r,Out * out,long mtime)845e0d6d19cSDavid du Colombier doftp(URL *u, URL *px, Range *r, Out *out, long mtime)
84659cc4ca5SDavid du Colombier {
8479a747e4fSDavid du Colombier int pid, ctl, data, rv;
8489a747e4fSDavid du Colombier Waitmsg *w;
84959cc4ca5SDavid du Colombier char msg[64];
85059cc4ca5SDavid du Colombier char conndir[NETPATHLEN];
85159cc4ca5SDavid du Colombier char *p;
85259cc4ca5SDavid du Colombier
85305406af2SDavid du Colombier /* untested, proxy doesn't work with ftp (I think) */
85440ef9009SDavid du Colombier if(px->host == nil){
85559cc4ca5SDavid du Colombier ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0);
856e288d156SDavid du Colombier } else {
857e288d156SDavid du Colombier ctl = dial(netmkaddr(px->host, tcpdir, px->port), 0, conndir, 0);
858e288d156SDavid du Colombier }
859e288d156SDavid du Colombier
86059cc4ca5SDavid du Colombier if(ctl < 0)
86159cc4ca5SDavid du Colombier return Error;
86259cc4ca5SDavid du Colombier if(net == nil){
86359cc4ca5SDavid du Colombier p = strrchr(conndir, '/');
86459cc4ca5SDavid du Colombier *p = 0;
86559cc4ca5SDavid du Colombier snprint(tcpdir, sizeof(tcpdir), conndir);
86659cc4ca5SDavid du Colombier }
86759cc4ca5SDavid du Colombier
86859cc4ca5SDavid du Colombier initibuf();
86959cc4ca5SDavid du Colombier
87059cc4ca5SDavid du Colombier rv = hello(ctl);
87159cc4ca5SDavid du Colombier if(rv < 0)
87259cc4ca5SDavid du Colombier return terminateftp(ctl, rv);
87359cc4ca5SDavid du Colombier
87459cc4ca5SDavid du Colombier rv = logon(ctl);
87559cc4ca5SDavid du Colombier if(rv < 0)
87659cc4ca5SDavid du Colombier return terminateftp(ctl, rv);
87759cc4ca5SDavid du Colombier
87859cc4ca5SDavid du Colombier rv = xfertype(ctl, "I");
87959cc4ca5SDavid du Colombier if(rv < 0)
88059cc4ca5SDavid du Colombier return terminateftp(ctl, rv);
88159cc4ca5SDavid du Colombier
88259cc4ca5SDavid du Colombier /* if file is up to date and the right size, stop */
88359cc4ca5SDavid du Colombier if(ftprestart(ctl, out, u, r, mtime) > 0){
88459cc4ca5SDavid du Colombier close(ctl);
88559cc4ca5SDavid du Colombier return Eof;
88659cc4ca5SDavid du Colombier }
88759cc4ca5SDavid du Colombier
88859cc4ca5SDavid du Colombier /* first try passive mode, then active */
88959cc4ca5SDavid du Colombier data = passive(ctl, u);
89059cc4ca5SDavid du Colombier if(data < 0){
89159cc4ca5SDavid du Colombier data = active(ctl, u);
89259cc4ca5SDavid du Colombier if(data < 0)
89359cc4ca5SDavid du Colombier return Error;
89459cc4ca5SDavid du Colombier }
89559cc4ca5SDavid du Colombier
89659cc4ca5SDavid du Colombier /* fork */
89759cc4ca5SDavid du Colombier switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
89859cc4ca5SDavid du Colombier case -1:
89959cc4ca5SDavid du Colombier close(data);
90059cc4ca5SDavid du Colombier return terminateftp(ctl, Error);
90159cc4ca5SDavid du Colombier case 0:
90259cc4ca5SDavid du Colombier ftpxfer(data, out, r);
90359cc4ca5SDavid du Colombier close(data);
90459cc4ca5SDavid du Colombier _exits(0);
90559cc4ca5SDavid du Colombier default:
90659cc4ca5SDavid du Colombier close(data);
90759cc4ca5SDavid du Colombier break;
90859cc4ca5SDavid du Colombier }
90959cc4ca5SDavid du Colombier
91059cc4ca5SDavid du Colombier /* wait for reply message */
91159cc4ca5SDavid du Colombier rv = ftprcode(ctl, msg, sizeof(msg));
91259cc4ca5SDavid du Colombier close(ctl);
91359cc4ca5SDavid du Colombier
91459cc4ca5SDavid du Colombier /* wait for process to terminate */
9159a747e4fSDavid du Colombier w = nil;
91659cc4ca5SDavid du Colombier for(;;){
9179a747e4fSDavid du Colombier free(w);
9189a747e4fSDavid du Colombier w = wait();
9199a747e4fSDavid du Colombier if(w == nil)
92059cc4ca5SDavid du Colombier return Error;
9219a747e4fSDavid du Colombier if(w->pid == pid){
9229a747e4fSDavid du Colombier if(w->msg[0] == 0){
9239a747e4fSDavid du Colombier free(w);
92459cc4ca5SDavid du Colombier break;
9259a747e4fSDavid du Colombier }
9269a747e4fSDavid du Colombier werrstr("xfer: %s", w->msg);
9279a747e4fSDavid du Colombier free(w);
92859cc4ca5SDavid du Colombier return Error;
92959cc4ca5SDavid du Colombier }
93059cc4ca5SDavid du Colombier }
93159cc4ca5SDavid du Colombier
93259cc4ca5SDavid du Colombier switch(rv){
93359cc4ca5SDavid du Colombier case Success:
93459cc4ca5SDavid du Colombier return Eof;
93559cc4ca5SDavid du Colombier case TempFail:
93659cc4ca5SDavid du Colombier return Server;
93759cc4ca5SDavid du Colombier default:
93859cc4ca5SDavid du Colombier return Error;
93959cc4ca5SDavid du Colombier }
94059cc4ca5SDavid du Colombier }
94159cc4ca5SDavid du Colombier
94259cc4ca5SDavid du Colombier int
ftpcmd(int ctl,char * fmt,...)94359cc4ca5SDavid du Colombier ftpcmd(int ctl, char *fmt, ...)
94459cc4ca5SDavid du Colombier {
94559cc4ca5SDavid du Colombier va_list arg;
94659cc4ca5SDavid du Colombier char buf[2*1024], *s;
94759cc4ca5SDavid du Colombier
94859cc4ca5SDavid du Colombier va_start(arg, fmt);
9499a747e4fSDavid du Colombier s = vseprint(buf, buf + (sizeof(buf)-4) / sizeof(*buf), fmt, arg);
95059cc4ca5SDavid du Colombier va_end(arg);
95159cc4ca5SDavid du Colombier if(debug)
95259cc4ca5SDavid du Colombier fprint(2, "%d -> %s\n", ctl, buf);
95359cc4ca5SDavid du Colombier *s++ = '\r';
95459cc4ca5SDavid du Colombier *s++ = '\n';
95559cc4ca5SDavid du Colombier if(write(ctl, buf, s - buf) != s - buf)
95659cc4ca5SDavid du Colombier return -1;
95759cc4ca5SDavid du Colombier return 0;
95859cc4ca5SDavid du Colombier }
95959cc4ca5SDavid du Colombier
96059cc4ca5SDavid du Colombier int
ftprcode(int ctl,char * msg,int len)96159cc4ca5SDavid du Colombier ftprcode(int ctl, char *msg, int len)
96259cc4ca5SDavid du Colombier {
96359cc4ca5SDavid du Colombier int rv;
96459cc4ca5SDavid du Colombier int i;
965d9306527SDavid du Colombier char *p;
96659cc4ca5SDavid du Colombier
96759cc4ca5SDavid du Colombier len--; /* room for terminating null */
96859cc4ca5SDavid du Colombier for(;;){
96959cc4ca5SDavid du Colombier *msg = 0;
97059cc4ca5SDavid du Colombier i = readline(ctl, msg, len);
97159cc4ca5SDavid du Colombier if(i < 0)
97259cc4ca5SDavid du Colombier break;
97359cc4ca5SDavid du Colombier if(debug)
97459cc4ca5SDavid du Colombier fprint(2, "%d <- %s\n", ctl, msg);
97559cc4ca5SDavid du Colombier
97659cc4ca5SDavid du Colombier /* stop if not a continuation */
977d9306527SDavid du Colombier rv = strtol(msg, &p, 10);
978d9306527SDavid du Colombier if(rv >= 100 && rv < 600 && p==msg+3 && *p == ' ')
97959cc4ca5SDavid du Colombier return rv/100;
98059cc4ca5SDavid du Colombier }
98159cc4ca5SDavid du Colombier *msg = 0;
98259cc4ca5SDavid du Colombier
98359cc4ca5SDavid du Colombier return -1;
98459cc4ca5SDavid du Colombier }
98559cc4ca5SDavid du Colombier
98659cc4ca5SDavid du Colombier int
hello(int ctl)98759cc4ca5SDavid du Colombier hello(int ctl)
98859cc4ca5SDavid du Colombier {
98959cc4ca5SDavid du Colombier char msg[1024];
99059cc4ca5SDavid du Colombier
99159cc4ca5SDavid du Colombier /* wait for hello from other side */
99259cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success){
99359cc4ca5SDavid du Colombier werrstr("HELLO: %s", msg);
99459cc4ca5SDavid du Colombier return Server;
99559cc4ca5SDavid du Colombier }
99659cc4ca5SDavid du Colombier return 0;
99759cc4ca5SDavid du Colombier }
99859cc4ca5SDavid du Colombier
99959cc4ca5SDavid du Colombier int
getdec(char * p,int n)100059cc4ca5SDavid du Colombier getdec(char *p, int n)
100159cc4ca5SDavid du Colombier {
100259cc4ca5SDavid du Colombier int x = 0;
100359cc4ca5SDavid du Colombier int i;
100459cc4ca5SDavid du Colombier
100559cc4ca5SDavid du Colombier for(i = 0; i < n; i++)
100659cc4ca5SDavid du Colombier x = x*10 + (*p++ - '0');
100759cc4ca5SDavid du Colombier return x;
100859cc4ca5SDavid du Colombier }
100959cc4ca5SDavid du Colombier
101059cc4ca5SDavid du Colombier int
ftprestart(int ctl,Out * out,URL * u,Range * r,long mtime)1011e0d6d19cSDavid du Colombier ftprestart(int ctl, Out *out, URL *u, Range *r, long mtime)
101259cc4ca5SDavid du Colombier {
101359cc4ca5SDavid du Colombier Tm tm;
101459cc4ca5SDavid du Colombier char msg[1024];
101559cc4ca5SDavid du Colombier long x, rmtime;
101659cc4ca5SDavid du Colombier
101759cc4ca5SDavid du Colombier ftpcmd(ctl, "MDTM %s", u->page);
101859cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success){
101959cc4ca5SDavid du Colombier r->start = 0;
102059cc4ca5SDavid du Colombier return 0; /* need to do something */
102159cc4ca5SDavid du Colombier }
102259cc4ca5SDavid du Colombier
102359cc4ca5SDavid du Colombier /* decode modification time */
102459cc4ca5SDavid du Colombier if(strlen(msg) < 4 + 4 + 2 + 2 + 2 + 2 + 2){
102559cc4ca5SDavid du Colombier r->start = 0;
102659cc4ca5SDavid du Colombier return 0; /* need to do something */
102759cc4ca5SDavid du Colombier }
102859cc4ca5SDavid du Colombier memset(&tm, 0, sizeof(tm));
102959cc4ca5SDavid du Colombier tm.year = getdec(msg+4, 4) - 1900;
103059cc4ca5SDavid du Colombier tm.mon = getdec(msg+4+4, 2) - 1;
103159cc4ca5SDavid du Colombier tm.mday = getdec(msg+4+4+2, 2);
103259cc4ca5SDavid du Colombier tm.hour = getdec(msg+4+4+2+2, 2);
103359cc4ca5SDavid du Colombier tm.min = getdec(msg+4+4+2+2+2, 2);
103459cc4ca5SDavid du Colombier tm.sec = getdec(msg+4+4+2+2+2+2, 2);
103559cc4ca5SDavid du Colombier strcpy(tm.zone, "GMT");
103659cc4ca5SDavid du Colombier rmtime = tm2sec(&tm);
103759cc4ca5SDavid du Colombier if(rmtime > mtime)
103859cc4ca5SDavid du Colombier r->start = 0;
103959cc4ca5SDavid du Colombier
104059cc4ca5SDavid du Colombier /* get size */
104159cc4ca5SDavid du Colombier ftpcmd(ctl, "SIZE %s", u->page);
104259cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) == Success){
104359cc4ca5SDavid du Colombier x = atol(msg+4);
104459cc4ca5SDavid du Colombier if(r->start == x)
104559cc4ca5SDavid du Colombier return 1; /* we're up to date */
104659cc4ca5SDavid du Colombier r->end = x;
104759cc4ca5SDavid du Colombier }
104859cc4ca5SDavid du Colombier
104959cc4ca5SDavid du Colombier /* seek to restart point */
105059cc4ca5SDavid du Colombier if(r->start > 0){
105159cc4ca5SDavid du Colombier ftpcmd(ctl, "REST %lud", r->start);
1052e0d6d19cSDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) == Incomplete){
1053e0d6d19cSDavid du Colombier setoffset(out, r->start);
1054e0d6d19cSDavid du Colombier }else
105559cc4ca5SDavid du Colombier r->start = 0;
105659cc4ca5SDavid du Colombier }
105759cc4ca5SDavid du Colombier
105859cc4ca5SDavid du Colombier return 0; /* need to do something */
105959cc4ca5SDavid du Colombier }
106059cc4ca5SDavid du Colombier
106159cc4ca5SDavid du Colombier int
logon(int ctl)106259cc4ca5SDavid du Colombier logon(int ctl)
106359cc4ca5SDavid du Colombier {
106459cc4ca5SDavid du Colombier char msg[1024];
106559cc4ca5SDavid du Colombier
106659cc4ca5SDavid du Colombier /* login anonymous */
106759cc4ca5SDavid du Colombier ftpcmd(ctl, "USER anonymous");
106859cc4ca5SDavid du Colombier switch(ftprcode(ctl, msg, sizeof(msg))){
106959cc4ca5SDavid du Colombier case Success:
107059cc4ca5SDavid du Colombier return 0;
107159cc4ca5SDavid du Colombier case Incomplete:
107259cc4ca5SDavid du Colombier break; /* need password */
107359cc4ca5SDavid du Colombier default:
107459cc4ca5SDavid du Colombier werrstr("USER: %s", msg);
107559cc4ca5SDavid du Colombier return Server;
107659cc4ca5SDavid du Colombier }
107759cc4ca5SDavid du Colombier
107859cc4ca5SDavid du Colombier /* send user id as password */
107959cc4ca5SDavid du Colombier sprint(msg, "%s@closedmind.org", getuser());
108059cc4ca5SDavid du Colombier ftpcmd(ctl, "PASS %s", msg);
108159cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success){
108259cc4ca5SDavid du Colombier werrstr("PASS: %s", msg);
108359cc4ca5SDavid du Colombier return Server;
108459cc4ca5SDavid du Colombier }
108559cc4ca5SDavid du Colombier
108659cc4ca5SDavid du Colombier return 0;
108759cc4ca5SDavid du Colombier }
108859cc4ca5SDavid du Colombier
108959cc4ca5SDavid du Colombier int
xfertype(int ctl,char * t)109059cc4ca5SDavid du Colombier xfertype(int ctl, char *t)
109159cc4ca5SDavid du Colombier {
109259cc4ca5SDavid du Colombier char msg[1024];
109359cc4ca5SDavid du Colombier
109459cc4ca5SDavid du Colombier ftpcmd(ctl, "TYPE %s", t);
109559cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success){
109659cc4ca5SDavid du Colombier werrstr("TYPE %s: %s", t, msg);
109759cc4ca5SDavid du Colombier return Server;
109859cc4ca5SDavid du Colombier }
109959cc4ca5SDavid du Colombier
110059cc4ca5SDavid du Colombier return 0;
110159cc4ca5SDavid du Colombier }
110259cc4ca5SDavid du Colombier
110359cc4ca5SDavid du Colombier int
passive(int ctl,URL * u)110459cc4ca5SDavid du Colombier passive(int ctl, URL *u)
110559cc4ca5SDavid du Colombier {
110659cc4ca5SDavid du Colombier char msg[1024];
110759cc4ca5SDavid du Colombier char ipaddr[32];
110859cc4ca5SDavid du Colombier char *f[6];
110959cc4ca5SDavid du Colombier char *p;
111059cc4ca5SDavid du Colombier int fd;
111159cc4ca5SDavid du Colombier int port;
111259cc4ca5SDavid du Colombier char aport[12];
111359cc4ca5SDavid du Colombier
111459cc4ca5SDavid du Colombier ftpcmd(ctl, "PASV");
111559cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success)
111659cc4ca5SDavid du Colombier return Error;
111759cc4ca5SDavid du Colombier
111859cc4ca5SDavid du Colombier /* get address and port number from reply, this is AI */
111959cc4ca5SDavid du Colombier p = strchr(msg, '(');
112059cc4ca5SDavid du Colombier if(p == nil){
112159cc4ca5SDavid du Colombier for(p = msg+3; *p; p++)
112259cc4ca5SDavid du Colombier if(isdigit(*p))
112359cc4ca5SDavid du Colombier break;
112459cc4ca5SDavid du Colombier } else
112559cc4ca5SDavid du Colombier p++;
112659cc4ca5SDavid du Colombier if(getfields(p, f, 6, 0, ",)") < 6){
112759cc4ca5SDavid du Colombier werrstr("ftp protocol botch");
112859cc4ca5SDavid du Colombier return Server;
112959cc4ca5SDavid du Colombier }
113059cc4ca5SDavid du Colombier snprint(ipaddr, sizeof(ipaddr), "%s.%s.%s.%s",
113159cc4ca5SDavid du Colombier f[0], f[1], f[2], f[3]);
113259cc4ca5SDavid du Colombier port = ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff);
113359cc4ca5SDavid du Colombier sprint(aport, "%d", port);
113459cc4ca5SDavid du Colombier
113559cc4ca5SDavid du Colombier /* open data connection */
113659cc4ca5SDavid du Colombier fd = dial(netmkaddr(ipaddr, tcpdir, aport), 0, 0, 0);
113759cc4ca5SDavid du Colombier if(fd < 0){
113859cc4ca5SDavid du Colombier werrstr("passive mode failed: %r");
113959cc4ca5SDavid du Colombier return Error;
114059cc4ca5SDavid du Colombier }
114159cc4ca5SDavid du Colombier
114259cc4ca5SDavid du Colombier /* tell remote to send a file */
114359cc4ca5SDavid du Colombier ftpcmd(ctl, "RETR %s", u->page);
114459cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
114559cc4ca5SDavid du Colombier werrstr("RETR %s: %s", u->page, msg);
114659cc4ca5SDavid du Colombier return Error;
114759cc4ca5SDavid du Colombier }
114859cc4ca5SDavid du Colombier return fd;
114959cc4ca5SDavid du Colombier }
115059cc4ca5SDavid du Colombier
115159cc4ca5SDavid du Colombier int
active(int ctl,URL * u)115259cc4ca5SDavid du Colombier active(int ctl, URL *u)
115359cc4ca5SDavid du Colombier {
115459cc4ca5SDavid du Colombier char msg[1024];
115559cc4ca5SDavid du Colombier char dir[40], ldir[40];
115659cc4ca5SDavid du Colombier uchar ipaddr[4];
115759cc4ca5SDavid du Colombier uchar port[2];
115859cc4ca5SDavid du Colombier int lcfd, dfd, afd;
115959cc4ca5SDavid du Colombier
116059cc4ca5SDavid du Colombier /* announce a port for the call back */
116159cc4ca5SDavid du Colombier snprint(msg, sizeof(msg), "%s!*!0", tcpdir);
116259cc4ca5SDavid du Colombier afd = announce(msg, dir);
116359cc4ca5SDavid du Colombier if(afd < 0)
116459cc4ca5SDavid du Colombier return Error;
116559cc4ca5SDavid du Colombier
116659cc4ca5SDavid du Colombier /* get a local address/port of the annoucement */
116759cc4ca5SDavid du Colombier if(getaddrport(dir, ipaddr, port) < 0){
116859cc4ca5SDavid du Colombier close(afd);
116959cc4ca5SDavid du Colombier return Error;
117059cc4ca5SDavid du Colombier }
117159cc4ca5SDavid du Colombier
117259cc4ca5SDavid du Colombier /* tell remote side address and port*/
117359cc4ca5SDavid du Colombier ftpcmd(ctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2],
117459cc4ca5SDavid du Colombier ipaddr[3], port[0], port[1]);
117559cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Success){
117659cc4ca5SDavid du Colombier close(afd);
117759cc4ca5SDavid du Colombier werrstr("active: %s", msg);
117859cc4ca5SDavid du Colombier return Error;
117959cc4ca5SDavid du Colombier }
118059cc4ca5SDavid du Colombier
118159cc4ca5SDavid du Colombier /* tell remote to send a file */
118259cc4ca5SDavid du Colombier ftpcmd(ctl, "RETR %s", u->page);
118359cc4ca5SDavid du Colombier if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
118459cc4ca5SDavid du Colombier close(afd);
118559cc4ca5SDavid du Colombier werrstr("RETR: %s", msg);
118659cc4ca5SDavid du Colombier return Server;
118759cc4ca5SDavid du Colombier }
118859cc4ca5SDavid du Colombier
118959cc4ca5SDavid du Colombier /* wait for a connection */
119059cc4ca5SDavid du Colombier lcfd = listen(dir, ldir);
119159cc4ca5SDavid du Colombier if(lcfd < 0){
119259cc4ca5SDavid du Colombier close(afd);
119359cc4ca5SDavid du Colombier return Error;
119459cc4ca5SDavid du Colombier }
119559cc4ca5SDavid du Colombier dfd = accept(lcfd, ldir);
119659cc4ca5SDavid du Colombier if(dfd < 0){
119759cc4ca5SDavid du Colombier close(afd);
119859cc4ca5SDavid du Colombier close(lcfd);
119959cc4ca5SDavid du Colombier return Error;
120059cc4ca5SDavid du Colombier }
120159cc4ca5SDavid du Colombier close(afd);
120259cc4ca5SDavid du Colombier close(lcfd);
120359cc4ca5SDavid du Colombier
120459cc4ca5SDavid du Colombier return dfd;
120559cc4ca5SDavid du Colombier }
120659cc4ca5SDavid du Colombier
120759cc4ca5SDavid du Colombier int
ftpxfer(int in,Out * out,Range * r)1208e0d6d19cSDavid du Colombier ftpxfer(int in, Out *out, Range *r)
120959cc4ca5SDavid du Colombier {
121059cc4ca5SDavid du Colombier char buf[1024];
121159cc4ca5SDavid du Colombier long vtime;
121259cc4ca5SDavid du Colombier int i, n;
121359cc4ca5SDavid du Colombier
121459cc4ca5SDavid du Colombier vtime = 0;
121559cc4ca5SDavid du Colombier for(n = 0;;n += i){
121659cc4ca5SDavid du Colombier i = read(in, buf, sizeof(buf));
121759cc4ca5SDavid du Colombier if(i == 0)
121859cc4ca5SDavid du Colombier break;
121959cc4ca5SDavid du Colombier if(i < 0)
122059cc4ca5SDavid du Colombier return Error;
1221e0d6d19cSDavid du Colombier if(output(out, buf, i) != i)
122259cc4ca5SDavid du Colombier return Error;
122359cc4ca5SDavid du Colombier r->start += i;
12242cba34c7SDavid du Colombier if(verbose && (vtime != time(0) || r->start == r->end)) {
122559cc4ca5SDavid du Colombier vtime = time(0);
122659cc4ca5SDavid du Colombier fprint(2, "%ld %ld\n", r->start, r->end);
122759cc4ca5SDavid du Colombier }
122859cc4ca5SDavid du Colombier }
122959cc4ca5SDavid du Colombier return n;
123059cc4ca5SDavid du Colombier }
123159cc4ca5SDavid du Colombier
123259cc4ca5SDavid du Colombier int
terminateftp(int ctl,int rv)123359cc4ca5SDavid du Colombier terminateftp(int ctl, int rv)
123459cc4ca5SDavid du Colombier {
123559cc4ca5SDavid du Colombier close(ctl);
123659cc4ca5SDavid du Colombier return rv;
123759cc4ca5SDavid du Colombier }
123859cc4ca5SDavid du Colombier
123959cc4ca5SDavid du Colombier /*
124059cc4ca5SDavid du Colombier * case insensitive strcmp (why aren't these in libc?)
124159cc4ca5SDavid du Colombier */
12427dd7cddfSDavid du Colombier int
cistrncmp(char * a,char * b,int n)12437dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
12447dd7cddfSDavid du Colombier {
12457dd7cddfSDavid du Colombier while(n-- > 0){
12467dd7cddfSDavid du Colombier if(tolower(*a++) != tolower(*b++))
12477dd7cddfSDavid du Colombier return -1;
12487dd7cddfSDavid du Colombier }
12497dd7cddfSDavid du Colombier return 0;
12507dd7cddfSDavid du Colombier }
12517dd7cddfSDavid du Colombier
12527dd7cddfSDavid du Colombier int
cistrcmp(char * a,char * b)12537dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
12547dd7cddfSDavid du Colombier {
12557dd7cddfSDavid du Colombier while(*a || *b)
12567dd7cddfSDavid du Colombier if(tolower(*a++) != tolower(*b++))
12577dd7cddfSDavid du Colombier return -1;
12587dd7cddfSDavid du Colombier
12597dd7cddfSDavid du Colombier return 0;
12607dd7cddfSDavid du Colombier }
12617dd7cddfSDavid du Colombier
12627dd7cddfSDavid du Colombier /*
12637dd7cddfSDavid du Colombier * buffered io
12647dd7cddfSDavid du Colombier */
12657dd7cddfSDavid du Colombier struct
12667dd7cddfSDavid du Colombier {
12677dd7cddfSDavid du Colombier char *rp;
12687dd7cddfSDavid du Colombier char *wp;
12697dd7cddfSDavid du Colombier char buf[4*1024];
12707dd7cddfSDavid du Colombier } b;
12717dd7cddfSDavid du Colombier
12727dd7cddfSDavid du Colombier void
initibuf(void)12737dd7cddfSDavid du Colombier initibuf(void)
12747dd7cddfSDavid du Colombier {
12757dd7cddfSDavid du Colombier b.rp = b.wp = b.buf;
12767dd7cddfSDavid du Colombier }
12777dd7cddfSDavid du Colombier
127859cc4ca5SDavid du Colombier /*
127959cc4ca5SDavid du Colombier * read a possibly buffered line, strip off trailing while
128059cc4ca5SDavid du Colombier */
12817dd7cddfSDavid du Colombier int
readline(int fd,char * buf,int len)12827dd7cddfSDavid du Colombier readline(int fd, char *buf, int len)
12837dd7cddfSDavid du Colombier {
12847dd7cddfSDavid du Colombier int n;
12857dd7cddfSDavid du Colombier char *p;
128659cc4ca5SDavid du Colombier int eof = 0;
12877dd7cddfSDavid du Colombier
12887dd7cddfSDavid du Colombier len--;
12897dd7cddfSDavid du Colombier
12907dd7cddfSDavid du Colombier for(p = buf;;){
12917dd7cddfSDavid du Colombier if(b.rp >= b.wp){
12927dd7cddfSDavid du Colombier n = read(fd, b.wp, sizeof(b.buf)/2);
12937dd7cddfSDavid du Colombier if(n < 0)
12947dd7cddfSDavid du Colombier return -1;
129559cc4ca5SDavid du Colombier if(n == 0){
129659cc4ca5SDavid du Colombier eof = 1;
12977dd7cddfSDavid du Colombier break;
129859cc4ca5SDavid du Colombier }
12997dd7cddfSDavid du Colombier b.wp += n;
13007dd7cddfSDavid du Colombier }
13017dd7cddfSDavid du Colombier n = *b.rp++;
13027dd7cddfSDavid du Colombier if(len > 0){
13037dd7cddfSDavid du Colombier *p++ = n;
13047dd7cddfSDavid du Colombier len--;
13057dd7cddfSDavid du Colombier }
13067dd7cddfSDavid du Colombier if(n == '\n')
13077dd7cddfSDavid du Colombier break;
13087dd7cddfSDavid du Colombier }
13097dd7cddfSDavid du Colombier
13107dd7cddfSDavid du Colombier /* drop trailing white */
13117dd7cddfSDavid du Colombier for(;;){
13127dd7cddfSDavid du Colombier if(p <= buf)
13137dd7cddfSDavid du Colombier break;
13147dd7cddfSDavid du Colombier n = *(p-1);
13157dd7cddfSDavid du Colombier if(n != ' ' && n != '\t' && n != '\r' && n != '\n')
13167dd7cddfSDavid du Colombier break;
13177dd7cddfSDavid du Colombier p--;
13187dd7cddfSDavid du Colombier }
13197dd7cddfSDavid du Colombier *p = 0;
132059cc4ca5SDavid du Colombier
132159cc4ca5SDavid du Colombier if(eof && p == buf)
132259cc4ca5SDavid du Colombier return -1;
132359cc4ca5SDavid du Colombier
13247dd7cddfSDavid du Colombier return p-buf;
13257dd7cddfSDavid du Colombier }
13267dd7cddfSDavid du Colombier
13277dd7cddfSDavid du Colombier void
unreadline(char * line)13287dd7cddfSDavid du Colombier unreadline(char *line)
13297dd7cddfSDavid du Colombier {
13307dd7cddfSDavid du Colombier int i, n;
13317dd7cddfSDavid du Colombier
13327dd7cddfSDavid du Colombier i = strlen(line);
13337dd7cddfSDavid du Colombier n = b.wp-b.rp;
13347dd7cddfSDavid du Colombier memmove(&b.buf[i+1], b.rp, n);
13357dd7cddfSDavid du Colombier memmove(b.buf, line, i);
13367dd7cddfSDavid du Colombier b.buf[i] = '\n';
13377dd7cddfSDavid du Colombier b.rp = b.buf;
13387dd7cddfSDavid du Colombier b.wp = b.rp + i + 1 + n;
13397dd7cddfSDavid du Colombier }
13407dd7cddfSDavid du Colombier
13417dd7cddfSDavid du Colombier int
readibuf(int fd,char * buf,int len)13427dd7cddfSDavid du Colombier readibuf(int fd, char *buf, int len)
13437dd7cddfSDavid du Colombier {
13447dd7cddfSDavid du Colombier int n;
13457dd7cddfSDavid du Colombier
13467dd7cddfSDavid du Colombier n = b.wp-b.rp;
13477dd7cddfSDavid du Colombier if(n > 0){
13487dd7cddfSDavid du Colombier if(n > len)
13497dd7cddfSDavid du Colombier n = len;
13507dd7cddfSDavid du Colombier memmove(buf, b.rp, n);
13517dd7cddfSDavid du Colombier b.rp += n;
13527dd7cddfSDavid du Colombier return n;
13537dd7cddfSDavid du Colombier }
13547dd7cddfSDavid du Colombier return read(fd, buf, len);
13557dd7cddfSDavid du Colombier }
13567dd7cddfSDavid du Colombier
13577dd7cddfSDavid du Colombier int
dfprint(int fd,char * fmt,...)13587dd7cddfSDavid du Colombier dfprint(int fd, char *fmt, ...)
13597dd7cddfSDavid du Colombier {
13607dd7cddfSDavid du Colombier char buf[4*1024];
13617dd7cddfSDavid du Colombier va_list arg;
13627dd7cddfSDavid du Colombier
13637dd7cddfSDavid du Colombier va_start(arg, fmt);
13649a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, arg);
13657dd7cddfSDavid du Colombier va_end(arg);
13667dd7cddfSDavid du Colombier if(debug)
13677dd7cddfSDavid du Colombier fprint(2, "%d -> %s", fd, buf);
13687dd7cddfSDavid du Colombier return fprint(fd, "%s", buf);
13697dd7cddfSDavid du Colombier }
137059cc4ca5SDavid du Colombier
137159cc4ca5SDavid du Colombier int
getaddrport(char * dir,uchar * ipaddr,uchar * port)137259cc4ca5SDavid du Colombier getaddrport(char *dir, uchar *ipaddr, uchar *port)
137359cc4ca5SDavid du Colombier {
137459cc4ca5SDavid du Colombier char buf[256];
137559cc4ca5SDavid du Colombier int fd, i;
137659cc4ca5SDavid du Colombier char *p;
137759cc4ca5SDavid du Colombier
137859cc4ca5SDavid du Colombier snprint(buf, sizeof(buf), "%s/local", dir);
137959cc4ca5SDavid du Colombier fd = open(buf, OREAD);
138059cc4ca5SDavid du Colombier if(fd < 0)
138159cc4ca5SDavid du Colombier return -1;
138259cc4ca5SDavid du Colombier i = read(fd, buf, sizeof(buf)-1);
138359cc4ca5SDavid du Colombier close(fd);
138459cc4ca5SDavid du Colombier if(i <= 0)
138559cc4ca5SDavid du Colombier return -1;
138659cc4ca5SDavid du Colombier buf[i] = 0;
138759cc4ca5SDavid du Colombier p = strchr(buf, '!');
138859cc4ca5SDavid du Colombier if(p != nil)
138959cc4ca5SDavid du Colombier *p++ = 0;
139059cc4ca5SDavid du Colombier v4parseip(ipaddr, buf);
139159cc4ca5SDavid du Colombier i = atoi(p);
139259cc4ca5SDavid du Colombier port[0] = i>>8;
139359cc4ca5SDavid du Colombier port[1] = i;
139459cc4ca5SDavid du Colombier return 0;
139559cc4ca5SDavid du Colombier }
1396e0d6d19cSDavid du Colombier
1397e0d6d19cSDavid du Colombier void
md5free(DigestState * state)1398e0d6d19cSDavid du Colombier md5free(DigestState *state)
1399e0d6d19cSDavid du Colombier {
1400e0d6d19cSDavid du Colombier uchar x[MD5dlen];
1401e0d6d19cSDavid du Colombier md5(nil, 0, x, state);
1402e0d6d19cSDavid du Colombier }
1403e0d6d19cSDavid du Colombier
1404e0d6d19cSDavid du Colombier DigestState*
md5dup(DigestState * state)1405e0d6d19cSDavid du Colombier md5dup(DigestState *state)
1406e0d6d19cSDavid du Colombier {
1407e0d6d19cSDavid du Colombier char *p;
1408e0d6d19cSDavid du Colombier
1409e0d6d19cSDavid du Colombier p = md5pickle(state);
1410e0d6d19cSDavid du Colombier if(p == nil)
1411e0d6d19cSDavid du Colombier sysfatal("md5pickle: %r");
1412e0d6d19cSDavid du Colombier state = md5unpickle(p);
1413e0d6d19cSDavid du Colombier if(state == nil)
1414e0d6d19cSDavid du Colombier sysfatal("md5unpickle: %r");
1415e0d6d19cSDavid du Colombier free(p);
1416e0d6d19cSDavid du Colombier return state;
1417e0d6d19cSDavid du Colombier }
1418e0d6d19cSDavid du Colombier
1419e0d6d19cSDavid du Colombier void
setoffset(Out * out,int offset)1420e0d6d19cSDavid du Colombier setoffset(Out *out, int offset)
1421e0d6d19cSDavid du Colombier {
1422e0d6d19cSDavid du Colombier md5free(out->curr);
1423e0d6d19cSDavid du Colombier if(offset == 0)
1424e0d6d19cSDavid du Colombier out->curr = md5(nil, 0, nil, nil);
1425e0d6d19cSDavid du Colombier else
1426e0d6d19cSDavid du Colombier out->curr = nil;
1427e0d6d19cSDavid du Colombier out->offset = offset;
142805406af2SDavid du Colombier out->written = offset;
142905406af2SDavid du Colombier if(ofile != nil)
143005406af2SDavid du Colombier if(seek(out->fd, offset, 0) != offset)
143105406af2SDavid du Colombier sysfatal("seek: %r");
1432e0d6d19cSDavid du Colombier }
1433e0d6d19cSDavid du Colombier
1434e0d6d19cSDavid du Colombier /*
1435e0d6d19cSDavid du Colombier * write some output, discarding it (but keeping track)
1436e0d6d19cSDavid du Colombier * if we've already written it. if we've gone backwards,
1437e0d6d19cSDavid du Colombier * verify that everything previously written matches
1438e0d6d19cSDavid du Colombier * that which would have been written from the current
1439e0d6d19cSDavid du Colombier * output.
1440e0d6d19cSDavid du Colombier */
1441e0d6d19cSDavid du Colombier int
output(Out * out,char * buf,int nb)1442e0d6d19cSDavid du Colombier output(Out *out, char *buf, int nb)
1443e0d6d19cSDavid du Colombier {
1444e0d6d19cSDavid du Colombier int n, d;
1445e0d6d19cSDavid du Colombier uchar m0[MD5dlen], m1[MD5dlen];
1446e0d6d19cSDavid du Colombier
1447e0d6d19cSDavid du Colombier n = nb;
1448e0d6d19cSDavid du Colombier d = out->written - out->offset;
1449e0d6d19cSDavid du Colombier assert(d >= 0);
1450e0d6d19cSDavid du Colombier if(d > 0){
1451e0d6d19cSDavid du Colombier if(n < d){
1452e0d6d19cSDavid du Colombier if(out->curr != nil)
1453e0d6d19cSDavid du Colombier md5((uchar*)buf, n, nil, out->curr);
1454e0d6d19cSDavid du Colombier out->offset += n;
1455e0d6d19cSDavid du Colombier return n;
1456e0d6d19cSDavid du Colombier }
1457e0d6d19cSDavid du Colombier if(out->curr != nil){
1458e0d6d19cSDavid du Colombier md5((uchar*)buf, d, m0, out->curr);
1459e0d6d19cSDavid du Colombier out->curr = nil;
1460e0d6d19cSDavid du Colombier md5(nil, 0, m1, md5dup(out->hiwat));
1461e0d6d19cSDavid du Colombier if(memcmp(m0, m1, MD5dlen) != 0){
1462e0d6d19cSDavid du Colombier fprint(2, "integrity check failure at offset %d\n", out->written);
1463e0d6d19cSDavid du Colombier return -1;
1464e0d6d19cSDavid du Colombier }
1465e0d6d19cSDavid du Colombier }
1466e0d6d19cSDavid du Colombier buf += d;
1467e0d6d19cSDavid du Colombier n -= d;
1468e0d6d19cSDavid du Colombier out->offset += d;
1469e0d6d19cSDavid du Colombier }
1470e0d6d19cSDavid du Colombier if(n > 0){
1471e0d6d19cSDavid du Colombier out->hiwat = md5((uchar*)buf, n, nil, out->hiwat);
1472e0d6d19cSDavid du Colombier n = write(out->fd, buf, n);
1473e0d6d19cSDavid du Colombier if(n > 0){
1474e0d6d19cSDavid du Colombier out->offset += n;
1475e0d6d19cSDavid du Colombier out->written += n;
1476e0d6d19cSDavid du Colombier }
1477e0d6d19cSDavid du Colombier }
1478e0d6d19cSDavid du Colombier return n + d;
1479e0d6d19cSDavid du Colombier }
1480fd597ed8SDavid du Colombier
1481