1251881Speter/*
2251881Speter * config_win.c :  parsing configuration data from the registry
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include "svn_private_config.h"
27251881Speter
28251881Speter#ifdef WIN32
29251881Speter/* We must include windows.h ourselves or apr.h includes it for us with
30251881Speter   many ignore options set. Including Winsock is required to resolve IPv6
31251881Speter   compilation errors. APR_HAVE_IPV6 is only defined after including
32251881Speter   apr.h, so we can't detect this case here. */
33251881Speter
34251881Speter#define WIN32_LEAN_AND_MEAN
35251881Speter/* winsock2.h includes windows.h */
36251881Speter#include <winsock2.h>
37251881Speter#include <Ws2tcpip.h>
38251881Speter
39251881Speter#include <shlobj.h>
40251881Speter
41251881Speter#include <apr_file_info.h>
42251881Speter
43251881Speter#include "svn_error.h"
44251881Speter#include "svn_path.h"
45251881Speter#include "svn_pools.h"
46251881Speter#include "svn_utf.h"
47251881Speter
48251881Spetersvn_error_t *
49251881Spetersvn_config__win_config_path(const char **folder, int system_path,
50251881Speter                            apr_pool_t *pool)
51251881Speter{
52251881Speter  /* ### Adding CSIDL_FLAG_CREATE here, because those folders really
53251881Speter     must exist.  I'm not too sure about the SHGFP_TYPE_CURRENT
54251881Speter     semancics, though; maybe we should use ..._DEFAULT instead? */
55251881Speter  const int csidl = ((system_path ? CSIDL_COMMON_APPDATA : CSIDL_APPDATA)
56251881Speter                     | CSIDL_FLAG_CREATE);
57251881Speter
58251881Speter  WCHAR folder_ucs2[MAX_PATH];
59251881Speter  int inwords, outbytes, outlength;
60251881Speter  char *folder_utf8;
61251881Speter
62251881Speter  if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT,
63251881Speter                               folder_ucs2))
64251881Speter    return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
65251881Speter                          (system_path
66251881Speter                           ? "Can't determine the system config path"
67251881Speter                           : "Can't determine the user's config path"));
68251881Speter
69251881Speter  /* ### When mapping from UCS-2 to UTF-8, we need at most 3 bytes
70251881Speter         per wide char, plus extra space for the nul terminator. */
71251881Speter  inwords = lstrlenW(folder_ucs2);
72251881Speter  outbytes = outlength = 3 * (inwords + 1);
73251881Speter
74251881Speter  folder_utf8 = apr_palloc(pool, outlength);
75251881Speter
76251881Speter  outbytes = WideCharToMultiByte(CP_UTF8, 0, folder_ucs2, inwords,
77251881Speter                                 folder_utf8, outbytes, NULL, NULL);
78251881Speter
79251881Speter  if (outbytes == 0)
80251881Speter    return svn_error_wrap_apr(apr_get_os_error(),
81251881Speter                              "Can't convert config path to UTF-8");
82251881Speter
83251881Speter  /* Note that WideCharToMultiByte does _not_ terminate the
84251881Speter     outgoing buffer. */
85251881Speter  folder_utf8[outbytes] = '\0';
86251881Speter  *folder = folder_utf8;
87251881Speter
88251881Speter  return SVN_NO_ERROR;
89251881Speter}
90251881Speter
91251881Speter
92251881Speter#include "config_impl.h"
93251881Speter
94251881Speter/* ### These constants are insanely large, but (a) we want to avoid
95251881Speter   reallocating strings if possible, and (b) the realloc logic might
96251881Speter   not actually work -- you never know with Win32 ... */
97251881Speter#define SVN_REG_DEFAULT_NAME_SIZE  2048
98251881Speter#define SVN_REG_DEFAULT_VALUE_SIZE 8192
99251881Speter
100251881Speterstatic svn_error_t *
101251881Speterparse_section(svn_config_t *cfg, HKEY hkey, const char *section,
102251881Speter              svn_stringbuf_t *option, svn_stringbuf_t *value)
103251881Speter{
104251881Speter  DWORD option_len, type, index;
105251881Speter  LONG err;
106251881Speter
107251881Speter  /* Start with a reasonable size for the buffers. */
108251881Speter  svn_stringbuf_ensure(option, SVN_REG_DEFAULT_NAME_SIZE);
109251881Speter  svn_stringbuf_ensure(value, SVN_REG_DEFAULT_VALUE_SIZE);
110251881Speter  for (index = 0; ; ++index)
111251881Speter    {
112251881Speter      option_len = (DWORD)option->blocksize;
113251881Speter      err = RegEnumValue(hkey, index, option->data, &option_len,
114251881Speter                         NULL, &type, NULL, NULL);
115251881Speter      if (err == ERROR_NO_MORE_ITEMS)
116251881Speter          break;
117251881Speter      if (err == ERROR_INSUFFICIENT_BUFFER)
118251881Speter        {
119251881Speter          svn_stringbuf_ensure(option, option_len);
120251881Speter          err = RegEnumValue(hkey, index, option->data, &option_len,
121251881Speter                             NULL, &type, NULL, NULL);
122251881Speter        }
123251881Speter      if (err != ERROR_SUCCESS)
124251881Speter        return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
125251881Speter                                "Can't enumerate registry values");
126251881Speter
127251881Speter      /* Ignore option names that start with '#', see
128251881Speter         http://subversion.tigris.org/issues/show_bug.cgi?id=671 */
129251881Speter      if (type == REG_SZ && option->data[0] != '#')
130251881Speter        {
131251881Speter          DWORD value_len = (DWORD)value->blocksize;
132251881Speter          err = RegQueryValueEx(hkey, option->data, NULL, NULL,
133251881Speter                                (LPBYTE)value->data, &value_len);
134251881Speter          if (err == ERROR_MORE_DATA)
135251881Speter            {
136251881Speter              svn_stringbuf_ensure(value, value_len);
137251881Speter              err = RegQueryValueEx(hkey, option->data, NULL, NULL,
138251881Speter                                    (LPBYTE)value->data, &value_len);
139251881Speter            }
140251881Speter          if (err != ERROR_SUCCESS)
141251881Speter            return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
142251881Speter                                    "Can't read registry value data");
143251881Speter
144251881Speter          svn_config_set(cfg, section, option->data, value->data);
145251881Speter        }
146251881Speter    }
147251881Speter
148251881Speter  return SVN_NO_ERROR;
149251881Speter}
150251881Speter
151251881Speter
152251881Speter
153251881Speter/*** Exported interface. ***/
154251881Speter
155251881Spetersvn_error_t *
156251881Spetersvn_config__parse_registry(svn_config_t *cfg, const char *file,
157251881Speter                           svn_boolean_t must_exist, apr_pool_t *pool)
158251881Speter{
159251881Speter  apr_pool_t *subpool;
160251881Speter  svn_stringbuf_t *section, *option, *value;
161251881Speter  svn_error_t *svn_err = SVN_NO_ERROR;
162251881Speter  HKEY base_hkey, hkey;
163251881Speter  DWORD index;
164251881Speter  LONG err;
165251881Speter
166251881Speter  if (0 == strncmp(file, SVN_REGISTRY_HKLM, SVN_REGISTRY_HKLM_LEN))
167251881Speter    {
168251881Speter      base_hkey = HKEY_LOCAL_MACHINE;
169251881Speter      file += SVN_REGISTRY_HKLM_LEN;
170251881Speter    }
171251881Speter  else if (0 == strncmp(file, SVN_REGISTRY_HKCU, SVN_REGISTRY_HKCU_LEN))
172251881Speter    {
173251881Speter      base_hkey = HKEY_CURRENT_USER;
174251881Speter      file += SVN_REGISTRY_HKCU_LEN;
175251881Speter    }
176251881Speter  else
177251881Speter    {
178251881Speter      return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
179251881Speter                               "Unrecognised registry path '%s'",
180251881Speter                               svn_dirent_local_style(file, pool));
181251881Speter    }
182251881Speter
183251881Speter  err = RegOpenKeyEx(base_hkey, file, 0,
184251881Speter                     KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
185251881Speter                     &hkey);
186251881Speter  if (err != ERROR_SUCCESS)
187251881Speter    {
188251881Speter      const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err));
189251881Speter      if (!is_enoent)
190251881Speter        return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
191251881Speter                                 "Can't open registry key '%s'",
192251881Speter                                 svn_dirent_local_style(file, pool));
193251881Speter      else if (must_exist && is_enoent)
194251881Speter        return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
195251881Speter                                 "Can't find registry key '%s'",
196251881Speter                                 svn_dirent_local_style(file, pool));
197251881Speter      else
198251881Speter        return SVN_NO_ERROR;
199251881Speter    }
200251881Speter
201251881Speter
202251881Speter  subpool = svn_pool_create(pool);
203251881Speter  section = svn_stringbuf_create_empty(subpool);
204251881Speter  option = svn_stringbuf_create_empty(subpool);
205251881Speter  value = svn_stringbuf_create_empty(subpool);
206251881Speter
207251881Speter  /* The top-level values belong to the [DEFAULT] section */
208251881Speter  svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION,
209251881Speter                          option, value);
210251881Speter  if (svn_err)
211251881Speter    goto cleanup;
212251881Speter
213251881Speter  /* Now enumerate the rest of the keys. */
214251881Speter  svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE);
215251881Speter  for (index = 0; ; ++index)
216251881Speter    {
217251881Speter      DWORD section_len = (DWORD)section->blocksize;
218251881Speter      HKEY sub_hkey;
219251881Speter
220251881Speter      err = RegEnumKeyEx(hkey, index, section->data, &section_len,
221251881Speter                         NULL, NULL, NULL, NULL);
222251881Speter      if (err == ERROR_NO_MORE_ITEMS)
223251881Speter          break;
224251881Speter      if (err == ERROR_MORE_DATA)
225251881Speter        {
226251881Speter          svn_stringbuf_ensure(section, section_len);
227251881Speter          err = RegEnumKeyEx(hkey, index, section->data, &section_len,
228251881Speter                             NULL, NULL, NULL, NULL);
229251881Speter        }
230251881Speter      if (err != ERROR_SUCCESS)
231251881Speter        {
232251881Speter          svn_err =  svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
233251881Speter                                      "Can't enumerate registry keys");
234251881Speter          goto cleanup;
235251881Speter        }
236251881Speter
237251881Speter      err = RegOpenKeyEx(hkey, section->data, 0,
238251881Speter                         KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
239251881Speter                         &sub_hkey);
240251881Speter      if (err != ERROR_SUCCESS)
241251881Speter        {
242251881Speter          svn_err =  svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
243251881Speter                                      "Can't open existing subkey");
244251881Speter          goto cleanup;
245251881Speter        }
246251881Speter
247251881Speter      svn_err = parse_section(cfg, sub_hkey, section->data, option, value);
248251881Speter      RegCloseKey(sub_hkey);
249251881Speter      if (svn_err)
250251881Speter        goto cleanup;
251251881Speter    }
252251881Speter
253251881Speter cleanup:
254251881Speter  RegCloseKey(hkey);
255251881Speter  svn_pool_destroy(subpool);
256251881Speter  return svn_err;
257251881Speter}
258251881Speter
259251881Speter#endif /* WIN32 */
260