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 */
54CURLcode 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 */
82CURLcode 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 */
143CURLcode 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 */
204CURLcode 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 */
269void 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 */
284void 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
298void 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 */
320CURLcode 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 */
344