]> sjero.net Git - wget/blob - src/iri.c
Fix copyright year and some GNU coding style
[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           invalid++;
189           **out = *in;
190           in++;
191           inlen--;
192           (*out)++;
193           outlen--;
194         }
195       else if (errno == E2BIG) /* Output buffer full */ 
196         {
197           char *new;
198
199           tooshort++;
200           done = len;
201           outlen = done + inlen * 2;
202           new = xmalloc (outlen + 1);
203           memcpy (new, s, done);
204           xfree (s);
205           s = new;
206           len = outlen;
207           *out = s + done;
208         }
209       else /* Weird, we got an unspecified error */
210         {
211           logprintf (LOG_VERBOSE, "Unhandled errno %d\n", errno);
212           break;
213         }
214     }
215
216     return false;
217 }
218
219 /* Try to encode UTF-8 host to ASCII. Return the new domain on success or NULL
220    on error. */
221 char *idn_encode (char *host)
222 {
223   char *new;
224   int ret;
225
226   /* toASCII UTF-8 NULL terminated string */
227   ret = idna_to_ascii_8z (host, &new, 0);
228   if (ret != IDNA_SUCCESS)
229     {
230       logprintf (LOG_VERBOSE, "idn_encode failed (%d): %s\n", ret,
231                  quote (idna_strerror (ret)));
232       return NULL;
233     }
234
235   return new;
236 }
237