1/* 2 * win32_xlate.c : Windows xlate stuff. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* prevent "empty compilation unit" warning on e.g. UNIX */ 25typedef int win32_xlate__dummy; 26 27#ifdef WIN32 28 29/* Define _WIN32_DCOM for CoInitializeEx(). */ 30#define _WIN32_DCOM 31 32/* We must include windows.h ourselves or apr.h includes it for us with 33 many ignore options set. Including Winsock is required to resolve IPv6 34 compilation errors. APR_HAVE_IPV6 is only defined after including 35 apr.h, so we can't detect this case here. */ 36 37/* winsock2.h includes windows.h */ 38#include <winsock2.h> 39#include <Ws2tcpip.h> 40#include <mlang.h> 41 42#include <apr.h> 43#include <apr_errno.h> 44#include <apr_portable.h> 45 46#include "svn_pools.h" 47#include "svn_string.h" 48#include "svn_utf.h" 49#include "private/svn_atomic.h" 50 51#include "win32_xlate.h" 52 53static svn_atomic_t com_initialized = 0; 54 55/* Initializes COM and keeps COM available until process exit. 56 Implements svn_atomic__init_once init_func */ 57static svn_error_t * 58initialize_com(void *baton, apr_pool_t* pool) 59{ 60 /* Try to initialize for apartment-threaded object concurrency. */ 61 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 62 63 if (hr == RPC_E_CHANGED_MODE) 64 { 65 /* COM already initalized for multi-threaded object concurrency. We are 66 neutral to object concurrency so try to initalize it in the same way 67 for us, to keep an handle open. */ 68 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); 69 } 70 71 if (FAILED(hr)) 72 return svn_error_create(APR_EGENERAL, NULL, NULL); 73 74 return SVN_NO_ERROR; 75} 76 77typedef struct win32_xlate_t 78{ 79 UINT from_page_id; 80 UINT to_page_id; 81} win32_xlate_t; 82 83static apr_status_t 84get_page_id_from_name(UINT *page_id_p, const char *page_name, apr_pool_t *pool) 85{ 86 IMultiLanguage * mlang = NULL; 87 HRESULT hr; 88 MIMECSETINFO page_info; 89 WCHAR ucs2_page_name[128]; 90 svn_error_t *err; 91 92 if (page_name == SVN_APR_DEFAULT_CHARSET) 93 { 94 *page_id_p = CP_ACP; 95 return APR_SUCCESS; 96 } 97 else if (page_name == SVN_APR_LOCALE_CHARSET) 98 { 99 *page_id_p = CP_THREAD_ACP; /* Valid on Windows 2000+ */ 100 return APR_SUCCESS; 101 } 102 else if (!strcmp(page_name, "UTF-8")) 103 { 104 *page_id_p = CP_UTF8; 105 return APR_SUCCESS; 106 } 107 108 /* Use codepage identifier nnn if the codepage name is in the form 109 of "CPnnn". 110 We need this code since apr_os_locale_encoding() and svn_cmdline_init() 111 generates such codepage names even if they are not valid IANA charset 112 name. */ 113 if ((page_name[0] == 'c' || page_name[0] == 'C') 114 && (page_name[1] == 'p' || page_name[1] == 'P')) 115 { 116 *page_id_p = atoi(page_name + 2); 117 return APR_SUCCESS; 118 } 119 120 err = svn_atomic__init_once(&com_initialized, initialize_com, NULL, pool); 121 122 if (err) 123 { 124 svn_error_clear(err); 125 return APR_EGENERAL; 126 } 127 128 hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, 129 &IID_IMultiLanguage, (void **) &mlang); 130 131 if (FAILED(hr)) 132 return APR_EGENERAL; 133 134 /* Convert page name to wide string. */ 135 MultiByteToWideChar(CP_UTF8, 0, page_name, -1, ucs2_page_name, 136 sizeof(ucs2_page_name) / sizeof(ucs2_page_name[0])); 137 memset(&page_info, 0, sizeof(page_info)); 138 hr = mlang->lpVtbl->GetCharsetInfo(mlang, ucs2_page_name, &page_info); 139 if (FAILED(hr)) 140 { 141 mlang->lpVtbl->Release(mlang); 142 return APR_EINVAL; 143 } 144 145 if (page_info.uiInternetEncoding) 146 *page_id_p = page_info.uiInternetEncoding; 147 else 148 *page_id_p = page_info.uiCodePage; 149 150 mlang->lpVtbl->Release(mlang); 151 152 return APR_SUCCESS; 153} 154 155apr_status_t 156svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, const char *topage, 157 const char *frompage, apr_pool_t *pool) 158{ 159 UINT from_page_id, to_page_id; 160 apr_status_t apr_err = APR_SUCCESS; 161 win32_xlate_t *xlate; 162 163 apr_err = get_page_id_from_name(&to_page_id, topage, pool); 164 if (apr_err == APR_SUCCESS) 165 apr_err = get_page_id_from_name(&from_page_id, frompage, pool); 166 167 if (apr_err == APR_SUCCESS) 168 { 169 xlate = apr_palloc(pool, sizeof(*xlate)); 170 xlate->from_page_id = from_page_id; 171 xlate->to_page_id = to_page_id; 172 173 *xlate_p = xlate; 174 } 175 176 return apr_err; 177} 178 179apr_status_t 180svn_subr__win32_xlate_to_stringbuf(win32_xlate_t *handle, 181 const char *src_data, 182 apr_size_t src_length, 183 svn_stringbuf_t **dest, 184 apr_pool_t *pool) 185{ 186 WCHAR * wide_str; 187 int retval, wide_size; 188 189 if (src_length == 0) 190 { 191 *dest = svn_stringbuf_create_empty(pool); 192 return APR_SUCCESS; 193 } 194 195 retval = MultiByteToWideChar(handle->from_page_id, 0, src_data, src_length, 196 NULL, 0); 197 if (retval == 0) 198 return apr_get_os_error(); 199 200 wide_size = retval; 201 202 /* Allocate temporary buffer for small strings on stack instead of heap. */ 203 if (wide_size <= MAX_PATH) 204 { 205 wide_str = alloca(wide_size * sizeof(WCHAR)); 206 } 207 else 208 { 209 wide_str = apr_palloc(pool, wide_size * sizeof(WCHAR)); 210 } 211 212 retval = MultiByteToWideChar(handle->from_page_id, 0, src_data, src_length, 213 wide_str, wide_size); 214 215 if (retval == 0) 216 return apr_get_os_error(); 217 218 retval = WideCharToMultiByte(handle->to_page_id, 0, wide_str, wide_size, 219 NULL, 0, NULL, NULL); 220 221 if (retval == 0) 222 return apr_get_os_error(); 223 224 /* Ensure that buffer is enough to hold result string and termination 225 character. */ 226 *dest = svn_stringbuf_create_ensure(retval + 1, pool); 227 (*dest)->len = retval; 228 229 retval = WideCharToMultiByte(handle->to_page_id, 0, wide_str, wide_size, 230 (*dest)->data, (*dest)->len, NULL, NULL); 231 if (retval == 0) 232 return apr_get_os_error(); 233 234 (*dest)->len = retval; 235 return APR_SUCCESS; 236} 237 238#endif /* WIN32 */ 239