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