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