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  */
Curl_convert_clone(struct Curl_easy * data,const char * indata,size_t insize,char ** outbuf)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  */
Curl_convert_to_network(struct Curl_easy * data,char * buffer,size_t length)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  */
Curl_convert_from_network(struct Curl_easy * data,char * buffer,size_t length)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  */
Curl_convert_from_utf8(struct Curl_easy * data,char * buffer,size_t length)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  */
Curl_convert_init(struct Curl_easy * data)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  */
Curl_convert_setup(struct Curl_easy * data)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 
Curl_convert_close(struct Curl_easy * data)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