Return to browscap.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / standard |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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@zend.com> | ! 16: +----------------------------------------------------------------------+ ! 17: */ ! 18: ! 19: /* $Id: browscap.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 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: /* browser data defined in startup phase, eagerly loaded in MINIT */ ! 38: static browser_data global_bdata = {0}; ! 39: ! 40: /* browser data defined in activation phase, lazily loaded in get_browser. ! 41: * Per request and per thread, if applicable */ ! 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: ! 48: #ifdef ZTS ! 49: #define BROWSCAP_G(v) TSRMG(browscap_globals_id, zend_browscap_globals *, v) ! 50: #else ! 51: #define BROWSCAP_G(v) (browscap_globals.v) ! 52: #endif ! 53: ! 54: #define DEFAULT_SECTION_NAME "Default Browser Capability Settings" ! 55: ! 56: /* OBJECTS_FIXME: This whole extension needs going through. The use of objects looks pretty broken here */ ! 57: ! 58: static void browscap_entry_dtor_request(zval **zvalue) /* {{{ */ ! 59: { ! 60: if (Z_TYPE_PP(zvalue) == IS_ARRAY) { ! 61: zend_hash_destroy(Z_ARRVAL_PP(zvalue)); ! 62: efree(Z_ARRVAL_PP(zvalue)); ! 63: } else if (Z_TYPE_PP(zvalue) == IS_STRING) { ! 64: if (Z_STRVAL_PP(zvalue)) { ! 65: efree(Z_STRVAL_PP(zvalue)); ! 66: } ! 67: } ! 68: efree(*zvalue); ! 69: } ! 70: /* }}} */ ! 71: ! 72: static void browscap_entry_dtor_persistent(zval **zvalue) /* {{{ */ { ! 73: if (Z_TYPE_PP(zvalue) == IS_ARRAY) { ! 74: zend_hash_destroy(Z_ARRVAL_PP(zvalue)); ! 75: free(Z_ARRVAL_PP(zvalue)); ! 76: } else if (Z_TYPE_PP(zvalue) == IS_STRING) { ! 77: if (Z_STRVAL_PP(zvalue)) { ! 78: free(Z_STRVAL_PP(zvalue)); ! 79: } ! 80: } ! 81: free(*zvalue); ! 82: } ! 83: /* }}} */ ! 84: ! 85: static void convert_browscap_pattern(zval *pattern, int persistent) /* {{{ */ ! 86: { ! 87: int i, j=0; ! 88: char *t; ! 89: ! 90: php_strtolower(Z_STRVAL_P(pattern), Z_STRLEN_P(pattern)); ! 91: ! 92: t = (char *) safe_pemalloc(Z_STRLEN_P(pattern), 2, 5, persistent); ! 93: ! 94: t[j++] = '\xA7'; /* section sign */ ! 95: t[j++] = '^'; ! 96: ! 97: for (i=0; i<Z_STRLEN_P(pattern); i++, j++) { ! 98: switch (Z_STRVAL_P(pattern)[i]) { ! 99: case '?': ! 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: case ')': ! 119: t[j++] = '\\'; ! 120: t[j] = ')'; ! 121: break; ! 122: case '\xA7': ! 123: t[j++] = '\\'; ! 124: t[j] = '\xA7'; ! 125: break; ! 126: default: ! 127: t[j] = Z_STRVAL_P(pattern)[i]; ! 128: break; ! 129: } ! 130: } ! 131: ! 132: t[j++] = '$'; ! 133: t[j++] = '\xA7'; ! 134: ! 135: t[j]=0; ! 136: Z_STRVAL_P(pattern) = t; ! 137: Z_STRLEN_P(pattern) = j; ! 138: } ! 139: /* }}} */ ! 140: ! 141: static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */ ! 142: { ! 143: browser_data *bdata = arg; ! 144: int persistent = bdata->htab->persistent; ! 145: ! 146: if (!arg1) { ! 147: return; ! 148: } ! 149: ! 150: switch (callback_type) { ! 151: case ZEND_INI_PARSER_ENTRY: ! 152: if (bdata->current_section && arg2) { ! 153: zval *new_property; ! 154: char *new_key; ! 155: ! 156: /* parent entry can not be same as current section -> causes infinite loop! */ ! 157: if (!strcasecmp(Z_STRVAL_P(arg1), "parent") && ! 158: bdata->current_section_name != NULL && ! 159: !strcasecmp(bdata->current_section_name, Z_STRVAL_P(arg2)) ! 160: ) { ! 161: zend_error(E_CORE_ERROR, "Invalid browscap ini file: " ! 162: "'Parent' value cannot be same as the section name: %s " ! 163: "(in file %s)", bdata->current_section_name, INI_STR("browscap")); ! 164: return; ! 165: } ! 166: ! 167: new_property = (zval *) pemalloc(sizeof(zval), persistent); ! 168: INIT_PZVAL(new_property); ! 169: Z_TYPE_P(new_property) = IS_STRING; ! 170: ! 171: /* Set proper value for true/false settings */ ! 172: if ((Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "on", sizeof("on") - 1)) || ! 173: (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "yes", sizeof("yes") - 1)) || ! 174: (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "true", sizeof("true") - 1)) ! 175: ) { ! 176: Z_STRVAL_P(new_property) = pestrndup("1", 1, persistent); ! 177: Z_STRLEN_P(new_property) = 1; ! 178: } else if ( ! 179: (Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "no", sizeof("no") - 1)) || ! 180: (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "off", sizeof("off") - 1)) || ! 181: (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "none", sizeof("none") - 1)) || ! 182: (Z_STRLEN_P(arg2) == 5 && !strncasecmp(Z_STRVAL_P(arg2), "false", sizeof("false") - 1)) ! 183: ) { ! 184: Z_STRVAL_P(new_property) = pestrndup("", 0, persistent); ! 185: Z_STRLEN_P(new_property) = 0; ! 186: } else { /* Other than true/false setting */ ! 187: Z_STRVAL_P(new_property) = pestrndup(Z_STRVAL_P(arg2), ! 188: Z_STRLEN_P(arg2), persistent); ! 189: Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2); ! 190: } ! 191: new_key = pestrndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), persistent); ! 192: zend_str_tolower(new_key, Z_STRLEN_P(arg1)); ! 193: zend_hash_update(Z_ARRVAL_P(bdata->current_section), new_key, Z_STRLEN_P(arg1) + 1, &new_property, sizeof(zval *), NULL); ! 194: pefree(new_key, persistent); ! 195: } ! 196: break; ! 197: case ZEND_INI_PARSER_SECTION: { ! 198: zval *processed; ! 199: zval *unprocessed; ! 200: HashTable *section_properties; ! 201: ! 202: /*printf("'%s' (%d)\n",$1.value.str.val,$1.value.str.len + 1);*/ ! 203: bdata->current_section = (zval *) pemalloc(sizeof(zval), persistent); ! 204: INIT_PZVAL(bdata->current_section); ! 205: processed = (zval *) pemalloc(sizeof(zval), persistent); ! 206: INIT_PZVAL(processed); ! 207: unprocessed = (zval *) pemalloc(sizeof(zval), persistent); ! 208: INIT_PZVAL(unprocessed); ! 209: ! 210: section_properties = (HashTable *) pemalloc(sizeof(HashTable), persistent); ! 211: zend_hash_init(section_properties, 0, NULL, ! 212: (dtor_func_t) (persistent?browscap_entry_dtor_persistent ! 213: :browscap_entry_dtor_request), ! 214: persistent); ! 215: Z_ARRVAL_P(bdata->current_section) = section_properties; ! 216: Z_TYPE_P(bdata->current_section) = IS_ARRAY; ! 217: if (bdata->current_section_name) { ! 218: pefree(bdata->current_section_name, persistent); ! 219: } ! 220: bdata->current_section_name = pestrndup(Z_STRVAL_P(arg1), ! 221: Z_STRLEN_P(arg1), persistent); ! 222: ! 223: zend_hash_update(bdata->htab, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void *) &bdata->current_section, sizeof(zval *), NULL); ! 224: ! 225: Z_STRVAL_P(processed) = Z_STRVAL_P(arg1); ! 226: Z_STRLEN_P(processed) = Z_STRLEN_P(arg1); ! 227: Z_TYPE_P(processed) = IS_STRING; ! 228: Z_STRVAL_P(unprocessed) = Z_STRVAL_P(arg1); ! 229: Z_STRLEN_P(unprocessed) = Z_STRLEN_P(arg1); ! 230: Z_TYPE_P(unprocessed) = IS_STRING; ! 231: Z_STRVAL_P(unprocessed) = pestrndup(Z_STRVAL_P(unprocessed), Z_STRLEN_P(unprocessed), persistent); ! 232: ! 233: convert_browscap_pattern(processed, persistent); ! 234: zend_hash_update(section_properties, "browser_name_regex", sizeof("browser_name_regex"), (void *) &processed, sizeof(zval *), NULL); ! 235: zend_hash_update(section_properties, "browser_name_pattern", sizeof("browser_name_pattern"), (void *) &unprocessed, sizeof(zval *), NULL); ! 236: } ! 237: break; ! 238: } ! 239: } ! 240: /* }}} */ ! 241: ! 242: static int browscap_read_file(char *filename, browser_data *browdata, int persistent TSRMLS_DC) /* {{{ */ ! 243: { ! 244: zend_file_handle fh = {0}; ! 245: ! 246: if (filename == NULL || filename[0] == '\0') { ! 247: return FAILURE; ! 248: } ! 249: ! 250: browdata->htab = pemalloc(sizeof *browdata->htab, persistent); ! 251: if (browdata->htab == NULL) { ! 252: return FAILURE; ! 253: } ! 254: ! 255: if (zend_hash_init_ex(browdata->htab, 0, NULL, ! 256: (dtor_func_t) (persistent?browscap_entry_dtor_persistent ! 257: :browscap_entry_dtor_request), ! 258: persistent, 0) == FAILURE) { ! 259: pefree(browdata->htab, persistent); ! 260: browdata->htab = NULL; ! 261: return FAILURE; ! 262: } ! 263: ! 264: fh.handle.fp = VCWD_FOPEN(filename, "r"); ! 265: fh.opened_path = NULL; ! 266: fh.free_filename = 0; ! 267: if (!fh.handle.fp) { ! 268: zend_hash_destroy(browdata->htab); ! 269: pefree(browdata->htab, persistent); ! 270: browdata->htab = NULL; ! 271: zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", filename); ! 272: return FAILURE; ! 273: } ! 274: fh.filename = filename; ! 275: Z_TYPE(fh) = ZEND_HANDLE_FP; ! 276: browdata->current_section_name = NULL; ! 277: zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_RAW, ! 278: (zend_ini_parser_cb_t) php_browscap_parser_cb, browdata TSRMLS_CC); ! 279: if (browdata->current_section_name != NULL) { ! 280: pefree(browdata->current_section_name, persistent); ! 281: browdata->current_section_name = NULL; ! 282: } ! 283: ! 284: return SUCCESS; ! 285: } ! 286: /* }}} */ ! 287: ! 288: #ifdef ZTS ! 289: static void browscap_globals_ctor(zend_browscap_globals *browscap_globals TSRMLS_DC) /* {{{ */ ! 290: { ! 291: browscap_globals->activation_bdata.htab = NULL; ! 292: browscap_globals->activation_bdata.current_section = NULL; ! 293: browscap_globals->activation_bdata.current_section_name = NULL; ! 294: browscap_globals->activation_bdata.filename[0] = '\0'; ! 295: } ! 296: /* }}} */ ! 297: #endif ! 298: ! 299: static void browscap_bdata_dtor(browser_data *bdata, int persistent TSRMLS_DC) /* {{{ */ ! 300: { ! 301: if (bdata->htab != NULL) { ! 302: zend_hash_destroy(bdata->htab); ! 303: pefree(bdata->htab, persistent); ! 304: bdata->htab = NULL; ! 305: } ! 306: bdata->filename[0] = '\0'; ! 307: /* current_section_* are only used during parsing */ ! 308: } ! 309: /* }}} */ ! 310: ! 311: /* {{{ PHP_INI_MH ! 312: */ ! 313: PHP_INI_MH(OnChangeBrowscap) ! 314: { ! 315: if (stage == PHP_INI_STAGE_STARTUP) { ! 316: /* value handled in browscap.c's MINIT */ ! 317: return SUCCESS; ! 318: } else if (stage == PHP_INI_STAGE_ACTIVATE) { ! 319: browser_data *bdata = &BROWSCAP_G(activation_bdata); ! 320: if (bdata->filename[0] != '\0') { ! 321: browscap_bdata_dtor(bdata, 0 TSRMLS_CC); ! 322: } ! 323: if (VCWD_REALPATH(new_value, bdata->filename) == NULL) { ! 324: return FAILURE; ! 325: } ! 326: return SUCCESS; ! 327: } ! 328: ! 329: return FAILURE; ! 330: } ! 331: /* }}} */ ! 332: ! 333: PHP_MINIT_FUNCTION(browscap) /* {{{ */ ! 334: { ! 335: char *browscap = INI_STR("browscap"); ! 336: ! 337: #ifdef ZTS ! 338: ts_allocate_id(&browscap_globals_id, sizeof(browser_data), ! 339: (ts_allocate_ctor)browscap_globals_ctor, NULL); ! 340: #endif ! 341: /* ctor call not really needed for non-ZTS */ ! 342: ! 343: if (browscap && browscap[0]) { ! 344: if (browscap_read_file(browscap, &global_bdata, 1 TSRMLS_CC) == FAILURE) { ! 345: return FAILURE; ! 346: } ! 347: } ! 348: ! 349: return SUCCESS; ! 350: } ! 351: /* }}} */ ! 352: ! 353: PHP_RSHUTDOWN_FUNCTION(browscap) /* {{{ */ ! 354: { ! 355: browser_data *bdata = &BROWSCAP_G(activation_bdata); ! 356: if (bdata->filename[0] != '\0') { ! 357: browscap_bdata_dtor(bdata, 0 TSRMLS_CC); ! 358: } ! 359: ! 360: return SUCCESS; ! 361: } ! 362: /* }}} */ ! 363: ! 364: PHP_MSHUTDOWN_FUNCTION(browscap) /* {{{ */ ! 365: { ! 366: browscap_bdata_dtor(&global_bdata, 1 TSRMLS_CC); ! 367: ! 368: return SUCCESS; ! 369: } ! 370: /* }}} */ ! 371: ! 372: static int browser_reg_compare(zval **browser TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */ ! 373: { ! 374: zval **browser_regex, **previous_match; ! 375: pcre *re; ! 376: int re_options; ! 377: pcre_extra *re_extra; ! 378: char *lookup_browser_name = va_arg(args, char *); ! 379: int lookup_browser_length = va_arg(args, int); ! 380: zval **found_browser_entry = va_arg(args, zval **); ! 381: ! 382: /* See if we have an exact match, if so, we're done... */ ! 383: if (*found_browser_entry) { ! 384: if (zend_hash_find(Z_ARRVAL_PP(found_browser_entry), "browser_name_pattern", sizeof("browser_name_pattern"), (void**) &previous_match) == FAILURE) { ! 385: return 0; ! 386: } ! 387: else if (!strcasecmp(Z_STRVAL_PP(previous_match), lookup_browser_name)) { ! 388: return 0; ! 389: } ! 390: } ! 391: ! 392: if (zend_hash_find(Z_ARRVAL_PP(browser), "browser_name_regex", sizeof("browser_name_regex"), (void **) &browser_regex) == FAILURE) { ! 393: return 0; ! 394: } ! 395: ! 396: re = pcre_get_compiled_regex(Z_STRVAL_PP(browser_regex), &re_extra, &re_options TSRMLS_CC); ! 397: if (re == NULL) { ! 398: return 0; ! 399: } ! 400: ! 401: if (pcre_exec(re, re_extra, lookup_browser_name, lookup_browser_length, 0, re_options, NULL, 0) == 0) { ! 402: /* If we've found a possible browser, we need to do a comparison of the ! 403: number of characters changed in the user agent being checked versus ! 404: the previous match found and the current match. */ ! 405: if (*found_browser_entry) { ! 406: int i, prev_len = 0, curr_len = 0, ua_len; ! 407: zval **current_match; ! 408: ! 409: if (zend_hash_find(Z_ARRVAL_PP(browser), "browser_name_pattern", sizeof("browser_name_pattern"), (void**) ¤t_match) == FAILURE) { ! 410: return 0; ! 411: } ! 412: ! 413: ua_len = lookup_browser_length; ! 414: ! 415: for (i = 0; i < Z_STRLEN_PP(previous_match); i++) { ! 416: switch (Z_STRVAL_PP(previous_match)[i]) { ! 417: case '?': ! 418: case '*': ! 419: /* do nothing, ignore these characters in the count */ ! 420: break; ! 421: ! 422: default: ! 423: ++prev_len; ! 424: } ! 425: } ! 426: ! 427: for (i = 0; i < Z_STRLEN_PP(current_match); i++) { ! 428: switch (Z_STRVAL_PP(current_match)[i]) { ! 429: case '?': ! 430: case '*': ! 431: /* do nothing, ignore these characters in the count */ ! 432: break; ! 433: ! 434: default: ! 435: ++curr_len; ! 436: } ! 437: } ! 438: ! 439: /* Pick which browser pattern replaces the least amount of ! 440: characters when compared to the original user agent string... */ ! 441: if (ua_len - prev_len > ua_len - curr_len) { ! 442: *found_browser_entry = *browser; ! 443: } ! 444: } ! 445: else { ! 446: *found_browser_entry = *browser; ! 447: } ! 448: } ! 449: ! 450: return 0; ! 451: } ! 452: /* }}} */ ! 453: ! 454: /* {{{ proto mixed get_browser([string browser_name [, bool return_array]]) ! 455: Get information about the capabilities of a browser. If browser_name is omitted or null, HTTP_USER_AGENT is used. Returns an object by default; if return_array is true, returns an array. */ ! 456: PHP_FUNCTION(get_browser) ! 457: { ! 458: char *agent_name = NULL; ! 459: int agent_name_len = 0; ! 460: zend_bool return_array = 0; ! 461: zval **agent, **z_agent_name, **http_user_agent; ! 462: zval *found_browser_entry, *tmp_copy; ! 463: char *lookup_browser_name; ! 464: browser_data *bdata; ! 465: ! 466: if (BROWSCAP_G(activation_bdata).filename[0] != '\0') { ! 467: bdata = &BROWSCAP_G(activation_bdata); ! 468: if (bdata->htab == NULL) { /* not initialized yet */ ! 469: if (browscap_read_file(bdata->filename, bdata, 0 TSRMLS_CC) == FAILURE) { ! 470: RETURN_FALSE; ! 471: } ! 472: } ! 473: } else { ! 474: if (!global_bdata.htab) { ! 475: php_error_docref(NULL TSRMLS_CC, E_WARNING, "browscap ini directive not set"); ! 476: RETURN_FALSE; ! 477: } ! 478: bdata = &global_bdata; ! 479: } ! 480: ! 481: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &agent_name, &agent_name_len, &return_array) == FAILURE) { ! 482: return; ! 483: } ! 484: ! 485: if (agent_name == NULL) { ! 486: zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC); ! 487: if (!PG(http_globals)[TRACK_VARS_SERVER] || ! 488: zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_USER_AGENT", sizeof("HTTP_USER_AGENT"), (void **) &http_user_agent) == FAILURE ! 489: ) { ! 490: php_error_docref(NULL TSRMLS_CC, E_WARNING, "HTTP_USER_AGENT variable is not set, cannot determine user agent name"); ! 491: RETURN_FALSE; ! 492: } ! 493: agent_name = Z_STRVAL_PP(http_user_agent); ! 494: agent_name_len = Z_STRLEN_PP(http_user_agent); ! 495: } ! 496: ! 497: lookup_browser_name = estrndup(agent_name, agent_name_len); ! 498: php_strtolower(lookup_browser_name, agent_name_len); ! 499: ! 500: if (zend_hash_find(bdata->htab, lookup_browser_name, agent_name_len + 1, (void **) &agent) == FAILURE) { ! 501: found_browser_entry = NULL; ! 502: zend_hash_apply_with_arguments(bdata->htab TSRMLS_CC, (apply_func_args_t) browser_reg_compare, 3, lookup_browser_name, agent_name_len, &found_browser_entry); ! 503: ! 504: if (found_browser_entry) { ! 505: agent = &found_browser_entry; ! 506: } else if (zend_hash_find(bdata->htab, DEFAULT_SECTION_NAME, sizeof(DEFAULT_SECTION_NAME), (void **) &agent) == FAILURE) { ! 507: efree(lookup_browser_name); ! 508: RETURN_FALSE; ! 509: } ! 510: } ! 511: ! 512: if (return_array) { ! 513: array_init(return_value); ! 514: zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *)); ! 515: } ! 516: else { ! 517: object_init(return_value); ! 518: zend_hash_copy(Z_OBJPROP_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *)); ! 519: } ! 520: ! 521: while (zend_hash_find(Z_ARRVAL_PP(agent), "parent", sizeof("parent"), (void **) &z_agent_name) == SUCCESS) { ! 522: if (zend_hash_find(bdata->htab, Z_STRVAL_PP(z_agent_name), Z_STRLEN_PP(z_agent_name) + 1, (void **)&agent) == FAILURE) { ! 523: break; ! 524: } ! 525: ! 526: if (return_array) { ! 527: zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *), 0); ! 528: } ! 529: else { ! 530: zend_hash_merge(Z_OBJPROP_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *), 0); ! 531: } ! 532: } ! 533: ! 534: efree(lookup_browser_name); ! 535: } ! 536: /* }}} */ ! 537: ! 538: /* ! 539: * Local variables: ! 540: * tab-width: 4 ! 541: * c-basic-offset: 4 ! 542: * End: ! 543: * vim600: sw=4 ts=4 fdm=marker ! 544: * vim<600: sw=4 ts=4 ! 545: */