1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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,v 1.85.2.2.2.3 2007/03/07 00:52:40 iliaa Exp $ */
20 :
21 : #include "php.h"
22 : #include "php_regex.h"
23 : #include "php_browscap.h"
24 : #include "php_ini.h"
25 : #include "php_string.h"
26 :
27 : #include "zend_globals.h"
28 :
29 : static HashTable browser_hash;
30 : static zval *current_section;
31 :
32 : #define DEFAULT_SECTION_NAME "Default Browser Capability Settings"
33 :
34 : /* OBJECTS_FIXME: This whole extension needs going through. The use of objects looks pretty broken here */
35 :
36 : static void browscap_entry_dtor(zval **zvalue)
37 0 : {
38 0 : if (Z_TYPE_PP(zvalue) == IS_ARRAY) {
39 0 : zend_hash_destroy(Z_ARRVAL_PP(zvalue));
40 0 : free(Z_ARRVAL_PP(zvalue));
41 0 : } else if (Z_TYPE_PP(zvalue) == IS_STRING) {
42 0 : if (Z_STRVAL_PP(zvalue)) {
43 0 : free(Z_STRVAL_PP(zvalue));
44 : }
45 : }
46 0 : free(*zvalue);
47 0 : }
48 :
49 : /* {{{ convert_browscap_pattern
50 : */
51 : static void convert_browscap_pattern(zval *pattern)
52 0 : {
53 : register int i, j;
54 : char *t;
55 :
56 0 : php_strtolower(Z_STRVAL_P(pattern), Z_STRLEN_P(pattern));
57 :
58 0 : t = (char *) safe_pemalloc(Z_STRLEN_P(pattern), 2, 3, 1);
59 :
60 0 : t[0] = '^';
61 :
62 0 : for (i=0, j=1; i<Z_STRLEN_P(pattern); i++, j++) {
63 0 : switch (Z_STRVAL_P(pattern)[i]) {
64 : case '?':
65 0 : t[j] = '.';
66 0 : break;
67 : case '*':
68 0 : t[j++] = '.';
69 0 : t[j] = '*';
70 0 : break;
71 : case '.':
72 0 : t[j++] = '\\';
73 0 : t[j] = '.';
74 0 : break;
75 : default:
76 0 : t[j] = Z_STRVAL_P(pattern)[i];
77 : break;
78 : }
79 : }
80 :
81 0 : t[j++] = '$';
82 :
83 0 : t[j]=0;
84 0 : Z_STRVAL_P(pattern) = t;
85 0 : Z_STRLEN_P(pattern) = j;
86 0 : }
87 : /* }}} */
88 :
89 : /* {{{ php_browscap_parser_cb
90 : */
91 : static void php_browscap_parser_cb(zval *arg1, zval *arg2, int callback_type, void *arg)
92 0 : {
93 0 : if (!arg1) {
94 0 : return;
95 : }
96 :
97 0 : switch (callback_type) {
98 : case ZEND_INI_PARSER_ENTRY:
99 0 : if (current_section && arg2) {
100 : zval *new_property;
101 : char *new_key;
102 :
103 0 : new_property = (zval *) pemalloc(sizeof(zval), 1);
104 0 : INIT_PZVAL(new_property);
105 0 : Z_STRVAL_P(new_property) = zend_strndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
106 0 : Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2);
107 0 : Z_TYPE_P(new_property) = IS_STRING;
108 :
109 0 : new_key = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1));
110 0 : zend_str_tolower(new_key, Z_STRLEN_P(arg1));
111 0 : zend_hash_update(Z_ARRVAL_P(current_section), new_key, Z_STRLEN_P(arg1)+1, &new_property, sizeof(zval *), NULL);
112 0 : free(new_key);
113 : }
114 0 : break;
115 : case ZEND_INI_PARSER_SECTION: {
116 : zval *processed;
117 : zval *unprocessed;
118 : HashTable *section_properties;
119 :
120 : /*printf("'%s' (%d)\n",$1.value.str.val,$1.value.str.len+1);*/
121 0 : current_section = (zval *) pemalloc(sizeof(zval), 1);
122 0 : INIT_PZVAL(current_section);
123 0 : processed = (zval *) pemalloc(sizeof(zval), 1);
124 0 : INIT_PZVAL(processed);
125 0 : unprocessed = (zval *) pemalloc(sizeof(zval), 1);
126 0 : INIT_PZVAL(unprocessed);
127 :
128 0 : section_properties = (HashTable *) pemalloc(sizeof(HashTable), 1);
129 0 : zend_hash_init(section_properties, 0, NULL, (dtor_func_t) browscap_entry_dtor, 1);
130 0 : current_section->value.ht = section_properties;
131 0 : current_section->type = IS_ARRAY;
132 0 : zend_hash_update(&browser_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)+1, (void *) ¤t_section, sizeof(zval *), NULL);
133 :
134 0 : Z_STRVAL_P(processed) = Z_STRVAL_P(arg1);
135 0 : Z_STRLEN_P(processed) = Z_STRLEN_P(arg1);
136 0 : Z_TYPE_P(processed) = IS_STRING;
137 0 : Z_STRVAL_P(unprocessed) = Z_STRVAL_P(arg1);
138 0 : Z_STRLEN_P(unprocessed) = Z_STRLEN_P(arg1);
139 0 : Z_TYPE_P(unprocessed) = IS_STRING;
140 0 : Z_STRVAL_P(unprocessed) = zend_strndup(Z_STRVAL_P(unprocessed), Z_STRLEN_P(unprocessed));
141 :
142 0 : convert_browscap_pattern(processed);
143 0 : zend_hash_update(section_properties, "browser_name_regex", sizeof("browser_name_regex"), (void *) &processed, sizeof(zval *), NULL);
144 0 : zend_hash_update(section_properties, "browser_name_pattern", sizeof("browser_name_pattern"), (void *) &unprocessed, sizeof(zval *), NULL);
145 : }
146 : break;
147 : }
148 : }
149 : /* }}} */
150 :
151 : /* {{{ PHP_MINIT_FUNCTION
152 : */
153 : PHP_MINIT_FUNCTION(browscap)
154 220 : {
155 220 : char *browscap = INI_STR("browscap");
156 :
157 220 : if (browscap && browscap[0]) {
158 : zend_file_handle fh;
159 0 : memset(&fh, 0, sizeof(fh));
160 :
161 0 : if (zend_hash_init_ex(&browser_hash, 0, NULL, (dtor_func_t) browscap_entry_dtor, 1, 0)==FAILURE) {
162 0 : return FAILURE;
163 : }
164 :
165 0 : fh.handle.fp = VCWD_FOPEN(browscap, "r");
166 0 : fh.opened_path = NULL;
167 0 : fh.free_filename = 0;
168 0 : if (!fh.handle.fp) {
169 0 : zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", browscap);
170 0 : return FAILURE;
171 : }
172 0 : fh.filename = browscap;
173 0 : Z_TYPE(fh) = ZEND_HANDLE_FP;
174 0 : zend_parse_ini_file(&fh, 1, (zend_ini_parser_cb_t) php_browscap_parser_cb, &browser_hash);
175 : }
176 :
177 220 : return SUCCESS;
178 : }
179 : /* }}} */
180 :
181 : /* {{{ PHP_MSHUTDOWN_FUNCTION
182 : */
183 : PHP_MSHUTDOWN_FUNCTION(browscap)
184 219 : {
185 219 : char *browscap = INI_STR("browscap");
186 219 : if (browscap && browscap[0]) {
187 0 : zend_hash_destroy(&browser_hash);
188 : }
189 219 : return SUCCESS;
190 : }
191 : /* }}} */
192 :
193 :
194 : /* {{{ browser_reg_compare
195 : */
196 : static int browser_reg_compare(zval **browser, int num_args, va_list args, zend_hash_key *key)
197 0 : {
198 : zval **browser_regex, **previous_match;
199 : regex_t r;
200 0 : char *lookup_browser_name = va_arg(args, char *);
201 0 : zval **found_browser_entry = va_arg(args, zval **);
202 :
203 : /* See if we have an exact match, if so, we're done... */
204 0 : if (*found_browser_entry) {
205 0 : if (zend_hash_find(Z_ARRVAL_PP(found_browser_entry), "browser_name_pattern", sizeof("browser_name_pattern"), (void**) &previous_match) == FAILURE) {
206 0 : return 0;
207 : }
208 0 : else if (!strcasecmp(Z_STRVAL_PP(previous_match), lookup_browser_name)) {
209 0 : return 0;
210 : }
211 : }
212 :
213 :
214 0 : if (zend_hash_find(Z_ARRVAL_PP(browser), "browser_name_regex", sizeof("browser_name_regex"), (void **) &browser_regex) == FAILURE) {
215 0 : return 0;
216 : }
217 :
218 0 : if (regcomp(&r, Z_STRVAL_PP(browser_regex), REG_NOSUB)!=0) {
219 0 : return 0;
220 : }
221 0 : if (regexec(&r, lookup_browser_name, 0, NULL, 0)==0) {
222 : /* If we've found a possible browser, we need to do a comparison of the
223 : number of characters changed in the user agent being checked versus
224 : the previous match found and the current match. */
225 0 : if (*found_browser_entry) {
226 0 : int i, prev_len = 0, curr_len = 0, ua_len;
227 : zval **current_match;
228 :
229 0 : if (zend_hash_find(Z_ARRVAL_PP(browser), "browser_name_pattern", sizeof("browser_name_pattern"), (void**) ¤t_match) == FAILURE) {
230 0 : regfree(&r);
231 0 : return 0;
232 : }
233 :
234 0 : ua_len = strlen(lookup_browser_name);
235 :
236 0 : for (i = 0; i < Z_STRLEN_PP(previous_match); i++) {
237 0 : switch (Z_STRVAL_PP(previous_match)[i]) {
238 : case '?':
239 : case '*':
240 : /* do nothing, ignore these characters in the count */
241 0 : break;
242 :
243 : default:
244 0 : ++prev_len;
245 : }
246 : }
247 :
248 0 : for (i = 0; i < Z_STRLEN_PP(current_match); i++) {
249 0 : switch (Z_STRVAL_PP(current_match)[i]) {
250 : case '?':
251 : case '*':
252 : /* do nothing, ignore these characters in the count */
253 0 : break;
254 :
255 : default:
256 0 : ++curr_len;
257 : }
258 : }
259 :
260 :
261 : /* Pick which browser pattern replaces the least amount of
262 : characters when compared to the original user agent string... */
263 0 : if (ua_len - prev_len > ua_len - curr_len) {
264 0 : *found_browser_entry = *browser;
265 : }
266 : }
267 : else {
268 0 : *found_browser_entry = *browser;
269 : }
270 : }
271 :
272 : if (&r) {
273 0 : regfree(&r);
274 : }
275 :
276 0 : return 0;
277 : }
278 : /* }}} */
279 :
280 : /* {{{ proto mixed get_browser([string browser_name [, bool return_array]])
281 : Get information about the capabilities of a browser. If browser_name is omitted
282 : or null, HTTP_USER_AGENT is used. Returns an object by default; if return_array
283 : is true, returns an array. */
284 : PHP_FUNCTION(get_browser)
285 0 : {
286 0 : zval **agent_name = NULL, **agent, **retarr;
287 : zval *found_browser_entry, *tmp_copy;
288 : char *lookup_browser_name;
289 0 : zend_bool return_array = 0;
290 0 : char *browscap = INI_STR("browscap");
291 :
292 0 : if (!browscap || !browscap[0]) {
293 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "browscap ini directive not set.");
294 0 : RETURN_FALSE;
295 : }
296 :
297 0 : if (ZEND_NUM_ARGS() > 2 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &agent_name, &retarr) == FAILURE) {
298 0 : ZEND_WRONG_PARAM_COUNT();
299 : }
300 :
301 0 : if (agent_name == NULL || Z_TYPE_PP(agent_name) == IS_NULL) {
302 0 : zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
303 0 : if (!PG(http_globals)[TRACK_VARS_SERVER]
304 : || zend_hash_find(PG(http_globals)[TRACK_VARS_SERVER]->value.ht, "HTTP_USER_AGENT", sizeof("HTTP_USER_AGENT"), (void **) &agent_name)==FAILURE) {
305 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "HTTP_USER_AGENT variable is not set, cannot determine user agent name");
306 0 : RETURN_FALSE;
307 : }
308 : }
309 :
310 0 : convert_to_string_ex(agent_name);
311 0 : lookup_browser_name = estrndup(Z_STRVAL_PP(agent_name), Z_STRLEN_PP(agent_name));
312 0 : php_strtolower(lookup_browser_name, strlen(lookup_browser_name));
313 :
314 0 : if (ZEND_NUM_ARGS() == 2) {
315 0 : convert_to_boolean_ex(retarr);
316 0 : return_array = Z_BVAL_PP(retarr);
317 : }
318 :
319 0 : if (zend_hash_find(&browser_hash, lookup_browser_name, strlen(lookup_browser_name)+1, (void **) &agent)==FAILURE) {
320 0 : found_browser_entry = NULL;
321 0 : zend_hash_apply_with_arguments(&browser_hash, (apply_func_args_t) browser_reg_compare, 2, lookup_browser_name, &found_browser_entry);
322 :
323 0 : if (found_browser_entry) {
324 0 : agent = &found_browser_entry;
325 0 : } else if (zend_hash_find(&browser_hash, DEFAULT_SECTION_NAME, sizeof(DEFAULT_SECTION_NAME), (void **) &agent)==FAILURE) {
326 0 : efree(lookup_browser_name);
327 0 : RETURN_FALSE;
328 : }
329 : }
330 :
331 0 : if (return_array) {
332 0 : array_init(return_value);
333 0 : zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *));
334 : }
335 : else {
336 0 : object_init(return_value);
337 0 : zend_hash_copy(Z_OBJPROP_P(return_value), Z_ARRVAL_PP(agent), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_copy, sizeof(zval *));
338 : }
339 :
340 0 : while (zend_hash_find(Z_ARRVAL_PP(agent), "parent", sizeof("parent"), (void **) &agent_name)==SUCCESS) {
341 0 : if (zend_hash_find(&browser_hash, Z_STRVAL_PP(agent_name), Z_STRLEN_PP(agent_name)+1, (void **)&agent)==FAILURE) {
342 0 : break;
343 : }
344 :
345 0 : if (return_array) {
346 0 : 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);
347 : }
348 : else {
349 0 : 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);
350 : }
351 : }
352 :
353 0 : efree(lookup_browser_name);
354 : }
355 : /* }}} */
356 :
357 : /*
358 : * Local variables:
359 : * tab-width: 4
360 : * c-basic-offset: 4
361 : * End:
362 : * vim600: sw=4 ts=4 fdm=marker
363 : * vim<600: sw=4 ts=4
364 : */
|