xref: /PHP-8.4/win32/registry.c (revision 90b7bde6)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Zeev Suraski <zeev@php.net>                                  |
14    +----------------------------------------------------------------------+
15  */
16 
17 #include "php.h"
18 #include "php_ini.h"
19 #include "php_win32_globals.h"
20 
21 #define PHP_REGISTRY_KEY              "SOFTWARE\\PHP"
22 
23 #define PHP_VER1(V1)                  #V1
24 #define PHP_VER2(V1,V2)               #V1"."#V2
25 #define PHP_VER3(V1,V2,V3)            #V1"."#V2"."#V3
26 
27 #define PHP_REGISTRY_KEYV(VER)        PHP_REGISTRY_KEY"\\"VER
28 #define PHP_REGISTRY_KEY1(V1)         PHP_REGISTRY_KEY"\\"PHP_VER1(V1)
29 #define PHP_REGISTRY_KEY2(V1,V2)      PHP_REGISTRY_KEY"\\"PHP_VER2(V1,V2)
30 #define PHP_REGISTRY_KEY3(V1,V2,V3)   PHP_REGISTRY_KEY"\\"PHP_VER3(V1,V2,V3)
31 
32 static const char* registry_keys[] = {
33 	PHP_REGISTRY_KEYV(PHP_VERSION),
34 	PHP_REGISTRY_KEY3(PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION),
35 	PHP_REGISTRY_KEY2(PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
36 	PHP_REGISTRY_KEY1(PHP_MAJOR_VERSION),
37 	PHP_REGISTRY_KEY,
38 	NULL
39 };
40 
OpenPhpRegistryKey(char * sub_key,HKEY * hKey)41 static int OpenPhpRegistryKey(char* sub_key, HKEY *hKey)
42 {/*{{{*/
43 	const char **key_name = registry_keys;
44 
45 	if (sub_key) {
46 		size_t main_key_len;
47 		size_t sub_key_len = strlen(sub_key);
48 		char *reg_key;
49 
50 		while (*key_name) {
51 			LONG ret;
52 
53 			main_key_len = strlen(*key_name);
54 			reg_key = emalloc(main_key_len + sub_key_len + 1);
55 			memcpy(reg_key, *key_name, main_key_len);
56 			memcpy(reg_key + main_key_len, sub_key, sub_key_len + 1);
57 			ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_READ, hKey);
58 			efree(reg_key);
59 
60 			if (ret == ERROR_SUCCESS) {
61 				return 1;
62 			}
63 			++key_name;
64 		}
65 	} else {
66 		while (*key_name) {
67 			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, *key_name, 0, KEY_READ, hKey) == ERROR_SUCCESS) {
68 				return 1;
69 			}
70 			++key_name;
71 		}
72 	}
73 	return 0;
74 }/*}}}*/
75 
LoadDirectory(HashTable * directories,HKEY key,char * path,int path_len,HashTable * parent_ht)76 static int LoadDirectory(HashTable *directories, HKEY key, char *path, int path_len, HashTable *parent_ht)
77 {/*{{{*/
78 	DWORD keys, values, max_key, max_name, max_value;
79 	int ret = 0;
80 	HashTable *ht = NULL;
81 
82 	if (RegQueryInfoKey(key, NULL, NULL, NULL, &keys, &max_key, NULL, &values, &max_name, &max_value, NULL, NULL) == ERROR_SUCCESS) {
83 
84 		if (values) {
85 			DWORD i;
86 			char *name = (char*)emalloc(max_name+1);
87 			char *value = (char*)emalloc(max_value+1);
88 			DWORD name_len, type, value_len;
89 
90 			for (i = 0; i < values; i++) {
91 				name_len = max_name+1;
92 				value_len = max_value+1;
93 
94 				memset(name, '\0', max_name+1);
95 				memset(value, '\0', max_value+1);
96 
97 				if (RegEnumValue(key, i, name, &name_len, NULL, &type, value, &value_len) == ERROR_SUCCESS) {
98 					if ((type == REG_SZ) || (type == REG_EXPAND_SZ)) {
99 						zval data;
100 
101 						if (!ht) {
102 							ht = (HashTable*)malloc(sizeof(HashTable));
103 							if (!ht) {
104 								return ret;
105 							}
106 							zend_hash_init(ht, 0, NULL, ZVAL_INTERNAL_PTR_DTOR, 1);
107 						}
108 						ZVAL_PSTRINGL(&data, value, value_len-1);
109 						zend_hash_str_update(ht, name, name_len, &data);
110 					}
111 				}
112 			}
113 			if (ht) {
114 				if (parent_ht) {
115 					zend_string *index;
116 					zend_ulong num;
117 					zval *tmpdata;
118 
119 					ZEND_HASH_MAP_FOREACH_KEY_VAL(parent_ht, num, index, tmpdata) {
120 						zend_hash_add(ht, index, tmpdata);
121 					} ZEND_HASH_FOREACH_END();
122 				}
123 				zend_hash_str_update_mem(directories, path, path_len, ht, sizeof(HashTable));
124 				ret = 1;
125 			}
126 
127 			efree(name);
128 			efree(value);
129 		}
130 
131 		if (ht == NULL) {
132 			ht = parent_ht;
133 		}
134 
135 		if (keys) {
136 			DWORD i;
137 			char *name = (char*)emalloc(max_key+1);
138 			char *new_path = (char*)emalloc(path_len+max_key+2);
139 			DWORD name_len;
140 			FILETIME t;
141 			HKEY subkey;
142 
143 			for (i = 0; i < keys; i++) {
144 				name_len = max_key+1;
145 				if (RegEnumKeyEx(key, i, name, &name_len, NULL, NULL, NULL, &t) == ERROR_SUCCESS) {
146 					if (RegOpenKeyEx(key, name, 0, KEY_READ, &subkey) == ERROR_SUCCESS) {
147 						if (path_len) {
148 							memcpy(new_path, path, path_len);
149 							new_path[path_len] = '/';
150 							memcpy(new_path+path_len+1, name, name_len+1);
151 							zend_str_tolower(new_path, path_len+name_len+1);
152 							name_len += path_len+1;
153 						} else {
154 							memcpy(new_path, name, name_len+1);
155 							zend_str_tolower(new_path, name_len);
156 						}
157 						if (LoadDirectory(directories, subkey, new_path, name_len, ht)) {
158 							ret = 1;
159 						}
160 						RegCloseKey(subkey);
161 					}
162 				}
163 			}
164 			efree(new_path);
165 			efree(name);
166 		}
167 	}
168 	return ret;
169 }/*}}}*/
170 
delete_internal_hashtable(zval * zv)171 static void delete_internal_hashtable(zval *zv)
172 {/*{{{*/
173 	HashTable *ht = (HashTable *)Z_PTR_P(zv);
174 	zend_hash_destroy(ht);
175 	free(ht);
176 }/*}}}*/
177 
178 #define RegNotifyFlags (REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET)
179 
UpdateIniFromRegistry(char * path)180 void UpdateIniFromRegistry(char *path)
181 {/*{{{*/
182 	char *p, *orig_path;
183 	int path_len;
184 
185 	if(!path) {
186 		return;
187 	}
188 
189 	if (!PW32G(registry_directories)) {
190 		PW32G(registry_directories) = (HashTable*)malloc(sizeof(HashTable));
191 		if (!PW32G(registry_directories)) {
192 			return;
193 		}
194 		zend_hash_init(PW32G(registry_directories), 0, NULL, delete_internal_hashtable, 1);
195 		if (!OpenPhpRegistryKey("\\Per Directory Values", &PW32G(registry_key))) {
196 			PW32G(registry_key) = NULL;
197 			return;
198 		}
199 		PW32G(registry_event) = CreateEvent(NULL, TRUE, FALSE, NULL);
200 		if (PW32G(registry_event)) {
201 			RegNotifyChangeKeyValue(PW32G(registry_key), TRUE, RegNotifyFlags, PW32G(registry_event), TRUE);
202 		}
203 		if (!LoadDirectory(PW32G(registry_directories), PW32G(registry_key), "", 0, NULL)) {
204 			return;
205 		}
206 	} else if (PW32G(registry_event) && WaitForSingleObject(PW32G(registry_event), 0) == WAIT_OBJECT_0) {
207 		RegNotifyChangeKeyValue(PW32G(registry_key), TRUE, RegNotifyFlags, PW32G(registry_event), TRUE);
208 		zend_hash_clean(PW32G(registry_directories));
209 		if (!LoadDirectory(PW32G(registry_directories), PW32G(registry_key), "", 0, NULL)) {
210 			return;
211 		}
212 	} else if (zend_hash_num_elements(PW32G(registry_directories)) == 0) {
213 		return;
214 	}
215 
216 	orig_path = path = estrdup(path);
217 
218 	/* Get rid of C:, if exists */
219 	p = strchr(path, ':');
220 	if (p) {
221 		*p = path[0];	/* replace the colon with the drive letter */
222 		path = p;		/* make path point to the drive letter */
223 	} else {
224 		if (path[0] != '\\' && path[0] != '/') {
225 			char tmp_buf[MAXPATHLEN], *cwd;
226 			char drive_letter;
227 
228 			/* get current working directory and prepend it to the path */
229 			if (!VCWD_GETCWD(tmp_buf, MAXPATHLEN)) {
230 				efree(orig_path);
231 				return;
232 			}
233 			cwd = strchr(tmp_buf, ':');
234 			if (!cwd) {
235 				drive_letter = 'C';
236 				cwd = tmp_buf;
237 			} else {
238 				drive_letter = tmp_buf[0];
239 				cwd++;
240 			}
241 			while (*cwd == '\\' || *cwd == '/') {
242 				cwd++;
243 			}
244 			spprintf(&path, 0, "%c\\%s\\%s", drive_letter, cwd, orig_path);
245 			efree(orig_path);
246 			orig_path = path;
247 		}
248 	}
249 
250 	path_len = 0;
251 	while (path[path_len] != 0) {
252 		if (path[path_len] == '\\') {
253 			path[path_len] = '/';
254 		}
255 		path_len++;
256 	}
257 	zend_str_tolower(path, path_len);
258 
259 	while (path_len > 0) {
260 		HashTable *ht = (HashTable *)zend_hash_str_find_ptr(PW32G(registry_directories), path, path_len);
261 
262 		if (ht != NULL) {
263 			zend_string *index;
264 			zval *data;
265 
266 			ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht, index, data) {
267 				zend_alter_ini_entry(index, Z_STR_P(data), PHP_INI_USER, PHP_INI_STAGE_ACTIVATE);
268 			} ZEND_HASH_FOREACH_END();
269 		}
270 
271 		do {
272 			path_len--;
273 		} while (path_len > 0 && path[path_len] != '/');
274 		path[path_len] = 0;
275 	}
276 
277 	efree(orig_path);
278 }/*}}}*/
279 
280 #define PHPRC_REGISTRY_NAME "IniFilePath"
281 
GetIniPathFromRegistry()282 char *GetIniPathFromRegistry()
283 {/*{{{*/
284 	char *reg_location = NULL;
285 	HKEY hKey;
286 
287 	if (OpenPhpRegistryKey(NULL, &hKey)) {
288 		DWORD buflen = MAXPATHLEN;
289 		reg_location = emalloc(MAXPATHLEN+1);
290 		if(RegQueryValueEx(hKey, PHPRC_REGISTRY_NAME, 0, NULL, reg_location, &buflen) != ERROR_SUCCESS) {
291 			RegCloseKey(hKey);
292 			efree(reg_location);
293 			reg_location = NULL;
294 			return reg_location;
295 		}
296 		RegCloseKey(hKey);
297 	}
298 	return reg_location;
299 }/*}}}*/
300