#include <u.h>#include <libc.h>#include <ctype.h>#include <bio.h>#include <ip.h> typedef struct URL URL;struct URL{ int method; char *host; char *port; char *page; char *etag; char *redirect; char *postbody; long mtime;}; typedef struct Range Range;struct Range{ long start; /* only 2 gig supported, tdb */ long end;}; enum{ Http, Ftp, Other}; enum{ Eof = 0, Error = -1, Server = -2, Changed = -3,}; int debug;char *ofile; int doftp(URL*, Range*, int, long);int dohttp(URL*, Range*, int, long);int crackurl(URL*, char*);Range* crackrange(char*);int getheader(int, char*, int);int httpheaders(int, int, URL*, Range*);int httprcode(int);int cistrncmp(char*, char*, int);int cistrcmp(char*, char*);void initibuf(void);int readline(int, char*, int);int readibuf(int, char*, int);int dfprint(int, char*, ...);void unreadline(char*); int verbose;char *net;char tcpdir[64]; struct { char *name; int (*f)(URL*, Range*, int, long);} method[] = { [Http] { "http", dohttp }, [Ftp] { "ftp", doftp }, [Other] { "_______", nil },}; voidusage(void){ fprint(2, "usage: %s [-v] [-o outfile] [-p body] [-x netmtpt] url\n", argv0); exits("usage");} voidmain(int argc, char **argv){ URL u; Range r; int fd, errs, n; ulong mtime; Dir *d; char postbody[4096], *p, *e, *t; ofile = nil; p = postbody; e = p + sizeof(postbody); r.start = 0; r.end = -1; mtime = 0; memset(&u, 0, sizeof(u)); ARGBEGIN { case 'o': ofile = ARGF(); break; case 'd': debug = 1; break; case 'v': verbose = 1; break; case 'x': net = ARGF(); if(net == nil) usage(); break; case 'p': t = ARGF(); if(t == nil) usage(); if(p != postbody) p = seprint(p, e, "&%s", t); else p = seprint(p, e, "%s", t); u.postbody = postbody; break; default: usage(); } ARGEND; if(net != nil){ if(strlen(net) > sizeof(tcpdir)-5) sysfatal("network mount point too long"); snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net); } else snprint(tcpdir, sizeof(tcpdir), "tcp"); if(argc != 1) usage(); fd = 1; if(ofile != nil){ d = dirstat(ofile); if(d == nil){ fd = create(ofile, OWRITE, 0664); if(fd < 0) sysfatal("creating %s: %r", ofile); } else { fd = open(ofile, OWRITE); if(fd < 0) sysfatal("can't open %s: %r", ofile); r.start = d->length; mtime = d->mtime; free(d); } } errs = 0; if(crackurl(&u, argv[0]) < 0) sysfatal("%r"); for(;;){ /* transfer data */ werrstr(""); seek(fd, 0, 0); n = (*method[u.method].f)(&u, &r, fd, mtime); switch(n){ case Eof: exits(0); break; case Error: if(errs++ < 10) continue; sysfatal("too many errors with no progress %r"); break; case Server: sysfatal("server returned: %r"); break; } /* forward progress */ errs = 0; r.start += n; if(r.start >= r.end) break; } exits(0);} intcrackurl(URL *u, char *s){ char *p; int i; if(u->host != nil){ free(u->host); u->host = nil; } if(u->page != nil){ free(u->page); u->page = nil; } /* get type */ u->method = Other; for(p = s; *p; p++){ if(*p == '/'){ u->method = Http; p = s; break; } if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){ *p = 0; p += 3; for(i = 0; i < nelem(method); i++){ if(cistrcmp(s, method[i].name) == 0){ u->method = i; break; } } break; } } if(u->method == Other){ werrstr("unsupported URL type %s", s); return -1; } /* get system */ s = p; p = strchr(s, '/'); if(p == nil){ u->host = strdup(s); u->page = strdup("/"); } else { u->page = strdup(p); *p = 0; u->host = strdup(s); *p = '/'; } if(p = strchr(u->host, ':')) { *p++ = 0; u->port = p; } else u->port = method[u->method].name; if(*(u->host) == 0){ werrstr("bad url, null host"); return -1; } return 0;} char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; struct{ int fd; long mtime;} note; voidcatch(void*, char*){ Dir d; nulldir(&d); d.mtime = note.mtime; if(dirfwstat(note.fd, &d) < 0) sysfatal("catch: can't dirfwstat: %r"); noted(NDFLT);} intdohttp(URL *u, Range *r, int out, long mtime){ int fd, cfd; int redirect, loop; int n, rv, code; long tot, vtime; Tm *tm; char buf[1024]; char err[ERRMAX]; /* always move back to a previous 512 byte bound because some * servers can't seem to deal with requests that start at the * end of the file */ if(r->start) r->start = ((r->start-1)/512)*512; /* loop for redirects, requires reading both response code and headers */ fd = -1; for(loop = 0; loop < 32; loop++){ fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0); if(fd < 0) return Error; /* write request, use range if not start of file */ if(u->postbody == nil){ dfprint(fd, "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "User-agent: Plan9/hget\r\n", u->page, u->host); } else { dfprint(fd, "POST %s HTTP/1.0\r\n" "Host: %s\r\n" "Content-type: application/x-www-form-urlencoded\r\n" "Content-length: %d\r\n" "User-agent: Plan9/hget\r\n" "\r\n", u->page, u->host, strlen(u->postbody)); dfprint(fd, "%s", u->postbody); } if(r->start != 0){ dfprint(fd, "Range: bytes=%d-\n", r->start); if(u->etag != nil){ dfprint(fd, "If-range: %s\n", u->etag); } else { tm = gmtime(mtime); dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n", day[tm->wday], tm->mday, month[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec); } } if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){ if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){ while((n = read(cfd, buf, sizeof buf)) > 0){ if(debug) write(2, buf, n); write(fd, buf, n); } }else{ close(cfd); cfd = -1; } } dfprint(fd, "\r\n", u->host); redirect = 0; initibuf(); code = httprcode(fd); switch(code){ case Error: /* connection timed out */ case Eof: close(fd); close(cfd); return code; case 200: /* OK */ case 201: /* Created */ case 202: /* Accepted */ if(ofile == nil && r->start != 0) sysfatal("page changed underfoot"); break; case 204: /* No Content */ sysfatal("No Content"); case 206: /* Partial Content */ seek(out, r->start, 0); break; case 301: /* Moved Permanently */ case 302: /* Moved Temporarily */ redirect = 1; u->postbody = nil; break; case 304: /* Not Modified */ break; case 400: /* Bad Request */ sysfatal("Bad Request"); case 401: /* Unauthorized */ case 402: /* ??? */ sysfatal("Unauthorized"); case 403: /* Forbidden */ sysfatal("Forbidden by server"); case 404: /* Not Found */ sysfatal("Not found on server"); case 500: /* Internal server error */ sysfatal("Server choked"); case 501: /* Not implemented */ sysfatal("Server can't do it!"); case 502: /* Bad gateway */ sysfatal("Bad gateway"); case 503: /* Service unavailable */ sysfatal("Service unavailable"); default: sysfatal("Unknown response code"); } if(u->redirect != nil){ free(u->redirect); u->redirect = nil; } rv = httpheaders(fd, cfd, u, r); close(cfd); if(rv != 0){ close(fd); return rv; } if(!redirect) break; if(u->redirect == nil) sysfatal("redirect: no URL"); if(crackurl(u, u->redirect) < 0) sysfatal("redirect: %r"); } /* transfer whatever you get */ if(ofile != nil && u->mtime != 0){ note.fd = out; note.mtime = u->mtime; notify(catch); } tot = 0; vtime = 0; for(;;){ n = readibuf(fd, buf, sizeof(buf)); if(n <= 0) break; if(write(out, buf, n) != n) break; tot += n; if(verbose && vtime != time(0)) { vtime = time(0); fprint(2, "%ld %ld\n", r->start+tot, r->end); } } notify(nil); close(fd); if(ofile != nil && u->mtime != 0){ Dir d; rerrstr(err, sizeof err); nulldir(&d); d.mtime = u->mtime; if(dirfwstat(out, &d) < 0) fprint(2, "couldn't set mtime: %r\n"); errstr(err, sizeof err); } return tot;} /* get the http response code */inthttprcode(int fd){ int n; char *p; char buf[256]; n = readline(fd, buf, sizeof(buf)-1); if(n <= 0) return n; if(debug) fprint(2, "%d <- %s\n", fd, buf); p = strchr(buf, ' '); if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){ werrstr("bad response from server"); return -1; } buf[n] = 0; return atoi(p+1);} /* read in and crack the http headers, update u and r */void hhetag(char*, URL*, Range*);void hhmtime(char*, URL*, Range*);void hhclen(char*, URL*, Range*);void hhcrange(char*, URL*, Range*);void hhuri(char*, URL*, Range*);void hhlocation(char*, URL*, Range*);Lines 1–500 of 1269. Load more
#include <u.h> #include <libc.h> #include <ctype.h> #include <bio.h> #include <ip.h> typedef struct URL URL; struct URL { int method; char *host; char *port; char *page; char *etag; char *redirect; char *postbody; long mtime; }; typedef struct Range Range; struct Range { long start; /* only 2 gig supported, tdb */ long end; }; enum { Http, Ftp, Other }; enum { Eof = 0, Error = -1, Server = -2, Changed = -3, }; int debug; char *ofile; int doftp(URL*, Range*, int, long); int dohttp(URL*, Range*, int, long); int crackurl(URL*, char*); Range* crackrange(char*); int getheader(int, char*, int); int httpheaders(int, int, URL*, Range*); int httprcode(int); int cistrncmp(char*, char*, int); int cistrcmp(char*, char*); void initibuf(void); int readline(int, char*, int); int readibuf(int, char*, int); int dfprint(int, char*, ...); void unreadline(char*); int verbose; char *net; char tcpdir[64]; struct { char *name; int (*f)(URL*, Range*, int, long); } method[] = { [Http] { "http", dohttp }, [Ftp] { "ftp", doftp }, [Other] { "_______", nil }, }; void usage(void) { fprint(2, "usage: %s [-v] [-o outfile] [-p body] [-x netmtpt] url\n", argv0); exits("usage"); } void main(int argc, char **argv) { URL u; Range r; int fd, errs, n; ulong mtime; Dir *d; char postbody[4096], *p, *e, *t; ofile = nil; p = postbody; e = p + sizeof(postbody); r.start = 0; r.end = -1; mtime = 0; memset(&u, 0, sizeof(u)); ARGBEGIN { case 'o': ofile = ARGF(); break; case 'd': debug = 1; break; case 'v': verbose = 1; break; case 'x': net = ARGF(); if(net == nil) usage(); break; case 'p': t = ARGF(); if(t == nil) usage(); if(p != postbody) p = seprint(p, e, "&%s", t); else p = seprint(p, e, "%s", t); u.postbody = postbody; break; default: usage(); } ARGEND; if(net != nil){ if(strlen(net) > sizeof(tcpdir)-5) sysfatal("network mount point too long"); snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net); } else snprint(tcpdir, sizeof(tcpdir), "tcp"); if(argc != 1) usage(); fd = 1; if(ofile != nil){ d = dirstat(ofile); if(d == nil){ fd = create(ofile, OWRITE, 0664); if(fd < 0) sysfatal("creating %s: %r", ofile); } else { fd = open(ofile, OWRITE); if(fd < 0) sysfatal("can't open %s: %r", ofile); r.start = d->length; mtime = d->mtime; free(d); } } errs = 0; if(crackurl(&u, argv[0]) < 0) sysfatal("%r"); for(;;){ /* transfer data */ werrstr(""); seek(fd, 0, 0); n = (*method[u.method].f)(&u, &r, fd, mtime); switch(n){ case Eof: exits(0); break; case Error: if(errs++ < 10) continue; sysfatal("too many errors with no progress %r"); break; case Server: sysfatal("server returned: %r"); break; } /* forward progress */ errs = 0; r.start += n; if(r.start >= r.end) break; } exits(0); } int crackurl(URL *u, char *s) { char *p; int i; if(u->host != nil){ free(u->host); u->host = nil; } if(u->page != nil){ free(u->page); u->page = nil; } /* get type */ u->method = Other; for(p = s; *p; p++){ if(*p == '/'){ u->method = Http; p = s; break; } if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){ *p = 0; p += 3; for(i = 0; i < nelem(method); i++){ if(cistrcmp(s, method[i].name) == 0){ u->method = i; break; } } break; } } if(u->method == Other){ werrstr("unsupported URL type %s", s); return -1; } /* get system */ s = p; p = strchr(s, '/'); if(p == nil){ u->host = strdup(s); u->page = strdup("/"); } else { u->page = strdup(p); *p = 0; u->host = strdup(s); *p = '/'; } if(p = strchr(u->host, ':')) { *p++ = 0; u->port = p; } else u->port = method[u->method].name; if(*(u->host) == 0){ werrstr("bad url, null host"); return -1; } return 0; } char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct { int fd; long mtime; } note; void catch(void*, char*) { Dir d; nulldir(&d); d.mtime = note.mtime; if(dirfwstat(note.fd, &d) < 0) sysfatal("catch: can't dirfwstat: %r"); noted(NDFLT); } int dohttp(URL *u, Range *r, int out, long mtime) { int fd, cfd; int redirect, loop; int n, rv, code; long tot, vtime; Tm *tm; char buf[1024]; char err[ERRMAX]; /* always move back to a previous 512 byte bound because some * servers can't seem to deal with requests that start at the * end of the file */ if(r->start) r->start = ((r->start-1)/512)*512; /* loop for redirects, requires reading both response code and headers */ fd = -1; for(loop = 0; loop < 32; loop++){ fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0); if(fd < 0) return Error; /* write request, use range if not start of file */ if(u->postbody == nil){ dfprint(fd, "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "User-agent: Plan9/hget\r\n", u->page, u->host); } else { dfprint(fd, "POST %s HTTP/1.0\r\n" "Host: %s\r\n" "Content-type: application/x-www-form-urlencoded\r\n" "Content-length: %d\r\n" "User-agent: Plan9/hget\r\n" "\r\n", u->page, u->host, strlen(u->postbody)); dfprint(fd, "%s", u->postbody); } if(r->start != 0){ dfprint(fd, "Range: bytes=%d-\n", r->start); if(u->etag != nil){ dfprint(fd, "If-range: %s\n", u->etag); } else { tm = gmtime(mtime); dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n", day[tm->wday], tm->mday, month[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec); } } if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){ if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){ while((n = read(cfd, buf, sizeof buf)) > 0){ if(debug) write(2, buf, n); write(fd, buf, n); } }else{ close(cfd); cfd = -1; } } dfprint(fd, "\r\n", u->host); redirect = 0; initibuf(); code = httprcode(fd); switch(code){ case Error: /* connection timed out */ case Eof: close(fd); close(cfd); return code; case 200: /* OK */ case 201: /* Created */ case 202: /* Accepted */ if(ofile == nil && r->start != 0) sysfatal("page changed underfoot"); break; case 204: /* No Content */ sysfatal("No Content"); case 206: /* Partial Content */ seek(out, r->start, 0); break; case 301: /* Moved Permanently */ case 302: /* Moved Temporarily */ redirect = 1; u->postbody = nil; break; case 304: /* Not Modified */ break; case 400: /* Bad Request */ sysfatal("Bad Request"); case 401: /* Unauthorized */ case 402: /* ??? */ sysfatal("Unauthorized"); case 403: /* Forbidden */ sysfatal("Forbidden by server"); case 404: /* Not Found */ sysfatal("Not found on server"); case 500: /* Internal server error */ sysfatal("Server choked"); case 501: /* Not implemented */ sysfatal("Server can't do it!"); case 502: /* Bad gateway */ sysfatal("Bad gateway"); case 503: /* Service unavailable */ sysfatal("Service unavailable"); default: sysfatal("Unknown response code"); } if(u->redirect != nil){ free(u->redirect); u->redirect = nil; } rv = httpheaders(fd, cfd, u, r); close(cfd); if(rv != 0){ close(fd); return rv; } if(!redirect) break; if(u->redirect == nil) sysfatal("redirect: no URL"); if(crackurl(u, u->redirect) < 0) sysfatal("redirect: %r"); } /* transfer whatever you get */ if(ofile != nil && u->mtime != 0){ note.fd = out; note.mtime = u->mtime; notify(catch); } tot = 0; vtime = 0; for(;;){ n = readibuf(fd, buf, sizeof(buf)); if(n <= 0) break; if(write(out, buf, n) != n) break; tot += n; if(verbose && vtime != time(0)) { vtime = time(0); fprint(2, "%ld %ld\n", r->start+tot, r->end); } } notify(nil); close(fd); if(ofile != nil && u->mtime != 0){ Dir d; rerrstr(err, sizeof err); nulldir(&d); d.mtime = u->mtime; if(dirfwstat(out, &d) < 0) fprint(2, "couldn't set mtime: %r\n"); errstr(err, sizeof err); } return tot; } /* get the http response code */ int httprcode(int fd) { int n; char *p; char buf[256]; n = readline(fd, buf, sizeof(buf)-1); if(n <= 0) return n; if(debug) fprint(2, "%d <- %s\n", fd, buf); p = strchr(buf, ' '); if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){ werrstr("bad response from server"); return -1; } buf[n] = 0; return atoi(p+1); } /* read in and crack the http headers, update u and r */ void hhetag(char*, URL*, Range*); void hhmtime(char*, URL*, Range*); void hhclen(char*, URL*, Range*); void hhcrange(char*, URL*, Range*); void hhuri(char*, URL*, Range*); void hhlocation(char*, URL*, Range*);