]> sjero.net Git - wget/blob - src/url.c
44e7280d0994d91dd34be34aae66d3f15fefc689
[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 if (last_slash && last_slash != url && *(last_slash - 1) == '/')
1373             {
1374               /* example: http://host"  */
1375               /*                      ^ */
1376               start_insert = end + 1;
1377               need_explicit_slash = 1;
1378             }
1379           else
1380             {
1381               /* example: "whatever/foo/bar" */
1382               /*                        ^    */
1383               start_insert = last_slash + 1;
1384             }
1385
1386           span = start_insert - url;
1387           constr = (char *)xmalloc (span + subsize + 1);
1388           if (span)
1389             memcpy (constr, url, span);
1390           if (need_explicit_slash)
1391             constr[span - 1] = '/';
1392           if (subsize)
1393             memcpy (constr + span, sub, subsize);
1394           constr[span + subsize] = '\0';
1395         }
1396       else /* *sub == `/' */
1397         {
1398           /* SUB is an absolute path: we need to replace everything
1399              after (and including) the FIRST slash with SUB.
1400
1401              So, if URL is "http://host/whatever/foo/bar", and SUB is
1402              "/qux/xyzzy", our result should be
1403              "http://host/qux/xyzzy".  */
1404           int span;
1405           const char *slash;
1406           const char *start_insert = NULL; /* for gcc to shut up. */
1407           const char *pos = url;
1408           int seen_slash_slash = 0;
1409           /* We're looking for the first slash, but want to ignore
1410              double slash. */
1411         again:
1412           slash = memchr (pos, '/', end - pos);
1413           if (slash && !seen_slash_slash)
1414             if (*(slash + 1) == '/')
1415               {
1416                 pos = slash + 2;
1417                 seen_slash_slash = 1;
1418                 goto again;
1419               }
1420
1421           /* At this point, SLASH is the location of the first / after
1422              "//", or the first slash altogether.  START_INSERT is the
1423              pointer to the location where SUB will be inserted.  When
1424              examining the last two examples, keep in mind that SUB
1425              begins with '/'. */
1426
1427           if (!slash && !seen_slash_slash)
1428             /* example: "foo" */
1429             /*           ^    */
1430             start_insert = url;
1431           else if (!slash && seen_slash_slash)
1432             /* example: "http://foo" */
1433             /*                     ^ */
1434             start_insert = end;
1435           else if (slash && !seen_slash_slash)
1436             /* example: "foo/bar" */
1437             /*           ^        */
1438             start_insert = url;
1439           else if (slash && seen_slash_slash)
1440             /* example: "http://something/" */
1441             /*                           ^  */
1442             start_insert = slash;
1443
1444           span = start_insert - url;
1445           constr = (char *)xmalloc (span + subsize + 1);
1446           if (span)
1447             memcpy (constr, url, span);
1448           if (subsize)
1449             memcpy (constr + span, sub, subsize);
1450           constr[span + subsize] = '\0';
1451         }
1452     }
1453   else /* !no_proto */
1454     {
1455       constr = strdupdelim (sub, sub + subsize);
1456     }
1457   return constr;
1458 }
1459
1460 /* Like the function above, but with a saner caller interface. */
1461 char *
1462 url_concat (const char *base_url, const char *new_url)
1463 {
1464   return construct (base_url, new_url, strlen (new_url), !has_proto (new_url));
1465 }
1466 \f
1467 /* Optimize URL by host, destructively replacing u->host with realhost
1468    (u->host).  Do this regardless of opt.simple_check.  */
1469 void
1470 opt_url (struct urlinfo *u)
1471 {
1472   /* Find the "true" host.  */
1473   char *host = realhost (u->host);
1474   free (u->host);
1475   u->host = host;
1476   assert (u->dir != NULL);      /* the URL must have been parsed */
1477   /* Refresh the printed representation.  */
1478   free (u->url);
1479   u->url = str_url (u, 0);
1480 }
1481
1482 /* This beautiful kludge is fortunately not needed, as I've made
1483    parse_dir do the (almost) right thing, so that a query can never
1484    become a part of directory.  */
1485 #if 0
1486 /* Call path_simplify, but make sure that the part after the
1487    question-mark, if any, is not destroyed by path_simplify's
1488    "optimizations".  */
1489 void
1490 path_simplify_with_kludge (char *path)
1491 {
1492   char *query = strchr (path, '?');
1493   if (query)
1494     /* path_simplify also works destructively, so we also have the
1495        license to write. */
1496     *query = '\0';
1497   path_simplify (path);
1498   if (query)
1499     {
1500       char *newend = path + strlen (path);
1501       *query = '?';
1502       if (newend != query)
1503         memmove (newend, query, strlen (query) + 1);
1504     }
1505 }
1506 #endif
1507 \f
1508 /* Returns proxy host address, in accordance with PROTO.  */
1509 char *
1510 getproxy (uerr_t proto)
1511 {
1512   if (proto == URLHTTP)
1513     return opt.http_proxy ? opt.http_proxy : getenv ("http_proxy");
1514   else if (proto == URLFTP)
1515     return opt.ftp_proxy ? opt.ftp_proxy : getenv ("ftp_proxy");
1516   else
1517     return NULL;
1518 }
1519
1520 /* Should a host be accessed through proxy, concerning no_proxy?  */
1521 int
1522 no_proxy_match (const char *host, const char **no_proxy)
1523 {
1524   if (!no_proxy)
1525     return 1;
1526   else
1527     return !sufmatch (no_proxy, host);
1528 }
1529 \f
1530 /* Change the links in an HTML document.  Accepts a structure that
1531    defines the positions of all the links.  */
1532 void
1533 convert_links (const char *file, urlpos *l)
1534 {
1535   FILE               *fp;
1536   char               *buf, *p, *p2;
1537   downloaded_file_t  downloaded_file_return;
1538   long               size;
1539
1540   logprintf (LOG_VERBOSE, _("Converting %s... "), file);
1541   /* Read from the file....  */
1542   fp = fopen (file, "rb");
1543   if (!fp)
1544     {
1545       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
1546                  file, strerror (errno));
1547       return;
1548     }
1549   /* ...to a buffer.  */
1550   load_file (fp, &buf, &size);
1551   fclose (fp);
1552
1553   downloaded_file_return = downloaded_file(CHECK_FOR_FILE, file);
1554
1555   if (opt.backup_converted && downloaded_file_return)
1556     /* Rather than just writing over the original .html file with the converted
1557        version, save the former to *.orig.  Note we only do this for files we've
1558        _successfully_ downloaded, so we don't clobber .orig files sitting around
1559        from previous invocations. */
1560     {
1561       /* Construct the backup filename as the original name plus ".orig". */
1562       size_t         filename_len = strlen(file);
1563       char*          filename_plus_orig_suffix;
1564       boolean        already_wrote_backup_file = FALSE;
1565       slist*         converted_file_ptr;
1566       static slist*  converted_files = NULL;
1567
1568       if (downloaded_file_return == FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED)
1569         {
1570           /* Just write "orig" over "html".  We need to do it this way because
1571              when we're checking to see if we've downloaded the file before (to
1572              see if we can skip downloading it), we don't know if it's a
1573              text/html file.  Therefore we don't know yet at that stage that -E
1574              is going to cause us to tack on ".html", so we need to compare
1575              vs. the original URL plus ".orig", not the original URL plus
1576              ".html.orig". */
1577           filename_plus_orig_suffix = xmalloc(filename_len + 1);
1578           strcpy(filename_plus_orig_suffix, file);
1579           strcpy((filename_plus_orig_suffix + filename_len) - 4, "orig");
1580         }
1581       else /* downloaded_file_return == FILE_DOWNLOADED_NORMALLY */
1582         {
1583           /* Append ".orig" to the name. */
1584           filename_plus_orig_suffix = xmalloc(filename_len + sizeof(".orig"));
1585           strcpy(filename_plus_orig_suffix, file);
1586           strcpy(filename_plus_orig_suffix + filename_len, ".orig");
1587         }
1588
1589       /* We can get called twice on the same URL thanks to the
1590          convert_all_links() call in main().  If we write the .orig file each
1591          time in such a case, it'll end up containing the first-pass conversion,
1592          not the original file.  So, see if we've already been called on this
1593          file. */
1594       converted_file_ptr = converted_files;
1595       while (converted_file_ptr != NULL)
1596         if (strcmp(converted_file_ptr->string, file) == 0)
1597           {
1598             already_wrote_backup_file = TRUE;
1599             break;
1600           }
1601         else
1602           converted_file_ptr = converted_file_ptr->next;
1603
1604       if (!already_wrote_backup_file)
1605         {
1606           /* Rename <file> to <file>.orig before former gets written over. */
1607           if (rename(file, filename_plus_orig_suffix) != 0)
1608             logprintf (LOG_NOTQUIET, _("Cannot back up %s as %s: %s\n"),
1609                        file, filename_plus_orig_suffix, strerror (errno));
1610
1611           /* Remember that we've already written a .orig backup for this file.
1612              Note that we never free this memory since we need it till the
1613              convert_all_links() call, which is one of the last things the
1614              program does before terminating.  BTW, I'm not sure if it would be
1615              safe to just set 'converted_file_ptr->string' to 'file' below,
1616              rather than making a copy of the string...  Another note is that I
1617              thought I could just add a field to the urlpos structure saying
1618              that we'd written a .orig file for this URL, but that didn't work,
1619              so I had to make this separate list. */
1620           converted_file_ptr = xmalloc(sizeof(*converted_file_ptr));
1621           converted_file_ptr->string = xstrdup(file);  /* die on out-of-mem. */
1622           converted_file_ptr->next = converted_files;
1623           converted_files = converted_file_ptr;
1624         }
1625
1626       free(filename_plus_orig_suffix);
1627     }
1628   /* Now open the file for writing.  */
1629   fp = fopen (file, "wb");
1630   if (!fp)
1631     {
1632       logprintf (LOG_NOTQUIET, _("Cannot convert links in %s: %s\n"),
1633                  file, strerror (errno));
1634       free (buf);
1635       return;
1636     }
1637   /* Presumably we have to loop through multiple URLs here (even though we're
1638      only talking about a single local file) because of the -O option. */
1639   for (p = buf; l; l = l->next)
1640     {
1641       if (l->pos >= size)
1642         {
1643           DEBUGP (("Something strange is going on.  Please investigate."));
1644           break;
1645         }
1646       /* If the URL already is relative or it is not to be converted
1647          for some other reason (e.g. because of not having been
1648          downloaded in the first place), skip it.  */
1649       if ((l->flags & URELATIVE) || !(l->flags & UABS2REL))
1650         {
1651           DEBUGP (("Skipping %s at position %d (flags %d).\n", l->url,
1652                    l->pos, l->flags));
1653           continue;
1654         }
1655       /* Else, reach the position of the offending URL, echoing
1656          everything up to it to the outfile.  */
1657       for (p2 = buf + l->pos; p < p2; p++)
1658         putc (*p, fp);
1659       if (l->flags & UABS2REL)
1660         /* Convert absolute URL to relative. */
1661         {
1662           char *newname = construct_relative (file, l->local_name);
1663           fprintf (fp, "%s", newname);
1664           DEBUGP (("ABS2REL: %s to %s at position %d in %s.\n",
1665                    l->url, newname, l->pos, file));
1666           free (newname);
1667         }
1668       p += l->size;
1669     }
1670   /* Output the rest of the file. */
1671   if (p - buf < size)
1672     {
1673       for (p2 = buf + size; p < p2; p++)
1674         putc (*p, fp);
1675     }
1676   fclose (fp);
1677   free (buf);
1678   logputs (LOG_VERBOSE, _("done.\n"));
1679 }
1680
1681 /* Construct and return a malloced copy of the relative link from two
1682    pieces of information: local name S1 of the referring file and
1683    local name S2 of the referred file.
1684
1685    So, if S1 is "jagor.srce.hr/index.html" and S2 is
1686    "jagor.srce.hr/images/news.gif", the function will return
1687    "images/news.gif".
1688
1689    Alternately, if S1 is "fly.cc.fer.hr/ioccc/index.html", and S2 is
1690    "fly.cc.fer.hr/images/fly.gif", the function will return
1691    "../images/fly.gif".
1692
1693    Caveats: S1 should not begin with `/', unless S2 also begins with
1694    '/'.  S1 should not contain things like ".." and such --
1695    construct_relative ("fly/ioccc/../index.html",
1696    "fly/images/fly.gif") will fail.  (A workaround is to call
1697    something like path_simplify() on S1).  */
1698 static char *
1699 construct_relative (const char *s1, const char *s2)
1700 {
1701   int i, cnt, sepdirs1;
1702   char *res;
1703
1704   if (*s2 == '/')
1705     return xstrdup (s2);
1706   /* S1 should *not* be absolute, if S2 wasn't.  */
1707   assert (*s1 != '/');
1708   i = cnt = 0;
1709   /* Skip the directories common to both strings.  */
1710   while (1)
1711     {
1712       while (s1[i] && s2[i]
1713              && (s1[i] == s2[i])
1714              && (s1[i] != '/')
1715              && (s2[i] != '/'))
1716         ++i;
1717       if (s1[i] == '/' && s2[i] == '/')
1718         cnt = ++i;
1719       else
1720         break;
1721     }
1722   for (sepdirs1 = 0; s1[i]; i++)
1723     if (s1[i] == '/')
1724       ++sepdirs1;
1725   /* Now, construct the file as of:
1726      - ../ repeated sepdirs1 time
1727      - all the non-mutual directories of S2.  */
1728   res = (char *)xmalloc (3 * sepdirs1 + strlen (s2 + cnt) + 1);
1729   for (i = 0; i < sepdirs1; i++)
1730     memcpy (res + 3 * i, "../", 3);
1731   strcpy (res + 3 * i, s2 + cnt);
1732   return res;
1733 }
1734 \f
1735 /* Add URL to the head of the list L.  */
1736 urlpos *
1737 add_url (urlpos *l, const char *url, const char *file)
1738 {
1739   urlpos *t;
1740
1741   t = (urlpos *)xmalloc (sizeof (urlpos));
1742   memset (t, 0, sizeof (*t));
1743   t->url = xstrdup (url);
1744   t->local_name = xstrdup (file);
1745   t->next = l;
1746   return t;
1747 }
1748
1749
1750 /* Remembers which files have been downloaded.  In the standard case, should be
1751    called with mode == FILE_DOWNLOADED_NORMALLY for each file we actually
1752    download successfully (i.e. not for ones we have failures on or that we skip
1753    due to -N).
1754
1755    When we've downloaded a file and tacked on a ".html" extension due to -E,
1756    call this function with FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED rather than
1757    FILE_DOWNLOADED_NORMALLY.
1758
1759    If you just want to check if a file has been previously added without adding
1760    it, call with mode == CHECK_FOR_FILE.  Please be sure to call this function
1761    with local filenames, not remote URLs. */
1762 downloaded_file_t
1763 downloaded_file (downloaded_file_t  mode, const char*  file)
1764 {
1765   typedef struct _downloaded_file_list
1766   {
1767     char*                          file;
1768     downloaded_file_t              download_type;
1769     struct _downloaded_file_list*  next;
1770   } downloaded_file_list;
1771   
1772   boolean                       found_file = FALSE;
1773   static downloaded_file_list*  downloaded_files = NULL;
1774   downloaded_file_list*         rover = downloaded_files;
1775
1776   while (rover != NULL)
1777     if (strcmp(rover->file, file) == 0)
1778       {
1779         found_file = TRUE;
1780         break;
1781       }
1782     else
1783       rover = rover->next;
1784
1785   if (found_file)
1786     return rover->download_type;  /* file had already been downloaded */
1787   else
1788     {
1789       if (mode != CHECK_FOR_FILE)
1790         {
1791           rover = xmalloc(sizeof(*rover));
1792           rover->file = xstrdup(file); /* use xstrdup() so die on out-of-mem. */
1793           rover->download_type = mode;
1794           rover->next = downloaded_files;
1795           downloaded_files = rover;
1796         }
1797
1798       return FILE_NOT_ALREADY_DOWNLOADED;
1799     }
1800 }