]> sjero.net Git - wget/blob - src/iri.c
Get rid of the supplementary bool pointer in url_parse () arguments; UGLY :)
[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 #include "iri.h"
43
44 char *remote;
45 char *current;
46 bool utf8_encode;
47 bool ugly_no_encode;
48
49 static iconv_t locale2utf8;
50
51
52 static bool open_locale_to_utf8 (void);
53 static bool do_conversion (iconv_t cd, char *in, size_t inlen, char **out);
54
55
56 /* Given a string containing "charset=XXX", return the encoding if found,
57    or NULL otherwise */
58 char *
59 parse_charset (char *str)
60 {
61   char *charset;
62
63   if (!str || !*str)
64     return NULL;
65
66   str = strcasestr (str, "charset=");
67   if (!str)
68     return NULL;
69
70   str += 8;
71   charset = str;
72
73   /* sXXXav: which chars should be banned ??? */
74   while (*charset && !c_isspace (*charset))
75     charset++;
76
77   /* sXXXav: could strdupdelim return NULL ? */
78   charset = strdupdelim (str, charset);
79
80   /* Do a minimum check on the charset value */
81   if (!check_encoding_name (charset))
82     {
83       xfree (charset);
84       return NULL;
85     }
86
87   /*logprintf (LOG_VERBOSE, "parse_charset: %s\n", quote (charset));*/
88
89   return charset;
90 }
91
92 /* Find the locale used, or fall back on a default value */
93 char *
94 find_locale (void)
95 {
96   /* sXXXav, made our own function or use libidn one ?! */
97   return (char *) stringprep_locale_charset ();
98 }
99
100 /* Basic check of an encoding name. */
101 bool
102 check_encoding_name (char *encoding)
103 {
104   char *s = encoding;
105
106   while (*s)
107     {
108       if (!c_isascii (*s) || c_isspace (*s))
109         {
110           logprintf (LOG_VERBOSE, "Encoding %s isn't valid\n", quote (encoding));
111           return false;
112         }
113
114       s++;
115     }
116
117   return true;
118 }
119
120 /* Try opening an iconv_t descriptor for conversion from locale to UTF-8 */
121 static bool
122 open_locale_to_utf8 (void)
123 {
124   if (locale2utf8)
125     return true;
126
127   /* sXXXav : That shouldn't happen, just in case */
128   if (!opt.locale)
129     {
130       logprintf (LOG_VERBOSE, "open_locale_to_utf8: locale is unset\n");
131       opt.locale = find_locale ();
132     }
133
134   if (!opt.locale)
135     return false;
136
137   locale2utf8 = iconv_open ("UTF-8", opt.locale);
138   if (locale2utf8 != (iconv_t)(-1))
139     return true;
140
141   logprintf (LOG_VERBOSE, "Conversion from %s to %s isn't supported\n",
142              quote (opt.locale), quote ("UTF-8"));
143   locale2utf8 = NULL;
144   return false;
145 }
146
147 /* Return a new string */
148 const char *
149 locale_to_utf8 (const char *str)
150 {
151   char *new;
152
153   if (!strcasecmp (opt.locale, "utf-8"))
154     return str;
155
156   if (!open_locale_to_utf8 ())
157     return str;
158
159   if (do_conversion (locale2utf8, (char *) str, strlen ((char *) str), &new))
160     return (const char *) new;
161
162   return str;
163 }
164
165 /* */
166 static bool
167 do_conversion (iconv_t cd, char *in, size_t inlen, char **out)
168 {
169   /* sXXXav : hummm hard to guess... */
170   size_t len, done, outlen = inlen * 2;
171   int invalid = 0, tooshort = 0;
172   char *s;
173
174   s = xmalloc (outlen + 1);
175   *out = s;
176   len = outlen;
177   done = 0;
178
179   /* sXXXav : put a maximum looping factor ??? */
180   for (;;)
181     {
182       if (iconv (cd, &in, &inlen, out, &outlen) != (size_t)(-1))
183         {
184           *out = s;
185           *(s + len - outlen - done) = '\0';
186           return true;
187         }
188
189       /* Incomplete or invalid multibyte sequence */
190       if (errno == EINVAL || errno == EILSEQ)
191         {
192           if (!invalid)
193             logprintf (LOG_VERBOSE,
194                       "Incomplete or invalide multibyte sequence encountered\n");
195
196           invalid++;
197           **out = *in;
198           in++;
199           inlen--;
200           (*out)++;
201           outlen--;
202         }
203       else if (errno == E2BIG) /* Output buffer full */
204         {
205           char *new;
206
207           tooshort++;
208           done = len;
209           outlen = done + inlen * 2;
210           new = xmalloc (outlen + 1);
211           memcpy (new, s, done);
212           xfree (s);
213           s = new;
214           len = outlen;
215           *out = s + done;
216         }
217       else /* Weird, we got an unspecified error */
218         {
219           logprintf (LOG_VERBOSE, "Unhandled errno %d\n", errno);
220           break;
221         }
222     }
223
224     return false;
225 }
226
227 /* Try to ASCII encode UTF-8 host. Return the new domain on success or NULL
228    on error. */
229 char *
230 idn_encode (char *host, bool utf8_encoded)
231 {
232   char *new;
233   int ret;
234
235   /* Encode to UTF-8 if not done using current remote */
236   if (!utf8_encoded)
237     {
238       if (!remote_to_utf8 ((const char *) host, (const char **) &new))
239         {
240           /* Nothing to encode or an error occured */
241           return NULL;
242         }
243
244       host = new;
245     }
246
247   /* toASCII UTF-8 NULL terminated string */
248   ret = idna_to_ascii_8z (host, &new, 0);
249   if (ret != IDNA_SUCCESS)
250     {
251       /* sXXXav : free new when needed ! */
252       logprintf (LOG_VERBOSE, "idn_encode failed (%d): %s\n", ret,
253                  quote (idna_strerror (ret)));
254       return NULL;
255     }
256
257   return new;
258 }
259
260 /* Try to decode an ASCII encoded host. Return the new domain in the locale on
261    success or NULL on error. */
262 char *
263 idn_decode (char *host)
264 {
265   char *new;
266   int ret;
267
268   ret = idna_to_unicode_8zlz (host, &new, 0);
269   if (ret != IDNA_SUCCESS)
270     {
271       logprintf (LOG_VERBOSE, "idn_decode failed (%d): %s\n", ret,
272                  quote (idna_strerror (ret)));
273       return NULL;
274     }
275
276   return new;
277 }
278
279 /* Return a new string */
280 bool
281 remote_to_utf8 (const char *str, const char **new)
282 {
283   char *remote;
284   iconv_t cd;
285   bool ret = false;
286
287   if (opt.encoding_remote)
288     remote = opt.encoding_remote;
289   else if (current)
290     remote = current;
291   else
292     return false;
293
294   cd = iconv_open ("UTF-8", remote);
295   if (cd == (iconv_t)(-1))
296     return false;
297
298   if (do_conversion (cd, (char *) str, strlen ((char *) str), (char **) new))
299     ret = true;
300
301   iconv_close (cd);
302
303   /* Test if something was converted */
304   if (!strcmp (str, *new))
305     {
306       xfree ((char *) *new);
307       return false;
308     }
309
310   return ret;
311 }
312
313 char *get_remote_charset (void)
314 {
315   return remote;
316 }
317
318 char *get_current_charset (void)
319 {
320   return current;
321 }
322
323 void set_current_charset (char *charset)
324 {
325   /*printf("[ current = `%s'\n", charset);*/
326
327   if (current)
328     xfree (current);
329
330   current = charset ? xstrdup (charset) : NULL;
331 }
332
333 void set_current_as_locale (void)
334 {
335   /*printf("[ current = locale = `%s'\n", opt.locale);*/
336   if (current)
337     xfree (current);
338
339   /* sXXXav : assert opt.locale NULL ? */
340   current = xstrdup (opt.locale);
341 }
342
343 void
344 set_remote_charset (char *charset)
345 {
346   /*printf("[ remote = `%s'\n", charset);*/
347   if (remote)
348     xfree (remote);
349
350   remote = charset ? xstrdup (charset) : NULL;
351 }
352
353 void
354 set_remote_as_current (void)
355 {
356   /*printf("[ remote = current = `%s'\n", current);*/
357   if (remote)
358     xfree (remote);
359
360   remote = current ? xstrdup (current) : NULL;
361 }
362
363 void reset_utf8_encode (void)
364 {
365   set_utf8_encode (opt.enable_iri);
366 }
367
368 void set_utf8_encode (bool encode)
369 {
370   utf8_encode = encode;
371 }
372
373 bool get_utf8_encode (void)
374 {
375   return utf8_encode;
376 }
377
378 void set_ugly_no_encode (bool ugly)
379 {
380   ugly_no_encode = ugly;
381 }
382
383 bool get_ugly_no_encode (void)
384 {
385   return ugly_no_encode;
386 }
387