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