static int browser_reg_compare(zval *browser, int num_args, va_list args, zend_hash_key *key) /* {{{ */ { zval *browser_regex, *previous_match; pcre *re; int re_options; pcre_extra *re_extra; char *lookup_browser_name = va_arg(args, char *); int lookup_browser_length = va_arg(args, int); zval *found_browser_entry = va_arg(args, zval *); /* See if we have an exact match, if so, we're done... */ if (Z_TYPE_P(found_browser_entry) == IS_ARRAY) { if ((previous_match = zend_hash_str_find(Z_ARRVAL_P(found_browser_entry), "browser_name_pattern", sizeof("browser_name_pattern")-1)) == NULL) { return 0; } else if (!strcasecmp(Z_STRVAL_P(previous_match), lookup_browser_name)) { return 0; } } if ((browser_regex = zend_hash_str_find(Z_ARRVAL_P(browser), "browser_name_regex", sizeof("browser_name_regex")-1)) == NULL) { return 0; } re = pcre_get_compiled_regex(Z_STR_P(browser_regex), &re_extra, &re_options); if (re == NULL) { return 0; } if (pcre_exec(re, re_extra, lookup_browser_name, lookup_browser_length, 0, re_options, NULL, 0) == 0) { /* If we've found a possible browser, we need to do a comparison of the number of characters changed in the user agent being checked versus the previous match found and the current match. */ if (Z_TYPE_P(found_browser_entry) == IS_ARRAY) { size_t i, prev_len = 0, curr_len = 0; zval *current_match = zend_hash_str_find(Z_ARRVAL_P(browser), "browser_name_pattern", sizeof("browser_name_pattern")-1); if (!current_match) { return 0; } for (i = 0; i < Z_STRLEN_P(previous_match); i++) { switch (Z_STRVAL_P(previous_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++prev_len; } } for (i = 0; i < Z_STRLEN_P(current_match); i++) { switch (Z_STRVAL_P(current_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++curr_len; } } /* Pick which browser pattern replaces the least amount of characters when compared to the original user agent string... */ if (prev_len < curr_len) { ZVAL_COPY_VALUE(found_browser_entry, browser); } } else { ZVAL_COPY_VALUE(found_browser_entry, browser); } } return 0; }
static int browser_reg_compare( zval *entry_zv, int num_args, va_list args, zend_hash_key *key) /* {{{ */ { browscap_entry *entry = Z_PTR_P(entry_zv); zend_string *agent_name = va_arg(args, zend_string *); browscap_entry **found_entry_ptr = va_arg(args, browscap_entry **); browscap_entry *found_entry = *found_entry_ptr; ALLOCA_FLAG(use_heap); zend_string *pattern_lc, *regex; const char *cur; int i; pcre *re; int re_options; pcre_extra *re_extra; /* Agent name too short */ if (ZSTR_LEN(agent_name) < browscap_get_minimum_length(entry)) { return 0; } /* Quickly discard patterns where the prefix doesn't match. */ if (zend_binary_strcasecmp( ZSTR_VAL(agent_name), entry->prefix_len, ZSTR_VAL(entry->pattern), entry->prefix_len) != 0) { return 0; } /* Lowercase the pattern, the agent name is already lowercase */ ZSTR_ALLOCA_ALLOC(pattern_lc, ZSTR_LEN(entry->pattern), use_heap); zend_str_tolower_copy(ZSTR_VAL(pattern_lc), ZSTR_VAL(entry->pattern), ZSTR_LEN(entry->pattern)); /* Check if the agent contains the "contains" portions */ cur = ZSTR_VAL(agent_name) + entry->prefix_len; for (i = 0; i < BROWSCAP_NUM_CONTAINS; i++) { if (entry->contains_len[i] != 0) { cur = zend_memnstr(cur, ZSTR_VAL(pattern_lc) + entry->contains_start[i], entry->contains_len[i], ZSTR_VAL(agent_name) + ZSTR_LEN(agent_name)); if (!cur) { ZSTR_ALLOCA_FREE(pattern_lc, use_heap); return 0; } cur += entry->contains_len[i]; } } /* See if we have an exact match, if so, we're done... */ if (zend_string_equals(agent_name, pattern_lc)) { *found_entry_ptr = entry; ZSTR_ALLOCA_FREE(pattern_lc, use_heap); return ZEND_HASH_APPLY_STOP; } regex = browscap_convert_pattern(entry->pattern, 0); re = pcre_get_compiled_regex(regex, &re_extra, &re_options); if (re == NULL) { ZSTR_ALLOCA_FREE(pattern_lc, use_heap); zend_string_release(regex); return 0; } if (pcre_exec(re, re_extra, ZSTR_VAL(agent_name), ZSTR_LEN(agent_name), 0, re_options, NULL, 0) == 0) { /* If we've found a possible browser, we need to do a comparison of the number of characters changed in the user agent being checked versus the previous match found and the current match. */ if (found_entry) { size_t i, prev_len = 0, curr_len = 0; zend_string *previous_match = found_entry->pattern; zend_string *current_match = entry->pattern; for (i = 0; i < ZSTR_LEN(previous_match); i++) { switch (ZSTR_VAL(previous_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++prev_len; } } for (i = 0; i < ZSTR_LEN(current_match); i++) { switch (ZSTR_VAL(current_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++curr_len; } } /* Pick which browser pattern replaces the least amount of characters when compared to the original user agent string... */ if (prev_len < curr_len) { *found_entry_ptr = entry; } } else { *found_entry_ptr = entry; } } ZSTR_ALLOCA_FREE(pattern_lc, use_heap); zend_string_release(regex); return 0; }