| 1 | /*************************************************************************** | 
|---|
| 2 | *                                  _   _ ____  _ | 
|---|
| 3 | *  Project                     ___| | | |  _ \| | | 
|---|
| 4 | *                             / __| | | | |_) | | | 
|---|
| 5 | *                            | (__| |_| |  _ <| |___ | 
|---|
| 6 | *                             \___|\___/|_| \_\_____| | 
|---|
| 7 | * | 
|---|
| 8 | * Copyright (C) 1998 - 2019, 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 converts 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 converts 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 */ | 
|---|
| 333 |  | 
|---|