diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-28 01:02:24 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-28 01:02:24 +0200 |
commit | 7f4328042078f45c592d3f4c0e726a77643fd1c6 (patch) | |
tree | eeb8ec885c6b86ef7db49914a626292b970b1700 | |
parent | 1881ba48b1f43a312581796afc7a68b01e0e91a1 (diff) | |
download | busybox-w32-7f4328042078f45c592d3f4c0e726a77643fd1c6.tar.gz busybox-w32-7f4328042078f45c592d3f4c0e726a77643fd1c6.tar.bz2 busybox-w32-7f4328042078f45c592d3f4c0e726a77643fd1c6.zip |
wget: code shrink by splitting main() into easier-to-optimize functions
function old new delta
retrieve_file_data - 356 +356
wget_main 2793 2326 -467
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/1 up/down: 356/-467) Total: -111 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | networking/wget.c | 335 |
1 files changed, 174 insertions, 161 deletions
diff --git a/networking/wget.c b/networking/wget.c index ca3acd085..5e387891a 100644 --- a/networking/wget.c +++ b/networking/wget.c | |||
@@ -32,7 +32,8 @@ struct globals { | |||
32 | unsigned lastupdate_sec; | 32 | unsigned lastupdate_sec; |
33 | unsigned start_sec; | 33 | unsigned start_sec; |
34 | #endif | 34 | #endif |
35 | smallint chunked; /* chunked transfer encoding */ | 35 | smallint chunked; /* chunked transfer encoding */ |
36 | smallint got_clen; /* got content-length: from server */ | ||
36 | }; | 37 | }; |
37 | #define G (*(struct globals*)&bb_common_bufsiz1) | 38 | #define G (*(struct globals*)&bb_common_bufsiz1) |
38 | struct BUG_G_too_big { | 39 | struct BUG_G_too_big { |
@@ -46,7 +47,6 @@ struct BUG_G_too_big { | |||
46 | #define curfile (G.curfile ) | 47 | #define curfile (G.curfile ) |
47 | #define lastupdate_sec (G.lastupdate_sec ) | 48 | #define lastupdate_sec (G.lastupdate_sec ) |
48 | #define start_sec (G.start_sec ) | 49 | #define start_sec (G.start_sec ) |
49 | #define chunked (G.chunked ) | ||
50 | #define INIT_G() do { } while (0) | 50 | #define INIT_G() do { } while (0) |
51 | 51 | ||
52 | 52 | ||
@@ -79,7 +79,7 @@ static void progress_meter(int flag) | |||
79 | } | 79 | } |
80 | 80 | ||
81 | ratio = 100; | 81 | ratio = 100; |
82 | if (totalsize != 0 && !chunked) { | 82 | if (totalsize != 0 && !G.chunked) { |
83 | /* long long helps to have it working even if !LFS */ | 83 | /* long long helps to have it working even if !LFS */ |
84 | ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize); | 84 | ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize); |
85 | if (ratio > 100) ratio = 100; | 85 | if (ratio > 100) ratio = 100; |
@@ -127,7 +127,7 @@ static void progress_meter(int flag) | |||
127 | fprintf(stderr, " - stalled -"); | 127 | fprintf(stderr, " - stalled -"); |
128 | } else { | 128 | } else { |
129 | off_t to_download = totalsize - beg_range; | 129 | off_t to_download = totalsize - beg_range; |
130 | if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) { | 130 | if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || G.chunked) { |
131 | fprintf(stderr, "--:--:-- ETA"); | 131 | fprintf(stderr, "--:--:-- ETA"); |
132 | } else { | 132 | } else { |
133 | /* to_download / (transferred/elapsed) - elapsed: */ | 133 | /* to_download / (transferred/elapsed) - elapsed: */ |
@@ -239,7 +239,6 @@ static char *base64enc_512(char buf[512], const char *str) | |||
239 | } | 239 | } |
240 | #endif | 240 | #endif |
241 | 241 | ||
242 | |||
243 | static FILE *open_socket(len_and_sockaddr *lsa) | 242 | static FILE *open_socket(len_and_sockaddr *lsa) |
244 | { | 243 | { |
245 | FILE *fp; | 244 | FILE *fp; |
@@ -253,7 +252,6 @@ static FILE *open_socket(len_and_sockaddr *lsa) | |||
253 | return fp; | 252 | return fp; |
254 | } | 253 | } |
255 | 254 | ||
256 | |||
257 | static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) | 255 | static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) |
258 | { | 256 | { |
259 | int result; | 257 | int result; |
@@ -281,7 +279,6 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) | |||
281 | return result; | 279 | return result; |
282 | } | 280 | } |
283 | 281 | ||
284 | |||
285 | static void parse_url(char *src_url, struct host_info *h) | 282 | static void parse_url(char *src_url, struct host_info *h) |
286 | { | 283 | { |
287 | char *url, *p, *sp; | 284 | char *url, *p, *sp; |
@@ -340,7 +337,6 @@ static void parse_url(char *src_url, struct host_info *h) | |||
340 | sp = h->host; | 337 | sp = h->host; |
341 | } | 338 | } |
342 | 339 | ||
343 | |||
344 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) | 340 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) |
345 | { | 341 | { |
346 | char *s, *hdrval; | 342 | char *s, *hdrval; |
@@ -380,7 +376,7 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) | |||
380 | return hdrval; | 376 | return hdrval; |
381 | } | 377 | } |
382 | 378 | ||
383 | /* Rats! The buffer isn't big enough to hold the entire header value. */ | 379 | /* Rats! The buffer isn't big enough to hold the entire header value */ |
384 | while (c = getc(fp), c != EOF && c != '\n') | 380 | while (c = getc(fp), c != EOF && c != '\n') |
385 | continue; | 381 | continue; |
386 | /* *istrunc = 1; */ | 382 | /* *istrunc = 1; */ |
@@ -425,30 +421,171 @@ static char *URL_escape(const char *str) | |||
425 | } | 421 | } |
426 | #endif | 422 | #endif |
427 | 423 | ||
424 | static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) | ||
425 | { | ||
426 | char buf[512]; | ||
427 | FILE *sfp; | ||
428 | char *str; | ||
429 | int port; | ||
430 | |||
431 | if (!target->user) | ||
432 | target->user = xstrdup("anonymous:busybox@"); | ||
433 | |||
434 | sfp = open_socket(lsa); | ||
435 | if (ftpcmd(NULL, NULL, sfp, buf) != 220) | ||
436 | bb_error_msg_and_die("%s", buf+4); | ||
437 | |||
438 | /* | ||
439 | * Splitting username:password pair, | ||
440 | * trying to log in | ||
441 | */ | ||
442 | str = strchr(target->user, ':'); | ||
443 | if (str) | ||
444 | *str++ = '\0'; | ||
445 | switch (ftpcmd("USER ", target->user, sfp, buf)) { | ||
446 | case 230: | ||
447 | break; | ||
448 | case 331: | ||
449 | if (ftpcmd("PASS ", str, sfp, buf) == 230) | ||
450 | break; | ||
451 | /* fall through (failed login) */ | ||
452 | default: | ||
453 | bb_error_msg_and_die("ftp login: %s", buf+4); | ||
454 | } | ||
455 | |||
456 | ftpcmd("TYPE I", NULL, sfp, buf); | ||
457 | |||
458 | /* | ||
459 | * Querying file size | ||
460 | */ | ||
461 | if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) { | ||
462 | content_len = BB_STRTOOFF(buf+4, NULL, 10); | ||
463 | if (errno || content_len < 0) { | ||
464 | bb_error_msg_and_die("SIZE value is garbage"); | ||
465 | } | ||
466 | G.got_clen = 1; | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * Entering passive mode | ||
471 | */ | ||
472 | if (ftpcmd("PASV", NULL, sfp, buf) != 227) { | ||
473 | pasv_error: | ||
474 | bb_error_msg_and_die("bad response to %s: %s", "PASV", buf); | ||
475 | } | ||
476 | // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] | ||
477 | // Server's IP is N1.N2.N3.N4 (we ignore it) | ||
478 | // Server's port for data connection is P1*256+P2 | ||
479 | str = strrchr(buf, ')'); | ||
480 | if (str) str[0] = '\0'; | ||
481 | str = strrchr(buf, ','); | ||
482 | if (!str) goto pasv_error; | ||
483 | port = xatou_range(str+1, 0, 255); | ||
484 | *str = '\0'; | ||
485 | str = strrchr(buf, ','); | ||
486 | if (!str) goto pasv_error; | ||
487 | port += xatou_range(str+1, 0, 255) * 256; | ||
488 | set_nport(lsa, htons(port)); | ||
489 | |||
490 | *dfpp = open_socket(lsa); | ||
491 | |||
492 | if (beg_range) { | ||
493 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
494 | if (ftpcmd(buf, NULL, sfp, buf) == 350) | ||
495 | content_len -= beg_range; | ||
496 | } | ||
497 | |||
498 | if (ftpcmd("RETR ", target->path, sfp, buf) > 150) | ||
499 | bb_error_msg_and_die("bad response to %s: %s", "RETR", buf); | ||
500 | |||
501 | return sfp; | ||
502 | } | ||
503 | |||
504 | /* Must match option string! */ | ||
505 | enum { | ||
506 | WGET_OPT_CONTINUE = (1 << 0), | ||
507 | WGET_OPT_SPIDER = (1 << 1), | ||
508 | WGET_OPT_QUIET = (1 << 2), | ||
509 | WGET_OPT_OUTNAME = (1 << 3), | ||
510 | WGET_OPT_PREFIX = (1 << 4), | ||
511 | WGET_OPT_PROXY = (1 << 5), | ||
512 | WGET_OPT_USER_AGENT = (1 << 6), | ||
513 | WGET_OPT_RETRIES = (1 << 7), | ||
514 | WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8), | ||
515 | WGET_OPT_PASSIVE = (1 << 9), | ||
516 | WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | ||
517 | WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | ||
518 | }; | ||
519 | |||
520 | static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) | ||
521 | { | ||
522 | char buf[512]; | ||
523 | |||
524 | if (!(option_mask32 & WGET_OPT_QUIET)) | ||
525 | progress_meter(-1); | ||
526 | |||
527 | if (G.chunked) | ||
528 | goto get_clen; | ||
529 | |||
530 | /* Loops only if chunked */ | ||
531 | while (1) { | ||
532 | while (content_len > 0 || !G.got_clen) { | ||
533 | int n; | ||
534 | unsigned rdsz = sizeof(buf); | ||
535 | |||
536 | if (content_len < sizeof(buf) && (G.chunked || G.got_clen)) | ||
537 | rdsz = (unsigned)content_len; | ||
538 | n = safe_fread(buf, rdsz, dfp); | ||
539 | if (n <= 0) { | ||
540 | if (ferror(dfp)) { | ||
541 | /* perror will not work: ferror doesn't set errno */ | ||
542 | bb_error_msg_and_die(bb_msg_read_error); | ||
543 | } | ||
544 | break; | ||
545 | } | ||
546 | xwrite(output_fd, buf, n); | ||
547 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
548 | transferred += n; | ||
549 | #endif | ||
550 | if (G.got_clen) | ||
551 | content_len -= n; | ||
552 | } | ||
553 | |||
554 | if (!G.chunked) | ||
555 | break; | ||
556 | |||
557 | safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ | ||
558 | get_clen: | ||
559 | safe_fgets(buf, sizeof(buf), dfp); | ||
560 | content_len = STRTOOFF(buf, NULL, 16); | ||
561 | /* FIXME: error check? */ | ||
562 | if (content_len == 0) | ||
563 | break; /* all done! */ | ||
564 | } | ||
565 | |||
566 | if (!(option_mask32 & WGET_OPT_QUIET)) | ||
567 | progress_meter(0); | ||
568 | } | ||
569 | |||
428 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 570 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
429 | int wget_main(int argc UNUSED_PARAM, char **argv) | 571 | int wget_main(int argc UNUSED_PARAM, char **argv) |
430 | { | 572 | { |
431 | char buf[512]; | 573 | char buf[512]; |
432 | struct host_info server, target; | 574 | struct host_info server, target; |
433 | len_and_sockaddr *lsa; | 575 | len_and_sockaddr *lsa; |
434 | int status; | ||
435 | int port; | ||
436 | int try = 5; | ||
437 | unsigned opt; | 576 | unsigned opt; |
438 | char *str; | 577 | char *proxy = NULL; |
439 | char *proxy = 0; | ||
440 | char *dir_prefix = NULL; | 578 | char *dir_prefix = NULL; |
441 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | 579 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS |
442 | char *post_data; | 580 | char *post_data; |
443 | char *extra_headers = NULL; | 581 | char *extra_headers = NULL; |
444 | llist_t *headers_llist = NULL; | 582 | llist_t *headers_llist = NULL; |
445 | #endif | 583 | #endif |
446 | FILE *sfp = NULL; /* socket to web/ftp server */ | 584 | FILE *sfp; /* socket to web/ftp server */ |
447 | FILE *dfp; /* socket to ftp server (data) */ | 585 | FILE *dfp; /* socket to ftp server (data) */ |
448 | char *fname_out; /* where to direct output (-O) */ | 586 | char *fname_out; /* where to direct output (-O) */ |
449 | bool got_clen = 0; /* got content-length: from server */ | ||
450 | int output_fd = -1; | 587 | int output_fd = -1; |
451 | bool use_proxy = 1; /* Use proxies if env vars are set */ | 588 | bool use_proxy; /* Use proxies if env vars are set */ |
452 | const char *proxy_flag = "on"; /* Use proxies if env vars are set */ | 589 | const char *proxy_flag = "on"; /* Use proxies if env vars are set */ |
453 | const char *user_agent = "Wget";/* "User-Agent" header field */ | 590 | const char *user_agent = "Wget";/* "User-Agent" header field */ |
454 | 591 | ||
@@ -457,20 +594,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
457 | enum { | 594 | enum { |
458 | KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location | 595 | KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location |
459 | }; | 596 | }; |
460 | enum { | ||
461 | WGET_OPT_CONTINUE = (1 << 0), | ||
462 | WGET_OPT_SPIDER = (1 << 1), | ||
463 | WGET_OPT_QUIET = (1 << 2), | ||
464 | WGET_OPT_OUTNAME = (1 << 3), | ||
465 | WGET_OPT_PREFIX = (1 << 4), | ||
466 | WGET_OPT_PROXY = (1 << 5), | ||
467 | WGET_OPT_USER_AGENT = (1 << 6), | ||
468 | WGET_OPT_RETRIES = (1 << 7), | ||
469 | WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8), | ||
470 | WGET_OPT_PASSIVE = (1 << 9), | ||
471 | WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | ||
472 | WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, | ||
473 | }; | ||
474 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | 597 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS |
475 | static const char wget_longopts[] ALIGN1 = | 598 | static const char wget_longopts[] ALIGN1 = |
476 | /* name, has_arg, val */ | 599 | /* name, has_arg, val */ |
@@ -506,10 +629,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
506 | IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) | 629 | IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) |
507 | IF_FEATURE_WGET_LONG_OPTIONS(, &post_data) | 630 | IF_FEATURE_WGET_LONG_OPTIONS(, &post_data) |
508 | ); | 631 | ); |
509 | if (strcmp(proxy_flag, "off") == 0) { | ||
510 | /* Use the proxy if necessary */ | ||
511 | use_proxy = 0; | ||
512 | } | ||
513 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS | 632 | #if ENABLE_FEATURE_WGET_LONG_OPTIONS |
514 | if (headers_llist) { | 633 | if (headers_llist) { |
515 | int size = 1; | 634 | int size = 1; |
@@ -526,11 +645,13 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
526 | } | 645 | } |
527 | #endif | 646 | #endif |
528 | 647 | ||
648 | /* TODO: compat issue: should handle "wget URL1 URL2..." */ | ||
529 | parse_url(argv[optind], &target); | 649 | parse_url(argv[optind], &target); |
530 | server.host = target.host; | 650 | server.host = target.host; |
531 | server.port = target.port; | 651 | server.port = target.port; |
532 | 652 | ||
533 | /* Use the proxy if necessary */ | 653 | /* Use the proxy if necessary */ |
654 | use_proxy = (strcmp(proxy_flag, "off") != 0); | ||
534 | if (use_proxy) { | 655 | if (use_proxy) { |
535 | proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); | 656 | proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); |
536 | if (proxy && *proxy) { | 657 | if (proxy && *proxy) { |
@@ -562,7 +683,8 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
562 | 683 | ||
563 | /* Impossible? | 684 | /* Impossible? |
564 | if ((opt & WGET_OPT_CONTINUE) && !fname_out) | 685 | if ((opt & WGET_OPT_CONTINUE) && !fname_out) |
565 | bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */ | 686 | bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); |
687 | */ | ||
566 | 688 | ||
567 | /* Determine where to start transfer */ | 689 | /* Determine where to start transfer */ |
568 | if (opt & WGET_OPT_CONTINUE) { | 690 | if (opt & WGET_OPT_CONTINUE) { |
@@ -571,7 +693,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
571 | beg_range = xlseek(output_fd, 0, SEEK_END); | 693 | beg_range = xlseek(output_fd, 0, SEEK_END); |
572 | } | 694 | } |
573 | /* File doesn't exist. We do not create file here yet. | 695 | /* File doesn't exist. We do not create file here yet. |
574 | We are not sure it exists on remove side */ | 696 | * We are not sure it exists on remove side */ |
575 | } | 697 | } |
576 | 698 | ||
577 | /* We want to do exactly _one_ DNS lookup, since some | 699 | /* We want to do exactly _one_ DNS lookup, since some |
@@ -584,13 +706,20 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
584 | /* We leak result of xmalloc_sockaddr2dotted */ | 706 | /* We leak result of xmalloc_sockaddr2dotted */ |
585 | } | 707 | } |
586 | 708 | ||
709 | /* G.got_clen = 0; - already is */ | ||
710 | sfp = NULL; | ||
587 | if (use_proxy || !target.is_ftp) { | 711 | if (use_proxy || !target.is_ftp) { |
588 | /* | 712 | /* |
589 | * HTTP session | 713 | * HTTP session |
590 | */ | 714 | */ |
715 | int status; | ||
716 | int try = 5; | ||
717 | |||
591 | do { | 718 | do { |
592 | got_clen = 0; | 719 | char *str; |
593 | chunked = 0; | 720 | |
721 | G.got_clen = 0; | ||
722 | G.chunked = 0; | ||
594 | 723 | ||
595 | if (!--try) | 724 | if (!--try) |
596 | bb_error_msg_and_die("too many redirections"); | 725 | bb_error_msg_and_die("too many redirections"); |
@@ -599,7 +728,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv) | |||
599 | if (sfp) fclose(sfp); | 728 | if (sfp) fclose(sfp); |
600 | sfp = open_socket(lsa); | 729 | sfp = open_socket(lsa); |
601 | 730 | ||
602 | /* Send HTTP request. */ | 731 | /* Send HTTP request */ |
603 | if (use_proxy) { | 732 | if (use_proxy) { |
604 | fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n", | 733 | fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n", |
605 | target.is_ftp ? "f" : "ht", target.host, | 734 | target.is_ftp ? "f" : "ht", target.host, |
@@ -710,20 +839,20 @@ However, in real world it was observed that some web servers | |||
710 | * Retrieve HTTP headers. | 839 | * Retrieve HTTP headers. |
711 | */ | 840 | */ |
712 | while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) { | 841 | while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) { |
713 | /* gethdr did already convert the "FOO:" string to lowercase */ | 842 | /* gethdr converted "FOO:" string to lowercase */ |
714 | smalluint key = index_in_strings(keywords, *&buf) + 1; | 843 | smalluint key = index_in_strings(keywords, *&buf) + 1; |
715 | if (key == KEY_content_length) { | 844 | if (key == KEY_content_length) { |
716 | content_len = BB_STRTOOFF(str, NULL, 10); | 845 | content_len = BB_STRTOOFF(str, NULL, 10); |
717 | if (errno || content_len < 0) { | 846 | if (errno || content_len < 0) { |
718 | bb_error_msg_and_die("content-length %s is garbage", str); | 847 | bb_error_msg_and_die("content-length %s is garbage", str); |
719 | } | 848 | } |
720 | got_clen = 1; | 849 | G.got_clen = 1; |
721 | continue; | 850 | continue; |
722 | } | 851 | } |
723 | if (key == KEY_transfer_encoding) { | 852 | if (key == KEY_transfer_encoding) { |
724 | if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) | 853 | if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) |
725 | bb_error_msg_and_die("transfer encoding '%s' is not supported", str); | 854 | bb_error_msg_and_die("transfer encoding '%s' is not supported", str); |
726 | chunked = got_clen = 1; | 855 | G.chunked = G.got_clen = 1; |
727 | } | 856 | } |
728 | if (key == KEY_location) { | 857 | if (key == KEY_location) { |
729 | if (str[0] == '/') | 858 | if (str[0] == '/') |
@@ -746,78 +875,10 @@ However, in real world it was observed that some web servers | |||
746 | dfp = sfp; | 875 | dfp = sfp; |
747 | 876 | ||
748 | } else { | 877 | } else { |
749 | |||
750 | /* | 878 | /* |
751 | * FTP session | 879 | * FTP session |
752 | */ | 880 | */ |
753 | if (!target.user) | 881 | sfp = prepare_ftp_session(&dfp, &target, lsa); |
754 | target.user = xstrdup("anonymous:busybox@"); | ||
755 | |||
756 | sfp = open_socket(lsa); | ||
757 | if (ftpcmd(NULL, NULL, sfp, buf) != 220) | ||
758 | bb_error_msg_and_die("%s", buf+4); | ||
759 | |||
760 | /* | ||
761 | * Splitting username:password pair, | ||
762 | * trying to log in | ||
763 | */ | ||
764 | str = strchr(target.user, ':'); | ||
765 | if (str) | ||
766 | *(str++) = '\0'; | ||
767 | switch (ftpcmd("USER ", target.user, sfp, buf)) { | ||
768 | case 230: | ||
769 | break; | ||
770 | case 331: | ||
771 | if (ftpcmd("PASS ", str, sfp, buf) == 230) | ||
772 | break; | ||
773 | /* fall through (failed login) */ | ||
774 | default: | ||
775 | bb_error_msg_and_die("ftp login: %s", buf+4); | ||
776 | } | ||
777 | |||
778 | ftpcmd("TYPE I", NULL, sfp, buf); | ||
779 | |||
780 | /* | ||
781 | * Querying file size | ||
782 | */ | ||
783 | if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) { | ||
784 | content_len = BB_STRTOOFF(buf+4, NULL, 10); | ||
785 | if (errno || content_len < 0) { | ||
786 | bb_error_msg_and_die("SIZE value is garbage"); | ||
787 | } | ||
788 | got_clen = 1; | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | * Entering passive mode | ||
793 | */ | ||
794 | if (ftpcmd("PASV", NULL, sfp, buf) != 227) { | ||
795 | pasv_error: | ||
796 | bb_error_msg_and_die("bad response to %s: %s", "PASV", buf); | ||
797 | } | ||
798 | // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] | ||
799 | // Server's IP is N1.N2.N3.N4 (we ignore it) | ||
800 | // Server's port for data connection is P1*256+P2 | ||
801 | str = strrchr(buf, ')'); | ||
802 | if (str) str[0] = '\0'; | ||
803 | str = strrchr(buf, ','); | ||
804 | if (!str) goto pasv_error; | ||
805 | port = xatou_range(str+1, 0, 255); | ||
806 | *str = '\0'; | ||
807 | str = strrchr(buf, ','); | ||
808 | if (!str) goto pasv_error; | ||
809 | port += xatou_range(str+1, 0, 255) * 256; | ||
810 | set_nport(lsa, htons(port)); | ||
811 | dfp = open_socket(lsa); | ||
812 | |||
813 | if (beg_range) { | ||
814 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
815 | if (ftpcmd(buf, NULL, sfp, buf) == 350) | ||
816 | content_len -= beg_range; | ||
817 | } | ||
818 | |||
819 | if (ftpcmd("RETR ", target.path, sfp, buf) > 150) | ||
820 | bb_error_msg_and_die("bad response to %s: %s", "RETR", buf); | ||
821 | } | 882 | } |
822 | 883 | ||
823 | if (opt & WGET_OPT_SPIDER) { | 884 | if (opt & WGET_OPT_SPIDER) { |
@@ -826,11 +887,6 @@ However, in real world it was observed that some web servers | |||
826 | return EXIT_SUCCESS; | 887 | return EXIT_SUCCESS; |
827 | } | 888 | } |
828 | 889 | ||
829 | /* | ||
830 | * Retrieve file | ||
831 | */ | ||
832 | |||
833 | /* Do it before progress_meter (want to have nice error message) */ | ||
834 | if (output_fd < 0) { | 890 | if (output_fd < 0) { |
835 | int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; | 891 | int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; |
836 | /* compat with wget: -O FILE can overwrite */ | 892 | /* compat with wget: -O FILE can overwrite */ |
@@ -839,50 +895,7 @@ However, in real world it was observed that some web servers | |||
839 | output_fd = xopen(fname_out, o_flags); | 895 | output_fd = xopen(fname_out, o_flags); |
840 | } | 896 | } |
841 | 897 | ||
842 | if (!(opt & WGET_OPT_QUIET)) | 898 | retrieve_file_data(dfp, output_fd); |
843 | progress_meter(-1); | ||
844 | |||
845 | if (chunked) | ||
846 | goto get_clen; | ||
847 | |||
848 | /* Loops only if chunked */ | ||
849 | while (1) { | ||
850 | while (content_len > 0 || !got_clen) { | ||
851 | int n; | ||
852 | unsigned rdsz = sizeof(buf); | ||
853 | |||
854 | if (content_len < sizeof(buf) && (chunked || got_clen)) | ||
855 | rdsz = (unsigned)content_len; | ||
856 | n = safe_fread(buf, rdsz, dfp); | ||
857 | if (n <= 0) { | ||
858 | if (ferror(dfp)) { | ||
859 | /* perror will not work: ferror doesn't set errno */ | ||
860 | bb_error_msg_and_die(bb_msg_read_error); | ||
861 | } | ||
862 | break; | ||
863 | } | ||
864 | xwrite(output_fd, buf, n); | ||
865 | #if ENABLE_FEATURE_WGET_STATUSBAR | ||
866 | transferred += n; | ||
867 | #endif | ||
868 | if (got_clen) | ||
869 | content_len -= n; | ||
870 | } | ||
871 | |||
872 | if (!chunked) | ||
873 | break; | ||
874 | |||
875 | safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ | ||
876 | get_clen: | ||
877 | safe_fgets(buf, sizeof(buf), dfp); | ||
878 | content_len = STRTOOFF(buf, NULL, 16); | ||
879 | /* FIXME: error check? */ | ||
880 | if (content_len == 0) | ||
881 | break; /* all done! */ | ||
882 | } | ||
883 | |||
884 | if (!(opt & WGET_OPT_QUIET)) | ||
885 | progress_meter(0); | ||
886 | 899 | ||
887 | if ((use_proxy == 0) && target.is_ftp) { | 900 | if ((use_proxy == 0) && target.is_ftp) { |
888 | fclose(dfp); | 901 | fclose(dfp); |