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 */
55CURLcode 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 */
83CURLcode 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 */
149CURLcode 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 */
215CURLcode 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 */
285void 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 */
300void 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
314void 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