Annotation of embedaddon/php/ext/standard/browscap.c, revision 1.1

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**) &current_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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>