1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | |
23 | #include "curl_setup.h" |
24 | |
25 | #ifdef CURL_DOES_CONVERSIONS |
26 | |
27 | #include <curl/curl.h> |
28 | |
29 | #include "non-ascii.h" |
30 | #include "formdata.h" |
31 | #include "sendf.h" |
32 | #include "urldata.h" |
33 | #include "multiif.h" |
34 | |
35 | #include "curl_memory.h" |
36 | /* The last #include file should be: */ |
37 | #include "memdebug.h" |
38 | |
39 | #ifdef HAVE_ICONV |
40 | #include <iconv.h> |
41 | /* set default codesets for iconv */ |
42 | #ifndef CURL_ICONV_CODESET_OF_NETWORK |
43 | #define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1" |
44 | #endif |
45 | #ifndef CURL_ICONV_CODESET_FOR_UTF8 |
46 | #define CURL_ICONV_CODESET_FOR_UTF8 "UTF-8" |
47 | #endif |
48 | #define ICONV_ERROR (size_t)-1 |
49 | #endif /* HAVE_ICONV */ |
50 | |
51 | /* |
52 | * Curl_convert_clone() returns a malloced copy of the source string (if |
53 | * returning CURLE_OK), with the data converted to network format. |
54 | */ |
55 | CURLcode Curl_convert_clone(struct Curl_easy *data, |
56 | const char *indata, |
57 | size_t insize, |
58 | char **outbuf) |
59 | { |
60 | char *convbuf; |
61 | CURLcode result; |
62 | |
63 | convbuf = malloc(insize); |
64 | if(!convbuf) |
65 | return CURLE_OUT_OF_MEMORY; |
66 | |
67 | memcpy(convbuf, indata, insize); |
68 | result = Curl_convert_to_network(data, convbuf, insize); |
69 | if(result) { |
70 | free(convbuf); |
71 | return result; |
72 | } |
73 | |
74 | *outbuf = convbuf; /* return the converted buffer */ |
75 | |
76 | return CURLE_OK; |
77 | } |
78 | |
79 | /* |
80 | * Curl_convert_to_network() is an internal function for performing ASCII |
81 | * conversions on non-ASCII platforms. It converts the buffer _in place_. |
82 | */ |
83 | CURLcode Curl_convert_to_network(struct Curl_easy *data, |
84 | char *buffer, size_t length) |
85 | { |
86 | if(data && data->set.convtonetwork) { |
87 | /* use translation callback */ |
88 | CURLcode result; |
89 | Curl_set_in_callback(data, true); |
90 | result = data->set.convtonetwork(buffer, length); |
91 | Curl_set_in_callback(data, false); |
92 | if(result) { |
93 | failf(data, |
94 | "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s" , |
95 | (int)result, curl_easy_strerror(result)); |
96 | } |
97 | |
98 | return result; |
99 | } |
100 | else { |
101 | #ifdef HAVE_ICONV |
102 | /* do the translation ourselves */ |
103 | iconv_t tmpcd = (iconv_t) -1; |
104 | iconv_t *cd = &tmpcd; |
105 | char *input_ptr, *output_ptr; |
106 | size_t in_bytes, out_bytes, rc; |
107 | |
108 | /* open an iconv conversion descriptor if necessary */ |
109 | if(data) |
110 | cd = &data->outbound_cd; |
111 | if(*cd == (iconv_t)-1) { |
112 | *cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, |
113 | CURL_ICONV_CODESET_OF_HOST); |
114 | if(*cd == (iconv_t)-1) { |
115 | failf(data, |
116 | "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s" , |
117 | CURL_ICONV_CODESET_OF_NETWORK, |
118 | CURL_ICONV_CODESET_OF_HOST, |
119 | errno, strerror(errno)); |
120 | return CURLE_CONV_FAILED; |
121 | } |
122 | } |
123 | /* call iconv */ |
124 | input_ptr = output_ptr = buffer; |
125 | in_bytes = out_bytes = length; |
126 | rc = iconv(*cd, &input_ptr, &in_bytes, |
127 | &output_ptr, &out_bytes); |
128 | if(!data) |
129 | iconv_close(tmpcd); |
130 | if((rc == ICONV_ERROR) || (in_bytes != 0)) { |
131 | failf(data, |
132 | "The Curl_convert_to_network iconv call failed with errno %i: %s" , |
133 | errno, strerror(errno)); |
134 | return CURLE_CONV_FAILED; |
135 | } |
136 | #else |
137 | failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required" ); |
138 | return CURLE_CONV_REQD; |
139 | #endif /* HAVE_ICONV */ |
140 | } |
141 | |
142 | return CURLE_OK; |
143 | } |
144 | |
145 | /* |
146 | * Curl_convert_from_network() is an internal function for performing ASCII |
147 | * conversions on non-ASCII platforms. It converts the buffer _in place_. |
148 | */ |
149 | CURLcode Curl_convert_from_network(struct Curl_easy *data, |
150 | char *buffer, size_t length) |
151 | { |
152 | if(data && data->set.convfromnetwork) { |
153 | /* use translation callback */ |
154 | CURLcode result; |
155 | Curl_set_in_callback(data, true); |
156 | result = data->set.convfromnetwork(buffer, length); |
157 | Curl_set_in_callback(data, false); |
158 | if(result) { |
159 | failf(data, |
160 | "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s" , |
161 | (int)result, curl_easy_strerror(result)); |
162 | } |
163 | |
164 | return result; |
165 | } |
166 | else { |
167 | #ifdef HAVE_ICONV |
168 | /* do the translation ourselves */ |
169 | iconv_t tmpcd = (iconv_t) -1; |
170 | iconv_t *cd = &tmpcd; |
171 | char *input_ptr, *output_ptr; |
172 | size_t in_bytes, out_bytes, rc; |
173 | |
174 | /* open an iconv conversion descriptor if necessary */ |
175 | if(data) |
176 | cd = &data->inbound_cd; |
177 | if(*cd == (iconv_t)-1) { |
178 | *cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, |
179 | CURL_ICONV_CODESET_OF_NETWORK); |
180 | if(*cd == (iconv_t)-1) { |
181 | failf(data, |
182 | "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s" , |
183 | CURL_ICONV_CODESET_OF_HOST, |
184 | CURL_ICONV_CODESET_OF_NETWORK, |
185 | errno, strerror(errno)); |
186 | return CURLE_CONV_FAILED; |
187 | } |
188 | } |
189 | /* call iconv */ |
190 | input_ptr = output_ptr = buffer; |
191 | in_bytes = out_bytes = length; |
192 | rc = iconv(*cd, &input_ptr, &in_bytes, |
193 | &output_ptr, &out_bytes); |
194 | if(!data) |
195 | iconv_close(tmpcd); |
196 | if((rc == ICONV_ERROR) || (in_bytes != 0)) { |
197 | failf(data, |
198 | "Curl_convert_from_network iconv call failed with errno %i: %s" , |
199 | errno, strerror(errno)); |
200 | return CURLE_CONV_FAILED; |
201 | } |
202 | #else |
203 | failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required" ); |
204 | return CURLE_CONV_REQD; |
205 | #endif /* HAVE_ICONV */ |
206 | } |
207 | |
208 | return CURLE_OK; |
209 | } |
210 | |
211 | /* |
212 | * Curl_convert_from_utf8() is an internal function for performing UTF-8 |
213 | * conversions on non-ASCII platforms. |
214 | */ |
215 | CURLcode Curl_convert_from_utf8(struct Curl_easy *data, |
216 | char *buffer, size_t length) |
217 | { |
218 | if(data && data->set.convfromutf8) { |
219 | /* use translation callback */ |
220 | CURLcode result; |
221 | Curl_set_in_callback(data, true); |
222 | result = data->set.convfromutf8(buffer, length); |
223 | Curl_set_in_callback(data, false); |
224 | if(result) { |
225 | failf(data, |
226 | "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s" , |
227 | (int)result, curl_easy_strerror(result)); |
228 | } |
229 | |
230 | return result; |
231 | } |
232 | else { |
233 | #ifdef HAVE_ICONV |
234 | /* do the translation ourselves */ |
235 | iconv_t tmpcd = (iconv_t) -1; |
236 | iconv_t *cd = &tmpcd; |
237 | char *input_ptr; |
238 | char *output_ptr; |
239 | size_t in_bytes, out_bytes, rc; |
240 | |
241 | /* open an iconv conversion descriptor if necessary */ |
242 | if(data) |
243 | cd = &data->utf8_cd; |
244 | if(*cd == (iconv_t)-1) { |
245 | *cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, |
246 | CURL_ICONV_CODESET_FOR_UTF8); |
247 | if(*cd == (iconv_t)-1) { |
248 | failf(data, |
249 | "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s" , |
250 | CURL_ICONV_CODESET_OF_HOST, |
251 | CURL_ICONV_CODESET_FOR_UTF8, |
252 | errno, strerror(errno)); |
253 | return CURLE_CONV_FAILED; |
254 | } |
255 | } |
256 | /* call iconv */ |
257 | input_ptr = output_ptr = buffer; |
258 | in_bytes = out_bytes = length; |
259 | rc = iconv(*cd, &input_ptr, &in_bytes, |
260 | &output_ptr, &out_bytes); |
261 | if(!data) |
262 | iconv_close(tmpcd); |
263 | if((rc == ICONV_ERROR) || (in_bytes != 0)) { |
264 | failf(data, |
265 | "The Curl_convert_from_utf8 iconv call failed with errno %i: %s" , |
266 | errno, strerror(errno)); |
267 | return CURLE_CONV_FAILED; |
268 | } |
269 | if(output_ptr < input_ptr) { |
270 | /* null terminate the now shorter output string */ |
271 | *output_ptr = 0x00; |
272 | } |
273 | #else |
274 | failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required" ); |
275 | return CURLE_CONV_REQD; |
276 | #endif /* HAVE_ICONV */ |
277 | } |
278 | |
279 | return CURLE_OK; |
280 | } |
281 | |
282 | /* |
283 | * Init conversion stuff for a Curl_easy |
284 | */ |
285 | void Curl_convert_init(struct Curl_easy *data) |
286 | { |
287 | #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) |
288 | /* conversion descriptors for iconv calls */ |
289 | data->outbound_cd = (iconv_t)-1; |
290 | data->inbound_cd = (iconv_t)-1; |
291 | data->utf8_cd = (iconv_t)-1; |
292 | #else |
293 | (void)data; |
294 | #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ |
295 | } |
296 | |
297 | /* |
298 | * Setup conversion stuff for a Curl_easy |
299 | */ |
300 | void Curl_convert_setup(struct Curl_easy *data) |
301 | { |
302 | data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, |
303 | CURL_ICONV_CODESET_OF_NETWORK); |
304 | data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, |
305 | CURL_ICONV_CODESET_OF_HOST); |
306 | data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, |
307 | CURL_ICONV_CODESET_FOR_UTF8); |
308 | } |
309 | |
310 | /* |
311 | * Close conversion stuff for a Curl_easy |
312 | */ |
313 | |
314 | void Curl_convert_close(struct Curl_easy *data) |
315 | { |
316 | #ifdef HAVE_ICONV |
317 | /* close iconv conversion descriptors */ |
318 | if(data->inbound_cd != (iconv_t)-1) { |
319 | iconv_close(data->inbound_cd); |
320 | } |
321 | if(data->outbound_cd != (iconv_t)-1) { |
322 | iconv_close(data->outbound_cd); |
323 | } |
324 | if(data->utf8_cd != (iconv_t)-1) { |
325 | iconv_close(data->utf8_cd); |
326 | } |
327 | #else |
328 | (void)data; |
329 | #endif /* HAVE_ICONV */ |
330 | } |
331 | |
332 | #endif /* CURL_DOES_CONVERSIONS */ |
333 | |