1/* 2 * config_win.c : parsing configuration data from the registry 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 25 26#include "svn_private_config.h" 27 28#ifdef WIN32 29/* We must include windows.h ourselves or apr.h includes it for us with 30 many ignore options set. Including Winsock is required to resolve IPv6 31 compilation errors. APR_HAVE_IPV6 is only defined after including 32 apr.h, so we can't detect this case here. */ 33 34#define WIN32_LEAN_AND_MEAN 35/* winsock2.h includes windows.h */ 36#include <winsock2.h> 37#include <Ws2tcpip.h> 38 39#include <shlobj.h> 40 41#include <apr_file_info.h> 42 43#include "svn_error.h" 44#include "svn_path.h" 45#include "svn_pools.h" 46#include "svn_utf.h" 47#include "private/svn_utf_private.h" 48 49#include "config_impl.h" 50 51svn_error_t * 52svn_config__win_config_path(const char **folder, 53 svn_boolean_t system_path, 54 apr_pool_t *result_pool, 55 apr_pool_t *scratch_pool) 56{ 57 /* ### Adding CSIDL_FLAG_CREATE here, because those folders really 58 must exist. I'm not too sure about the SHGFP_TYPE_CURRENT 59 semancics, though; maybe we should use ..._DEFAULT instead? */ 60 const int csidl = ((system_path ? CSIDL_COMMON_APPDATA : CSIDL_APPDATA) 61 | CSIDL_FLAG_CREATE); 62 63 WCHAR folder_ucs2[MAX_PATH]; 64 const char *folder_utf8; 65 66 if (! system_path) 67 { 68 HKEY hkey_tmp; 69 70 /* Verify if we actually have a *per user* profile to read from */ 71 if (ERROR_SUCCESS == RegOpenCurrentUser(KEY_SET_VALUE, &hkey_tmp)) 72 RegCloseKey(hkey_tmp); /* We have a profile */ 73 else 74 { 75 /* The user is not properly logged in. (Most likely we are running 76 in a service process). In this case Windows will return a default 77 read only 'roaming profile' directory, which we assume to be 78 writable. We will then spend many seconds trying to create a 79 configuration and then fail, because we are not allowed to write 80 there, but the retry loop in io.c doesn't know that. 81 82 We just answer that there is no user configuration directory. */ 83 84 *folder = NULL; 85 return SVN_NO_ERROR; 86 } 87 } 88 89 if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, 90 folder_ucs2)) 91 return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 92 (system_path 93 ? _("Can't determine the system config path") 94 : _("Can't determine the user's config path"))); 95 96 SVN_ERR(svn_utf__win32_utf16_to_utf8(&folder_utf8, folder_ucs2, 97 NULL, scratch_pool)); 98 *folder = svn_dirent_internal_style(folder_utf8, result_pool); 99 100 return SVN_NO_ERROR; 101} 102 103 104 105/* ### These constants are insanely large, but we want to avoid 106 reallocating strings if possible. */ 107#define SVN_REG_DEFAULT_NAME_SIZE 2048 108#define SVN_REG_DEFAULT_VALUE_SIZE 8192 109 110/* ### This function should be converted to use the unicode functions 111 ### instead of the ansi functions */ 112static svn_error_t * 113parse_section(svn_config_t *cfg, HKEY hkey, const char *section, 114 svn_stringbuf_t *option, svn_stringbuf_t *value) 115{ 116 DWORD option_len, type, index; 117 LONG err; 118 119 /* Start with a reasonable size for the buffers. */ 120 svn_stringbuf_ensure(option, SVN_REG_DEFAULT_NAME_SIZE); 121 svn_stringbuf_ensure(value, SVN_REG_DEFAULT_VALUE_SIZE); 122 for (index = 0; ; ++index) 123 { 124 option_len = (DWORD)option->blocksize; 125 err = RegEnumValue(hkey, index, option->data, &option_len, 126 NULL, &type, NULL, NULL); 127 if (err == ERROR_NO_MORE_ITEMS) 128 break; 129 if (err == ERROR_INSUFFICIENT_BUFFER) 130 { 131 svn_stringbuf_ensure(option, option_len); 132 err = RegEnumValue(hkey, index, option->data, &option_len, 133 NULL, &type, NULL, NULL); 134 } 135 if (err != ERROR_SUCCESS) 136 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 137 _("Can't enumerate registry values")); 138 139 /* Ignore option names that start with '#', see 140 https://issues.apache.org/jira/browse/SVN-671 */ 141 if (type == REG_SZ && option->data[0] != '#') 142 { 143 DWORD value_len = (DWORD)value->blocksize; 144 err = RegQueryValueEx(hkey, option->data, NULL, NULL, 145 (LPBYTE)value->data, &value_len); 146 if (err == ERROR_MORE_DATA) 147 { 148 svn_stringbuf_ensure(value, value_len); 149 err = RegQueryValueEx(hkey, option->data, NULL, NULL, 150 (LPBYTE)value->data, &value_len); 151 } 152 if (err != ERROR_SUCCESS) 153 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 154 _("Can't read registry value data")); 155 156 svn_config_set(cfg, section, option->data, value->data); 157 } 158 } 159 160 return SVN_NO_ERROR; 161} 162 163 164 165/*** Exported interface. ***/ 166 167svn_error_t * 168svn_config__parse_registry(svn_config_t *cfg, const char *file, 169 svn_boolean_t must_exist, apr_pool_t *pool) 170{ 171 apr_pool_t *subpool; 172 svn_stringbuf_t *section, *option, *value; 173 svn_error_t *svn_err = SVN_NO_ERROR; 174 HKEY base_hkey, hkey; 175 DWORD index; 176 LONG err; 177 178 if (0 == strncmp(file, SVN_REGISTRY_HKLM, SVN_REGISTRY_HKLM_LEN)) 179 { 180 base_hkey = HKEY_LOCAL_MACHINE; 181 file += SVN_REGISTRY_HKLM_LEN; 182 } 183 else if (0 == strncmp(file, SVN_REGISTRY_HKCU, SVN_REGISTRY_HKCU_LEN)) 184 { 185 base_hkey = HKEY_CURRENT_USER; 186 file += SVN_REGISTRY_HKCU_LEN; 187 } 188 else 189 { 190 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 191 _("Unrecognised registry path '%s'"), 192 svn_dirent_local_style(file, pool)); 193 } 194 195 err = RegOpenKeyEx(base_hkey, file, 0, 196 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 197 &hkey); 198 if (err != ERROR_SUCCESS) 199 { 200 apr_status_t apr_err = APR_FROM_OS_ERROR(err); 201 svn_boolean_t is_enoent = APR_STATUS_IS_ENOENT(apr_err) 202 || (err == ERROR_INVALID_HANDLE); 203 204 if (!is_enoent) 205 return svn_error_createf(SVN_ERR_BAD_FILENAME, 206 svn_error_wrap_apr(apr_err, NULL), 207 _("Can't open registry key '%s'"), 208 svn_dirent_local_style(file, pool)); 209 else if (must_exist) 210 return svn_error_createf(SVN_ERR_BAD_FILENAME, 211 NULL, 212 _("Can't open registry key '%s'"), 213 svn_dirent_local_style(file, pool)); 214 else 215 return SVN_NO_ERROR; 216 } 217 218 219 subpool = svn_pool_create(pool); 220 section = svn_stringbuf_create_empty(subpool); 221 option = svn_stringbuf_create_empty(subpool); 222 value = svn_stringbuf_create_empty(subpool); 223 224 /* The top-level values belong to the [DEFAULT] section */ 225 svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION, 226 option, value); 227 if (svn_err) 228 goto cleanup; 229 230 /* Now enumerate the rest of the keys. */ 231 svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE); 232 for (index = 0; ; ++index) 233 { 234 DWORD section_len = (DWORD)section->blocksize; 235 HKEY sub_hkey; 236 237 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 238 NULL, NULL, NULL, NULL); 239 if (err == ERROR_NO_MORE_ITEMS) 240 break; 241 if (err == ERROR_MORE_DATA) 242 { 243 svn_stringbuf_ensure(section, section_len); 244 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 245 NULL, NULL, NULL, NULL); 246 } 247 if (err != ERROR_SUCCESS) 248 { 249 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 250 _("Can't enumerate registry keys")); 251 goto cleanup; 252 } 253 254 err = RegOpenKeyEx(hkey, section->data, 0, 255 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 256 &sub_hkey); 257 if (err != ERROR_SUCCESS) 258 { 259 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 260 _("Can't open existing subkey")); 261 goto cleanup; 262 } 263 264 svn_err = parse_section(cfg, sub_hkey, section->data, option, value); 265 RegCloseKey(sub_hkey); 266 if (svn_err) 267 goto cleanup; 268 } 269 270 cleanup: 271 RegCloseKey(hkey); 272 svn_pool_destroy(subpool); 273 return svn_err; 274} 275 276#else /* !WIN32 */ 277 278/* Silence OSX ranlib warnings about object files with no symbols. */ 279#include <apr.h> 280extern const apr_uint32_t svn__fake__config_win; 281const apr_uint32_t svn__fake__config_win = 0xdeadbeef; 282 283#endif /* WIN32 */ 284