]> sjero.net Git - wget/blob - src/retr.c
[svn] Implemented breadth-first retrieval.
[wget] / src / retr.c
1 /* File retrieval.
2    Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif /* HAVE_UNISTD_H */
28 #include <errno.h>
29 #ifdef HAVE_STRING_H
30 # include <string.h>
31 #else
32 # include <strings.h>
33 #endif /* HAVE_STRING_H */
34 #include <assert.h>
35
36 #include "wget.h"
37 #include "utils.h"
38 #include "retr.h"
39 #include "progress.h"
40 #include "url.h"
41 #include "recur.h"
42 #include "ftp.h"
43 #include "host.h"
44 #include "connect.h"
45 #include "hash.h"
46
47 #ifndef errno
48 extern int errno;
49 #endif
50
51 /* See the comment in gethttp() why this is needed. */
52 int global_download_count;
53
54 \f
55 #define MIN(i, j) ((i) <= (j) ? (i) : (j))
56
57 /* Reads the contents of file descriptor FD, until it is closed, or a
58    read error occurs.  The data is read in 8K chunks, and stored to
59    stream fp, which should have been open for writing.  If BUF is
60    non-NULL and its file descriptor is equal to FD, flush RBUF first.
61    This function will *not* use the rbuf_* functions!
62
63    The EXPECTED argument is passed to show_progress() unchanged, but
64    otherwise ignored.
65
66    If opt.verbose is set, the progress is also shown.  RESTVAL
67    represents a value from which to start downloading (which will be
68    shown accordingly).  If RESTVAL is non-zero, the stream should have
69    been open for appending.
70
71    The function exits and returns codes of 0, -1 and -2 if the
72    connection was closed, there was a read error, or if it could not
73    write to the output stream, respectively.
74
75    IMPORTANT: The function flushes the contents of the buffer in
76    rbuf_flush() before actually reading from fd.  If you wish to read
77    from fd immediately, flush or discard the buffer.  */
78 int
79 get_contents (int fd, FILE *fp, long *len, long restval, long expected,
80               struct rbuf *rbuf, int use_expected)
81 {
82   int res = 0;
83   static char c[8192];
84   void *progress = NULL;
85
86   *len = restval;
87   if (opt.verbose)
88     progress = progress_create (restval, expected);
89
90   if (rbuf && RBUF_FD (rbuf) == fd)
91     {
92       int need_flush = 0;
93       while ((res = rbuf_flush (rbuf, c, sizeof (c))) != 0)
94         {
95           if (fwrite (c, sizeof (char), res, fp) < res)
96             return -2;
97           if (opt.verbose)
98             progress_update (progress, res);
99           *len += res;
100           need_flush = 1;
101         }
102       if (need_flush)
103         fflush (fp);
104       if (ferror (fp))
105         return -2;
106     }
107   /* Read from fd while there is available data.
108
109      Normally, if expected is 0, it means that it is not known how
110      much data is expected.  However, if use_expected is specified,
111      then expected being zero means exactly that.  */
112   while (!use_expected || (*len < expected))
113     {
114       int amount_to_read = (use_expected
115                             ? MIN (expected - *len, sizeof (c))
116                             : sizeof (c));
117 #ifdef HAVE_SSL
118                 if (rbuf->ssl!=NULL) {
119                   res = ssl_iread (rbuf->ssl, c, amount_to_read);
120                 } else {
121 #endif /* HAVE_SSL */
122                   res = iread (fd, c, amount_to_read);
123 #ifdef HAVE_SSL
124                 }
125 #endif /* HAVE_SSL */
126       if (res > 0)
127         {
128           fwrite (c, sizeof (char), res, fp);
129           /* Always flush the contents of the network packet.  This
130              should not be adverse to performance, as the network
131              packets typically won't be too tiny anyway.  */
132           fflush (fp);
133           if (ferror (fp))
134             return -2;
135           if (opt.verbose)
136             progress_update (progress, res);
137           *len += res;
138         }
139       else
140         break;
141     }
142   if (res < -1)
143     res = -1;
144   if (opt.verbose)
145     progress_finish (progress);
146   return res;
147 }
148 \f
149 /* Return a printed representation of the download rate, as
150    appropriate for the speed.  Appropriate means that if rate is
151    greater than 1K/s, kilobytes are used, and if rate is greater than
152    1MB/s, megabytes are used.
153
154    If PAD is non-zero, strings will be padded to the width of 7
155    characters (xxxx.xx).  */
156 char *
157 rate (long bytes, long msecs, int pad)
158 {
159   static char res[15];
160   double dlrate;
161
162   assert (msecs >= 0);
163   assert (bytes >= 0);
164
165   if (msecs == 0)
166     /* If elapsed time is 0, it means we're under the granularity of
167        the timer.  This often happens on systems that use time() for
168        the timer.  */
169     msecs = wtimer_granularity ();
170
171   dlrate = (double)1000 * bytes / msecs;
172   if (dlrate < 1024.0)
173     sprintf (res, pad ? "%7.2f B/s" : "%.2f B/s", dlrate);
174   else if (dlrate < 1024.0 * 1024.0)
175     sprintf (res, pad ? "%7.2f K/s" : "%.2f K/s", dlrate / 1024.0);
176   else if (dlrate < 1024.0 * 1024.0 * 1024.0)
177     sprintf (res, pad ? "%7.2f M/s" : "%.2f M/s", dlrate / (1024.0 * 1024.0));
178   else
179     /* Maybe someone will need this one day.  More realistically, it
180        will get tickled by buggy timers. */
181     sprintf (res, pad ? "%7.2f GB/s" : "%.2f GB/s",
182              dlrate / (1024.0 * 1024.0 * 1024.0));
183
184   return res;
185 }
186 \f
187 static int
188 register_redirections_mapper (void *key, void *value, void *arg)
189 {
190   const char *redirected_from = (const char *)key;
191   const char *redirected_to   = (const char *)arg;
192   if (0 != strcmp (redirected_from, redirected_to))
193     register_redirection (redirected_from, redirected_to);
194   return 0;
195 }
196
197 /* Register the redirections that lead to the successful download of
198    this URL.  This is necessary so that the link converter can convert
199    redirected URLs to the local file.  */
200
201 static void
202 register_all_redirections (struct hash_table *redirections, const char *final)
203 {
204   hash_table_map (redirections, register_redirections_mapper, (void *)final);
205 }
206
207 #define USE_PROXY_P(u) (opt.use_proxy && getproxy((u)->scheme)          \
208                         && no_proxy_match((u)->host,                    \
209                                           (const char **)opt.no_proxy))
210
211 /* Retrieve the given URL.  Decides which loop to call -- HTTP(S), FTP,
212    or simply copy it with file:// (#### the latter not yet
213    implemented!).  */
214 uerr_t
215 retrieve_url (const char *origurl, char **file, char **newloc,
216               const char *refurl, int *dt)
217 {
218   uerr_t result;
219   char *url;
220   int location_changed, dummy;
221   int use_proxy;
222   char *mynewloc, *proxy;
223   struct url *u;
224   int up_error_code;            /* url parse error code */
225   char *local_file;
226   struct hash_table *redirections = NULL;
227
228   /* If dt is NULL, just ignore it.  */
229   if (!dt)
230     dt = &dummy;
231   url = xstrdup (origurl);
232   if (newloc)
233     *newloc = NULL;
234   if (file)
235     *file = NULL;
236
237   u = url_parse (url, &up_error_code);
238   if (!u)
239     {
240       logprintf (LOG_NOTQUIET, "%s: %s.\n", url, url_error (up_error_code));
241       if (redirections)
242         string_set_free (redirections);
243       xfree (url);
244       return URLERROR;
245     }
246
247   if (!refurl)
248     refurl = opt.referer;
249
250  redirected:
251
252   result = NOCONERROR;
253   mynewloc = NULL;
254   local_file = NULL;
255
256   use_proxy = USE_PROXY_P (u);
257   if (use_proxy)
258     {
259       struct url *proxy_url;
260
261       /* Get the proxy server for the current scheme.  */
262       proxy = getproxy (u->scheme);
263       if (!proxy)
264         {
265           logputs (LOG_NOTQUIET, _("Could not find proxy host.\n"));
266           url_free (u);
267           if (redirections)
268             string_set_free (redirections);
269           xfree (url);
270           return PROXERR;
271         }
272
273       /* Parse the proxy URL.  */
274       proxy_url = url_parse (proxy, &up_error_code);
275       if (!proxy_url)
276         {
277           logprintf (LOG_NOTQUIET, _("Error parsing proxy URL %s: %s.\n"),
278                      proxy, url_error (up_error_code));
279           if (redirections)
280             string_set_free (redirections);
281           xfree (url);
282           return PROXERR;
283         }
284       if (proxy_url->scheme != SCHEME_HTTP)
285         {
286           logprintf (LOG_NOTQUIET, _("Error in proxy URL %s: Must be HTTP.\n"), proxy);
287           url_free (proxy_url);
288           if (redirections)
289             string_set_free (redirections);
290           xfree (url);
291           return PROXERR;
292         }
293
294       result = http_loop (u, &mynewloc, &local_file, refurl, dt, proxy_url);
295       url_free (proxy_url);
296     }
297   else if (u->scheme == SCHEME_HTTP
298 #ifdef HAVE_SSL
299       || u->scheme == SCHEME_HTTPS
300 #endif
301       )
302     {
303       result = http_loop (u, &mynewloc, &local_file, refurl, dt, NULL);
304     }
305   else if (u->scheme == SCHEME_FTP)
306     {
307       /* If this is a redirection, we must not allow recursive FTP
308          retrieval, so we save recursion to oldrec, and restore it
309          later.  */
310       int oldrec = opt.recursive;
311       if (redirections)
312         opt.recursive = 0;
313       result = ftp_loop (u, dt);
314       opt.recursive = oldrec;
315 #if 0
316       /* There is a possibility of having HTTP being redirected to
317          FTP.  In these cases we must decide whether the text is HTML
318          according to the suffix.  The HTML suffixes are `.html' and
319          `.htm', case-insensitive.  */
320       if (redirections && u->local && (u->scheme == SCHEME_FTP))
321         {
322           char *suf = suffix (u->local);
323           if (suf && (!strcasecmp (suf, "html") || !strcasecmp (suf, "htm")))
324             *dt |= TEXTHTML;
325           FREE_MAYBE (suf);
326         }
327 #endif
328     }
329   location_changed = (result == NEWLOCATION);
330   if (location_changed)
331     {
332       char *construced_newloc;
333       struct url *newloc_parsed;
334
335       assert (mynewloc != NULL);
336
337       if (local_file)
338         xfree (local_file);
339
340       /* The HTTP specs only allow absolute URLs to appear in
341          redirects, but a ton of boneheaded webservers and CGIs out
342          there break the rules and use relative URLs, and popular
343          browsers are lenient about this, so wget should be too. */
344       construced_newloc = uri_merge (url, mynewloc);
345       xfree (mynewloc);
346       mynewloc = construced_newloc;
347
348       /* Now, see if this new location makes sense. */
349       newloc_parsed = url_parse (mynewloc, &up_error_code);
350       if (!newloc_parsed)
351         {
352           logprintf (LOG_NOTQUIET, "%s: %s.\n", mynewloc,
353                      url_error (up_error_code));
354           url_free (u);
355           if (redirections)
356             string_set_free (redirections);
357           xfree (url);
358           xfree (mynewloc);
359           return result;
360         }
361
362       /* Now mynewloc will become newloc_parsed->url, because if the
363          Location contained relative paths like .././something, we
364          don't want that propagating as url.  */
365       xfree (mynewloc);
366       mynewloc = xstrdup (newloc_parsed->url);
367
368       if (!redirections)
369         {
370           redirections = make_string_hash_table (0);
371           /* Add current URL immediately so we can detect it as soon
372              as possible in case of a cycle. */
373           string_set_add (redirections, u->url);
374         }
375
376       /* The new location is OK.  Check for redirection cycle by
377          peeking through the history of redirections. */
378       if (string_set_contains (redirections, newloc_parsed->url))
379         {
380           logprintf (LOG_NOTQUIET, _("%s: Redirection cycle detected.\n"),
381                      mynewloc);
382           url_free (newloc_parsed);
383           url_free (u);
384           if (redirections)
385             string_set_free (redirections);
386           xfree (url);
387           xfree (mynewloc);
388           return WRONGCODE;
389         }
390       string_set_add (redirections, newloc_parsed->url);
391
392       xfree (url);
393       url = mynewloc;
394       url_free (u);
395       u = newloc_parsed;
396       goto redirected;
397     }
398
399   if (local_file)
400     {
401       if (*dt & RETROKF)
402         {
403           register_download (url, local_file);
404           if (redirections)
405             register_all_redirections (redirections, url);
406           if (*dt & TEXTHTML)
407             register_html (url, local_file);
408         }
409     }
410
411   if (file)
412     *file = local_file ? local_file : NULL;
413   else
414     FREE_MAYBE (local_file);
415
416   url_free (u);
417   if (redirections)
418     string_set_free (redirections);
419
420   if (newloc)
421     *newloc = url;
422   else
423     xfree (url);
424
425   ++global_download_count;
426
427   return result;
428 }
429
430 /* Find the URLs in the file and call retrieve_url() for each of
431    them.  If HTML is non-zero, treat the file as HTML, and construct
432    the URLs accordingly.
433
434    If opt.recursive is set, call recursive_retrieve() for each file.  */
435 uerr_t
436 retrieve_from_file (const char *file, int html, int *count)
437 {
438   uerr_t status;
439   struct urlpos *url_list, *cur_url;
440
441   url_list = (html ? get_urls_html (file, NULL, FALSE, NULL)
442               : get_urls_file (file));
443   status = RETROK;             /* Suppose everything is OK.  */
444   *count = 0;                  /* Reset the URL count.  */
445
446   for (cur_url = url_list; cur_url; cur_url = cur_url->next, ++*count)
447     {
448       char *filename = NULL, *new_file;
449       int dt;
450
451       if (downloaded_exceeds_quota ())
452         {
453           status = QUOTEXC;
454           break;
455         }
456       if (opt.recursive && cur_url->url->scheme != SCHEME_FTP)
457         status = retrieve_tree (cur_url->url->url);
458       else
459         status = retrieve_url (cur_url->url->url, &filename, &new_file, NULL, &dt);
460
461       if (filename && opt.delete_after && file_exists_p (filename))
462         {
463           DEBUGP (("Removing file due to --delete-after in"
464                    " retrieve_from_file():\n"));
465           logprintf (LOG_VERBOSE, _("Removing %s.\n"), filename);
466           if (unlink (filename))
467             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
468           dt &= ~RETROKF;
469         }
470
471       FREE_MAYBE (new_file);
472       FREE_MAYBE (filename);
473     }
474
475   /* Free the linked list of URL-s.  */
476   free_urlpos (url_list);
477
478   return status;
479 }
480
481 /* Print `giving up', or `retrying', depending on the impending
482    action.  N1 and N2 are the attempt number and the attempt limit.  */
483 void
484 printwhat (int n1, int n2)
485 {
486   logputs (LOG_VERBOSE, (n1 == n2) ? _("Giving up.\n\n") : _("Retrying.\n\n"));
487 }
488
489 /* Increment opt.downloaded by BY_HOW_MUCH.  If an overflow occurs,
490    set opt.downloaded_overflow to 1. */
491 void
492 downloaded_increase (unsigned long by_how_much)
493 {
494   VERY_LONG_TYPE old;
495   if (opt.downloaded_overflow)
496     return;
497   old = opt.downloaded;
498   opt.downloaded += by_how_much;
499   if (opt.downloaded < old)     /* carry flag, where are you when I
500                                    need you? */
501     {
502       /* Overflow. */
503       opt.downloaded_overflow = 1;
504       opt.downloaded = ~((VERY_LONG_TYPE)0);
505     }
506 }
507
508 /* Return non-zero if the downloaded amount of bytes exceeds the
509    desired quota.  If quota is not set or if the amount overflowed, 0
510    is returned. */
511 int
512 downloaded_exceeds_quota (void)
513 {
514   if (!opt.quota)
515     return 0;
516   if (opt.downloaded_overflow)
517     /* We don't really know.  (Wildly) assume not. */
518     return 0;
519
520   return opt.downloaded > opt.quota;
521 }
522
523 /* If opt.wait or opt.waitretry are specified, and if certain
524    conditions are met, sleep the appropriate number of seconds.  See
525    the documentation of --wait and --waitretry for more information.
526
527    COUNT is the count of current retrieval, beginning with 1. */
528
529 void
530 sleep_between_retrievals (int count)
531 {
532   static int first_retrieval = 1;
533
534   if (!first_retrieval && (opt.wait || opt.waitretry))
535     {
536       if (opt.waitretry && count > 1)
537         {
538           /* If opt.waitretry is specified and this is a retry, wait
539              for COUNT-1 number of seconds, or for opt.waitretry
540              seconds.  */
541           if (count <= opt.waitretry)
542             sleep (count - 1);
543           else
544             sleep (opt.waitretry);
545         }
546       else if (opt.wait)
547         /* Otherwise, check if opt.wait is specified.  If so, sleep.  */
548         sleep (opt.wait);
549     }
550   if (first_retrieval)
551     first_retrieval = 0;
552 }