Imported Upstream version 7.59.0
[platform/upstream/curl.git] / lib / non-ascii.c
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 #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  */
55 CURLcode 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 convers the buffer _in place_.
82  */
83 CURLcode 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 convers the buffer _in place_.
148  */
149 CURLcode 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  */
215 CURLcode 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  */
285 void 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  */
300 void 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
314 void 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 */