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 
24 struct ap_regkey_t {
25     apr_pool_t *pool;
26     HKEY        hkey;
27 };
28 
29 
ap_regkey_const(int i)30 AP_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 
regkey_cleanup(void * key)46 static 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 
ap_regkey_open(ap_regkey_t ** newkey,const ap_regkey_t * parentkey,const char * keyname,apr_int32_t flags,apr_pool_t * pool)58 AP_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 
ap_regkey_close(ap_regkey_t * regkey)120 AP_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 
ap_regkey_remove(const ap_regkey_t * parent,const char * keyname,apr_pool_t * pool)130 AP_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 
ap_regkey_value_get(char ** result,ap_regkey_t * key,const char * valuename,apr_pool_t * pool)179 AP_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 
ap_regkey_value_set(ap_regkey_t * key,const char * valuename,const char * value,apr_int32_t flags,apr_pool_t * pool)283 AP_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 
ap_regkey_value_raw_get(void ** result,apr_size_t * resultsize,apr_int32_t * resulttype,ap_regkey_t * key,const char * valuename,apr_pool_t * pool)341 AP_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 
ap_regkey_value_raw_set(ap_regkey_t * key,const char * valuename,const void * value,apr_size_t valuesize,apr_int32_t valuetype,apr_pool_t * pool)403 AP_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 
ap_regkey_value_array_get(apr_array_header_t ** result,ap_regkey_t * key,const char * valuename,apr_pool_t * pool)443 AP_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 
ap_regkey_value_array_set(ap_regkey_t * key,const char * valuename,int nelts,const char * const * elts,apr_pool_t * pool)525 AP_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 
ap_regkey_value_remove(const ap_regkey_t * key,const char * valuename,apr_pool_t * pool)610 AP_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