]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/config_win.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_subr / config_win.c
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 \f
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
48 svn_error_t *
49 svn_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 \f
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
100 static svn_error_t *
101 parse_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 \f
153 /*** Exported interface. ***/
154
155 svn_error_t *
156 svn_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, &section_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, &section_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 */