]> sjero.net Git - wget/blob - src/iri.c
45650c85e131ed81e218a9d0e60242e467624ed1
[wget] / src / iri.c
1 /* IRI related functions.
2    Copyright (C) 2008 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget 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 3 of the License, or (at
9 your option) any later version.
10
11 GNU Wget 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 Wget.  If not, see <http://www.gnu.org/licenses/>.
18
19 Additional permission under GNU GPL version 3 section 7
20
21 If you modify this program, or any covered work, by linking or
22 combining it with the OpenSSL project's OpenSSL library (or a
23 modified version of that library), containing parts covered by the
24 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
25 grants you additional permission to convey the resulting work.
26 Corresponding Source for a non-source form of such a combination
27 shall include the source code for the parts of OpenSSL used as well
28 as that of the covered work.  */
29
30 #include "wget.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <string.h>
36 #include <iconv.h>
37 #include <stringprep.h>
38 #include <idna.h>
39 #include <errno.h>
40
41 #include "utils.h"
42
43 /* RFC3987 section 3.1 mandates STD3 ASCII RULES */
44 #define IDNA_FLAGS  IDNA_USE_STD3_ASCII_RULES
45
46 /* Note: locale encoding is kept in options struct (opt.locale) */
47
48 static bool do_conversion (iconv_t cd, char *in, size_t inlen, char **out);
49
50
51 /* Given a string containing "charset=XXX", return the encoding if found,
52    or NULL otherwise */
53 char *
54 parse_charset (char *str)
55 {
56   char *charset;
57
58   if (!str || !*str)
59     return NULL;
60
61   str = strcasestr (str, "charset=");
62   if (!str)
63     return NULL;
64
65   str += 8;
66   charset = str;
67
68   /* sXXXav: which chars should be banned ??? */
69   while (*charset && !c_isspace (*charset))
70     charset++;
71
72   /* sXXXav: could strdupdelim return NULL ? */
73   charset = strdupdelim (str, charset);
74
75   /* Do a minimum check on the charset value */
76   if (!check_encoding_name (charset))
77     {
78       xfree (charset);
79       return NULL;
80     }
81
82   /*logprintf (LOG_VERBOSE, "parse_charset: %s\n", quote (charset));*/
83
84   return charset;
85 }
86
87 /* Find the locale used, or fall back on a default value */
88 char *
89 find_locale (void)
90 {
91   return (char *) stringprep_locale_charset ();
92 }
93
94 /* Basic check of an encoding name. */
95 bool
96 check_encoding_name (char *encoding)
97 {
98   char *s = encoding;
99
100   while (*s)
101     {
102       if (!c_isascii (*s) || c_isspace (*s))
103         {
104           logprintf (LOG_VERBOSE, _("Encoding %s isn't valid\n"), quote (encoding));
105           return false;
106         }
107
108       s++;
109     }
110
111   return true;
112 }
113
114 /* Try opening an iconv_t descriptor for conversion from locale to UTF-8 */
115 static bool
116 open_locale_to_utf8 (void)
117 {
118
119 }
120
121 /* Try converting string str from locale to UTF-8. Return a new string
122    on success, or str on error or if conversion isn't needed. */
123 const char *
124 locale_to_utf8 (const char *str)
125 {
126   iconv_t l2u;
127   char *new;
128
129   /* That shouldn't happen, just in case */
130   if (!opt.locale)
131     {
132       logprintf (LOG_VERBOSE, _("locale_to_utf8: locale is unset\n"));
133       opt.locale = find_locale ();
134     }
135
136   if (!opt.locale || !strcasecmp (opt.locale, "utf-8"))
137     return str;
138
139   l2u = iconv_open ("UTF-8", opt.locale);
140   if (l2u != (iconv_t)(-1))
141     { 
142       logprintf (LOG_VERBOSE, _("Conversion from %s to %s isn't supported\n"),
143                  quote (opt.locale), quote ("UTF-8"));
144       return str;
145     }
146
147   if (do_conversion (l2u, (char *) str, strlen ((char *) str), &new))
148     return (const char *) new;
149
150   return str;
151 }
152
153 /* Do the conversion according to the passed conversion descriptor cd. *out
154    will contain the transcoded string on success. *out content is
155    unspecified otherwise. */
156 static bool
157 do_conversion (iconv_t cd, char *in, size_t inlen, char **out)
158 {
159   /* sXXXav : hummm hard to guess... */
160   size_t len, done, outlen = inlen * 2;
161   int invalid = 0, tooshort = 0;
162   char *s;
163
164   s = xmalloc (outlen + 1);
165   *out = s;
166   len = outlen;
167   done = 0;
168
169   for (;;)
170     {
171       if (iconv (cd, &in, &inlen, out, &outlen) != (size_t)(-1))
172         {
173           *out = s;
174           *(s + len - outlen - done) = '\0';
175           return true;
176         }
177
178       /* Incomplete or invalid multibyte sequence */
179       if (errno == EINVAL || errno == EILSEQ)
180         {
181           if (!invalid)
182             logprintf (LOG_VERBOSE,
183                       _("Incomplete or invalide multibyte sequence encountered\n"));
184
185           invalid++;
186           **out = *in;
187           in++;
188           inlen--;
189           (*out)++;
190           outlen--;
191         }
192       else if (errno == E2BIG) /* Output buffer full */
193         {
194           char *new;
195
196           tooshort++;
197           done = len;
198           outlen = done + inlen * 2;
199           new = xmalloc (outlen + 1);
200           memcpy (new, s, done);
201           xfree (s);
202           s = new;
203           len = outlen;
204           *out = s + done;
205         }
206       else /* Weird, we got an unspecified error */
207         {
208           logprintf (LOG_VERBOSE, _("Unhandled errno %d\n"), errno);
209           break;
210         }
211     }
212
213     return false;
214 }
215
216 /* Try to "ASCII encode" UTF-8 host. Return the new domain on success or NULL
217    on error. */
218 char *
219 idn_encode (struct iri *i, char *host)
220 {
221   char *new;
222   int ret;
223
224   /* Encode to UTF-8 if not done */
225   if (!i->utf8_encode)
226     {
227       if (!remote_to_utf8 (i, (const char *) host, (const char **) &new))
228           return NULL;  /* Nothing to encode or an error occured */
229       host = new;
230     }
231
232   /* toASCII UTF-8 NULL terminated string */
233   ret = idna_to_ascii_8z (host, &new, IDNA_FLAGS);
234   if (ret != IDNA_SUCCESS)
235     {
236       /* sXXXav : free new when needed ! */
237       logprintf (LOG_VERBOSE, _("idn_encode failed (%d): %s\n"), ret,
238                  quote (idna_strerror (ret)));
239       return NULL;
240     }
241
242   return new;
243 }
244
245 /* Try to decode an "ASCII encoded" host. Return the new domain in the locale
246    on success or NULL on error. */
247 char *
248 idn_decode (char *host)
249 {
250   char *new;
251   int ret;
252
253   ret = idna_to_unicode_8zlz (host, &new, IDNA_FLAGS);
254   if (ret != IDNA_SUCCESS)
255     {
256       logprintf (LOG_VERBOSE, _("idn_decode failed (%d): %s\n"), ret,
257                  quote (idna_strerror (ret)));
258       return NULL;
259     }
260
261   return new;
262 }
263
264 /* Try to transcode string str from remote encoding to UTF-8. On success, *new
265    contains the transcoded string. *new content is unspecified otherwise. */
266 bool
267 remote_to_utf8 (struct iri *i, const char *str, const char **new)
268 {
269   iconv_t cd;
270   bool ret = false;
271
272   if (!i->uri_encoding)
273     return false;
274
275   cd = iconv_open ("UTF-8", i->uri_encoding);
276   if (cd == (iconv_t)(-1))
277     return false;
278
279   if (do_conversion (cd, (char *) str, strlen ((char *) str), (char **) new))
280     ret = true;
281
282   iconv_close (cd);
283
284   /* Test if something was converted */
285   if (!strcmp (str, *new))
286     {
287       xfree ((char *) *new);
288       return false;
289     }
290
291   return ret;
292 }
293
294 /* Allocate a new iri structure and return a pointer to it. */
295 struct iri *
296 iri_new (void)
297 {
298   struct iri *i = xmalloc (sizeof *i);
299   i->uri_encoding = opt.encoding_remote ? xstrdup (opt.encoding_remote) : NULL;
300   i->content_encoding = NULL;
301   i->orig_url = NULL;
302   i->utf8_encode = opt.enable_iri;
303   return i;
304 }
305
306 struct iri *iri_dup (const struct iri *src)
307 {
308   struct iri *i = xmalloc (sizeof *i);
309   i->uri_encoding = src->uri_encoding ? xstrdup (src->uri_encoding) : NULL;
310   i->content_encoding = (src->content_encoding ?
311                          xstrdup (src->content_encoding) : NULL);
312   i->orig_url = src->orig_url ? xstrdup (src->orig_url) : NULL;
313   i->utf8_encode = src->utf8_encode;
314   return i;
315 }
316
317 /* Completely free an iri structure. */
318 void
319 iri_free (struct iri *i)
320 {
321   xfree_null (i->uri_encoding);
322   xfree_null (i->content_encoding);
323   xfree_null (i->orig_url);
324   xfree (i);
325 }
326
327 /* Set uri_encoding of struct iri i. If a remote encoding was specified, use
328    it unless force is true. */
329 void
330 set_uri_encoding (struct iri *i, char *charset, bool force)
331 {
332   DEBUGP (("URI encoding = %s\n", charset ? quote (charset) : "None"));
333   if (!force && opt.encoding_remote)
334     return;
335   if (i->uri_encoding)
336     {
337       if (charset && !strcasecmp (i->uri_encoding, charset))
338         return;
339       xfree (i->uri_encoding);
340     }
341
342   i->uri_encoding = charset ? xstrdup (charset) : NULL;
343 }
344
345 /* Set content_encoding of struct iri i. */
346 void
347 set_content_encoding (struct iri *i, char *charset)
348 {
349   DEBUGP (("URI content encoding = %s\n", charset ? quote (charset) : "None"));
350   if (opt.encoding_remote)
351     return;
352   if (i->content_encoding)
353     {
354       if (charset && !strcasecmp (i->content_encoding, charset))
355         return;
356       xfree (i->content_encoding);
357     }
358
359   i->content_encoding = charset ? xstrdup (charset) : NULL;
360 }
361