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