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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17#ifdef WIN32 18 19#include "apr.h" 20#include "arch/win32/apr_arch_file_io.h" 21#include "arch/win32/apr_arch_misc.h" 22#include "ap_regkey.h" 23 24struct ap_regkey_t { 25 apr_pool_t *pool; 26 HKEY hkey; 27}; 28 29 30AP_DECLARE(const ap_regkey_t *) ap_regkey_const(int i) 31{ 32 static struct ap_regkey_t ap_regkey_consts[7] = 33 { 34 {NULL, HKEY_CLASSES_ROOT}, 35 {NULL, HKEY_CURRENT_CONFIG}, 36 {NULL, HKEY_CURRENT_USER}, 37 {NULL, HKEY_LOCAL_MACHINE}, 38 {NULL, HKEY_USERS}, 39 {NULL, HKEY_PERFORMANCE_DATA}, 40 {NULL, HKEY_DYN_DATA} 41 }; 42 return ap_regkey_consts + i; 43} 44 45 46static apr_status_t regkey_cleanup(void *key) 47{ 48 ap_regkey_t *regkey = key; 49 50 if (regkey->hkey && regkey->hkey != INVALID_HANDLE_VALUE) { 51 RegCloseKey(regkey->hkey); 52 regkey->hkey = INVALID_HANDLE_VALUE; 53 } 54 return APR_SUCCESS; 55} 56 57 58AP_DECLARE(apr_status_t) ap_regkey_open(ap_regkey_t **newkey, 59 const ap_regkey_t *parentkey, 60 const char *keyname, 61 apr_int32_t flags, 62 apr_pool_t *pool) 63{ 64 DWORD access = KEY_QUERY_VALUE; 65 DWORD exists; 66 HKEY hkey; 67 LONG rc; 68 69 if (flags & APR_READ) 70 access |= KEY_READ; 71 if (flags & APR_WRITE) 72 access |= KEY_WRITE; 73 74#if APR_HAS_UNICODE_FS 75 IF_WIN_OS_IS_UNICODE 76 { 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) 82 return rv; 83 else if (keylen) 84 return APR_ENAMETOOLONG; 85 86 if (flags & APR_CREATE) 87 rc = RegCreateKeyExW(parentkey->hkey, wkeyname, 0, NULL, 0, 88 access, NULL, &hkey, &exists); 89 else 90 rc = RegOpenKeyExW(parentkey->hkey, wkeyname, 0, access, &hkey); 91 } 92#endif /* APR_HAS_UNICODE_FS */ 93#if APR_HAS_ANSI_FS 94 ELSE_WIN_OS_IS_ANSI 95 { 96 if (flags & APR_CREATE) 97 rc = RegCreateKeyEx(parentkey->hkey, keyname, 0, NULL, 0, 98 access, NULL, &hkey, &exists); 99 else 100 rc = RegOpenKeyEx(parentkey->hkey, keyname, 0, access, &hkey); 101 } 102#endif 103 if (rc != ERROR_SUCCESS) { 104 return APR_FROM_OS_ERROR(rc); 105 } 106 if ((flags & APR_EXCL) && (exists == REG_OPENED_EXISTING_KEY)) { 107 RegCloseKey(hkey); 108 return APR_EEXIST; 109 } 110 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); 116 return APR_SUCCESS; 117} 118 119 120AP_DECLARE(apr_status_t) ap_regkey_close(ap_regkey_t *regkey) 121{ 122 apr_status_t stat; 123 if ((stat = regkey_cleanup(regkey)) == APR_SUCCESS) { 124 apr_pool_cleanup_kill(regkey->pool, regkey, regkey_cleanup); 125 } 126 return stat; 127} 128 129 130AP_DECLARE(apr_status_t) ap_regkey_remove(const ap_regkey_t *parent, 131 const char *keyname, 132 apr_pool_t *pool) 133{ 134 LONG rc; 135 136#if APR_HAS_UNICODE_FS 137 IF_WIN_OS_IS_UNICODE 138 { 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) 144 return rv; 145 else if (keylen) 146 return APR_ENAMETOOLONG; 147 rc = RegDeleteKeyW(parent->hkey, wkeyname); 148 } 149#endif /* APR_HAS_UNICODE_FS */ 150#if APR_HAS_ANSI_FS 151 ELSE_WIN_OS_IS_ANSI 152 { 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. 156 */ 157 DWORD subkeys; 158 HKEY hkey; 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); 164 RegCloseKey(hkey); 165 if (rc != ERROR_SUCCESS) 166 return APR_FROM_OS_ERROR(rc); 167 else if (subkeys) 168 return APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED); 169 rc = RegDeleteKey(parent->hkey, keyname); 170 } 171#endif 172 if (rc != ERROR_SUCCESS) { 173 return APR_FROM_OS_ERROR(rc); 174 } 175 return APR_SUCCESS; 176} 177 178 179AP_DECLARE(apr_status_t) ap_regkey_value_get(char **result, 180 ap_regkey_t *key, 181 const char *valuename, 182 apr_pool_t *pool) 183{ 184 /* Retrieve a registry string value, and explode any envvars 185 * that the system has configured (e.g. %SystemRoot%/someapp.exe) 186 */ 187 LONG rc; 188 DWORD type; 189 apr_size_t size = 0; 190 191#if APR_HAS_UNICODE_FS 192 IF_WIN_OS_IS_UNICODE 193 { 194 apr_size_t valuelen = strlen(valuename) + 1; 195 apr_size_t wvallen = 256; 196 apr_wchar_t wvalname[256]; 197 apr_wchar_t *wvalue; 198 apr_status_t rv; 199 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen); 200 if (rv != APR_SUCCESS) 201 return rv; 202 else if (valuelen) 203 return APR_ENAMETOOLONG; 204 /* Read to NULL buffer to determine value size */ 205 rc = RegQueryValueExW(key->hkey, wvalname, 0, &type, NULL, (DWORD *)&size); 206 if (rc != ERROR_SUCCESS) { 207 return APR_FROM_OS_ERROR(rc); 208 } 209 if ((size < 2) || (type != REG_SZ && type != REG_EXPAND_SZ)) { 210 return APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER); 211 } 212 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, (DWORD *)&size); 217 if (rc != ERROR_SUCCESS) { 218 return APR_FROM_OS_ERROR(rc); 219 } 220 if (type == REG_EXPAND_SZ) { 221 apr_wchar_t zbuf[1]; 222 size = ExpandEnvironmentStringsW(wvalue, zbuf, 0); 223 if (size) { 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, (DWORD)size); 228 } 229 } 230 else { 231 /* count wchars from RegQueryValueExW, rather than bytes */ 232 size /= 2; 233 } 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. 237 */ 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) 242 return rv; 243 else if (size) 244 return APR_ENAMETOOLONG; 245 } 246#endif /* APR_HAS_UNICODE_FS */ 247#if APR_HAS_ANSI_FS 248 ELSE_WIN_OS_IS_ANSI 249 { 250 /* Read to NULL buffer to determine value size */ 251 rc = RegQueryValueEx(key->hkey, valuename, 0, &type, NULL, (DWORD *)&size); 252 if (rc != ERROR_SUCCESS) 253 return APR_FROM_OS_ERROR(rc); 254 255 if ((size < 1) || (type != REG_SZ && type != REG_EXPAND_SZ)) { 256 return APR_FROM_OS_ERROR(ERROR_INVALID_PARAMETER); 257 } 258 259 *result = apr_palloc(pool, size); 260 /* Read value based on size query above */ 261 rc = RegQueryValueEx(key->hkey, valuename, 0, &type, *result, (DWORD *)&size); 262 if (rc != ERROR_SUCCESS) 263 return APR_FROM_OS_ERROR(rc); 264 265 if (type == REG_EXPAND_SZ) { 266 /* Advise ExpandEnvironmentStrings that we have a zero char 267 * buffer to force computation of the required length. 268 */ 269 char zbuf[1]; 270 size = ExpandEnvironmentStrings(*result, zbuf, 0); 271 if (size) { 272 char *tmp = *result; 273 *result = apr_palloc(pool, size); 274 size = ExpandEnvironmentStrings(tmp, *result, (DWORD)size); 275 } 276 } 277 } 278#endif 279 return APR_SUCCESS; 280} 281 282 283AP_DECLARE(apr_status_t) ap_regkey_value_set(ap_regkey_t *key, 284 const char *valuename, 285 const char *value, 286 apr_int32_t flags, 287 apr_pool_t *pool) 288{ 289 /* Retrieve a registry string value, and explode any envvars 290 * that the system has configured (e.g. %SystemRoot%/someapp.exe) 291 */ 292 LONG rc; 293 apr_size_t size = strlen(value) + 1; 294 DWORD type = (flags & AP_REGKEY_EXPAND) ? REG_EXPAND_SZ : REG_SZ; 295 296#if APR_HAS_UNICODE_FS 297 IF_WIN_OS_IS_UNICODE 298 { 299 apr_size_t alloclen; 300 apr_size_t valuelen = strlen(valuename) + 1; 301 apr_size_t wvallen = 256; 302 apr_wchar_t wvalname[256]; 303 apr_wchar_t *wvalue; 304 apr_status_t rv; 305 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen); 306 if (rv != APR_SUCCESS) 307 return rv; 308 else if (valuelen) 309 return APR_ENAMETOOLONG; 310 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) 315 return rv; 316 else if (size) 317 return APR_ENAMETOOLONG; 318 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. 321 */ 322 size = (alloclen - wvallen) * 2; 323 rc = RegSetValueExW(key->hkey, wvalname, 0, type, 324 (LPBYTE)wvalue, (DWORD)size); 325 if (rc != ERROR_SUCCESS) 326 return APR_FROM_OS_ERROR(rc); 327 } 328#endif /* APR_HAS_UNICODE_FS */ 329#if APR_HAS_ANSI_FS 330 ELSE_WIN_OS_IS_ANSI 331 { 332 rc = RegSetValueEx(key->hkey, valuename, 0, type, value, (DWORD)size); 333 if (rc != ERROR_SUCCESS) 334 return APR_FROM_OS_ERROR(rc); 335 } 336#endif 337 return APR_SUCCESS; 338} 339 340 341AP_DECLARE(apr_status_t) ap_regkey_value_raw_get(void **result, 342 apr_size_t *resultsize, 343 apr_int32_t *resulttype, 344 ap_regkey_t *key, 345 const char *valuename, 346 apr_pool_t *pool) 347{ 348 /* Retrieve a registry string value, and explode any envvars 349 * that the system has configured (e.g. %SystemRoot%/someapp.exe) 350 */ 351 LONG rc; 352 353#if APR_HAS_UNICODE_FS 354 IF_WIN_OS_IS_UNICODE 355 { 356 apr_size_t valuelen = strlen(valuename) + 1; 357 apr_size_t wvallen = 256; 358 apr_wchar_t wvalname[256]; 359 apr_status_t rv; 360 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen); 361 if (rv != APR_SUCCESS) 362 return rv; 363 else if (valuelen) 364 return APR_ENAMETOOLONG; 365 /* Read to NULL buffer to determine value size */ 366 rc = RegQueryValueExW(key->hkey, wvalname, 0, (LPDWORD)resulttype, 367 NULL, (LPDWORD)resultsize); 368 if (rc != ERROR_SUCCESS) { 369 return APR_FROM_OS_ERROR(rc); 370 } 371 372 /* Read value based on size query above */ 373 *result = apr_palloc(pool, *resultsize); 374 rc = RegQueryValueExW(key->hkey, wvalname, 0, (LPDWORD)resulttype, 375 (LPBYTE)*result, (LPDWORD)resultsize); 376 } 377#endif /* APR_HAS_UNICODE_FS */ 378#if APR_HAS_ANSI_FS 379 ELSE_WIN_OS_IS_ANSI 380 { 381 /* Read to NULL buffer to determine value size */ 382 rc = RegQueryValueEx(key->hkey, valuename, 0, (LPDWORD)resulttype, 383 NULL, (LPDWORD)resultsize); 384 if (rc != ERROR_SUCCESS) 385 return APR_FROM_OS_ERROR(rc); 386 387 /* Read value based on size query above */ 388 *result = apr_palloc(pool, *resultsize); 389 rc = RegQueryValueEx(key->hkey, valuename, 0, (LPDWORD)resulttype, 390 (LPBYTE)*result, (LPDWORD)resultsize); 391 if (rc != ERROR_SUCCESS) 392 return APR_FROM_OS_ERROR(rc); 393 } 394#endif 395 if (rc != ERROR_SUCCESS) { 396 return APR_FROM_OS_ERROR(rc); 397 } 398 399 return APR_SUCCESS; 400} 401 402 403AP_DECLARE(apr_status_t) ap_regkey_value_raw_set(ap_regkey_t *key, 404 const char *valuename, 405 const void *value, 406 apr_size_t valuesize, 407 apr_int32_t valuetype, 408 apr_pool_t *pool) 409{ 410 LONG rc; 411 412#if APR_HAS_UNICODE_FS 413 IF_WIN_OS_IS_UNICODE 414 { 415 apr_size_t valuelen = strlen(valuename) + 1; 416 apr_size_t wvallen = 256; 417 apr_wchar_t wvalname[256]; 418 apr_status_t rv; 419 rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen); 420 if (rv != APR_SUCCESS) 421 return rv; 422 else if (valuelen) 423 return APR_ENAMETOOLONG; 424 425 rc = RegSetValueExW(key->hkey, wvalname, 0, valuetype, 426 (LPBYTE)value, (DWORD)valuesize); 427 } 428#endif /* APR_HAS_UNICODE_FS */ 429#if APR_HAS_ANSI_FS 430 ELSE_WIN_OS_IS_ANSI 431 { 432 rc = RegSetValueEx(key->hkey, valuename, 0, valuetype, 433 (LPBYTE)value, (DWORD)valuesize); 434 } 435#endif 436 if (rc != ERROR_SUCCESS) { 437 return APR_FROM_OS_ERROR(rc); 438 } 439 return APR_SUCCESS; 440} 441 442 443AP_DECLARE(apr_status_t) ap_regkey_value_array_get(apr_array_header_t **result, 444 ap_regkey_t *key, 445 const char *valuename, 446 apr_pool_t *pool) 447{ 448 /* Retrieve a registry string value, and explode any envvars 449 * that the system has configured (e.g. %SystemRoot%/someapp.exe) 450 */ 451 apr_status_t rv; 452 void *value; 453 char *buf; 454 char *tmp; 455 apr_int32_t type; 456 apr_size_t size = 0; 457 458 rv = ap_regkey_value_raw_get(&value, &size, &type, key, valuename, pool); 459 if (rv != APR_SUCCESS) { 460 return rv; 461 } 462 else if (type != REG_MULTI_SZ) { 463 return APR_EINVAL; 464 } 465 466#if APR_HAS_UNICODE_FS 467 IF_WIN_OS_IS_UNICODE 468 { 469 apr_size_t alloclen; 470 apr_size_t valuelen = strlen(valuename) + 1; 471 472 /* ###: deliberately overallocate plus two extra nulls. 473 * We could precalculate the exact buffer here instead, the question 474 * is a matter of storage v.s. cpu cycles. 475 */ 476 size /= 2; 477 alloclen = valuelen = size * 3 + 2; 478 buf = apr_palloc(pool, valuelen); 479 rv = apr_conv_ucs2_to_utf8(value, &size, buf, &valuelen); 480 if (rv != APR_SUCCESS) 481 return rv; 482 else if (size) 483 return APR_ENAMETOOLONG; 484 buf[(alloclen - valuelen)] = '\0'; 485 buf[(alloclen - valuelen) + 1] = '\0'; 486 } 487#endif /* APR_HAS_UNICODE_FS */ 488#if APR_HAS_ANSI_FS 489 ELSE_WIN_OS_IS_ANSI 490 { 491 /* Small possiblity the array is either unterminated 492 * or single NULL terminated. Avert. 493 */ 494 buf = (char *)value; 495 if (size < 2 || buf[size - 1] != '\0' || buf[size - 2] != '\0') { 496 buf = apr_palloc(pool, size + 2); 497 memcpy(buf, value, size); 498 buf[size + 1] = '\0'; 499 buf[size] = '\0'; 500 } 501 } 502#endif 503 504 size = 0; /* Element Count */ 505 for (tmp = buf; *tmp; ++tmp) { 506 ++size; 507 while (*tmp) { 508 ++tmp; 509 } 510 } 511 512 *result = apr_array_make(pool, (int)size, sizeof(char *)); 513 for (tmp = buf; *tmp; ++tmp) { 514 char **newelem = (char **) apr_array_push(*result); 515 *newelem = tmp; 516 while (*tmp) { 517 ++tmp; 518 } 519 } 520 521 return APR_SUCCESS; 522} 523 524 525AP_DECLARE(apr_status_t) ap_regkey_value_array_set(ap_regkey_t *key, 526 const char *valuename, 527 int nelts, 528 const char * const * elts, 529 apr_pool_t *pool) 530{ 531 /* Retrieve a registry string value, and explode any envvars 532 * that the system has configured (e.g. %SystemRoot%/someapp.exe) 533 */ 534 int i; 535 const void *value; 536 apr_size_t bufsize; 537 538#if APR_HAS_UNICODE_FS 539 IF_WIN_OS_IS_UNICODE 540 { 541 apr_status_t rv; 542 apr_wchar_t *buf; 543 apr_wchar_t *tmp; 544 apr_size_t bufrem; 545 546 bufsize = 1; /* For trailing second null */ 547 for (i = 0; i < nelts; ++i) { 548 bufsize += strlen(elts[i]) + 1; 549 } 550 if (!nelts) { 551 ++bufsize; 552 } 553 554 bufrem = bufsize; 555 buf = apr_palloc(pool, bufsize * 2); 556 tmp = buf; 557 for (i = 0; i < nelts; ++i) { 558 apr_size_t eltsize = strlen(elts[i]) + 1; 559 apr_size_t size = eltsize; 560 rv = apr_conv_utf8_to_ucs2(elts[i], &size, tmp, &bufrem); 561 if (rv != APR_SUCCESS) 562 return rv; 563 else if (size) 564 return APR_ENAMETOOLONG; 565 tmp += eltsize; 566 } 567 if (!nelts) { 568 --bufrem; 569 (*tmp++) = L'\0'; 570 } 571 --bufrem; 572 *tmp = L'\0'; /* Trailing second null */ 573 574 bufsize = (bufsize - bufrem) * 2; 575 value = (void*)buf; 576 } 577#endif /* APR_HAS_UNICODE_FS */ 578#if APR_HAS_ANSI_FS 579 ELSE_WIN_OS_IS_ANSI 580 { 581 char *buf; 582 char *tmp; 583 584 bufsize = 1; /* For trailing second null */ 585 for (i = 0; i < nelts; ++i) { 586 bufsize += strlen(elts[i]) + 1; 587 } 588 if (!nelts) { 589 ++bufsize; 590 } 591 buf = apr_palloc(pool, bufsize); 592 tmp = buf; 593 for (i = 0; i < nelts; ++i) { 594 apr_size_t len = strlen(elts[i]) + 1; 595 memcpy(tmp, elts[i], len); 596 tmp += len; 597 } 598 if (!nelts) { 599 (*tmp++) = '\0'; 600 } 601 *tmp = '\0'; /* Trailing second null */ 602 value = buf; 603 } 604#endif 605 return ap_regkey_value_raw_set(key, valuename, value, 606 bufsize, REG_MULTI_SZ, pool); 607} 608 609 610AP_DECLARE(apr_status_t) ap_regkey_value_remove(const ap_regkey_t *key, 611 const char *valuename, 612 apr_pool_t *pool) 613{ 614 LONG rc; 615 616#if APR_HAS_UNICODE_FS 617 IF_WIN_OS_IS_UNICODE 618 { 619 apr_size_t valuelen = strlen(valuename) + 1; 620 apr_size_t wvallen = 256; 621 apr_wchar_t wvalname[256]; 622 apr_status_t rv = apr_conv_utf8_to_ucs2(valuename, &valuelen, wvalname, &wvallen); 623 if (rv != APR_SUCCESS) 624 return rv; 625 else if (valuelen) 626 return APR_ENAMETOOLONG; 627 rc = RegDeleteValueW(key->hkey, wvalname); 628 } 629#endif /* APR_HAS_UNICODE_FS */ 630#if APR_HAS_ANSI_FS 631 ELSE_WIN_OS_IS_ANSI 632 { 633 rc = RegDeleteValue(key->hkey, valuename); 634 } 635#endif 636 if (rc != ERROR_SUCCESS) { 637 return APR_FROM_OS_ERROR(rc); 638 } 639 return APR_SUCCESS; 640} 641 642#endif /* defined WIN32 */ 643