aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--networking/wget.c335
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)
38struct BUG_G_too_big { 39struct 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
243static FILE *open_socket(len_and_sockaddr *lsa) 242static 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
257static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) 255static 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
285static void parse_url(char *src_url, struct host_info *h) 282static 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
344static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) 340static 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
424static 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! */
505enum {
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
520static 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
428int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 570int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
429int wget_main(int argc UNUSED_PARAM, char **argv) 571int 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);