1251881Speter/* 2251881Speter * config_win.c : parsing configuration data from the registry 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include "svn_private_config.h" 27251881Speter 28251881Speter#ifdef WIN32 29251881Speter/* We must include windows.h ourselves or apr.h includes it for us with 30251881Speter many ignore options set. Including Winsock is required to resolve IPv6 31251881Speter compilation errors. APR_HAVE_IPV6 is only defined after including 32251881Speter apr.h, so we can't detect this case here. */ 33251881Speter 34251881Speter#define WIN32_LEAN_AND_MEAN 35251881Speter/* winsock2.h includes windows.h */ 36251881Speter#include <winsock2.h> 37251881Speter#include <Ws2tcpip.h> 38251881Speter 39251881Speter#include <shlobj.h> 40251881Speter 41251881Speter#include <apr_file_info.h> 42251881Speter 43251881Speter#include "svn_error.h" 44251881Speter#include "svn_path.h" 45251881Speter#include "svn_pools.h" 46251881Speter#include "svn_utf.h" 47251881Speter 48251881Spetersvn_error_t * 49251881Spetersvn_config__win_config_path(const char **folder, int system_path, 50251881Speter apr_pool_t *pool) 51251881Speter{ 52251881Speter /* ### Adding CSIDL_FLAG_CREATE here, because those folders really 53251881Speter must exist. I'm not too sure about the SHGFP_TYPE_CURRENT 54251881Speter semancics, though; maybe we should use ..._DEFAULT instead? */ 55251881Speter const int csidl = ((system_path ? CSIDL_COMMON_APPDATA : CSIDL_APPDATA) 56251881Speter | CSIDL_FLAG_CREATE); 57251881Speter 58251881Speter WCHAR folder_ucs2[MAX_PATH]; 59251881Speter int inwords, outbytes, outlength; 60251881Speter char *folder_utf8; 61251881Speter 62251881Speter if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, 63251881Speter folder_ucs2)) 64251881Speter return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 65251881Speter (system_path 66251881Speter ? "Can't determine the system config path" 67251881Speter : "Can't determine the user's config path")); 68251881Speter 69251881Speter /* ### When mapping from UCS-2 to UTF-8, we need at most 3 bytes 70251881Speter per wide char, plus extra space for the nul terminator. */ 71251881Speter inwords = lstrlenW(folder_ucs2); 72251881Speter outbytes = outlength = 3 * (inwords + 1); 73251881Speter 74251881Speter folder_utf8 = apr_palloc(pool, outlength); 75251881Speter 76251881Speter outbytes = WideCharToMultiByte(CP_UTF8, 0, folder_ucs2, inwords, 77251881Speter folder_utf8, outbytes, NULL, NULL); 78251881Speter 79251881Speter if (outbytes == 0) 80251881Speter return svn_error_wrap_apr(apr_get_os_error(), 81251881Speter "Can't convert config path to UTF-8"); 82251881Speter 83251881Speter /* Note that WideCharToMultiByte does _not_ terminate the 84251881Speter outgoing buffer. */ 85251881Speter folder_utf8[outbytes] = '\0'; 86251881Speter *folder = folder_utf8; 87251881Speter 88251881Speter return SVN_NO_ERROR; 89251881Speter} 90251881Speter 91251881Speter 92251881Speter#include "config_impl.h" 93251881Speter 94251881Speter/* ### These constants are insanely large, but (a) we want to avoid 95251881Speter reallocating strings if possible, and (b) the realloc logic might 96251881Speter not actually work -- you never know with Win32 ... */ 97251881Speter#define SVN_REG_DEFAULT_NAME_SIZE 2048 98251881Speter#define SVN_REG_DEFAULT_VALUE_SIZE 8192 99251881Speter 100251881Speterstatic svn_error_t * 101251881Speterparse_section(svn_config_t *cfg, HKEY hkey, const char *section, 102251881Speter svn_stringbuf_t *option, svn_stringbuf_t *value) 103251881Speter{ 104251881Speter DWORD option_len, type, index; 105251881Speter LONG err; 106251881Speter 107251881Speter /* Start with a reasonable size for the buffers. */ 108251881Speter svn_stringbuf_ensure(option, SVN_REG_DEFAULT_NAME_SIZE); 109251881Speter svn_stringbuf_ensure(value, SVN_REG_DEFAULT_VALUE_SIZE); 110251881Speter for (index = 0; ; ++index) 111251881Speter { 112251881Speter option_len = (DWORD)option->blocksize; 113251881Speter err = RegEnumValue(hkey, index, option->data, &option_len, 114251881Speter NULL, &type, NULL, NULL); 115251881Speter if (err == ERROR_NO_MORE_ITEMS) 116251881Speter break; 117251881Speter if (err == ERROR_INSUFFICIENT_BUFFER) 118251881Speter { 119251881Speter svn_stringbuf_ensure(option, option_len); 120251881Speter err = RegEnumValue(hkey, index, option->data, &option_len, 121251881Speter NULL, &type, NULL, NULL); 122251881Speter } 123251881Speter if (err != ERROR_SUCCESS) 124251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 125251881Speter "Can't enumerate registry values"); 126251881Speter 127251881Speter /* Ignore option names that start with '#', see 128251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=671 */ 129251881Speter if (type == REG_SZ && option->data[0] != '#') 130251881Speter { 131251881Speter DWORD value_len = (DWORD)value->blocksize; 132251881Speter err = RegQueryValueEx(hkey, option->data, NULL, NULL, 133251881Speter (LPBYTE)value->data, &value_len); 134251881Speter if (err == ERROR_MORE_DATA) 135251881Speter { 136251881Speter svn_stringbuf_ensure(value, value_len); 137251881Speter err = RegQueryValueEx(hkey, option->data, NULL, NULL, 138251881Speter (LPBYTE)value->data, &value_len); 139251881Speter } 140251881Speter if (err != ERROR_SUCCESS) 141251881Speter return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 142251881Speter "Can't read registry value data"); 143251881Speter 144251881Speter svn_config_set(cfg, section, option->data, value->data); 145251881Speter } 146251881Speter } 147251881Speter 148251881Speter return SVN_NO_ERROR; 149251881Speter} 150251881Speter 151251881Speter 152251881Speter 153251881Speter/*** Exported interface. ***/ 154251881Speter 155251881Spetersvn_error_t * 156251881Spetersvn_config__parse_registry(svn_config_t *cfg, const char *file, 157251881Speter svn_boolean_t must_exist, apr_pool_t *pool) 158251881Speter{ 159251881Speter apr_pool_t *subpool; 160251881Speter svn_stringbuf_t *section, *option, *value; 161251881Speter svn_error_t *svn_err = SVN_NO_ERROR; 162251881Speter HKEY base_hkey, hkey; 163251881Speter DWORD index; 164251881Speter LONG err; 165251881Speter 166251881Speter if (0 == strncmp(file, SVN_REGISTRY_HKLM, SVN_REGISTRY_HKLM_LEN)) 167251881Speter { 168251881Speter base_hkey = HKEY_LOCAL_MACHINE; 169251881Speter file += SVN_REGISTRY_HKLM_LEN; 170251881Speter } 171251881Speter else if (0 == strncmp(file, SVN_REGISTRY_HKCU, SVN_REGISTRY_HKCU_LEN)) 172251881Speter { 173251881Speter base_hkey = HKEY_CURRENT_USER; 174251881Speter file += SVN_REGISTRY_HKCU_LEN; 175251881Speter } 176251881Speter else 177251881Speter { 178251881Speter return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 179251881Speter "Unrecognised registry path '%s'", 180251881Speter svn_dirent_local_style(file, pool)); 181251881Speter } 182251881Speter 183251881Speter err = RegOpenKeyEx(base_hkey, file, 0, 184251881Speter KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 185251881Speter &hkey); 186251881Speter if (err != ERROR_SUCCESS) 187251881Speter { 188251881Speter const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err)); 189251881Speter if (!is_enoent) 190251881Speter return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 191251881Speter "Can't open registry key '%s'", 192251881Speter svn_dirent_local_style(file, pool)); 193251881Speter else if (must_exist && is_enoent) 194251881Speter return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 195251881Speter "Can't find registry key '%s'", 196251881Speter svn_dirent_local_style(file, pool)); 197251881Speter else 198251881Speter return SVN_NO_ERROR; 199251881Speter } 200251881Speter 201251881Speter 202251881Speter subpool = svn_pool_create(pool); 203251881Speter section = svn_stringbuf_create_empty(subpool); 204251881Speter option = svn_stringbuf_create_empty(subpool); 205251881Speter value = svn_stringbuf_create_empty(subpool); 206251881Speter 207251881Speter /* The top-level values belong to the [DEFAULT] section */ 208251881Speter svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION, 209251881Speter option, value); 210251881Speter if (svn_err) 211251881Speter goto cleanup; 212251881Speter 213251881Speter /* Now enumerate the rest of the keys. */ 214251881Speter svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE); 215251881Speter for (index = 0; ; ++index) 216251881Speter { 217251881Speter DWORD section_len = (DWORD)section->blocksize; 218251881Speter HKEY sub_hkey; 219251881Speter 220251881Speter err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 221251881Speter NULL, NULL, NULL, NULL); 222251881Speter if (err == ERROR_NO_MORE_ITEMS) 223251881Speter break; 224251881Speter if (err == ERROR_MORE_DATA) 225251881Speter { 226251881Speter svn_stringbuf_ensure(section, section_len); 227251881Speter err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 228251881Speter NULL, NULL, NULL, NULL); 229251881Speter } 230251881Speter if (err != ERROR_SUCCESS) 231251881Speter { 232251881Speter svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 233251881Speter "Can't enumerate registry keys"); 234251881Speter goto cleanup; 235251881Speter } 236251881Speter 237251881Speter err = RegOpenKeyEx(hkey, section->data, 0, 238251881Speter KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 239251881Speter &sub_hkey); 240251881Speter if (err != ERROR_SUCCESS) 241251881Speter { 242251881Speter svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 243251881Speter "Can't open existing subkey"); 244251881Speter goto cleanup; 245251881Speter } 246251881Speter 247251881Speter svn_err = parse_section(cfg, sub_hkey, section->data, option, value); 248251881Speter RegCloseKey(sub_hkey); 249251881Speter if (svn_err) 250251881Speter goto cleanup; 251251881Speter } 252251881Speter 253251881Speter cleanup: 254251881Speter RegCloseKey(hkey); 255251881Speter svn_pool_destroy(subpool); 256251881Speter return svn_err; 257251881Speter} 258251881Speter 259251881Speter#endif /* WIN32 */ 260