release 2.6.0
[m6w6/ext-http] / src / php_http_negotiate.c
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 #ifndef PHP_HTTP_DEBUG_NEG
16 # define PHP_HTTP_DEBUG_NEG 0
17 #endif
18
19 static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
20 {
21 zval result, *first, *second;
22
23 first = *((zval **) (*((Bucket **) a))->pData);
24 second= *((zval **) (*((Bucket **) b))->pData);
25
26 if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
27 return 0;
28 }
29 return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
30 }
31
32 #define M_PRI 5
33 #define M_SEC 2
34 #define M_ANY -1
35 #define M_NOT 0
36 #define M_ALL ~0
37 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)
38 {
39 int match = M_NOT;
40
41 if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
42 /* that was easy */
43 match = M_ALL;
44 } else if (sep_str && sep_len) {
45 const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
46 size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
47 const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
48 size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
49 size_t cmp_len = MIN(param_pri_len, supported_pri_len);
50
51 if (((*param_str == '*') || (*supported_str == '*'))
52 || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
53 || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
54 ) {
55 match += M_PRI;
56 }
57
58 if (param_sec && supported_sec
59 && ((*(param_sec + sep_len) == '*' || *(supported_sec + sep_len) == '*')
60 || !strcasecmp(param_sec, supported_sec)
61 )
62 ) {
63 match += M_SEC;
64 }
65
66 if ((param_sec && *(param_sec + sep_len) == '*')
67 || (supported_sec && *(supported_sec + sep_len) == '*')
68 || ((*param_str == '*') || (*supported_str == '*'))
69 ) {
70 match += M_ANY;
71 }
72 }
73 #if PHP_HTTP_DEBUG_NEG
74 fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
75 #endif
76 return match;
77 }
78
79 static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
80 {
81 unsigned best_match = 0;
82 double q = 0;
83 HashPosition pos;
84 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
85 zval **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p);
86 HashTable *params = va_arg(args, HashTable *);
87 HashTable *result = va_arg(args, HashTable *);
88 const char *sep_str = va_arg(args, const char *);
89 size_t sep_len = va_arg(args, size_t);
90
91 FOREACH_HASH_KEYVAL(pos, params, key, val) {
92 if (key.type == HASH_KEY_IS_STRING) {
93 unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len);
94 #if PHP_HTTP_DEBUG_NEG
95 fprintf(stderr, "match(%u) > best_match(%u) = %u (q=%f)\n", match, best_match, match>best_match, Z_DVAL_PP(val));
96 #endif
97 if (match > best_match) {
98 best_match = match;
99 q = Z_DVAL_PP(val) - 0.1 / match;
100 }
101 }
102 }
103
104 if (q > 0) {
105 zval *tmp;
106
107 MAKE_STD_ZVAL(tmp);
108 ZVAL_DOUBLE(tmp, q);
109 zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) &tmp, sizeof(zval *), NULL);
110 }
111
112 zval_ptr_dtor(&supported);
113 return ZEND_HASH_APPLY_KEEP;
114 }
115
116 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)
117 {
118 HashTable *result = NULL;
119
120 if (value_str && value_len) {
121 unsigned i = 0;
122 zval arr, **val, **arg, **zq;
123 HashPosition pos;
124 HashTable params;
125 php_http_array_hashkey_t key = php_http_array_hashkey_init(1);
126 php_http_params_opts_t opts;
127
128 zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
129 php_http_params_opts_default_get(&opts);
130 opts.input.str = estrndup(value_str, value_len);
131 opts.input.len = value_len;
132 opts.flags &= ~PHP_HTTP_PARAMS_RFC5987;
133 php_http_params_parse(&params, &opts TSRMLS_CC);
134 efree(opts.input.str);
135
136 INIT_PZVAL(&arr);
137 array_init(&arr);
138
139 FOREACH_HASH_KEYVAL(pos, &params, key, val) {
140 double q;
141
142 if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg)
143 && IS_ARRAY == Z_TYPE_PP(arg)
144 && SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) {
145 zval *tmp = php_http_ztyp(IS_DOUBLE, *zq);
146
147 q = Z_DVAL_P(tmp);
148 zval_ptr_dtor(&tmp);
149 } else {
150 q = 1.0 - (((double) ++i) / 100.0);
151 }
152
153 if (key.type == HASH_KEY_IS_STRING) {
154 add_assoc_double_ex(&arr, key.str, key.len, q);
155 } else {
156 add_index_double(&arr, key.num, q);
157 }
158
159 PTR_FREE(key.str);
160 }
161
162 #if PHP_HTTP_DEBUG_NEG
163 zend_print_zval_r(&arr, 1 TSRMLS_CC);
164 #endif
165
166 ALLOC_HASHTABLE(result);
167 zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
168 zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
169 zend_hash_destroy(&params);
170 zval_dtor(&arr);
171 zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
172 }
173
174 return result;
175 }
176
177
178
179 /*
180 * Local variables:
181 * tab-width: 4
182 * c-basic-offset: 4
183 * End:
184 * vim600: noet sw=4 ts=4 fdm=marker
185 * vim<600: noet sw=4 ts=4
186 */
187
188