Line data Source code
1 : /*
2 : +--------------------------------------------------------------------+
3 : | PECL :: http |
4 : +--------------------------------------------------------------------+
5 : | Redistribution and use in source and binary forms, with or without |
6 : | modification, are permitted provided that the conditions mentioned |
7 : | in the accompanying LICENSE file are met. |
8 : +--------------------------------------------------------------------+
9 : | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : #include "php_http_api.h"
14 :
15 9 : static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
16 : {
17 : zval result, *first, *second;
18 :
19 9 : first = *((zval **) (*((Bucket **) a))->pData);
20 9 : second= *((zval **) (*((Bucket **) b))->pData);
21 :
22 9 : if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
23 0 : return 0;
24 : }
25 9 : return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
26 : }
27 :
28 : #define M_PRI 5
29 : #define M_SEC 2
30 : #define M_ANY 1
31 : #define M_NOT 0
32 : #define M_ALL -1
33 146 : static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
34 : {
35 146 : int match = M_NOT;
36 :
37 146 : if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
38 : /* that was easy */
39 28 : match = M_ALL;
40 118 : } else if (sep_str && sep_len) {
41 86 : const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
42 86 : size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
43 86 : const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
44 86 : size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
45 86 : size_t cmp_len = MIN(param_pri_len, supported_pri_len);
46 :
47 86 : if (((*param_str == '*') || (*supported_str == '*'))
48 69 : || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
49 41 : || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
50 : ) {
51 45 : match += M_PRI;
52 : }
53 :
54 86 : if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
55 0 : match += M_SEC;
56 : }
57 :
58 86 : if ((param_sec && *(param_sec + sep_len) == '*')
59 86 : || (supported_sec && *(supported_sec + sep_len) == '*')
60 86 : || ((*param_str == '*') || (*supported_str == '*'))
61 : ) {
62 17 : match += M_ANY;
63 : }
64 : }
65 : #if 0
66 : fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
67 : #endif
68 146 : return match;
69 : }
70 :
71 48 : static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
72 : {
73 48 : unsigned best_match = 0;
74 : HashPosition pos;
75 48 : php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
76 48 : zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p);
77 48 : HashTable *params = va_arg(args, HashTable *);
78 48 : HashTable *result = va_arg(args, HashTable *);
79 48 : const char *sep_str = va_arg(args, const char *);
80 48 : size_t sep_len = va_arg(args, size_t);
81 :
82 194 : FOREACH_HASH_KEYVAL(pos, params, key, val) {
83 146 : if (key.type == HASH_KEY_IS_STRING) {
84 146 : unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len);
85 :
86 146 : if (match > best_match) {
87 48 : best_match = match;
88 48 : q = val;
89 : }
90 : }
91 : }
92 :
93 48 : if (q && Z_DVAL_PP(q) > 0) {
94 24 : Z_ADDREF_PP(q);
95 24 : zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL);
96 : }
97 :
98 48 : zval_ptr_dtor(&supported);
99 48 : return ZEND_HASH_APPLY_KEEP;
100 : }
101 :
102 19 : HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC)
103 : {
104 19 : HashTable *result = NULL;
105 :
106 19 : if (value_str && value_len) {
107 19 : unsigned i = 0;
108 : zval arr, **val, **arg, **zq;
109 : HashPosition pos;
110 : HashTable params;
111 19 : php_http_array_hashkey_t key = php_http_array_hashkey_init(1);
112 : php_http_params_opts_t opts;
113 :
114 19 : zend_hash_init(¶ms, 10, NULL, ZVAL_PTR_DTOR, 0);
115 19 : php_http_params_opts_default_get(&opts);
116 19 : opts.input.str = estrndup(value_str, value_len);
117 19 : opts.input.len = value_len;
118 19 : php_http_params_parse(¶ms, &opts TSRMLS_CC);
119 19 : efree(opts.input.str);
120 :
121 19 : INIT_PZVAL(&arr);
122 19 : array_init(&arr);
123 :
124 78 : FOREACH_HASH_KEYVAL(pos, ¶ms, key, val) {
125 : double q;
126 :
127 59 : if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg)
128 59 : && IS_ARRAY == Z_TYPE_PP(arg)
129 96 : && SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) {
130 37 : zval *tmp = php_http_ztyp(IS_DOUBLE, *zq);
131 :
132 37 : q = Z_DVAL_P(tmp);
133 37 : zval_ptr_dtor(&tmp);
134 : } else {
135 22 : q = 1.0 - ++i / 100.0;
136 : }
137 :
138 59 : if (key.type == HASH_KEY_IS_STRING) {
139 59 : add_assoc_double_ex(&arr, key.str, key.len, q);
140 : } else {
141 0 : add_index_double(&arr, key.num, q);
142 : }
143 :
144 59 : PTR_FREE(key.str);
145 : }
146 :
147 : #if 0
148 : zend_print_zval_r(&arr, 1 TSRMLS_CC);
149 : #endif
150 :
151 19 : ALLOC_HASHTABLE(result);
152 19 : zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
153 19 : zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
154 19 : zend_hash_destroy(¶ms);
155 19 : zval_dtor(&arr);
156 19 : zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
157 : }
158 :
159 19 : return result;
160 : }
161 :
162 :
163 :
164 : /*
165 : * Local variables:
166 : * tab-width: 4
167 : * c-basic-offset: 4
168 : * End:
169 : * vim600: noet sw=4 ts=4 fdm=marker
170 : * vim<600: noet sw=4 ts=4
171 : */
172 :
173 :
|