📄 hget.c
字号:
tm.year--; } strcpy(tm.zone, "GMT"); /* convert to epoch seconds */ u->mtime = tm2sec(&tm);}voidhhclen(char *p, URL*, Range *r){ r->end = atoi(p);}voidhhcrange(char *p, URL*, Range *r){ char *x; vlong l; l = 0; x = strchr(p, '/'); if(x) l = atoll(x+1); if(l == 0) { x = strchr(p, '-'); if(x) l = atoll(x+1); } if(l) r->end = l;}voidhhuri(char *p, URL *u, Range*){ if(*p != '<') return; u->redirect = strdup(p+1); p = strchr(u->redirect, '>'); if(p != nil) *p = 0;}voidhhlocation(char *p, URL *u, Range*){ u->redirect = strdup(p);}voidhhauth(char *p, URL *u, Range*){ char *f[4]; UserPasswd *up; char *s, cred[64]; if (cistrncmp(p, "basic ", 6) != 0) sysfatal("only Basic authentication supported"); if (gettokens(p, f, nelem(f), "\"") < 2) sysfatal("garbled auth data"); if ((up = auth_getuserpasswd(auth_getkey, "proto=pass service=http server=%q realm=%q", u->host, f[1])) == nil) sysfatal("cannot authenticate"); s = smprint("%s:%s", up->user, up->passwd); if(enc64(cred, sizeof(cred), (uchar *)s, strlen(s)) == -1) sysfatal("enc64"); free(s); assert(u->cred = strdup(cred));}enum{ /* ftp return codes */ Extra= 1, Success= 2, Incomplete= 3, TempFail= 4, PermFail= 5, Nnetdir= 64, /* max length of network directory paths */ Ndialstr= 64, /* max length of dial strings */};int ftpcmd(int, char*, ...);int ftprcode(int, char*, int);int hello(int);int logon(int);int xfertype(int, char*);int passive(int, URL*);int active(int, URL*);int ftpxfer(int, Out*, Range*);int terminateftp(int, int);int getaddrport(char*, uchar*, uchar*);int ftprestart(int, Out*, URL*, Range*, long);intdoftp(URL *u, URL *px, Range *r, Out *out, long mtime){ int pid, ctl, data, rv; Waitmsg *w; char msg[64]; char conndir[NETPATHLEN]; char *p; /* untested, proxy doesn't work with ftp (I think) */ if(px->host == nil){ ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0); } else { ctl = dial(netmkaddr(px->host, tcpdir, px->port), 0, conndir, 0); } if(ctl < 0) return Error; if(net == nil){ p = strrchr(conndir, '/'); *p = 0; snprint(tcpdir, sizeof(tcpdir), conndir); } initibuf(); rv = hello(ctl); if(rv < 0) return terminateftp(ctl, rv); rv = logon(ctl); if(rv < 0) return terminateftp(ctl, rv); rv = xfertype(ctl, "I"); if(rv < 0) return terminateftp(ctl, rv); /* if file is up to date and the right size, stop */ if(ftprestart(ctl, out, u, r, mtime) > 0){ close(ctl); return Eof; } /* first try passive mode, then active */ data = passive(ctl, u); if(data < 0){ data = active(ctl, u); if(data < 0) return Error; } /* fork */ switch(pid = rfork(RFPROC|RFFDG|RFMEM)){ case -1: close(data); return terminateftp(ctl, Error); case 0: ftpxfer(data, out, r); close(data); _exits(0); default: close(data); break; } /* wait for reply message */ rv = ftprcode(ctl, msg, sizeof(msg)); close(ctl); /* wait for process to terminate */ w = nil; for(;;){ free(w); w = wait(); if(w == nil) return Error; if(w->pid == pid){ if(w->msg[0] == 0){ free(w); break; } werrstr("xfer: %s", w->msg); free(w); return Error; } } switch(rv){ case Success: return Eof; case TempFail: return Server; default: return Error; }}intftpcmd(int ctl, char *fmt, ...){ va_list arg; char buf[2*1024], *s; va_start(arg, fmt); s = vseprint(buf, buf + (sizeof(buf)-4) / sizeof(*buf), fmt, arg); va_end(arg); if(debug) fprint(2, "%d -> %s\n", ctl, buf); *s++ = '\r'; *s++ = '\n'; if(write(ctl, buf, s - buf) != s - buf) return -1; return 0;}intftprcode(int ctl, char *msg, int len){ int rv; int i; char *p; len--; /* room for terminating null */ for(;;){ *msg = 0; i = readline(ctl, msg, len); if(i < 0) break; if(debug) fprint(2, "%d <- %s\n", ctl, msg); /* stop if not a continuation */ rv = strtol(msg, &p, 10); if(rv >= 100 && rv < 600 && p==msg+3 && *p == ' ') return rv/100; } *msg = 0; return -1;}inthello(int ctl){ char msg[1024]; /* wait for hello from other side */ if(ftprcode(ctl, msg, sizeof(msg)) != Success){ werrstr("HELLO: %s", msg); return Server; } return 0;}intgetdec(char *p, int n){ int x = 0; int i; for(i = 0; i < n; i++) x = x*10 + (*p++ - '0'); return x;}intftprestart(int ctl, Out *out, URL *u, Range *r, long mtime){ Tm tm; char msg[1024]; long x, rmtime; ftpcmd(ctl, "MDTM %s", u->page); if(ftprcode(ctl, msg, sizeof(msg)) != Success){ r->start = 0; return 0; /* need to do something */ } /* decode modification time */ if(strlen(msg) < 4 + 4 + 2 + 2 + 2 + 2 + 2){ r->start = 0; return 0; /* need to do something */ } memset(&tm, 0, sizeof(tm)); tm.year = getdec(msg+4, 4) - 1900; tm.mon = getdec(msg+4+4, 2) - 1; tm.mday = getdec(msg+4+4+2, 2); tm.hour = getdec(msg+4+4+2+2, 2); tm.min = getdec(msg+4+4+2+2+2, 2); tm.sec = getdec(msg+4+4+2+2+2+2, 2); strcpy(tm.zone, "GMT"); rmtime = tm2sec(&tm); if(rmtime > mtime) r->start = 0; /* get size */ ftpcmd(ctl, "SIZE %s", u->page); if(ftprcode(ctl, msg, sizeof(msg)) == Success){ x = atol(msg+4); if(r->start == x) return 1; /* we're up to date */ r->end = x; } /* seek to restart point */ if(r->start > 0){ ftpcmd(ctl, "REST %lud", r->start); if(ftprcode(ctl, msg, sizeof(msg)) == Incomplete){ setoffset(out, r->start); }else r->start = 0; } return 0; /* need to do something */}intlogon(int ctl){ char msg[1024]; /* login anonymous */ ftpcmd(ctl, "USER anonymous"); switch(ftprcode(ctl, msg, sizeof(msg))){ case Success: return 0; case Incomplete: break; /* need password */ default: werrstr("USER: %s", msg); return Server; } /* send user id as password */ sprint(msg, "%s@closedmind.org", getuser()); ftpcmd(ctl, "PASS %s", msg); if(ftprcode(ctl, msg, sizeof(msg)) != Success){ werrstr("PASS: %s", msg); return Server; } return 0;}intxfertype(int ctl, char *t){ char msg[1024]; ftpcmd(ctl, "TYPE %s", t); if(ftprcode(ctl, msg, sizeof(msg)) != Success){ werrstr("TYPE %s: %s", t, msg); return Server; } return 0;}intpassive(int ctl, URL *u){ char msg[1024]; char ipaddr[32]; char *f[6]; char *p; int fd; int port; char aport[12]; ftpcmd(ctl, "PASV"); if(ftprcode(ctl, msg, sizeof(msg)) != Success) return Error; /* get address and port number from reply, this is AI */ p = strchr(msg, '('); if(p == nil){ for(p = msg+3; *p; p++) if(isdigit(*p)) break; } else p++; if(getfields(p, f, 6, 0, ",)") < 6){ werrstr("ftp protocol botch"); return Server; } snprint(ipaddr, sizeof(ipaddr), "%s.%s.%s.%s", f[0], f[1], f[2], f[3]); port = ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff); sprint(aport, "%d", port); /* open data connection */ fd = dial(netmkaddr(ipaddr, tcpdir, aport), 0, 0, 0); if(fd < 0){ werrstr("passive mode failed: %r"); return Error; } /* tell remote to send a file */ ftpcmd(ctl, "RETR %s", u->page); if(ftprcode(ctl, msg, sizeof(msg)) != Extra){ werrstr("RETR %s: %s", u->page, msg); return Error; } return fd;}intactive(int ctl, URL *u){ char msg[1024]; char dir[40], ldir[40]; uchar ipaddr[4]; uchar port[2]; int lcfd, dfd, afd; /* announce a port for the call back */ snprint(msg, sizeof(msg), "%s!*!0", tcpdir); afd = announce(msg, dir); if(afd < 0) return Error; /* get a local address/port of the annoucement */ if(getaddrport(dir, ipaddr, port) < 0){ close(afd); return Error; } /* tell remote side address and port*/ ftpcmd(ctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], port[0], port[1]); if(ftprcode(ctl, msg, sizeof(msg)) != Success){ close(afd); werrstr("active: %s", msg); return Error; } /* tell remote to send a file */ ftpcmd(ctl, "RETR %s", u->page); if(ftprcode(ctl, msg, sizeof(msg)) != Extra){ close(afd); werrstr("RETR: %s", msg); return Server; } /* wait for a connection */ lcfd = listen(dir, ldir); if(lcfd < 0){ close(afd); return Error; } dfd = accept(lcfd, ldir); if(dfd < 0){ close(afd); close(lcfd); return Error; } close(afd); close(lcfd); return dfd;}intftpxfer(int in, Out *out, Range *r){ char buf[1024]; long vtime; int i, n; vtime = 0; for(n = 0;;n += i){ i = read(in, buf, sizeof(buf)); if(i == 0) break; if(i < 0) return Error; if(output(out, buf, i) != i) return Error; r->start += i; if(verbose && (vtime != time(0) || r->start == r->end)) { vtime = time(0); fprint(2, "%ld %ld\n", r->start, r->end); } } return n;}intterminateftp(int ctl, int rv){ close(ctl); return rv;}/* * case insensitive strcmp (why aren't these in libc?) */intcistrncmp(char *a, char *b, int n){ while(n-- > 0){ if(tolower(*a++) != tolower(*b++)) return -1; } return 0;}intcistrcmp(char *a, char *b){ while(*a || *b) if(tolower(*a++) != tolower(*b++)) return -1; return 0;}/* * buffered io */struct{ char *rp; char *wp; char buf[4*1024];} b;voidinitibuf(void){ b.rp = b.wp = b.buf;}/* * read a possibly buffered line, strip off trailing while */intreadline(int fd, char *buf, int len){ int n; char *p; int eof = 0; len--; for(p = buf;;){ if(b.rp >= b.wp){ n = read(fd, b.wp, sizeof(b.buf)/2); if(n < 0) return -1; if(n == 0){ eof = 1; 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; if(eof && p == buf) return -1; return p-buf;}voidunreadline(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;}intreadibuf(int fd, 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 read(fd, buf, len);}intdfprint(int fd, char *fmt, ...){ char buf[4*1024]; va_list arg; va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); if(debug) fprint(2, "%d -> %s", fd, buf); return fprint(fd, "%s", buf);}intgetaddrport(char *dir, uchar *ipaddr, uchar *port){ char buf[256]; int fd, i; char *p; snprint(buf, sizeof(buf), "%s/local", dir); fd = open(buf, OREAD); if(fd < 0) return -1; i = read(fd, buf, sizeof(buf)-1); close(fd); if(i <= 0) return -1; buf[i] = 0; p = strchr(buf, '!'); if(p != nil) *p++ = 0; v4parseip(ipaddr, buf); i = atoi(p); port[0] = i>>8; port[1] = i; return 0;}voidmd5free(DigestState *state){ uchar x[MD5dlen]; md5(nil, 0, x, state);}DigestState*md5dup(DigestState *state){ char *p; p = md5pickle(state); if(p == nil) sysfatal("md5pickle: %r"); state = md5unpickle(p); if(state == nil) sysfatal("md5unpickle: %r"); free(p); return state;}voidsetoffset(Out *out, int offset){ md5free(out->curr); if(offset == 0) out->curr = md5(nil, 0, nil, nil); else out->curr = nil; out->offset = offset; out->written = offset; if(ofile != nil) if(seek(out->fd, offset, 0) != offset) sysfatal("seek: %r");}/* * write some output, discarding it (but keeping track) * if we've already written it. if we've gone backwards, * verify that everything previously written matches * that which would have been written from the current * output. */intoutput(Out *out, char *buf, int nb){ int n, d; uchar m0[MD5dlen], m1[MD5dlen]; n = nb; d = out->written - out->offset; assert(d >= 0); if(d > 0){ if(n < d){ if(out->curr != nil) md5((uchar*)buf, n, nil, out->curr); out->offset += n; return n; } if(out->curr != nil){ md5((uchar*)buf, d, m0, out->curr); out->curr = nil; md5(nil, 0, m1, md5dup(out->hiwat)); if(memcmp(m0, m1, MD5dlen) != 0){ fprint(2, "integrity check failure at offset %d\n", out->written); return -1; } } buf += d; n -= d; out->offset += d; } if(n > 0){ out->hiwat = md5((uchar*)buf, n, nil, out->hiwat); n = write(out->fd, buf, n); if(n > 0){ out->offset += n; out->written += n; } } return n + d;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -