diff --git a/lib/Makefile.inc b/lib/Makefile.inc index f2a230e..89e6b71 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -21,7 +21,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c \ curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c \ warnless.c hmac.c polarssl.c curl_rtmp.c openldap.c curl_gethostname.c\ - gopher.c axtls.c + gopher.c axtls.c idn_win32.c HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ diff --git a/lib/idn_win32.c b/lib/idn_win32.c new file mode 100644 index 0000000..6544533 --- /dev/null +++ b/lib/idn_win32.c @@ -0,0 +1,111 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/*************************************************************************** + * IDN Implementation using windows native APIs + * Pierre Joye + ***************************************************************************/ +#if defined(WIN32) && defined(USE_WIN32_IDN) +#include "windows.h" +#include +#include +#define IDN_MAX_LENGTH 255 + +static wchar_t *_curl_win32_UTF8_to_wchar(const char *str_utf8) +{ + wchar_t *str_w = NULL; + + if (str_utf8) { + int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str_utf8, -1, NULL, 0); + if (str_w_len) { + str_w = (wchar_t *) malloc(str_w_len * sizeof(wchar_t)); + if (str_w) { + if (MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, str_w_len) == 0) { + free(str_w); + str_w = NULL; + } + } + } + } + + return str_w; +} + +static const char *_curl_win32_wchar_to_UTF8(const wchar_t *str_w) +{ + char *str_utf8 = NULL; + + if (str_w) { + size_t str_utf8_len = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, NULL, 0, NULL, NULL); + DWORD err = GetLastError(); + if (str_utf8_len) { + str_utf8 = (char *) malloc(str_utf8_len * sizeof(wchar_t)); + if (str_w) { + if (WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, str_utf8_len, NULL, FALSE) == 0) { + DWORD err = GetLastError(); + free((void *)str_utf8); + str_utf8 = NULL; + } + } + } + } + + return str_utf8; +} + +int curl_win32_idn_to_ascii(const char *in, char **out) +{ + wchar_t *in_w = _curl_win32_UTF8_to_wchar(in); + if (in_w) { + wchar_t punycode[IDN_MAX_LENGTH]; + if (IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH) == 0) { + wprintf(L"ERROR %d converting to Punycode\n", GetLastError()); + free(in_w); + return 0; + } + free(in_w); + + *out = (char *)_curl_win32_wchar_to_UTF8(punycode); + if (!(*out)) { + return 0; + } + } + return 1; +} + +int curl_win32_ascii_to_idn(const char *in, size_t in_len, char **out_utf8) +{ + if (in) { + WCHAR unicode[IDN_MAX_LENGTH]; + + if (IdnToUnicode(0, (wchar_t *)in, -1, unicode, IDN_MAX_LENGTH) == 0) { + wprintf(L"ERROR %d converting to Punycode\n", GetLastError()); + return 0; + } else { + const char *out_utf8 = _curl_win32_wchar_to_UTF8(unicode); + if (!out_utf8) { + return 0; + } + } + } + return 1; +} +#endif /* WIN32 */ diff --git a/lib/url.c b/lib/url.c index 95d024d..a1a9d76 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2658,7 +2658,7 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) data->state.connc->connects[conn->connectindex] = NULL; } -#ifdef USE_LIBIDN +#if defined(USE_LIBIDN) if(conn->host.encalloc) idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed with idn_free() since this was allocated @@ -2667,6 +2667,14 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed with idn_free() since this was allocated by libidn */ +#elif defined(USE_WIN32_IDN) + free(conn->host.encalloc); /* encoded host name buffer, must be freed + with idn_free() since this was allocated + by curl_win32_idn_to_ascii */ + if(conn->proxy.encalloc) + free(conn->proxy.encalloc); /* encoded proxy name buffer, must be + freed with idn_free() since this was + allocated by curl_win32_idn_to_ascii */ #endif Curl_ssl_close(conn, FIRSTSOCKET); @@ -3368,7 +3376,6 @@ CURLcode Curl_protocol_connect(struct connectdata *conn, /* * Helpers for IDNA convertions. */ -#ifdef USE_LIBIDN static bool is_ASCII_name(const char *hostname) { const unsigned char *ch = (const unsigned char*)hostname; @@ -3380,6 +3387,7 @@ static bool is_ASCII_name(const char *hostname) return TRUE; } +#ifdef USE_LIBIDN /* * Check if characters in hostname is allowed in Top Level Domain. */ @@ -3438,13 +3446,12 @@ static void fix_hostname(struct SessionHandle *data, /* set the name we use to display the host name */ host->dispname = host->name; - + if(!is_ASCII_name(host->name)) { #ifdef USE_LIBIDN /************************************************************* * Check name for non-ASCII and convert hostname to ACE form. *************************************************************/ - if(!is_ASCII_name(host->name) && - stringprep_check_version(LIBIDN_REQUIRED_VERSION)) { + if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) { char *ace_hostname = NULL; int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0); infof (data, "Input domain encoded as `%s'\n", @@ -3462,7 +3469,24 @@ static void fix_hostname(struct SessionHandle *data, host->name = host->encalloc; } } +#elif defined(USE_WIN32_IDN) + /************************************************************* + * Check name for non-ASCII and convert hostname to ACE form. + *************************************************************/ + char *ace_hostname = NULL; + int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname, 0); + if(rc == 0) + infof(data, "Failed to convert %s to ACE;\n", + host->name); + else { + host->encalloc = ace_hostname; + /* change the name pointer to point to the encoded hostname */ + host->name = host->encalloc; + } +#else + infof (data, "IDN support not present, can't parse Unicode (UTF-8) domains"); #endif + } } static void llist_dtor(void *user, void *element) diff --git a/lib/version.c b/lib/version.c index 9ba2e33..71b516d 100644 --- a/lib/version.c +++ b/lib/version.c @@ -99,6 +99,11 @@ char *curl_version(void) ptr += len; } #endif +#ifdef USE_WIN32_IDN + len = snprintf(ptr, left, " IDN Windows native APIs support"); + left -= len; + ptr += len; +#endif #if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) #ifdef _LIBICONV_VERSION len = snprintf(ptr, left, " iconv/%d.%d", @@ -301,6 +306,8 @@ curl_version_info_data *curl_version_info(CURLversion stamp) version_info.libidn = stringprep_check_version(LIBIDN_REQUIRED_VERSION); if(version_info.libidn) version_info.features |= CURL_VERSION_IDN; +#else if (defined(USE_WIN32_IDN)) + version_info.features |= CURL_VERSION_IDN; #endif #if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)