hget.c 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <ctype.h>
  4. #include <bio.h>
  5. #include <ip.h>
  6. #include <libsec.h>
  7. typedef struct URL URL;
  8. struct URL
  9. {
  10. int method;
  11. char *host;
  12. char *port;
  13. char *page;
  14. char *etag;
  15. char *redirect;
  16. char *postbody;
  17. long mtime;
  18. };
  19. typedef struct Range Range;
  20. struct Range
  21. {
  22. long start; /* only 2 gig supported, tdb */
  23. long end;
  24. };
  25. enum
  26. {
  27. Http,
  28. Https,
  29. Ftp,
  30. Other
  31. };
  32. enum
  33. {
  34. Eof = 0,
  35. Error = -1,
  36. Server = -2,
  37. Changed = -3,
  38. };
  39. int debug;
  40. char *ofile;
  41. int doftp(URL*, URL*, Range*, int, long);
  42. int dohttp(URL*, URL*, Range*, int, long);
  43. int crackurl(URL*, char*);
  44. Range* crackrange(char*);
  45. int getheader(int, char*, int);
  46. int httpheaders(int, int, URL*, Range*);
  47. int httprcode(int);
  48. int cistrncmp(char*, char*, int);
  49. int cistrcmp(char*, char*);
  50. void initibuf(void);
  51. int readline(int, char*, int);
  52. int readibuf(int, char*, int);
  53. int dfprint(int, char*, ...);
  54. void unreadline(char*);
  55. int verbose;
  56. char *net;
  57. char tcpdir[64];
  58. int headerprint;
  59. struct {
  60. char *name;
  61. int (*f)(URL*, URL*, Range*, int, long);
  62. } method[] = {
  63. [Http] { "http", dohttp },
  64. [Https] { "https", dohttp },
  65. [Ftp] { "ftp", doftp },
  66. [Other] { "_______", nil },
  67. };
  68. void
  69. usage(void)
  70. {
  71. fprint(2, "usage: %s [-hv] [-o outfile] [-p body] [-x netmtpt] url\n", argv0);
  72. exits("usage");
  73. }
  74. void
  75. main(int argc, char **argv)
  76. {
  77. URL u;
  78. Range r;
  79. int fd, errs, n;
  80. ulong mtime;
  81. Dir *d;
  82. char postbody[4096], *p, *e, *t, *hpx;
  83. URL px; // Proxy
  84. ofile = nil;
  85. p = postbody;
  86. e = p + sizeof(postbody);
  87. r.start = 0;
  88. r.end = -1;
  89. mtime = 0;
  90. memset(&u, 0, sizeof(u));
  91. memset(&px, 0, sizeof(px));
  92. hpx = getenv("httpproxy");
  93. ARGBEGIN {
  94. case 'o':
  95. ofile = ARGF();
  96. break;
  97. case 'd':
  98. debug = 1;
  99. break;
  100. case 'h':
  101. headerprint = 1;
  102. break;
  103. case 'v':
  104. verbose = 1;
  105. break;
  106. case 'x':
  107. net = ARGF();
  108. if(net == nil)
  109. usage();
  110. break;
  111. case 'p':
  112. t = ARGF();
  113. if(t == nil)
  114. usage();
  115. if(p != postbody)
  116. p = seprint(p, e, "&%s", t);
  117. else
  118. p = seprint(p, e, "%s", t);
  119. u.postbody = postbody;
  120. break;
  121. default:
  122. usage();
  123. } ARGEND;
  124. if(net != nil){
  125. if(strlen(net) > sizeof(tcpdir)-5)
  126. sysfatal("network mount point too long");
  127. snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net);
  128. } else
  129. snprint(tcpdir, sizeof(tcpdir), "tcp");
  130. if(argc != 1)
  131. usage();
  132. fd = 1;
  133. if(ofile != nil){
  134. d = dirstat(ofile);
  135. if(d == nil){
  136. fd = create(ofile, OWRITE, 0664);
  137. if(fd < 0)
  138. sysfatal("creating %s: %r", ofile);
  139. } else {
  140. fd = open(ofile, OWRITE);
  141. if(fd < 0)
  142. sysfatal("can't open %s: %r", ofile);
  143. r.start = d->length;
  144. mtime = d->mtime;
  145. free(d);
  146. }
  147. }
  148. errs = 0;
  149. if(crackurl(&u, argv[0]) < 0)
  150. sysfatal("%r");
  151. if(hpx && crackurl(&px, hpx) < 0)
  152. sysfatal("%r");
  153. for(;;){
  154. /* transfer data */
  155. werrstr("");
  156. seek(fd, 0, 0);
  157. n = (*method[u.method].f)(&u, &px, &r, fd, mtime);
  158. switch(n){
  159. case Eof:
  160. exits(0);
  161. break;
  162. case Error:
  163. if(errs++ < 10)
  164. continue;
  165. sysfatal("too many errors with no progress %r");
  166. break;
  167. case Server:
  168. sysfatal("server returned: %r");
  169. break;
  170. }
  171. /* forward progress */
  172. errs = 0;
  173. r.start += n;
  174. if(r.start >= r.end)
  175. break;
  176. }
  177. exits(0);
  178. }
  179. int
  180. crackurl(URL *u, char *s)
  181. {
  182. char *p;
  183. int i;
  184. if(u->host != nil){
  185. free(u->host);
  186. u->host = nil;
  187. }
  188. if(u->page != nil){
  189. free(u->page);
  190. u->page = nil;
  191. }
  192. /* get type */
  193. u->method = Other;
  194. for(p = s; *p; p++){
  195. if(*p == '/'){
  196. u->method = Http;
  197. p = s;
  198. break;
  199. }
  200. if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){
  201. *p = 0;
  202. p += 3;
  203. for(i = 0; i < nelem(method); i++){
  204. if(cistrcmp(s, method[i].name) == 0){
  205. u->method = i;
  206. break;
  207. }
  208. }
  209. break;
  210. }
  211. }
  212. if(u->method == Other){
  213. werrstr("unsupported URL type %s", s);
  214. return -1;
  215. }
  216. /* get system */
  217. s = p;
  218. p = strchr(s, '/');
  219. if(p == nil){
  220. u->host = strdup(s);
  221. u->page = strdup("/");
  222. } else {
  223. u->page = strdup(p);
  224. *p = 0;
  225. u->host = strdup(s);
  226. *p = '/';
  227. }
  228. if(p = strchr(u->host, ':')) {
  229. *p++ = 0;
  230. u->port = p;
  231. } else
  232. u->port = method[u->method].name;
  233. if(*(u->host) == 0){
  234. werrstr("bad url, null host");
  235. return -1;
  236. }
  237. return 0;
  238. }
  239. char *day[] = {
  240. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  241. };
  242. char *month[] = {
  243. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  244. };
  245. struct
  246. {
  247. int fd;
  248. long mtime;
  249. } note;
  250. void
  251. catch(void*, char*)
  252. {
  253. Dir d;
  254. nulldir(&d);
  255. d.mtime = note.mtime;
  256. if(dirfwstat(note.fd, &d) < 0)
  257. sysfatal("catch: can't dirfwstat: %r");
  258. noted(NDFLT);
  259. }
  260. int
  261. dohttp(URL *u, URL *px, Range *r, int out, long mtime)
  262. {
  263. int fd, cfd;
  264. int redirect, loop;
  265. int n, rv, code;
  266. long tot, vtime;
  267. Tm *tm;
  268. char buf[1024];
  269. char err[ERRMAX];
  270. /* always move back to a previous 512 byte bound because some
  271. * servers can't seem to deal with requests that start at the
  272. * end of the file
  273. */
  274. if(r->start)
  275. r->start = ((r->start-1)/512)*512;
  276. /* loop for redirects, requires reading both response code and headers */
  277. fd = -1;
  278. for(loop = 0; loop < 32; loop++){
  279. if(px->host == nil){
  280. fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0);
  281. } else {
  282. fd = dial(netmkaddr(px->host, tcpdir, px->port), 0, 0, 0);
  283. }
  284. if(fd < 0)
  285. return Error;
  286. if(u->method == Https){
  287. int tfd;
  288. TLSconn conn;
  289. memset(&conn, 0, sizeof conn);
  290. tfd = tlsClient(fd, &conn);
  291. if(tfd < 0){
  292. fprint(2, "tlsClient: %r\n");
  293. close(fd);
  294. return Error;
  295. }
  296. /* BUG: check cert here? */
  297. if(conn.cert)
  298. free(conn.cert);
  299. close(fd);
  300. fd = tfd;
  301. }
  302. /* write request, use range if not start of file */
  303. if(u->postbody == nil){
  304. if(px->host == nil){
  305. dfprint(fd, "GET %s HTTP/1.0\r\n"
  306. "Host: %s\r\n"
  307. "User-agent: Plan9/hget\r\n"
  308. "Cache-Control: no-cache\r\n"
  309. "Pragma: no-cache\r\n",
  310. u->page, u->host);
  311. } else {
  312. dfprint(fd, "GET http://%s%s HTTP/1.0\r\n"
  313. "Host: %s\r\n"
  314. "User-agent: Plan9/hget\r\n"
  315. "Cache-Control: no-cache\r\n"
  316. "Pragma: no-cache\r\n",
  317. u->host, u->page, u->host);
  318. }
  319. } else {
  320. dfprint(fd, "POST %s HTTP/1.0\r\n"
  321. "Host: %s\r\n"
  322. "Content-type: application/x-www-form-urlencoded\r\n"
  323. "Content-length: %d\r\n"
  324. "User-agent: Plan9/hget\r\n"
  325. "\r\n",
  326. u->page, u->host, strlen(u->postbody));
  327. dfprint(fd, "%s", u->postbody);
  328. }
  329. if(r->start != 0){
  330. dfprint(fd, "Range: bytes=%d-\n", r->start);
  331. if(u->etag != nil){
  332. dfprint(fd, "If-range: %s\n", u->etag);
  333. } else {
  334. tm = gmtime(mtime);
  335. dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n",
  336. day[tm->wday], tm->mday, month[tm->mon],
  337. tm->year+1900, tm->hour, tm->min, tm->sec);
  338. }
  339. }
  340. if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
  341. if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){
  342. while((n = read(cfd, buf, sizeof buf)) > 0){
  343. if(debug)
  344. write(2, buf, n);
  345. write(fd, buf, n);
  346. }
  347. }else{
  348. close(cfd);
  349. cfd = -1;
  350. }
  351. }
  352. dfprint(fd, "\r\n", u->host);
  353. redirect = 0;
  354. initibuf();
  355. code = httprcode(fd);
  356. switch(code){
  357. case Error: /* connection timed out */
  358. case Eof:
  359. close(fd);
  360. close(cfd);
  361. return code;
  362. case 200: /* OK */
  363. case 201: /* Created */
  364. case 202: /* Accepted */
  365. if(ofile == nil && r->start != 0)
  366. sysfatal("page changed underfoot");
  367. break;
  368. case 204: /* No Content */
  369. sysfatal("No Content");
  370. case 206: /* Partial Content */
  371. seek(out, r->start, 0);
  372. break;
  373. case 301: /* Moved Permanently */
  374. case 302: /* Moved Temporarily */
  375. redirect = 1;
  376. u->postbody = nil;
  377. break;
  378. case 304: /* Not Modified */
  379. break;
  380. case 400: /* Bad Request */
  381. sysfatal("Bad Request");
  382. case 401: /* Unauthorized */
  383. case 402: /* ??? */
  384. sysfatal("Unauthorized");
  385. case 403: /* Forbidden */
  386. sysfatal("Forbidden by server");
  387. case 404: /* Not Found */
  388. sysfatal("Not found on server");
  389. case 500: /* Internal server error */
  390. sysfatal("Server choked");
  391. case 501: /* Not implemented */
  392. sysfatal("Server can't do it!");
  393. case 502: /* Bad gateway */
  394. sysfatal("Bad gateway");
  395. case 503: /* Service unavailable */
  396. sysfatal("Service unavailable");
  397. default:
  398. sysfatal("Unknown response code %d", code);
  399. }
  400. if(u->redirect != nil){
  401. free(u->redirect);
  402. u->redirect = nil;
  403. }
  404. rv = httpheaders(fd, cfd, u, r);
  405. close(cfd);
  406. if(rv != 0){
  407. close(fd);
  408. return rv;
  409. }
  410. if(!redirect)
  411. break;
  412. if(u->redirect == nil)
  413. sysfatal("redirect: no URL");
  414. if(crackurl(u, u->redirect) < 0)
  415. sysfatal("redirect: %r");
  416. }
  417. /* transfer whatever you get */
  418. if(ofile != nil && u->mtime != 0){
  419. note.fd = out;
  420. note.mtime = u->mtime;
  421. notify(catch);
  422. }
  423. tot = 0;
  424. vtime = 0;
  425. for(;;){
  426. n = readibuf(fd, buf, sizeof(buf));
  427. if(n <= 0)
  428. break;
  429. if(write(out, buf, n) != n)
  430. break;
  431. tot += n;
  432. if(verbose && vtime != time(0)) {
  433. vtime = time(0);
  434. fprint(2, "%ld %ld\n", r->start+tot, r->end);
  435. }
  436. }
  437. notify(nil);
  438. close(fd);
  439. if(ofile != nil && u->mtime != 0){
  440. Dir d;
  441. rerrstr(err, sizeof err);
  442. nulldir(&d);
  443. d.mtime = u->mtime;
  444. if(dirfwstat(out, &d) < 0)
  445. fprint(2, "couldn't set mtime: %r\n");
  446. errstr(err, sizeof err);
  447. }
  448. return tot;
  449. }
  450. /* get the http response code */
  451. int
  452. httprcode(int fd)
  453. {
  454. int n;
  455. char *p;
  456. char buf[256];
  457. n = readline(fd, buf, sizeof(buf)-1);
  458. if(n <= 0)
  459. return n;
  460. if(debug)
  461. fprint(2, "%d <- %s\n", fd, buf);
  462. p = strchr(buf, ' ');
  463. if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){
  464. werrstr("bad response from server");
  465. return -1;
  466. }
  467. buf[n] = 0;
  468. return atoi(p+1);
  469. }
  470. /* read in and crack the http headers, update u and r */
  471. void hhetag(char*, URL*, Range*);
  472. void hhmtime(char*, URL*, Range*);
  473. void hhclen(char*, URL*, Range*);
  474. void hhcrange(char*, URL*, Range*);
  475. void hhuri(char*, URL*, Range*);
  476. void hhlocation(char*, URL*, Range*);
  477. struct {
  478. char *name;
  479. void (*f)(char*, URL*, Range*);
  480. } headers[] = {
  481. { "etag:", hhetag },
  482. { "last-modified:", hhmtime },
  483. { "content-length:", hhclen },
  484. { "content-range:", hhcrange },
  485. { "uri:", hhuri },
  486. { "location:", hhlocation },
  487. };
  488. int
  489. httpheaders(int fd, int cfd, URL *u, Range *r)
  490. {
  491. char buf[2048];
  492. char *p;
  493. int i, n;
  494. for(;;){
  495. n = getheader(fd, buf, sizeof(buf));
  496. if(n <= 0)
  497. break;
  498. if(cfd >= 0)
  499. fprint(cfd, "%s\n", buf);
  500. for(i = 0; i < nelem(headers); i++){
  501. n = strlen(headers[i].name);
  502. if(cistrncmp(buf, headers[i].name, n) == 0){
  503. /* skip field name and leading white */
  504. p = buf + n;
  505. while(*p == ' ' || *p == '\t')
  506. p++;
  507. (*headers[i].f)(p, u, r);
  508. break;
  509. }
  510. }
  511. }
  512. return n;
  513. }
  514. /*
  515. * read a single mime header, collect continuations.
  516. *
  517. * this routine assumes that there is a blank line twixt
  518. * the header and the message body, otherwise bytes will
  519. * be lost.
  520. */
  521. int
  522. getheader(int fd, char *buf, int n)
  523. {
  524. char *p, *e;
  525. int i;
  526. n--;
  527. p = buf;
  528. for(e = p + n; ; p += i){
  529. i = readline(fd, p, e-p);
  530. if(i < 0)
  531. return i;
  532. if(p == buf){
  533. /* first line */
  534. if(strchr(buf, ':') == nil)
  535. break; /* end of headers */
  536. } else {
  537. /* continuation line */
  538. if(*p != ' ' && *p != '\t'){
  539. unreadline(p);
  540. *p = 0;
  541. break; /* end of this header */
  542. }
  543. }
  544. }
  545. if(headerprint)
  546. print("%s\n", buf);
  547. if(debug)
  548. fprint(2, "%d <- %s\n", fd, buf);
  549. return p-buf;
  550. }
  551. void
  552. hhetag(char *p, URL *u, Range*)
  553. {
  554. if(u->etag != nil){
  555. if(strcmp(u->etag, p) != 0)
  556. sysfatal("file changed underfoot");
  557. } else
  558. u->etag = strdup(p);
  559. }
  560. char* monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
  561. void
  562. hhmtime(char *p, URL *u, Range*)
  563. {
  564. char *month, *day, *yr, *hms;
  565. char *fields[6];
  566. Tm tm, now;
  567. int i;
  568. i = getfields(p, fields, 6, 1, " \t");
  569. if(i < 5)
  570. return;
  571. day = fields[1];
  572. month = fields[2];
  573. yr = fields[3];
  574. hms = fields[4];
  575. /* default time */
  576. now = *gmtime(time(0));
  577. tm = now;
  578. tm.yday = 0;
  579. /* convert ascii month to a number twixt 1 and 12 */
  580. if(*month >= '0' && *month <= '9'){
  581. tm.mon = atoi(month) - 1;
  582. if(tm.mon < 0 || tm.mon > 11)
  583. tm.mon = 5;
  584. } else {
  585. for(p = month; *p; p++)
  586. *p = tolower(*p);
  587. for(i = 0; i < 12; i++)
  588. if(strncmp(&monthchars[i*3], month, 3) == 0){
  589. tm.mon = i;
  590. break;
  591. }
  592. }
  593. tm.mday = atoi(day);
  594. if(hms) {
  595. tm.hour = strtoul(hms, &p, 10);
  596. if(*p == ':') {
  597. p++;
  598. tm.min = strtoul(p, &p, 10);
  599. if(*p == ':') {
  600. p++;
  601. tm.sec = strtoul(p, &p, 10);
  602. }
  603. }
  604. if(tolower(*p) == 'p')
  605. tm.hour += 12;
  606. }
  607. if(yr) {
  608. tm.year = atoi(yr);
  609. if(tm.year >= 1900)
  610. tm.year -= 1900;
  611. } else {
  612. if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
  613. tm.year--;
  614. }
  615. strcpy(tm.zone, "GMT");
  616. /* convert to epoch seconds */
  617. u->mtime = tm2sec(&tm);
  618. }
  619. void
  620. hhclen(char *p, URL*, Range *r)
  621. {
  622. r->end = atoi(p);
  623. }
  624. void
  625. hhcrange(char *p, URL*, Range *r)
  626. {
  627. char *x;
  628. vlong l;
  629. l = 0;
  630. x = strchr(p, '/');
  631. if(x)
  632. l = atoll(x+1);
  633. if(l == 0)
  634. x = strchr(p, '-');
  635. if(x)
  636. l = atoll(x+1);
  637. if(l)
  638. r->end = l;
  639. }
  640. void
  641. hhuri(char *p, URL *u, Range*)
  642. {
  643. if(*p != '<')
  644. return;
  645. u->redirect = strdup(p+1);
  646. p = strchr(u->redirect, '>');
  647. if(p != nil)
  648. *p = 0;
  649. }
  650. void
  651. hhlocation(char *p, URL *u, Range*)
  652. {
  653. u->redirect = strdup(p);
  654. }
  655. enum
  656. {
  657. /* ftp return codes */
  658. Extra= 1,
  659. Success= 2,
  660. Incomplete= 3,
  661. TempFail= 4,
  662. PermFail= 5,
  663. Nnetdir= 64, /* max length of network directory paths */
  664. Ndialstr= 64, /* max length of dial strings */
  665. };
  666. int ftpcmd(int, char*, ...);
  667. int ftprcode(int, char*, int);
  668. int hello(int);
  669. int logon(int);
  670. int xfertype(int, char*);
  671. int passive(int, URL*);
  672. int active(int, URL*);
  673. int ftpxfer(int, int, Range*);
  674. int terminateftp(int, int);
  675. int getaddrport(char*, uchar*, uchar*);
  676. int ftprestart(int, int, URL*, Range*, long);
  677. int
  678. doftp(URL *u, URL *px, Range *r, int out, long mtime)
  679. {
  680. int pid, ctl, data, rv;
  681. Waitmsg *w;
  682. char msg[64];
  683. char conndir[NETPATHLEN];
  684. char *p;
  685. /* untested, proxy dosn't work with ftp (I think) */
  686. if(px->host == nil){
  687. ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0);
  688. } else {
  689. ctl = dial(netmkaddr(px->host, tcpdir, px->port), 0, conndir, 0);
  690. }
  691. if(ctl < 0)
  692. return Error;
  693. if(net == nil){
  694. p = strrchr(conndir, '/');
  695. *p = 0;
  696. snprint(tcpdir, sizeof(tcpdir), conndir);
  697. }
  698. initibuf();
  699. rv = hello(ctl);
  700. if(rv < 0)
  701. return terminateftp(ctl, rv);
  702. rv = logon(ctl);
  703. if(rv < 0)
  704. return terminateftp(ctl, rv);
  705. rv = xfertype(ctl, "I");
  706. if(rv < 0)
  707. return terminateftp(ctl, rv);
  708. /* if file is up to date and the right size, stop */
  709. if(ftprestart(ctl, out, u, r, mtime) > 0){
  710. close(ctl);
  711. return Eof;
  712. }
  713. /* first try passive mode, then active */
  714. data = passive(ctl, u);
  715. if(data < 0){
  716. data = active(ctl, u);
  717. if(data < 0)
  718. return Error;
  719. }
  720. /* fork */
  721. switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
  722. case -1:
  723. close(data);
  724. return terminateftp(ctl, Error);
  725. case 0:
  726. ftpxfer(data, out, r);
  727. close(data);
  728. _exits(0);
  729. default:
  730. close(data);
  731. break;
  732. }
  733. /* wait for reply message */
  734. rv = ftprcode(ctl, msg, sizeof(msg));
  735. close(ctl);
  736. /* wait for process to terminate */
  737. w = nil;
  738. for(;;){
  739. free(w);
  740. w = wait();
  741. if(w == nil)
  742. return Error;
  743. if(w->pid == pid){
  744. if(w->msg[0] == 0){
  745. free(w);
  746. break;
  747. }
  748. werrstr("xfer: %s", w->msg);
  749. free(w);
  750. return Error;
  751. }
  752. }
  753. switch(rv){
  754. case Success:
  755. return Eof;
  756. case TempFail:
  757. return Server;
  758. default:
  759. return Error;
  760. }
  761. }
  762. int
  763. ftpcmd(int ctl, char *fmt, ...)
  764. {
  765. va_list arg;
  766. char buf[2*1024], *s;
  767. va_start(arg, fmt);
  768. s = vseprint(buf, buf + (sizeof(buf)-4) / sizeof(*buf), fmt, arg);
  769. va_end(arg);
  770. if(debug)
  771. fprint(2, "%d -> %s\n", ctl, buf);
  772. *s++ = '\r';
  773. *s++ = '\n';
  774. if(write(ctl, buf, s - buf) != s - buf)
  775. return -1;
  776. return 0;
  777. }
  778. int
  779. ftprcode(int ctl, char *msg, int len)
  780. {
  781. int rv;
  782. int i;
  783. char *p;
  784. len--; /* room for terminating null */
  785. for(;;){
  786. *msg = 0;
  787. i = readline(ctl, msg, len);
  788. if(i < 0)
  789. break;
  790. if(debug)
  791. fprint(2, "%d <- %s\n", ctl, msg);
  792. /* stop if not a continuation */
  793. rv = strtol(msg, &p, 10);
  794. if(rv >= 100 && rv < 600 && p==msg+3 && *p == ' ')
  795. return rv/100;
  796. }
  797. *msg = 0;
  798. return -1;
  799. }
  800. int
  801. hello(int ctl)
  802. {
  803. char msg[1024];
  804. /* wait for hello from other side */
  805. if(ftprcode(ctl, msg, sizeof(msg)) != Success){
  806. werrstr("HELLO: %s", msg);
  807. return Server;
  808. }
  809. return 0;
  810. }
  811. int
  812. getdec(char *p, int n)
  813. {
  814. int x = 0;
  815. int i;
  816. for(i = 0; i < n; i++)
  817. x = x*10 + (*p++ - '0');
  818. return x;
  819. }
  820. int
  821. ftprestart(int ctl, int out, URL *u, Range *r, long mtime)
  822. {
  823. Tm tm;
  824. char msg[1024];
  825. long x, rmtime;
  826. ftpcmd(ctl, "MDTM %s", u->page);
  827. if(ftprcode(ctl, msg, sizeof(msg)) != Success){
  828. r->start = 0;
  829. return 0; /* need to do something */
  830. }
  831. /* decode modification time */
  832. if(strlen(msg) < 4 + 4 + 2 + 2 + 2 + 2 + 2){
  833. r->start = 0;
  834. return 0; /* need to do something */
  835. }
  836. memset(&tm, 0, sizeof(tm));
  837. tm.year = getdec(msg+4, 4) - 1900;
  838. tm.mon = getdec(msg+4+4, 2) - 1;
  839. tm.mday = getdec(msg+4+4+2, 2);
  840. tm.hour = getdec(msg+4+4+2+2, 2);
  841. tm.min = getdec(msg+4+4+2+2+2, 2);
  842. tm.sec = getdec(msg+4+4+2+2+2+2, 2);
  843. strcpy(tm.zone, "GMT");
  844. rmtime = tm2sec(&tm);
  845. if(rmtime > mtime)
  846. r->start = 0;
  847. /* get size */
  848. ftpcmd(ctl, "SIZE %s", u->page);
  849. if(ftprcode(ctl, msg, sizeof(msg)) == Success){
  850. x = atol(msg+4);
  851. if(r->start == x)
  852. return 1; /* we're up to date */
  853. r->end = x;
  854. }
  855. /* seek to restart point */
  856. if(r->start > 0){
  857. ftpcmd(ctl, "REST %lud", r->start);
  858. if(ftprcode(ctl, msg, sizeof(msg)) == Incomplete)
  859. seek(out, r->start, 0);
  860. else
  861. r->start = 0;
  862. }
  863. return 0; /* need to do something */
  864. }
  865. int
  866. logon(int ctl)
  867. {
  868. char msg[1024];
  869. /* login anonymous */
  870. ftpcmd(ctl, "USER anonymous");
  871. switch(ftprcode(ctl, msg, sizeof(msg))){
  872. case Success:
  873. return 0;
  874. case Incomplete:
  875. break; /* need password */
  876. default:
  877. werrstr("USER: %s", msg);
  878. return Server;
  879. }
  880. /* send user id as password */
  881. sprint(msg, "%s@closedmind.org", getuser());
  882. ftpcmd(ctl, "PASS %s", msg);
  883. if(ftprcode(ctl, msg, sizeof(msg)) != Success){
  884. werrstr("PASS: %s", msg);
  885. return Server;
  886. }
  887. return 0;
  888. }
  889. int
  890. xfertype(int ctl, char *t)
  891. {
  892. char msg[1024];
  893. ftpcmd(ctl, "TYPE %s", t);
  894. if(ftprcode(ctl, msg, sizeof(msg)) != Success){
  895. werrstr("TYPE %s: %s", t, msg);
  896. return Server;
  897. }
  898. return 0;
  899. }
  900. int
  901. passive(int ctl, URL *u)
  902. {
  903. char msg[1024];
  904. char ipaddr[32];
  905. char *f[6];
  906. char *p;
  907. int fd;
  908. int port;
  909. char aport[12];
  910. ftpcmd(ctl, "PASV");
  911. if(ftprcode(ctl, msg, sizeof(msg)) != Success)
  912. return Error;
  913. /* get address and port number from reply, this is AI */
  914. p = strchr(msg, '(');
  915. if(p == nil){
  916. for(p = msg+3; *p; p++)
  917. if(isdigit(*p))
  918. break;
  919. } else
  920. p++;
  921. if(getfields(p, f, 6, 0, ",)") < 6){
  922. werrstr("ftp protocol botch");
  923. return Server;
  924. }
  925. snprint(ipaddr, sizeof(ipaddr), "%s.%s.%s.%s",
  926. f[0], f[1], f[2], f[3]);
  927. port = ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff);
  928. sprint(aport, "%d", port);
  929. /* open data connection */
  930. fd = dial(netmkaddr(ipaddr, tcpdir, aport), 0, 0, 0);
  931. if(fd < 0){
  932. werrstr("passive mode failed: %r");
  933. return Error;
  934. }
  935. /* tell remote to send a file */
  936. ftpcmd(ctl, "RETR %s", u->page);
  937. if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
  938. werrstr("RETR %s: %s", u->page, msg);
  939. return Error;
  940. }
  941. return fd;
  942. }
  943. int
  944. active(int ctl, URL *u)
  945. {
  946. char msg[1024];
  947. char dir[40], ldir[40];
  948. uchar ipaddr[4];
  949. uchar port[2];
  950. int lcfd, dfd, afd;
  951. /* announce a port for the call back */
  952. snprint(msg, sizeof(msg), "%s!*!0", tcpdir);
  953. afd = announce(msg, dir);
  954. if(afd < 0)
  955. return Error;
  956. /* get a local address/port of the annoucement */
  957. if(getaddrport(dir, ipaddr, port) < 0){
  958. close(afd);
  959. return Error;
  960. }
  961. /* tell remote side address and port*/
  962. ftpcmd(ctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2],
  963. ipaddr[3], port[0], port[1]);
  964. if(ftprcode(ctl, msg, sizeof(msg)) != Success){
  965. close(afd);
  966. werrstr("active: %s", msg);
  967. return Error;
  968. }
  969. /* tell remote to send a file */
  970. ftpcmd(ctl, "RETR %s", u->page);
  971. if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
  972. close(afd);
  973. werrstr("RETR: %s", msg);
  974. return Server;
  975. }
  976. /* wait for a connection */
  977. lcfd = listen(dir, ldir);
  978. if(lcfd < 0){
  979. close(afd);
  980. return Error;
  981. }
  982. dfd = accept(lcfd, ldir);
  983. if(dfd < 0){
  984. close(afd);
  985. close(lcfd);
  986. return Error;
  987. }
  988. close(afd);
  989. close(lcfd);
  990. return dfd;
  991. }
  992. int
  993. ftpxfer(int in, int out, Range *r)
  994. {
  995. char buf[1024];
  996. long vtime;
  997. int i, n;
  998. vtime = 0;
  999. for(n = 0;;n += i){
  1000. i = read(in, buf, sizeof(buf));
  1001. if(i == 0)
  1002. break;
  1003. if(i < 0)
  1004. return Error;
  1005. if(write(out, buf, i) != i)
  1006. return Error;
  1007. r->start += i;
  1008. if(verbose && vtime != time(0)) {
  1009. vtime = time(0);
  1010. fprint(2, "%ld %ld\n", r->start, r->end);
  1011. }
  1012. }
  1013. return n;
  1014. }
  1015. int
  1016. terminateftp(int ctl, int rv)
  1017. {
  1018. close(ctl);
  1019. return rv;
  1020. }
  1021. /*
  1022. * case insensitive strcmp (why aren't these in libc?)
  1023. */
  1024. int
  1025. cistrncmp(char *a, char *b, int n)
  1026. {
  1027. while(n-- > 0){
  1028. if(tolower(*a++) != tolower(*b++))
  1029. return -1;
  1030. }
  1031. return 0;
  1032. }
  1033. int
  1034. cistrcmp(char *a, char *b)
  1035. {
  1036. while(*a || *b)
  1037. if(tolower(*a++) != tolower(*b++))
  1038. return -1;
  1039. return 0;
  1040. }
  1041. /*
  1042. * buffered io
  1043. */
  1044. struct
  1045. {
  1046. char *rp;
  1047. char *wp;
  1048. char buf[4*1024];
  1049. } b;
  1050. void
  1051. initibuf(void)
  1052. {
  1053. b.rp = b.wp = b.buf;
  1054. }
  1055. /*
  1056. * read a possibly buffered line, strip off trailing while
  1057. */
  1058. int
  1059. readline(int fd, char *buf, int len)
  1060. {
  1061. int n;
  1062. char *p;
  1063. int eof = 0;
  1064. len--;
  1065. for(p = buf;;){
  1066. if(b.rp >= b.wp){
  1067. n = read(fd, b.wp, sizeof(b.buf)/2);
  1068. if(n < 0)
  1069. return -1;
  1070. if(n == 0){
  1071. eof = 1;
  1072. break;
  1073. }
  1074. b.wp += n;
  1075. }
  1076. n = *b.rp++;
  1077. if(len > 0){
  1078. *p++ = n;
  1079. len--;
  1080. }
  1081. if(n == '\n')
  1082. break;
  1083. }
  1084. /* drop trailing white */
  1085. for(;;){
  1086. if(p <= buf)
  1087. break;
  1088. n = *(p-1);
  1089. if(n != ' ' && n != '\t' && n != '\r' && n != '\n')
  1090. break;
  1091. p--;
  1092. }
  1093. *p = 0;
  1094. if(eof && p == buf)
  1095. return -1;
  1096. return p-buf;
  1097. }
  1098. void
  1099. unreadline(char *line)
  1100. {
  1101. int i, n;
  1102. i = strlen(line);
  1103. n = b.wp-b.rp;
  1104. memmove(&b.buf[i+1], b.rp, n);
  1105. memmove(b.buf, line, i);
  1106. b.buf[i] = '\n';
  1107. b.rp = b.buf;
  1108. b.wp = b.rp + i + 1 + n;
  1109. }
  1110. int
  1111. readibuf(int fd, char *buf, int len)
  1112. {
  1113. int n;
  1114. n = b.wp-b.rp;
  1115. if(n > 0){
  1116. if(n > len)
  1117. n = len;
  1118. memmove(buf, b.rp, n);
  1119. b.rp += n;
  1120. return n;
  1121. }
  1122. return read(fd, buf, len);
  1123. }
  1124. int
  1125. dfprint(int fd, char *fmt, ...)
  1126. {
  1127. char buf[4*1024];
  1128. va_list arg;
  1129. va_start(arg, fmt);
  1130. vseprint(buf, buf+sizeof(buf), fmt, arg);
  1131. va_end(arg);
  1132. if(debug)
  1133. fprint(2, "%d -> %s", fd, buf);
  1134. return fprint(fd, "%s", buf);
  1135. }
  1136. int
  1137. getaddrport(char *dir, uchar *ipaddr, uchar *port)
  1138. {
  1139. char buf[256];
  1140. int fd, i;
  1141. char *p;
  1142. snprint(buf, sizeof(buf), "%s/local", dir);
  1143. fd = open(buf, OREAD);
  1144. if(fd < 0)
  1145. return -1;
  1146. i = read(fd, buf, sizeof(buf)-1);
  1147. close(fd);
  1148. if(i <= 0)
  1149. return -1;
  1150. buf[i] = 0;
  1151. p = strchr(buf, '!');
  1152. if(p != nil)
  1153. *p++ = 0;
  1154. v4parseip(ipaddr, buf);
  1155. i = atoi(p);
  1156. port[0] = i>>8;
  1157. port[1] = i;
  1158. return 0;
  1159. }