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
51 svn_error_t *
svn_config__win_config_path(const char ** folder,svn_boolean_t system_path,apr_pool_t * result_pool,apr_pool_t * scratch_pool)52 svn_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 */
112 static svn_error_t *
parse_section(svn_config_t * cfg,HKEY hkey,const char * section,svn_stringbuf_t * option,svn_stringbuf_t * value)113 parse_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
167 svn_error_t *
svn_config__parse_registry(svn_config_t * cfg,const char * file,svn_boolean_t must_exist,apr_pool_t * pool)168 svn_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>
280 extern const apr_uint32_t svn__fake__config_win;
281 const apr_uint32_t svn__fake__config_win = 0xdeadbeef;
282
283 #endif /* WIN32 */
284