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 */
56CURLcode 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 */
84CURLcode 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 */
151CURLcode 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 */
218CURLcode 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 */
289void 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 */
304void 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
318void 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