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