6ccb4499e76412803e26ed5b8989e78bc38e1aa1
[platform/upstream/curl.git] / lib / non-ascii.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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 http://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  */
54 CURLcode Curl_convert_clone(struct SessionHandle *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  */
82 CURLcode Curl_convert_to_network(struct SessionHandle *data,
83                                  char *buffer, size_t length)
84 {
85   if(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     char *input_ptr, *output_ptr;
100     size_t in_bytes, out_bytes, rc;
101     int error;
102
103     /* open an iconv conversion descriptor if necessary */
104     if(data->outbound_cd == (iconv_t)-1) {
105       data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
106                                      CURL_ICONV_CODESET_OF_HOST);
107       if(data->outbound_cd == (iconv_t)-1) {
108         error = ERRNO;
109         failf(data,
110               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
111               CURL_ICONV_CODESET_OF_NETWORK,
112               CURL_ICONV_CODESET_OF_HOST,
113               error, strerror(error));
114         return CURLE_CONV_FAILED;
115       }
116     }
117     /* call iconv */
118     input_ptr = output_ptr = buffer;
119     in_bytes = out_bytes = length;
120     rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes,
121                &output_ptr, &out_bytes);
122     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
123       error = ERRNO;
124       failf(data,
125             "The Curl_convert_to_network iconv call failed with errno %i: %s",
126             error, strerror(error));
127       return CURLE_CONV_FAILED;
128     }
129 #else
130     failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required");
131     return CURLE_CONV_REQD;
132 #endif /* HAVE_ICONV */
133   }
134
135   return CURLE_OK;
136 }
137
138 /*
139  * Curl_convert_from_network() is an internal function for performing ASCII
140  * conversions on non-ASCII platforms. It convers the buffer _in place_.
141  */
142 CURLcode Curl_convert_from_network(struct SessionHandle *data,
143                                    char *buffer, size_t length)
144 {
145   if(data->set.convfromnetwork) {
146     /* use translation callback */
147     CURLcode result = data->set.convfromnetwork(buffer, length);
148     if(result) {
149       failf(data,
150             "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s",
151             (int)result, curl_easy_strerror(result));
152     }
153
154     return result;
155   }
156   else {
157 #ifdef HAVE_ICONV
158     /* do the translation ourselves */
159     char *input_ptr, *output_ptr;
160     size_t in_bytes, out_bytes, rc;
161     int error;
162
163     /* open an iconv conversion descriptor if necessary */
164     if(data->inbound_cd == (iconv_t)-1) {
165       data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
166                                     CURL_ICONV_CODESET_OF_NETWORK);
167       if(data->inbound_cd == (iconv_t)-1) {
168         error = ERRNO;
169         failf(data,
170               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
171               CURL_ICONV_CODESET_OF_HOST,
172               CURL_ICONV_CODESET_OF_NETWORK,
173               error, strerror(error));
174         return CURLE_CONV_FAILED;
175       }
176     }
177     /* call iconv */
178     input_ptr = output_ptr = buffer;
179     in_bytes = out_bytes = length;
180     rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes,
181                &output_ptr, &out_bytes);
182     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
183       error = ERRNO;
184       failf(data,
185             "Curl_convert_from_network iconv call failed with errno %i: %s",
186             error, strerror(error));
187       return CURLE_CONV_FAILED;
188     }
189 #else
190     failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required");
191     return CURLE_CONV_REQD;
192 #endif /* HAVE_ICONV */
193   }
194
195   return CURLE_OK;
196 }
197
198 /*
199  * Curl_convert_from_utf8() is an internal function for performing UTF-8
200  * conversions on non-ASCII platforms.
201  */
202 CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
203                                 char *buffer, size_t length)
204 {
205   if(data->set.convfromutf8) {
206     /* use translation callback */
207     CURLcode result = data->set.convfromutf8(buffer, length);
208     if(result) {
209       failf(data,
210             "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s",
211             (int)result, curl_easy_strerror(result));
212     }
213
214     return result;
215   }
216   else {
217 #ifdef HAVE_ICONV
218     /* do the translation ourselves */
219     const char *input_ptr;
220     char *output_ptr;
221     size_t in_bytes, out_bytes, rc;
222     int error;
223
224     /* open an iconv conversion descriptor if necessary */
225     if(data->utf8_cd == (iconv_t)-1) {
226       data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
227                                  CURL_ICONV_CODESET_FOR_UTF8);
228       if(data->utf8_cd == (iconv_t)-1) {
229         error = ERRNO;
230         failf(data,
231               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
232               CURL_ICONV_CODESET_OF_HOST,
233               CURL_ICONV_CODESET_FOR_UTF8,
234               error, strerror(error));
235         return CURLE_CONV_FAILED;
236       }
237     }
238     /* call iconv */
239     input_ptr = output_ptr = buffer;
240     in_bytes = out_bytes = length;
241     rc = iconv(data->utf8_cd, &input_ptr, &in_bytes,
242                &output_ptr, &out_bytes);
243     if((rc == ICONV_ERROR) || (in_bytes != 0)) {
244       error = ERRNO;
245       failf(data,
246             "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
247             error, strerror(error));
248       return CURLE_CONV_FAILED;
249     }
250     if(output_ptr < input_ptr) {
251       /* null terminate the now shorter output string */
252       *output_ptr = 0x00;
253     }
254 #else
255     failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required");
256     return CURLE_CONV_REQD;
257 #endif /* HAVE_ICONV */
258   }
259
260   return CURLE_OK;
261 }
262
263 /*
264  * Init conversion stuff for a SessionHandle
265  */
266 void Curl_convert_init(struct SessionHandle *data)
267 {
268 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
269   /* conversion descriptors for iconv calls */
270   data->outbound_cd = (iconv_t)-1;
271   data->inbound_cd  = (iconv_t)-1;
272   data->utf8_cd     = (iconv_t)-1;
273 #else
274   (void)data;
275 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
276 }
277
278 /*
279  * Setup conversion stuff for a SessionHandle
280  */
281 void Curl_convert_setup(struct SessionHandle *data)
282 {
283   data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
284                                 CURL_ICONV_CODESET_OF_NETWORK);
285   data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
286                                  CURL_ICONV_CODESET_OF_HOST);
287   data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
288                              CURL_ICONV_CODESET_FOR_UTF8);
289 }
290
291 /*
292  * Close conversion stuff for a SessionHandle
293  */
294
295 void Curl_convert_close(struct SessionHandle *data)
296 {
297 #ifdef HAVE_ICONV
298   /* close iconv conversion descriptors */
299   if(data->inbound_cd != (iconv_t)-1) {
300     iconv_close(data->inbound_cd);
301   }
302   if(data->outbound_cd != (iconv_t)-1) {
303     iconv_close(data->outbound_cd);
304   }
305   if(data->utf8_cd != (iconv_t)-1) {
306     iconv_close(data->utf8_cd);
307   }
308 #else
309   (void)data;
310 #endif /* HAVE_ICONV */
311 }
312
313 /*
314  * Curl_convert_form() is used from http.c, this converts any form items that
315    need to be sent in the network encoding.  Returns CURLE_OK on success.
316  */
317 CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form)
318 {
319   CURLcode result;
320
321   if(!data)
322     return CURLE_BAD_FUNCTION_ARGUMENT;
323
324   while(form) {
325     if(form->type == FORM_DATA) {
326       result = Curl_convert_to_network(data, form->line, form->length);
327       /* Curl_convert_to_network calls failf if unsuccessful */
328       if(result)
329         return result;
330     }
331
332     form = form->next;
333   }
334
335   return CURLE_OK;
336 }
337
338 #endif /* CURL_DOES_CONVERSIONS */