]> sjero.net Git - wget/blob - src/url.c
eac1cfdd193a44556777910221501e10436ba521
[wget] / src / url.c
1 /* URL handling.
2    Copyright (C) 1995, 1996, 1997, 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 (at
9 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 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #else
37 # include <strings.h>
38 #endif
39 #include <sys/types.h>
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <errno.h>
44 #include <assert.h>
45
46 #include "wget.h"
47 #include "utils.h"
48 #include "url.h"
49 #include "host.h"
50 #include "hash.h"
51
52 #ifndef errno
53 extern int errno;
54 #endif
55
56 /* Is X "."?  */
57 #define DOTP(x) ((*(x) == '.') && (!*(x + 1)))
58 /* Is X ".."?  */
59 #define DDOTP(x) ((*(x) == '.') && (*(x + 1) == '.') && (!*(x + 2)))
60
61 static const int NS_INADDRSZ  = 4;
62 static const int NS_IN6ADDRSZ = 16;
63 static const int NS_INT16SZ = 2;
64
65
66 struct scheme_data
67 {
68   char *leading_string;
69   int default_port;
70   int enabled;
71 };
72
73 /* Supported schemes: */
74 static struct scheme_data supported_schemes[] =
75 {
76   { "http://",  DEFAULT_HTTP_PORT,  1 },
77 #ifdef HAVE_SSL
78   { "https://", DEFAULT_HTTPS_PORT, 1 },
79 #endif
80   { "ftp://",   DEFAULT_FTP_PORT,   1 },
81
82   /* SCHEME_INVALID */
83   { NULL,       -1,                 0 }
84 };
85
86 /* Forward declarations: */
87
88 static char *construct_relative PARAMS ((const char *, const char *));
89 static int path_simplify PARAMS ((char *));
90
91
92 \f
93 /* Support for encoding and decoding of URL strings.  We determine
94    whether a character is unsafe through static table lookup.  This
95    code assumes ASCII character set and 8-bit chars.  */
96
97 enum {
98   urlchr_reserved = 1,
99   urlchr_unsafe   = 2
100 };
101
102 #define R  urlchr_reserved
103 #define U  urlchr_unsafe
104 #define RU R|U
105
106 #define urlchr_test(c, mask) (urlchr_table[(unsigned char)(c)] & (mask))
107
108 /* rfc1738 reserved chars, preserved from encoding.  */
109
110 #define RESERVED_CHAR(c) urlchr_test(c, urlchr_reserved)
111
112 /* rfc1738 unsafe chars, plus some more.  */
113
114 #define UNSAFE_CHAR(c) urlchr_test(c, urlchr_unsafe)
115
116 const static unsigned char urlchr_table[256] =
117 {
118   U,  U,  U,  U,   U,  U,  U,  U,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
119   U,  U,  U,  U,   U,  U,  U,  U,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
120   U,  U,  U,  U,   U,  U,  U,  U,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
121   U,  U,  U,  U,   U,  U,  U,  U,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
122   U,  0,  U, RU,   0,  U,  R,  0,   /* SP  !   "   #    $   %   &   '   */
123   0,  0,  0,  R,   0,  0,  0,  R,   /* (   )   *   +    ,   -   .   /   */
124   0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
125   0,  0, RU,  R,   U,  R,  U,  R,   /* 8   9   :   ;    <   =   >   ?   */
126  RU,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
127   0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
128   0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
129   0,  0,  0, RU,   U, RU,  U,  0,   /* X   Y   Z   [    \   ]   ^   _   */
130   U,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
131   0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
132   0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
133   0,  0,  0,  U,   U,  U,  U,  U,   /* x   y   z   {    |   }   ~   DEL */
134
135   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
136   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
137   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
138   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
139
140   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
141   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
142   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
143   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
144 };
145
146 /* Decodes the forms %xy in a URL to the character the hexadecimal
147    code of which is xy.  xy are hexadecimal digits from
148    [0123456789ABCDEF] (case-insensitive).  If x or y are not
149    hex-digits or `%' precedes `\0', the sequence is inserted
150    literally.  */
151
152 static void
153 decode_string (char *s)
154 {
155   char *t = s;                  /* t - tortoise */
156   char *h = s;                  /* h - hare     */
157
158   for (; *h; h++, t++)
159     {
160       if (*h != '%')
161         {
162         copychar:
163           *t = *h;
164         }
165       else
166         {
167           /* Do nothing if '%' is not followed by two hex digits. */
168           if (!*(h + 1) || !*(h + 2)
169               || !(ISXDIGIT (*(h + 1)) && ISXDIGIT (*(h + 2))))
170             goto copychar;
171           *t = (XCHAR_TO_XDIGIT (*(h + 1)) << 4) + XCHAR_TO_XDIGIT (*(h + 2));
172           h += 2;
173         }
174     }
175   *t = '\0';
176 }
177
178 /* Like encode_string, but return S if there are no unsafe chars.  */
179
180 static char *
181 encode_string_maybe (const char *s)
182 {
183   const char *p1;
184   char *p2, *newstr;
185   int newlen;
186   int addition = 0;
187
188   for (p1 = s; *p1; p1++)
189     if (UNSAFE_CHAR (*p1))
190       addition += 2;            /* Two more characters (hex digits) */
191
192   if (!addition)
193     return (char *)s;
194
195   newlen = (p1 - s) + addition;
196   newstr = (char *)xmalloc (newlen + 1);
197
198   p1 = s;
199   p2 = newstr;
200   while (*p1)
201     {
202       if (UNSAFE_CHAR (*p1))
203         {
204           unsigned char c = *p1++;
205           *p2++ = '%';
206           *p2++ = XDIGIT_TO_XCHAR (c >> 4);
207           *p2++ = XDIGIT_TO_XCHAR (c & 0xf);
208         }
209       else
210         *p2++ = *p1++;
211     }
212   *p2 = '\0';
213   assert (p2 - newstr == newlen);
214
215   return newstr;
216 }
217
218 /* Encode the unsafe characters (as determined by UNSAFE_CHAR) in a
219    given string, returning a malloc-ed %XX encoded string.  */
220   
221 char *
222 encode_string (const char *s)
223 {
224   char *encoded = encode_string_maybe (s);
225   if (encoded != s)
226     return encoded;
227   else
228     return xstrdup (s);
229 }
230
231 /* Encode unsafe characters in PTR to %xx.  If such encoding is done,
232    the old value of PTR is freed and PTR is made to point to the newly
233    allocated storage.  */
234
235 #define ENCODE(ptr) do {                        \
236   char *e_new = encode_string_maybe (ptr);      \
237   if (e_new != ptr)                             \
238     {                                           \
239       xfree (ptr);                              \
240       ptr = e_new;                              \
241     }                                           \
242 } while (0)
243 \f
244 enum copy_method { CM_DECODE, CM_ENCODE, CM_PASSTHROUGH };
245
246 /* Decide whether to encode, decode, or pass through the char at P.
247    This used to be a macro, but it got a little too convoluted.  */
248 static inline enum copy_method
249 decide_copy_method (const char *p)
250 {
251   if (*p == '%')
252     {
253       if (ISXDIGIT (*(p + 1)) && ISXDIGIT (*(p + 2)))
254         {
255           /* %xx sequence: decode it, unless it would decode to an
256              unsafe or a reserved char; in that case, leave it as
257              is. */
258           char preempt = (XCHAR_TO_XDIGIT (*(p + 1)) << 4) +
259             XCHAR_TO_XDIGIT (*(p + 2));
260
261           if (UNSAFE_CHAR (preempt) || RESERVED_CHAR (preempt))
262             return CM_PASSTHROUGH;
263           else
264             return CM_DECODE;
265         }
266       else
267         /* Garbled %.. sequence: encode `%'. */
268         return CM_ENCODE;
269     }
270   else if (UNSAFE_CHAR (*p) && !RESERVED_CHAR (*p))
271     return CM_ENCODE;
272   else
273     return CM_PASSTHROUGH;
274 }
275
276 /* Translate a %-quoting (but possibly non-conformant) input string S
277    into a %-quoting (and conformant) output string.  If no characters
278    are encoded or decoded, return the same string S; otherwise, return
279    a freshly allocated string with the new contents.
280
281    After a URL has been run through this function, the protocols that
282    use `%' as the quote character can use the resulting string as-is,
283    while those that don't call decode_string() to get to the intended
284    data.  This function is also stable: after an input string is
285    transformed the first time, all further transformations of the
286    result yield the same result string.
287
288    Let's discuss why this function is needed.
289
290    Imagine Wget is to retrieve `http://abc.xyz/abc def'.  Since a raw
291    space character would mess up the HTTP request, it needs to be
292    quoted, like this:
293
294        GET /abc%20def HTTP/1.0
295
296    So it appears that the unsafe chars need to be quoted, as with
297    encode_string.  But what if we're requested to download
298    `abc%20def'?  Remember that %-encoding is valid URL syntax, so what
299    the user meant was a literal space, and he was kind enough to quote
300    it.  In that case, Wget should obviously leave the `%20' as is, and
301    send the same request as above.  So in this case we may not call
302    encode_string.
303
304    But what if the requested URI is `abc%20 def'?  If we call
305    encode_string, we end up with `/abc%2520%20def', which is almost
306    certainly not intended.  If we don't call encode_string, we are
307    left with the embedded space and cannot send the request.  What the
308    user meant was for Wget to request `/abc%20%20def', and this is
309    where reencode_string kicks in.
310
311    Wget used to solve this by first decoding %-quotes, and then
312    encoding all the "unsafe" characters found in the resulting string.
313    This was wrong because it didn't preserve certain URL special
314    (reserved) characters.  For instance, URI containing "a%2B+b" (0x2b
315    == '+') would get translated to "a%2B%2Bb" or "a++b" depending on
316    whether we considered `+' reserved (it is).  One of these results
317    is inevitable because by the second step we would lose information
318    on whether the `+' was originally encoded or not.  Both results
319    were wrong because in CGI parameters + means space, while %2B means
320    literal plus.  reencode_string correctly translates the above to
321    "a%2B+b", i.e. returns the original string.
322
323    This function uses an algorithm proposed by Anon Sricharoenchai:
324
325    1. Encode all URL_UNSAFE and the "%" that are not followed by 2
326       hexdigits.
327
328    2. Decode all "%XX" except URL_UNSAFE, URL_RESERVED (";/?:@=&") and
329       "+".
330
331    ...except that this code conflates the two steps, and decides
332    whether to encode, decode, or pass through each character in turn.
333    The function still uses two passes, but their logic is the same --
334    the first pass exists merely for the sake of allocation.  Another
335    small difference is that we include `+' to URL_RESERVED.
336
337    Anon's test case:
338
339    "http://abc.xyz/%20%3F%%36%31%25aa% a?a=%61+a%2Ba&b=b%26c%3Dc"
340    ->
341    "http://abc.xyz/%20%3F%2561%25aa%25%20a?a=a+a%2Ba&b=b%26c%3Dc"
342
343    Simpler test cases:
344
345    "foo bar"         -> "foo%20bar"
346    "foo%20bar"       -> "foo%20bar"
347    "foo %20bar"      -> "foo%20%20bar"
348    "foo%%20bar"      -> "foo%25%20bar"       (0x25 == '%')
349    "foo%25%20bar"    -> "foo%25%20bar"
350    "foo%2%20bar"     -> "foo%252%20bar"
351    "foo+bar"         -> "foo+bar"            (plus is reserved!)
352    "foo%2b+bar"      -> "foo%2b+bar"  */
353
354 static char *
355 reencode_string (const char *s)
356 {
357   const char *p1;
358   char *newstr, *p2;
359   int oldlen, newlen;
360
361   int encode_count = 0;
362   int decode_count = 0;
363
364   /* First, pass through the string to see if there's anything to do,
365      and to calculate the new length.  */
366   for (p1 = s; *p1; p1++)
367     {
368       switch (decide_copy_method (p1))
369         {
370         case CM_ENCODE:
371           ++encode_count;
372           break;
373         case CM_DECODE:
374           ++decode_count;
375           break;
376         case CM_PASSTHROUGH:
377           break;
378         }
379     }
380
381   if (!encode_count && !decode_count)
382     /* The string is good as it is. */
383     return (char *)s;           /* C const model sucks. */
384
385   oldlen = p1 - s;
386   /* Each encoding adds two characters (hex digits), while each
387      decoding removes two characters.  */
388   newlen = oldlen + 2 * (encode_count - decode_count);
389   newstr = xmalloc (newlen + 1);
390
391   p1 = s;
392   p2 = newstr;
393
394   while (*p1)
395     {
396       switch (decide_copy_method (p1))
397         {
398         case CM_ENCODE:
399           {
400             unsigned char c = *p1++;
401             *p2++ = '%';
402             *p2++ = XDIGIT_TO_XCHAR (c >> 4);
403             *p2++ = XDIGIT_TO_XCHAR (c & 0xf);
404           }
405           break;
406         case CM_DECODE:
407           *p2++ = ((XCHAR_TO_XDIGIT (*(p1 + 1)) << 4)
408                    + (XCHAR_TO_XDIGIT (*(p1 + 2))));
409           p1 += 3;              /* skip %xx */
410           break;
411         case CM_PASSTHROUGH:
412           *p2++ = *p1++;
413         }
414     }
415   *p2 = '\0';
416   assert (p2 - newstr == newlen);
417   return newstr;
418 }
419
420 /* Run PTR_VAR through reencode_string.  If a new string is consed,
421    free PTR_VAR and make it point to the new storage.  Obviously,
422    PTR_VAR needs to be an lvalue.  */
423
424 #define REENCODE(ptr_var) do {                  \
425   char *rf_new = reencode_string (ptr_var);     \
426   if (rf_new != ptr_var)                        \
427     {                                           \
428       xfree (ptr_var);                          \
429       ptr_var = rf_new;                         \
430     }                                           \
431 } while (0)
432 \f
433 /* Returns the scheme type if the scheme is supported, or
434    SCHEME_INVALID if not.  */
435 enum url_scheme
436 url_scheme (const char *url)
437 {
438   int i;
439
440   for (i = 0; supported_schemes[i].leading_string; i++)
441     if (0 == strncasecmp (url, supported_schemes[i].leading_string,
442                           strlen (supported_schemes[i].leading_string)))
443       {
444         if (supported_schemes[i].enabled)
445           return (enum url_scheme) i;
446         else
447           return SCHEME_INVALID;
448       }
449
450   return SCHEME_INVALID;
451 }
452
453 /* Return the number of characters needed to skip the scheme part of
454    the URL, e.g. `http://'.  If no scheme is found, returns 0.  */
455 int
456 url_skip_scheme (const char *url)
457 {
458   const char *p = url;
459
460   /* Skip the scheme name.  We allow `-' and `+' because of `whois++',
461      etc. */
462   while (ISALNUM (*p) || *p == '-' || *p == '+')
463     ++p;
464   if (*p != ':')
465     return 0;
466   /* Skip ':'. */
467   ++p;
468
469   /* Skip "//" if found. */
470   if (*p == '/' && *(p + 1) == '/')
471     p += 2;
472
473   return p - url;
474 }
475
476 /* Returns 1 if the URL begins with a scheme (supported or
477    unsupported), 0 otherwise.  */
478 int
479 url_has_scheme (const char *url)
480 {
481   const char *p = url;
482   while (ISALNUM (*p) || *p == '-' || *p == '+')
483     ++p;
484   return *p == ':';
485 }
486
487 int
488 scheme_default_port (enum url_scheme scheme)
489 {
490   return supported_schemes[scheme].default_port;
491 }
492
493 void
494 scheme_disable (enum url_scheme scheme)
495 {
496   supported_schemes[scheme].enabled = 0;
497 }
498
499 /* Skip the username and password, if present here.  The function
500    should be called *not* with the complete URL, but with the part
501    right after the scheme.
502
503    If no username and password are found, return 0.  */
504 int
505 url_skip_uname (const char *url)
506 {
507   const char *p;
508
509   /* Look for '@' that comes before '/' or '?'. */
510   p = (const char *)strpbrk (url, "/?@");
511   if (!p || *p != '@')
512     return 0;
513
514   return p - url + 1;
515 }
516
517 static int
518 parse_uname (const char *str, int len, char **user, char **passwd)
519 {
520   char *colon;
521
522   if (len == 0)
523     /* Empty user name not allowed. */
524     return 0;
525
526   colon = memchr (str, ':', len);
527   if (colon == str)
528     /* Empty user name again. */
529     return 0;
530
531   if (colon)
532     {
533       int pwlen = len - (colon + 1 - str);
534       *passwd = xmalloc (pwlen + 1);
535       memcpy (*passwd, colon + 1, pwlen);
536       (*passwd)[pwlen] = '\0';
537       len -= pwlen + 1;
538     }
539   else
540     *passwd = NULL;
541
542   *user = xmalloc (len + 1);
543   memcpy (*user, str, len);
544   (*user)[len] = '\0';
545
546   if (*user)
547     decode_string (*user);
548   if (*passwd)
549     decode_string (*passwd);
550
551   return 1;
552 }
553
554 /* Used by main.c: detect URLs written using the "shorthand" URL forms
555    popularized by Netscape and NcFTP.  HTTP shorthands look like this:
556
557    www.foo.com[:port]/dir/file   -> http://www.foo.com[:port]/dir/file
558    www.foo.com[:port]            -> http://www.foo.com[:port]
559
560    FTP shorthands look like this:
561
562    foo.bar.com:dir/file          -> ftp://foo.bar.com/dir/file
563    foo.bar.com:/absdir/file      -> ftp://foo.bar.com//absdir/file
564
565    If the URL needs not or cannot be rewritten, return NULL.  */
566 char *
567 rewrite_shorthand_url (const char *url)
568 {
569   const char *p;
570
571   if (url_has_scheme (url))
572     return NULL;
573
574   /* Look for a ':' or '/'.  The former signifies NcFTP syntax, the
575      latter Netscape.  */
576   for (p = url; *p && *p != ':' && *p != '/'; p++)
577     ;
578
579   if (p == url)
580     return NULL;
581
582   if (*p == ':')
583     {
584       const char *pp;
585       char *res;
586       /* If the characters after the colon and before the next slash
587          or end of string are all digits, it's HTTP.  */
588       int digits = 0;
589       for (pp = p + 1; ISDIGIT (*pp); pp++)
590         ++digits;
591       if (digits > 0 && (*pp == '/' || *pp == '\0'))
592         goto http;
593
594       /* Prepend "ftp://" to the entire URL... */
595       res = xmalloc (6 + strlen (url) + 1);
596       sprintf (res, "ftp://%s", url);
597       /* ...and replace ':' with '/'. */
598       res[6 + (p - url)] = '/';
599       return res;
600     }
601   else
602     {
603       char *res;
604     http:
605       /* Just prepend "http://" to what we have. */
606       res = xmalloc (7 + strlen (url) + 1);
607       sprintf (res, "http://%s", url);
608       return res;
609     }
610 }
611 \f
612 static void parse_path PARAMS ((const char *, char **, char **));
613
614 static char *
615 strpbrk_or_eos (const char *s, const char *accept)
616 {
617   char *p = strpbrk (s, accept);
618   if (!p)
619     p = (char *)s + strlen (s);
620   return p;
621 }
622
623 /* Turn STR into lowercase; return non-zero if a character was
624    actually changed. */
625
626 static int
627 lowercase_str (char *str)
628 {
629   int change = 0;
630   for (; *str; str++)
631     if (ISUPPER (*str))
632       {
633         change = 1;
634         *str = TOLOWER (*str);
635       }
636   return change;
637 }
638
639 static char *parse_errors[] = {
640 #define PE_NO_ERROR                     0
641   "No error",
642 #define PE_UNSUPPORTED_SCHEME           1
643   "Unsupported scheme",
644 #define PE_EMPTY_HOST                   2
645   "Empty host",
646 #define PE_BAD_PORT_NUMBER              3
647   "Bad port number",
648 #define PE_INVALID_USER_NAME            4
649   "Invalid user name",
650 #define PE_UNTERMINATED_IPV6_ADDRESS    5
651   "Unterminated IPv6 numeric address",
652 #define PE_IPV6_NOT_SUPPORTED           6
653   "IPv6 addresses not supported",
654 #define PE_INVALID_IPV6_ADDRESS         7
655   "Invalid IPv6 numeric address"
656 };
657
658 #define SETERR(p, v) do {                       \
659   if (p)                                        \
660     *(p) = (v);                                 \
661 } while (0)
662
663 #ifdef ENABLE_IPV6
664 /* The following two functions were adapted from glibc. */
665
666 static int
667 is_valid_ipv4_address (const char *str, const char *end)
668 {
669   int saw_digit, octets;
670   int val;
671
672   saw_digit = 0;
673   octets = 0;
674   val = 0;
675
676   while (str < end) {
677     int ch = *str++;
678
679     if (ch >= '0' && ch <= '9') {
680       val = val * 10 + (ch - '0');
681
682       if (val > 255)
683         return 0;
684       if (saw_digit == 0) {
685         if (++octets > 4)
686           return 0;
687         saw_digit = 1;
688       }
689     } else if (ch == '.' && saw_digit == 1) {
690       if (octets == 4)
691         return 0;
692       val = 0;
693       saw_digit = 0;
694     } else
695       return 0;
696   }
697   if (octets < 4)
698     return 0;
699   
700   return 1;
701 }
702
703 static int
704 is_valid_ipv6_address (const char *str, const char *end)
705 {
706   static const char xdigits[] = "0123456789abcdef";
707   const char *curtok;
708   int tp;
709   const char *colonp;
710   int saw_xdigit;
711   unsigned int val;
712
713   tp = 0;
714   colonp = NULL;
715
716   if (str == end)
717     return 0;
718   
719   /* Leading :: requires some special handling. */
720   if (*str == ':')
721     {
722       ++str;
723       if (str == end || *str != ':')
724         return 0;
725     }
726
727   curtok = str;
728   saw_xdigit = 0;
729   val = 0;
730
731   while (str < end) {
732     int ch = *str++;
733     const char *pch;
734
735     /* if ch is a number, add it to val. */
736     pch = strchr(xdigits, ch);
737     if (pch != NULL) {
738       val <<= 4;
739       val |= (pch - xdigits);
740       if (val > 0xffff)
741         return 0;
742       saw_xdigit = 1;
743       continue;
744     }
745
746     /* if ch is a colon ... */
747     if (ch == ':') {
748       curtok = str;
749       if (saw_xdigit == 0) {
750         if (colonp != NULL)
751           return 0;
752         colonp = str + tp;
753         continue;
754       } else if (str == end) {
755         return 0;
756       }
757       if (tp > NS_IN6ADDRSZ - NS_INT16SZ)
758         return 0;
759       tp += NS_INT16SZ;
760       saw_xdigit = 0;
761       val = 0;
762       continue;
763     }
764
765     /* if ch is a dot ... */
766     if (ch == '.' && (tp <= NS_IN6ADDRSZ - NS_INADDRSZ) &&
767         is_valid_ipv4_address(curtok, end) == 1) {
768       tp += NS_INADDRSZ;
769       saw_xdigit = 0;
770       break;
771     }
772     
773     return 0;
774   }
775
776   if (saw_xdigit == 1) {
777     if (tp > NS_IN6ADDRSZ - NS_INT16SZ) 
778       return 0;
779     tp += NS_INT16SZ;
780   }
781
782   if (colonp != NULL) {
783     if (tp == NS_IN6ADDRSZ) 
784       return 0;
785     tp = NS_IN6ADDRSZ;
786   }
787
788   if (tp != NS_IN6ADDRSZ)
789     return 0;
790
791   return 1;
792 }
793 #endif
794
795 /* Parse a URL.
796
797    Return a new struct url if successful, NULL on error.  In case of
798    error, and if ERROR is not NULL, also set *ERROR to the appropriate
799    error code. */
800 struct url *
801 url_parse (const char *url, int *error)
802 {
803   struct url *u;
804   const char *p;
805   int path_modified, host_modified;
806
807   enum url_scheme scheme;
808
809   const char *uname_b,     *uname_e;
810   const char *host_b,      *host_e;
811   const char *path_b,      *path_e;
812   const char *params_b,    *params_e;
813   const char *query_b,     *query_e;
814   const char *fragment_b,  *fragment_e;
815
816   int port;
817   char *user = NULL, *passwd = NULL;
818
819   char *url_encoded;
820
821   scheme = url_scheme (url);
822   if (scheme == SCHEME_INVALID)
823     {
824       SETERR (error, PE_UNSUPPORTED_SCHEME);
825       return NULL;
826     }
827
828   url_encoded = reencode_string (url);
829   p = url_encoded;
830
831   p += strlen (supported_schemes[scheme].leading_string);
832   uname_b = p;
833   p += url_skip_uname (p);
834   uname_e = p;
835
836   /* scheme://user:pass@host[:port]... */
837   /*                    ^              */
838
839   /* We attempt to break down the URL into the components path,
840      params, query, and fragment.  They are ordered like this:
841
842        scheme://host[:port][/path][;params][?query][#fragment]  */
843
844   params_b   = params_e   = NULL;
845   query_b    = query_e    = NULL;
846   fragment_b = fragment_e = NULL;
847
848   host_b = p;
849
850   if (*p == '[')
851     {
852       /* Handle IPv6 address inside square brackets.  Ideally we'd
853          just look for the terminating ']', but rfc2732 mandates
854          rejecting invalid IPv6 addresses.  */
855
856       /* The address begins after '['. */
857       host_b = p + 1;
858       host_e = strchr (host_b, ']');
859
860       if (!host_e)
861         {
862           SETERR (error, PE_UNTERMINATED_IPV6_ADDRESS);
863           return NULL;
864         }
865
866 #ifdef ENABLE_IPV6
867       /* Check if the IPv6 address is valid. */
868       if (!is_valid_ipv6_address(host_b, host_e))
869         {
870           SETERR (error, PE_INVALID_IPV6_ADDRESS);
871           return NULL;
872         }
873
874       /* Continue parsing after the closing ']'. */
875       p = host_e + 1;
876 #else
877       SETERR (error, PE_IPV6_NOT_SUPPORTED);
878       return NULL;
879 #endif
880     }
881   else
882     {
883       p = strpbrk_or_eos (p, ":/;?#");
884       host_e = p;
885     }
886
887   if (host_b == host_e)
888     {
889       SETERR (error, PE_EMPTY_HOST);
890       return NULL;
891     }
892
893   port = scheme_default_port (scheme);
894   if (*p == ':')
895     {
896       const char *port_b, *port_e, *pp;
897
898       /* scheme://host:port/tralala */
899       /*              ^             */
900       ++p;
901       port_b = p;
902       p = strpbrk_or_eos (p, "/;?#");
903       port_e = p;
904
905       if (port_b == port_e)
906         {
907           /* http://host:/whatever */
908           /*             ^         */
909           SETERR (error, PE_BAD_PORT_NUMBER);
910           return NULL;
911         }
912
913       for (port = 0, pp = port_b; pp < port_e; pp++)
914         {
915           if (!ISDIGIT (*pp))
916             {
917               /* http://host:12randomgarbage/blah */
918               /*               ^                  */
919               SETERR (error, PE_BAD_PORT_NUMBER);
920               return NULL;
921             }
922           
923           port = 10 * port + (*pp - '0');
924         }
925     }
926
927   if (*p == '/')
928     {
929       ++p;
930       path_b = p;
931       p = strpbrk_or_eos (p, ";?#");
932       path_e = p;
933     }
934   else
935     {
936       /* Path is not allowed not to exist. */
937       path_b = path_e = p;
938     }
939
940   if (*p == ';')
941     {
942       ++p;
943       params_b = p;
944       p = strpbrk_or_eos (p, "?#");
945       params_e = p;
946     }
947   if (*p == '?')
948     {
949       ++p;
950       query_b = p;
951       p = strpbrk_or_eos (p, "#");
952       query_e = p;
953
954       /* Hack that allows users to use '?' (a wildcard character) in
955          FTP URLs without it being interpreted as a query string
956          delimiter.  */
957       if (scheme == SCHEME_FTP)
958         {
959           query_b = query_e = NULL;
960           path_e = p;
961         }
962     }
963   if (*p == '#')
964     {
965       ++p;
966       fragment_b = p;
967       p += strlen (p);
968       fragment_e = p;
969     }
970   assert (*p == 0);
971
972   if (uname_b != uname_e)
973     {
974       /* http://user:pass@host */
975       /*        ^         ^    */
976       /*     uname_b   uname_e */
977       if (!parse_uname (uname_b, uname_e - uname_b - 1, &user, &passwd))
978         {
979           SETERR (error, PE_INVALID_USER_NAME);
980           return NULL;
981         }
982     }
983
984   u = (struct url *)xmalloc (sizeof (struct url));
985   memset (u, 0, sizeof (*u));
986
987   u->scheme = scheme;
988   u->host   = strdupdelim (host_b, host_e);
989   u->port   = port;
990   u->user   = user;
991   u->passwd = passwd;
992
993   u->path = strdupdelim (path_b, path_e);
994   path_modified = path_simplify (u->path);
995   parse_path (u->path, &u->dir, &u->file);
996
997   host_modified = lowercase_str (u->host);
998
999   if (params_b)
1000     u->params = strdupdelim (params_b, params_e);
1001   if (query_b)
1002     u->query = strdupdelim (query_b, query_e);
1003   if (fragment_b)
1004     u->fragment = strdupdelim (fragment_b, fragment_e);
1005
1006   if (path_modified || u->fragment || host_modified || path_b == path_e)
1007     {
1008       /* If we suspect that a transformation has rendered what
1009          url_string might return different from URL_ENCODED, rebuild
1010          u->url using url_string.  */
1011       u->url = url_string (u, 0);
1012
1013       if (url_encoded != url)
1014         xfree ((char *) url_encoded);
1015     }
1016   else
1017     {
1018       if (url_encoded == url)
1019         u->url    = xstrdup (url);
1020       else
1021         u->url    = url_encoded;
1022     }
1023   url_encoded = NULL;
1024
1025   return u;
1026 }
1027
1028 const char *
1029 url_error (int error_code)
1030 {
1031   assert (error_code >= 0 && error_code < ARRAY_SIZE (parse_errors));
1032   return parse_errors[error_code];
1033 }
1034
1035 static void
1036 parse_path (const char *quoted_path, char **dir, char **file)
1037 {
1038   char *path, *last_slash;
1039
1040   STRDUP_ALLOCA (path, quoted_path);
1041   decode_string (path);
1042
1043   last_slash = strrchr (path, '/');
1044   if (!last_slash)
1045     {
1046       *dir = xstrdup ("");
1047       *file = xstrdup (path);
1048     }
1049   else
1050     {
1051       *dir = strdupdelim (path, last_slash);
1052       *file = xstrdup (last_slash + 1);
1053     }
1054 }
1055
1056 /* Note: URL's "full path" is the path with the query string and
1057    params appended.  The "fragment" (#foo) is intentionally ignored,
1058    but that might be changed.  For example, if the original URL was
1059    "http://host:port/foo/bar/baz;bullshit?querystring#uselessfragment",
1060    the full path will be "/foo/bar/baz;bullshit?querystring".  */
1061
1062 /* Return the length of the full path, without the terminating
1063    zero.  */
1064
1065 static int
1066 full_path_length (const struct url *url)
1067 {
1068   int len = 0;
1069
1070 #define FROB(el) if (url->el) len += 1 + strlen (url->el)
1071
1072   FROB (path);
1073   FROB (params);
1074   FROB (query);
1075
1076 #undef FROB
1077
1078   return len;
1079 }
1080
1081 /* Write out the full path. */
1082
1083 static void
1084 full_path_write (const struct url *url, char *where)
1085 {
1086 #define FROB(el, chr) do {                      \
1087   char *f_el = url->el;                         \
1088   if (f_el) {                                   \
1089     int l = strlen (f_el);                      \
1090     *where++ = chr;                             \
1091     memcpy (where, f_el, l);                    \
1092     where += l;                                 \
1093   }                                             \
1094 } while (0)
1095
1096   FROB (path, '/');
1097   FROB (params, ';');
1098   FROB (query, '?');
1099
1100 #undef FROB
1101 }
1102
1103 /* Public function for getting the "full path".  E.g. if u->path is
1104    "foo/bar" and u->query is "param=value", full_path will be
1105    "/foo/bar?param=value". */
1106
1107 char *
1108 url_full_path (const struct url *url)
1109 {
1110   int length = full_path_length (url);
1111   char *full_path = (char *)xmalloc(length + 1);
1112
1113   full_path_write (url, full_path);
1114   full_path[length] = '\0';
1115
1116   return full_path;
1117 }
1118
1119 /* Sync u->path and u->url with u->dir and u->file. */
1120
1121 static void
1122 sync_path (struct url *url)
1123 {
1124   char *newpath;
1125
1126   xfree (url->path);
1127
1128   if (!*url->dir)
1129     {
1130       newpath = xstrdup (url->file);
1131       REENCODE (newpath);
1132     }
1133   else
1134     {
1135       int dirlen = strlen (url->dir);
1136       int filelen = strlen (url->file);
1137
1138       newpath = xmalloc (dirlen + 1 + filelen + 1);
1139       memcpy (newpath, url->dir, dirlen);
1140       newpath[dirlen] = '/';
1141       memcpy (newpath + dirlen + 1, url->file, filelen);
1142       newpath[dirlen + 1 + filelen] = '\0';
1143       REENCODE (newpath);
1144     }
1145
1146   url->path = newpath;
1147
1148   /* Synchronize u->url. */
1149   xfree (url->url);
1150   url->url = url_string (url, 0);
1151 }
1152
1153 /* Mutators.  Code in ftp.c insists on changing u->dir and u->file.
1154    This way we can sync u->path and u->url when they get changed.  */
1155
1156 void
1157 url_set_dir (struct url *url, const char *newdir)
1158 {
1159   xfree (url->dir);
1160   url->dir = xstrdup (newdir);
1161   sync_path (url);
1162 }
1163
1164 void
1165 url_set_file (struct url *url, const char *newfile)
1166 {
1167   xfree (url->file);
1168   url->file = xstrdup (newfile);
1169   sync_path (url);
1170 }
1171
1172 void
1173 url_free (struct url *url)
1174 {
1175   xfree (url->host);
1176   xfree (url->path);
1177   xfree (url->url);
1178
1179   FREE_MAYBE (url->params);
1180   FREE_MAYBE (url->query);
1181   FREE_MAYBE (url->fragment);
1182   FREE_MAYBE (url->user);
1183   FREE_MAYBE (url->passwd);
1184
1185   xfree (url->dir);
1186   xfree (url->file);
1187
1188   xfree (url);
1189 }
1190 \f
1191 struct urlpos *
1192 get_urls_file (const char *file)
1193 {
1194   struct file_memory *fm;
1195   struct urlpos *head, *tail;
1196   const char *text, *text_end;
1197
1198   /* Load the file.  */
1199   fm = read_file (file);
1200   if (!fm)
1201     {
1202       logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
1203       return NULL;
1204     }
1205   DEBUGP (("Loaded %s (size %ld).\n", file, fm->length));
1206
1207   head = tail = NULL;
1208   text = fm->content;
1209   text_end = fm->content + fm->length;
1210   while (text < text_end)
1211     {
1212       const char *line_beg = text;
1213       const char *line_end = memchr (text, '\n', text_end - text);
1214       if (!line_end)
1215         line_end = text_end;
1216       else
1217         ++line_end;
1218       text = line_end;
1219
1220       /* Strip whitespace from the beginning and end of line. */
1221       while (line_beg < line_end && ISSPACE (*line_beg))
1222         ++line_beg;
1223       while (line_end > line_beg && ISSPACE (*(line_end - 1)))
1224         --line_end;
1225
1226       if (line_end > line_beg)
1227         {
1228           /* URL is in the [line_beg, line_end) region. */
1229
1230           int up_error_code;
1231           char *url_text;
1232           struct urlpos *entry;
1233           struct url *url;
1234
1235           /* We must copy the URL to a zero-terminated string, and we
1236              can't use alloca because we're in a loop.  *sigh*.  */
1237           url_text = strdupdelim (line_beg, line_end);
1238
1239           if (opt.base_href)
1240             {
1241               /* Merge opt.base_href with URL. */
1242               char *merged = uri_merge (opt.base_href, url_text);
1243               xfree (url_text);
1244               url_text = merged;
1245             }
1246
1247           url = url_parse (url_text, &up_error_code);
1248           if (!url)
1249             {
1250               logprintf (LOG_NOTQUIET, "%s: Invalid URL %s: %s\n",
1251                          file, url_text, url_error (up_error_code));
1252               xfree (url_text);
1253               continue;
1254             }
1255           xfree (url_text);
1256
1257           entry = (struct urlpos *)xmalloc (sizeof (struct urlpos));
1258           memset (entry, 0, sizeof (*entry));
1259           entry->next = NULL;
1260           entry->url = url;
1261
1262           if (!head)
1263             head = entry;
1264           else
1265             tail->next = entry;
1266           tail = entry;
1267         }
1268     }
1269   read_file_free (fm);
1270   return head;
1271 }
1272 \f
1273 /* Free the linked list of urlpos.  */
1274 void
1275 free_urlpos (struct urlpos *l)
1276 {
1277   while (l)
1278     {
1279       struct urlpos *next = l->next;
1280       if (l->url)
1281         url_free (l->url);
1282       FREE_MAYBE (l->local_name);
1283       xfree (l);
1284       l = next;
1285     }
1286 }
1287
1288 /* Rotate FNAME opt.backups times */
1289 void
1290 rotate_backups(const char *fname)
1291 {
1292   int maxlen = strlen (fname) + 1 + numdigit (opt.backups) + 1;
1293   char *from = (char *)alloca (maxlen);
1294   char *to = (char *)alloca (maxlen);
1295   struct stat sb;
1296   int i;
1297
1298   if (stat (fname, &sb) == 0)
1299     if (S_ISREG (sb.st_mode) == 0)
1300       return;
1301
1302   for (i = opt.backups; i > 1; i--)
1303     {
1304       sprintf (from, "%s.%d", fname, i - 1);
1305       sprintf (to, "%s.%d", fname, i);
1306       /* #### This will fail on machines without the rename() system
1307          call.  */
1308       rename (from, to);
1309     }
1310
1311   sprintf (to, "%s.%d", fname, 1);
1312   rename(fname, to);
1313 }
1314
1315 /* Create all the necessary directories for PATH (a file).  Calls
1316    mkdirhier() internally.  */
1317 int
1318 mkalldirs (const char *path)
1319 {
1320   const char *p;
1321   char *t;
1322   struct stat st;
1323   int res;
1324
1325   p = path + strlen (path);
1326   for (; *p != '/' && p != path; p--);
1327   /* Don't create if it's just a file.  */
1328   if ((p == path) && (*p != '/'))
1329     return 0;
1330   t = strdupdelim (path, p);
1331   /* Check whether the directory exists.  */
1332   if ((stat (t, &st) == 0))
1333     {
1334       if (S_ISDIR (st.st_mode))
1335         {
1336           xfree (t);
1337           return 0;
1338         }
1339       else
1340         {
1341           /* If the dir exists as a file name, remove it first.  This
1342              is *only* for Wget to work with buggy old CERN http
1343              servers.  Here is the scenario: When Wget tries to
1344              retrieve a directory without a slash, e.g.
1345              http://foo/bar (bar being a directory), CERN server will
1346              not redirect it too http://foo/bar/ -- it will generate a
1347              directory listing containing links to bar/file1,
1348              bar/file2, etc.  Wget will lose because it saves this
1349              HTML listing to a file `bar', so it cannot create the
1350              directory.  To work around this, if the file of the same
1351              name exists, we just remove it and create the directory
1352              anyway.  */
1353           DEBUGP (("Removing %s because of directory danger!\n", t));
1354           unlink (t);
1355         }
1356     }
1357   res = make_directory (t);
1358   if (res != 0)
1359     logprintf (LOG_NOTQUIET, "%s: %s", t, strerror (errno));
1360   xfree (t);
1361   return res;
1362 }
1363
1364 static int
1365 count_slashes (const char *s)
1366 {
1367   int i = 0;
1368   while (*s)
1369     if (*s++ == '/')
1370       ++i;
1371   return i;
1372 }
1373
1374 /* Return the path name of the URL-equivalent file name, with a
1375    remote-like structure of directories.  */
1376 static char *
1377 mkstruct (const struct url *u)
1378 {
1379   char *dir, *file;
1380   char *res, *dirpref;
1381   int l;
1382
1383   if (opt.cut_dirs)
1384     {
1385       char *ptr = u->dir + (*u->dir == '/');
1386       int slash_count = 1 + count_slashes (ptr);
1387       int cut = MINVAL (opt.cut_dirs, slash_count);
1388       for (; cut && *ptr; ptr++)
1389         if (*ptr == '/')
1390           --cut;
1391       STRDUP_ALLOCA (dir, ptr);
1392     }
1393   else
1394     dir = u->dir + (*u->dir == '/');
1395
1396   /* Check for the true name (or at least a consistent name for saving
1397      to directory) of HOST, reusing the hlist if possible.  */
1398   if (opt.add_hostdir)
1399     {
1400       /* Add dir_prefix and hostname (if required) to the beginning of
1401          dir.  */
1402       dirpref = (char *)alloca (strlen (opt.dir_prefix) + 1
1403                                 + strlen (u->host)
1404                                 + 1 + numdigit (u->port)
1405                                 + 1);
1406       if (!DOTP (opt.dir_prefix))
1407         sprintf (dirpref, "%s/%s", opt.dir_prefix, u->host);
1408       else
1409         strcpy (dirpref, u->host);
1410
1411       if (u->port != scheme_default_port (u->scheme))
1412         {
1413           int len = strlen (dirpref);
1414           dirpref[len] = ':';
1415           number_to_string (dirpref + len + 1, u->port);
1416         }
1417     }
1418   else                          /* not add_hostdir */
1419     {
1420       if (!DOTP (opt.dir_prefix))
1421         dirpref = opt.dir_prefix;
1422       else
1423         dirpref = "";
1424     }
1425
1426   /* If there is a prefix, prepend it.  */
1427   if (*dirpref)
1428     {
1429       char *newdir = (char *)alloca (strlen (dirpref) + 1 + strlen (dir) + 2);
1430       sprintf (newdir, "%s%s%s", dirpref, *dir == '/' ? "" : "/", dir);
1431       dir = newdir;
1432     }
1433
1434   l = strlen (dir);
1435   if (l && dir[l - 1] == '/')
1436     dir[l - 1] = '\0';
1437
1438   if (!*u->file)
1439     file = "index.html";
1440   else
1441     file = u->file;
1442
1443   /* Finally, construct the full name.  */
1444   res = (char *)xmalloc (strlen (dir) + 1 + strlen (file)
1445                          + 1);
1446   sprintf (res, "%s%s%s", dir, *dir ? "/" : "", file);
1447
1448   return res;
1449 }
1450
1451 /* Compose a file name out of BASE, an unescaped file name, and QUERY,
1452    an escaped query string.  The trick is to make sure that unsafe
1453    characters in BASE are escaped, and that slashes in QUERY are also
1454    escaped.  */
1455
1456 static char *
1457 compose_file_name (char *base, char *query)
1458 {
1459   char result[256];
1460   char *from;
1461   char *to = result;
1462
1463   /* Copy BASE to RESULT and encode all unsafe characters.  */
1464   from = base;
1465   while (*from && to - result < sizeof (result))
1466     {
1467       if (UNSAFE_CHAR (*from))
1468         {
1469           unsigned char c = *from++;
1470           *to++ = '%';
1471           *to++ = XDIGIT_TO_XCHAR (c >> 4);
1472           *to++ = XDIGIT_TO_XCHAR (c & 0xf);
1473         }
1474       else
1475         *to++ = *from++;
1476     }
1477
1478   if (query && to - result < sizeof (result))
1479     {
1480       *to++ = '?';
1481
1482       /* Copy QUERY to RESULT and encode all '/' characters. */
1483       from = query;
1484       while (*from && to - result < sizeof (result))
1485         {
1486           if (*from == '/')
1487             {
1488               *to++ = '%';
1489               *to++ = '2';
1490               *to++ = 'F';
1491               ++from;
1492             }
1493           else
1494             *to++ = *from++;
1495         }
1496     }
1497
1498   if (to - result < sizeof (result))
1499     *to = '\0';
1500   else
1501     /* Truncate input which is too long, presumably due to a huge
1502        query string.  */
1503     result[sizeof (result) - 1] = '\0';
1504
1505   return xstrdup (result);
1506 }
1507
1508 /* Create a unique filename, corresponding to a given URL.  Calls
1509    mkstruct if necessary.  Does *not* actually create any directories.  */
1510 char *
1511 url_filename (const struct url *u)
1512 {
1513   char *file, *name;
1514
1515   char *query = u->query && *u->query ? u->query : NULL;
1516
1517   if (opt.dirstruct)
1518     {
1519       char *base = mkstruct (u);
1520       file = compose_file_name (base, query);
1521       xfree (base);
1522     }
1523   else
1524     {
1525       char *base = *u->file ? u->file : "index.html";
1526       file = compose_file_name (base, query);
1527
1528       /* Check whether the prefix directory is something other than "."
1529          before prepending it.  */
1530       if (!DOTP (opt.dir_prefix))
1531         {
1532           /* #### should just realloc FILE and prepend dir_prefix. */
1533           char *nfile = (char *)xmalloc (strlen (opt.dir_prefix)
1534                                          + 1 + strlen (file) + 1);
1535           sprintf (nfile, "%s/%s", opt.dir_prefix, file);
1536           xfree (file);
1537           file = nfile;
1538         }
1539     }
1540
1541   /* DOS-ish file systems don't like `%' signs in them; we change it
1542      to `@'.  */
1543 #ifdef WINDOWS
1544   {
1545     char *p = file;
1546     for (p = file; *p; p++)
1547       if (*p == '%')
1548         *p = '@';
1549   }
1550 #endif /* WINDOWS */
1551
1552   /* Check the cases in which the unique extensions are not used:
1553      1) Clobbering is turned off (-nc).
1554      2) Retrieval with regetting.
1555      3) Timestamping is used.
1556      4) Hierarchy is built.
1557
1558      The exception is the case when file does exist and is a
1559      directory (actually support for bad httpd-s).  */
1560   if ((opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct)
1561       && !(file_exists_p (file) && !file_non_directory_p (file)))
1562     return file;
1563
1564   /* Find a unique name.  */
1565   name = unique_name (file);
1566   xfree (file);
1567   return name;
1568 }
1569
1570 /* Return the langth of URL's path.  Path is considered to be
1571    terminated by one of '?', ';', '#', or by the end of the
1572    string.  */
1573 static int
1574 path_length (const char *url)
1575 {
1576   const char *q = strpbrk_or_eos (url, "?;#");
1577   return q - url;
1578 }
1579
1580 /* Find the last occurrence of character C in the range [b, e), or
1581    NULL, if none are present.  This is equivalent to strrchr(b, c),
1582    except that it accepts an END argument instead of requiring the
1583    string to be zero-terminated.  Why is there no memrchr()?  */
1584 static const char *
1585 find_last_char (const char *b, const char *e, char c)
1586 {
1587   for (; e > b; e--)
1588     if (*e == c)
1589       return e;
1590   return NULL;
1591 }
1592 \f
1593 /* Resolve "." and ".." elements of PATH by destructively modifying
1594    PATH.  "." is resolved by removing that path element, and ".." is
1595    resolved by removing the preceding path element.  Leading and
1596    trailing slashes are preserved.
1597
1598    Return non-zero if any changes have been made.
1599
1600    For example, "a/b/c/./../d/.." will yield "a/b/".  More exhaustive
1601    test examples are provided below.  If you change anything in this
1602    function, run test_path_simplify to make sure you haven't broken a
1603    test case.
1604
1605    A previous version of this function was based on path_simplify()
1606    from GNU Bash, but it has been rewritten for Wget 1.8.1.  */
1607
1608 static int
1609 path_simplify (char *path)
1610 {
1611   int change = 0;
1612   char *p, *end;
1613
1614   if (path[0] == '/')
1615     ++path;                     /* preserve the leading '/'. */
1616
1617   p = path;
1618   end = p + strlen (p) + 1;     /* position past the terminating zero. */
1619
1620   while (1)
1621     {
1622     again:
1623       /* P should point to the beginning of a path element. */
1624
1625       if (*p == '.' && (*(p + 1) == '/' || *(p + 1) == '\0'))
1626         {
1627           /* Handle "./foo" by moving "foo" two characters to the
1628              left. */
1629           if (*(p + 1) == '/')
1630             {
1631               change = 1;
1632               memmove (p, p + 2, end - p);
1633               end -= 2;
1634               goto again;
1635             }
1636           else
1637             {
1638               change = 1;
1639               *p = '\0';
1640               break;
1641             }
1642         }
1643       else if (*p == '.' && *(p + 1) == '.'
1644                && (*(p + 2) == '/' || *(p + 2) == '\0'))
1645         {
1646           /* Handle "../foo" by moving "foo" one path element to the
1647              left.  */
1648           char *b = p;          /* not p-1 because P can equal PATH */
1649
1650           /* Backtrack by one path element, but not past the beginning
1651              of PATH. */
1652
1653           /* foo/bar/../baz */
1654           /*         ^ p    */
1655           /*     ^ b        */
1656
1657           if (b > path)
1658             {
1659               /* Move backwards until B hits the beginning of the
1660                  previous path element or the beginning of path. */
1661               for (--b; b > path && *(b - 1) != '/'; b--)
1662                 ;
1663             }
1664
1665           change = 1;
1666           if (*(p + 2) == '/')
1667             {
1668               memmove (b, p + 3, end - (p + 3));
1669               end -= (p + 3) - b;
1670               p = b;
1671             }
1672           else
1673             {
1674               *b = '\0';
1675               break;
1676             }
1677
1678           goto again;
1679         }
1680       else if (*p == '/')
1681         {
1682           /* Remove empty path elements.  Not mandated by rfc1808 et
1683              al, but empty path elements are not all that useful, and
1684              the rest of Wget might not deal with them well. */
1685           char *q = p;
1686           while (*q == '/')
1687             ++q;
1688           change = 1;
1689           if (*q == '\0')
1690             {
1691               *p = '\0';
1692               break;
1693             }
1694           memmove (p, q, end - q);
1695           end -= q - p;
1696           goto again;
1697         }
1698
1699       /* Skip to the next path element. */
1700       while (*p && *p != '/')
1701         ++p;
1702       if (*p == '\0')
1703         break;
1704
1705       /* Make sure P points to the beginning of the next path element,
1706          which is location after the slash. */
1707       ++p;
1708     }
1709
1710   return change;
1711 }
1712 \f
1713 /* Resolve the result of "linking" a base URI (BASE) to a
1714    link-specified URI (LINK).
1715
1716    Either of the URIs may be absolute or relative, complete with the
1717    host name, or path only.  This tries to behave "reasonably" in all
1718    foreseeable cases.  It employs little specific knowledge about
1719    schemes or URL-specific stuff -- it just works on strings.
1720
1721    The parameters LINKLENGTH is useful if LINK is not zero-terminated.
1722    See uri_merge for a gentler interface to this functionality.
1723
1724    Perhaps this function should call path_simplify so that the callers
1725    don't have to call url_parse unconditionally.  */
1726 static char *
1727 uri_merge_1 (const char *base, const char *link, int linklength, int no_scheme)
1728 {
1729   char *constr;
1730
1731   if (no_scheme)
1732     {
1733       const char *end = base + path_length (base);
1734
1735       if (!*link)
1736         {
1737           /* Empty LINK points back to BASE, query string and all. */
1738           constr = xstrdup (base);
1739         }
1740       else if (*link == '?')
1741         {
1742           /* LINK points to the same location, but changes the query
1743              string.  Examples: */
1744           /* uri_merge("path",         "?new") -> "path?new"     */
1745           /* uri_merge("path?foo",     "?new") -> "path?new"     */
1746           /* uri_merge("path?foo#bar", "?new") -> "path?new"     */
1747           /* uri_merge("path#foo",     "?new") -> "path?new"     */
1748           int baselength = end - base;
1749           constr = xmalloc (baselength + linklength + 1);
1750           memcpy (constr, base, baselength);
1751           memcpy (constr + baselength, link, linklength);
1752           constr[baselength + linklength] = '\0';
1753         }
1754       else if (*link == '#')
1755         {
1756           /* uri_merge("path",         "#new") -> "path#new"     */
1757           /* uri_merge("path#foo",     "#new") -> "path#new"     */
1758           /* uri_merge("path?foo",     "#new") -> "path?foo#new" */
1759           /* uri_merge("path?foo#bar", "#new") -> "path?foo#new" */
1760           int baselength;
1761           const char *end1 = strchr (base, '#');
1762           if (!end1)
1763             end1 = base + strlen (base);
1764           baselength = end1 - base;
1765           constr = xmalloc (baselength + linklength + 1);
1766           memcpy (constr, base, baselength);
1767           memcpy (constr + baselength, link, linklength);
1768           constr[baselength + linklength] = '\0';
1769         }
1770       else if (linklength > 1 && *link == '/' && *(link + 1) == '/')
1771         {
1772           /* LINK begins with "//" and so is a net path: we need to
1773              replace everything after (and including) the double slash
1774              with LINK. */
1775
1776           /* uri_merge("foo", "//new/bar")            -> "//new/bar"      */
1777           /* uri_merge("//old/foo", "//new/bar")      -> "//new/bar"      */
1778           /* uri_merge("http://old/foo", "//new/bar") -> "http://new/bar" */
1779
1780           int span;
1781           const char *slash;
1782           const char *start_insert;
1783
1784           /* Look for first slash. */
1785           slash = memchr (base, '/', end - base);
1786           /* If found slash and it is a double slash, then replace
1787              from this point, else default to replacing from the
1788              beginning.  */
1789           if (slash && *(slash + 1) == '/')
1790             start_insert = slash;
1791           else
1792             start_insert = base;
1793
1794           span = start_insert - base;
1795           constr = (char *)xmalloc (span + linklength + 1);
1796           if (span)
1797             memcpy (constr, base, span);
1798           memcpy (constr + span, link, linklength);
1799           constr[span + linklength] = '\0';
1800         }
1801       else if (*link == '/')
1802         {
1803           /* LINK is an absolute path: we need to replace everything
1804              after (and including) the FIRST slash with LINK.
1805
1806              So, if BASE is "http://host/whatever/foo/bar", and LINK is
1807              "/qux/xyzzy", our result should be
1808              "http://host/qux/xyzzy".  */
1809           int span;
1810           const char *slash;
1811           const char *start_insert = NULL; /* for gcc to shut up. */
1812           const char *pos = base;
1813           int seen_slash_slash = 0;
1814           /* We're looking for the first slash, but want to ignore
1815              double slash. */
1816         again:
1817           slash = memchr (pos, '/', end - pos);
1818           if (slash && !seen_slash_slash)
1819             if (*(slash + 1) == '/')
1820               {
1821                 pos = slash + 2;
1822                 seen_slash_slash = 1;
1823                 goto again;
1824               }
1825
1826           /* At this point, SLASH is the location of the first / after
1827              "//", or the first slash altogether.  START_INSERT is the
1828              pointer to the location where LINK will be inserted.  When
1829              examining the last two examples, keep in mind that LINK
1830              begins with '/'. */
1831
1832           if (!slash && !seen_slash_slash)
1833             /* example: "foo" */
1834             /*           ^    */
1835             start_insert = base;
1836           else if (!slash && seen_slash_slash)
1837             /* example: "http://foo" */
1838             /*                     ^ */
1839             start_insert = end;
1840           else if (slash && !seen_slash_slash)
1841             /* example: "foo/bar" */
1842             /*           ^        */
1843             start_insert = base;
1844           else if (slash && seen_slash_slash)
1845             /* example: "http://something/" */
1846             /*                           ^  */
1847             start_insert = slash;
1848
1849           span = start_insert - base;
1850           constr = (char *)xmalloc (span + linklength + 1);
1851           if (span)
1852             memcpy (constr, base, span);
1853           if (linklength)
1854             memcpy (constr + span, link, linklength);
1855           constr[span + linklength] = '\0';
1856         }
1857       else
1858         {
1859           /* LINK is a relative URL: we need to replace everything
1860              after last slash (possibly empty) with LINK.
1861
1862              So, if BASE is "whatever/foo/bar", and LINK is "qux/xyzzy",
1863              our result should be "whatever/foo/qux/xyzzy".  */
1864           int need_explicit_slash = 0;
1865           int span;
1866           const char *start_insert;
1867           const char *last_slash = find_last_char (base, end, '/');
1868           if (!last_slash)
1869             {
1870               /* No slash found at all.  Append LINK to what we have,
1871                  but we'll need a slash as a separator.
1872
1873                  Example: if base == "foo" and link == "qux/xyzzy", then
1874                  we cannot just append link to base, because we'd get
1875                  "fooqux/xyzzy", whereas what we want is
1876                  "foo/qux/xyzzy".
1877
1878                  To make sure the / gets inserted, we set
1879                  need_explicit_slash to 1.  We also set start_insert
1880                  to end + 1, so that the length calculations work out
1881                  correctly for one more (slash) character.  Accessing
1882                  that character is fine, since it will be the
1883                  delimiter, '\0' or '?'.  */
1884               /* example: "foo?..." */
1885               /*               ^    ('?' gets changed to '/') */
1886               start_insert = end + 1;
1887               need_explicit_slash = 1;
1888             }
1889           else if (last_slash && last_slash != base && *(last_slash - 1) == '/')
1890             {
1891               /* example: http://host"  */
1892               /*                      ^ */
1893               start_insert = end + 1;
1894               need_explicit_slash = 1;
1895             }
1896           else
1897             {
1898               /* example: "whatever/foo/bar" */
1899               /*                        ^    */
1900               start_insert = last_slash + 1;
1901             }
1902
1903           span = start_insert - base;
1904           constr = (char *)xmalloc (span + linklength + 1);
1905           if (span)
1906             memcpy (constr, base, span);
1907           if (need_explicit_slash)
1908             constr[span - 1] = '/';
1909           if (linklength)
1910             memcpy (constr + span, link, linklength);
1911           constr[span + linklength] = '\0';
1912         }
1913     }
1914   else /* !no_scheme */
1915     {
1916       constr = strdupdelim (link, link + linklength);
1917     }
1918   return constr;
1919 }
1920
1921 /* Merge BASE with LINK and return the resulting URI.  This is an
1922    interface to uri_merge_1 that assumes that LINK is a
1923    zero-terminated string.  */
1924 char *
1925 uri_merge (const char *base, const char *link)
1926 {
1927   return uri_merge_1 (base, link, strlen (link), !url_has_scheme (link));
1928 }
1929 \f
1930 #define APPEND(p, s) do {                       \
1931   int len = strlen (s);                         \
1932   memcpy (p, s, len);                           \
1933   p += len;                                     \
1934 } while (0)
1935
1936 /* Use this instead of password when the actual password is supposed
1937    to be hidden.  We intentionally use a generic string without giving
1938    away the number of characters in the password, like previous
1939    versions did.  */
1940 #define HIDDEN_PASSWORD "*password*"
1941
1942 /* Recreate the URL string from the data in URL.
1943
1944    If HIDE is non-zero (as it is when we're calling this on a URL we
1945    plan to print, but not when calling it to canonicalize a URL for
1946    use within the program), password will be hidden.  Unsafe
1947    characters in the URL will be quoted.  */
1948
1949 char *
1950 url_string (const struct url *url, int hide_password)
1951 {
1952   int size;
1953   char *result, *p;
1954   char *quoted_user = NULL, *quoted_passwd = NULL;
1955
1956   int scheme_port  = supported_schemes[url->scheme].default_port;
1957   char *scheme_str = supported_schemes[url->scheme].leading_string;
1958   int fplen = full_path_length (url);
1959
1960   int brackets_around_host = 0;
1961
1962   assert (scheme_str != NULL);
1963
1964   /* Make sure the user name and password are quoted. */
1965   if (url->user)
1966     {
1967       quoted_user = encode_string_maybe (url->user);
1968       if (url->passwd)
1969         {
1970           if (hide_password)
1971             quoted_passwd = HIDDEN_PASSWORD;
1972           else
1973             quoted_passwd = encode_string_maybe (url->passwd);
1974         }
1975     }
1976
1977   if (strchr (url->host, ':'))
1978     brackets_around_host = 1;
1979
1980   size = (strlen (scheme_str)
1981           + strlen (url->host)
1982           + (brackets_around_host ? 2 : 0)
1983           + fplen
1984           + 1);
1985   if (url->port != scheme_port)
1986     size += 1 + numdigit (url->port);
1987   if (quoted_user)
1988     {
1989       size += 1 + strlen (quoted_user);
1990       if (quoted_passwd)
1991         size += 1 + strlen (quoted_passwd);
1992     }
1993
1994   p = result = xmalloc (size);
1995
1996   APPEND (p, scheme_str);
1997   if (quoted_user)
1998     {
1999       APPEND (p, quoted_user);
2000       if (quoted_passwd)
2001         {
2002           *p++ = ':';
2003           APPEND (p, quoted_passwd);
2004         }
2005       *p++ = '@';
2006     }
2007
2008   if (brackets_around_host)
2009     *p++ = '[';
2010   APPEND (p, url->host);
2011   if (brackets_around_host)
2012     *p++ = ']';
2013   if (url->port != scheme_port)
2014     {
2015       *p++ = ':';
2016       p = number_to_string (p, url->port);
2017     }
2018
2019   full_path_write (url, p);
2020   p += fplen;
2021   *p++ = '\0';
2022
2023   assert (p - result == size);
2024
2025   if (quoted_user && quoted_user != url->user)
2026     xfree (quoted_user);
2027   if (quoted_passwd && !hide_password
2028       && quoted_passwd != url->passwd)
2029     xfree (quoted_passwd);
2030
2031   return result;
2032 }
2033 \f
2034 /* Return the URL of the proxy appropriate for url U.  */
2035 char *
2036 getproxy (struct url *u)
2037 {
2038   char *proxy = NULL;
2039   char *rewritten_url;
2040   static char rewritten_storage[1024];
2041
2042   if (!opt.use_proxy)
2043     return NULL;
2044   if (!no_proxy_match (u->host, (const char **)opt.no_proxy))
2045     return NULL;
2046
2047   switch (u->scheme)
2048     {
2049     case SCHEME_HTTP:
2050       proxy = opt.http_proxy ? opt.http_proxy : getenv ("http_proxy");
2051       break;
2052 #ifdef HAVE_SSL
2053     case SCHEME_HTTPS:
2054       proxy = opt.https_proxy ? opt.https_proxy : getenv ("https_proxy");
2055       break;
2056 #endif
2057     case SCHEME_FTP:
2058       proxy = opt.ftp_proxy ? opt.ftp_proxy : getenv ("ftp_proxy");
2059       break;
2060     case SCHEME_INVALID:
2061       break;
2062     }
2063   if (!proxy || !*proxy)
2064     return NULL;
2065
2066   /* Handle shorthands.  `rewritten_storage' is a kludge to allow
2067      getproxy() to return static storage. */
2068   rewritten_url = rewrite_shorthand_url (proxy);
2069   if (rewritten_url)
2070     {
2071       strncpy (rewritten_storage, rewritten_url, sizeof(rewritten_storage));
2072       rewritten_storage[sizeof (rewritten_storage) - 1] = '\0';
2073       proxy = rewritten_storage;
2074     }
2075
2076   return proxy;
2077 }
2078
2079 /* Should a host be accessed through proxy, concerning no_proxy?  */
2080 int
2081 no_proxy_match (const char *host, const char **no_proxy)
2082 {
2083   if (!no_proxy)
2084     return 1;
2085   else
2086     return !sufmatch (no_proxy, host);
2087 }
2088 \f
2089 /* Support for converting links for local viewing in downloaded HTML
2090    files.  This should be moved to another file, because it has
2091    nothing to do with processing URLs.  */
2092
2093 static void write_backup_file PARAMS ((const char *, downloaded_file_t));
2094 static const char *replace_attr PARAMS ((const char *, int, FILE *,
2095                                          const char *));
2096 static const char *replace_attr_refresh_hack PARAMS ((const char *, int, FILE *,
2097                                                       const char *, int));
2098 static char *local_quote_string PARAMS ((const char *));
2099
2100 /* Change the links in one HTML file.  LINKS is a list of links in the
2101    document, along with their positions and the desired direction of
2102    the conversion.  */
2103 void
2104 convert_links (const char *file, struct urlpos *links)
2105 {
2106   struct file_memory *fm;
2107   FILE *fp;
2108   const char *p;
2109   downloaded_file_t downloaded_file_return;
2110
2111   struct urlpos *link;
2112   int to_url_count = 0, to_file_count = 0;
2113
2114   logprintf (LOG_VERBOSE, _("Converting %s... "), file);
2115
2116   {
2117     /* First we do a "dry run": go through the list L and see whether
2118        any URL needs to be converted in the first place.  If not, just
2119        leave the file alone.  */
2120     int dry_count = 0;
2121     struct urlpos *dry = links;
2122     for (dry = links; dry; dry = dry->next)
2123       if (dry->convert != CO_NOCONVERT)
2124         ++dry_count;
2125     if (!dry_count)
2126       {
2127         logputs (LOG_VERBOSE, _("nothing to do.\n"));
2128         return;
2129       }
2130   }
2131
2132   fm = read_file (file);
2133   if (!fm)
2134     {
2135       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
2136                  file, strerror (errno));
2137       return;
2138     }
2139
2140   downloaded_file_return = downloaded_file (CHECK_FOR_FILE, file);
2141   if (opt.backup_converted && downloaded_file_return)
2142     write_backup_file (file, downloaded_file_return);
2143
2144   /* Before opening the file for writing, unlink the file.  This is
2145      important if the data in FM is mmaped.  In such case, nulling the
2146      file, which is what fopen() below does, would make us read all
2147      zeroes from the mmaped region.  */
2148   if (unlink (file) < 0 && errno != ENOENT)
2149     {
2150       logprintf (LOG_NOTQUIET, _("Unable to delete `%s': %s\n"),
2151                  file, strerror (errno));
2152       read_file_free (fm);
2153       return;
2154     }
2155   /* Now open the file for writing.  */
2156   fp = fopen (file, "wb");
2157   if (!fp)
2158     {
2159       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
2160                  file, strerror (errno));
2161       read_file_free (fm);
2162       return;
2163     }
2164
2165   /* Here we loop through all the URLs in file, replacing those of
2166      them that are downloaded with relative references.  */
2167   p = fm->content;
2168   for (link = links; link; link = link->next)
2169     {
2170       char *url_start = fm->content + link->pos;
2171
2172       if (link->pos >= fm->length)
2173         {
2174           DEBUGP (("Something strange is going on.  Please investigate."));
2175           break;
2176         }
2177       /* If the URL is not to be converted, skip it.  */
2178       if (link->convert == CO_NOCONVERT)
2179         {
2180           DEBUGP (("Skipping %s at position %d.\n", link->url->url, link->pos));
2181           continue;
2182         }
2183
2184       /* Echo the file contents, up to the offending URL's opening
2185          quote, to the outfile.  */
2186       fwrite (p, 1, url_start - p, fp);
2187       p = url_start;
2188
2189       switch (link->convert)
2190         {
2191         case CO_CONVERT_TO_RELATIVE:
2192           /* Convert absolute URL to relative. */
2193           {
2194             char *newname = construct_relative (file, link->local_name);
2195             char *quoted_newname = local_quote_string (newname);
2196
2197             if (!link->link_refresh_p)
2198               p = replace_attr (p, link->size, fp, quoted_newname);
2199             else
2200               p = replace_attr_refresh_hack (p, link->size, fp, quoted_newname,
2201                                              link->refresh_timeout);
2202
2203             DEBUGP (("TO_RELATIVE: %s to %s at position %d in %s.\n",
2204                      link->url->url, newname, link->pos, file));
2205             xfree (newname);
2206             xfree (quoted_newname);
2207             ++to_file_count;
2208             break;
2209           }
2210         case CO_CONVERT_TO_COMPLETE:
2211           /* Convert the link to absolute URL. */
2212           {
2213             char *newlink = link->url->url;
2214             char *quoted_newlink = html_quote_string (newlink);
2215
2216             if (!link->link_refresh_p)
2217               p = replace_attr (p, link->size, fp, quoted_newlink);
2218             else
2219               p = replace_attr_refresh_hack (p, link->size, fp, quoted_newlink,
2220                                              link->refresh_timeout);
2221
2222             DEBUGP (("TO_COMPLETE: <something> to %s at position %d in %s.\n",
2223                      newlink, link->pos, file));
2224             xfree (quoted_newlink);
2225             ++to_url_count;
2226             break;
2227           }
2228         case CO_NULLIFY_BASE:
2229           /* Change the base href to "". */
2230           p = replace_attr (p, link->size, fp, "");
2231           break;
2232         case CO_NOCONVERT:
2233           abort ();
2234           break;
2235         }
2236     }
2237
2238   /* Output the rest of the file. */
2239   if (p - fm->content < fm->length)
2240     fwrite (p, 1, fm->length - (p - fm->content), fp);
2241   fclose (fp);
2242   read_file_free (fm);
2243
2244   logprintf (LOG_VERBOSE, "%d-%d\n", to_file_count, to_url_count);
2245 }
2246
2247 /* Construct and return a malloced copy of the relative link from two
2248    pieces of information: local name S1 of the referring file and
2249    local name S2 of the referred file.
2250
2251    So, if S1 is "jagor.srce.hr/index.html" and S2 is
2252    "jagor.srce.hr/images/news.gif", the function will return
2253    "images/news.gif".
2254
2255    Alternately, if S1 is "fly.cc.fer.hr/ioccc/index.html", and S2 is
2256    "fly.cc.fer.hr/images/fly.gif", the function will return
2257    "../images/fly.gif".
2258
2259    Caveats: S1 should not begin with `/', unless S2 also begins with
2260    '/'.  S1 should not contain things like ".." and such --
2261    construct_relative ("fly/ioccc/../index.html",
2262    "fly/images/fly.gif") will fail.  (A workaround is to call
2263    something like path_simplify() on S1).  */
2264 static char *
2265 construct_relative (const char *s1, const char *s2)
2266 {
2267   int i, cnt, sepdirs1;
2268   char *res;
2269
2270   if (*s2 == '/')
2271     return xstrdup (s2);
2272   /* S1 should *not* be absolute, if S2 wasn't.  */
2273   assert (*s1 != '/');
2274   i = cnt = 0;
2275   /* Skip the directories common to both strings.  */
2276   while (1)
2277     {
2278       while (s1[i] && s2[i]
2279              && (s1[i] == s2[i])
2280              && (s1[i] != '/')
2281              && (s2[i] != '/'))
2282         ++i;
2283       if (s1[i] == '/' && s2[i] == '/')
2284         cnt = ++i;
2285       else
2286         break;
2287     }
2288   for (sepdirs1 = 0; s1[i]; i++)
2289     if (s1[i] == '/')
2290       ++sepdirs1;
2291   /* Now, construct the file as of:
2292      - ../ repeated sepdirs1 time
2293      - all the non-mutual directories of S2.  */
2294   res = (char *)xmalloc (3 * sepdirs1 + strlen (s2 + cnt) + 1);
2295   for (i = 0; i < sepdirs1; i++)
2296     memcpy (res + 3 * i, "../", 3);
2297   strcpy (res + 3 * i, s2 + cnt);
2298   return res;
2299 }
2300 \f
2301 static void
2302 write_backup_file (const char *file, downloaded_file_t downloaded_file_return)
2303 {
2304   /* Rather than just writing over the original .html file with the
2305      converted version, save the former to *.orig.  Note we only do
2306      this for files we've _successfully_ downloaded, so we don't
2307      clobber .orig files sitting around from previous invocations. */
2308
2309   /* Construct the backup filename as the original name plus ".orig". */
2310   size_t         filename_len = strlen(file);
2311   char*          filename_plus_orig_suffix;
2312   boolean        already_wrote_backup_file = FALSE;
2313   slist*         converted_file_ptr;
2314   static slist*  converted_files = NULL;
2315
2316   if (downloaded_file_return == FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED)
2317     {
2318       /* Just write "orig" over "html".  We need to do it this way
2319          because when we're checking to see if we've downloaded the
2320          file before (to see if we can skip downloading it), we don't
2321          know if it's a text/html file.  Therefore we don't know yet
2322          at that stage that -E is going to cause us to tack on
2323          ".html", so we need to compare vs. the original URL plus
2324          ".orig", not the original URL plus ".html.orig". */
2325       filename_plus_orig_suffix = alloca (filename_len + 1);
2326       strcpy(filename_plus_orig_suffix, file);
2327       strcpy((filename_plus_orig_suffix + filename_len) - 4, "orig");
2328     }
2329   else /* downloaded_file_return == FILE_DOWNLOADED_NORMALLY */
2330     {
2331       /* Append ".orig" to the name. */
2332       filename_plus_orig_suffix = alloca (filename_len + sizeof(".orig"));
2333       strcpy(filename_plus_orig_suffix, file);
2334       strcpy(filename_plus_orig_suffix + filename_len, ".orig");
2335     }
2336
2337   /* We can get called twice on the same URL thanks to the
2338      convert_all_links() call in main().  If we write the .orig file
2339      each time in such a case, it'll end up containing the first-pass
2340      conversion, not the original file.  So, see if we've already been
2341      called on this file. */
2342   converted_file_ptr = converted_files;
2343   while (converted_file_ptr != NULL)
2344     if (strcmp(converted_file_ptr->string, file) == 0)
2345       {
2346         already_wrote_backup_file = TRUE;
2347         break;
2348       }
2349     else
2350       converted_file_ptr = converted_file_ptr->next;
2351
2352   if (!already_wrote_backup_file)
2353     {
2354       /* Rename <file> to <file>.orig before former gets written over. */
2355       if (rename(file, filename_plus_orig_suffix) != 0)
2356         logprintf (LOG_NOTQUIET, _("Cannot back up %s as %s: %s\n"),
2357                    file, filename_plus_orig_suffix, strerror (errno));
2358
2359       /* Remember that we've already written a .orig backup for this file.
2360          Note that we never free this memory since we need it till the
2361          convert_all_links() call, which is one of the last things the
2362          program does before terminating.  BTW, I'm not sure if it would be
2363          safe to just set 'converted_file_ptr->string' to 'file' below,
2364          rather than making a copy of the string...  Another note is that I
2365          thought I could just add a field to the urlpos structure saying
2366          that we'd written a .orig file for this URL, but that didn't work,
2367          so I had to make this separate list.
2368          -- Dan Harkless <wget@harkless.org>
2369
2370          This [adding a field to the urlpos structure] didn't work
2371          because convert_file() is called from convert_all_links at
2372          the end of the retrieval with a freshly built new urlpos
2373          list.
2374          -- Hrvoje Niksic <hniksic@arsdigita.com>
2375       */
2376       converted_file_ptr = xmalloc(sizeof(*converted_file_ptr));
2377       converted_file_ptr->string = xstrdup(file);  /* die on out-of-mem. */
2378       converted_file_ptr->next = converted_files;
2379       converted_files = converted_file_ptr;
2380     }
2381 }
2382
2383 static int find_fragment PARAMS ((const char *, int, const char **,
2384                                   const char **));
2385
2386 /* Replace an attribute's original text with NEW_TEXT. */
2387
2388 static const char *
2389 replace_attr (const char *p, int size, FILE *fp, const char *new_text)
2390 {
2391   int quote_flag = 0;
2392   char quote_char = '\"';       /* use "..." for quoting, unless the
2393                                    original value is quoted, in which
2394                                    case reuse its quoting char. */
2395   const char *frag_beg, *frag_end;
2396
2397   /* Structure of our string is:
2398        "...old-contents..."
2399        <---    size    --->  (with quotes)
2400      OR:
2401        ...old-contents...
2402        <---    size   -->    (no quotes)   */
2403
2404   if (*p == '\"' || *p == '\'')
2405     {
2406       quote_char = *p;
2407       quote_flag = 1;
2408       ++p;
2409       size -= 2;                /* disregard opening and closing quote */
2410     }
2411   putc (quote_char, fp);
2412   fputs (new_text, fp);
2413
2414   /* Look for fragment identifier, if any. */
2415   if (find_fragment (p, size, &frag_beg, &frag_end))
2416     fwrite (frag_beg, 1, frag_end - frag_beg, fp);
2417   p += size;
2418   if (quote_flag)
2419     ++p;
2420   putc (quote_char, fp);
2421
2422   return p;
2423 }
2424
2425 /* The same as REPLACE_ATTR, but used when replacing
2426    <meta http-equiv=refresh content="new_text"> because we need to
2427    append "timeout_value; URL=" before the next_text.  */
2428
2429 static const char *
2430 replace_attr_refresh_hack (const char *p, int size, FILE *fp,
2431                            const char *new_text, int timeout)
2432 {
2433   /* "0; URL=..." */
2434   char *new_with_timeout = (char *)alloca (numdigit (timeout)
2435                                            + 6 /* "; URL=" */
2436                                            + strlen (new_text)
2437                                            + 1);
2438   sprintf (new_with_timeout, "%d; URL=%s", timeout, new_text);
2439
2440   return replace_attr (p, size, fp, new_with_timeout);
2441 }
2442
2443 /* Find the first occurrence of '#' in [BEG, BEG+SIZE) that is not
2444    preceded by '&'.  If the character is not found, return zero.  If
2445    the character is found, return 1 and set BP and EP to point to the
2446    beginning and end of the region.
2447
2448    This is used for finding the fragment indentifiers in URLs.  */
2449
2450 static int
2451 find_fragment (const char *beg, int size, const char **bp, const char **ep)
2452 {
2453   const char *end = beg + size;
2454   int saw_amp = 0;
2455   for (; beg < end; beg++)
2456     {
2457       switch (*beg)
2458         {
2459         case '&':
2460           saw_amp = 1;
2461           break;
2462         case '#':
2463           if (!saw_amp)
2464             {
2465               *bp = beg;
2466               *ep = end;
2467               return 1;
2468             }
2469           /* fallthrough */
2470         default:
2471           saw_amp = 0;
2472         }
2473     }
2474   return 0;
2475 }
2476
2477 /* Quote FILE for use as local reference to an HTML file.
2478
2479    We quote ? as %3F to avoid passing part of the file name as the
2480    parameter when browsing the converted file through HTTP.  However,
2481    it is safe to do this only when `--html-extension' is turned on.
2482    This is because converting "index.html?foo=bar" to
2483    "index.html%3Ffoo=bar" would break local browsing, as the latter
2484    isn't even recognized as an HTML file!  However, converting
2485    "index.html?foo=bar.html" to "index.html%3Ffoo=bar.html" should be
2486    safe for both local and HTTP-served browsing.  */
2487
2488 static char *
2489 local_quote_string (const char *file)
2490 {
2491   const char *file_sans_qmark;
2492   int qm;
2493
2494   if (!opt.html_extension)
2495     return html_quote_string (file);
2496
2497   qm = count_char (file, '?');
2498
2499   if (qm)
2500     {
2501       const char *from = file;
2502       char *to, *newname;
2503
2504       /* qm * 2 because we replace each question mark with "%3F",
2505          i.e. replace one char with three, hence two more.  */
2506       int fsqlen = strlen (file) + qm * 2;
2507
2508       to = newname = (char *)alloca (fsqlen + 1);
2509       for (; *from; from++)
2510         {
2511           if (*from != '?')
2512             *to++ = *from;
2513           else
2514             {
2515               *to++ = '%';
2516               *to++ = '3';
2517               *to++ = 'F';
2518             }
2519         }
2520       assert (to - newname == fsqlen);
2521       *to = '\0';
2522
2523       file_sans_qmark = newname;
2524     }
2525   else
2526     file_sans_qmark = file;
2527
2528   return html_quote_string (file_sans_qmark);
2529 }
2530
2531 /* We're storing "modes" of type downloaded_file_t in the hash table.
2532    However, our hash tables only accept pointers for keys and values.
2533    So when we need a pointer, we use the address of a
2534    downloaded_file_t variable of static storage.  */
2535    
2536 static downloaded_file_t *
2537 downloaded_mode_to_ptr (downloaded_file_t mode)
2538 {
2539   static downloaded_file_t
2540     v1 = FILE_NOT_ALREADY_DOWNLOADED,
2541     v2 = FILE_DOWNLOADED_NORMALLY,
2542     v3 = FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED,
2543     v4 = CHECK_FOR_FILE;
2544
2545   switch (mode)
2546     {
2547     case FILE_NOT_ALREADY_DOWNLOADED:
2548       return &v1;
2549     case FILE_DOWNLOADED_NORMALLY:
2550       return &v2;
2551     case FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED:
2552       return &v3;
2553     case CHECK_FOR_FILE:
2554       return &v4;
2555     }
2556   return NULL;
2557 }
2558
2559 /* This should really be merged with dl_file_url_map and
2560    downloaded_html_files in recur.c.  This was originally a list, but
2561    I changed it to a hash table beause it was actually taking a lot of
2562    time to find things in it.  */
2563
2564 static struct hash_table *downloaded_files_hash;
2565
2566 /* Remembers which files have been downloaded.  In the standard case, should be
2567    called with mode == FILE_DOWNLOADED_NORMALLY for each file we actually
2568    download successfully (i.e. not for ones we have failures on or that we skip
2569    due to -N).
2570
2571    When we've downloaded a file and tacked on a ".html" extension due to -E,
2572    call this function with FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED rather than
2573    FILE_DOWNLOADED_NORMALLY.
2574
2575    If you just want to check if a file has been previously added without adding
2576    it, call with mode == CHECK_FOR_FILE.  Please be sure to call this function
2577    with local filenames, not remote URLs. */
2578 downloaded_file_t
2579 downloaded_file (downloaded_file_t mode, const char *file)
2580 {
2581   downloaded_file_t *ptr;
2582
2583   if (mode == CHECK_FOR_FILE)
2584     {
2585       if (!downloaded_files_hash)
2586         return FILE_NOT_ALREADY_DOWNLOADED;
2587       ptr = hash_table_get (downloaded_files_hash, file);
2588       if (!ptr)
2589         return FILE_NOT_ALREADY_DOWNLOADED;
2590       return *ptr;
2591     }
2592
2593   if (!downloaded_files_hash)
2594     downloaded_files_hash = make_string_hash_table (0);
2595
2596   ptr = hash_table_get (downloaded_files_hash, file);
2597   if (ptr)
2598     return *ptr;
2599
2600   ptr = downloaded_mode_to_ptr (mode);
2601   hash_table_put (downloaded_files_hash, xstrdup (file), &ptr);
2602
2603   return FILE_NOT_ALREADY_DOWNLOADED;
2604 }
2605
2606 static int
2607 df_free_mapper (void *key, void *value, void *ignored)
2608 {
2609   xfree (key);
2610   return 0;
2611 }
2612
2613 void
2614 downloaded_files_free (void)
2615 {
2616   if (downloaded_files_hash)
2617     {
2618       hash_table_map (downloaded_files_hash, df_free_mapper, NULL);
2619       hash_table_destroy (downloaded_files_hash);
2620       downloaded_files_hash = NULL;
2621     }
2622 }
2623
2624 /* Return non-zero if scheme a is similar to scheme b.
2625  
2626    Schemes are similar if they are equal.  If SSL is supported, schemes
2627    are also similar if one is http (SCHEME_HTTP) and the other is https
2628    (SCHEME_HTTPS).  */
2629 int
2630 schemes_are_similar_p (enum url_scheme a, enum url_scheme b)
2631 {
2632   if (a == b)
2633     return 1;
2634 #ifdef HAVE_SSL
2635   if ((a == SCHEME_HTTP && b == SCHEME_HTTPS)
2636       || (a == SCHEME_HTTPS && b == SCHEME_HTTP))
2637     return 1;
2638 #endif
2639   return 0;
2640 }
2641 \f
2642 #if 0
2643 /* Debugging and testing support for path_simplify. */
2644
2645 /* Debug: run path_simplify on PATH and return the result in a new
2646    string.  Useful for calling from the debugger.  */
2647 static char *
2648 ps (char *path)
2649 {
2650   char *copy = xstrdup (path);
2651   path_simplify (copy);
2652   return copy;
2653 }
2654
2655 static void
2656 run_test (char *test, char *expected_result, int expected_change)
2657 {
2658   char *test_copy = xstrdup (test);
2659   int modified = path_simplify (test_copy);
2660
2661   if (0 != strcmp (test_copy, expected_result))
2662     {
2663       printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
2664               test, expected_result, test_copy);
2665     }
2666   if (modified != expected_change)
2667     {
2668       if (expected_change == 1)
2669         printf ("Expected no modification with path_simplify(\"%s\").\n",
2670                 test);
2671       else
2672         printf ("Expected modification with path_simplify(\"%s\").\n",
2673                 test);
2674     }
2675   xfree (test_copy);
2676 }
2677
2678 static void
2679 test_path_simplify (void)
2680 {
2681   static struct {
2682     char *test, *result;
2683     int should_modify;
2684   } tests[] = {
2685     { "",               "",             0 },
2686     { ".",              "",             1 },
2687     { "..",             "",             1 },
2688     { "foo",            "foo",          0 },
2689     { "foo/bar",        "foo/bar",      0 },
2690     { "foo///bar",      "foo/bar",      1 },
2691     { "foo/.",          "foo/",         1 },
2692     { "foo/./",         "foo/",         1 },
2693     { "foo./",          "foo./",        0 },
2694     { "foo/../bar",     "bar",          1 },
2695     { "foo/../bar/",    "bar/",         1 },
2696     { "foo/bar/..",     "foo/",         1 },
2697     { "foo/bar/../x",   "foo/x",        1 },
2698     { "foo/bar/../x/",  "foo/x/",       1 },
2699     { "foo/..",         "",             1 },
2700     { "foo/../..",      "",             1 },
2701     { "a/b/../../c",    "c",            1 },
2702     { "./a/../b",       "b",            1 }
2703   };
2704   int i;
2705
2706   for (i = 0; i < ARRAY_SIZE (tests); i++)
2707     {
2708       char *test = tests[i].test;
2709       char *expected_result = tests[i].result;
2710       int   expected_change = tests[i].should_modify;
2711       run_test (test, expected_result, expected_change);
2712     }
2713
2714   /* Now run all the tests with a leading slash before the test case,
2715      to prove that the slash is being preserved.  */
2716   for (i = 0; i < ARRAY_SIZE (tests); i++)
2717     {
2718       char *test, *expected_result;
2719       int expected_change = tests[i].should_modify;
2720
2721       test = xmalloc (1 + strlen (tests[i].test) + 1);
2722       sprintf (test, "/%s", tests[i].test);
2723
2724       expected_result = xmalloc (1 + strlen (tests[i].result) + 1);
2725       sprintf (expected_result, "/%s", tests[i].result);
2726
2727       run_test (test, expected_result, expected_change);
2728
2729       xfree (test);
2730       xfree (expected_result);
2731     }
2732 }
2733 #endif