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