1178476Sjb/* 2178476Sjb * config_win.c : parsing configuration data from the registry 3178476Sjb * 4178476Sjb * ==================================================================== 5178476Sjb * Licensed to the Apache Software Foundation (ASF) under one 6178476Sjb * or more contributor license agreements. See the NOTICE file 7178476Sjb * distributed with this work for additional information 8178476Sjb * regarding copyright ownership. The ASF licenses this file 9178476Sjb * to you under the Apache License, Version 2.0 (the 10178476Sjb * "License"); you may not use this file except in compliance 11178476Sjb * with the License. You may obtain a copy of the License at 12178476Sjb * 13178476Sjb * http://www.apache.org/licenses/LICENSE-2.0 14178476Sjb * 15178476Sjb * Unless required by applicable law or agreed to in writing, 16178476Sjb * software distributed under the License is distributed on an 17178476Sjb * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18178476Sjb * KIND, either express or implied. See the License for the 19178476Sjb * specific language governing permissions and limitations 20178476Sjb * under the License. 21178476Sjb * ==================================================================== 22178476Sjb */ 23178476Sjb 24178476Sjb 25178476Sjb 26178476Sjb#include "svn_private_config.h" 27178476Sjb 28178476Sjb#ifdef WIN32 29178476Sjb/* We must include windows.h ourselves or apr.h includes it for us with 30178476Sjb many ignore options set. Including Winsock is required to resolve IPv6 31178476Sjb compilation errors. APR_HAVE_IPV6 is only defined after including 32178476Sjb apr.h, so we can't detect this case here. */ 33178476Sjb 34178476Sjb#define WIN32_LEAN_AND_MEAN 35178476Sjb/* winsock2.h includes windows.h */ 36178476Sjb#include <winsock2.h> 37178476Sjb#include <Ws2tcpip.h> 38178476Sjb 39178476Sjb#include <shlobj.h> 40178476Sjb 41178476Sjb#include <apr_file_info.h> 42178476Sjb 43178476Sjb#include "svn_error.h" 44178476Sjb#include "svn_path.h" 45178476Sjb#include "svn_pools.h" 46178476Sjb#include "svn_utf.h" 47 48svn_error_t * 49svn_config__win_config_path(const char **folder, int system_path, 50 apr_pool_t *pool) 51{ 52 /* ### Adding CSIDL_FLAG_CREATE here, because those folders really 53 must exist. I'm not too sure about the SHGFP_TYPE_CURRENT 54 semancics, though; maybe we should use ..._DEFAULT instead? */ 55 const int csidl = ((system_path ? CSIDL_COMMON_APPDATA : CSIDL_APPDATA) 56 | CSIDL_FLAG_CREATE); 57 58 WCHAR folder_ucs2[MAX_PATH]; 59 int inwords, outbytes, outlength; 60 char *folder_utf8; 61 62 if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, 63 folder_ucs2)) 64 return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 65 (system_path 66 ? "Can't determine the system config path" 67 : "Can't determine the user's config path")); 68 69 /* ### When mapping from UCS-2 to UTF-8, we need at most 3 bytes 70 per wide char, plus extra space for the nul terminator. */ 71 inwords = lstrlenW(folder_ucs2); 72 outbytes = outlength = 3 * (inwords + 1); 73 74 folder_utf8 = apr_palloc(pool, outlength); 75 76 outbytes = WideCharToMultiByte(CP_UTF8, 0, folder_ucs2, inwords, 77 folder_utf8, outbytes, NULL, NULL); 78 79 if (outbytes == 0) 80 return svn_error_wrap_apr(apr_get_os_error(), 81 "Can't convert config path to UTF-8"); 82 83 /* Note that WideCharToMultiByte does _not_ terminate the 84 outgoing buffer. */ 85 folder_utf8[outbytes] = '\0'; 86 *folder = folder_utf8; 87 88 return SVN_NO_ERROR; 89} 90 91 92#include "config_impl.h" 93 94/* ### These constants are insanely large, but (a) we want to avoid 95 reallocating strings if possible, and (b) the realloc logic might 96 not actually work -- you never know with Win32 ... */ 97#define SVN_REG_DEFAULT_NAME_SIZE 2048 98#define SVN_REG_DEFAULT_VALUE_SIZE 8192 99 100static svn_error_t * 101parse_section(svn_config_t *cfg, HKEY hkey, const char *section, 102 svn_stringbuf_t *option, svn_stringbuf_t *value) 103{ 104 DWORD option_len, type, index; 105 LONG err; 106 107 /* Start with a reasonable size for the buffers. */ 108 svn_stringbuf_ensure(option, SVN_REG_DEFAULT_NAME_SIZE); 109 svn_stringbuf_ensure(value, SVN_REG_DEFAULT_VALUE_SIZE); 110 for (index = 0; ; ++index) 111 { 112 option_len = (DWORD)option->blocksize; 113 err = RegEnumValue(hkey, index, option->data, &option_len, 114 NULL, &type, NULL, NULL); 115 if (err == ERROR_NO_MORE_ITEMS) 116 break; 117 if (err == ERROR_INSUFFICIENT_BUFFER) 118 { 119 svn_stringbuf_ensure(option, option_len); 120 err = RegEnumValue(hkey, index, option->data, &option_len, 121 NULL, &type, NULL, NULL); 122 } 123 if (err != ERROR_SUCCESS) 124 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 125 "Can't enumerate registry values"); 126 127 /* Ignore option names that start with '#', see 128 http://subversion.tigris.org/issues/show_bug.cgi?id=671 */ 129 if (type == REG_SZ && option->data[0] != '#') 130 { 131 DWORD value_len = (DWORD)value->blocksize; 132 err = RegQueryValueEx(hkey, option->data, NULL, NULL, 133 (LPBYTE)value->data, &value_len); 134 if (err == ERROR_MORE_DATA) 135 { 136 svn_stringbuf_ensure(value, value_len); 137 err = RegQueryValueEx(hkey, option->data, NULL, NULL, 138 (LPBYTE)value->data, &value_len); 139 } 140 if (err != ERROR_SUCCESS) 141 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 142 "Can't read registry value data"); 143 144 svn_config_set(cfg, section, option->data, value->data); 145 } 146 } 147 148 return SVN_NO_ERROR; 149} 150 151 152 153/*** Exported interface. ***/ 154 155svn_error_t * 156svn_config__parse_registry(svn_config_t *cfg, const char *file, 157 svn_boolean_t must_exist, apr_pool_t *pool) 158{ 159 apr_pool_t *subpool; 160 svn_stringbuf_t *section, *option, *value; 161 svn_error_t *svn_err = SVN_NO_ERROR; 162 HKEY base_hkey, hkey; 163 DWORD index; 164 LONG err; 165 166 if (0 == strncmp(file, SVN_REGISTRY_HKLM, SVN_REGISTRY_HKLM_LEN)) 167 { 168 base_hkey = HKEY_LOCAL_MACHINE; 169 file += SVN_REGISTRY_HKLM_LEN; 170 } 171 else if (0 == strncmp(file, SVN_REGISTRY_HKCU, SVN_REGISTRY_HKCU_LEN)) 172 { 173 base_hkey = HKEY_CURRENT_USER; 174 file += SVN_REGISTRY_HKCU_LEN; 175 } 176 else 177 { 178 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 179 "Unrecognised registry path '%s'", 180 svn_dirent_local_style(file, pool)); 181 } 182 183 err = RegOpenKeyEx(base_hkey, file, 0, 184 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 185 &hkey); 186 if (err != ERROR_SUCCESS) 187 { 188 const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err)); 189 if (!is_enoent) 190 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 191 "Can't open registry key '%s'", 192 svn_dirent_local_style(file, pool)); 193 else if (must_exist && is_enoent) 194 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 195 "Can't find registry key '%s'", 196 svn_dirent_local_style(file, pool)); 197 else 198 return SVN_NO_ERROR; 199 } 200 201 202 subpool = svn_pool_create(pool); 203 section = svn_stringbuf_create_empty(subpool); 204 option = svn_stringbuf_create_empty(subpool); 205 value = svn_stringbuf_create_empty(subpool); 206 207 /* The top-level values belong to the [DEFAULT] section */ 208 svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION, 209 option, value); 210 if (svn_err) 211 goto cleanup; 212 213 /* Now enumerate the rest of the keys. */ 214 svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE); 215 for (index = 0; ; ++index) 216 { 217 DWORD section_len = (DWORD)section->blocksize; 218 HKEY sub_hkey; 219 220 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 221 NULL, NULL, NULL, NULL); 222 if (err == ERROR_NO_MORE_ITEMS) 223 break; 224 if (err == ERROR_MORE_DATA) 225 { 226 svn_stringbuf_ensure(section, section_len); 227 err = RegEnumKeyEx(hkey, index, section->data, §ion_len, 228 NULL, NULL, NULL, NULL); 229 } 230 if (err != ERROR_SUCCESS) 231 { 232 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 233 "Can't enumerate registry keys"); 234 goto cleanup; 235 } 236 237 err = RegOpenKeyEx(hkey, section->data, 0, 238 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 239 &sub_hkey); 240 if (err != ERROR_SUCCESS) 241 { 242 svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 243 "Can't open existing subkey"); 244 goto cleanup; 245 } 246 247 svn_err = parse_section(cfg, sub_hkey, section->data, option, value); 248 RegCloseKey(sub_hkey); 249 if (svn_err) 250 goto cleanup; 251 } 252 253 cleanup: 254 RegCloseKey(hkey); 255 svn_pool_destroy(subpool); 256 return svn_err; 257} 258 259#endif /* WIN32 */ 260