Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/networking/wget.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/networking/wget.c
r821 r1770 7 7 */ 8 8 9 #include "busybox.h" 10 #include <errno.h> 11 #include <signal.h> 12 #include <sys/ioctl.h> 13 #include <getopt.h> 14 9 /* We want libc to give us xxx64 functions also */ 10 /* http://www.unix.org/version2/whatsnew/lfs20mar.html */ 11 //#define _LARGEFILE64_SOURCE 1 12 13 #include <getopt.h> /* for struct option */ 14 #include "libbb.h" 15 15 16 16 struct host_info { 17 // May be used if we ever will want to free() all xstrdup()s... 18 /* char *allocated; */ 17 19 char *host; 18 20 int port; … … 23 25 24 26 static void parse_url(char *url, struct host_info *h); 25 static FILE *open_socket( struct sockaddr_in *s_in);27 static FILE *open_socket(len_and_sockaddr *lsa); 26 28 static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); 27 static int ftpcmd(c har *s1,char *s2, FILE *fp, char *buf);29 static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf); 28 30 29 31 /* Globals (can be accessed from signal handlers */ 30 static off_t filesize; /* content-length of the file */ 31 static int chunked; /* chunked transfer encoding */ 32 #ifdef CONFIG_FEATURE_WGET_STATUSBAR 32 static off_t content_len; /* Content-length of the file */ 33 static off_t beg_range; /* Range at which continue begins */ 34 #if ENABLE_FEATURE_WGET_STATUSBAR 35 static off_t transferred; /* Number of bytes transferred so far */ 36 #endif 37 static bool chunked; /* chunked transfer encoding */ 38 #if ENABLE_FEATURE_WGET_STATUSBAR 33 39 static void progressmeter(int flag); 34 static char *curfile; /* Name of current file being transferred. */ 35 static struct timeval start; /* Time a transfer started. */ 36 static off_t transferred; /* Number of bytes transferred so far. */ 37 /* For progressmeter() -- number of seconds before xfer considered "stalled" */ 40 static const char *curfile; /* Name of current file being transferred */ 38 41 enum { 39 STALLTIME = 5 42 STALLTIME = 5 /* Seconds when xfer considered "stalled" */ 40 43 }; 41 44 #else 42 static inline void progressmeter(int flag) {} 43 #endif 44 45 static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue) 46 { 47 if (output != stdout && do_continue==0) { 48 fclose(output); 49 unlink(fname_out); 50 } 51 } 52 53 /* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the 54 * number of elements read, and a short count if an eof or non-interrupt 55 * error is encountered. */ 56 static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 57 { 58 size_t ret = 0; 45 static ALWAYS_INLINE void progressmeter(int flag) {} 46 #endif 47 48 /* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read, 49 * and a short count if an eof or non-interrupt error is encountered. */ 50 static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream) 51 { 52 size_t ret; 53 char *p = (char*)ptr; 59 54 60 55 do { 61 56 clearerr(stream); 62 ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); 63 } while (ret < nmemb && ferror(stream) && errno == EINTR); 64 65 return ret; 66 } 67 68 /* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the 69 * number of elements written, and a short count if an eof or non-interrupt 70 * error is encountered. */ 71 static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream) 72 { 73 size_t ret = 0; 74 75 do { 76 clearerr(stream); 77 ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream); 78 } while (ret < nmemb && ferror(stream) && errno == EINTR); 79 80 return ret; 81 } 82 83 /* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM. 57 ret = fread(p, 1, nmemb, stream); 58 p += ret; 59 nmemb -= ret; 60 } while (nmemb && ferror(stream) && errno == EINTR); 61 62 return p - (char*)ptr; 63 } 64 65 /* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM. 84 66 * Returns S, or NULL if an eof or non-interrupt error is encountered. */ 85 67 static char *safe_fgets(char *s, int size, FILE *stream) … … 95 77 } 96 78 97 #define close_delete_and_die(s...) { \ 98 close_and_delete_outfile(output, fname_out, do_continue); \ 99 bb_error_msg_and_die(s); } 100 101 102 #ifdef CONFIG_FEATURE_WGET_AUTHENTICATION 103 /* 104 * Base64-encode character string 105 * oops... isn't something similar in uuencode.c? 106 * XXX: It would be better to use already existing code 107 */ 108 static char *base64enc(unsigned char *p, char *buf, int len) { 109 110 char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 111 "0123456789+/"; 112 char *s = buf; 113 114 while(*p) { 115 if (s >= buf+len-4) 116 bb_error_msg_and_die("buffer overflow"); 117 *(s++) = al[(*p >> 2) & 0x3F]; 118 *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)]; 119 *s = *(s+1) = '='; 120 *(s+2) = 0; 121 if (! *(++p)) break; 122 *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)]; 123 if (! *(++p)) break; 124 *(s++) = al[*(p++) & 0x3F]; 125 } 126 79 #if ENABLE_FEATURE_WGET_AUTHENTICATION 80 /* Base64-encode character string. buf is assumed to be char buf[512]. */ 81 static char *base64enc_512(char buf[512], const char *str) 82 { 83 unsigned len = strlen(str); 84 if (len > 512/4*3 - 10) /* paranoia */ 85 len = 512/4*3 - 10; 86 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64); 127 87 return buf; 128 88 } 129 89 #endif 130 90 131 #define WGET_OPT_CONTINUE 1 132 #define WGET_OPT_QUIET 2 133 #define WGET_OPT_PASSIVE 4 134 #define WGET_OPT_OUTNAME 8 135 #define WGET_OPT_HEADER 16 136 #define WGET_OPT_PREFIX 32 137 #define WGET_OPT_PROXY 64 138 139 #if ENABLE_WGET_LONG_OPTIONS 140 static const struct option wget_long_options[] = { 141 { "continue", 0, NULL, 'c' }, 142 { "quiet", 0, NULL, 'q' }, 143 { "passive-ftp", 0, NULL, 139 }, 144 { "output-document", 1, NULL, 'O' }, 145 { "header", 1, NULL, 131 }, 146 { "directory-prefix",1, NULL, 'P' }, 147 { "proxy", 1, NULL, 'Y' }, 148 { 0, 0, 0, 0 } 149 }; 150 #endif 151 91 int wget_main(int argc, char **argv); 152 92 int wget_main(int argc, char **argv) 153 93 { 154 int n, try=5, status; 155 unsigned long opt; 94 char buf[512]; 95 struct host_info server, target; 96 len_and_sockaddr *lsa; 97 int n, status; 156 98 int port; 99 int try = 5; 100 unsigned opt; 101 char *str; 157 102 char *proxy = 0; 158 char *dir_prefix=NULL; 159 char *s, buf[512]; 160 struct stat sbuf; 161 char extra_headers[1024]; 162 char *extra_headers_ptr = extra_headers; 163 int extra_headers_left = sizeof(extra_headers); 164 struct host_info server, target; 165 struct sockaddr_in s_in; 103 char *dir_prefix = NULL; 104 #if ENABLE_FEATURE_WGET_LONG_OPTIONS 105 char *extra_headers = NULL; 166 106 llist_t *headers_llist = NULL; 167 168 FILE *sfp = NULL; /* socket to web/ftp server */ 169 FILE *dfp = NULL; /* socket to ftp server (data) */ 170 char *fname_out = NULL; /* where to direct output (-O) */ 171 int do_continue = 0; /* continue a prev transfer (-c) */ 172 long beg_range = 0L; /* range at which continue begins */ 173 int got_clen = 0; /* got content-length: from server */ 174 FILE *output; /* socket to web server */ 175 int quiet_flag = FALSE; /* Be verry, verry quiet... */ 176 int use_proxy = 1; /* Use proxies if env vars are set */ 177 char *proxy_flag = "on"; /* Use proxies if env vars are set */ 178 179 /* 180 * Crack command line. 181 */ 182 bb_opt_complementally = "-1:\203::"; 183 #if ENABLE_WGET_LONG_OPTIONS 184 bb_applet_long_options = wget_long_options; 185 #endif 186 opt = bb_getopt_ulflags(argc, argv, "cq\213O:\203:P:Y:", 187 &fname_out, &headers_llist, 188 &dir_prefix, &proxy_flag); 189 if (opt & WGET_OPT_CONTINUE) { 190 ++do_continue; 191 } 192 if (opt & WGET_OPT_QUIET) { 193 quiet_flag = TRUE; 194 } 107 #endif 108 109 FILE *sfp = NULL; /* socket to web/ftp server */ 110 FILE *dfp = NULL; /* socket to ftp server (data) */ 111 char *fname_out = NULL; /* where to direct output (-O) */ 112 bool got_clen = 0; /* got content-length: from server */ 113 int output_fd = -1; 114 bool use_proxy = 1; /* Use proxies if env vars are set */ 115 const char *proxy_flag = "on"; /* Use proxies if env vars are set */ 116 const char *user_agent = "Wget";/* "User-Agent" header field */ 117 static const char keywords[] ALIGN1 = 118 "content-length\0""transfer-encoding\0""chunked\0""location\0"; 119 enum { 120 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location 121 }; 122 enum { 123 WGET_OPT_CONTINUE = 0x1, 124 WGET_OPT_SPIDER = 0x2, 125 WGET_OPT_QUIET = 0x4, 126 WGET_OPT_OUTNAME = 0x8, 127 WGET_OPT_PREFIX = 0x10, 128 WGET_OPT_PROXY = 0x20, 129 WGET_OPT_USER_AGENT = 0x40, 130 WGET_OPT_PASSIVE = 0x80, 131 WGET_OPT_HEADER = 0x100, 132 }; 133 #if ENABLE_FEATURE_WGET_LONG_OPTIONS 134 static const char wget_longopts[] ALIGN1 = 135 /* name, has_arg, val */ 136 "continue\0" No_argument "c" 137 "spider\0" No_argument "s" 138 "quiet\0" No_argument "q" 139 "output-document\0" Required_argument "O" 140 "directory-prefix\0" Required_argument "P" 141 "proxy\0" Required_argument "Y" 142 "user-agent\0" Required_argument "U" 143 "passive-ftp\0" No_argument "\xff" 144 "header\0" Required_argument "\xfe" 145 ; 146 applet_long_options = wget_longopts; 147 #endif 148 /* server.allocated = target.allocated = NULL; */ 149 opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); 150 opt = getopt32(argv, "csqO:P:Y:U:", 151 &fname_out, &dir_prefix, 152 &proxy_flag, &user_agent 153 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) 154 ); 195 155 if (strcmp(proxy_flag, "off") == 0) { 196 /* Use the proxy if necessary .*/156 /* Use the proxy if necessary */ 197 157 use_proxy = 0; 198 158 } 199 if (opt & WGET_OPT_HEADER) { 159 #if ENABLE_FEATURE_WGET_LONG_OPTIONS 160 if (headers_llist) { 161 int size = 1; 162 char *cp; 163 llist_t *ll = headers_llist; 164 while (ll) { 165 size += strlen(ll->data) + 2; 166 ll = ll->link; 167 } 168 extra_headers = cp = xmalloc(size); 200 169 while (headers_llist) { 201 int arglen = strlen(headers_llist->data); 202 if (extra_headers_left - arglen - 2 <= 0) 203 bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen); 204 strcpy(extra_headers_ptr, headers_llist->data); 205 extra_headers_ptr += arglen; 206 extra_headers_left -= ( arglen + 2 ); 207 *extra_headers_ptr++ = '\r'; 208 *extra_headers_ptr++ = '\n'; 209 *(extra_headers_ptr + 1) = 0; 170 cp += sprintf(cp, "%s\r\n", headers_llist->data); 210 171 headers_llist = headers_llist->link; 211 172 } 212 173 } 174 #endif 213 175 214 176 parse_url(argv[optind], &target); … … 216 178 server.port = target.port; 217 179 218 /* 219 * Use the proxy if necessary. 220 */ 180 /* Use the proxy if necessary */ 221 181 if (use_proxy) { 222 182 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); 223 183 if (proxy && *proxy) { 224 parse_url( bb_xstrdup(proxy), &server);184 parse_url(proxy, &server); 225 185 } else { 226 186 use_proxy = 0; … … 232 192 // Dirty hack. Needed because bb_get_last_path_component 233 193 // will destroy trailing / by storing '\0' in last byte! 234 if (*target.path && target.path[strlen(target.path)-1]!='/') {235 fname_out = 236 #if def CONFIG_FEATURE_WGET_STATUSBAR237 curfile =238 #endif 239 bb_get_last_path_component(target.path);240 }241 if (fname_out==NULL || strlen(fname_out)<1) {242 fname_out =243 #ifdef CONFIG_FEATURE_WGET_STATUSBAR 244 curfile = 245 #endif 246 "index.html"; 194 if (!last_char_is(target.path, '/')) { 195 fname_out = bb_get_last_path_component(target.path); 196 #if ENABLE_FEATURE_WGET_STATUSBAR 197 curfile = fname_out; 198 #endif 199 } 200 if (!fname_out || !fname_out[0]) { 201 /* bb_get_last_path_component writes 202 * to last '/' only. We don't have one here... */ 203 fname_out = (char*)"index.html"; 204 #if ENABLE_FEATURE_WGET_STATUSBAR 205 curfile = fname_out; 206 #endif 247 207 } 248 208 if (dir_prefix != NULL) 249 209 fname_out = concat_path_file(dir_prefix, fname_out); 250 #if def CONFIG_FEATURE_WGET_STATUSBAR210 #if ENABLE_FEATURE_WGET_STATUSBAR 251 211 } else { 252 212 curfile = bb_get_last_path_component(fname_out); 253 213 #endif 254 214 } 255 if (do_continue && !fname_out) 256 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); 257 258 259 /* 260 * Open the output file stream. 261 */ 262 if (strcmp(fname_out, "-") == 0) { 263 output = stdout; 264 quiet_flag = TRUE; 265 } else { 266 output = bb_xfopen(fname_out, (do_continue ? "a" : "w")); 267 } 268 269 /* 270 * Determine where to start transfer. 271 */ 272 if (do_continue) { 273 if (fstat(fileno(output), &sbuf) < 0) 274 bb_perror_msg_and_die("fstat()"); 275 if (sbuf.st_size > 0) 276 beg_range = sbuf.st_size; 277 else 278 do_continue = 0; 215 /* Impossible? 216 if ((opt & WGET_OPT_CONTINUE) && !fname_out) 217 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */ 218 219 /* Determine where to start transfer */ 220 if (LONE_DASH(fname_out)) { 221 output_fd = 1; 222 opt &= ~WGET_OPT_CONTINUE; 223 } 224 if (opt & WGET_OPT_CONTINUE) { 225 output_fd = open(fname_out, O_WRONLY); 226 if (output_fd >= 0) { 227 beg_range = xlseek(output_fd, 0, SEEK_END); 228 } 229 /* File doesn't exist. We do not create file here yet. 230 We are not sure it exists on remove side */ 279 231 } 280 232 … … 282 234 * sites (i.e. ftp.us.debian.org) use round-robin DNS 283 235 * and we want to connect to only one IP... */ 284 bb_lookup_host(&s_in, server.host);285 s_in.sin_port = server.port;286 if (quiet_flag==FALSE) {287 fprintf(stdout, "Connecting to %s[%s]:%d\n",288 server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port));236 lsa = xhost2sockaddr(server.host, server.port); 237 if (!(opt & WGET_OPT_QUIET)) { 238 fprintf(stderr, "Connecting to %s (%s)\n", server.host, 239 xmalloc_sockaddr2dotted(&lsa->sa)); 240 /* We leak result of xmalloc_sockaddr2dotted */ 289 241 } 290 242 … … 296 248 got_clen = chunked = 0; 297 249 298 if (! --try) 299 close_delete_and_die("too many redirections"); 300 301 /* 302 * Open socket to http server 303 */ 250 if (!--try) 251 bb_error_msg_and_die("too many redirections"); 252 253 /* Open socket to http server */ 304 254 if (sfp) fclose(sfp); 305 sfp = open_socket(&s_in); 306 307 /* 308 * Send HTTP request. 309 */ 255 sfp = open_socket(lsa); 256 257 /* Send HTTP request. */ 310 258 if (use_proxy) { 311 const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n"; 312 #ifdef CONFIG_FEATURE_WGET_IP6_LITERAL 313 if (strchr(target.host, ':')) 314 format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n"; 315 #endif 316 fprintf(sfp, format, 259 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n", 317 260 target.is_ftp ? "f" : "ht", target.host, 318 ntohs(target.port),target.path);261 target.path); 319 262 } else { 320 263 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); 321 264 } 322 265 323 fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host); 324 325 #ifdef CONFIG_FEATURE_WGET_AUTHENTICATION 266 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", 267 target.host, user_agent); 268 269 #if ENABLE_FEATURE_WGET_AUTHENTICATION 326 270 if (target.user) { 327 fprintf(sfp, " Authorization: Basic %s\r\n",328 base64enc ((unsigned char*)target.user, buf, sizeof(buf)));271 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, 272 base64enc_512(buf, target.user)); 329 273 } 330 274 if (use_proxy && server.user) { 331 275 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", 332 base64enc ((unsigned char*)server.user, buf, sizeof(buf)));276 base64enc_512(buf, server.user)); 333 277 } 334 278 #endif 335 279 336 if (do_continue) 337 fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range); 338 if(extra_headers_left < sizeof(extra_headers)) 339 fputs(extra_headers,sfp); 340 fprintf(sfp,"Connection: close\r\n\r\n"); 280 if (beg_range) 281 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range); 282 #if ENABLE_FEATURE_WGET_LONG_OPTIONS 283 if (extra_headers) 284 fputs(extra_headers, sfp); 285 #endif 286 fprintf(sfp, "Connection: close\r\n\r\n"); 341 287 342 288 /* 343 289 * Retrieve HTTP response line and check for "200" status code. 344 290 */ 345 read_response:291 read_response: 346 292 if (fgets(buf, sizeof(buf), sfp) == NULL) 347 close_delete_and_die("no response from server"); 348 349 for (s = buf ; *s != '\0' && !isspace(*s) ; ++s) 350 ; 351 for ( ; isspace(*s) ; ++s) 352 ; 353 switch (status = atoi(s)) { 354 case 0: 355 case 100: 356 while (gethdr(buf, sizeof(buf), sfp, &n) != NULL); 357 goto read_response; 358 case 200: 359 if (do_continue && output != stdout) 360 output = freopen(fname_out, "w", output); 361 do_continue = 0; 293 bb_error_msg_and_die("no response from server"); 294 295 str = buf; 296 str = skip_non_whitespace(str); 297 str = skip_whitespace(str); 298 // FIXME: no error check 299 // xatou wouldn't work: "200 OK" 300 status = atoi(str); 301 switch (status) { 302 case 0: 303 case 100: 304 while (gethdr(buf, sizeof(buf), sfp, &n) != NULL) 305 /* eat all remaining headers */; 306 goto read_response; 307 case 200: 308 break; 309 case 300: /* redirection */ 310 case 301: 311 case 302: 312 case 303: 313 break; 314 case 206: 315 if (beg_range) 362 316 break; 363 case 300: /* redirection */ 364 case 301: 365 case 302: 366 case 303: 367 break; 368 case 206: 369 if (do_continue) 370 break; 371 /*FALLTHRU*/ 372 default: 373 chomp(buf); 374 close_delete_and_die("server returned error %d: %s", atoi(s), buf); 317 /*FALLTHRU*/ 318 default: 319 /* Show first line only and kill any ESC tricks */ 320 buf[strcspn(buf, "\n\r\x1b")] = '\0'; 321 bb_error_msg_and_die("server returned error: %s", buf); 375 322 } 376 323 … … 378 325 * Retrieve HTTP headers. 379 326 */ 380 while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { 381 if (strcasecmp(buf, "content-length") == 0) { 382 unsigned long value; 383 if (safe_strtoul(s, &value)) { 384 close_delete_and_die("content-length %s is garbage", s); 327 while ((str = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { 328 /* gethdr did already convert the "FOO:" string to lowercase */ 329 smalluint key = index_in_strings(keywords, *&buf) + 1; 330 if (key == KEY_content_length) { 331 content_len = BB_STRTOOFF(str, NULL, 10); 332 if (errno || content_len < 0) { 333 bb_error_msg_and_die("content-length %s is garbage", str); 385 334 } 386 filesize = value;387 335 got_clen = 1; 388 336 continue; 389 337 } 390 if (strcasecmp(buf, "transfer-encoding") == 0) { 391 if (strcasecmp(s, "chunked") == 0) { 392 chunked = got_clen = 1; 393 } else { 394 close_delete_and_die("server wants to do %s transfer encoding", s); 395 } 338 if (key == KEY_transfer_encoding) { 339 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) 340 bb_error_msg_and_die("server wants to do %s transfer encoding", str); 341 chunked = got_clen = 1; 396 342 } 397 if (strcasecmp(buf, "location") == 0) { 398 if (s[0] == '/') 399 target.path = bb_xstrdup(s+1); 343 if (key == KEY_location) { 344 if (str[0] == '/') 345 /* free(target.allocated); */ 346 target.path = /* target.allocated = */ xstrdup(str+1); 400 347 else { 401 parse_url( bb_xstrdup(s), &target);348 parse_url(str, &target); 402 349 if (use_proxy == 0) { 403 350 server.host = target.host; 404 351 server.port = target.port; 405 352 } 406 bb_lookup_host(&s_in, server.host);407 s_in.sin_port = server.port;353 free(lsa); 354 lsa = xhost2sockaddr(server.host, server.port); 408 355 break; 409 356 } 410 357 } 411 358 } 412 } while (status >= 300);359 } while (status >= 300); 413 360 414 361 dfp = sfp; 415 } 416 else417 { 362 363 } else { 364 418 365 /* 419 366 * FTP session 420 367 */ 421 if (! 422 target.user = bb_xstrdup("anonymous:busybox@");423 424 sfp = open_socket( &s_in);368 if (!target.user) 369 target.user = xstrdup("anonymous:busybox@"); 370 371 sfp = open_socket(lsa); 425 372 if (ftpcmd(NULL, NULL, sfp, buf) != 220) 426 close_delete_and_die("%s", buf+4);373 bb_error_msg_and_die("%s", buf+4); 427 374 428 375 /* … … 430 377 * trying to log in 431 378 */ 432 s = strchr(target.user, ':'); 433 if (s) 434 *(s++) = '\0'; 435 switch(ftpcmd("USER ", target.user, sfp, buf)) { 436 case 230: 379 str = strchr(target.user, ':'); 380 if (str) 381 *(str++) = '\0'; 382 switch (ftpcmd("USER ", target.user, sfp, buf)) { 383 case 230: 384 break; 385 case 331: 386 if (ftpcmd("PASS ", str, sfp, buf) == 230) 437 387 break; 438 case 331: 439 if (ftpcmd("PASS ", s, sfp, buf) == 230) 440 break; 441 /* FALLTHRU (failed login) */ 442 default: 443 close_delete_and_die("ftp login: %s", buf+4); 388 /* FALLTHRU (failed login) */ 389 default: 390 bb_error_msg_and_die("ftp login: %s", buf+4); 444 391 } 445 392 … … 450 397 */ 451 398 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) { 452 unsigned long value;453 if ( safe_strtoul(buf+4, &value)) {454 close_delete_and_die("SIZE value is garbage");399 content_len = BB_STRTOOFF(buf+4, NULL, 10); 400 if (errno || content_len < 0) { 401 bb_error_msg_and_die("SIZE value is garbage"); 455 402 } 456 filesize = value;457 403 got_clen = 1; 458 404 } … … 461 407 * Entering passive mode 462 408 */ 463 if (ftpcmd("PASV", NULL, sfp, buf) != 227) 464 close_delete_and_die("PASV: %s", buf+4); 465 s = strrchr(buf, ','); 466 *s = 0; 467 port = atoi(s+1); 468 s = strrchr(buf, ','); 469 port += atoi(s+1) * 256; 470 s_in.sin_port = htons(port); 471 dfp = open_socket(&s_in); 472 473 if (do_continue) { 474 sprintf(buf, "REST %ld", beg_range); 475 if (ftpcmd(buf, NULL, sfp, buf) != 350) { 476 if (output != stdout) 477 output = freopen(fname_out, "w", output); 478 do_continue = 0; 479 } else 480 filesize -= beg_range; 409 if (ftpcmd("PASV", NULL, sfp, buf) != 227) { 410 pasv_error: 411 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf); 412 } 413 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] 414 // Server's IP is N1.N2.N3.N4 (we ignore it) 415 // Server's port for data connection is P1*256+P2 416 str = strrchr(buf, ')'); 417 if (str) str[0] = '\0'; 418 str = strrchr(buf, ','); 419 if (!str) goto pasv_error; 420 port = xatou_range(str+1, 0, 255); 421 *str = '\0'; 422 str = strrchr(buf, ','); 423 if (!str) goto pasv_error; 424 port += xatou_range(str+1, 0, 255) * 256; 425 set_nport(lsa, htons(port)); 426 dfp = open_socket(lsa); 427 428 if (beg_range) { 429 sprintf(buf, "REST %"OFF_FMT"d", beg_range); 430 if (ftpcmd(buf, NULL, sfp, buf) == 350) 431 content_len -= beg_range; 481 432 } 482 433 483 434 if (ftpcmd("RETR ", target.path, sfp, buf) > 150) 484 close_delete_and_die("RETR: %s", buf+4); 485 } 486 435 bb_error_msg_and_die("bad response to RETR: %s", buf); 436 } 437 if (opt & WGET_OPT_SPIDER) { 438 if (ENABLE_FEATURE_CLEAN_UP) 439 fclose(sfp); 440 goto done; 441 } 487 442 488 443 /* … … 491 446 if (chunked) { 492 447 fgets(buf, sizeof(buf), dfp); 493 filesize = strtol(buf, (char **) NULL, 16); 494 } 495 496 if (quiet_flag==FALSE) 448 content_len = STRTOOFF(buf, NULL, 16); 449 /* FIXME: error check?? */ 450 } 451 452 /* Do it before progressmeter (want to have nice error message) */ 453 if (output_fd < 0) 454 output_fd = xopen(fname_out, 455 O_WRONLY|O_CREAT|O_EXCL|O_TRUNC); 456 457 if (!(opt & WGET_OPT_QUIET)) 497 458 progressmeter(-1); 498 459 499 460 do { 500 while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && (filesize < sizeof(buf)) ? filesize : sizeof(buf)), dfp)) > 0) { 501 if (safe_fwrite(buf, 1, n, output) != n) { 461 while (content_len > 0 || !got_clen) { 462 unsigned rdsz = sizeof(buf); 463 if (content_len < sizeof(buf) && (chunked || got_clen)) 464 rdsz = (unsigned)content_len; 465 n = safe_fread(buf, rdsz, dfp); 466 if (n <= 0) 467 break; 468 if (full_write(output_fd, buf, n) != n) { 502 469 bb_perror_msg_and_die(bb_msg_write_error); 503 470 } 504 #if def CONFIG_FEATURE_WGET_STATUSBAR471 #if ENABLE_FEATURE_WGET_STATUSBAR 505 472 transferred += n; 506 473 #endif 507 474 if (got_clen) { 508 filesize-= n;475 content_len -= n; 509 476 } 510 477 } … … 513 480 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ 514 481 safe_fgets(buf, sizeof(buf), dfp); 515 filesize = strtol(buf, (char **) NULL, 16); 516 if (filesize==0) { 482 content_len = STRTOOFF(buf, NULL, 16); 483 /* FIXME: error check? */ 484 if (content_len == 0) { 517 485 chunked = 0; /* all done! */ 518 486 } … … 524 492 } while (chunked); 525 493 526 if ( quiet_flag==FALSE)494 if (!(opt & WGET_OPT_QUIET)) 527 495 progressmeter(1); 528 496 … … 533 501 ftpcmd("QUIT", NULL, sfp, buf); 534 502 } 503 done: 535 504 exit(EXIT_SUCCESS); 536 505 } 537 506 538 507 539 void parse_url(char *url, struct host_info *h) 540 { 541 char *cp, *sp, *up, *pp; 508 static void parse_url(char *src_url, struct host_info *h) 509 { 510 char *url, *p, *sp; 511 512 /* h->allocated = */ url = xstrdup(src_url); 542 513 543 514 if (strncmp(url, "http://", 7) == 0) { … … 546 517 h->is_ftp = 0; 547 518 } else if (strncmp(url, "ftp://", 6) == 0) { 548 h->port = bb_lookup_port("ftp", "t fp", 21);519 h->port = bb_lookup_port("ftp", "tcp", 21); 549 520 h->host = url + 6; 550 521 h->is_ftp = 1; … … 552 523 bb_error_msg_and_die("not an http or ftp url: %s", url); 553 524 525 // FYI: 526 // "Real" wget 'http://busybox.net?var=a/b' sends this request: 527 // 'GET /?var=a/b HTTP 1.0' 528 // and saves 'index.html?var=a%2Fb' (we save 'b') 529 // wget 'http://busybox.net?login=john@doe': 530 // request: 'GET /?login=john@doe HTTP/1.0' 531 // saves: 'index.html?login=john@doe' (we save '?login=john@doe') 532 // wget 'http://busybox.net#test/test': 533 // request: 'GET / HTTP/1.0' 534 // saves: 'index.html' (we save 'test') 535 // 536 // We also don't add unique .N suffix if file exists... 554 537 sp = strchr(h->host, '/'); 555 if (sp) { 556 *sp++ = '\0'; 538 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p; 539 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p; 540 if (!sp) { 541 /* must be writable because of bb_get_last_path_component() */ 542 static char nullstr[] ALIGN1 = ""; 543 h->path = nullstr; 544 } else if (*sp == '/') { 545 *sp = '\0'; 546 h->path = sp + 1; 547 } else { // '#' or '?' 548 // http://busybox.net?login=john@doe is a valid URL 549 // memmove converts to: 550 // http:/busybox.nett?login=john@doe... 551 memmove(h->host-1, h->host, sp - h->host); 552 h->host--; 553 sp[-1] = '\0'; 557 554 h->path = sp; 558 } else559 h->path = bb_xstrdup(""); 560 561 up = strrchr(h->host, '@');562 if ( up != NULL) {555 } 556 557 sp = strrchr(h->host, '@'); 558 h->user = NULL; 559 if (sp != NULL) { 563 560 h->user = h->host; 564 *up++ = '\0'; 565 h->host = up; 566 } else 567 h->user = NULL; 568 569 pp = h->host; 570 571 #ifdef CONFIG_FEATURE_WGET_IP6_LITERAL 572 if (h->host[0] == '[') { 573 char *ep; 574 575 ep = h->host + 1; 576 while (*ep == ':' || isxdigit (*ep)) 577 ep++; 578 if (*ep == ']') { 579 h->host++; 580 *ep = '\0'; 581 pp = ep + 1; 582 } 583 } 584 #endif 585 586 cp = strchr(pp, ':'); 587 if (cp != NULL) { 588 *cp++ = '\0'; 589 h->port = htons(atoi(cp)); 590 } 591 } 592 593 594 FILE *open_socket(struct sockaddr_in *s_in) 561 *sp = '\0'; 562 h->host = sp + 1; 563 } 564 565 sp = h->host; 566 } 567 568 569 static FILE *open_socket(len_and_sockaddr *lsa) 595 570 { 596 571 FILE *fp; 597 572 598 fp = fdopen(xconnect(s_in), "r+"); 573 /* glibc 2.4 seems to try seeking on it - ??! */ 574 /* hopefully it understands what ESPIPE means... */ 575 fp = fdopen(xconnect_stream(lsa), "r+"); 599 576 if (fp == NULL) 600 bb_perror_msg_and_die("fdopen ()");577 bb_perror_msg_and_die("fdopen"); 601 578 602 579 return fp; … … 604 581 605 582 606 char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)583 static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) 607 584 { 608 585 char *s, *hdrval; … … 616 593 617 594 /* see if we are at the end of the headers */ 618 for (s = buf ; *s == '\r'; ++s)595 for (s = buf; *s == '\r'; ++s) 619 596 ; 620 597 if (s[0] == '\n') … … 622 599 623 600 /* convert the header name to lower case */ 624 for (s = buf ; isalnum(*s) || *s == '-'; ++s)601 for (s = buf; isalnum(*s) || *s == '-'; ++s) 625 602 *s = tolower(*s); 626 603 … … 630 607 631 608 /* locate the start of the header value */ 632 for (*s++ = '\0' ; *s == ' ' || *s == '\t'; ++s)609 for (*s++ = '\0'; *s == ' ' || *s == '\t'; ++s) 633 610 ; 634 611 hdrval = s; … … 651 628 } 652 629 653 static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf) 654 { 630 static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) 631 { 632 int result; 655 633 if (s1) { 656 if (!s2) s2 ="";634 if (!s2) s2 = ""; 657 635 fprintf(fp, "%s%s\r\n", s1, s2); 658 636 fflush(fp); … … 663 641 664 642 if (fgets(buf, 510, fp) == NULL) { 665 bb_perror_msg_and_die(" fgets()");643 bb_perror_msg_and_die("error getting response"); 666 644 } 667 645 buf_ptr = strstr(buf, "\r\n"); … … 669 647 *buf_ptr = '\0'; 670 648 } 671 } while (! isdigit(buf[0]) || buf[3] != ' '); 672 673 return atoi(buf); 674 } 675 676 #ifdef CONFIG_FEATURE_WGET_STATUSBAR 649 } while (!isdigit(buf[0]) || buf[3] != ' '); 650 651 buf[3] = '\0'; 652 result = xatoi_u(buf); 653 buf[3] = ' '; 654 return result; 655 } 656 657 #if ENABLE_FEATURE_WGET_STATUSBAR 677 658 /* Stuff below is from BSD rcp util.c, as added to openshh. 678 659 * Original copyright notice is retained at the end of this file. 679 *680 660 */ 681 682 683 661 static int 684 662 getttywidth(void) 685 663 { 686 int width =0;664 int width; 687 665 get_terminal_width_height(0, &width, NULL); 688 return (width);666 return width; 689 667 } 690 668 … … 698 676 } 699 677 700 static void 701 alarmtimer(int wait) 678 static void alarmtimer(int iwait) 702 679 { 703 680 struct itimerval itv; 704 681 705 itv.it_value.tv_sec = wait;682 itv.it_value.tv_sec = iwait; 706 683 itv.it_value.tv_usec = 0; 707 684 itv.it_interval = itv.it_value; … … 709 686 } 710 687 711 712 688 static void 713 689 progressmeter(int flag) 714 690 { 715 static struct timeval lastupdate; 691 static unsigned lastupdate_sec; 692 static unsigned start_sec; 716 693 static off_t lastsize, totalsize; 717 694 718 struct timeval now, td, wait;719 695 off_t abbrevsize; 720 int elapsed, ratio, barlength, i; 721 char buf[256]; 722 723 if (flag == -1) { 724 (void) gettimeofday(&start, (struct timezone *) 0); 725 lastupdate = start; 696 unsigned since_last_update, elapsed; 697 unsigned ratio; 698 int barlength, i; 699 700 if (flag == -1) { /* first call to progressmeter */ 701 start_sec = monotonic_sec(); 702 lastupdate_sec = start_sec; 726 703 lastsize = 0; 727 totalsize = filesize; /* as filesize changes.. */ 728 } 729 730 (void) gettimeofday(&now, (struct timezone *) 0); 704 totalsize = content_len + beg_range; /* as content_len changes.. */ 705 } 706 731 707 ratio = 100; 732 708 if (totalsize != 0 && !chunked) { 733 ratio = (int) (100 * transferred / totalsize); 734 ratio = MIN(ratio, 100); 709 /* long long helps to have it working even if !LFS */ 710 ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize); 711 if (ratio > 100) ratio = 100; 735 712 } 736 713 737 714 fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio); 738 715 739 barlength = getttywidth() - 51; 740 if (barlength > 0 && barlength < sizeof(buf)) { 716 barlength = getttywidth() - 49; 717 if (barlength > 0) { 718 /* god bless gcc for variable arrays :) */ 741 719 i = barlength * ratio / 100; 742 memset(buf, '*', i); 743 memset(buf + i, ' ', barlength - i); 744 buf[barlength] = '\0'; 745 fprintf(stderr, "|%s|", buf); 720 { 721 char buf[i+1]; 722 memset(buf, '*', i); 723 buf[i] = '\0'; 724 fprintf(stderr, "|%s%*s|", buf, barlength - i, ""); 725 } 746 726 } 747 727 i = 0; 748 abbrevsize = transferred ;728 abbrevsize = transferred + beg_range; 749 729 while (abbrevsize >= 100000) { 750 730 i++; 751 731 abbrevsize >>= 10; 752 732 } 753 /* See http://en.wikipedia.org/wiki/Tera */ 754 fprintf(stderr, "%6d %c%c ", (int)abbrevsize, " KMGTPEZY"[i], i?'B':' '); 755 756 timersub(&now, &lastupdate, &wait); 733 /* see http://en.wikipedia.org/wiki/Tera */ 734 fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]); 735 736 // Nuts! Ain't it easier to update progress meter ONLY when we transferred++? 737 // FIXME: get rid of alarmtimer + updateprogressmeter mess 738 739 elapsed = monotonic_sec(); 740 since_last_update = elapsed - lastupdate_sec; 757 741 if (transferred > lastsize) { 758 lastupdate = now;742 lastupdate_sec = elapsed; 759 743 lastsize = transferred; 760 if (wait.tv_sec >= STALLTIME) 761 timeradd(&start, &wait, &start); 762 wait.tv_sec = 0; 763 } 764 timersub(&now, &start, &td); 765 elapsed = td.tv_sec; 766 767 if (wait.tv_sec >= STALLTIME) { 744 if (since_last_update >= STALLTIME) { 745 /* We "cut off" these seconds from elapsed time 746 * by adjusting start time */ 747 start_sec += since_last_update; 748 } 749 since_last_update = 0; /* we are un-stalled now */ 750 } 751 elapsed -= start_sec; /* now it's "elapsed since start" */ 752 753 if (since_last_update >= STALLTIME) { 768 754 fprintf(stderr, " - stalled -"); 769 } else if (transferred <= 0 || elapsed <= 0 || transferred > totalsize || chunked) {770 fprintf(stderr, "--:--:-- ETA");771 755 } else { 772 /* totalsize / (transferred/elapsed) - elapsed: */ 773 int eta = (int) (totalsize*elapsed/transferred - elapsed); 774 i = eta % 3600; 775 fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60); 776 } 777 778 if (flag == -1) { 756 off_t to_download = totalsize - beg_range; 757 if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) { 758 fprintf(stderr, "--:--:-- ETA"); 759 } else { 760 /* to_download / (transferred/elapsed) - elapsed: */ 761 int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed); 762 /* (long long helps to have working ETA even if !LFS) */ 763 i = eta % 3600; 764 fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60); 765 } 766 } 767 768 if (flag == -1) { /* first call to progressmeter */ 779 769 struct sigaction sa; 780 770 sa.sa_handler = updateprogressmeter; … … 783 773 sigaction(SIGALRM, &sa, NULL); 784 774 alarmtimer(1); 785 } else if (flag == 1) { 775 } else if (flag == 1) { /* last call to progressmeter */ 786 776 alarmtimer(0); 787 777 transferred = 0; … … 789 779 } 790 780 } 791 #endif 781 #endif /* FEATURE_WGET_STATUSBAR */ 792 782 793 783 /* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff, … … 826 816 * SUCH DAMAGE. 827 817 * 828 * $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $829 818 */
Note:
See TracChangeset
for help on using the changeset viewer.