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, &section_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, &section_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