]> sjero.net Git - wget/blob - src/iri.c
dfcb05783fa9fce2661275427b9ef18f5ba1c9ae
[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
45 static iconv_t locale2utf8;
46
47
48 static bool open_locale_to_utf8 (void);
49 static bool do_conversion (iconv_t cd, char *in, size_t inlen, char **out);
50
51
52 /* Given a string containing "charset=XXX", return the encoding if found,
53    or NULL otherwise */
54 char *
55 parse_charset (char *str)
56 {
57   char *charset;
58
59   if (!str || !*str)
60     return NULL;
61
62   str = strcasestr (str, "charset=");
63   if (!str)
64     return NULL;
65
66   str += 8;
67   charset = str;
68
69   /* sXXXav: which chars should be banned ??? */
70   while (*charset && !c_isspace (*charset))
71     charset++;
72
73   /* sXXXav: could strdupdelim return NULL ? */
74   charset = strdupdelim (str, charset);
75
76   /* Do a minimum check on the charset value */
77   if (!check_encoding_name (charset))
78     {
79       xfree (charset);
80       return NULL;
81     }
82
83   logprintf (LOG_VERBOSE, "parse_charset: %s\n", quote (charset));
84
85   return charset;
86 }
87
88 /* Find the locale used, or fall back on a default value */
89 char *
90 find_locale (void)
91 {
92   /* sXXXav, made our own function or use libidn one ?! */
93   return (char *) stringprep_locale_charset ();
94 }
95
96 /* Basic check of an encoding name. */
97 bool
98 check_encoding_name (char *encoding)
99 {
100   char *s = encoding;
101
102   while (*s)
103     {
104       if (!c_isascii (*s) || c_isspace (*s))
105         {
106           logprintf (LOG_VERBOSE, "Encoding %s isn't valid\n", quote (encoding));
107           return false;
108         }
109
110       s++;
111     }
112
113   return true;
114 }
115
116 /* Try opening an iconv_t descriptor for conversion from locale to UTF-8 */
117 static bool
118 open_locale_to_utf8 (void)
119 {
120   if (locale2utf8)
121     return true;
122
123   /* sXXXav : That shouldn't happen, just in case */
124   if (!opt.locale)
125     {
126       logprintf (LOG_VERBOSE, "open_locale_to_utf8: locale is unset\n");
127       opt.locale = find_locale ();
128     }
129
130   if (!opt.locale)
131     return false;
132
133   locale2utf8 = iconv_open ("UTF-8", opt.locale);
134   if (locale2utf8 != (iconv_t)(-1))
135     return true;
136
137   logprintf (LOG_VERBOSE, "Conversion from %s to %s isn't supported\n",
138              quote (opt.locale), quote ("UTF-8"));
139   locale2utf8 = NULL;
140   return false;
141 }
142
143 /* Return a new string */
144 const char *
145 locale_to_utf8 (const char *str)
146 {
147   char *new;
148
149   if (!strcasecmp (opt.locale, "utf-8"))
150     return str;
151
152   if (!open_locale_to_utf8 ())
153     return str;
154
155   if (do_conversion (locale2utf8, (char *) str, strlen ((char *) str), &new))
156     return (const char *) new;
157
158   return str;
159 }
160
161 /* */
162 static bool
163 do_conversion (iconv_t cd, char *in, size_t inlen, char **out)
164 {
165   /* sXXXav : hummm hard to guess... */
166   size_t len, done, outlen = inlen * 2;
167   int invalid = 0, tooshort = 0;
168   char *s;
169
170   s = xmalloc (outlen + 1);
171   *out = s;
172   len = outlen;
173   done = 0;
174
175   /* sXXXav : put a maximum looping factor ??? */
176   for (;;)
177     {
178       if (iconv (cd, &in, &inlen, out, &outlen) != (size_t)(-1))
179         {
180           *out = s;
181           *(s + len - outlen - done) = '\0';
182           return true;
183         }
184
185       /* Incomplete or invalid multibyte sequence */
186       if (errno == EINVAL || errno == EILSEQ)
187         {
188           if (!invalid)
189             logprintf (LOG_VERBOSE,
190                       "Incomplete or invalide multibyte sequence encountered\n");
191
192           invalid++;
193           **out = *in;
194           in++;
195           inlen--;
196           (*out)++;
197           outlen--;
198         }
199       else if (errno == E2BIG) /* Output buffer full */ 
200         {
201           char *new;
202
203           tooshort++;
204           done = len;
205           outlen = done + inlen * 2;
206           new = xmalloc (outlen + 1);
207           memcpy (new, s, done);
208           xfree (s);
209           s = new;
210           len = outlen;
211           *out = s + done;
212         }
213       else /* Weird, we got an unspecified error */
214         {
215           logprintf (LOG_VERBOSE, "Unhandled errno %d\n", errno);
216           break;
217         }
218     }
219
220     return false;
221 }
222
223 /* Try to encode UTF-8 host to ASCII. Return the new domain on success or NULL
224    on error. */
225 char *idn_encode (char *host)
226 {
227   char *new;
228   int ret;
229
230   /* toASCII UTF-8 NULL terminated string */
231   ret = idna_to_ascii_8z (host, &new, 0);
232   if (ret != IDNA_SUCCESS)
233     {
234       logprintf (LOG_VERBOSE, "idn_encode failed (%d): %s\n", ret,
235                  quote (idna_strerror (ret)));
236       return NULL;
237     }
238
239   return new;
240 }
241