]> sjero.net Git - wget/blob - src/url.c
[svn] Commit "minor fixes".
[wget] / src / url.c
1 /* URL handling.
2    Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
3
4 This file is part of Wget.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #else
27 # include <strings.h>
28 #endif
29 #include <ctype.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <errno.h>
35 #include <assert.h>
36
37 #include "wget.h"
38 #include "utils.h"
39 #include "url.h"
40 #include "host.h"
41 #include "html.h"
42
43 #ifndef errno
44 extern int errno;
45 #endif
46
47 /* Default port definitions */
48 #define DEFAULT_HTTP_PORT 80
49 #define DEFAULT_FTP_PORT 21
50
51 /* URL separator (for findurl) */
52 #define URL_SEPARATOR "!\"#'(),>`{}|<>"
53
54 /* A list of unsafe characters for encoding, as per RFC1738.  '@' and
55    ':' (not listed in RFC) were added because of user/password
56    encoding.  */
57
58 #ifndef WINDOWS
59 # define URL_UNSAFE_CHARS "<>\"#%{}|\\^~[]`@:"
60 #else  /* WINDOWS */
61 # define URL_UNSAFE_CHARS "<>\"%{}|\\^[]`"
62 #endif /* WINDOWS */
63
64 #define UNSAFE_CHAR(c) (   ((unsigned char)(c) <= ' ')  /* ASCII 32  */  \
65                         || ((unsigned char)(c) >  '~')  /* ASCII 127 */  \
66                         || strchr (URL_UNSAFE_CHARS, c))
67
68 /* If S contains unsafe characters, free it and replace it with a
69    version that doesn't.  */
70 #define URL_CLEANSE(s) do                       \
71 {                                               \
72   if (contains_unsafe (s))                      \
73     {                                           \
74       char *uc_tmp = encode_string (s);         \
75       free (s);                                 \
76       (s) = uc_tmp;                             \
77     }                                           \
78 } while (0)
79
80 /* Is a directory "."?  */
81 #define DOTP(x) ((*(x) == '.') && (!*(x + 1)))
82 /* Is a directory ".."?  */
83 #define DDOTP(x) ((*(x) == '.') && (*(x + 1) == '.') && (!*(x + 2)))
84
85 #if 0
86 static void path_simplify_with_kludge PARAMS ((char *));
87 #endif
88 static int urlpath_length PARAMS ((const char *));
89
90 /* NULL-terminated list of strings to be recognized as prototypes (URL
91    schemes).  Note that recognized doesn't mean supported -- only HTTP
92    and FTP are currently supported.
93
94    However, a string that does not match anything in the list will be
95    considered a relative URL.  Thus it's important that this list has
96    anything anyone could think of being legal.
97
98    There are wild things here.  :-) Take a look at
99    <URL:http://www.w3.org/pub/WWW/Addressing/schemes.html> for more
100    fun.  */
101 static char *protostrings[] =
102 {
103   "cid:",
104   "clsid:",
105   "file:",
106   "finger:",
107   "ftp:",
108   "gopher:",
109   "hdl:",
110   "http:",
111   "https:",
112   "ilu:",
113   "ior:",
114   "irc:",
115   "java:",
116   "javascript:",
117   "lifn:",
118   "mailto:",
119   "mid:",
120   "news:",
121   "nntp:",
122   "path:",
123   "prospero:",
124   "rlogin:",
125   "service:",
126   "shttp:",
127   "snews:",
128   "stanf:",
129   "telnet:",
130   "tn3270:",
131   "wais:",
132   "whois++:",
133   NULL
134 };
135
136 struct proto
137 {
138   char *name;
139   uerr_t ind;
140   unsigned short port;
141 };
142
143 /* Similar to former, but for supported protocols: */
144 static struct proto sup_protos[] =
145 {
146   { "http://", URLHTTP, DEFAULT_HTTP_PORT },
147   { "ftp://", URLFTP, DEFAULT_FTP_PORT },
148   /*{ "file://", URLFILE, DEFAULT_FTP_PORT },*/
149 };
150
151 static void parse_dir PARAMS ((const char *, char **, char **));
152 static uerr_t parse_uname PARAMS ((const char *, char **, char **));
153 static char *construct PARAMS ((const char *, const char *, int , int));
154 static char *construct_relative PARAMS ((const char *, const char *));
155 static char process_ftp_type PARAMS ((char *));
156
157 \f
158 /* Returns the number of characters to be skipped if the first thing
159    in a URL is URL: (which is 0 or 4+).  The optional spaces after
160    URL: are also skipped.  */
161 int
162 skip_url (const char *url)
163 {
164   int i;
165
166   if (TOUPPER (url[0]) == 'U'
167       && TOUPPER (url[1]) == 'R'
168       && TOUPPER (url[2]) == 'L'
169       && url[3] == ':')
170     {
171       /* Skip blanks.  */
172       for (i = 4; url[i] && ISSPACE (url[i]); i++);
173       return i;
174     }
175   else
176     return 0;
177 }
178
179 /* Returns 1 if the string contains unsafe characters, 0 otherwise.  */
180 int
181 contains_unsafe (const char *s)
182 {
183   for (; *s; s++)
184     if (UNSAFE_CHAR (*s))
185       return 1;
186   return 0;
187 }
188
189 /* Decodes the forms %xy in a URL to the character the hexadecimal
190    code of which is xy.  xy are hexadecimal digits from
191    [0123456789ABCDEF] (case-insensitive).  If x or y are not
192    hex-digits or `%' precedes `\0', the sequence is inserted
193    literally.  */
194
195 static void
196 decode_string (char *s)
197 {
198   char *p = s;
199
200   for (; *s; s++, p++)
201     {
202       if (*s != '%')
203         *p = *s;
204       else
205         {
206           /* Do nothing if at the end of the string, or if the chars
207              are not hex-digits.  */
208           if (!*(s + 1) || !*(s + 2)
209               || !(ISXDIGIT (*(s + 1)) && ISXDIGIT (*(s + 2))))
210             {
211               *p = *s;
212               continue;
213             }
214           *p = (ASC2HEXD (*(s + 1)) << 4) + ASC2HEXD (*(s + 2));
215           s += 2;
216         }
217     }
218   *p = '\0';
219 }
220
221 /* Encode the unsafe characters (as determined by URL_UNSAFE) in a
222    given string, returning a malloc-ed %XX encoded string.  */
223 char *
224 encode_string (const char *s)
225 {
226   const char *b;
227   char *p, *res;
228   int i;
229
230   b = s;
231   for (i = 0; *s; s++, i++)
232     if (UNSAFE_CHAR (*s))
233       i += 2; /* Two more characters (hex digits) */
234   res = (char *)xmalloc (i + 1);
235   s = b;
236   for (p = res; *s; s++)
237     if (UNSAFE_CHAR (*s))
238       {
239         const unsigned char c = *s;
240         *p++ = '%';
241         *p++ = HEXD2ASC (c >> 4);
242         *p++ = HEXD2ASC (c & 0xf);
243       }
244     else
245       *p++ = *s;
246   *p = '\0';
247   return res;
248 }
249 \f
250 /* Returns the proto-type if URL's protocol is supported, or
251    URLUNKNOWN if not.  */
252 uerr_t
253 urlproto (const char *url)
254 {
255   int i;
256
257   url += skip_url (url);
258   for (i = 0; i < ARRAY_SIZE (sup_protos); i++)
259     if (!strncasecmp (url, sup_protos[i].name, strlen (sup_protos[i].name)))
260       return sup_protos[i].ind;
261   for (i = 0; url[i] && url[i] != ':' && url[i] != '/'; i++);
262   if (url[i] == ':')
263     {
264       for (++i; url[i] && url[i] != '/'; i++)
265         if (!ISDIGIT (url[i]))
266           return URLBADPORT;
267       if (url[i - 1] == ':')
268         return URLFTP;
269       else
270         return URLHTTP;
271     }
272   else
273     return URLHTTP;
274 }
275
276 /* Skip the protocol part of the URL, e.g. `http://'.  If no protocol
277    part is found, returns 0.  */
278 int
279 skip_proto (const char *url)
280 {
281   char **s;
282   int l;
283
284   for (s = protostrings; *s; s++)
285     if (!strncasecmp (*s, url, strlen (*s)))
286       break;
287   if (!*s)
288     return 0;
289   l = strlen (*s);
290   /* HTTP and FTP protocols are expected to yield exact host names
291      (i.e. the `//' part must be skipped, too).  */
292   if (!strcmp (*s, "http:") || !strcmp (*s, "ftp:"))
293     l += 2;
294   return l;
295 }
296
297 /* Returns 1 if the URL begins with a protocol (supported or
298    unsupported), 0 otherwise.  */
299 static int
300 has_proto (const char *url)
301 {
302   char **s;
303
304   url += skip_url (url);
305   for (s = protostrings; *s; s++)
306     if (strncasecmp (url, *s, strlen (*s)) == 0)
307       return 1;
308   return 0;
309 }
310
311 /* Skip the username and password, if present here.  The function
312    should be called *not* with the complete URL, but with the part
313    right after the protocol.
314
315    If no username and password are found, return 0.  */
316 int
317 skip_uname (const char *url)
318 {
319   const char *p;
320   for (p = url; *p && *p != '/'; p++)
321     if (*p == '@')
322       break;
323   /* If a `@' was found before the first occurrence of `/', skip
324      it.  */
325   if (*p == '@')
326     return p - url + 1;
327   else
328     return 0;
329 }
330 \f
331 /* Allocate a new urlinfo structure, fill it with default values and
332    return a pointer to it.  */
333 struct urlinfo *
334 newurl (void)
335 {
336   struct urlinfo *u;
337
338   u = (struct urlinfo *)xmalloc (sizeof (struct urlinfo));
339   memset (u, 0, sizeof (*u));
340   u->proto = URLUNKNOWN;
341   return u;
342 }
343
344 /* Perform a "deep" free of the urlinfo structure.  The structure
345    should have been created with newurl, but need not have been used.
346    If free_pointer is non-0, free the pointer itself.  */
347 void
348 freeurl (struct urlinfo *u, int complete)
349 {
350   assert (u != NULL);
351   FREE_MAYBE (u->url);
352   FREE_MAYBE (u->host);
353   FREE_MAYBE (u->path);
354   FREE_MAYBE (u->file);
355   FREE_MAYBE (u->dir);
356   FREE_MAYBE (u->user);
357   FREE_MAYBE (u->passwd);
358   FREE_MAYBE (u->local);
359   FREE_MAYBE (u->referer);
360   if (u->proxy)
361     freeurl (u->proxy, 1);
362   if (complete)
363     free (u);
364   return;
365 }
366 \f
367 /* Extract the given URL of the form
368    (http:|ftp:)// (user (:password)?@)?hostname (:port)? (/path)?
369    1. hostname (terminated with `/' or `:')
370    2. port number (terminated with `/'), or chosen for the protocol
371    3. dirname (everything after hostname)
372    Most errors are handled.  No allocation is done, you must supply
373    pointers to allocated memory.
374    ...and a host of other stuff :-)
375
376    - Recognizes hostname:dir/file for FTP and
377      hostname (:portnum)?/dir/file for HTTP.
378    - Parses the path to yield directory and file
379    - Parses the URL to yield the username and passwd (if present)
380    - Decodes the strings, in case they contain "forbidden" characters
381    - Writes the result to struct urlinfo
382
383    If the argument STRICT is set, it recognizes only the canonical
384    form.  */
385 uerr_t
386 parseurl (const char *url, struct urlinfo *u, int strict)
387 {
388   int i, l, abs_ftp;
389   int recognizable;            /* Recognizable URL is the one where
390                                   the protocol name was explicitly
391                                   named, i.e. it wasn't deduced from
392                                   the URL format.  */
393   uerr_t type;
394
395   DEBUGP (("parseurl (\"%s\") -> ", url));
396   url += skip_url (url);
397   recognizable = has_proto (url);
398   if (strict && !recognizable)
399     return URLUNKNOWN;
400   for (i = 0, l = 0; i < ARRAY_SIZE (sup_protos); i++)
401     {
402       l = strlen (sup_protos[i].name);
403       if (!strncasecmp (sup_protos[i].name, url, l))
404         break;
405     }
406   /* If protocol is recognizable, but unsupported, bail out, else
407      suppose unknown.  */
408   if (recognizable && i == ARRAY_SIZE (sup_protos))
409     return URLUNKNOWN;
410   else if (i == ARRAY_SIZE (sup_protos))
411     type = URLUNKNOWN;
412   else
413     u->proto = type = sup_protos[i].ind;
414
415   if (type == URLUNKNOWN)
416     l = 0;
417   /* Allow a username and password to be specified (i.e. just skip
418      them for now).  */
419   if (recognizable)
420     l += skip_uname (url + l);
421   for (i = l; url[i] && url[i] != ':' && url[i] != '/'; i++);
422   if (i == l)
423     return URLBADHOST;
424   /* Get the hostname.  */
425   u->host = strdupdelim (url + l, url + i);
426   DEBUGP (("host %s -> ", u->host));
427
428   /* Assume no port has been given.  */
429   u->port = 0;
430   if (url[i] == ':')
431     {
432       /* We have a colon delimiting the hostname.  It could mean that
433          a port number is following it, or a directory.  */
434       if (ISDIGIT (url[++i]))    /* A port number */
435         {
436           if (type == URLUNKNOWN)
437             u->proto = type = URLHTTP;
438           for (; url[i] && url[i] != '/'; i++)
439             if (ISDIGIT (url[i]))
440               u->port = 10 * u->port + (url[i] - '0');
441             else
442               return URLBADPORT;
443           if (!u->port)
444             return URLBADPORT;
445           DEBUGP (("port %hu -> ", u->port));
446         }
447       else if (type == URLUNKNOWN) /* or a directory */
448         u->proto = type = URLFTP;
449       else                      /* or just a misformed port number */
450         return URLBADPORT;
451     }
452   else if (type == URLUNKNOWN)
453     u->proto = type = URLHTTP;
454   if (!u->port)
455     {
456       int i;
457       for (i = 0; i < ARRAY_SIZE (sup_protos); i++)
458         if (sup_protos[i].ind == type)
459           break;
460       if (i == ARRAY_SIZE (sup_protos))
461         return URLUNKNOWN;
462       u->port = sup_protos[i].port;
463     }
464   /* Some delimiter troubles...  */
465   if (url[i] == '/' && url[i - 1] != ':')
466     ++i;
467   if (type == URLHTTP)
468     while (url[i] && url[i] == '/')
469       ++i;
470   u->path = (char *)xmalloc (strlen (url + i) + 8);
471   strcpy (u->path, url + i);
472   if (type == URLFTP)
473     {
474       u->ftp_type = process_ftp_type (u->path);
475       /* #### We don't handle type `d' correctly yet.  */
476       if (!u->ftp_type || TOUPPER (u->ftp_type) == 'D')
477         u->ftp_type = 'I';
478     }
479   DEBUGP (("opath %s -> ", u->path));
480   /* Parse the username and password (if existing).  */
481   parse_uname (url, &u->user, &u->passwd);
482   /* Decode the strings, as per RFC 1738.  */
483   decode_string (u->host);
484   decode_string (u->path);
485   if (u->user)
486     decode_string (u->user);
487   if (u->passwd)
488     decode_string (u->passwd);
489   /* Parse the directory.  */
490   parse_dir (u->path, &u->dir, &u->file);
491   DEBUGP (("dir %s -> file %s -> ", u->dir, u->file));
492   /* Simplify the directory.  */
493   path_simplify (u->dir);
494   /* Remove the leading `/' in HTTP.  */
495   if (type == URLHTTP && *u->dir == '/')
496     strcpy (u->dir, u->dir + 1);
497   DEBUGP (("ndir %s\n", u->dir));
498   /* Strip trailing `/'.  */
499   l = strlen (u->dir);
500   if (l && u->dir[l - 1] == '/')
501     u->dir[l - 1] = '\0';
502   /* Re-create the path: */
503   abs_ftp = (u->proto == URLFTP && *u->dir == '/');
504   /*  sprintf (u->path, "%s%s%s%s", abs_ftp ? "%2F": "/",
505       abs_ftp ? (u->dir + 1) : u->dir, *u->dir ? "/" : "", u->file); */
506   strcpy (u->path, abs_ftp ? "%2F" : "/");
507   strcat (u->path, abs_ftp ? (u->dir + 1) : u->dir);
508   strcat (u->path, *u->dir ? "/" : "");
509   strcat (u->path, u->file);
510   URL_CLEANSE (u->path);
511   DEBUGP (("newpath: %s\n", u->path));
512   /* Create the clean URL.  */
513   u->url = str_url (u, 0);
514   return URLOK;
515 }
516 \f
517 /* Special versions of DOTP and DDOTP for parse_dir(). */
518
519 #define PD_DOTP(x)  ((*(x) == '.') && (!*((x) + 1) || *((x) + 1) == '?'))
520 #define PD_DDOTP(x) ((*(x) == '.') && (*(x) == '.')             \
521                      && (!*((x) + 2) || *((x) + 2) == '?'))
522
523 /* Build the directory and filename components of the path.  Both
524    components are *separately* malloc-ed strings!  It does not change
525    the contents of path.
526
527    If the path ends with "." or "..", they are (correctly) counted as
528    directories.  */
529 static void
530 parse_dir (const char *path, char **dir, char **file)
531 {
532   int i, l;
533
534   l = urlpath_length (path);
535   for (i = l; i && path[i] != '/'; i--);
536
537   if (!i && *path != '/')   /* Just filename */
538     {
539       if (PD_DOTP (path) || PD_DDOTP (path))
540         {
541           *dir = strdupdelim (path, path + l);
542           *file = xstrdup (path + l); /* normally empty, but could
543                                          contain ?... */
544         }
545       else
546         {
547           *dir = xstrdup ("");     /* This is required because of FTP */
548           *file = xstrdup (path);
549         }
550     }
551   else if (!i)                 /* /filename */
552     {
553       if (PD_DOTP (path + 1) || PD_DDOTP (path + 1))
554         {
555           *dir = strdupdelim (path, path + l);
556           *file = xstrdup (path + l); /* normally empty, but could
557                                          contain ?... */
558         }
559       else
560         {
561           *dir = xstrdup ("/");
562           *file = xstrdup (path + 1);
563         }
564     }
565   else /* Nonempty directory with or without a filename */
566     {
567       if (PD_DOTP (path + i + 1) || PD_DDOTP (path + i + 1))
568         {
569           *dir = strdupdelim (path, path + l);
570           *file = xstrdup (path + l); /* normally empty, but could
571                                          contain ?... */
572         }
573       else
574         {
575           *dir = strdupdelim (path, path + i);
576           *file = xstrdup (path + i + 1);
577         }
578     }
579 }
580
581 /* Find the optional username and password within the URL, as per
582    RFC1738.  The returned user and passwd char pointers are
583    malloc-ed.  */
584 static uerr_t
585 parse_uname (const char *url, char **user, char **passwd)
586 {
587   int l;
588   const char *p, *col;
589   char **where;
590
591   *user = NULL;
592   *passwd = NULL;
593   url += skip_url (url);
594   /* Look for end of protocol string.  */
595   l = skip_proto (url);
596   if (!l)
597     return URLUNKNOWN;
598   /* Add protocol offset.  */
599   url += l;
600   /* Is there an `@' character?  */
601   for (p = url; *p && *p != '/'; p++)
602     if (*p == '@')
603       break;
604   /* If not, return.  */
605   if (*p != '@')
606     return URLOK;
607   /* Else find the username and password.  */
608   for (p = col = url; *p != '@'; p++)
609     {
610       if (*p == ':' && !*user)
611         {
612           *user = (char *)xmalloc (p - url + 1);
613           memcpy (*user, url, p - url);
614           (*user)[p - url] = '\0';
615           col = p + 1;
616         }
617     }
618   /* Decide whether you have only the username or both.  */
619   where = *user ? passwd : user;
620   *where = (char *)xmalloc (p - col + 1);
621   memcpy (*where, col, p - col);
622   (*where)[p - col] = '\0';
623   return URLOK;
624 }
625
626 /* If PATH ends with `;type=X', return the character X.  */
627 static char
628 process_ftp_type (char *path)
629 {
630   int len = strlen (path);
631
632   if (len >= 7
633       && !memcmp (path + len - 7, ";type=", 6))
634     {
635       path[len - 7] = '\0';
636       return path[len - 1];
637     }
638   else
639     return '\0';
640 }
641 \f
642 /* Return the URL as fine-formed string, with a proper protocol,
643    optional port number, directory and optional user/password.  If
644    HIDE is non-zero, password will be hidden.  The forbidden
645    characters in the URL will be cleansed.  */
646 char *
647 str_url (const struct urlinfo *u, int hide)
648 {
649   char *res, *host, *user, *passwd, *proto_name, *dir, *file;
650   int i, l, ln, lu, lh, lp, lf, ld;
651   unsigned short proto_default_port;
652
653   /* Look for the protocol name.  */
654   for (i = 0; i < ARRAY_SIZE (sup_protos); i++)
655     if (sup_protos[i].ind == u->proto)
656       break;
657   if (i == ARRAY_SIZE (sup_protos))
658     return NULL;
659   proto_name = sup_protos[i].name;
660   proto_default_port = sup_protos[i].port;
661   host = CLEANDUP (u->host);
662   dir = CLEANDUP (u->dir);
663   file = CLEANDUP (u->file);
664   user = passwd = NULL;
665   if (u->user)
666     user = CLEANDUP (u->user);
667   if (u->passwd)
668     {
669       int i;
670       passwd = CLEANDUP (u->passwd);
671       if (hide)
672         for (i = 0; passwd[i]; i++)
673           passwd[i] = 'x';
674     }
675   if (u->proto == URLFTP && *dir == '/')
676     {
677       char *tmp = (char *)xmalloc (strlen (dir) + 3);
678       /*sprintf (tmp, "%%2F%s", dir + 1);*/
679       tmp[0] = '%';
680       tmp[1] = '2';
681       tmp[2] = 'F';
682       strcpy (tmp + 3, dir + 1);
683       free (dir);
684       dir = tmp;
685     }
686
687   ln = strlen (proto_name);
688   lu = user ? strlen (user) : 0;
689   lp = passwd ? strlen (passwd) : 0;
690   lh = strlen (host);
691   ld = strlen (dir);
692   lf = strlen (file);
693   res = (char *)xmalloc (ln + lu + lp + lh + ld + lf + 20); /* safe sex */
694   /* sprintf (res, "%s%s%s%s%s%s:%d/%s%s%s", proto_name,
695      (user ? user : ""), (passwd ? ":" : ""),
696      (passwd ? passwd : ""), (user ? "@" : ""),
697      host, u->port, dir, *dir ? "/" : "", file); */
698   l = 0;
699   memcpy (res, proto_name, ln);
700   l += ln;
701   if (user)
702     {
703       memcpy (res + l, user, lu);
704       l += lu;
705       if (passwd)
706         {
707           res[l++] = ':';
708           memcpy (res + l, passwd, lp);
709           l += lp;
710         }
711       res[l++] = '@';
712     }
713   memcpy (res + l, host, lh);
714   l += lh;
715   if (u->port != proto_default_port)
716     {
717       res[l++] = ':';
718       long_to_string (res + l, (long)u->port);
719       l += numdigit (u->port);
720     }
721   res[l++] = '/';
722   memcpy (res + l, dir, ld);
723   l += ld;
724   if (*dir)
725     res[l++] = '/';
726   strcpy (res + l, file);
727   free (host);
728   free (dir);
729   free (file);
730   FREE_MAYBE (user);
731   FREE_MAYBE (passwd);
732   return res;
733 }
734
735 /* Check whether two URL-s are equivalent, i.e. pointing to the same
736    location.  Uses parseurl to parse them, and compares the canonical
737    forms.
738
739    Returns 1 if the URL1 is equivalent to URL2, 0 otherwise.  Also
740    return 0 on error.  */
741 int
742 url_equal (const char *url1, const char *url2)
743 {
744   struct urlinfo *u1, *u2;
745   uerr_t err;
746   int res;
747
748   u1 = newurl ();
749   err = parseurl (url1, u1, 0);
750   if (err != URLOK)
751     {
752       freeurl (u1, 1);
753       return 0;
754     }
755   u2 = newurl ();
756   err = parseurl (url2, u2, 0);
757   if (err != URLOK)
758     {
759       freeurl (u2, 1);
760       return 0;
761     }
762   res = !strcmp (u1->url, u2->url);
763   freeurl (u1, 1);
764   freeurl (u2, 1);
765   return res;
766 }
767 \f
768 /* Find URL of format scheme:hostname[:port]/dir in a buffer.  The
769    buffer may contain pretty much anything; no errors are signaled.  */
770 static const char *
771 findurl (const char *buf, int howmuch, int *count)
772 {
773   char **prot;
774   const char *s1, *s2;
775
776   for (s1 = buf; howmuch; s1++, howmuch--)
777     for (prot = protostrings; *prot; prot++)
778       if (howmuch <= strlen (*prot))
779         continue;
780       else if (!strncasecmp (*prot, s1, strlen (*prot)))
781         {
782           for (s2 = s1, *count = 0;
783                howmuch && *s2 && *s2 >= 32 && *s2 < 127 && !ISSPACE (*s2) &&
784                  !strchr (URL_SEPARATOR, *s2);
785                s2++, (*count)++, howmuch--);
786           return s1;
787         }
788   return NULL;
789 }
790
791 /* Scans the file for signs of URL-s.  Returns a vector of pointers,
792    each pointer representing a URL string.  The file is *not* assumed
793    to be HTML.  */
794 urlpos *
795 get_urls_file (const char *file)
796 {
797   long nread;
798   FILE *fp;
799   char *buf;
800   const char *pbuf;
801   int size;
802   urlpos *first, *current, *old;
803
804   if (file && !HYPHENP (file))
805     {
806       fp = fopen (file, "rb");
807       if (!fp)
808         {
809           logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
810           return NULL;
811         }
812     }
813   else
814     fp = stdin;
815   /* Load the file.  */
816   load_file (fp, &buf, &nread);
817   if (file && !HYPHENP (file))
818     fclose (fp);
819   DEBUGP (("Loaded %s (size %ld).\n", file, nread));
820   first = current = NULL;
821   /* Fill the linked list with URLs.  */
822   for (pbuf = buf; (pbuf = findurl (pbuf, nread - (pbuf - buf), &size));
823        pbuf += size)
824     {
825       /* Allocate the space.  */
826       old = current;
827       current = (urlpos *)xmalloc (sizeof (urlpos));
828       if (old)
829         old->next = current;
830       memset (current, 0, sizeof (*current));
831       current->next = NULL;
832       current->url = (char *)xmalloc (size + 1);
833       memcpy (current->url, pbuf, size);
834       current->url[size] = '\0';
835       if (!first)
836         first = current;
837     }
838   /* Free the buffer.  */
839   free (buf);
840
841   return first;
842 }
843
844 /* Similar to get_urls_file, but for HTML files.  FILE is scanned as
845    an HTML document using htmlfindurl(), which see.  get_urls_html()
846    constructs the HTML-s from the relative href-s.
847
848    If SILENT is non-zero, do not barf on baseless relative links.  */
849 urlpos *
850 get_urls_html (const char *file, const char *this_url, int silent,
851                int dash_p_leaf_HTML)
852 {
853   long nread;
854   FILE *fp;
855   char *orig_buf;
856   const char *buf;
857   int step, first_time;
858   urlpos *first, *current, *old;
859
860   if (file && !HYPHENP (file))
861     {
862       fp = fopen (file, "rb");
863       if (!fp)
864         {
865           logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
866           return NULL;
867         }
868     }
869   else
870     fp = stdin;
871   /* Load the file.  */
872   load_file (fp, &orig_buf, &nread);
873   if (file && !HYPHENP (file))
874     fclose (fp);
875   DEBUGP (("Loaded HTML file %s (size %ld).\n", file, nread));
876   first = current = NULL;
877   first_time = 1;
878   /* Iterate over the URLs in BUF, picked by htmlfindurl().  */
879   for (buf = orig_buf;
880        (buf = htmlfindurl (buf, nread - (buf - orig_buf), &step, first_time,
881                            dash_p_leaf_HTML));
882        buf += step)
883     {
884       int i, no_proto;
885       int size = step;
886       const char *pbuf = buf;
887       char *constr, *base;
888       const char *cbase;
889       char *needs_freeing, *url_data;
890
891       first_time = 0;
892
893       /* A frequent phenomenon that needs to be handled are pages
894          generated by brain-damaged HTML generators, which refer to to
895          URI-s as <a href="<spaces>URI<spaces>">.  We simply ignore
896          any spaces at the beginning or at the end of the string.
897          This is probably not strictly correct, but that's what the
898          browsers do, so we may follow.  May the authors of "WYSIWYG"
899          HTML tools burn in hell for the damage they've inflicted!  */
900       while ((pbuf < buf + step) && ISSPACE (*pbuf))
901         {
902           ++pbuf;
903           --size;
904         }
905       while (size && ISSPACE (pbuf[size - 1]))
906         --size;
907       if (!size)
908         break;
909
910       /* It would be nice if we could avoid allocating memory in this
911          loop, but I don't see an easy way.  To process the entities,
912          we need to either copy the data, or change it destructively.
913          I choose the former.
914
915          We have two pointers: needs_freeing and url_data, because the
916          code below does thing like url_data += <something>, and we
917          want to pass the original string to free(). */
918       needs_freeing = url_data = html_decode_entities (pbuf, pbuf + size);
919       size = strlen (url_data);
920
921       for (i = 0; protostrings[i]; i++)
922         {
923           if (!strncasecmp (protostrings[i], url_data,
924                             MINVAL (strlen (protostrings[i]), size)))
925             break;
926         }
927       /* Check for http:RELATIVE_URI.  See below for details.  */
928       if (protostrings[i]
929           && !(strncasecmp (url_data, "http:", 5) == 0
930                && strncasecmp (url_data, "http://", 7) != 0))
931         {
932           no_proto = 0;
933         }
934       else
935         {
936           no_proto = 1;
937           /* This is for extremely brain-damaged pages that refer to
938              relative URI-s as <a href="http:URL">.  Just strip off the
939              silly leading "http:" (as well as any leading blanks
940              before it).  */
941           if ((size > 5) && !strncasecmp ("http:", url_data, 5))
942             url_data += 5, size -= 5;
943         }
944       if (!no_proto)
945         {
946           for (i = 0; i < ARRAY_SIZE (sup_protos); i++)
947             {
948               if (!strncasecmp (sup_protos[i].name, url_data,
949                                MINVAL (strlen (sup_protos[i].name), size)))
950                 break;
951             }
952           /* Do *not* accept a non-supported protocol.  */
953           if (i == ARRAY_SIZE (sup_protos))
954             {
955               free (needs_freeing);
956               continue;
957             }
958         }
959       if (no_proto)
960         {
961           /* First, construct the base, which can be relative itself.
962
963              Criteria for creating the base are:
964              1) html_base created by <base href="...">
965              2) current URL
966              3) base provided from the command line */
967           cbase = html_base ();
968           if (!cbase)
969             cbase = this_url;
970           if (!cbase)
971             cbase = opt.base_href;
972           if (!cbase)             /* Error condition -- a baseless
973                                      relative link.  */
974             {
975               if (!opt.quiet && !silent)
976                 {
977                   /* Use malloc, not alloca because this is called in
978                      a loop. */
979                   char *temp = (char *)malloc (size + 1);
980                   strncpy (temp, url_data, size);
981                   temp[size] = '\0';
982                   logprintf (LOG_NOTQUIET,
983                              _("Error (%s): Link %s without a base provided.\n"),
984                              file, temp);
985                   free (temp);
986                 }
987               free (needs_freeing);
988               continue;
989             }
990           if (this_url)
991             base = construct (this_url, cbase, strlen (cbase),
992                               !has_proto (cbase));
993           else
994             {
995               /* Base must now be absolute, with host name and
996                  protocol.  */
997               if (!has_proto (cbase))
998                 {
999                   logprintf (LOG_NOTQUIET, _("\
1000 Error (%s): Base %s relative, without referer URL.\n"),
1001                              file, cbase);
1002                   free (needs_freeing);
1003                   continue;
1004                 }
1005               base = xstrdup (cbase);
1006             }
1007           constr = construct (base, url_data, size, no_proto);
1008           free (base);
1009         }
1010       else /* has proto */
1011         {
1012           constr = (char *)xmalloc (size + 1);
1013           strncpy (constr, url_data, size);
1014           constr[size] = '\0';
1015         }
1016 #ifdef DEBUG
1017       if (opt.debug)
1018         {
1019           char *tmp;
1020           const char *tmp2;
1021
1022           tmp2 = html_base ();
1023           /* Use malloc, not alloca because this is called in a loop. */
1024           tmp = (char *)xmalloc (size + 1);
1025           strncpy (tmp, url_data, size);
1026           tmp[size] = '\0';
1027           logprintf (LOG_ALWAYS,
1028                      "file %s; this_url %s; base %s\nlink: %s; constr: %s\n",
1029                      file, this_url ? this_url : "(null)",
1030                      tmp2 ? tmp2 : "(null)", tmp, constr);
1031           free (tmp);
1032         }
1033 #endif
1034
1035       /* Allocate the space.  */
1036       old = current;
1037       current = (urlpos *)xmalloc (sizeof (urlpos));
1038       if (old)
1039         old->next = current;
1040       if (!first)
1041         first = current;
1042       /* Fill the values.  */
1043       memset (current, 0, sizeof (*current));
1044       current->next = NULL;
1045       current->url = constr;
1046       current->size = step;
1047       current->pos = buf - orig_buf;
1048       /* A URL is relative if the host and protocol are not named,
1049          and the name does not start with `/'.  */
1050       if (no_proto && *url_data != '/')
1051         current->flags |= (URELATIVE | UNOPROTO);
1052       else if (no_proto)
1053         current->flags |= UNOPROTO;
1054       free (needs_freeing);
1055     }
1056   free (orig_buf);
1057
1058   return first;
1059 }
1060 \f
1061 /* Free the linked list of urlpos.  */
1062 void
1063 free_urlpos (urlpos *l)
1064 {
1065   while (l)
1066     {
1067       urlpos *next = l->next;
1068       free (l->url);
1069       FREE_MAYBE (l->local_name);
1070       free (l);
1071       l = next;
1072     }
1073 }
1074
1075 /* Rotate FNAME opt.backups times */
1076 void
1077 rotate_backups(const char *fname)
1078 {
1079   int maxlen = strlen (fname) + 1 + numdigit (opt.backups) + 1;
1080   char *from = (char *)alloca (maxlen);
1081   char *to = (char *)alloca (maxlen);
1082   struct stat sb;
1083   int i;
1084
1085   if (stat (fname, &sb) == 0)
1086     if (S_ISREG (sb.st_mode) == 0)
1087       return;
1088
1089   for (i = opt.backups; i > 1; i--)
1090     {
1091       sprintf (from, "%s.%d", fname, i - 1);
1092       sprintf (to, "%s.%d", fname, i);
1093       /* #### This will fail on machines without the rename() system
1094          call.  */
1095       rename (from, to);
1096     }
1097
1098   sprintf (to, "%s.%d", fname, 1);
1099   rename(fname, to);
1100 }
1101
1102 /* Create all the necessary directories for PATH (a file).  Calls
1103    mkdirhier() internally.  */
1104 int
1105 mkalldirs (const char *path)
1106 {
1107   const char *p;
1108   char *t;
1109   struct stat st;
1110   int res;
1111
1112   p = path + strlen (path);
1113   for (; *p != '/' && p != path; p--);
1114   /* Don't create if it's just a file.  */
1115   if ((p == path) && (*p != '/'))
1116     return 0;
1117   t = strdupdelim (path, p);
1118   /* Check whether the directory exists.  */
1119   if ((stat (t, &st) == 0))
1120     {
1121       if (S_ISDIR (st.st_mode))
1122         {
1123           free (t);
1124           return 0;
1125         }
1126       else
1127         {
1128           /* If the dir exists as a file name, remove it first.  This
1129              is *only* for Wget to work with buggy old CERN http
1130              servers.  Here is the scenario: When Wget tries to
1131              retrieve a directory without a slash, e.g.
1132              http://foo/bar (bar being a directory), CERN server will
1133              not redirect it too http://foo/bar/ -- it will generate a
1134              directory listing containing links to bar/file1,
1135              bar/file2, etc.  Wget will lose because it saves this
1136              HTML listing to a file `bar', so it cannot create the
1137              directory.  To work around this, if the file of the same
1138              name exists, we just remove it and create the directory
1139              anyway.  */
1140           DEBUGP (("Removing %s because of directory danger!\n", t));
1141           unlink (t);
1142         }
1143     }
1144   res = make_directory (t);
1145   if (res != 0)
1146     logprintf (LOG_NOTQUIET, "%s: %s", t, strerror (errno));
1147   free (t);
1148   return res;
1149 }
1150
1151 static int
1152 count_slashes (const char *s)
1153 {
1154   int i = 0;
1155   while (*s)
1156     if (*s++ == '/')
1157       ++i;
1158   return i;
1159 }
1160
1161 /* Return the path name of the URL-equivalent file name, with a
1162    remote-like structure of directories.  */
1163 static char *
1164 mkstruct (const struct urlinfo *u)
1165 {
1166   char *host, *dir, *file, *res, *dirpref;
1167   int l;
1168
1169   assert (u->dir != NULL);
1170   assert (u->host != NULL);
1171
1172   if (opt.cut_dirs)
1173     {
1174       char *ptr = u->dir + (*u->dir == '/');
1175       int slash_count = 1 + count_slashes (ptr);
1176       int cut = MINVAL (opt.cut_dirs, slash_count);
1177       for (; cut && *ptr; ptr++)
1178         if (*ptr == '/')
1179           --cut;
1180       STRDUP_ALLOCA (dir, ptr);
1181     }
1182   else
1183     dir = u->dir + (*u->dir == '/');
1184
1185   host = xstrdup (u->host);
1186   /* Check for the true name (or at least a consistent name for saving
1187      to directory) of HOST, reusing the hlist if possible.  */
1188   if (opt.add_hostdir && !opt.simple_check)
1189     {
1190       char *nhost = realhost (host);
1191       free (host);
1192       host = nhost;
1193     }
1194   /* Add dir_prefix and hostname (if required) to the beginning of
1195      dir.  */
1196   if (opt.add_hostdir)
1197     {
1198       if (!DOTP (opt.dir_prefix))
1199         {
1200           dirpref = (char *)alloca (strlen (opt.dir_prefix) + 1
1201                                     + strlen (host) + 1);
1202           sprintf (dirpref, "%s/%s", opt.dir_prefix, host);
1203         }
1204       else
1205         STRDUP_ALLOCA (dirpref, host);
1206     }
1207   else                         /* not add_hostdir */
1208     {
1209       if (!DOTP (opt.dir_prefix))
1210         dirpref = opt.dir_prefix;
1211       else
1212         dirpref = "";
1213     }
1214   free (host);
1215
1216   /* If there is a prefix, prepend it.  */
1217   if (*dirpref)
1218     {
1219       char *newdir = (char *)alloca (strlen (dirpref) + 1 + strlen (dir) + 2);
1220       sprintf (newdir, "%s%s%s", dirpref, *dir == '/' ? "" : "/", dir);
1221       dir = newdir;
1222     }
1223   dir = xstrdup (dir);
1224   URL_CLEANSE (dir);
1225   l = strlen (dir);
1226   if (l && dir[l - 1] == '/')
1227     dir[l - 1] = '\0';
1228
1229   if (!*u->file)
1230     file = "index.html";
1231   else
1232     file = u->file;
1233
1234   /* Finally, construct the full name.  */
1235   res = (char *)xmalloc (strlen (dir) + 1 + strlen (file) + 1);
1236   sprintf (res, "%s%s%s", dir, *dir ? "/" : "", file);
1237   free (dir);
1238   return res;
1239 }
1240
1241 /* Create a unique filename, corresponding to a given URL.  Calls
1242    mkstruct if necessary.  Does *not* actually create any directories.  */
1243 char *
1244 url_filename (const struct urlinfo *u)
1245 {
1246   char *file, *name;
1247   int have_prefix = 0;          /* whether we must prepend opt.dir_prefix */
1248
1249   if (opt.dirstruct)
1250     {
1251       file = mkstruct (u);
1252       have_prefix = 1;
1253     }
1254   else
1255     {
1256       if (!*u->file)
1257         file = xstrdup ("index.html");
1258       else
1259         file = xstrdup (u->file);
1260     }
1261
1262   if (!have_prefix)
1263     {
1264       /* Check whether the prefix directory is something other than "."
1265          before prepending it.  */
1266       if (!DOTP (opt.dir_prefix))
1267         {
1268           char *nfile = (char *)xmalloc (strlen (opt.dir_prefix)
1269                                          + 1 + strlen (file) + 1);
1270           sprintf (nfile, "%s/%s", opt.dir_prefix, file);
1271           free (file);
1272           file = nfile;
1273         }
1274     }
1275   /* DOS-ish file systems don't like `%' signs in them; we change it
1276      to `@'.  */
1277 #ifdef WINDOWS
1278   {
1279     char *p = file;
1280     for (p = file; *p; p++)
1281       if (*p == '%')
1282         *p = '@';
1283   }
1284 #endif /* WINDOWS */
1285
1286   /* Check the cases in which the unique extensions are not used:
1287      1) Clobbering is turned off (-nc).
1288      2) Retrieval with regetting.
1289      3) Timestamping is used.
1290      4) Hierarchy is built.
1291
1292      The exception is the case when file does exist and is a
1293      directory (actually support for bad httpd-s).  */
1294   if ((opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct)
1295       && !(file_exists_p (file) && !file_non_directory_p (file)))
1296     return file;
1297
1298   /* Find a unique name.  */
1299   name = unique_name (file);
1300   free (file);
1301   return name;
1302 }
1303
1304 /* Like strlen(), but allow the URL to be ended with '?'.  */
1305 static int
1306 urlpath_length (const char *url)
1307 {
1308   const char *q = strchr (url, '?');
1309   if (q)
1310     return q - url;
1311   return strlen (url);
1312 }
1313
1314 /* Find the last occurrence of character C in the range [b, e), or
1315    NULL, if none are present.  This is almost completely equivalent to
1316    { *e = '\0'; return strrchr(b); }, except that it doesn't change
1317    the contents of the string.  */
1318 static const char *
1319 find_last_char (const char *b, const char *e, char c)
1320 {
1321   for (; e > b; e--)
1322     if (*e == c)
1323       return e;
1324   return NULL;
1325 }
1326
1327 /* Construct a URL by concatenating an absolute URL and a path, which
1328    may or may not be absolute.  This tries to behave "reasonably" in
1329    all foreseeable cases.  It employs little specific knowledge about
1330    protocols or URL-specific stuff -- it just works on strings.  */
1331 static char *
1332 construct (const char *url, const char *sub, int subsize, int no_proto)
1333 {
1334   char *constr;
1335
1336   if (no_proto)
1337     {
1338       const char *end = url + urlpath_length (url);
1339
1340       if (*sub != '/')
1341         {
1342           /* SUB is a relative URL: we need to replace everything
1343              after last slash (possibly empty) with SUB.
1344
1345              So, if URL is "whatever/foo/bar", and SUB is "qux/xyzzy",
1346              our result should be "whatever/foo/qux/xyzzy".  */
1347           int need_explicit_slash = 0;
1348           int span;
1349           const char *start_insert;
1350           const char *last_slash = find_last_char (url, end, '/'); /* the last slash. */
1351           if (!last_slash)
1352             {
1353               /* No slash found at all.  Append SUB to what we have,
1354                  but we'll need a slash as a separator.
1355
1356                  Example: if url == "foo" and sub == "qux/xyzzy", then
1357                  we cannot just append sub to url, because we'd get
1358                  "fooqux/xyzzy", whereas what we want is
1359                  "foo/qux/xyzzy".
1360
1361                  To make sure the / gets inserted, we set
1362                  need_explicit_slash to 1.  We also set start_insert
1363                  to end + 1, so that the length calculations work out
1364                  correctly for one more (slash) character.  Accessing
1365                  that character is fine, since it will be the
1366                  delimiter, '\0' or '?'.  */
1367               /* example: "foo?..." */
1368               /*               ^    ('?' gets changed to '/') */
1369               start_insert = end + 1;
1370               need_explicit_slash = 1;
1371             }
1372           else
1373             {
1374               /* example: "whatever/foo/bar" */
1375               /*                        ^    */
1376               start_insert = last_slash + 1;
1377             }
1378
1379           span = start_insert - url;
1380           constr = (char *)xmalloc (span + subsize + 1);
1381           if (span)
1382             memcpy (constr, url, span);
1383           if (need_explicit_slash)
1384             constr[span - 1] = '/';
1385           if (subsize)
1386             memcpy (constr + span, sub, subsize);
1387           constr[span + subsize] = '\0';
1388         }
1389       else /* *sub == `/' */
1390         {
1391           /* SUB is an absolute path: we need to replace everything
1392              after (and including) the FIRST slash with SUB.
1393
1394              So, if URL is "http://host/whatever/foo/bar", and SUB is
1395              "/qux/xyzzy", our result should be
1396              "http://host/qux/xyzzy".  */
1397           int span;
1398           const char *slash;
1399           const char *start_insert = NULL; /* for gcc to shut up. */
1400           const char *pos = url;
1401           int seen_slash_slash = 0;
1402           /* We're looking for the first slash, but want to ignore
1403              double slash. */
1404         again:
1405           slash = memchr (pos, '/', end - pos);
1406           if (slash && !seen_slash_slash)
1407             if (*(slash + 1) == '/')
1408               {
1409                 pos = slash + 2;
1410                 seen_slash_slash = 1;
1411                 goto again;
1412               }
1413
1414           /* At this point, SLASH is the location of the first / after
1415              "//", or the first slash altogether.  START_INSERT is the
1416              pointer to the location where SUB will be inserted.  When
1417              examining the last two examples, keep in mind that SUB
1418              begins with '/'. */
1419
1420           if (!slash && !seen_slash_slash)
1421             /* example: "foo" */
1422             /*           ^    */
1423             start_insert = url;
1424           else if (!slash && seen_slash_slash)
1425             /* example: "http://foo" */
1426             /*                     ^ */
1427             start_insert = end;
1428           else if (slash && !seen_slash_slash)
1429             /* example: "foo/bar" */
1430             /*           ^        */
1431             start_insert = url;
1432           else if (slash && seen_slash_slash)
1433             /* example: "http://something/" */
1434             /*                           ^  */
1435             start_insert = slash;
1436
1437           span = start_insert - url;
1438           constr = (char *)xmalloc (span + subsize + 1);
1439           if (span)
1440             memcpy (constr, url, span);
1441           if (subsize)
1442             memcpy (constr + span, sub, subsize);
1443           constr[span + subsize] = '\0';
1444         }
1445     }
1446   else /* !no_proto */
1447     {
1448       constr = strdupdelim (sub, sub + subsize);
1449     }
1450   return constr;
1451 }
1452
1453 /* Like the function above, but with a saner caller interface. */
1454 char *
1455 url_concat (const char *base_url, const char *new_url)
1456 {
1457   return construct (base_url, new_url, strlen (new_url), !has_proto (new_url));
1458 }
1459 \f
1460 /* Optimize URL by host, destructively replacing u->host with realhost
1461    (u->host).  Do this regardless of opt.simple_check.  */
1462 void
1463 opt_url (struct urlinfo *u)
1464 {
1465   /* Find the "true" host.  */
1466   char *host = realhost (u->host);
1467   free (u->host);
1468   u->host = host;
1469   assert (u->dir != NULL);      /* the URL must have been parsed */
1470   /* Refresh the printed representation.  */
1471   free (u->url);
1472   u->url = str_url (u, 0);
1473 }
1474
1475 /* This beautiful kludge is fortunately not needed, as I've made
1476    parse_dir do the (almost) right thing, so that a query can never
1477    become a part of directory.  */
1478 #if 0
1479 /* Call path_simplify, but make sure that the part after the
1480    question-mark, if any, is not destroyed by path_simplify's
1481    "optimizations".  */
1482 void
1483 path_simplify_with_kludge (char *path)
1484 {
1485   char *query = strchr (path, '?');
1486   if (query)
1487     /* path_simplify also works destructively, so we also have the
1488        license to write. */
1489     *query = '\0';
1490   path_simplify (path);
1491   if (query)
1492     {
1493       char *newend = path + strlen (path);
1494       *query = '?';
1495       if (newend != query)
1496         memmove (newend, query, strlen (query) + 1);
1497     }
1498 }
1499 #endif
1500 \f
1501 /* Returns proxy host address, in accordance with PROTO.  */
1502 char *
1503 getproxy (uerr_t proto)
1504 {
1505   if (proto == URLHTTP)
1506     return opt.http_proxy ? opt.http_proxy : getenv ("http_proxy");
1507   else if (proto == URLFTP)
1508     return opt.ftp_proxy ? opt.ftp_proxy : getenv ("ftp_proxy");
1509   else
1510     return NULL;
1511 }
1512
1513 /* Should a host be accessed through proxy, concerning no_proxy?  */
1514 int
1515 no_proxy_match (const char *host, const char **no_proxy)
1516 {
1517   if (!no_proxy)
1518     return 1;
1519   else
1520     return !sufmatch (no_proxy, host);
1521 }
1522 \f
1523 /* Change the links in an HTML document.  Accepts a structure that
1524    defines the positions of all the links.  */
1525 void
1526 convert_links (const char *file, urlpos *l)
1527 {
1528   FILE               *fp;
1529   char               *buf, *p, *p2;
1530   downloaded_file_t  downloaded_file_return;
1531   long               size;
1532
1533   logprintf (LOG_VERBOSE, _("Converting %s... "), file);
1534   /* Read from the file....  */
1535   fp = fopen (file, "rb");
1536   if (!fp)
1537     {
1538       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
1539                  file, strerror (errno));
1540       return;
1541     }
1542   /* ...to a buffer.  */
1543   load_file (fp, &buf, &size);
1544   fclose (fp);
1545
1546   downloaded_file_return = downloaded_file(CHECK_FOR_FILE, file);
1547
1548   if (opt.backup_converted && downloaded_file_return)
1549     /* Rather than just writing over the original .html file with the converted
1550        version, save the former to *.orig.  Note we only do this for files we've
1551        _successfully_ downloaded, so we don't clobber .orig files sitting around
1552        from previous invocations. */
1553     {
1554       /* Construct the backup filename as the original name plus ".orig". */
1555       size_t         filename_len = strlen(file);
1556       char*          filename_plus_orig_suffix;
1557       boolean        already_wrote_backup_file = FALSE;
1558       slist*         converted_file_ptr;
1559       static slist*  converted_files = NULL;
1560
1561       if (downloaded_file_return == FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED)
1562         {
1563           /* Just write "orig" over "html".  We need to do it this way because
1564              when we're checking to see if we've downloaded the file before (to
1565              see if we can skip downloading it), we don't know if it's a
1566              text/html file.  Therefore we don't know yet at that stage that -E
1567              is going to cause us to tack on ".html", so we need to compare
1568              vs. the original URL plus ".orig", not the original URL plus
1569              ".html.orig". */
1570           filename_plus_orig_suffix = xmalloc(filename_len + 1);
1571           strcpy(filename_plus_orig_suffix, file);
1572           strcpy((filename_plus_orig_suffix + filename_len) - 4, "orig");
1573         }
1574       else /* downloaded_file_return == FILE_DOWNLOADED_NORMALLY */
1575         {
1576           /* Append ".orig" to the name. */
1577           filename_plus_orig_suffix = xmalloc(filename_len + sizeof(".orig"));
1578           strcpy(filename_plus_orig_suffix, file);
1579           strcpy(filename_plus_orig_suffix + filename_len, ".orig");
1580         }
1581
1582       /* We can get called twice on the same URL thanks to the
1583          convert_all_links() call in main().  If we write the .orig file each
1584          time in such a case, it'll end up containing the first-pass conversion,
1585          not the original file.  So, see if we've already been called on this
1586          file. */
1587       converted_file_ptr = converted_files;
1588       while (converted_file_ptr != NULL)
1589         if (strcmp(converted_file_ptr->string, file) == 0)
1590           {
1591             already_wrote_backup_file = TRUE;
1592             break;
1593           }
1594         else
1595           converted_file_ptr = converted_file_ptr->next;
1596
1597       if (!already_wrote_backup_file)
1598         {
1599           /* Rename <file> to <file>.orig before former gets written over. */
1600           if (rename(file, filename_plus_orig_suffix) != 0)
1601             logprintf (LOG_NOTQUIET, _("Cannot back up %s as %s: %s\n"),
1602                        file, filename_plus_orig_suffix, strerror (errno));
1603
1604           /* Remember that we've already written a .orig backup for this file.
1605              Note that we never free this memory since we need it till the
1606              convert_all_links() call, which is one of the last things the
1607              program does before terminating.  BTW, I'm not sure if it would be
1608              safe to just set 'converted_file_ptr->string' to 'file' below,
1609              rather than making a copy of the string...  Another note is that I
1610              thought I could just add a field to the urlpos structure saying
1611              that we'd written a .orig file for this URL, but that didn't work,
1612              so I had to make this separate list. */
1613           converted_file_ptr = xmalloc(sizeof(*converted_file_ptr));
1614           converted_file_ptr->string = xstrdup(file);  /* die on out-of-mem. */
1615           converted_file_ptr->next = converted_files;
1616           converted_files = converted_file_ptr;
1617         }
1618
1619       free(filename_plus_orig_suffix);
1620     }
1621   /* Now open the file for writing.  */
1622   fp = fopen (file, "wb");
1623   if (!fp)
1624     {
1625       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
1626                  file, strerror (errno));
1627       free (buf);
1628       return;
1629     }
1630   /* Presumably we have to loop through multiple URLs here (even though we're
1631      only talking about a single local file) because of the -O option. */
1632   for (p = buf; l; l = l->next)
1633     {
1634       if (l->pos >= size)
1635         {
1636           DEBUGP (("Something strange is going on.  Please investigate."));
1637           break;
1638         }
1639       /* If the URL already is relative or it is not to be converted
1640          for some other reason (e.g. because of not having been
1641          downloaded in the first place), skip it.  */
1642       if ((l->flags & URELATIVE) || !(l->flags & UABS2REL))
1643         {
1644           DEBUGP (("Skipping %s at position %d (flags %d).\n", l->url,
1645                    l->pos, l->flags));
1646           continue;
1647         }
1648       /* Else, reach the position of the offending URL, echoing
1649          everything up to it to the outfile.  */
1650       for (p2 = buf + l->pos; p < p2; p++)
1651         putc (*p, fp);
1652       if (l->flags & UABS2REL)
1653         /* Convert absolute URL to relative. */
1654         {
1655           char *newname = construct_relative (file, l->local_name);
1656           fprintf (fp, "%s", newname);
1657           DEBUGP (("ABS2REL: %s to %s at position %d in %s.\n",
1658                    l->url, newname, l->pos, file));
1659           free (newname);
1660         }
1661       p += l->size;
1662     }
1663   /* Output the rest of the file. */
1664   if (p - buf < size)
1665     {
1666       for (p2 = buf + size; p < p2; p++)
1667         putc (*p, fp);
1668     }
1669   fclose (fp);
1670   free (buf);
1671   logputs (LOG_VERBOSE, _("done.\n"));
1672 }
1673
1674 /* Construct and return a malloced copy of the relative link from two
1675    pieces of information: local name S1 of the referring file and
1676    local name S2 of the referred file.
1677
1678    So, if S1 is "jagor.srce.hr/index.html" and S2 is
1679    "jagor.srce.hr/images/news.gif", the function will return
1680    "images/news.gif".
1681
1682    Alternately, if S1 is "fly.cc.fer.hr/ioccc/index.html", and S2 is
1683    "fly.cc.fer.hr/images/fly.gif", the function will return
1684    "../images/fly.gif".
1685
1686    Caveats: S1 should not begin with `/', unless S2 also begins with
1687    '/'.  S1 should not contain things like ".." and such --
1688    construct_relative ("fly/ioccc/../index.html",
1689    "fly/images/fly.gif") will fail.  (A workaround is to call
1690    something like path_simplify() on S1).  */
1691 static char *
1692 construct_relative (const char *s1, const char *s2)
1693 {
1694   int i, cnt, sepdirs1;
1695   char *res;
1696
1697   if (*s2 == '/')
1698     return xstrdup (s2);
1699   /* S1 should *not* be absolute, if S2 wasn't.  */
1700   assert (*s1 != '/');
1701   i = cnt = 0;
1702   /* Skip the directories common to both strings.  */
1703   while (1)
1704     {
1705       while (s1[i] && s2[i]
1706              && (s1[i] == s2[i])
1707              && (s1[i] != '/')
1708              && (s2[i] != '/'))
1709         ++i;
1710       if (s1[i] == '/' && s2[i] == '/')
1711         cnt = ++i;
1712       else
1713         break;
1714     }
1715   for (sepdirs1 = 0; s1[i]; i++)
1716     if (s1[i] == '/')
1717       ++sepdirs1;
1718   /* Now, construct the file as of:
1719      - ../ repeated sepdirs1 time
1720      - all the non-mutual directories of S2.  */
1721   res = (char *)xmalloc (3 * sepdirs1 + strlen (s2 + cnt) + 1);
1722   for (i = 0; i < sepdirs1; i++)
1723     memcpy (res + 3 * i, "../", 3);
1724   strcpy (res + 3 * i, s2 + cnt);
1725   return res;
1726 }
1727 \f
1728 /* Add URL to the head of the list L.  */
1729 urlpos *
1730 add_url (urlpos *l, const char *url, const char *file)
1731 {
1732   urlpos *t;
1733
1734   t = (urlpos *)xmalloc (sizeof (urlpos));
1735   memset (t, 0, sizeof (*t));
1736   t->url = xstrdup (url);
1737   t->local_name = xstrdup (file);
1738   t->next = l;
1739   return t;
1740 }
1741
1742
1743 /* Remembers which files have been downloaded.  In the standard case, should be
1744    called with mode == FILE_DOWNLOADED_NORMALLY for each file we actually
1745    download successfully (i.e. not for ones we have failures on or that we skip
1746    due to -N).
1747
1748    When we've downloaded a file and tacked on a ".html" extension due to -E,
1749    call this function with FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED rather than
1750    FILE_DOWNLOADED_NORMALLY.
1751
1752    If you just want to check if a file has been previously added without adding
1753    it, call with mode == CHECK_FOR_FILE.  Please be sure to call this function
1754    with local filenames, not remote URLs. */
1755 downloaded_file_t
1756 downloaded_file (downloaded_file_t  mode, const char*  file)
1757 {
1758   typedef struct _downloaded_file_list
1759   {
1760     char*                          file;
1761     downloaded_file_t              download_type;
1762     struct _downloaded_file_list*  next;
1763   } downloaded_file_list;
1764   
1765   boolean                       found_file = FALSE;
1766   static downloaded_file_list*  downloaded_files = NULL;
1767   downloaded_file_list*         rover = downloaded_files;
1768
1769   while (rover != NULL)
1770     if (strcmp(rover->file, file) == 0)
1771       {
1772         found_file = TRUE;
1773         break;
1774       }
1775     else
1776       rover = rover->next;
1777
1778   if (found_file)
1779     return rover->download_type;  /* file had already been downloaded */
1780   else
1781     {
1782       if (mode != CHECK_FOR_FILE)
1783         {
1784           rover = xmalloc(sizeof(*rover));
1785           rover->file = xstrdup(file); /* use xstrdup() so die on out-of-mem. */
1786           rover->download_type = mode;
1787           rover->next = downloaded_files;
1788           downloaded_files = rover;
1789         }
1790
1791       return FILE_NOT_ALREADY_DOWNLOADED;
1792     }
1793 }