From c07121768d2c9023243e70cb377013fb0a201637 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 31 Jul 2014 13:03:58 +0100 Subject: [PATCH] Add openconnect_utf8_to_legacy() helper function for charset conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases, the library itself really is going to have to know about legacy non-UTF8 locales — when opening files or setting environment variables, for example. Signed-off-by: David Woodhouse --- Makefile.am | 6 ++- configure.ac | 3 +- iconv.c | 91 ++++++++++++++++++++++++++++++++++++++++++ library.c | 17 ++++++++ openconnect-internal.h | 18 +++++++++ 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 iconv.c diff --git a/Makefile.am b/Makefile.am index 8ed039fb..d0e378aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ lib_srcs_openssl = openssl.c lib_srcs_win32 = tun-win32.c sspi.c lib_srcs_posix = tun.c lib_srcs_gssapi = gssapi.c +lib_srcs_iconv = iconv.c POTFILES = $(openconnect_SOURCES) $(lib_srcs_openssl) $(lib_srcs_gnutls) \ $(library_srcs) $(lib_srcs_win32) $(lib_srcs_posix) $(lib_srcs_gssapi) @@ -38,6 +39,9 @@ endif if OPENCONNECT_OPENSSL library_srcs += $(lib_srcs_openssl) endif +if OPENCONNECT_ICONV +library_srcs += $(lib_srcs_iconv) +endif if OPENCONNECT_WIN32 library_srcs += $(lib_srcs_win32) else @@ -46,7 +50,7 @@ endif libopenconnect_la_SOURCES = version.c $(library_srcs) libopenconnect_la_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(P11KIT_CFLAGS) $(TSS_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBOATH_CFLAGS) $(GSSAPI_CFLAGS) -libopenconnect_la_LIBADD = $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL) $(P11KIT_LIBS) $(TSS_LIBS) $(LIBSTOKEN_LIBS) $(LIBOATH_LIBS) $(GSSAPI_LIBS) +libopenconnect_la_LIBADD = $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL) $(P11KIT_LIBS) $(TSS_LIBS) $(LIBSTOKEN_LIBS) $(LIBOATH_LIBS) $(GSSAPI_LIBS) $(LIBICONV) if OPENBSD_LIBTOOL # OpenBSD's libtool doesn't have -version-number, but its -version-info arg # does what GNU libtool's -version-number does. Which arguably is what the diff --git a/configure.ac b/configure.ac index da519454..895c00c7 100644 --- a/configure.ac +++ b/configure.ac @@ -181,8 +181,8 @@ AC_DISABLE_STATIC AC_CHECK_FUNC(nl_langinfo, [AC_DEFINE(HAVE_NL_LANGINFO, 1, [Have nl_langinfo() function])], []) +have_iconv=no if test "$ac_cv_func_nl_langinfo" = "yes"; then - have_iconv=no LIBICONV= AC_MSG_CHECKING([for iconv support]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ @@ -205,6 +205,7 @@ if test "$ac_cv_func_nl_langinfo" = "yes"; then AC_DEFINE(HAVE_ICONV, 1, [Have iconv() function]) fi fi +AM_CONDITIONAL(OPENCONNECT_ICONV, [test "$have_iconv" = "yes"]) AC_ARG_ENABLE([nls], [ --disable-nls do not use Native Language Support], diff --git a/iconv.c b/iconv.c new file mode 100644 index 00000000..26cc891e --- /dev/null +++ b/iconv.c @@ -0,0 +1,91 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2008-2014 Intel Corporation. + * + * Author: David Woodhouse + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +#include +#include +#include + +#include "openconnect-internal.h" + +static char *convert_str(struct openconnect_info *vpninfo, iconv_t ic, + char *instr) + +{ + char *ic_in, *ic_out, *outstr; + size_t insize, outsize; + int addq = 0; + + if (ic == (iconv_t)-1) + return instr; + + iconv(ic, NULL, NULL, NULL, NULL); + + insize = strlen(instr) + 1; + ic_in = instr; + + outsize = insize; + ic_out = outstr = malloc(outsize); + if (!outstr) + return instr; + + while (insize) { + if (iconv(ic, &ic_in, &insize, &ic_out, &outsize) == (size_t)-1) { + perror("iconv"); + if (errno == EILSEQ) { + do { + ic_in++; + insize--; + } while (insize && (ic_in[0] & 0xc0) == 0x80); + addq = 1; + } + + if (!outsize || errno == E2BIG) { + int outlen = ic_out - outstr; + realloc_inplace(outstr, outlen + 10); + if (!outstr) + return instr; + ic_out = outstr + outlen; + outsize = 10; + } else if (errno != EILSEQ) { + /* Should never happen */ + free(outstr); + return instr; + } + if (addq) { + addq = 0; + *(ic_out++) = '?'; + outsize--; + } + } + } + + return outstr; +} + +char *openconnect_legacy_to_utf8(struct openconnect_info *vpninfo, + const char *legacy) +{ + return convert_str(vpninfo, vpninfo->ic_legacy_to_utf8, (char *)legacy); +} + +char *openconnect_utf8_to_legacy(struct openconnect_info *vpninfo, + const char *utf8) +{ + return convert_str(vpninfo, vpninfo->ic_utf8_to_legacy, (char *)utf8); +} diff --git a/library.c b/library.c index 83c3f394..a68733df 100644 --- a/library.c +++ b/library.c @@ -45,10 +45,22 @@ struct openconnect_info *openconnect_vpninfo_new(char *useragent, void *privdata) { struct openconnect_info *vpninfo = calloc(sizeof(*vpninfo), 1); +#ifdef HAVE_ICONV + char *charset = nl_langinfo(CODESET); +#endif if (!vpninfo) return NULL; +#ifdef HAVE_ICONV + if (charset && strcmp(charset, "UTF-8")) { + vpninfo->ic_utf8_to_legacy = iconv_open(charset, "UTF-8"); + vpninfo->ic_legacy_to_utf8 = iconv_open("UTF-8", charset); + } else { + vpninfo->ic_utf8_to_legacy = (iconv_t)-1; + vpninfo->ic_legacy_to_utf8 = (iconv_t)-1; + } +#endif #ifndef _WIN32 vpninfo->tun_fd = -1; #endif @@ -141,6 +153,11 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo) close(vpninfo->cmd_fd); close(vpninfo->cmd_fd_write); } + +#ifdef HAVE_ICONV + iconv_close(vpninfo->ic_utf8_to_legacy); + iconv_close(vpninfo->ic_legacy_to_utf8); +#endif #ifdef _WIN32 if (vpninfo->cmd_event) CloseHandle(vpninfo->cmd_event); diff --git a/openconnect-internal.h b/openconnect-internal.h index 6fa5a675..2900d0cb 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -62,6 +62,11 @@ #endif #endif +#ifdef HAVE_ICONV +#include +#include +#endif + #include #include #include @@ -174,6 +179,10 @@ struct proxy_auth_state { }; struct openconnect_info { +#ifdef HAVE_ICONV + iconv_t ic_legacy_to_utf8; + iconv_t ic_utf8_to_legacy; +#endif char *redirect_url; int redirect_type; @@ -521,6 +530,15 @@ int dumb_socketpair(int socks[2], int make_overlapped); /****************************************************************************/ +/* iconv.c */ +#ifdef HAVE_ICONV +char *openconnect_utf8_to_legacy(struct openconnect_info *vpninfo, const char *utf8); +char *openconnect_legacy_to_utf8(struct openconnect_info *vpninfo, const char *legacy); +#else +#define openconnect_utf8_to_legacy(v, str) ((char *)str) +#define openconnect_legacy_to_utf8(v, str) ((char *)str) +#endif + /* script.c */ int setenv_int(const char *opt, int value); void set_script_env(struct openconnect_info *vpninfo);