This source file includes following definitions.
- browscap_entry_dtor_request
- browscap_entry_dtor_persistent
- convert_browscap_pattern
- php_browscap_parser_cb
- browscap_read_file
- browscap_globals_ctor
- browscap_bdata_dtor
- PHP_INI_MH
- PHP_MINIT_FUNCTION
- PHP_RSHUTDOWN_FUNCTION
- PHP_MSHUTDOWN_FUNCTION
- browser_reg_compare
- browscap_zval_copy_ctor
- PHP_FUNCTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #include "php.h"
22 #include "php_browscap.h"
23 #include "php_ini.h"
24 #include "php_string.h"
25 #include "ext/pcre/php_pcre.h"
26
27 #include "zend_ini_scanner.h"
28 #include "zend_globals.h"
29
30 typedef struct {
31 HashTable *htab;
32 zval current_section;
33 char *current_section_name;
34 char filename[MAXPATHLEN];
35 } browser_data;
36
37
38 static browser_data global_bdata = {0};
39
40
41
42 ZEND_BEGIN_MODULE_GLOBALS(browscap)
43 browser_data activation_bdata;
44 ZEND_END_MODULE_GLOBALS(browscap)
45
46 ZEND_DECLARE_MODULE_GLOBALS(browscap)
47 #define BROWSCAP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(browscap, v)
48
49 #define DEFAULT_SECTION_NAME "Default Browser Capability Settings"
50
51
52
53 static void browscap_entry_dtor_request(zval *zvalue)
54 {
55 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
56 zend_hash_destroy(Z_ARRVAL_P(zvalue));
57 efree(Z_ARR_P(zvalue));
58 } else if (Z_TYPE_P(zvalue) == IS_STRING) {
59 zend_string_release(Z_STR_P(zvalue));
60 }
61 }
62
63
64 static void browscap_entry_dtor_persistent(zval *zvalue) {
65 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
66 zend_hash_destroy(Z_ARRVAL_P(zvalue));
67 free(Z_ARR_P(zvalue));
68 } else if (Z_TYPE_P(zvalue) == IS_STRING) {
69 zend_string_release(Z_STR_P(zvalue));
70 }
71 }
72
73
74 static void convert_browscap_pattern(zval *pattern, int persistent)
75 {
76 int i, j=0;
77 char *t;
78 zend_string *res;
79 char *lc_pattern;
80
81 res = zend_string_safe_alloc(Z_STRLEN_P(pattern), 2, 4, persistent);
82 t = ZSTR_VAL(res);
83
84 lc_pattern = zend_str_tolower_dup(Z_STRVAL_P(pattern), Z_STRLEN_P(pattern));
85
86 t[j++] = '~';
87 t[j++] = '^';
88
89 for (i=0; i<Z_STRLEN_P(pattern); i++, j++) {
90 switch (lc_pattern[i]) {
91 case '?':
92 t[j] = '.';
93 break;
94 case '*':
95 t[j++] = '.';
96 t[j] = '*';
97 break;
98 case '.':
99 t[j++] = '\\';
100 t[j] = '.';
101 break;
102 case '\\':
103 t[j++] = '\\';
104 t[j] = '\\';
105 break;
106 case '(':
107 t[j++] = '\\';
108 t[j] = '(';
109 break;
110 case ')':
111 t[j++] = '\\';
112 t[j] = ')';
113 break;
114 case '~':
115 t[j++] = '\\';
116 t[j] = '~';
117 break;
118 default:
119 t[j] = lc_pattern[i];
120 break;
121 }
122 }
123
124 t[j++] = '$';
125 t[j++] = '~';
126
127 t[j]=0;
128 ZSTR_LEN(res) = j;
129 Z_STR_P(pattern) = res;
130 efree(lc_pattern);
131 }
132
133
134 static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg)
135 {
136 browser_data *bdata = arg;
137 int persistent = bdata->htab->u.flags & HASH_FLAG_PERSISTENT;
138
139 if (!arg1) {
140 return;
141 }
142
143 switch (callback_type) {
144 case ZEND_INI_PARSER_ENTRY:
145 if (Z_TYPE(bdata->current_section) != IS_UNDEF && arg2) {
146 zval new_property;
147 zend_string *new_key;
148
149
150 if (!strcasecmp(Z_STRVAL_P(arg1), "parent") &&
151 bdata->current_section_name != NULL &&
152 !strcasecmp(bdata->current_section_name, Z_STRVAL_P(arg2))
153 ) {
154 zend_error(E_CORE_ERROR, "Invalid browscap ini file: "
155 "'Parent' value cannot be same as the section name: %s "
156 "(in file %s)", bdata->current_section_name, INI_STR("browscap"));
157 return;
158 }
159
160
161 if ((Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "on", sizeof("on") - 1)) ||
162 (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "yes", sizeof("yes") - 1)) ||
163 (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "true", sizeof("true") - 1))
164 ) {
165 ZVAL_NEW_STR(&new_property, zend_string_init("1", sizeof("1")-1, persistent));
166 } else if (
167 (Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "no", sizeof("no") - 1)) ||
168 (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "off", sizeof("off") - 1)) ||
169 (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "none", sizeof("none") - 1)) ||
170 (Z_STRLEN_P(arg2) == 5 && !strncasecmp(Z_STRVAL_P(arg2), "false", sizeof("false") - 1))
171 ) {
172
173 ZVAL_NEW_STR(&new_property, zend_string_init("", sizeof("")-1, persistent));
174 } else {
175 ZVAL_STR(&new_property, zend_string_dup(Z_STR_P(arg2), persistent));
176 }
177 new_key = zend_string_dup(Z_STR_P(arg1), persistent);
178 zend_str_tolower(ZSTR_VAL(new_key), ZSTR_LEN(new_key));
179 zend_hash_update(Z_ARRVAL(bdata->current_section), new_key, &new_property);
180 zend_string_release(new_key);
181 }
182 break;
183 case ZEND_INI_PARSER_SECTION: {
184 zval processed;
185 zval unprocessed;
186
187
188 if (persistent) {
189 ZVAL_NEW_PERSISTENT_ARR(&bdata->current_section);
190 } else {
191 ZVAL_NEW_ARR(&bdata->current_section);
192 }
193 zend_hash_init(Z_ARRVAL(bdata->current_section), 0, NULL,
194 (dtor_func_t) (persistent?browscap_entry_dtor_persistent
195 :browscap_entry_dtor_request),
196 persistent);
197 if (bdata->current_section_name) {
198 pefree(bdata->current_section_name, persistent);
199 }
200 bdata->current_section_name = pestrndup(Z_STRVAL_P(arg1),
201 Z_STRLEN_P(arg1), persistent);
202
203 zend_hash_update(bdata->htab, Z_STR_P(arg1), &bdata->current_section);
204
205 ZVAL_STR(&processed, Z_STR_P(arg1));
206 ZVAL_STR(&unprocessed, zend_string_dup(Z_STR_P(arg1), persistent));
207
208 convert_browscap_pattern(&processed, persistent);
209 zend_hash_str_update(Z_ARRVAL(bdata->current_section), "browser_name_regex", sizeof("browser_name_regex")-1, &processed);
210 zend_hash_str_update(Z_ARRVAL(bdata->current_section), "browser_name_pattern", sizeof("browser_name_pattern")-1, &unprocessed);
211 }
212 break;
213 }
214 }
215
216
217 static int browscap_read_file(char *filename, browser_data *browdata, int persistent)
218 {
219 zend_file_handle fh = {{0}};
220
221 if (filename == NULL || filename[0] == '\0') {
222 return FAILURE;
223 }
224
225 browdata->htab = pemalloc(sizeof *browdata->htab, persistent);
226 if (browdata->htab == NULL) {
227 return FAILURE;
228 }
229
230 zend_hash_init_ex(browdata->htab, 0, NULL,
231 (dtor_func_t) (persistent?browscap_entry_dtor_persistent
232 :browscap_entry_dtor_request),
233 persistent, 0);
234
235 fh.handle.fp = VCWD_FOPEN(filename, "r");
236 fh.opened_path = NULL;
237 fh.free_filename = 0;
238 if (!fh.handle.fp) {
239 zend_hash_destroy(browdata->htab);
240 pefree(browdata->htab, persistent);
241 browdata->htab = NULL;
242 zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", filename);
243 return FAILURE;
244 }
245 fh.filename = filename;
246 fh.type = ZEND_HANDLE_FP;
247 browdata->current_section_name = NULL;
248 zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_RAW,
249 (zend_ini_parser_cb_t) php_browscap_parser_cb, browdata);
250 if (browdata->current_section_name != NULL) {
251 pefree(browdata->current_section_name, persistent);
252 browdata->current_section_name = NULL;
253 }
254
255 return SUCCESS;
256 }
257
258
259 #ifdef ZTS
260 static void browscap_globals_ctor(zend_browscap_globals *browscap_globals)
261 {
262 browscap_globals->activation_bdata.htab = NULL;
263 ZVAL_UNDEF(&browscap_globals->activation_bdata.current_section);
264 browscap_globals->activation_bdata.current_section_name = NULL;
265 browscap_globals->activation_bdata.filename[0] = '\0';
266 }
267
268 #endif
269
270 static void browscap_bdata_dtor(browser_data *bdata, int persistent)
271 {
272 if (bdata->htab != NULL) {
273 zend_hash_destroy(bdata->htab);
274 pefree(bdata->htab, persistent);
275 bdata->htab = NULL;
276 }
277 bdata->filename[0] = '\0';
278
279 }
280
281
282
283
284 PHP_INI_MH(OnChangeBrowscap)
285 {
286 if (stage == PHP_INI_STAGE_STARTUP) {
287
288 return SUCCESS;
289 } else if (stage == PHP_INI_STAGE_ACTIVATE) {
290 browser_data *bdata = &BROWSCAP_G(activation_bdata);
291 if (bdata->filename[0] != '\0') {
292 browscap_bdata_dtor(bdata, 0);
293 }
294 if (VCWD_REALPATH(ZSTR_VAL(new_value), bdata->filename) == NULL) {
295 return FAILURE;
296 }
297 return SUCCESS;
298 }
299
300 return FAILURE;
301 }
302
303
304 PHP_MINIT_FUNCTION(browscap)
305 {
306 char *browscap = INI_STR("browscap");
307
308 #ifdef ZTS
309 ts_allocate_id(&browscap_globals_id, sizeof(browser_data), (ts_allocate_ctor) browscap_globals_ctor, NULL);
310 #endif
311
312
313 if (browscap && browscap[0]) {
314 if (browscap_read_file(browscap, &global_bdata, 1) == FAILURE) {
315 return FAILURE;
316 }
317 }
318
319 return SUCCESS;
320 }
321
322
323 PHP_RSHUTDOWN_FUNCTION(browscap)
324 {
325 browser_data *bdata = &BROWSCAP_G(activation_bdata);
326 if (bdata->filename[0] != '\0') {
327 browscap_bdata_dtor(bdata, 0);
328 }
329
330 return SUCCESS;
331 }
332
333
334 PHP_MSHUTDOWN_FUNCTION(browscap)
335 {
336 browscap_bdata_dtor(&global_bdata, 1);
337
338 return SUCCESS;
339 }
340
341
342 static int browser_reg_compare(zval *browser, int num_args, va_list args, zend_hash_key *key)
343 {
344 zval *browser_regex, *previous_match;
345 pcre *re;
346 int re_options;
347 pcre_extra *re_extra;
348 char *lookup_browser_name = va_arg(args, char *);
349 int lookup_browser_length = va_arg(args, int);
350 zval *found_browser_entry = va_arg(args, zval *);
351
352
353 if (Z_TYPE_P(found_browser_entry) == IS_ARRAY) {
354 if ((previous_match = zend_hash_str_find(Z_ARRVAL_P(found_browser_entry), "browser_name_pattern", sizeof("browser_name_pattern")-1)) == NULL) {
355 return 0;
356 }
357 else if (!strcasecmp(Z_STRVAL_P(previous_match), lookup_browser_name)) {
358 return 0;
359 }
360 }
361
362 if ((browser_regex = zend_hash_str_find(Z_ARRVAL_P(browser), "browser_name_regex", sizeof("browser_name_regex")-1)) == NULL) {
363 return 0;
364 }
365
366 re = pcre_get_compiled_regex(Z_STR_P(browser_regex), &re_extra, &re_options);
367 if (re == NULL) {
368 return 0;
369 }
370
371 if (pcre_exec(re, re_extra, lookup_browser_name, lookup_browser_length, 0, re_options, NULL, 0) == 0) {
372
373
374
375 if (Z_TYPE_P(found_browser_entry) == IS_ARRAY) {
376 size_t i, prev_len = 0, curr_len = 0;
377 zval *current_match = zend_hash_str_find(Z_ARRVAL_P(browser), "browser_name_pattern", sizeof("browser_name_pattern")-1);
378
379 if (!current_match) {
380 return 0;
381 }
382
383 for (i = 0; i < Z_STRLEN_P(previous_match); i++) {
384 switch (Z_STRVAL_P(previous_match)[i]) {
385 case '?':
386 case '*':
387
388 break;
389
390 default:
391 ++prev_len;
392 }
393 }
394
395 for (i = 0; i < Z_STRLEN_P(current_match); i++) {
396 switch (Z_STRVAL_P(current_match)[i]) {
397 case '?':
398 case '*':
399
400 break;
401
402 default:
403 ++curr_len;
404 }
405 }
406
407
408
409 if (prev_len < curr_len) {
410 ZVAL_COPY_VALUE(found_browser_entry, browser);
411 }
412 }
413 else {
414 ZVAL_COPY_VALUE(found_browser_entry, browser);
415 }
416 }
417
418 return 0;
419 }
420
421
422 static void browscap_zval_copy_ctor(zval *p)
423 {
424 zval_copy_ctor(p);
425 }
426
427
428
429
430 PHP_FUNCTION(get_browser)
431 {
432 char *agent_name = NULL;
433 size_t agent_name_len = 0;
434 zend_bool return_array = 0;
435 zval *agent, *z_agent_name, *http_user_agent;
436 zval found_browser_entry;
437 char *lookup_browser_name;
438 browser_data *bdata;
439
440 if (BROWSCAP_G(activation_bdata).filename[0] != '\0') {
441 bdata = &BROWSCAP_G(activation_bdata);
442 if (bdata->htab == NULL) {
443 if (browscap_read_file(bdata->filename, bdata, 0) == FAILURE) {
444 RETURN_FALSE;
445 }
446 }
447 } else {
448 if (!global_bdata.htab) {
449 php_error_docref(NULL, E_WARNING, "browscap ini directive not set");
450 RETURN_FALSE;
451 }
452 bdata = &global_bdata;
453 }
454
455 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!b", &agent_name, &agent_name_len, &return_array) == FAILURE) {
456 return;
457 }
458
459 if (agent_name == NULL) {
460 if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY || zend_is_auto_global_str(ZEND_STRL("_SERVER"))) &&
461 (http_user_agent = zend_hash_str_find(Z_ARRVAL_P(&PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_USER_AGENT", sizeof("HTTP_USER_AGENT")-1)) == NULL
462 ) {
463 php_error_docref(NULL, E_WARNING, "HTTP_USER_AGENT variable is not set, cannot determine user agent name");
464 RETURN_FALSE;
465 }
466 agent_name = Z_STRVAL_P(http_user_agent);
467 agent_name_len = Z_STRLEN_P(http_user_agent);
468 }
469
470 lookup_browser_name = estrndup(agent_name, agent_name_len);
471 php_strtolower(lookup_browser_name, agent_name_len);
472
473 if ((agent = zend_hash_str_find(bdata->htab, lookup_browser_name, agent_name_len)) == NULL) {
474 ZVAL_UNDEF(&found_browser_entry);
475 zend_hash_apply_with_arguments(bdata->htab, browser_reg_compare, 3, lookup_browser_name, agent_name_len, &found_browser_entry);
476
477 if (Z_TYPE(found_browser_entry) != IS_UNDEF) {
478 agent = &found_browser_entry;
479 } else if ((agent = zend_hash_str_find(bdata->htab, DEFAULT_SECTION_NAME, sizeof(DEFAULT_SECTION_NAME)-1)) == NULL) {
480 efree(lookup_browser_name);
481 RETURN_FALSE;
482 }
483 }
484
485 if (return_array) {
486 RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(agent)));
487 }
488 else {
489 object_init(return_value);
490 zend_hash_copy(Z_OBJPROP_P(return_value), Z_ARRVAL_P(agent), (copy_ctor_func_t) browscap_zval_copy_ctor);
491 }
492
493 while ((z_agent_name = zend_hash_str_find(Z_ARRVAL_P(agent), "parent", sizeof("parent")-1)) != NULL) {
494 if ((agent = zend_hash_find(bdata->htab, Z_STR_P(z_agent_name))) == NULL) {
495 break;
496 }
497
498 if (return_array) {
499 zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(agent), (copy_ctor_func_t) browscap_zval_copy_ctor, 0);
500 }
501 else {
502 zend_hash_merge(Z_OBJPROP_P(return_value), Z_ARRVAL_P(agent), (copy_ctor_func_t) browscap_zval_copy_ctor, 0);
503 }
504 }
505
506 efree(lookup_browser_name);
507 }
508
509
510
511
512
513
514
515
516
517