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