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