]> sjero.net Git - wget/blob - src/url.c
[svn] Fix oversight in escape handling.
[wget] / src / url.c
1 /* URL handling.
2    Copyright (C) 1995, 1996, 1997, 2000, 2001, 2003, 2003
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables.  You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL".  If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so.  If you do not wish to do
29 so, delete this exception statement from your version.  */
30
31 #include <config.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #ifdef HAVE_STRING_H
36 # include <string.h>
37 #else
38 # include <strings.h>
39 #endif
40 #include <sys/types.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <errno.h>
45 #include <assert.h>
46
47 #include "wget.h"
48 #include "utils.h"
49 #include "url.h"
50
51 #ifndef errno
52 extern int errno;
53 #endif
54
55 struct scheme_data
56 {
57   char *leading_string;
58   int default_port;
59   int enabled;
60 };
61
62 /* Supported schemes: */
63 static struct scheme_data supported_schemes[] =
64 {
65   { "http://",  DEFAULT_HTTP_PORT,  1 },
66 #ifdef HAVE_SSL
67   { "https://", DEFAULT_HTTPS_PORT, 1 },
68 #endif
69   { "ftp://",   DEFAULT_FTP_PORT,   1 },
70
71   /* SCHEME_INVALID */
72   { NULL,       -1,                 0 }
73 };
74
75 /* Forward declarations: */
76
77 static int path_simplify PARAMS ((char *));
78 \f
79 /* Support for encoding and decoding of URL strings.  We determine
80    whether a character is unsafe through static table lookup.  This
81    code assumes ASCII character set and 8-bit chars.  */
82
83 enum {
84   /* rfc1738 reserved chars, preserved from encoding.  */
85   urlchr_reserved = 1,
86
87   /* rfc1738 unsafe chars, plus some more.  */
88   urlchr_unsafe   = 2
89 };
90
91 #define urlchr_test(c, mask) (urlchr_table[(unsigned char)(c)] & (mask))
92 #define URL_RESERVED_CHAR(c) urlchr_test(c, urlchr_reserved)
93 #define URL_UNSAFE_CHAR(c) urlchr_test(c, urlchr_unsafe)
94
95 /* Shorthands for the table: */
96 #define R  urlchr_reserved
97 #define U  urlchr_unsafe
98 #define RU R|U
99
100 const static unsigned char urlchr_table[256] =
101 {
102   U,  U,  U,  U,   U,  U,  U,  U,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
103   U,  U,  U,  U,   U,  U,  U,  U,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
104   U,  U,  U,  U,   U,  U,  U,  U,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
105   U,  U,  U,  U,   U,  U,  U,  U,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
106   U,  0,  U, RU,   0,  U,  R,  0,   /* SP  !   "   #    $   %   &   '   */
107   0,  0,  0,  R,   0,  0,  0,  R,   /* (   )   *   +    ,   -   .   /   */
108   0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
109   0,  0, RU,  R,   U,  R,  U,  R,   /* 8   9   :   ;    <   =   >   ?   */
110  RU,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
111   0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
112   0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
113   0,  0,  0, RU,   U, RU,  U,  0,   /* X   Y   Z   [    \   ]   ^   _   */
114   U,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
115   0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
116   0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
117   0,  0,  0,  U,   U,  U,  U,  U,   /* x   y   z   {    |   }   ~   DEL */
118
119   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
120   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
121   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
122   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
123
124   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
125   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
126   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
127   U, U, U, U,  U, U, U, U,  U, U, U, U,  U, U, U, U,
128 };
129 #undef R
130 #undef U
131 #undef RU
132
133 /* URL-unescape the string S.
134
135    This is done by transforming the sequences "%HH" to the character
136    represented by the hexadecimal digits HH.  If % is not followed by
137    two hexadecimal digits, it is inserted literally.
138
139    The transformation is done in place.  If you need the original
140    string intact, make a copy before calling this function.  */
141
142 static void
143 url_unescape (char *s)
144 {
145   char *t = s;                  /* t - tortoise */
146   char *h = s;                  /* h - hare     */
147
148   for (; *h; h++, t++)
149     {
150       if (*h != '%')
151         {
152         copychar:
153           *t = *h;
154         }
155       else
156         {
157           /* Do nothing if '%' is not followed by two hex digits. */
158           if (!h[1] || !h[2] || !(ISXDIGIT (h[1]) && ISXDIGIT (h[2])))
159             goto copychar;
160           *t = X2DIGITS_TO_NUM (h[1], h[2]);
161           h += 2;
162         }
163     }
164   *t = '\0';
165 }
166
167 /* The core of url_escape_* functions.  Escapes the characters that
168    match the provided mask in urlchr_table.
169
170    If ALLOW_PASSTHROUGH is non-zero, a string with no unsafe chars
171    will be returned unchanged.  If ALLOW_PASSTHROUGH is zero, a
172    freshly allocated string will be returned in all cases.  */
173
174 static char *
175 url_escape_1 (const char *s, unsigned char mask, int allow_passthrough)
176 {
177   const char *p1;
178   char *p2, *newstr;
179   int newlen;
180   int addition = 0;
181
182   for (p1 = s; *p1; p1++)
183     if (urlchr_test (*p1, mask))
184       addition += 2;            /* Two more characters (hex digits) */
185
186   if (!addition)
187     return allow_passthrough ? (char *)s : xstrdup (s);
188
189   newlen = (p1 - s) + addition;
190   newstr = (char *)xmalloc (newlen + 1);
191
192   p1 = s;
193   p2 = newstr;
194   while (*p1)
195     {
196       /* Quote the characters that match the test mask. */
197       if (urlchr_test (*p1, mask))
198         {
199           unsigned char c = *p1++;
200           *p2++ = '%';
201           *p2++ = XNUM_TO_DIGIT (c >> 4);
202           *p2++ = XNUM_TO_DIGIT (c & 0xf);
203         }
204       else
205         *p2++ = *p1++;
206     }
207   assert (p2 - newstr == newlen);
208   *p2 = '\0';
209
210   return newstr;
211 }
212
213 /* URL-escape the unsafe characters (see urlchr_table) in a given
214    string, returning a freshly allocated string.  */
215
216 char *
217 url_escape (const char *s)
218 {
219   return url_escape_1 (s, urlchr_unsafe, 0);
220 }
221
222 /* URL-escape the unsafe characters (see urlchr_table) in a given
223    string.  If no characters are unsafe, S is returned.  */
224
225 static char *
226 url_escape_allow_passthrough (const char *s)
227 {
228   return url_escape_1 (s, urlchr_unsafe, 1);
229 }
230 \f
231 enum copy_method { CM_DECODE, CM_ENCODE, CM_PASSTHROUGH };
232
233 /* Decide whether to encode, decode, or pass through the char at P.
234    This used to be a macro, but it got a little too convoluted.  */
235 static inline enum copy_method
236 decide_copy_method (const char *p)
237 {
238   if (*p == '%')
239     {
240       if (ISXDIGIT (*(p + 1)) && ISXDIGIT (*(p + 2)))
241         {
242           /* %xx sequence: decode it, unless it would decode to an
243              unsafe or a reserved char; in that case, leave it as
244              is. */
245           char preempt = X2DIGITS_TO_NUM (*(p + 1), *(p + 2));
246           if (URL_UNSAFE_CHAR (preempt) || URL_RESERVED_CHAR (preempt))
247             return CM_PASSTHROUGH;
248           else
249             return CM_DECODE;
250         }
251       else
252         /* Garbled %.. sequence: encode `%'. */
253         return CM_ENCODE;
254     }
255   else if (URL_UNSAFE_CHAR (*p) && !URL_RESERVED_CHAR (*p))
256     return CM_ENCODE;
257   else
258     return CM_PASSTHROUGH;
259 }
260
261 /* Translate a %-escaped (but possibly non-conformant) input string S
262    into a %-escaped (and conformant) output string.  If no characters
263    are encoded or decoded, return the same string S; otherwise, return
264    a freshly allocated string with the new contents.
265
266    After a URL has been run through this function, the protocols that
267    use `%' as the quote character can use the resulting string as-is,
268    while those that don't call url_unescape() to get to the intended
269    data.  This function is also stable: after an input string is
270    transformed the first time, all further transformations of the
271    result yield the same result string.
272
273    Let's discuss why this function is needed.
274
275    Imagine Wget is to retrieve `http://abc.xyz/abc def'.  Since a raw
276    space character would mess up the HTTP request, it needs to be
277    quoted, like this:
278
279        GET /abc%20def HTTP/1.0
280
281    It appears that the unsafe chars need to be quoted, for example
282    with url_escape.  But what if we're requested to download
283    `abc%20def'?  url_escape transforms "%" to "%25", which would leave
284    us with `abc%2520def'.  This is incorrect -- since %-escapes are
285    part of URL syntax, "%20" is the correct way to denote a literal
286    space on the Wget command line.  This leaves us in the conclusion
287    that in that case Wget should not call url_escape, but leave the
288    `%20' as is.
289
290    And what if the requested URI is `abc%20 def'?  If we call
291    url_escape, we end up with `/abc%2520%20def', which is almost
292    certainly not intended.  If we don't call url_escape, we are left
293    with the embedded space and cannot complete the request.  What the
294    user meant was for Wget to request `/abc%20%20def', and this is
295    where reencode_escapes kicks in.
296
297    Wget used to solve this by first decoding %-quotes, and then
298    encoding all the "unsafe" characters found in the resulting string.
299    This was wrong because it didn't preserve certain URL special
300    (reserved) characters.  For instance, URI containing "a%2B+b" (0x2b
301    == '+') would get translated to "a%2B%2Bb" or "a++b" depending on
302    whether we considered `+' reserved (it is).  One of these results
303    is inevitable because by the second step we would lose information
304    on whether the `+' was originally encoded or not.  Both results
305    were wrong because in CGI parameters + means space, while %2B means
306    literal plus.  reencode_escapes correctly translates the above to
307    "a%2B+b", i.e. returns the original string.
308
309    This function uses an algorithm proposed by Anon Sricharoenchai:
310
311    1. Encode all URL_UNSAFE and the "%" that are not followed by 2
312       hexdigits.
313
314    2. Decode all "%XX" except URL_UNSAFE, URL_RESERVED (";/?:@=&") and
315       "+".
316
317    ...except that this code conflates the two steps, and decides
318    whether to encode, decode, or pass through each character in turn.
319    The function still uses two passes, but their logic is the same --
320    the first pass exists merely for the sake of allocation.  Another
321    small difference is that we include `+' to URL_RESERVED.
322
323    Anon's test case:
324
325    "http://abc.xyz/%20%3F%%36%31%25aa% a?a=%61+a%2Ba&b=b%26c%3Dc"
326    ->
327    "http://abc.xyz/%20%3F%2561%25aa%25%20a?a=a+a%2Ba&b=b%26c%3Dc"
328
329    Simpler test cases:
330
331    "foo bar"         -> "foo%20bar"
332    "foo%20bar"       -> "foo%20bar"
333    "foo %20bar"      -> "foo%20%20bar"
334    "foo%%20bar"      -> "foo%25%20bar"       (0x25 == '%')
335    "foo%25%20bar"    -> "foo%25%20bar"
336    "foo%2%20bar"     -> "foo%252%20bar"
337    "foo+bar"         -> "foo+bar"            (plus is reserved!)
338    "foo%2b+bar"      -> "foo%2b+bar"  */
339
340 static char *
341 reencode_escapes (const char *s)
342 {
343   const char *p1;
344   char *newstr, *p2;
345   int oldlen, newlen;
346
347   int encode_count = 0;
348   int decode_count = 0;
349
350   /* First, pass through the string to see if there's anything to do,
351      and to calculate the new length.  */
352   for (p1 = s; *p1; p1++)
353     {
354       switch (decide_copy_method (p1))
355         {
356         case CM_ENCODE:
357           ++encode_count;
358           break;
359         case CM_DECODE:
360           ++decode_count;
361           break;
362         case CM_PASSTHROUGH:
363           break;
364         }
365     }
366
367   if (!encode_count && !decode_count)
368     /* The string is good as it is. */
369     return (char *)s;           /* C const model sucks. */
370
371   oldlen = p1 - s;
372   /* Each encoding adds two characters (hex digits), while each
373      decoding removes two characters.  */
374   newlen = oldlen + 2 * (encode_count - decode_count);
375   newstr = xmalloc (newlen + 1);
376
377   p1 = s;
378   p2 = newstr;
379
380   while (*p1)
381     {
382       switch (decide_copy_method (p1))
383         {
384         case CM_ENCODE:
385           {
386             unsigned char c = *p1++;
387             *p2++ = '%';
388             *p2++ = XNUM_TO_DIGIT (c >> 4);
389             *p2++ = XNUM_TO_DIGIT (c & 0xf);
390           }
391           break;
392         case CM_DECODE:
393           *p2++ = X2DIGITS_TO_NUM (p1[1], p1[2]);
394           p1 += 3;              /* skip %xx */
395           break;
396         case CM_PASSTHROUGH:
397           *p2++ = *p1++;
398         }
399     }
400   *p2 = '\0';
401   assert (p2 - newstr == newlen);
402   return newstr;
403 }
404 \f
405 /* Returns the scheme type if the scheme is supported, or
406    SCHEME_INVALID if not.  */
407
408 enum url_scheme
409 url_scheme (const char *url)
410 {
411   int i;
412
413   for (i = 0; supported_schemes[i].leading_string; i++)
414     if (0 == strncasecmp (url, supported_schemes[i].leading_string,
415                           strlen (supported_schemes[i].leading_string)))
416       {
417         if (supported_schemes[i].enabled)
418           return (enum url_scheme) i;
419         else
420           return SCHEME_INVALID;
421       }
422
423   return SCHEME_INVALID;
424 }
425
426 #define SCHEME_CHAR(ch) (ISALNUM (ch) || (ch) == '-' || (ch) == '+')
427
428 /* Return 1 if the URL begins with any "scheme", 0 otherwise.  As
429    currently implemented, it returns true if URL begins with
430    [-+a-zA-Z0-9]+: .  */
431
432 int
433 url_has_scheme (const char *url)
434 {
435   const char *p = url;
436
437   /* The first char must be a scheme char. */
438   if (!*p || !SCHEME_CHAR (*p))
439     return 0;
440   ++p;
441   /* Followed by 0 or more scheme chars. */
442   while (*p && SCHEME_CHAR (*p))
443     ++p;
444   /* Terminated by ':'. */
445   return *p == ':';
446 }
447
448 int
449 scheme_default_port (enum url_scheme scheme)
450 {
451   return supported_schemes[scheme].default_port;
452 }
453
454 void
455 scheme_disable (enum url_scheme scheme)
456 {
457   supported_schemes[scheme].enabled = 0;
458 }
459
460 /* Skip the username and password, if present here.  The function
461    should *not* be called with the complete URL, but with the part
462    right after the scheme.
463
464    If no username and password are found, return 0.  */
465
466 static int
467 url_skip_credentials (const char *url)
468 {
469   /* Look for '@' that comes before terminators, such as '/', '?',
470      '#', or ';'.  */
471   const char *p = (const char *)strpbrk (url, "@/?#;");
472   if (!p || *p != '@')
473     return 0;
474   return p + 1 - url;
475 }
476
477 /* Parse credentials contained in [BEG, END).  The region is expected
478    to have come from a URL and is unescaped.  */
479
480 static int
481 parse_credentials (const char *beg, const char *end, char **user, char **passwd)
482 {
483   char *colon;
484   const char *userend;
485
486   if (beg == end)
487     return 0;                   /* empty user name */
488
489   colon = memchr (beg, ':', end - beg);
490   if (colon == beg)
491     return 0;                   /* again empty user name */
492
493   if (colon)
494     {
495       *passwd = strdupdelim (colon + 1, end);
496       userend = colon;
497       url_unescape (*passwd);
498     }
499   else
500     {
501       *passwd = NULL;
502       userend = end;
503     }
504   *user = strdupdelim (beg, userend);
505   url_unescape (*user);
506   return 1;
507 }
508
509 /* Used by main.c: detect URLs written using the "shorthand" URL forms
510    popularized by Netscape and NcFTP.  HTTP shorthands look like this:
511
512    www.foo.com[:port]/dir/file   -> http://www.foo.com[:port]/dir/file
513    www.foo.com[:port]            -> http://www.foo.com[:port]
514
515    FTP shorthands look like this:
516
517    foo.bar.com:dir/file          -> ftp://foo.bar.com/dir/file
518    foo.bar.com:/absdir/file      -> ftp://foo.bar.com//absdir/file
519
520    If the URL needs not or cannot be rewritten, return NULL.  */
521
522 char *
523 rewrite_shorthand_url (const char *url)
524 {
525   const char *p;
526
527   if (url_has_scheme (url))
528     return NULL;
529
530   /* Look for a ':' or '/'.  The former signifies NcFTP syntax, the
531      latter Netscape.  */
532   for (p = url; *p && *p != ':' && *p != '/'; p++)
533     ;
534
535   if (p == url)
536     return NULL;
537
538   if (*p == ':')
539     {
540       const char *pp;
541       char *res;
542       /* If the characters after the colon and before the next slash
543          or end of string are all digits, it's HTTP.  */
544       int digits = 0;
545       for (pp = p + 1; ISDIGIT (*pp); pp++)
546         ++digits;
547       if (digits > 0 && (*pp == '/' || *pp == '\0'))
548         goto http;
549
550       /* Prepend "ftp://" to the entire URL... */
551       res = xmalloc (6 + strlen (url) + 1);
552       sprintf (res, "ftp://%s", url);
553       /* ...and replace ':' with '/'. */
554       res[6 + (p - url)] = '/';
555       return res;
556     }
557   else
558     {
559       char *res;
560     http:
561       /* Just prepend "http://" to what we have. */
562       res = xmalloc (7 + strlen (url) + 1);
563       sprintf (res, "http://%s", url);
564       return res;
565     }
566 }
567 \f
568 static void split_path PARAMS ((const char *, char **, char **));
569
570 /* Like strpbrk, with the exception that it returns the pointer to the
571    terminating zero (end-of-string aka "eos") if no matching character
572    is found.
573
574    Although I normally balk at Gcc-specific optimizations, it probably
575    makes sense here: glibc has optimizations that detect strpbrk being
576    called with literal string as ACCEPT and inline the search.  That
577    optimization is defeated if strpbrk is hidden within the call to
578    another function.  (And no, making strpbrk_or_eos inline doesn't
579    help because the check for literal accept is in the
580    preprocessor.)  */
581
582 #ifdef __GNUC__
583
584 #define strpbrk_or_eos(s, accept) ({            \
585   char *SOE_p = strpbrk (s, accept);            \
586   if (!SOE_p)                                   \
587     SOE_p = (char *)s + strlen (s);             \
588   SOE_p;                                        \
589 })
590
591 #else  /* not __GNUC__ */
592
593 static char *
594 strpbrk_or_eos (const char *s, const char *accept)
595 {
596   char *p = strpbrk (s, accept);
597   if (!p)
598     p = (char *)s + strlen (s);
599   return p;
600 }
601 #endif
602
603 /* Turn STR into lowercase; return non-zero if a character was
604    actually changed. */
605
606 static int
607 lowercase_str (char *str)
608 {
609   int change = 0;
610   for (; *str; str++)
611     if (ISUPPER (*str))
612       {
613         change = 1;
614         *str = TOLOWER (*str);
615       }
616   return change;
617 }
618
619 static char *parse_errors[] = {
620 #define PE_NO_ERROR                     0
621   "No error",
622 #define PE_UNSUPPORTED_SCHEME           1
623   "Unsupported scheme",
624 #define PE_EMPTY_HOST                   2
625   "Empty host",
626 #define PE_BAD_PORT_NUMBER              3
627   "Bad port number",
628 #define PE_INVALID_USER_NAME            4
629   "Invalid user name",
630 #define PE_UNTERMINATED_IPV6_ADDRESS    5
631   "Unterminated IPv6 numeric address",
632 #define PE_IPV6_NOT_SUPPORTED           6
633   "IPv6 addresses not supported",
634 #define PE_INVALID_IPV6_ADDRESS         7
635   "Invalid IPv6 numeric address"
636 };
637
638 #define SETERR(p, v) do {                       \
639   if (p)                                        \
640     *(p) = (v);                                 \
641 } while (0)
642
643 #ifdef ENABLE_IPV6
644 /* The following two functions were adapted from glibc. */
645
646 static int
647 is_valid_ipv4_address (const char *str, const char *end)
648 {
649   int saw_digit, octets;
650   int val;
651
652   saw_digit = 0;
653   octets = 0;
654   val = 0;
655
656   while (str < end) {
657     int ch = *str++;
658
659     if (ch >= '0' && ch <= '9') {
660       val = val * 10 + (ch - '0');
661
662       if (val > 255)
663         return 0;
664       if (saw_digit == 0) {
665         if (++octets > 4)
666           return 0;
667         saw_digit = 1;
668       }
669     } else if (ch == '.' && saw_digit == 1) {
670       if (octets == 4)
671         return 0;
672       val = 0;
673       saw_digit = 0;
674     } else
675       return 0;
676   }
677   if (octets < 4)
678     return 0;
679   
680   return 1;
681 }
682
683 static const int NS_INADDRSZ  = 4;
684 static const int NS_IN6ADDRSZ = 16;
685 static const int NS_INT16SZ   = 2;
686
687 static int
688 is_valid_ipv6_address (const char *str, const char *end)
689 {
690   static const char xdigits[] = "0123456789abcdef";
691   const char *curtok;
692   int tp;
693   const char *colonp;
694   int saw_xdigit;
695   unsigned int val;
696
697   tp = 0;
698   colonp = NULL;
699
700   if (str == end)
701     return 0;
702   
703   /* Leading :: requires some special handling. */
704   if (*str == ':')
705     {
706       ++str;
707       if (str == end || *str != ':')
708         return 0;
709     }
710
711   curtok = str;
712   saw_xdigit = 0;
713   val = 0;
714
715   while (str < end) {
716     int ch = *str++;
717     const char *pch;
718
719     /* if ch is a number, add it to val. */
720     pch = strchr(xdigits, ch);
721     if (pch != NULL) {
722       val <<= 4;
723       val |= (pch - xdigits);
724       if (val > 0xffff)
725         return 0;
726       saw_xdigit = 1;
727       continue;
728     }
729
730     /* if ch is a colon ... */
731     if (ch == ':') {
732       curtok = str;
733       if (saw_xdigit == 0) {
734         if (colonp != NULL)
735           return 0;
736         colonp = str + tp;
737         continue;
738       } else if (str == end) {
739         return 0;
740       }
741       if (tp > NS_IN6ADDRSZ - NS_INT16SZ)
742         return 0;
743       tp += NS_INT16SZ;
744       saw_xdigit = 0;
745       val = 0;
746       continue;
747     }
748
749     /* if ch is a dot ... */
750     if (ch == '.' && (tp <= NS_IN6ADDRSZ - NS_INADDRSZ) &&
751         is_valid_ipv4_address(curtok, end) == 1) {
752       tp += NS_INADDRSZ;
753       saw_xdigit = 0;
754       break;
755     }
756     
757     return 0;
758   }
759
760   if (saw_xdigit == 1) {
761     if (tp > NS_IN6ADDRSZ - NS_INT16SZ) 
762       return 0;
763     tp += NS_INT16SZ;
764   }
765
766   if (colonp != NULL) {
767     if (tp == NS_IN6ADDRSZ) 
768       return 0;
769     tp = NS_IN6ADDRSZ;
770   }
771
772   if (tp != NS_IN6ADDRSZ)
773     return 0;
774
775   return 1;
776 }
777 #endif
778
779 /* Parse a URL.
780
781    Return a new struct url if successful, NULL on error.  In case of
782    error, and if ERROR is not NULL, also set *ERROR to the appropriate
783    error code. */
784 struct url *
785 url_parse (const char *url, int *error)
786 {
787   struct url *u;
788   const char *p;
789   int path_modified, host_modified;
790
791   enum url_scheme scheme;
792
793   const char *uname_b,     *uname_e;
794   const char *host_b,      *host_e;
795   const char *path_b,      *path_e;
796   const char *params_b,    *params_e;
797   const char *query_b,     *query_e;
798   const char *fragment_b,  *fragment_e;
799
800   int port;
801   char *user = NULL, *passwd = NULL;
802
803   char *url_encoded;
804
805   scheme = url_scheme (url);
806   if (scheme == SCHEME_INVALID)
807     {
808       SETERR (error, PE_UNSUPPORTED_SCHEME);
809       return NULL;
810     }
811
812   url_encoded = reencode_escapes (url);
813   p = url_encoded;
814
815   p += strlen (supported_schemes[scheme].leading_string);
816   uname_b = p;
817   p += url_skip_credentials (p);
818   uname_e = p;
819
820   /* scheme://user:pass@host[:port]... */
821   /*                    ^              */
822
823   /* We attempt to break down the URL into the components path,
824      params, query, and fragment.  They are ordered like this:
825
826        scheme://host[:port][/path][;params][?query][#fragment]  */
827
828   params_b   = params_e   = NULL;
829   query_b    = query_e    = NULL;
830   fragment_b = fragment_e = NULL;
831
832   host_b = p;
833
834   if (*p == '[')
835     {
836       /* Handle IPv6 address inside square brackets.  Ideally we'd
837          just look for the terminating ']', but rfc2732 mandates
838          rejecting invalid IPv6 addresses.  */
839
840       /* The address begins after '['. */
841       host_b = p + 1;
842       host_e = strchr (host_b, ']');
843
844       if (!host_e)
845         {
846           SETERR (error, PE_UNTERMINATED_IPV6_ADDRESS);
847           return NULL;
848         }
849
850 #ifdef ENABLE_IPV6
851       /* Check if the IPv6 address is valid. */
852       if (!is_valid_ipv6_address(host_b, host_e))
853         {
854           SETERR (error, PE_INVALID_IPV6_ADDRESS);
855           return NULL;
856         }
857
858       /* Continue parsing after the closing ']'. */
859       p = host_e + 1;
860 #else
861       SETERR (error, PE_IPV6_NOT_SUPPORTED);
862       return NULL;
863 #endif
864     }
865   else
866     {
867       p = strpbrk_or_eos (p, ":/;?#");
868       host_e = p;
869     }
870
871   if (host_b == host_e)
872     {
873       SETERR (error, PE_EMPTY_HOST);
874       return NULL;
875     }
876
877   port = scheme_default_port (scheme);
878   if (*p == ':')
879     {
880       const char *port_b, *port_e, *pp;
881
882       /* scheme://host:port/tralala */
883       /*              ^             */
884       ++p;
885       port_b = p;
886       p = strpbrk_or_eos (p, "/;?#");
887       port_e = p;
888
889       if (port_b == port_e)
890         {
891           /* http://host:/whatever */
892           /*             ^         */
893           SETERR (error, PE_BAD_PORT_NUMBER);
894           return NULL;
895         }
896
897       for (port = 0, pp = port_b; pp < port_e; pp++)
898         {
899           if (!ISDIGIT (*pp))
900             {
901               /* http://host:12randomgarbage/blah */
902               /*               ^                  */
903               SETERR (error, PE_BAD_PORT_NUMBER);
904               return NULL;
905             }
906           
907           port = 10 * port + (*pp - '0');
908         }
909     }
910
911   if (*p == '/')
912     {
913       ++p;
914       path_b = p;
915       p = strpbrk_or_eos (p, ";?#");
916       path_e = p;
917     }
918   else
919     {
920       /* Path is not allowed not to exist. */
921       path_b = path_e = p;
922     }
923
924   if (*p == ';')
925     {
926       ++p;
927       params_b = p;
928       p = strpbrk_or_eos (p, "?#");
929       params_e = p;
930     }
931   if (*p == '?')
932     {
933       ++p;
934       query_b = p;
935       p = strpbrk_or_eos (p, "#");
936       query_e = p;
937
938       /* Hack that allows users to use '?' (a wildcard character) in
939          FTP URLs without it being interpreted as a query string
940          delimiter.  */
941       if (scheme == SCHEME_FTP)
942         {
943           query_b = query_e = NULL;
944           path_e = p;
945         }
946     }
947   if (*p == '#')
948     {
949       ++p;
950       fragment_b = p;
951       p += strlen (p);
952       fragment_e = p;
953     }
954   assert (*p == 0);
955
956   if (uname_b != uname_e)
957     {
958       /* http://user:pass@host */
959       /*        ^         ^    */
960       /*     uname_b   uname_e */
961       if (!parse_credentials (uname_b, uname_e - 1, &user, &passwd))
962         {
963           SETERR (error, PE_INVALID_USER_NAME);
964           return NULL;
965         }
966     }
967
968   u = (struct url *)xmalloc (sizeof (struct url));
969   memset (u, 0, sizeof (*u));
970
971   u->scheme = scheme;
972   u->host   = strdupdelim (host_b, host_e);
973   u->port   = port;
974   u->user   = user;
975   u->passwd = passwd;
976
977   u->path = strdupdelim (path_b, path_e);
978   path_modified = path_simplify (u->path);
979   split_path (u->path, &u->dir, &u->file);
980
981   host_modified = lowercase_str (u->host);
982
983   if (params_b)
984     u->params = strdupdelim (params_b, params_e);
985   if (query_b)
986     u->query = strdupdelim (query_b, query_e);
987   if (fragment_b)
988     u->fragment = strdupdelim (fragment_b, fragment_e);
989
990   if (path_modified || u->fragment || host_modified || path_b == path_e)
991     {
992       /* If we suspect that a transformation has rendered what
993          url_string might return different from URL_ENCODED, rebuild
994          u->url using url_string.  */
995       u->url = url_string (u, 0);
996
997       if (url_encoded != url)
998         xfree ((char *) url_encoded);
999     }
1000   else
1001     {
1002       if (url_encoded == url)
1003         u->url = xstrdup (url);
1004       else
1005         u->url = url_encoded;
1006     }
1007   url_encoded = NULL;
1008
1009   return u;
1010 }
1011
1012 const char *
1013 url_error (int error_code)
1014 {
1015   assert (error_code >= 0 && error_code < countof (parse_errors));
1016   return parse_errors[error_code];
1017 }
1018
1019 /* Split PATH into DIR and FILE.  PATH comes from the URL and is
1020    expected to be URL-escaped.
1021
1022    The path is split into directory (the part up to the last slash)
1023    and file (the part after the last slash), which are subsequently
1024    unescaped.  Examples:
1025
1026    PATH                 DIR           FILE
1027    "foo/bar/baz"        "foo/bar"     "baz"
1028    "foo/bar/"           "foo/bar"     ""
1029    "foo"                ""            "foo"
1030    "foo/bar/baz%2fqux"  "foo/bar"     "baz/qux" (!)
1031
1032    DIR and FILE are freshly allocated.  */
1033
1034 static void
1035 split_path (const char *path, char **dir, char **file)
1036 {
1037   char *last_slash = strrchr (path, '/');
1038   if (!last_slash)
1039     {
1040       *dir = xstrdup ("");
1041       *file = xstrdup (path);
1042     }
1043   else
1044     {
1045       *dir = strdupdelim (path, last_slash);
1046       *file = xstrdup (last_slash + 1);
1047     }
1048   url_unescape (*dir);
1049   url_unescape (*file);
1050 }
1051
1052 /* Note: URL's "full path" is the path with the query string and
1053    params appended.  The "fragment" (#foo) is intentionally ignored,
1054    but that might be changed.  For example, if the original URL was
1055    "http://host:port/foo/bar/baz;bullshit?querystring#uselessfragment",
1056    the full path will be "/foo/bar/baz;bullshit?querystring".  */
1057
1058 /* Return the length of the full path, without the terminating
1059    zero.  */
1060
1061 static int
1062 full_path_length (const struct url *url)
1063 {
1064   int len = 0;
1065
1066 #define FROB(el) if (url->el) len += 1 + strlen (url->el)
1067
1068   FROB (path);
1069   FROB (params);
1070   FROB (query);
1071
1072 #undef FROB
1073
1074   return len;
1075 }
1076
1077 /* Write out the full path. */
1078
1079 static void
1080 full_path_write (const struct url *url, char *where)
1081 {
1082 #define FROB(el, chr) do {                      \
1083   char *f_el = url->el;                         \
1084   if (f_el) {                                   \
1085     int l = strlen (f_el);                      \
1086     *where++ = chr;                             \
1087     memcpy (where, f_el, l);                    \
1088     where += l;                                 \
1089   }                                             \
1090 } while (0)
1091
1092   FROB (path, '/');
1093   FROB (params, ';');
1094   FROB (query, '?');
1095
1096 #undef FROB
1097 }
1098
1099 /* Public function for getting the "full path".  E.g. if u->path is
1100    "foo/bar" and u->query is "param=value", full_path will be
1101    "/foo/bar?param=value". */
1102
1103 char *
1104 url_full_path (const struct url *url)
1105 {
1106   int length = full_path_length (url);
1107   char *full_path = (char *)xmalloc(length + 1);
1108
1109   full_path_write (url, full_path);
1110   full_path[length] = '\0';
1111
1112   return full_path;
1113 }
1114
1115 /* Escape unsafe and reserved characters, except for the slash
1116    characters.  */
1117
1118 static char *
1119 url_escape_dir (const char *dir)
1120 {
1121   char *newdir = url_escape_1 (dir, urlchr_unsafe | urlchr_reserved, 1);
1122   char *h, *t;
1123   if (newdir == dir)
1124     return (char *)dir;
1125
1126   /* Unescape slashes in NEWDIR. */
1127
1128   h = newdir;                   /* hare */
1129   t = newdir;                   /* tortoise */
1130
1131   for (; *h; h++, t++)
1132     {
1133       /* url_escape_1 having converted '/' to "%2F" exactly. */
1134       if (*h == '%' && h[1] == '2' && h[2] == 'F')
1135         {
1136           *t = '/';
1137           h += 2;
1138         }
1139       else
1140         *t = *h;
1141     }
1142   *t = '\0';
1143
1144   return newdir;
1145 }
1146
1147 /* Sync u->path and u->url with u->dir and u->file.  Called after
1148    u->file or u->dir have been changed, typically by the FTP code.  */
1149
1150 static void
1151 sync_path (struct url *u)
1152 {
1153   char *newpath, *efile, *edir;
1154
1155   xfree (u->path);
1156
1157   /* u->dir and u->file are not escaped.  URL-escape them before
1158      reassembling them into u->path.  That way, if they contain
1159      separators like '?' or even if u->file contains slashes, the
1160      path will be correctly assembled.  (u->file can contain slashes
1161      if the URL specifies it with %2f, or if an FTP server returns
1162      it.)  */
1163   edir = url_escape_dir (u->dir);
1164   efile = url_escape_1 (u->file, urlchr_unsafe | urlchr_reserved, 1);
1165
1166   if (!*edir)
1167     newpath = xstrdup (efile);
1168   else
1169     {
1170       int dirlen = strlen (edir);
1171       int filelen = strlen (efile);
1172
1173       /* Copy "DIR/FILE" to newpath. */
1174       char *p = newpath = xmalloc (dirlen + 1 + filelen + 1);
1175       memcpy (p, edir, dirlen);
1176       p += dirlen;
1177       *p++ = '/';
1178       memcpy (p, efile, filelen);
1179       p += filelen;
1180       *p++ = '\0';
1181     }
1182
1183   u->path = newpath;
1184
1185   if (edir != u->dir)
1186     xfree (edir);
1187   if (efile != u->file)
1188     xfree (efile);
1189
1190   /* Regenerate u->url as well.  */
1191   xfree (u->url);
1192   u->url = url_string (u, 0);
1193 }
1194
1195 /* Mutators.  Code in ftp.c insists on changing u->dir and u->file.
1196    This way we can sync u->path and u->url when they get changed.  */
1197
1198 void
1199 url_set_dir (struct url *url, const char *newdir)
1200 {
1201   xfree (url->dir);
1202   url->dir = xstrdup (newdir);
1203   sync_path (url);
1204 }
1205
1206 void
1207 url_set_file (struct url *url, const char *newfile)
1208 {
1209   xfree (url->file);
1210   url->file = xstrdup (newfile);
1211   sync_path (url);
1212 }
1213
1214 void
1215 url_free (struct url *url)
1216 {
1217   xfree (url->host);
1218   xfree (url->path);
1219   xfree (url->url);
1220
1221   FREE_MAYBE (url->params);
1222   FREE_MAYBE (url->query);
1223   FREE_MAYBE (url->fragment);
1224   FREE_MAYBE (url->user);
1225   FREE_MAYBE (url->passwd);
1226
1227   xfree (url->dir);
1228   xfree (url->file);
1229
1230   xfree (url);
1231 }
1232 \f
1233 /* Create all the necessary directories for PATH (a file).  Calls
1234    mkdirhier() internally.  */
1235 int
1236 mkalldirs (const char *path)
1237 {
1238   const char *p;
1239   char *t;
1240   struct stat st;
1241   int res;
1242
1243   p = path + strlen (path);
1244   for (; *p != '/' && p != path; p--)
1245     ;
1246
1247   /* Don't create if it's just a file.  */
1248   if ((p == path) && (*p != '/'))
1249     return 0;
1250   t = strdupdelim (path, p);
1251
1252   /* Check whether the directory exists.  */
1253   if ((stat (t, &st) == 0))
1254     {
1255       if (S_ISDIR (st.st_mode))
1256         {
1257           xfree (t);
1258           return 0;
1259         }
1260       else
1261         {
1262           /* If the dir exists as a file name, remove it first.  This
1263              is *only* for Wget to work with buggy old CERN http
1264              servers.  Here is the scenario: When Wget tries to
1265              retrieve a directory without a slash, e.g.
1266              http://foo/bar (bar being a directory), CERN server will
1267              not redirect it too http://foo/bar/ -- it will generate a
1268              directory listing containing links to bar/file1,
1269              bar/file2, etc.  Wget will lose because it saves this
1270              HTML listing to a file `bar', so it cannot create the
1271              directory.  To work around this, if the file of the same
1272              name exists, we just remove it and create the directory
1273              anyway.  */
1274           DEBUGP (("Removing %s because of directory danger!\n", t));
1275           unlink (t);
1276         }
1277     }
1278   res = make_directory (t);
1279   if (res != 0)
1280     logprintf (LOG_NOTQUIET, "%s: %s", t, strerror (errno));
1281   xfree (t);
1282   return res;
1283 }
1284 \f
1285 /* Functions for constructing the file name out of URL components.  */
1286
1287 /* A growable string structure, used by url_file_name and friends.
1288    This should perhaps be moved to utils.c.
1289
1290    The idea is to have a convenient and efficient way to construct a
1291    string by having various functions append data to it.  Instead of
1292    passing the obligatory BASEVAR, SIZEVAR and TAILPOS to all the
1293    functions in questions, we pass the pointer to this struct.  */
1294
1295 struct growable {
1296   char *base;
1297   int size;
1298   int tail;
1299 };
1300
1301 /* Ensure that the string can accept APPEND_COUNT more characters past
1302    the current TAIL position.  If necessary, this will grow the string
1303    and update its allocated size.  If the string is already large
1304    enough to take TAIL+APPEND_COUNT characters, this does nothing.  */
1305 #define GROW(g, append_size) do {                                       \
1306   struct growable *G_ = g;                                              \
1307   DO_REALLOC (G_->base, G_->size, G_->tail + append_size, char);        \
1308 } while (0)
1309
1310 /* Return the tail position of the string. */
1311 #define TAIL(r) ((r)->base + (r)->tail)
1312
1313 /* Move the tail position by APPEND_COUNT characters. */
1314 #define TAIL_INCR(r, append_count) ((r)->tail += append_count)
1315
1316 /* Append the string STR to DEST.  NOTICE: the string in DEST is not
1317    terminated.  */
1318
1319 static void
1320 append_string (const char *str, struct growable *dest)
1321 {
1322   int l = strlen (str);
1323   GROW (dest, l);
1324   memcpy (TAIL (dest), str, l);
1325   TAIL_INCR (dest, l);
1326 }
1327
1328 /* Append CH to DEST.  For example, append_char (0, DEST)
1329    zero-terminates DEST.  */
1330
1331 static void
1332 append_char (char ch, struct growable *dest)
1333 {
1334   GROW (dest, 1);
1335   *TAIL (dest) = ch;
1336   TAIL_INCR (dest, 1);
1337 }
1338
1339 enum {
1340   filechr_not_unix    = 1,      /* unusable on Unix, / and \0 */
1341   filechr_not_windows = 2,      /* unusable on Windows, one of \|/<>?:*" */
1342   filechr_control     = 4,      /* a control character, e.g. 0-31 */
1343 };
1344
1345 #define FILE_CHAR_TEST(c, mask) (filechr_table[(unsigned char)(c)] & (mask))
1346
1347 /* Shorthands for the table: */
1348 #define U filechr_not_unix
1349 #define W filechr_not_windows
1350 #define C filechr_control
1351
1352 #define UW U|W
1353 #define UWC U|W|C
1354
1355 /* Table of characters unsafe under various conditions (see above).
1356
1357    Arguably we could also claim `%' to be unsafe, since we use it as
1358    the escape character.  If we ever want to be able to reliably
1359    translate file name back to URL, this would become important
1360    crucial.  Right now, it's better to be minimal in escaping.  */
1361
1362 const static unsigned char filechr_table[256] =
1363 {
1364 UWC,  C,  C,  C,   C,  C,  C,  C,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
1365   C,  C,  C,  C,   C,  C,  C,  C,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
1366   C,  C,  C,  C,   C,  C,  C,  C,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
1367   C,  C,  C,  C,   C,  C,  C,  C,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
1368   0,  0,  W,  0,   0,  0,  0,  0,   /* SP  !   "   #    $   %   &   '   */
1369   0,  0,  W,  0,   0,  0,  0, UW,   /* (   )   *   +    ,   -   .   /   */
1370   0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
1371   0,  0,  W,  0,   W,  0,  W,  W,   /* 8   9   :   ;    <   =   >   ?   */
1372   0,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
1373   0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
1374   0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
1375   0,  0,  0,  0,   W,  0,  0,  0,   /* X   Y   Z   [    \   ]   ^   _   */
1376   0,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
1377   0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
1378   0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
1379   0,  0,  0,  0,   0,  0,  0,  0,   /* x   y   z   {    |   }   ~   DEL */
1380
1381   C, C, C, C,  C, C, C, C,  C, C, C, C,  C, C, C, C, /* 128-143 */
1382   C, C, C, C,  C, C, C, C,  C, C, C, C,  C, C, C, C, /* 144-159 */
1383   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1384   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1385
1386   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1387   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1388   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1389   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1390 };
1391 #undef U
1392 #undef W
1393 #undef C
1394 #undef UW
1395 #undef UWC
1396
1397 /* FN_PORT_SEP is the separator between host and port in file names
1398    for non-standard port numbers.  On Unix this is normally ':', as in
1399    "www.xemacs.org:4001/index.html".  Under Windows, we set it to +
1400    because Windows can't handle ':' in file names.  */
1401 #define FN_PORT_SEP  (opt.restrict_files_os != restrict_windows ? ':' : '+')
1402
1403 /* FN_QUERY_SEP is the separator between the file name and the URL
1404    query, normally '?'.  Since Windows cannot handle '?' as part of
1405    file name, we use '@' instead there.  */
1406 #define FN_QUERY_SEP (opt.restrict_files_os != restrict_windows ? '?' : '@')
1407
1408 /* Quote path element, characters in [b, e), as file name, and append
1409    the quoted string to DEST.  Each character is quoted as per
1410    file_unsafe_char and the corresponding table.  */
1411
1412 static void
1413 append_uri_pathel (const char *b, const char *e, struct growable *dest)
1414 {
1415   char *pathel;
1416   int pathlen;
1417
1418   const char *p;
1419   int quoted, outlen;
1420
1421   int mask;
1422   if (opt.restrict_files_os == restrict_unix)
1423     mask = filechr_not_unix;
1424   else
1425     mask = filechr_not_windows;
1426   if (opt.restrict_files_ctrl)
1427     mask |= filechr_control;
1428
1429   /* Copy [b, e) to PATHEL and URL-unescape it. */
1430   BOUNDED_TO_ALLOCA (b, e, pathel);
1431   url_unescape (pathel);
1432   pathlen = strlen (pathel);
1433
1434   /* Go through PATHEL and check how many characters we'll need to
1435      add for file quoting. */
1436   quoted = 0;
1437   for (p = pathel; *p; p++)
1438     if (FILE_CHAR_TEST (*p, mask))
1439       ++quoted;
1440
1441   /* p - pathel is the string length.  Each quoted char means two
1442      additional characters in the string, hence 2*quoted.  */
1443   outlen = (p - pathel) + (2 * quoted);
1444   GROW (dest, outlen);
1445
1446   if (!quoted)
1447     {
1448       /* If there's nothing to quote, we don't need to go through the
1449          string the second time.  */
1450       memcpy (TAIL (dest), pathel, outlen);
1451     }
1452   else
1453     {
1454       char *q = TAIL (dest);
1455       for (p = pathel; *p; p++)
1456         {
1457           if (!FILE_CHAR_TEST (*p, mask))
1458             *q++ = *p;
1459           else
1460             {
1461               unsigned char ch = *p;
1462               *q++ = '%';
1463               *q++ = XNUM_TO_DIGIT (ch >> 4);
1464               *q++ = XNUM_TO_DIGIT (ch & 0xf);
1465             }
1466         }
1467       assert (q - TAIL (dest) == outlen);
1468     }
1469   TAIL_INCR (dest, outlen);
1470 }
1471
1472 /* Append to DEST the directory structure that corresponds the
1473    directory part of URL's path.  For example, if the URL is
1474    http://server/dir1/dir2/file, this appends "/dir1/dir2".
1475
1476    Each path element ("dir1" and "dir2" in the above example) is
1477    examined, url-unescaped, and re-escaped as file name element.
1478
1479    Additionally, it cuts as many directories from the path as
1480    specified by opt.cut_dirs.  For example, if opt.cut_dirs is 1, it
1481    will produce "bar" for the above example.  For 2 or more, it will
1482    produce "".
1483
1484    Each component of the path is quoted for use as file name.  */
1485
1486 static void
1487 append_dir_structure (const struct url *u, struct growable *dest)
1488 {
1489   char *pathel, *next;
1490   int cut = opt.cut_dirs;
1491
1492   /* Go through the path components, de-URL-quote them, and quote them
1493      (if necessary) as file names.  */
1494
1495   pathel = u->path;
1496   for (; (next = strchr (pathel, '/')) != NULL; pathel = next + 1)
1497     {
1498       if (cut-- > 0)
1499         continue;
1500       if (pathel == next)
1501         /* Ignore empty pathels.  path_simplify should remove
1502            occurrences of "//" from the path, but it has special cases
1503            for starting / which generates an empty pathel here.  */
1504         continue;
1505
1506       if (dest->tail)
1507         append_char ('/', dest);
1508       append_uri_pathel (pathel, next, dest);
1509     }
1510 }
1511
1512 /* Return a unique file name that matches the given URL as good as
1513    possible.  Does not create directories on the file system.  */
1514
1515 char *
1516 url_file_name (const struct url *u)
1517 {
1518   struct growable fnres;
1519
1520   char *u_file, *u_query;
1521   char *fname, *unique;
1522
1523   fnres.base = NULL;
1524   fnres.size = 0;
1525   fnres.tail = 0;
1526
1527   /* Start with the directory prefix, if specified. */
1528   if (opt.dir_prefix)
1529     append_string (opt.dir_prefix, &fnres);
1530
1531   /* If "dirstruct" is turned on (typically the case with -r), add
1532      the host and port (unless those have been turned off) and
1533      directory structure.  */
1534   if (opt.dirstruct)
1535     {
1536       if (opt.add_hostdir)
1537         {
1538           if (fnres.tail)
1539             append_char ('/', &fnres);
1540           append_string (u->host, &fnres);
1541           if (u->port != scheme_default_port (u->scheme))
1542             {
1543               char portstr[24];
1544               number_to_string (portstr, u->port);
1545               append_char (FN_PORT_SEP, &fnres);
1546               append_string (portstr, &fnres);
1547             }
1548         }
1549
1550       append_dir_structure (u, &fnres);
1551     }
1552
1553   /* Add the file name. */
1554   if (fnres.tail)
1555     append_char ('/', &fnres);
1556   u_file = *u->file ? u->file : "index.html";
1557   append_uri_pathel (u_file, u_file + strlen (u_file), &fnres);
1558
1559   /* Append "?query" to the file name. */
1560   u_query = u->query && *u->query ? u->query : NULL;
1561   if (u_query)
1562     {
1563       append_char (FN_QUERY_SEP, &fnres);
1564       append_uri_pathel (u_query, u_query + strlen (u_query), &fnres);
1565     }
1566
1567   /* Zero-terminate the file name. */
1568   append_char ('\0', &fnres);
1569
1570   fname = fnres.base;
1571
1572   /* Check the cases in which the unique extensions are not used:
1573      1) Clobbering is turned off (-nc).
1574      2) Retrieval with regetting.
1575      3) Timestamping is used.
1576      4) Hierarchy is built.
1577
1578      The exception is the case when file does exist and is a
1579      directory (see `mkalldirs' for explanation).  */
1580
1581   if ((opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct)
1582       && !(file_exists_p (fname) && !file_non_directory_p (fname)))
1583     return fname;
1584
1585   unique = unique_name (fname, 1);
1586   if (unique != fname)
1587     xfree (fname);
1588   return unique;
1589 }
1590
1591 /* Return the length of URL's path.  Path is considered to be
1592    terminated by one of '?', ';', '#', or by the end of the
1593    string.  */
1594 static int
1595 path_length (const char *url)
1596 {
1597   const char *q = strpbrk_or_eos (url, "?;#");
1598   return q - url;
1599 }
1600
1601 /* Find the last occurrence of character C in the range [b, e), or
1602    NULL, if none are present.  This is equivalent to strrchr(b, c),
1603    except that it accepts an END argument instead of requiring the
1604    string to be zero-terminated.  Why is there no memrchr()?  */
1605 static const char *
1606 find_last_char (const char *b, const char *e, char c)
1607 {
1608   for (; e > b; e--)
1609     if (*e == c)
1610       return e;
1611   return NULL;
1612 }
1613 \f
1614 /* Resolve "." and ".." elements of PATH by destructively modifying
1615    PATH and return non-zero if PATH has been modified, zero otherwise.
1616
1617    The algorithm is in spirit similar to the one described in rfc1808,
1618    although implemented differently, in one pass.  To recap, path
1619    elements containing only "." are removed, and ".." is taken to mean
1620    "back up one element".  Single leading and trailing slashes are
1621    preserved.
1622
1623    This function does not handle URL escapes explicitly.  If you're
1624    passing paths from URLs, make sure to unquote "%2e" and "%2E" to
1625    ".", so that this function can find the dots.  (Wget's URL parser
1626    calls reencode_escapes, which see.)
1627
1628    For example, "a/b/c/./../d/.." will yield "a/b/".  More exhaustive
1629    test examples are provided below.  If you change anything in this
1630    function, run test_path_simplify to make sure you haven't broken a
1631    test case.  */
1632
1633 static int
1634 path_simplify (char *path)
1635 {
1636   char *h, *t, *end;
1637
1638   /* Preserve the leading '/'. */
1639   if (path[0] == '/')
1640     ++path;
1641
1642   h = path;                     /* hare */
1643   t = path;                     /* tortoise */
1644   end = path + strlen (path);
1645
1646   while (h < end)
1647     {
1648       /* Hare should be at the beginning of a path element. */
1649
1650       if (h[0] == '.' && (h[1] == '/' || h[1] == '\0'))
1651         {
1652           /* Ignore "./". */
1653           h += 2;
1654         }
1655       else if (h[0] == '.' && h[1] == '.' && (h[2] == '/' || h[2] == '\0'))
1656         {
1657           /* Handle "../" by retreating the tortoise by one path
1658              element -- but not past beggining of PATH.  */
1659           if (t > path)
1660             {
1661               /* Move backwards until B hits the beginning of the
1662                  previous path element or the beginning of path. */
1663               for (--t; t > path && t[-1] != '/'; t--)
1664                 ;
1665             }
1666           h += 3;
1667         }
1668       else if (*h == '/')
1669         {
1670           /* Ignore empty path elements.  Supporting them well is hard
1671              (where do you save "http://x.com///y.html"?), and they
1672              don't bring any practical gain.  Plus, they break our
1673              filesystem-influenced assumptions: allowing them would
1674              make "x/y//../z" simplify to "x/y/z", whereas most people
1675              would expect "x/z".  */
1676           ++h;
1677         }
1678       else
1679         {
1680           /* A regular path element.  If H hasn't advanced past T,
1681              simply skip to the next path element.  Otherwise, copy
1682              the path element until the next slash.  */
1683           if (t == h)
1684             {
1685               /* Skip the path element, including the slash.  */
1686               while (h < end && *h != '/')
1687                 t++, h++;
1688               if (h < end)
1689                 t++, h++;
1690             }
1691           else
1692             {
1693               /* Copy the path element, including the final slash.  */
1694               while (h < end && *h != '/')
1695                 *t++ = *h++;
1696               if (h < end)
1697                 *t++ = *h++;
1698             }
1699         }
1700     }
1701
1702   if (t != h)
1703     *t = '\0';
1704
1705   return t != h;
1706 }
1707 \f
1708 /* Merge BASE with LINK and return the resulting URI.
1709
1710    Either of the URIs may be absolute or relative, complete with the
1711    host name, or path only.  This tries to reasonably handle all
1712    foreseeable cases.  It only employs minimal URL parsing, without
1713    knowledge of the specifics of schemes.
1714
1715    Perhaps this function should call path_simplify so that the callers
1716    don't have to call url_parse unconditionally.  */
1717
1718 char *
1719 uri_merge (const char *base, const char *link)
1720 {
1721   int linklength;
1722   const char *end;
1723   char *merge;
1724
1725   if (url_has_scheme (link))
1726     return xstrdup (link);
1727
1728   /* We may not examine BASE past END. */
1729   end = base + path_length (base);
1730   linklength = strlen (link);
1731
1732   if (!*link)
1733     {
1734       /* Empty LINK points back to BASE, query string and all. */
1735       return xstrdup (base);
1736     }
1737   else if (*link == '?')
1738     {
1739       /* LINK points to the same location, but changes the query
1740          string.  Examples: */
1741       /* uri_merge("path",         "?new") -> "path?new"     */
1742       /* uri_merge("path?foo",     "?new") -> "path?new"     */
1743       /* uri_merge("path?foo#bar", "?new") -> "path?new"     */
1744       /* uri_merge("path#foo",     "?new") -> "path?new"     */
1745       int baselength = end - base;
1746       merge = xmalloc (baselength + linklength + 1);
1747       memcpy (merge, base, baselength);
1748       memcpy (merge + baselength, link, linklength);
1749       merge[baselength + linklength] = '\0';
1750     }
1751   else if (*link == '#')
1752     {
1753       /* uri_merge("path",         "#new") -> "path#new"     */
1754       /* uri_merge("path#foo",     "#new") -> "path#new"     */
1755       /* uri_merge("path?foo",     "#new") -> "path?foo#new" */
1756       /* uri_merge("path?foo#bar", "#new") -> "path?foo#new" */
1757       int baselength;
1758       const char *end1 = strchr (base, '#');
1759       if (!end1)
1760         end1 = base + strlen (base);
1761       baselength = end1 - base;
1762       merge = xmalloc (baselength + linklength + 1);
1763       memcpy (merge, base, baselength);
1764       memcpy (merge + baselength, link, linklength);
1765       merge[baselength + linklength] = '\0';
1766     }
1767   else if (*link == '/' && *(link + 1) == '/')
1768     {
1769       /* LINK begins with "//" and so is a net path: we need to
1770          replace everything after (and including) the double slash
1771          with LINK. */
1772
1773       /* uri_merge("foo", "//new/bar")            -> "//new/bar"      */
1774       /* uri_merge("//old/foo", "//new/bar")      -> "//new/bar"      */
1775       /* uri_merge("http://old/foo", "//new/bar") -> "http://new/bar" */
1776
1777       int span;
1778       const char *slash;
1779       const char *start_insert;
1780
1781       /* Look for first slash. */
1782       slash = memchr (base, '/', end - base);
1783       /* If found slash and it is a double slash, then replace
1784          from this point, else default to replacing from the
1785          beginning.  */
1786       if (slash && *(slash + 1) == '/')
1787         start_insert = slash;
1788       else
1789         start_insert = base;
1790
1791       span = start_insert - base;
1792       merge = (char *)xmalloc (span + linklength + 1);
1793       if (span)
1794         memcpy (merge, base, span);
1795       memcpy (merge + span, link, linklength);
1796       merge[span + linklength] = '\0';
1797     }
1798   else if (*link == '/')
1799     {
1800       /* LINK is an absolute path: we need to replace everything
1801          after (and including) the FIRST slash with LINK.
1802
1803          So, if BASE is "http://host/whatever/foo/bar", and LINK is
1804          "/qux/xyzzy", our result should be
1805          "http://host/qux/xyzzy".  */
1806       int span;
1807       const char *slash;
1808       const char *start_insert = NULL; /* for gcc to shut up. */
1809       const char *pos = base;
1810       int seen_slash_slash = 0;
1811       /* We're looking for the first slash, but want to ignore
1812          double slash. */
1813     again:
1814       slash = memchr (pos, '/', end - pos);
1815       if (slash && !seen_slash_slash)
1816         if (*(slash + 1) == '/')
1817           {
1818             pos = slash + 2;
1819             seen_slash_slash = 1;
1820             goto again;
1821           }
1822
1823       /* At this point, SLASH is the location of the first / after
1824          "//", or the first slash altogether.  START_INSERT is the
1825          pointer to the location where LINK will be inserted.  When
1826          examining the last two examples, keep in mind that LINK
1827          begins with '/'. */
1828
1829       if (!slash && !seen_slash_slash)
1830         /* example: "foo" */
1831         /*           ^    */
1832         start_insert = base;
1833       else if (!slash && seen_slash_slash)
1834         /* example: "http://foo" */
1835         /*                     ^ */
1836         start_insert = end;
1837       else if (slash && !seen_slash_slash)
1838         /* example: "foo/bar" */
1839         /*           ^        */
1840         start_insert = base;
1841       else if (slash && seen_slash_slash)
1842         /* example: "http://something/" */
1843         /*                           ^  */
1844         start_insert = slash;
1845
1846       span = start_insert - base;
1847       merge = (char *)xmalloc (span + linklength + 1);
1848       if (span)
1849         memcpy (merge, base, span);
1850       memcpy (merge + span, link, linklength);
1851       merge[span + linklength] = '\0';
1852     }
1853   else
1854     {
1855       /* LINK is a relative URL: we need to replace everything
1856          after last slash (possibly empty) with LINK.
1857
1858          So, if BASE is "whatever/foo/bar", and LINK is "qux/xyzzy",
1859          our result should be "whatever/foo/qux/xyzzy".  */
1860       int need_explicit_slash = 0;
1861       int span;
1862       const char *start_insert;
1863       const char *last_slash = find_last_char (base, end, '/');
1864       if (!last_slash)
1865         {
1866           /* No slash found at all.  Append LINK to what we have,
1867              but we'll need a slash as a separator.
1868
1869              Example: if base == "foo" and link == "qux/xyzzy", then
1870              we cannot just append link to base, because we'd get
1871              "fooqux/xyzzy", whereas what we want is
1872              "foo/qux/xyzzy".
1873
1874              To make sure the / gets inserted, we set
1875              need_explicit_slash to 1.  We also set start_insert
1876              to end + 1, so that the length calculations work out
1877              correctly for one more (slash) character.  Accessing
1878              that character is fine, since it will be the
1879              delimiter, '\0' or '?'.  */
1880           /* example: "foo?..." */
1881           /*               ^    ('?' gets changed to '/') */
1882           start_insert = end + 1;
1883           need_explicit_slash = 1;
1884         }
1885       else if (last_slash && last_slash >= base + 2
1886                && last_slash[-2] == ':' && last_slash[-1] == '/')
1887         {
1888           /* example: http://host"  */
1889           /*                      ^ */
1890           start_insert = end + 1;
1891           need_explicit_slash = 1;
1892         }
1893       else
1894         {
1895           /* example: "whatever/foo/bar" */
1896           /*                        ^    */
1897           start_insert = last_slash + 1;
1898         }
1899
1900       span = start_insert - base;
1901       merge = (char *)xmalloc (span + linklength + 1);
1902       if (span)
1903         memcpy (merge, base, span);
1904       if (need_explicit_slash)
1905         merge[span - 1] = '/';
1906       memcpy (merge + span, link, linklength);
1907       merge[span + linklength] = '\0';
1908     }
1909
1910   return merge;
1911 }
1912 \f
1913 #define APPEND(p, s) do {                       \
1914   int len = strlen (s);                         \
1915   memcpy (p, s, len);                           \
1916   p += len;                                     \
1917 } while (0)
1918
1919 /* Use this instead of password when the actual password is supposed
1920    to be hidden.  We intentionally use a generic string without giving
1921    away the number of characters in the password, like previous
1922    versions did.  */
1923 #define HIDDEN_PASSWORD "*password*"
1924
1925 /* Recreate the URL string from the data in URL.
1926
1927    If HIDE is non-zero (as it is when we're calling this on a URL we
1928    plan to print, but not when calling it to canonicalize a URL for
1929    use within the program), password will be hidden.  Unsafe
1930    characters in the URL will be quoted.  */
1931
1932 char *
1933 url_string (const struct url *url, int hide_password)
1934 {
1935   int size;
1936   char *result, *p;
1937   char *quoted_user = NULL, *quoted_passwd = NULL;
1938
1939   int scheme_port  = supported_schemes[url->scheme].default_port;
1940   char *scheme_str = supported_schemes[url->scheme].leading_string;
1941   int fplen = full_path_length (url);
1942
1943   int brackets_around_host = 0;
1944
1945   assert (scheme_str != NULL);
1946
1947   /* Make sure the user name and password are quoted. */
1948   if (url->user)
1949     {
1950       quoted_user = url_escape_allow_passthrough (url->user);
1951       if (url->passwd)
1952         {
1953           if (hide_password)
1954             quoted_passwd = HIDDEN_PASSWORD;
1955           else
1956             quoted_passwd = url_escape_allow_passthrough (url->passwd);
1957         }
1958     }
1959
1960   if (strchr (url->host, ':'))
1961     brackets_around_host = 1;
1962
1963   size = (strlen (scheme_str)
1964           + strlen (url->host)
1965           + (brackets_around_host ? 2 : 0)
1966           + fplen
1967           + 1);
1968   if (url->port != scheme_port)
1969     size += 1 + numdigit (url->port);
1970   if (quoted_user)
1971     {
1972       size += 1 + strlen (quoted_user);
1973       if (quoted_passwd)
1974         size += 1 + strlen (quoted_passwd);
1975     }
1976
1977   p = result = xmalloc (size);
1978
1979   APPEND (p, scheme_str);
1980   if (quoted_user)
1981     {
1982       APPEND (p, quoted_user);
1983       if (quoted_passwd)
1984         {
1985           *p++ = ':';
1986           APPEND (p, quoted_passwd);
1987         }
1988       *p++ = '@';
1989     }
1990
1991   if (brackets_around_host)
1992     *p++ = '[';
1993   APPEND (p, url->host);
1994   if (brackets_around_host)
1995     *p++ = ']';
1996   if (url->port != scheme_port)
1997     {
1998       *p++ = ':';
1999       p = number_to_string (p, url->port);
2000     }
2001
2002   full_path_write (url, p);
2003   p += fplen;
2004   *p++ = '\0';
2005
2006   assert (p - result == size);
2007
2008   if (quoted_user && quoted_user != url->user)
2009     xfree (quoted_user);
2010   if (quoted_passwd && !hide_password
2011       && quoted_passwd != url->passwd)
2012     xfree (quoted_passwd);
2013
2014   return result;
2015 }
2016 \f
2017 /* Return non-zero if scheme a is similar to scheme b.
2018  
2019    Schemes are similar if they are equal.  If SSL is supported, schemes
2020    are also similar if one is http (SCHEME_HTTP) and the other is https
2021    (SCHEME_HTTPS).  */
2022 int
2023 schemes_are_similar_p (enum url_scheme a, enum url_scheme b)
2024 {
2025   if (a == b)
2026     return 1;
2027 #ifdef HAVE_SSL
2028   if ((a == SCHEME_HTTP && b == SCHEME_HTTPS)
2029       || (a == SCHEME_HTTPS && b == SCHEME_HTTP))
2030     return 1;
2031 #endif
2032   return 0;
2033 }
2034 \f
2035 #if 0
2036 /* Debugging and testing support for path_simplify. */
2037
2038 /* Debug: run path_simplify on PATH and return the result in a new
2039    string.  Useful for calling from the debugger.  */
2040 static char *
2041 ps (char *path)
2042 {
2043   char *copy = xstrdup (path);
2044   path_simplify (copy);
2045   return copy;
2046 }
2047
2048 static void
2049 run_test (char *test, char *expected_result, int expected_change)
2050 {
2051   char *test_copy = xstrdup (test);
2052   int modified = path_simplify (test_copy);
2053
2054   if (0 != strcmp (test_copy, expected_result))
2055     {
2056       printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
2057               test, expected_result, test_copy);
2058     }
2059   if (modified != expected_change)
2060     {
2061       if (expected_change == 1)
2062         printf ("Expected no modification with path_simplify(\"%s\").\n",
2063                 test);
2064       else
2065         printf ("Expected modification with path_simplify(\"%s\").\n",
2066                 test);
2067     }
2068   xfree (test_copy);
2069 }
2070
2071 static void
2072 test_path_simplify (void)
2073 {
2074   static struct {
2075     char *test, *result;
2076     int should_modify;
2077   } tests[] = {
2078     { "",               "",             0 },
2079     { ".",              "",             1 },
2080     { "..",             "",             1 },
2081     { "foo",            "foo",          0 },
2082     { "foo/bar",        "foo/bar",      0 },
2083     { "foo///bar",      "foo/bar",      1 },
2084     { "foo/.",          "foo/",         1 },
2085     { "foo/./",         "foo/",         1 },
2086     { "foo./",          "foo./",        0 },
2087     { "foo/../bar",     "bar",          1 },
2088     { "foo/../bar/",    "bar/",         1 },
2089     { "foo/bar/..",     "foo/",         1 },
2090     { "foo/bar/../x",   "foo/x",        1 },
2091     { "foo/bar/../x/",  "foo/x/",       1 },
2092     { "foo/..",         "",             1 },
2093     { "foo/../..",      "",             1 },
2094     { "a/b/../../c",    "c",            1 },
2095     { "./a/../b",       "b",            1 }
2096   };
2097   int i;
2098
2099   for (i = 0; i < countof (tests); i++)
2100     {
2101       char *test = tests[i].test;
2102       char *expected_result = tests[i].result;
2103       int   expected_change = tests[i].should_modify;
2104       run_test (test, expected_result, expected_change);
2105     }
2106
2107   /* Now run all the tests with a leading slash before the test case,
2108      to prove that the slash is being preserved.  */
2109   for (i = 0; i < countof (tests); i++)
2110     {
2111       char *test, *expected_result;
2112       int expected_change = tests[i].should_modify;
2113
2114       test = xmalloc (1 + strlen (tests[i].test) + 1);
2115       sprintf (test, "/%s", tests[i].test);
2116
2117       expected_result = xmalloc (1 + strlen (tests[i].result) + 1);
2118       sprintf (expected_result, "/%s", tests[i].result);
2119
2120       run_test (test, expected_result, expected_change);
2121
2122       xfree (test);
2123       xfree (expected_result);
2124     }
2125 }
2126 #endif