1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "arch/win32/apr_arch_file_io.h"
21 #include "arch/win32/apr_arch_misc.h"
22 #include "ap_regkey.h"
30 AP_DECLARE(const ap_regkey_t *) ap_regkey_const(int i)
32 static struct ap_regkey_t ap_regkey_consts[7] =
34 {NULL, HKEY_CLASSES_ROOT},
35 {NULL, HKEY_CURRENT_CONFIG},
36 {NULL, HKEY_CURRENT_USER},
37 {NULL, HKEY_LOCAL_MACHINE},
39 {NULL, HKEY_PERFORMANCE_DATA},
42 return ap_regkey_consts + i;
46 apr_status_t regkey_cleanup(void *key)
48 ap_regkey_t *regkey = key;
50 if (regkey->hkey && regkey->hkey != INVALID_HANDLE_VALUE) {
51 RegCloseKey(regkey->hkey);
52 regkey->hkey = INVALID_HANDLE_VALUE;
58 AP_DECLARE(apr_status_t) ap_regkey_open(ap_regkey_t **newkey,
59 const ap_regkey_t *parentkey,
64 DWORD access = KEY_QUERY_VALUE;
71 if (flags & APR_WRITE)
74 #if APR_HAS_UNICODE_FS
77 apr_size_t keylen = strlen(keyname) + 1;
78 apr_size_t wkeylen = 256;
79 apr_wchar_t wkeyname[256];
80 apr_status_t rv = apr_conv_utf8_to_ucs2(keyname, &keylen, wkeyname, &wkeylen);
81 if (rv != APR_SUCCESS)
84 return APR_ENAMETOOLONG;
86 if (flags & APR_CREATE)
87 rc = RegCreateKeyExW(parentkey->hkey, wkeyname, 0, NULL, 0,
88 access, NULL, &hkey, &exists);
90 rc = RegOpenKeyExW(parentkey->hkey, wkeyname, 0, access, &hkey);
92 #endif /* APR_HAS_UNICODE_FS */
96 if (flags & APR_CREATE)
97 rc = RegCreateKeyEx(parentkey->hkey, keyname, 0, NULL, 0,
98 access, NULL, &hkey, &exists);
100 rc = RegOpenKeyEx(parentkey->hkey, keyname, 0, access, &hkey);
103 if (rc != ERROR_SUCCESS) {
104 return APR_FROM_OS_ERROR(rc);
106 if ((flags & APR_EXCL) && (exists == REG_OPENED_EXISTING_KEY)) {
111 *newkey = apr_palloc(pool, sizeof(**newkey));
112 (*newkey)->pool = pool;
113 (*newkey)->hkey = hkey;
114 apr_pool_cleanup_register((*newkey)->pool, (void *)(*newkey),
115 regkey_cleanup, apr_pool_cleanup_null);
120 AP_DECLARE(apr_status_t) ap_regkey_close(ap_regkey_t *regkey)
123 if ((stat = regkey_cleanup(regkey)) == APR_SUCCESS) {
124 apr_pool_cleanup_kill(regkey->pool, regkey, regkey_cleanup);
130 AP_DECLARE(apr_status_t) ap_regkey_remove(const ap_regkey_t *parent,
136 #if APR_HAS_UNICODE_FS
139 apr_size_t keylen = strlen(keyname) + 1;
140 apr_size_t wkeylen = 256;
141 apr_wchar_t wkeyname[256];
142 apr_status_t rv = apr_conv_utf8_to_ucs2(keyname, &keylen, wkeyname, &wkeylen);
143 if (rv != APR_SUCCESS)
146 return APR_ENAMETOOLONG;
147 rc = RegDeleteKeyW(parent->hkey, wkeyname);
149 #endif /* APR_HAS_UNICODE_FS */
153 /* We need to determine if subkeys exist on Win9x, to provide
154 * consistent behavior with NT, which returns access denied
155 * if subkeys exist when attempting to delete a key.
159 rc = RegOpenKeyEx(parent->hkey, keyname, 0, KEY_READ, &hkey);
160 if (rc != ERROR_SUCCESS)
161 return APR_FROM_OS_ERROR(rc);
162 rc = RegQueryInfoKey(hkey, NULL, NULL, NULL, &subkeys, NULL, NULL,
163 NULL, NULL, NULL, NULL, NULL);
165 if (rc != ERROR_SUCCESS)
166 return APR_FROM_OS_ERROR(rc);
168 return APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED);
169 rc = RegDeleteKey(parent->hkey, keyname);
172 if (rc != ERROR_SUCCESS) {
173 return APR_FROM_OS_ERROR(rc);
179 AP_DECLARE(apr_status_t) ap_regkey_value_get(char **result,
181 const char *valuename,
184 /* Retrieve a registry string value, and explode any envvars
185 * that the system has configured (e.g. %SystemRoot%/someapp.exe)
191 #if APR_HAS_UNICODE_FS
194 apr_size_t valuelen = strlen(valuename) + 1;
195 apr_size_t wvallen = 256;
196 apr_wchar_t wvalname[256];
199 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen);
200 if (rv != APR_SUCCESS)
203 return APR_ENAMETOOLONG;
204 /* Read to NULL buffer to determine value size */
205 rc = RegQueryValueExW(key->hkey, wvalname, 0, &type, NULL, &size);
206 if (rc != ERROR_SUCCESS) {
207 return APR_FROM_OS_ERROR(rc);
209 if ((size < 2) || (type != REG_SZ && type != REG_EXPAND_SZ)) {
210 return APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER);
213 wvalue = apr_palloc(pool, size);
214 /* Read value based on size query above */
215 rc = RegQueryValueExW(key->hkey, wvalname, 0, &type,
216 (LPBYTE)wvalue, &size);
217 if (rc != ERROR_SUCCESS) {
218 return APR_FROM_OS_ERROR(rc);
220 if (type == REG_EXPAND_SZ) {
222 size = ExpandEnvironmentStringsW(wvalue, zbuf, 0);
224 apr_wchar_t *tmp = wvalue;
225 /* The size returned by ExpandEnvironmentStringsW is wchars */
226 wvalue = apr_palloc(pool, size * 2);
227 size = ExpandEnvironmentStringsW(tmp, wvalue, size);
231 /* count wchars from RegQueryValueExW, rather than bytes */
234 /* ###: deliberately overallocate all but the trailing null.
235 * We could precalculate the exact buffer here instead, the question
236 * is a matter of storage v.s. cpu cycles.
238 valuelen = (size - 1) * 3 + 1;
239 *result = apr_palloc(pool, valuelen);
240 rv = apr_conv_ucs2_to_utf8(wvalue, &size, *result, &valuelen);
241 if (rv != APR_SUCCESS)
244 return APR_ENAMETOOLONG;
246 #endif /* APR_HAS_UNICODE_FS */
250 /* Read to NULL buffer to determine value size */
251 rc = RegQueryValueEx(key->hkey, valuename, 0, &type, NULL, &size);
252 if (rc != ERROR_SUCCESS)
253 return APR_FROM_OS_ERROR(rc);
255 if ((size < 1) || (type != REG_SZ && type != REG_EXPAND_SZ)) {
256 return APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER);
259 *result = apr_palloc(pool, size);
260 /* Read value based on size query above */
261 rc = RegQueryValueEx(key->hkey, valuename, 0, &type, *result, &size);
262 if (rc != ERROR_SUCCESS)
263 return APR_FROM_OS_ERROR(rc);
265 if (type == REG_EXPAND_SZ) {
266 /* Advise ExpandEnvironmentStrings that we have a zero char
267 * buffer to force computation of the required length.
270 size = ExpandEnvironmentStrings(*result, zbuf, 0);
273 *result = apr_palloc(pool, size);
274 size = ExpandEnvironmentStrings(tmp, *result, size);
283 AP_DECLARE(apr_status_t) ap_regkey_value_set(ap_regkey_t *key,
284 const char *valuename,
289 /* Retrieve a registry string value, and explode any envvars
290 * that the system has configured (e.g. %SystemRoot%/someapp.exe)
293 DWORD size = strlen(value) + 1;
294 DWORD type = (flags & AP_REGKEY_EXPAND) ? REG_EXPAND_SZ : REG_SZ;
296 #if APR_HAS_UNICODE_FS
300 apr_size_t valuelen = strlen(valuename) + 1;
301 apr_size_t wvallen = 256;
302 apr_wchar_t wvalname[256];
305 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen);
306 if (rv != APR_SUCCESS)
309 return APR_ENAMETOOLONG;
311 wvallen = alloclen = size;
312 wvalue = apr_palloc(pool, alloclen * 2);
313 rv = apr_conv_utf8_to_ucs2(value, &size, wvalue, &wvallen);
314 if (rv != APR_SUCCESS)
317 return APR_ENAMETOOLONG;
319 /* The size is the number of wchars consumed by apr_conv_utf8_to_ucs2
320 * converted to bytes; the trailing L'\0' continues to be counted.
322 size = (alloclen - wvallen) * 2;
323 rc = RegSetValueExW(key->hkey, wvalname, 0, type,
324 (LPBYTE)wvalue, size);
325 if (rc != ERROR_SUCCESS)
326 return APR_FROM_OS_ERROR(rc);
328 #endif /* APR_HAS_UNICODE_FS */
332 rc = RegSetValueEx(key->hkey, valuename, 0, type, value, size);
333 if (rc != ERROR_SUCCESS)
334 return APR_FROM_OS_ERROR(rc);
341 AP_DECLARE(apr_status_t) ap_regkey_value_raw_get(void **result,
342 apr_size_t *resultsize,
343 apr_int32_t *resulttype,
345 const char *valuename,
348 /* Retrieve a registry string value, and explode any envvars
349 * that the system has configured (e.g. %SystemRoot%/someapp.exe)
353 #if APR_HAS_UNICODE_FS
356 apr_size_t valuelen = strlen(valuename) + 1;
357 apr_size_t wvallen = 256;
358 apr_wchar_t wvalname[256];
360 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen);
361 if (rv != APR_SUCCESS)
364 return APR_ENAMETOOLONG;
365 /* Read to NULL buffer to determine value size */
366 rc = RegQueryValueExW(key->hkey, wvalname, 0, resulttype,
368 if (rc != ERROR_SUCCESS) {
369 return APR_FROM_OS_ERROR(rc);
372 /* Read value based on size query above */
373 *result = apr_palloc(pool, *resultsize);
374 rc = RegQueryValueExW(key->hkey, wvalname, 0, resulttype,
375 (LPBYTE)*result, resultsize);
377 #endif /* APR_HAS_UNICODE_FS */
381 /* Read to NULL buffer to determine value size */
382 rc = RegQueryValueEx(key->hkey, valuename, 0, resulttype,
384 if (rc != ERROR_SUCCESS)
385 return APR_FROM_OS_ERROR(rc);
387 /* Read value based on size query above */
388 *result = apr_palloc(pool, *resultsize);
389 rc = RegQueryValueEx(key->hkey, valuename, 0, resulttype,
390 (LPBYTE)*result, resultsize);
391 if (rc != ERROR_SUCCESS)
392 return APR_FROM_OS_ERROR(rc);
395 if (rc != ERROR_SUCCESS) {
396 return APR_FROM_OS_ERROR(rc);
403 AP_DECLARE(apr_status_t) ap_regkey_value_raw_set(ap_regkey_t *key,
404 const char *valuename,
406 apr_size_t valuesize,
407 apr_int32_t valuetype,
412 #if APR_HAS_UNICODE_FS
415 apr_size_t valuelen = strlen(valuename) + 1;
416 apr_size_t wvallen = 256;
417 apr_wchar_t wvalname[256];
419 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen);
420 if (rv != APR_SUCCESS)
423 return APR_ENAMETOOLONG;
425 rc = RegSetValueExW(key->hkey, wvalname, 0, valuetype,
426 (LPBYTE)value, valuesize);
428 #endif /* APR_HAS_UNICODE_FS */
432 rc = RegSetValueEx(key->hkey, valuename, 0, valuetype,
433 (LPBYTE)value, valuesize);
436 if (rc != ERROR_SUCCESS) {
437 return APR_FROM_OS_ERROR(rc);
443 AP_DECLARE(apr_status_t) ap_regkey_value_array_get(apr_array_header_t **result,
445 const char *valuename,
448 /* Retrieve a registry string value, and explode any envvars
449 * that the system has configured (e.g. %SystemRoot%/someapp.exe)
458 rv = ap_regkey_value_raw_get(&value, &size, &type, key, valuename, pool);
459 if (rv != APR_SUCCESS) {
462 else if (type != REG_MULTI_SZ) {
466 #if APR_HAS_UNICODE_FS
470 apr_size_t valuelen = strlen(valuename) + 1;
471 apr_size_t wvallen = 256;
472 apr_wchar_t *wvalue = (apr_wchar_t *)value;
474 /* ###: deliberately overallocate plus two extra nulls.
475 * We could precalculate the exact buffer here instead, the question
476 * is a matter of storage v.s. cpu cycles.
479 alloclen = valuelen = size * 3 + 2;
480 buf = apr_palloc(pool, valuelen);
481 rv = apr_conv_ucs2_to_utf8(value, &size, buf, &valuelen);
482 if (rv != APR_SUCCESS)
485 return APR_ENAMETOOLONG;
486 buf[(alloclen - valuelen)] = '\0';
487 buf[(alloclen - valuelen) + 1] = '\0';
489 #endif /* APR_HAS_UNICODE_FS */
493 /* Small possiblity the array is either unterminated
494 * or single NULL terminated. Avert.
497 if (size < 2 || buf[size - 1] != '\0' || buf[size - 2] != '\0') {
498 buf = apr_palloc(pool, size + 2);
499 memcpy(buf, value, size);
500 buf[size + 1] = '\0';
506 size = 0; /* Element Count */
507 for (tmp = buf; *tmp; ++tmp) {
514 *result = apr_array_make(pool, size, sizeof(char *));
515 for (tmp = buf; *tmp; ++tmp) {
516 char **newelem = (char **) apr_array_push(*result);
527 AP_DECLARE(apr_status_t) ap_regkey_value_array_set(ap_regkey_t *key,
528 const char *valuename,
530 const char * const * elts,
533 /* Retrieve a registry string value, and explode any envvars
534 * that the system has configured (e.g. %SystemRoot%/someapp.exe)
540 #if APR_HAS_UNICODE_FS
548 bufsize = 1; /* For trailing second null */
549 for (i = 0; i < nelts; ++i) {
550 bufsize += strlen(elts[i]) + 1;
557 buf = apr_palloc(pool, bufsize * 2);
559 for (i = 0; i < nelts; ++i) {
560 apr_size_t eltsize = strlen(elts[i]) + 1;
561 apr_size_t size = eltsize;
562 rv = apr_conv_utf8_to_ucs2(elts[i], &size, tmp, &bufrem);
563 if (rv != APR_SUCCESS)
566 return APR_ENAMETOOLONG;
574 *tmp = L'\0'; /* Trailing second null */
576 bufsize = (bufsize - bufrem) * 2;
579 #endif /* APR_HAS_UNICODE_FS */
586 bufsize = 1; /* For trailing second null */
587 for (i = 0; i < nelts; ++i) {
588 bufsize += strlen(elts[i]) + 1;
593 buf = apr_palloc(pool, bufsize);
595 for (i = 0; i < nelts; ++i) {
596 apr_size_t len = strlen(elts[i]) + 1;
597 memcpy(tmp, elts[i], len);
603 *tmp = '\0'; /* Trailing second null */
607 return ap_regkey_value_raw_set(key, valuename, value,
608 bufsize, REG_MULTI_SZ, pool);
612 AP_DECLARE(apr_status_t) ap_regkey_value_remove(const ap_regkey_t *key,
613 const char *valuename,
618 #if APR_HAS_UNICODE_FS
621 apr_size_t valuelen = strlen(valuename) + 1;
622 apr_size_t wvallen = 256;
623 apr_wchar_t wvalname[256];
624 apr_status_t rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen);
625 if (rv != APR_SUCCESS)
628 return APR_ENAMETOOLONG;
629 rc = RegDeleteValueW(key->hkey, wvalname);
631 #endif /* APR_HAS_UNICODE_FS */
635 rc = RegDeleteValue(key->hkey, valuename);
638 if (rc != ERROR_SUCCESS) {
639 return APR_FROM_OS_ERROR(rc);
644 #endif /* defined WIN32 */