]> sjero.net Git - wget/blob - src/url.c
[svn] Doc typo fix.
[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   N_("No error"),
622 #define PE_UNSUPPORTED_SCHEME           1
623   N_("Unsupported scheme"),
624 #define PE_EMPTY_HOST                   2
625   N_("Empty host"),
626 #define PE_BAD_PORT_NUMBER              3
627   N_("Bad port number"),
628 #define PE_INVALID_USER_NAME            4
629   N_("Invalid user name"),
630 #define PE_UNTERMINATED_IPV6_ADDRESS    5
631   N_("Unterminated IPv6 numeric address"),
632 #define PE_IPV6_NOT_SUPPORTED           6
633   N_("IPv6 addresses not supported"),
634 #define PE_INVALID_IPV6_ADDRESS         7
635   N_("Invalid IPv6 numeric address")
636 };
637
638 #ifdef ENABLE_IPV6
639 /* The following two functions were adapted from glibc. */
640
641 static int
642 is_valid_ipv4_address (const char *str, const char *end)
643 {
644   int saw_digit, octets;
645   int val;
646
647   saw_digit = 0;
648   octets = 0;
649   val = 0;
650
651   while (str < end) {
652     int ch = *str++;
653
654     if (ch >= '0' && ch <= '9') {
655       val = val * 10 + (ch - '0');
656
657       if (val > 255)
658         return 0;
659       if (saw_digit == 0) {
660         if (++octets > 4)
661           return 0;
662         saw_digit = 1;
663       }
664     } else if (ch == '.' && saw_digit == 1) {
665       if (octets == 4)
666         return 0;
667       val = 0;
668       saw_digit = 0;
669     } else
670       return 0;
671   }
672   if (octets < 4)
673     return 0;
674   
675   return 1;
676 }
677
678 static const int NS_INADDRSZ  = 4;
679 static const int NS_IN6ADDRSZ = 16;
680 static const int NS_INT16SZ   = 2;
681
682 static int
683 is_valid_ipv6_address (const char *str, const char *end)
684 {
685   static const char xdigits[] = "0123456789abcdef";
686   const char *curtok;
687   int tp;
688   const char *colonp;
689   int saw_xdigit;
690   unsigned int val;
691
692   tp = 0;
693   colonp = NULL;
694
695   if (str == end)
696     return 0;
697   
698   /* Leading :: requires some special handling. */
699   if (*str == ':')
700     {
701       ++str;
702       if (str == end || *str != ':')
703         return 0;
704     }
705
706   curtok = str;
707   saw_xdigit = 0;
708   val = 0;
709
710   while (str < end) {
711     int ch = *str++;
712     const char *pch;
713
714     /* if ch is a number, add it to val. */
715     pch = strchr(xdigits, ch);
716     if (pch != NULL) {
717       val <<= 4;
718       val |= (pch - xdigits);
719       if (val > 0xffff)
720         return 0;
721       saw_xdigit = 1;
722       continue;
723     }
724
725     /* if ch is a colon ... */
726     if (ch == ':') {
727       curtok = str;
728       if (saw_xdigit == 0) {
729         if (colonp != NULL)
730           return 0;
731         colonp = str + tp;
732         continue;
733       } else if (str == end) {
734         return 0;
735       }
736       if (tp > NS_IN6ADDRSZ - NS_INT16SZ)
737         return 0;
738       tp += NS_INT16SZ;
739       saw_xdigit = 0;
740       val = 0;
741       continue;
742     }
743
744     /* if ch is a dot ... */
745     if (ch == '.' && (tp <= NS_IN6ADDRSZ - NS_INADDRSZ) &&
746         is_valid_ipv4_address(curtok, end) == 1) {
747       tp += NS_INADDRSZ;
748       saw_xdigit = 0;
749       break;
750     }
751     
752     return 0;
753   }
754
755   if (saw_xdigit == 1) {
756     if (tp > NS_IN6ADDRSZ - NS_INT16SZ) 
757       return 0;
758     tp += NS_INT16SZ;
759   }
760
761   if (colonp != NULL) {
762     if (tp == NS_IN6ADDRSZ) 
763       return 0;
764     tp = NS_IN6ADDRSZ;
765   }
766
767   if (tp != NS_IN6ADDRSZ)
768     return 0;
769
770   return 1;
771 }
772 #endif
773
774 /* Parse a URL.
775
776    Return a new struct url if successful, NULL on error.  In case of
777    error, and if ERROR is not NULL, also set *ERROR to the appropriate
778    error code. */
779 struct url *
780 url_parse (const char *url, int *error)
781 {
782   struct url *u;
783   const char *p;
784   int path_modified, host_modified;
785
786   enum url_scheme scheme;
787
788   const char *uname_b,     *uname_e;
789   const char *host_b,      *host_e;
790   const char *path_b,      *path_e;
791   const char *params_b,    *params_e;
792   const char *query_b,     *query_e;
793   const char *fragment_b,  *fragment_e;
794
795   int port;
796   char *user = NULL, *passwd = NULL;
797
798   char *url_encoded = NULL;
799
800   int error_code;
801
802   scheme = url_scheme (url);
803   if (scheme == SCHEME_INVALID)
804     {
805       error_code = PE_UNSUPPORTED_SCHEME;
806       goto error;
807     }
808
809   url_encoded = reencode_escapes (url);
810   p = url_encoded;
811
812   p += strlen (supported_schemes[scheme].leading_string);
813   uname_b = p;
814   p += url_skip_credentials (p);
815   uname_e = p;
816
817   /* scheme://user:pass@host[:port]... */
818   /*                    ^              */
819
820   /* We attempt to break down the URL into the components path,
821      params, query, and fragment.  They are ordered like this:
822
823        scheme://host[:port][/path][;params][?query][#fragment]  */
824
825   params_b   = params_e   = NULL;
826   query_b    = query_e    = NULL;
827   fragment_b = fragment_e = NULL;
828
829   host_b = p;
830
831   if (*p == '[')
832     {
833       /* Handle IPv6 address inside square brackets.  Ideally we'd
834          just look for the terminating ']', but rfc2732 mandates
835          rejecting invalid IPv6 addresses.  */
836
837       /* The address begins after '['. */
838       host_b = p + 1;
839       host_e = strchr (host_b, ']');
840
841       if (!host_e)
842         {
843           error_code = PE_UNTERMINATED_IPV6_ADDRESS;
844           goto error;
845         }
846
847 #ifdef ENABLE_IPV6
848       /* Check if the IPv6 address is valid. */
849       if (!is_valid_ipv6_address(host_b, host_e))
850         {
851           error_code = PE_INVALID_IPV6_ADDRESS;
852           goto error;
853         }
854
855       /* Continue parsing after the closing ']'. */
856       p = host_e + 1;
857 #else
858       error_code = PE_IPV6_NOT_SUPPORTED;
859       goto error;
860 #endif
861     }
862   else
863     {
864       p = strpbrk_or_eos (p, ":/;?#");
865       host_e = p;
866     }
867
868   if (host_b == host_e)
869     {
870       error_code = PE_EMPTY_HOST;
871       goto error;
872     }
873
874   port = scheme_default_port (scheme);
875   if (*p == ':')
876     {
877       const char *port_b, *port_e, *pp;
878
879       /* scheme://host:port/tralala */
880       /*              ^             */
881       ++p;
882       port_b = p;
883       p = strpbrk_or_eos (p, "/;?#");
884       port_e = p;
885
886       if (port_b == port_e)
887         {
888           /* http://host:/whatever */
889           /*             ^         */
890           error_code = PE_BAD_PORT_NUMBER;
891           goto error;
892         }
893
894       for (port = 0, pp = port_b; pp < port_e; pp++)
895         {
896           if (!ISDIGIT (*pp))
897             {
898               /* http://host:12randomgarbage/blah */
899               /*               ^                  */
900               error_code = PE_BAD_PORT_NUMBER;
901               goto error;
902             }
903           
904           port = 10 * port + (*pp - '0');
905         }
906     }
907
908   if (*p == '/')
909     {
910       ++p;
911       path_b = p;
912       p = strpbrk_or_eos (p, ";?#");
913       path_e = p;
914     }
915   else
916     {
917       /* Path is not allowed not to exist. */
918       path_b = path_e = p;
919     }
920
921   if (*p == ';')
922     {
923       ++p;
924       params_b = p;
925       p = strpbrk_or_eos (p, "?#");
926       params_e = p;
927     }
928   if (*p == '?')
929     {
930       ++p;
931       query_b = p;
932       p = strpbrk_or_eos (p, "#");
933       query_e = p;
934
935       /* Hack that allows users to use '?' (a wildcard character) in
936          FTP URLs without it being interpreted as a query string
937          delimiter.  */
938       if (scheme == SCHEME_FTP)
939         {
940           query_b = query_e = NULL;
941           path_e = p;
942         }
943     }
944   if (*p == '#')
945     {
946       ++p;
947       fragment_b = p;
948       p += strlen (p);
949       fragment_e = p;
950     }
951   assert (*p == 0);
952
953   if (uname_b != uname_e)
954     {
955       /* http://user:pass@host */
956       /*        ^         ^    */
957       /*     uname_b   uname_e */
958       if (!parse_credentials (uname_b, uname_e - 1, &user, &passwd))
959         {
960           error_code = PE_INVALID_USER_NAME;
961           goto error;
962         }
963     }
964
965   u = (struct url *)xmalloc (sizeof (struct url));
966   memset (u, 0, sizeof (*u));
967
968   u->scheme = scheme;
969   u->host   = strdupdelim (host_b, host_e);
970   u->port   = port;
971   u->user   = user;
972   u->passwd = passwd;
973
974   u->path = strdupdelim (path_b, path_e);
975   path_modified = path_simplify (u->path);
976   split_path (u->path, &u->dir, &u->file);
977
978   host_modified = lowercase_str (u->host);
979
980   if (params_b)
981     u->params = strdupdelim (params_b, params_e);
982   if (query_b)
983     u->query = strdupdelim (query_b, query_e);
984   if (fragment_b)
985     u->fragment = strdupdelim (fragment_b, fragment_e);
986
987   if (path_modified || u->fragment || host_modified || path_b == path_e)
988     {
989       /* If we suspect that a transformation has rendered what
990          url_string might return different from URL_ENCODED, rebuild
991          u->url using url_string.  */
992       u->url = url_string (u, 0);
993
994       if (url_encoded != url)
995         xfree ((char *) url_encoded);
996     }
997   else
998     {
999       if (url_encoded == url)
1000         u->url = xstrdup (url);
1001       else
1002         u->url = url_encoded;
1003     }
1004   url_encoded = NULL;
1005
1006   return u;
1007
1008  error:
1009   /* Cleanup in case of error: */
1010   if (url_encoded && url_encoded != url)
1011     xfree (url_encoded);
1012
1013   /* Transmit the error code to the caller, if the caller wants to
1014      know.  */
1015   if (error)
1016     *error = error_code;
1017   return NULL;
1018 }
1019
1020 /* Return the error message string from ERROR_CODE, which should have
1021    been retrieved from url_parse.  The error message is translated.  */
1022
1023 const char *
1024 url_error (int error_code)
1025 {
1026   assert (error_code >= 0 && error_code < countof (parse_errors));
1027   return _(parse_errors[error_code]);
1028 }
1029
1030 /* Split PATH into DIR and FILE.  PATH comes from the URL and is
1031    expected to be URL-escaped.
1032
1033    The path is split into directory (the part up to the last slash)
1034    and file (the part after the last slash), which are subsequently
1035    unescaped.  Examples:
1036
1037    PATH                 DIR           FILE
1038    "foo/bar/baz"        "foo/bar"     "baz"
1039    "foo/bar/"           "foo/bar"     ""
1040    "foo"                ""            "foo"
1041    "foo/bar/baz%2fqux"  "foo/bar"     "baz/qux" (!)
1042
1043    DIR and FILE are freshly allocated.  */
1044
1045 static void
1046 split_path (const char *path, char **dir, char **file)
1047 {
1048   char *last_slash = strrchr (path, '/');
1049   if (!last_slash)
1050     {
1051       *dir = xstrdup ("");
1052       *file = xstrdup (path);
1053     }
1054   else
1055     {
1056       *dir = strdupdelim (path, last_slash);
1057       *file = xstrdup (last_slash + 1);
1058     }
1059   url_unescape (*dir);
1060   url_unescape (*file);
1061 }
1062
1063 /* Note: URL's "full path" is the path with the query string and
1064    params appended.  The "fragment" (#foo) is intentionally ignored,
1065    but that might be changed.  For example, if the original URL was
1066    "http://host:port/foo/bar/baz;bullshit?querystring#uselessfragment",
1067    the full path will be "/foo/bar/baz;bullshit?querystring".  */
1068
1069 /* Return the length of the full path, without the terminating
1070    zero.  */
1071
1072 static int
1073 full_path_length (const struct url *url)
1074 {
1075   int len = 0;
1076
1077 #define FROB(el) if (url->el) len += 1 + strlen (url->el)
1078
1079   FROB (path);
1080   FROB (params);
1081   FROB (query);
1082
1083 #undef FROB
1084
1085   return len;
1086 }
1087
1088 /* Write out the full path. */
1089
1090 static void
1091 full_path_write (const struct url *url, char *where)
1092 {
1093 #define FROB(el, chr) do {                      \
1094   char *f_el = url->el;                         \
1095   if (f_el) {                                   \
1096     int l = strlen (f_el);                      \
1097     *where++ = chr;                             \
1098     memcpy (where, f_el, l);                    \
1099     where += l;                                 \
1100   }                                             \
1101 } while (0)
1102
1103   FROB (path, '/');
1104   FROB (params, ';');
1105   FROB (query, '?');
1106
1107 #undef FROB
1108 }
1109
1110 /* Public function for getting the "full path".  E.g. if u->path is
1111    "foo/bar" and u->query is "param=value", full_path will be
1112    "/foo/bar?param=value". */
1113
1114 char *
1115 url_full_path (const struct url *url)
1116 {
1117   int length = full_path_length (url);
1118   char *full_path = (char *)xmalloc(length + 1);
1119
1120   full_path_write (url, full_path);
1121   full_path[length] = '\0';
1122
1123   return full_path;
1124 }
1125
1126 /* Escape unsafe and reserved characters, except for the slash
1127    characters.  */
1128
1129 static char *
1130 url_escape_dir (const char *dir)
1131 {
1132   char *newdir = url_escape_1 (dir, urlchr_unsafe | urlchr_reserved, 1);
1133   char *h, *t;
1134   if (newdir == dir)
1135     return (char *)dir;
1136
1137   /* Unescape slashes in NEWDIR. */
1138
1139   h = newdir;                   /* hare */
1140   t = newdir;                   /* tortoise */
1141
1142   for (; *h; h++, t++)
1143     {
1144       /* url_escape_1 having converted '/' to "%2F" exactly. */
1145       if (*h == '%' && h[1] == '2' && h[2] == 'F')
1146         {
1147           *t = '/';
1148           h += 2;
1149         }
1150       else
1151         *t = *h;
1152     }
1153   *t = '\0';
1154
1155   return newdir;
1156 }
1157
1158 /* Sync u->path and u->url with u->dir and u->file.  Called after
1159    u->file or u->dir have been changed, typically by the FTP code.  */
1160
1161 static void
1162 sync_path (struct url *u)
1163 {
1164   char *newpath, *efile, *edir;
1165
1166   xfree (u->path);
1167
1168   /* u->dir and u->file are not escaped.  URL-escape them before
1169      reassembling them into u->path.  That way, if they contain
1170      separators like '?' or even if u->file contains slashes, the
1171      path will be correctly assembled.  (u->file can contain slashes
1172      if the URL specifies it with %2f, or if an FTP server returns
1173      it.)  */
1174   edir = url_escape_dir (u->dir);
1175   efile = url_escape_1 (u->file, urlchr_unsafe | urlchr_reserved, 1);
1176
1177   if (!*edir)
1178     newpath = xstrdup (efile);
1179   else
1180     {
1181       int dirlen = strlen (edir);
1182       int filelen = strlen (efile);
1183
1184       /* Copy "DIR/FILE" to newpath. */
1185       char *p = newpath = xmalloc (dirlen + 1 + filelen + 1);
1186       memcpy (p, edir, dirlen);
1187       p += dirlen;
1188       *p++ = '/';
1189       memcpy (p, efile, filelen);
1190       p += filelen;
1191       *p++ = '\0';
1192     }
1193
1194   u->path = newpath;
1195
1196   if (edir != u->dir)
1197     xfree (edir);
1198   if (efile != u->file)
1199     xfree (efile);
1200
1201   /* Regenerate u->url as well.  */
1202   xfree (u->url);
1203   u->url = url_string (u, 0);
1204 }
1205
1206 /* Mutators.  Code in ftp.c insists on changing u->dir and u->file.
1207    This way we can sync u->path and u->url when they get changed.  */
1208
1209 void
1210 url_set_dir (struct url *url, const char *newdir)
1211 {
1212   xfree (url->dir);
1213   url->dir = xstrdup (newdir);
1214   sync_path (url);
1215 }
1216
1217 void
1218 url_set_file (struct url *url, const char *newfile)
1219 {
1220   xfree (url->file);
1221   url->file = xstrdup (newfile);
1222   sync_path (url);
1223 }
1224
1225 void
1226 url_free (struct url *url)
1227 {
1228   xfree (url->host);
1229   xfree (url->path);
1230   xfree (url->url);
1231
1232   FREE_MAYBE (url->params);
1233   FREE_MAYBE (url->query);
1234   FREE_MAYBE (url->fragment);
1235   FREE_MAYBE (url->user);
1236   FREE_MAYBE (url->passwd);
1237
1238   xfree (url->dir);
1239   xfree (url->file);
1240
1241   xfree (url);
1242 }
1243 \f
1244 /* Create all the necessary directories for PATH (a file).  Calls
1245    mkdirhier() internally.  */
1246 int
1247 mkalldirs (const char *path)
1248 {
1249   const char *p;
1250   char *t;
1251   struct stat st;
1252   int res;
1253
1254   p = path + strlen (path);
1255   for (; *p != '/' && p != path; p--)
1256     ;
1257
1258   /* Don't create if it's just a file.  */
1259   if ((p == path) && (*p != '/'))
1260     return 0;
1261   t = strdupdelim (path, p);
1262
1263   /* Check whether the directory exists.  */
1264   if ((stat (t, &st) == 0))
1265     {
1266       if (S_ISDIR (st.st_mode))
1267         {
1268           xfree (t);
1269           return 0;
1270         }
1271       else
1272         {
1273           /* If the dir exists as a file name, remove it first.  This
1274              is *only* for Wget to work with buggy old CERN http
1275              servers.  Here is the scenario: When Wget tries to
1276              retrieve a directory without a slash, e.g.
1277              http://foo/bar (bar being a directory), CERN server will
1278              not redirect it too http://foo/bar/ -- it will generate a
1279              directory listing containing links to bar/file1,
1280              bar/file2, etc.  Wget will lose because it saves this
1281              HTML listing to a file `bar', so it cannot create the
1282              directory.  To work around this, if the file of the same
1283              name exists, we just remove it and create the directory
1284              anyway.  */
1285           DEBUGP (("Removing %s because of directory danger!\n", t));
1286           unlink (t);
1287         }
1288     }
1289   res = make_directory (t);
1290   if (res != 0)
1291     logprintf (LOG_NOTQUIET, "%s: %s", t, strerror (errno));
1292   xfree (t);
1293   return res;
1294 }
1295 \f
1296 /* Functions for constructing the file name out of URL components.  */
1297
1298 /* A growable string structure, used by url_file_name and friends.
1299    This should perhaps be moved to utils.c.
1300
1301    The idea is to have a convenient and efficient way to construct a
1302    string by having various functions append data to it.  Instead of
1303    passing the obligatory BASEVAR, SIZEVAR and TAILPOS to all the
1304    functions in questions, we pass the pointer to this struct.  */
1305
1306 struct growable {
1307   char *base;
1308   int size;
1309   int tail;
1310 };
1311
1312 /* Ensure that the string can accept APPEND_COUNT more characters past
1313    the current TAIL position.  If necessary, this will grow the string
1314    and update its allocated size.  If the string is already large
1315    enough to take TAIL+APPEND_COUNT characters, this does nothing.  */
1316 #define GROW(g, append_size) do {                                       \
1317   struct growable *G_ = g;                                              \
1318   DO_REALLOC (G_->base, G_->size, G_->tail + append_size, char);        \
1319 } while (0)
1320
1321 /* Return the tail position of the string. */
1322 #define TAIL(r) ((r)->base + (r)->tail)
1323
1324 /* Move the tail position by APPEND_COUNT characters. */
1325 #define TAIL_INCR(r, append_count) ((r)->tail += append_count)
1326
1327 /* Append the string STR to DEST.  NOTICE: the string in DEST is not
1328    terminated.  */
1329
1330 static void
1331 append_string (const char *str, struct growable *dest)
1332 {
1333   int l = strlen (str);
1334   GROW (dest, l);
1335   memcpy (TAIL (dest), str, l);
1336   TAIL_INCR (dest, l);
1337 }
1338
1339 /* Append CH to DEST.  For example, append_char (0, DEST)
1340    zero-terminates DEST.  */
1341
1342 static void
1343 append_char (char ch, struct growable *dest)
1344 {
1345   GROW (dest, 1);
1346   *TAIL (dest) = ch;
1347   TAIL_INCR (dest, 1);
1348 }
1349
1350 enum {
1351   filechr_not_unix    = 1,      /* unusable on Unix, / and \0 */
1352   filechr_not_windows = 2,      /* unusable on Windows, one of \|/<>?:*" */
1353   filechr_control     = 4,      /* a control character, e.g. 0-31 */
1354 };
1355
1356 #define FILE_CHAR_TEST(c, mask) (filechr_table[(unsigned char)(c)] & (mask))
1357
1358 /* Shorthands for the table: */
1359 #define U filechr_not_unix
1360 #define W filechr_not_windows
1361 #define C filechr_control
1362
1363 #define UW U|W
1364 #define UWC U|W|C
1365
1366 /* Table of characters unsafe under various conditions (see above).
1367
1368    Arguably we could also claim `%' to be unsafe, since we use it as
1369    the escape character.  If we ever want to be able to reliably
1370    translate file name back to URL, this would become important
1371    crucial.  Right now, it's better to be minimal in escaping.  */
1372
1373 const static unsigned char filechr_table[256] =
1374 {
1375 UWC,  C,  C,  C,   C,  C,  C,  C,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
1376   C,  C,  C,  C,   C,  C,  C,  C,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
1377   C,  C,  C,  C,   C,  C,  C,  C,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
1378   C,  C,  C,  C,   C,  C,  C,  C,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
1379   0,  0,  W,  0,   0,  0,  0,  0,   /* SP  !   "   #    $   %   &   '   */
1380   0,  0,  W,  0,   0,  0,  0, UW,   /* (   )   *   +    ,   -   .   /   */
1381   0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
1382   0,  0,  W,  0,   W,  0,  W,  W,   /* 8   9   :   ;    <   =   >   ?   */
1383   0,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
1384   0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
1385   0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
1386   0,  0,  0,  0,   W,  0,  0,  0,   /* X   Y   Z   [    \   ]   ^   _   */
1387   0,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
1388   0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
1389   0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
1390   0,  0,  0,  0,   0,  0,  0,  0,   /* x   y   z   {    |   }   ~   DEL */
1391
1392   C, C, C, C,  C, C, C, C,  C, C, C, C,  C, C, C, C, /* 128-143 */
1393   C, C, C, C,  C, C, C, C,  C, C, C, C,  C, C, C, C, /* 144-159 */
1394   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1395   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1396
1397   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1398   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1399   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1400   0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
1401 };
1402 #undef U
1403 #undef W
1404 #undef C
1405 #undef UW
1406 #undef UWC
1407
1408 /* FN_PORT_SEP is the separator between host and port in file names
1409    for non-standard port numbers.  On Unix this is normally ':', as in
1410    "www.xemacs.org:4001/index.html".  Under Windows, we set it to +
1411    because Windows can't handle ':' in file names.  */
1412 #define FN_PORT_SEP  (opt.restrict_files_os != restrict_windows ? ':' : '+')
1413
1414 /* FN_QUERY_SEP is the separator between the file name and the URL
1415    query, normally '?'.  Since Windows cannot handle '?' as part of
1416    file name, we use '@' instead there.  */
1417 #define FN_QUERY_SEP (opt.restrict_files_os != restrict_windows ? '?' : '@')
1418
1419 /* Quote path element, characters in [b, e), as file name, and append
1420    the quoted string to DEST.  Each character is quoted as per
1421    file_unsafe_char and the corresponding table.  */
1422
1423 static void
1424 append_uri_pathel (const char *b, const char *e, struct growable *dest)
1425 {
1426   char *pathel;
1427   int pathlen;
1428
1429   const char *p;
1430   int quoted, outlen;
1431
1432   int mask;
1433   if (opt.restrict_files_os == restrict_unix)
1434     mask = filechr_not_unix;
1435   else
1436     mask = filechr_not_windows;
1437   if (opt.restrict_files_ctrl)
1438     mask |= filechr_control;
1439
1440   /* Copy [b, e) to PATHEL and URL-unescape it. */
1441   BOUNDED_TO_ALLOCA (b, e, pathel);
1442   url_unescape (pathel);
1443   pathlen = strlen (pathel);
1444
1445   /* Go through PATHEL and check how many characters we'll need to
1446      add for file quoting. */
1447   quoted = 0;
1448   for (p = pathel; *p; p++)
1449     if (FILE_CHAR_TEST (*p, mask))
1450       ++quoted;
1451
1452   /* p - pathel is the string length.  Each quoted char means two
1453      additional characters in the string, hence 2*quoted.  */
1454   outlen = (p - pathel) + (2 * quoted);
1455   GROW (dest, outlen);
1456
1457   if (!quoted)
1458     {
1459       /* If there's nothing to quote, we don't need to go through the
1460          string the second time.  */
1461       memcpy (TAIL (dest), pathel, outlen);
1462     }
1463   else
1464     {
1465       char *q = TAIL (dest);
1466       for (p = pathel; *p; p++)
1467         {
1468           if (!FILE_CHAR_TEST (*p, mask))
1469             *q++ = *p;
1470           else
1471             {
1472               unsigned char ch = *p;
1473               *q++ = '%';
1474               *q++ = XNUM_TO_DIGIT (ch >> 4);
1475               *q++ = XNUM_TO_DIGIT (ch & 0xf);
1476             }
1477         }
1478       assert (q - TAIL (dest) == outlen);
1479     }
1480   TAIL_INCR (dest, outlen);
1481 }
1482
1483 /* Append to DEST the directory structure that corresponds the
1484    directory part of URL's path.  For example, if the URL is
1485    http://server/dir1/dir2/file, this appends "/dir1/dir2".
1486
1487    Each path element ("dir1" and "dir2" in the above example) is
1488    examined, url-unescaped, and re-escaped as file name element.
1489
1490    Additionally, it cuts as many directories from the path as
1491    specified by opt.cut_dirs.  For example, if opt.cut_dirs is 1, it
1492    will produce "bar" for the above example.  For 2 or more, it will
1493    produce "".
1494
1495    Each component of the path is quoted for use as file name.  */
1496
1497 static void
1498 append_dir_structure (const struct url *u, struct growable *dest)
1499 {
1500   char *pathel, *next;
1501   int cut = opt.cut_dirs;
1502
1503   /* Go through the path components, de-URL-quote them, and quote them
1504      (if necessary) as file names.  */
1505
1506   pathel = u->path;
1507   for (; (next = strchr (pathel, '/')) != NULL; pathel = next + 1)
1508     {
1509       if (cut-- > 0)
1510         continue;
1511       if (pathel == next)
1512         /* Ignore empty pathels.  path_simplify should remove
1513            occurrences of "//" from the path, but it has special cases
1514            for starting / which generates an empty pathel here.  */
1515         continue;
1516
1517       if (dest->tail)
1518         append_char ('/', dest);
1519       append_uri_pathel (pathel, next, dest);
1520     }
1521 }
1522
1523 /* Return a unique file name that matches the given URL as good as
1524    possible.  Does not create directories on the file system.  */
1525
1526 char *
1527 url_file_name (const struct url *u)
1528 {
1529   struct growable fnres;
1530
1531   char *u_file, *u_query;
1532   char *fname, *unique;
1533
1534   fnres.base = NULL;
1535   fnres.size = 0;
1536   fnres.tail = 0;
1537
1538   /* Start with the directory prefix, if specified. */
1539   if (opt.dir_prefix)
1540     append_string (opt.dir_prefix, &fnres);
1541
1542   /* If "dirstruct" is turned on (typically the case with -r), add
1543      the host and port (unless those have been turned off) and
1544      directory structure.  */
1545   if (opt.dirstruct)
1546     {
1547       if (opt.add_hostdir)
1548         {
1549           if (fnres.tail)
1550             append_char ('/', &fnres);
1551           append_string (u->host, &fnres);
1552           if (u->port != scheme_default_port (u->scheme))
1553             {
1554               char portstr[24];
1555               number_to_string (portstr, u->port);
1556               append_char (FN_PORT_SEP, &fnres);
1557               append_string (portstr, &fnres);
1558             }
1559         }
1560
1561       append_dir_structure (u, &fnres);
1562     }
1563
1564   /* Add the file name. */
1565   if (fnres.tail)
1566     append_char ('/', &fnres);
1567   u_file = *u->file ? u->file : "index.html";
1568   append_uri_pathel (u_file, u_file + strlen (u_file), &fnres);
1569
1570   /* Append "?query" to the file name. */
1571   u_query = u->query && *u->query ? u->query : NULL;
1572   if (u_query)
1573     {
1574       append_char (FN_QUERY_SEP, &fnres);
1575       append_uri_pathel (u_query, u_query + strlen (u_query), &fnres);
1576     }
1577
1578   /* Zero-terminate the file name. */
1579   append_char ('\0', &fnres);
1580
1581   fname = fnres.base;
1582
1583   /* Check the cases in which the unique extensions are not used:
1584      1) Clobbering is turned off (-nc).
1585      2) Retrieval with regetting.
1586      3) Timestamping is used.
1587      4) Hierarchy is built.
1588
1589      The exception is the case when file does exist and is a
1590      directory (see `mkalldirs' for explanation).  */
1591
1592   if ((opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct)
1593       && !(file_exists_p (fname) && !file_non_directory_p (fname)))
1594     return fname;
1595
1596   unique = unique_name (fname, 1);
1597   if (unique != fname)
1598     xfree (fname);
1599   return unique;
1600 }
1601
1602 /* Return the length of URL's path.  Path is considered to be
1603    terminated by one of '?', ';', '#', or by the end of the
1604    string.  */
1605 static int
1606 path_length (const char *url)
1607 {
1608   const char *q = strpbrk_or_eos (url, "?;#");
1609   return q - url;
1610 }
1611
1612 /* Find the last occurrence of character C in the range [b, e), or
1613    NULL, if none are present.  This is equivalent to strrchr(b, c),
1614    except that it accepts an END argument instead of requiring the
1615    string to be zero-terminated.  Why is there no memrchr()?  */
1616 static const char *
1617 find_last_char (const char *b, const char *e, char c)
1618 {
1619   for (; e > b; e--)
1620     if (*e == c)
1621       return e;
1622   return NULL;
1623 }
1624 \f
1625 /* Resolve "." and ".." elements of PATH by destructively modifying
1626    PATH and return non-zero if PATH has been modified, zero otherwise.
1627
1628    The algorithm is in spirit similar to the one described in rfc1808,
1629    although implemented differently, in one pass.  To recap, path
1630    elements containing only "." are removed, and ".." is taken to mean
1631    "back up one element".  Single leading and trailing slashes are
1632    preserved.
1633
1634    This function does not handle URL escapes explicitly.  If you're
1635    passing paths from URLs, make sure to unquote "%2e" and "%2E" to
1636    ".", so that this function can find the dots.  (Wget's URL parser
1637    calls reencode_escapes, which see.)
1638
1639    For example, "a/b/c/./../d/.." will yield "a/b/".  More exhaustive
1640    test examples are provided below.  If you change anything in this
1641    function, run test_path_simplify to make sure you haven't broken a
1642    test case.  */
1643
1644 static int
1645 path_simplify (char *path)
1646 {
1647   char *h, *t, *end;
1648
1649   /* Preserve the leading '/'. */
1650   if (path[0] == '/')
1651     ++path;
1652
1653   h = path;                     /* hare */
1654   t = path;                     /* tortoise */
1655   end = path + strlen (path);
1656
1657   while (h < end)
1658     {
1659       /* Hare should be at the beginning of a path element. */
1660
1661       if (h[0] == '.' && (h[1] == '/' || h[1] == '\0'))
1662         {
1663           /* Ignore "./". */
1664           h += 2;
1665         }
1666       else if (h[0] == '.' && h[1] == '.' && (h[2] == '/' || h[2] == '\0'))
1667         {
1668           /* Handle "../" by retreating the tortoise by one path
1669              element -- but not past beggining of PATH.  */
1670           if (t > path)
1671             {
1672               /* Move backwards until T hits the beginning of the
1673                  previous path element or the beginning of path. */
1674               for (--t; t > path && t[-1] != '/'; t--)
1675                 ;
1676             }
1677           h += 3;
1678         }
1679       else if (*h == '/')
1680         {
1681           /* Ignore empty path elements.  Supporting them well is hard
1682              (where do you save "http://x.com///y.html"?), and they
1683              don't bring any practical gain.  Plus, they break our
1684              filesystem-influenced assumptions: allowing them would
1685              make "x/y//../z" simplify to "x/y/z", whereas most people
1686              would expect "x/z".  */
1687           ++h;
1688         }
1689       else
1690         {
1691           /* A regular path element.  If H hasn't advanced past T,
1692              simply skip to the next path element.  Otherwise, copy
1693              the path element until the next slash.  */
1694           if (t == h)
1695             {
1696               /* Skip the path element, including the slash.  */
1697               while (h < end && *h != '/')
1698                 t++, h++;
1699               if (h < end)
1700                 t++, h++;
1701             }
1702           else
1703             {
1704               /* Copy the path element, including the final slash.  */
1705               while (h < end && *h != '/')
1706                 *t++ = *h++;
1707               if (h < end)
1708                 *t++ = *h++;
1709             }
1710         }
1711     }
1712
1713   if (t != h)
1714     *t = '\0';
1715
1716   return t != h;
1717 }
1718 \f
1719 /* Merge BASE with LINK and return the resulting URI.
1720
1721    Either of the URIs may be absolute or relative, complete with the
1722    host name, or path only.  This tries to reasonably handle all
1723    foreseeable cases.  It only employs minimal URL parsing, without
1724    knowledge of the specifics of schemes.
1725
1726    Perhaps this function should call path_simplify so that the callers
1727    don't have to call url_parse unconditionally.  */
1728
1729 char *
1730 uri_merge (const char *base, const char *link)
1731 {
1732   int linklength;
1733   const char *end;
1734   char *merge;
1735
1736   if (url_has_scheme (link))
1737     return xstrdup (link);
1738
1739   /* We may not examine BASE past END. */
1740   end = base + path_length (base);
1741   linklength = strlen (link);
1742
1743   if (!*link)
1744     {
1745       /* Empty LINK points back to BASE, query string and all. */
1746       return xstrdup (base);
1747     }
1748   else if (*link == '?')
1749     {
1750       /* LINK points to the same location, but changes the query
1751          string.  Examples: */
1752       /* uri_merge("path",         "?new") -> "path?new"     */
1753       /* uri_merge("path?foo",     "?new") -> "path?new"     */
1754       /* uri_merge("path?foo#bar", "?new") -> "path?new"     */
1755       /* uri_merge("path#foo",     "?new") -> "path?new"     */
1756       int baselength = end - base;
1757       merge = xmalloc (baselength + linklength + 1);
1758       memcpy (merge, base, baselength);
1759       memcpy (merge + baselength, link, linklength);
1760       merge[baselength + linklength] = '\0';
1761     }
1762   else if (*link == '#')
1763     {
1764       /* uri_merge("path",         "#new") -> "path#new"     */
1765       /* uri_merge("path#foo",     "#new") -> "path#new"     */
1766       /* uri_merge("path?foo",     "#new") -> "path?foo#new" */
1767       /* uri_merge("path?foo#bar", "#new") -> "path?foo#new" */
1768       int baselength;
1769       const char *end1 = strchr (base, '#');
1770       if (!end1)
1771         end1 = base + strlen (base);
1772       baselength = end1 - base;
1773       merge = xmalloc (baselength + linklength + 1);
1774       memcpy (merge, base, baselength);
1775       memcpy (merge + baselength, link, linklength);
1776       merge[baselength + linklength] = '\0';
1777     }
1778   else if (*link == '/' && *(link + 1) == '/')
1779     {
1780       /* LINK begins with "//" and so is a net path: we need to
1781          replace everything after (and including) the double slash
1782          with LINK. */
1783
1784       /* uri_merge("foo", "//new/bar")            -> "//new/bar"      */
1785       /* uri_merge("//old/foo", "//new/bar")      -> "//new/bar"      */
1786       /* uri_merge("http://old/foo", "//new/bar") -> "http://new/bar" */
1787
1788       int span;
1789       const char *slash;
1790       const char *start_insert;
1791
1792       /* Look for first slash. */
1793       slash = memchr (base, '/', end - base);
1794       /* If found slash and it is a double slash, then replace
1795          from this point, else default to replacing from the
1796          beginning.  */
1797       if (slash && *(slash + 1) == '/')
1798         start_insert = slash;
1799       else
1800         start_insert = base;
1801
1802       span = start_insert - base;
1803       merge = (char *)xmalloc (span + linklength + 1);
1804       if (span)
1805         memcpy (merge, base, span);
1806       memcpy (merge + span, link, linklength);
1807       merge[span + linklength] = '\0';
1808     }
1809   else if (*link == '/')
1810     {
1811       /* LINK is an absolute path: we need to replace everything
1812          after (and including) the FIRST slash with LINK.
1813
1814          So, if BASE is "http://host/whatever/foo/bar", and LINK is
1815          "/qux/xyzzy", our result should be
1816          "http://host/qux/xyzzy".  */
1817       int span;
1818       const char *slash;
1819       const char *start_insert = NULL; /* for gcc to shut up. */
1820       const char *pos = base;
1821       int seen_slash_slash = 0;
1822       /* We're looking for the first slash, but want to ignore
1823          double slash. */
1824     again:
1825       slash = memchr (pos, '/', end - pos);
1826       if (slash && !seen_slash_slash)
1827         if (*(slash + 1) == '/')
1828           {
1829             pos = slash + 2;
1830             seen_slash_slash = 1;
1831             goto again;
1832           }
1833
1834       /* At this point, SLASH is the location of the first / after
1835          "//", or the first slash altogether.  START_INSERT is the
1836          pointer to the location where LINK will be inserted.  When
1837          examining the last two examples, keep in mind that LINK
1838          begins with '/'. */
1839
1840       if (!slash && !seen_slash_slash)
1841         /* example: "foo" */
1842         /*           ^    */
1843         start_insert = base;
1844       else if (!slash && seen_slash_slash)
1845         /* example: "http://foo" */
1846         /*                     ^ */
1847         start_insert = end;
1848       else if (slash && !seen_slash_slash)
1849         /* example: "foo/bar" */
1850         /*           ^        */
1851         start_insert = base;
1852       else if (slash && seen_slash_slash)
1853         /* example: "http://something/" */
1854         /*                           ^  */
1855         start_insert = slash;
1856
1857       span = start_insert - base;
1858       merge = (char *)xmalloc (span + linklength + 1);
1859       if (span)
1860         memcpy (merge, base, span);
1861       memcpy (merge + span, link, linklength);
1862       merge[span + linklength] = '\0';
1863     }
1864   else
1865     {
1866       /* LINK is a relative URL: we need to replace everything
1867          after last slash (possibly empty) with LINK.
1868
1869          So, if BASE is "whatever/foo/bar", and LINK is "qux/xyzzy",
1870          our result should be "whatever/foo/qux/xyzzy".  */
1871       int need_explicit_slash = 0;
1872       int span;
1873       const char *start_insert;
1874       const char *last_slash = find_last_char (base, end, '/');
1875       if (!last_slash)
1876         {
1877           /* No slash found at all.  Append LINK to what we have,
1878              but we'll need a slash as a separator.
1879
1880              Example: if base == "foo" and link == "qux/xyzzy", then
1881              we cannot just append link to base, because we'd get
1882              "fooqux/xyzzy", whereas what we want is
1883              "foo/qux/xyzzy".
1884
1885              To make sure the / gets inserted, we set
1886              need_explicit_slash to 1.  We also set start_insert
1887              to end + 1, so that the length calculations work out
1888              correctly for one more (slash) character.  Accessing
1889              that character is fine, since it will be the
1890              delimiter, '\0' or '?'.  */
1891           /* example: "foo?..." */
1892           /*               ^    ('?' gets changed to '/') */
1893           start_insert = end + 1;
1894           need_explicit_slash = 1;
1895         }
1896       else if (last_slash && last_slash >= base + 2
1897                && last_slash[-2] == ':' && last_slash[-1] == '/')
1898         {
1899           /* example: http://host"  */
1900           /*                      ^ */
1901           start_insert = end + 1;
1902           need_explicit_slash = 1;
1903         }
1904       else
1905         {
1906           /* example: "whatever/foo/bar" */
1907           /*                        ^    */
1908           start_insert = last_slash + 1;
1909         }
1910
1911       span = start_insert - base;
1912       merge = (char *)xmalloc (span + linklength + 1);
1913       if (span)
1914         memcpy (merge, base, span);
1915       if (need_explicit_slash)
1916         merge[span - 1] = '/';
1917       memcpy (merge + span, link, linklength);
1918       merge[span + linklength] = '\0';
1919     }
1920
1921   return merge;
1922 }
1923 \f
1924 #define APPEND(p, s) do {                       \
1925   int len = strlen (s);                         \
1926   memcpy (p, s, len);                           \
1927   p += len;                                     \
1928 } while (0)
1929
1930 /* Use this instead of password when the actual password is supposed
1931    to be hidden.  We intentionally use a generic string without giving
1932    away the number of characters in the password, like previous
1933    versions did.  */
1934 #define HIDDEN_PASSWORD "*password*"
1935
1936 /* Recreate the URL string from the data in URL.
1937
1938    If HIDE is non-zero (as it is when we're calling this on a URL we
1939    plan to print, but not when calling it to canonicalize a URL for
1940    use within the program), password will be hidden.  Unsafe
1941    characters in the URL will be quoted.  */
1942
1943 char *
1944 url_string (const struct url *url, int hide_password)
1945 {
1946   int size;
1947   char *result, *p;
1948   char *quoted_user = NULL, *quoted_passwd = NULL;
1949
1950   int scheme_port  = supported_schemes[url->scheme].default_port;
1951   char *scheme_str = supported_schemes[url->scheme].leading_string;
1952   int fplen = full_path_length (url);
1953
1954   int brackets_around_host = 0;
1955
1956   assert (scheme_str != NULL);
1957
1958   /* Make sure the user name and password are quoted. */
1959   if (url->user)
1960     {
1961       quoted_user = url_escape_allow_passthrough (url->user);
1962       if (url->passwd)
1963         {
1964           if (hide_password)
1965             quoted_passwd = HIDDEN_PASSWORD;
1966           else
1967             quoted_passwd = url_escape_allow_passthrough (url->passwd);
1968         }
1969     }
1970
1971   if (strchr (url->host, ':'))
1972     brackets_around_host = 1;
1973
1974   size = (strlen (scheme_str)
1975           + strlen (url->host)
1976           + (brackets_around_host ? 2 : 0)
1977           + fplen
1978           + 1);
1979   if (url->port != scheme_port)
1980     size += 1 + numdigit (url->port);
1981   if (quoted_user)
1982     {
1983       size += 1 + strlen (quoted_user);
1984       if (quoted_passwd)
1985         size += 1 + strlen (quoted_passwd);
1986     }
1987
1988   p = result = xmalloc (size);
1989
1990   APPEND (p, scheme_str);
1991   if (quoted_user)
1992     {
1993       APPEND (p, quoted_user);
1994       if (quoted_passwd)
1995         {
1996           *p++ = ':';
1997           APPEND (p, quoted_passwd);
1998         }
1999       *p++ = '@';
2000     }
2001
2002   if (brackets_around_host)
2003     *p++ = '[';
2004   APPEND (p, url->host);
2005   if (brackets_around_host)
2006     *p++ = ']';
2007   if (url->port != scheme_port)
2008     {
2009       *p++ = ':';
2010       p = number_to_string (p, url->port);
2011     }
2012
2013   full_path_write (url, p);
2014   p += fplen;
2015   *p++ = '\0';
2016
2017   assert (p - result == size);
2018
2019   if (quoted_user && quoted_user != url->user)
2020     xfree (quoted_user);
2021   if (quoted_passwd && !hide_password
2022       && quoted_passwd != url->passwd)
2023     xfree (quoted_passwd);
2024
2025   return result;
2026 }
2027 \f
2028 /* Return non-zero if scheme a is similar to scheme b.
2029  
2030    Schemes are similar if they are equal.  If SSL is supported, schemes
2031    are also similar if one is http (SCHEME_HTTP) and the other is https
2032    (SCHEME_HTTPS).  */
2033 int
2034 schemes_are_similar_p (enum url_scheme a, enum url_scheme b)
2035 {
2036   if (a == b)
2037     return 1;
2038 #ifdef HAVE_SSL
2039   if ((a == SCHEME_HTTP && b == SCHEME_HTTPS)
2040       || (a == SCHEME_HTTPS && b == SCHEME_HTTP))
2041     return 1;
2042 #endif
2043   return 0;
2044 }
2045 \f
2046 #if 0
2047 /* Debugging and testing support for path_simplify. */
2048
2049 /* Debug: run path_simplify on PATH and return the result in a new
2050    string.  Useful for calling from the debugger.  */
2051 static char *
2052 ps (char *path)
2053 {
2054   char *copy = xstrdup (path);
2055   path_simplify (copy);
2056   return copy;
2057 }
2058
2059 static void
2060 run_test (char *test, char *expected_result, int expected_change)
2061 {
2062   char *test_copy = xstrdup (test);
2063   int modified = path_simplify (test_copy);
2064
2065   if (0 != strcmp (test_copy, expected_result))
2066     {
2067       printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
2068               test, expected_result, test_copy);
2069     }
2070   if (modified != expected_change)
2071     {
2072       if (expected_change == 1)
2073         printf ("Expected no modification with path_simplify(\"%s\").\n",
2074                 test);
2075       else
2076         printf ("Expected modification with path_simplify(\"%s\").\n",
2077                 test);
2078     }
2079   xfree (test_copy);
2080 }
2081
2082 static void
2083 test_path_simplify (void)
2084 {
2085   static struct {
2086     char *test, *result;
2087     int should_modify;
2088   } tests[] = {
2089     { "",               "",             0 },
2090     { ".",              "",             1 },
2091     { "..",             "",             1 },
2092     { "foo",            "foo",          0 },
2093     { "foo/bar",        "foo/bar",      0 },
2094     { "foo///bar",      "foo/bar",      1 },
2095     { "foo/.",          "foo/",         1 },
2096     { "foo/./",         "foo/",         1 },
2097     { "foo./",          "foo./",        0 },
2098     { "foo/../bar",     "bar",          1 },
2099     { "foo/../bar/",    "bar/",         1 },
2100     { "foo/bar/..",     "foo/",         1 },
2101     { "foo/bar/../x",   "foo/x",        1 },
2102     { "foo/bar/../x/",  "foo/x/",       1 },
2103     { "foo/..",         "",             1 },
2104     { "foo/../..",      "",             1 },
2105     { "a/b/../../c",    "c",            1 },
2106     { "./a/../b",       "b",            1 }
2107   };
2108   int i;
2109
2110   for (i = 0; i < countof (tests); i++)
2111     {
2112       char *test = tests[i].test;
2113       char *expected_result = tests[i].result;
2114       int   expected_change = tests[i].should_modify;
2115       run_test (test, expected_result, expected_change);
2116     }
2117
2118   /* Now run all the tests with a leading slash before the test case,
2119      to prove that the slash is being preserved.  */
2120   for (i = 0; i < countof (tests); i++)
2121     {
2122       char *test, *expected_result;
2123       int expected_change = tests[i].should_modify;
2124
2125       test = xmalloc (1 + strlen (tests[i].test) + 1);
2126       sprintf (test, "/%s", tests[i].test);
2127
2128       expected_result = xmalloc (1 + strlen (tests[i].result) + 1);
2129       sprintf (expected_result, "/%s", tests[i].result);
2130
2131       run_test (test, expected_result, expected_change);
2132
2133       xfree (test);
2134       xfree (expected_result);
2135     }
2136 }
2137 #endif