]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/config_win.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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 #include "private/svn_utf_private.h"
48
49 #include "config_impl.h"
50
51 svn_error_t *
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 \f
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 *
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 \f
165 /*** Exported interface. ***/
166
167 svn_error_t *
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 (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 */