diff options
Diffstat (limited to 'wget.c')
-rw-r--r-- | wget.c | 834 |
1 files changed, 0 insertions, 834 deletions
diff --git a/wget.c b/wget.c deleted file mode 100644 index 59373d1d9..000000000 --- a/wget.c +++ /dev/null | |||
@@ -1,834 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * wget - retrieve a file using HTTP or FTP | ||
4 | * | ||
5 | * Chip Rosenthal Covad Communications <chip@laserlink.net> | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <stdio.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <unistd.h> | ||
13 | #include <ctype.h> | ||
14 | #include <string.h> | ||
15 | #include <unistd.h> | ||
16 | #include <signal.h> | ||
17 | #include <sys/ioctl.h> | ||
18 | |||
19 | #include <sys/time.h> | ||
20 | #include <sys/types.h> | ||
21 | #include <sys/stat.h> | ||
22 | #include <sys/socket.h> | ||
23 | #include <netinet/in.h> | ||
24 | #include <arpa/inet.h> | ||
25 | #include <netdb.h> | ||
26 | |||
27 | #ifndef _GNU_SOURCE | ||
28 | #define _GNU_SOURCE | ||
29 | #endif | ||
30 | #include <getopt.h> | ||
31 | |||
32 | #include "busybox.h" | ||
33 | |||
34 | /* Stupid libc5 doesn't define this... */ | ||
35 | #ifndef timersub | ||
36 | #define timersub(a, b, result) \ | ||
37 | do { \ | ||
38 | (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ | ||
39 | (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ | ||
40 | if ((result)->tv_usec < 0) { \ | ||
41 | --(result)->tv_sec; \ | ||
42 | (result)->tv_usec += 1000000; \ | ||
43 | } \ | ||
44 | } while (0) | ||
45 | #endif | ||
46 | |||
47 | struct host_info { | ||
48 | char *host; | ||
49 | int port; | ||
50 | char *path; | ||
51 | int is_ftp; | ||
52 | char *user; | ||
53 | }; | ||
54 | |||
55 | static void parse_url(char *url, struct host_info *h); | ||
56 | static FILE *open_socket(char *host, int port); | ||
57 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); | ||
58 | static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf); | ||
59 | |||
60 | /* Globals (can be accessed from signal handlers */ | ||
61 | static off_t filesize = 0; /* content-length of the file */ | ||
62 | static int chunked = 0; /* chunked transfer encoding */ | ||
63 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
64 | static void progressmeter(int flag); | ||
65 | static char *curfile; /* Name of current file being transferred. */ | ||
66 | static struct timeval start; /* Time a transfer started. */ | ||
67 | static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */ | ||
68 | /* For progressmeter() -- number of seconds before xfer considered "stalled" */ | ||
69 | static const int STALLTIME = 5; | ||
70 | #endif | ||
71 | |||
72 | static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue) | ||
73 | { | ||
74 | if (output != stdout && do_continue==0) { | ||
75 | fclose(output); | ||
76 | unlink(fname_out); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | /* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the | ||
81 | * number of elements read, and a short count if an eof or non-interrupt | ||
82 | * error is encountered. */ | ||
83 | static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) | ||
84 | { | ||
85 | size_t ret = 0; | ||
86 | |||
87 | do { | ||
88 | clearerr(stream); | ||
89 | ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); | ||
90 | } while (ret < nmemb && ferror(stream) && errno == EINTR); | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the | ||
96 | * number of elements written, and a short count if an eof or non-interrupt | ||
97 | * error is encountered. */ | ||
98 | static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream) | ||
99 | { | ||
100 | size_t ret = 0; | ||
101 | |||
102 | do { | ||
103 | clearerr(stream); | ||
104 | ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream); | ||
105 | } while (ret < nmemb && ferror(stream) && errno == EINTR); | ||
106 | |||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | /* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM. | ||
111 | * Returns S, or NULL if an eof or non-interrupt error is encountered. */ | ||
112 | static char *safe_fgets(char *s, int size, FILE *stream) | ||
113 | { | ||
114 | char *ret; | ||
115 | |||
116 | do { | ||
117 | clearerr(stream); | ||
118 | ret = fgets(s, size, stream); | ||
119 | } while (ret == NULL && ferror(stream) && errno == EINTR); | ||
120 | |||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | #define close_delete_and_die(s...) { \ | ||
125 | close_and_delete_outfile(output, fname_out, do_continue); \ | ||
126 | error_msg_and_die(s); } | ||
127 | |||
128 | |||
129 | #ifdef BB_FEATURE_WGET_AUTHENTICATION | ||
130 | /* | ||
131 | * Base64-encode character string | ||
132 | * oops... isn't something similar in uuencode.c? | ||
133 | * It would be better to use already existing code | ||
134 | */ | ||
135 | char *base64enc(char *p, char *buf, int len) { | ||
136 | |||
137 | char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||
138 | "0123456789+/"; | ||
139 | char *s = buf; | ||
140 | |||
141 | while(*p) { | ||
142 | if (s >= buf+len-4) | ||
143 | error_msg_and_die("buffer overflow"); | ||
144 | *(s++) = al[(*p >> 2) & 0x3F]; | ||
145 | *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)]; | ||
146 | *s = *(s+1) = '='; | ||
147 | *(s+2) = 0; | ||
148 | if (! *(++p)) break; | ||
149 | *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)]; | ||
150 | if (! *(++p)) break; | ||
151 | *(s++) = al[*(p++) & 0x3F]; | ||
152 | } | ||
153 | |||
154 | return buf; | ||
155 | } | ||
156 | #endif | ||
157 | |||
158 | int wget_main(int argc, char **argv) | ||
159 | { | ||
160 | int n, try=5, status; | ||
161 | int port; | ||
162 | char *proxy; | ||
163 | char *dir_prefix=NULL; | ||
164 | char *s, buf[512]; | ||
165 | struct stat sbuf; | ||
166 | char extra_headers[1024]; | ||
167 | char *extra_headers_ptr = extra_headers; | ||
168 | int extra_headers_left = sizeof(extra_headers); | ||
169 | int which_long_opt = 0, option_index = -1; | ||
170 | struct host_info server, target; | ||
171 | |||
172 | FILE *sfp = NULL; /* socket to web/ftp server */ | ||
173 | FILE *dfp = NULL; /* socket to ftp server (data) */ | ||
174 | char *fname_out = NULL; /* where to direct output (-O) */ | ||
175 | int do_continue = 0; /* continue a prev transfer (-c) */ | ||
176 | long beg_range = 0L; /* range at which continue begins */ | ||
177 | int got_clen = 0; /* got content-length: from server */ | ||
178 | FILE *output; /* socket to web server */ | ||
179 | int quiet_flag = FALSE; /* Be verry, verry quiet... */ | ||
180 | |||
181 | #define LONG_HEADER 1 | ||
182 | struct option long_options[] = { | ||
183 | { "continue", 0, NULL, 'c' }, | ||
184 | { "quiet", 0, NULL, 'q' }, | ||
185 | { "output-document", 1, NULL, 'O' }, | ||
186 | { "header", 1, &which_long_opt, LONG_HEADER }, | ||
187 | { 0, 0, 0, 0 } | ||
188 | }; | ||
189 | /* | ||
190 | * Crack command line. | ||
191 | */ | ||
192 | while ((n = getopt_long(argc, argv, "cqO:P:", long_options, &option_index)) != EOF) { | ||
193 | switch (n) { | ||
194 | case 'c': | ||
195 | ++do_continue; | ||
196 | break; | ||
197 | case 'P': | ||
198 | dir_prefix = optarg; | ||
199 | break; | ||
200 | case 'q': | ||
201 | quiet_flag = TRUE; | ||
202 | break; | ||
203 | case 'O': | ||
204 | /* can't set fname_out to NULL if outputting to stdout, because | ||
205 | * this gets interpreted as the auto-gen output filename | ||
206 | * case below - tausq@debian.org | ||
207 | */ | ||
208 | fname_out = optarg; | ||
209 | break; | ||
210 | case 0: | ||
211 | switch (which_long_opt) { | ||
212 | case LONG_HEADER: { | ||
213 | int arglen = strlen(optarg); | ||
214 | if(extra_headers_left - arglen - 2 <= 0) | ||
215 | error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen); | ||
216 | strcpy(extra_headers_ptr, optarg); | ||
217 | extra_headers_ptr += arglen; | ||
218 | extra_headers_left -= ( arglen + 2 ); | ||
219 | *extra_headers_ptr++ = '\r'; | ||
220 | *extra_headers_ptr++ = '\n'; | ||
221 | *(extra_headers_ptr + 1) = 0; | ||
222 | break; | ||
223 | } | ||
224 | } | ||
225 | break; | ||
226 | default: | ||
227 | show_usage(); | ||
228 | } | ||
229 | } | ||
230 | |||
231 | if (argc - optind != 1) | ||
232 | show_usage(); | ||
233 | |||
234 | parse_url(argv[optind], &target); | ||
235 | server.host = target.host; | ||
236 | server.port = target.port; | ||
237 | |||
238 | /* | ||
239 | * Use the proxy if necessary. | ||
240 | */ | ||
241 | proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); | ||
242 | if (proxy) | ||
243 | parse_url(xstrdup(proxy), &server); | ||
244 | |||
245 | /* Guess an output filename */ | ||
246 | if (!fname_out) { | ||
247 | fname_out = | ||
248 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
249 | curfile = | ||
250 | #endif | ||
251 | get_last_path_component(target.path); | ||
252 | if (fname_out==NULL || strlen(fname_out)<1) { | ||
253 | fname_out = | ||
254 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
255 | curfile = | ||
256 | #endif | ||
257 | "index.html"; | ||
258 | } | ||
259 | if (dir_prefix != NULL) | ||
260 | fname_out = concat_path_file(dir_prefix, fname_out); | ||
261 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
262 | } else { | ||
263 | curfile = get_last_path_component(fname_out); | ||
264 | #endif | ||
265 | } | ||
266 | if (do_continue && !fname_out) | ||
267 | error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); | ||
268 | |||
269 | |||
270 | /* | ||
271 | * Open the output file stream. | ||
272 | */ | ||
273 | if (strcmp(fname_out, "-") == 0) { | ||
274 | output = stdout; | ||
275 | quiet_flag = TRUE; | ||
276 | } else { | ||
277 | output = xfopen(fname_out, (do_continue ? "a" : "w")); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Determine where to start transfer. | ||
282 | */ | ||
283 | if (do_continue) { | ||
284 | if (fstat(fileno(output), &sbuf) < 0) | ||
285 | perror_msg_and_die("fstat()"); | ||
286 | if (sbuf.st_size > 0) | ||
287 | beg_range = sbuf.st_size; | ||
288 | else | ||
289 | do_continue = 0; | ||
290 | } | ||
291 | |||
292 | if (proxy || !target.is_ftp) { | ||
293 | /* | ||
294 | * HTTP session | ||
295 | */ | ||
296 | do { | ||
297 | if (! --try) | ||
298 | close_delete_and_die("too many redirections"); | ||
299 | |||
300 | /* | ||
301 | * Open socket to http server | ||
302 | */ | ||
303 | if (sfp) fclose(sfp); | ||
304 | sfp = open_socket(server.host, server.port); | ||
305 | |||
306 | /* | ||
307 | * Send HTTP request. | ||
308 | */ | ||
309 | if (proxy) { | ||
310 | fprintf(sfp, "GET %stp://%s:%d/%s HTTP/1.1\r\n", | ||
311 | target.is_ftp ? "f" : "ht", target.host, | ||
312 | target.port, target.path); | ||
313 | } else { | ||
314 | fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); | ||
315 | } | ||
316 | |||
317 | fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host); | ||
318 | |||
319 | #ifdef BB_FEATURE_WGET_AUTHENTICATION | ||
320 | if (target.user) { | ||
321 | fprintf(sfp, "Authorization: Basic %s\r\n", | ||
322 | base64enc(target.user, buf, sizeof(buf))); | ||
323 | } | ||
324 | if (proxy && server.user) { | ||
325 | fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", | ||
326 | base64enc(server.user, buf, sizeof(buf))); | ||
327 | } | ||
328 | #endif | ||
329 | |||
330 | if (do_continue) | ||
331 | fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range); | ||
332 | if(extra_headers_left < sizeof(extra_headers)) | ||
333 | fputs(extra_headers,sfp); | ||
334 | fprintf(sfp,"Connection: close\r\n\r\n"); | ||
335 | |||
336 | /* | ||
337 | * Retrieve HTTP response line and check for "200" status code. | ||
338 | */ | ||
339 | read_response: if (fgets(buf, sizeof(buf), sfp) == NULL) | ||
340 | close_delete_and_die("no response from server"); | ||
341 | |||
342 | for (s = buf ; *s != '\0' && !isspace(*s) ; ++s) | ||
343 | ; | ||
344 | for ( ; isspace(*s) ; ++s) | ||
345 | ; | ||
346 | switch (status = atoi(s)) { | ||
347 | case 0: | ||
348 | case 100: | ||
349 | while (gethdr(buf, sizeof(buf), sfp, &n) != NULL); | ||
350 | goto read_response; | ||
351 | case 200: | ||
352 | if (do_continue && output != stdout) | ||
353 | output = freopen(fname_out, "w", output); | ||
354 | do_continue = 0; | ||
355 | break; | ||
356 | case 300: /* redirection */ | ||
357 | case 301: | ||
358 | case 302: | ||
359 | case 303: | ||
360 | break; | ||
361 | case 206: | ||
362 | if (do_continue) | ||
363 | break; | ||
364 | /*FALLTHRU*/ | ||
365 | default: | ||
366 | chomp(buf); | ||
367 | close_delete_and_die("server returned error %d: %s", atoi(s), buf); | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Retrieve HTTP headers. | ||
372 | */ | ||
373 | while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { | ||
374 | if (strcasecmp(buf, "content-length") == 0) { | ||
375 | filesize = atol(s); | ||
376 | got_clen = 1; | ||
377 | continue; | ||
378 | } | ||
379 | if (strcasecmp(buf, "transfer-encoding") == 0) { | ||
380 | if (strcasecmp(s, "chunked") == 0) { | ||
381 | chunked = got_clen = 1; | ||
382 | } else { | ||
383 | close_delete_and_die("server wants to do %s transfer encoding", s); | ||
384 | } | ||
385 | } | ||
386 | if (strcasecmp(buf, "location") == 0) { | ||
387 | if (s[0] == '/') | ||
388 | target.path = xstrdup(s+1); | ||
389 | else { | ||
390 | parse_url(xstrdup(s), &target); | ||
391 | if (!proxy) { | ||
392 | server.host = target.host; | ||
393 | server.port = target.port; | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } while(status >= 300); | ||
399 | |||
400 | dfp = sfp; | ||
401 | } | ||
402 | else | ||
403 | { | ||
404 | /* | ||
405 | * FTP session | ||
406 | */ | ||
407 | if (! target.user) | ||
408 | target.user = xstrdup("anonymous:busybox@"); | ||
409 | |||
410 | sfp = open_socket(server.host, server.port); | ||
411 | if (ftpcmd(NULL, NULL, sfp, buf) != 220) | ||
412 | close_delete_and_die("%s", buf+4); | ||
413 | |||
414 | /* | ||
415 | * Splitting username:password pair, | ||
416 | * trying to log in | ||
417 | */ | ||
418 | s = strchr(target.user, ':'); | ||
419 | if (s) | ||
420 | *(s++) = '\0'; | ||
421 | switch(ftpcmd("USER ", target.user, sfp, buf)) { | ||
422 | case 230: | ||
423 | break; | ||
424 | case 331: | ||
425 | if (ftpcmd("PASS ", s, sfp, buf) == 230) | ||
426 | break; | ||
427 | /* FALLTHRU (failed login) */ | ||
428 | default: | ||
429 | close_delete_and_die("ftp login: %s", buf+4); | ||
430 | } | ||
431 | |||
432 | ftpcmd("CDUP", NULL, sfp, buf); | ||
433 | ftpcmd("TYPE I", NULL, sfp, buf); | ||
434 | |||
435 | /* | ||
436 | * Querying file size | ||
437 | */ | ||
438 | if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) { | ||
439 | filesize = atol(buf+4); | ||
440 | got_clen = 1; | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * Entering passive mode | ||
445 | */ | ||
446 | if (ftpcmd("PASV", NULL, sfp, buf) != 227) | ||
447 | close_delete_and_die("PASV: %s", buf+4); | ||
448 | s = strrchr(buf, ','); | ||
449 | *s = 0; | ||
450 | port = atoi(s+1); | ||
451 | s = strrchr(buf, ','); | ||
452 | port += atoi(s+1) * 256; | ||
453 | dfp = open_socket(server.host, port); | ||
454 | |||
455 | if (do_continue) { | ||
456 | sprintf(buf, "REST %ld", beg_range); | ||
457 | if (ftpcmd(buf, NULL, sfp, buf) != 350) { | ||
458 | if (output != stdout) | ||
459 | output = freopen(fname_out, "w", output); | ||
460 | do_continue = 0; | ||
461 | } else | ||
462 | filesize -= beg_range; | ||
463 | } | ||
464 | |||
465 | if (ftpcmd("RETR /", target.path, sfp, buf) > 150) | ||
466 | close_delete_and_die("RETR: %s", buf+4); | ||
467 | |||
468 | } | ||
469 | |||
470 | |||
471 | /* | ||
472 | * Retrieve file | ||
473 | */ | ||
474 | if (chunked) { | ||
475 | fgets(buf, sizeof(buf), dfp); | ||
476 | filesize = strtol(buf, (char **) NULL, 16); | ||
477 | } | ||
478 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
479 | if (quiet_flag==FALSE) | ||
480 | progressmeter(-1); | ||
481 | #endif | ||
482 | do { | ||
483 | while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) { | ||
484 | safe_fwrite(buf, 1, n, output); | ||
485 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
486 | statbytes+=n; | ||
487 | #endif | ||
488 | if (got_clen) | ||
489 | filesize -= n; | ||
490 | } | ||
491 | |||
492 | if (chunked) { | ||
493 | safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ | ||
494 | safe_fgets(buf, sizeof(buf), dfp); | ||
495 | filesize = strtol(buf, (char **) NULL, 16); | ||
496 | if (filesize==0) chunked = 0; /* all done! */ | ||
497 | } | ||
498 | |||
499 | if (n == 0 && ferror(dfp)) | ||
500 | perror_msg_and_die("network read error"); | ||
501 | } while (chunked); | ||
502 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
503 | if (quiet_flag==FALSE) | ||
504 | progressmeter(1); | ||
505 | #endif | ||
506 | if (!proxy && target.is_ftp) { | ||
507 | fclose(dfp); | ||
508 | if (ftpcmd(NULL, NULL, sfp, buf) != 226) | ||
509 | error_msg_and_die("ftp error: %s", buf+4); | ||
510 | ftpcmd("QUIT", NULL, sfp, buf); | ||
511 | } | ||
512 | exit(EXIT_SUCCESS); | ||
513 | } | ||
514 | |||
515 | |||
516 | void parse_url(char *url, struct host_info *h) | ||
517 | { | ||
518 | char *cp, *sp, *up; | ||
519 | |||
520 | if (strncmp(url, "http://", 7) == 0) { | ||
521 | h->port = 80; | ||
522 | h->host = url + 7; | ||
523 | h->is_ftp = 0; | ||
524 | } else if (strncmp(url, "ftp://", 6) == 0) { | ||
525 | h->port = 21; | ||
526 | h->host = url + 6; | ||
527 | h->is_ftp = 1; | ||
528 | } else | ||
529 | error_msg_and_die("not an http or ftp url: %s", url); | ||
530 | |||
531 | sp = strchr(h->host, '/'); | ||
532 | if (sp != NULL) { | ||
533 | *sp++ = '\0'; | ||
534 | h->path = sp; | ||
535 | } else | ||
536 | h->path = ""; | ||
537 | |||
538 | up = strrchr(h->host, '@'); | ||
539 | if (up != NULL) { | ||
540 | h->user = h->host; | ||
541 | *up++ = '\0'; | ||
542 | h->host = up; | ||
543 | } else | ||
544 | h->user = NULL; | ||
545 | |||
546 | cp = strchr(h->host, ':'); | ||
547 | if (cp != NULL) { | ||
548 | *cp++ = '\0'; | ||
549 | h->port = atoi(cp); | ||
550 | } | ||
551 | |||
552 | } | ||
553 | |||
554 | |||
555 | FILE *open_socket(char *host, int port) | ||
556 | { | ||
557 | struct sockaddr_in s_in; | ||
558 | struct hostent *hp; | ||
559 | int fd; | ||
560 | FILE *fp; | ||
561 | |||
562 | memset(&s_in, 0, sizeof(s_in)); | ||
563 | s_in.sin_family = AF_INET; | ||
564 | hp = xgethostbyname(host); | ||
565 | memcpy(&s_in.sin_addr, hp->h_addr_list[0], hp->h_length); | ||
566 | s_in.sin_port = htons(port); | ||
567 | |||
568 | /* | ||
569 | * Get the server onto a stdio stream. | ||
570 | */ | ||
571 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
572 | perror_msg_and_die("socket()"); | ||
573 | if (connect(fd, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) | ||
574 | perror_msg_and_die("connect(%s)", host); | ||
575 | if ((fp = fdopen(fd, "r+")) == NULL) | ||
576 | perror_msg_and_die("fdopen()"); | ||
577 | |||
578 | return fp; | ||
579 | } | ||
580 | |||
581 | |||
582 | char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) | ||
583 | { | ||
584 | char *s, *hdrval; | ||
585 | int c; | ||
586 | |||
587 | *istrunc = 0; | ||
588 | |||
589 | /* retrieve header line */ | ||
590 | if (fgets(buf, bufsiz, fp) == NULL) | ||
591 | return NULL; | ||
592 | |||
593 | /* see if we are at the end of the headers */ | ||
594 | for (s = buf ; *s == '\r' ; ++s) | ||
595 | ; | ||
596 | if (s[0] == '\n') | ||
597 | return NULL; | ||
598 | |||
599 | /* convert the header name to lower case */ | ||
600 | for (s = buf ; isalnum(*s) || *s == '-' ; ++s) | ||
601 | *s = tolower(*s); | ||
602 | |||
603 | /* verify we are at the end of the header name */ | ||
604 | if (*s != ':') | ||
605 | error_msg_and_die("bad header line: %s", buf); | ||
606 | |||
607 | /* locate the start of the header value */ | ||
608 | for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s) | ||
609 | ; | ||
610 | hdrval = s; | ||
611 | |||
612 | /* locate the end of header */ | ||
613 | while (*s != '\0' && *s != '\r' && *s != '\n') | ||
614 | ++s; | ||
615 | |||
616 | /* end of header found */ | ||
617 | if (*s != '\0') { | ||
618 | *s = '\0'; | ||
619 | return hdrval; | ||
620 | } | ||
621 | |||
622 | /* Rats! The buffer isn't big enough to hold the entire header value. */ | ||
623 | while (c = getc(fp), c != EOF && c != '\n') | ||
624 | ; | ||
625 | *istrunc = 1; | ||
626 | return hdrval; | ||
627 | } | ||
628 | |||
629 | static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf) | ||
630 | { | ||
631 | char *p; | ||
632 | |||
633 | if (s1) { | ||
634 | if (!s2) s2=""; | ||
635 | fprintf(fp, "%s%s\n", s1, s2); | ||
636 | fflush(fp); | ||
637 | } | ||
638 | |||
639 | do { | ||
640 | p = fgets(buf, 510, fp); | ||
641 | if (!p) | ||
642 | perror_msg_and_die("fgets()"); | ||
643 | } while (! isdigit(buf[0]) || buf[3] != ' '); | ||
644 | |||
645 | return atoi(buf); | ||
646 | } | ||
647 | |||
648 | #ifdef BB_FEATURE_WGET_STATUSBAR | ||
649 | /* Stuff below is from BSD rcp util.c, as added to openshh. | ||
650 | * Original copyright notice is retained at the end of this file. | ||
651 | * | ||
652 | */ | ||
653 | |||
654 | |||
655 | static int | ||
656 | getttywidth(void) | ||
657 | { | ||
658 | struct winsize winsize; | ||
659 | |||
660 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) | ||
661 | return (winsize.ws_col ? winsize.ws_col : 80); | ||
662 | else | ||
663 | return (80); | ||
664 | } | ||
665 | |||
666 | static void | ||
667 | updateprogressmeter(int ignore) | ||
668 | { | ||
669 | int save_errno = errno; | ||
670 | |||
671 | progressmeter(0); | ||
672 | errno = save_errno; | ||
673 | } | ||
674 | |||
675 | static void | ||
676 | alarmtimer(int wait) | ||
677 | { | ||
678 | struct itimerval itv; | ||
679 | |||
680 | itv.it_value.tv_sec = wait; | ||
681 | itv.it_value.tv_usec = 0; | ||
682 | itv.it_interval = itv.it_value; | ||
683 | setitimer(ITIMER_REAL, &itv, NULL); | ||
684 | } | ||
685 | |||
686 | |||
687 | static void | ||
688 | progressmeter(int flag) | ||
689 | { | ||
690 | static const char prefixes[] = " KMGTP"; | ||
691 | static struct timeval lastupdate; | ||
692 | static off_t lastsize, totalsize; | ||
693 | struct timeval now, td, wait; | ||
694 | off_t cursize, abbrevsize; | ||
695 | double elapsed; | ||
696 | int ratio, barlength, i, remaining; | ||
697 | char buf[256]; | ||
698 | |||
699 | if (flag == -1) { | ||
700 | (void) gettimeofday(&start, (struct timezone *) 0); | ||
701 | lastupdate = start; | ||
702 | lastsize = 0; | ||
703 | totalsize = filesize; /* as filesize changes.. */ | ||
704 | } | ||
705 | |||
706 | (void) gettimeofday(&now, (struct timezone *) 0); | ||
707 | cursize = statbytes; | ||
708 | if (totalsize != 0 && !chunked) { | ||
709 | ratio = 100.0 * cursize / totalsize; | ||
710 | ratio = MAX(ratio, 0); | ||
711 | ratio = MIN(ratio, 100); | ||
712 | } else | ||
713 | ratio = 100; | ||
714 | |||
715 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); | ||
716 | |||
717 | barlength = getttywidth() - 51; | ||
718 | if (barlength > 0) { | ||
719 | i = barlength * ratio / 100; | ||
720 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
721 | "|%.*s%*s|", i, | ||
722 | "*****************************************************************************" | ||
723 | "*****************************************************************************", | ||
724 | barlength - i, ""); | ||
725 | } | ||
726 | i = 0; | ||
727 | abbrevsize = cursize; | ||
728 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { | ||
729 | i++; | ||
730 | abbrevsize >>= 10; | ||
731 | } | ||
732 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ", | ||
733 | (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : | ||
734 | 'B'); | ||
735 | |||
736 | timersub(&now, &lastupdate, &wait); | ||
737 | if (cursize > lastsize) { | ||
738 | lastupdate = now; | ||
739 | lastsize = cursize; | ||
740 | if (wait.tv_sec >= STALLTIME) { | ||
741 | start.tv_sec += wait.tv_sec; | ||
742 | start.tv_usec += wait.tv_usec; | ||
743 | } | ||
744 | wait.tv_sec = 0; | ||
745 | } | ||
746 | timersub(&now, &start, &td); | ||
747 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); | ||
748 | |||
749 | if (wait.tv_sec >= STALLTIME) { | ||
750 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
751 | " - stalled -"); | ||
752 | } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) { | ||
753 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
754 | " --:-- ETA"); | ||
755 | } else { | ||
756 | remaining = (int) (totalsize / (statbytes / elapsed) - elapsed); | ||
757 | i = remaining / 3600; | ||
758 | if (i) | ||
759 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
760 | "%2d:", i); | ||
761 | else | ||
762 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
763 | " "); | ||
764 | i = remaining % 3600; | ||
765 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
766 | "%02d:%02d ETA", i / 60, i % 60); | ||
767 | } | ||
768 | write(fileno(stderr), buf, strlen(buf)); | ||
769 | |||
770 | if (flag == -1) { | ||
771 | struct sigaction sa; | ||
772 | sa.sa_handler = updateprogressmeter; | ||
773 | sigemptyset(&sa.sa_mask); | ||
774 | sa.sa_flags = SA_RESTART; | ||
775 | sigaction(SIGALRM, &sa, NULL); | ||
776 | alarmtimer(1); | ||
777 | } else if (flag == 1) { | ||
778 | alarmtimer(0); | ||
779 | statbytes = 0; | ||
780 | putc('\n', stderr); | ||
781 | } | ||
782 | } | ||
783 | #endif | ||
784 | |||
785 | /* Original copyright notice which applies to the BB_FEATURE_WGET_STATUSBAR stuff, | ||
786 | * much of which was blatently stolen from openssh. */ | ||
787 | |||
788 | /*- | ||
789 | * Copyright (c) 1992, 1993 | ||
790 | * The Regents of the University of California. All rights reserved. | ||
791 | * | ||
792 | * Redistribution and use in source and binary forms, with or without | ||
793 | * modification, are permitted provided that the following conditions | ||
794 | * are met: | ||
795 | * 1. Redistributions of source code must retain the above copyright | ||
796 | * notice, this list of conditions and the following disclaimer. | ||
797 | * 2. Redistributions in binary form must reproduce the above copyright | ||
798 | * notice, this list of conditions and the following disclaimer in the | ||
799 | * documentation and/or other materials provided with the distribution. | ||
800 | * | ||
801 | * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change | ||
802 | * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> | ||
803 | * | ||
804 | * 4. Neither the name of the University nor the names of its contributors | ||
805 | * may be used to endorse or promote products derived from this software | ||
806 | * without specific prior written permission. | ||
807 | * | ||
808 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
809 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
810 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
811 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
812 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
813 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
814 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
815 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
816 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
817 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
818 | * SUCH DAMAGE. | ||
819 | * | ||
820 | * $Id: wget.c,v 1.45 2001/07/19 22:28:01 andersen Exp $ | ||
821 | */ | ||
822 | |||
823 | |||
824 | |||
825 | /* | ||
826 | Local Variables: | ||
827 | c-file-style: "linux" | ||
828 | c-basic-offset: 4 | ||
829 | tab-width: 4 | ||
830 | End: | ||
831 | */ | ||
832 | |||
833 | |||
834 | |||