config_win.c revision 299742
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 http://subversion.tigris.org/issues/show_bug.cgi?id=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 (must_exist || !is_enoent) 205 return svn_error_createf(SVN_ERR_BAD_FILENAME, 206 is_enoent ? NULL 207 : svn_error_wrap_apr(apr_err, NULL), 208 _("Can't open registry key '%s'"), 209 svn_dirent_local_style(file, pool)); 210 else 211 return SVN_NO_ERROR; 212 } 213 214 215 subpool = svn_pool_create(pool); 216 section = svn_stringbuf_create_empty(subpool); 217 option = svn_stringbuf_create_empty(subpool); 218 value = svn_stringbuf_create_empty(subpool); 219 220 /* The top-level values belong to the [DEFAULT] section */ 221 svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION, 222 option, value); 223 if (svn_err) 224 goto cleanup; 225 226 /* Now enumerate the rest of the keys. */ 227 svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE); 228 for (index = 0; ; ++index) 229 { 230 DWORD section_len = (DWORD)section->blocksize; 231 HKEY sub_hkey; 232 233 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 234 NULL, NULL, NULL, NULL); 235 if (err == ERROR_NO_MORE_ITEMS) 236 break; 237 if (err == ERROR_MORE_DATA) 238 { 239 svn_stringbuf_ensure(section, section_len); 240 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 241 NULL, NULL, NULL, NULL); 242 } 243 if (err != ERROR_SUCCESS) 244 { 245 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 246 _("Can't enumerate registry keys")); 247 goto cleanup; 248 } 249 250 err = RegOpenKeyEx(hkey, section->data, 0, 251 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 252 &sub_hkey); 253 if (err != ERROR_SUCCESS) 254 { 255 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 256 _("Can't open existing subkey")); 257 goto cleanup; 258 } 259 260 svn_err = parse_section(cfg, sub_hkey, section->data, option, value); 261 RegCloseKey(sub_hkey); 262 if (svn_err) 263 goto cleanup; 264 } 265 266 cleanup: 267 RegCloseKey(hkey); 268 svn_pool_destroy(subpool); 269 return svn_err; 270} 271 272#endif /* WIN32 */ 273