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