test fixups from Remi
[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 static int php_http_negotiate_sort(const void *first, const void *second)
16 {
17 Bucket *b1 = (Bucket *) first, *b2 = (Bucket *) second;
18 int result = numeric_compare_function(&b1->val, &b2->val);
19
20 return (result > 0 ? -1 : (result < 0 ? 1 : 0));
21 }
22
23 #define M_PRI 5
24 #define M_SEC 2
25 #define M_ANY 1
26 #define M_NOT 0
27 #define M_ALL ~0
28 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)
29 {
30 unsigned match = M_NOT;
31
32 if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
33 /* that was easy */
34 match = M_ALL;
35 } else if (sep_str && sep_len) {
36 const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
37 size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
38 const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
39 size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
40 size_t cmp_len = MIN(param_pri_len, supported_pri_len);
41
42 if (((*param_str == '*') || (*supported_str == '*'))
43 || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
44 || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
45 ) {
46 match += M_PRI;
47 }
48
49 if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
50 match += M_SEC;
51 }
52
53 if ((param_sec && *(param_sec + sep_len) == '*')
54 || (supported_sec && *(supported_sec + sep_len) == '*')
55 || ((*param_str == '*') || (*supported_str == '*'))
56 ) {
57 match += M_ANY;
58 }
59 }
60 #if 0
61 fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
62 #endif
63 return match;
64 }
65 static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_hash_key *hash_key)
66 {
67 unsigned best_match = 0;
68 php_http_arrkey_t key;
69 zval *value, *q = NULL;
70 zend_string *supported = zval_get_string(p);
71 HashTable *params = va_arg(args, HashTable *);
72 HashTable *result = va_arg(args, HashTable *);
73 const char *sep_str = va_arg(args, const char *);
74 size_t sep_len = va_arg(args, size_t);
75
76 ZEND_HASH_FOREACH_KEY_VAL(params, key.h, key.key, value)
77 {
78 unsigned match;
79
80 php_http_arrkey_stringify(&key, NULL);
81 match = php_http_negotiate_match(key.key->val, key.key->len, supported->val, supported->len, sep_str, sep_len);
82
83 if (match > best_match) {
84 best_match = match;
85 q = value;
86 }
87 php_http_arrkey_dtor(&key);
88 }
89 ZEND_HASH_FOREACH_END();
90
91 if (q && Z_DVAL_P(q) > 0) {
92 Z_TRY_ADDREF_P(q);
93 zend_hash_update(result, supported, q);
94 }
95
96 zend_string_release(supported);
97 return ZEND_HASH_APPLY_KEEP;
98 }
99
100 HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len)
101 {
102 HashTable *result = NULL;
103
104 if (value_str && value_len) {
105 unsigned i = 0;
106 zval arr, *val, *arg, *zq;
107 HashTable params;
108 php_http_arrkey_t key;
109 php_http_params_opts_t opts;
110
111 zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
112 php_http_params_opts_default_get(&opts);
113 opts.input.str = estrndup(value_str, value_len);
114 opts.input.len = value_len;
115 php_http_params_parse(&params, &opts);
116 efree(opts.input.str);
117
118 array_init(&arr);
119
120 ZEND_HASH_FOREACH_KEY_VAL(&params, key.h, key.key, val)
121 {
122 double q;
123
124 if ((arg = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("arguments")))
125 && (IS_ARRAY == Z_TYPE_P(arg))
126 && (zq = zend_hash_str_find(Z_ARRVAL_P(arg), ZEND_STRL("q")))) {
127 q = zval_get_double(zq);
128 } else {
129 q = 1.0 - ++i / 100.0;
130 }
131
132 #if 0
133 fprintf(stderr, "Q: %s=%1.3f\n", key.key->val, q);
134 #endif
135
136 if (key.key) {
137 add_assoc_double_ex(&arr, key.key->val, key.key->len, q);
138 } else {
139 add_index_double(&arr, key.h, q);
140 }
141
142 }
143 ZEND_HASH_FOREACH_END();
144
145 #if 0
146 zend_print_zval_r(&arr, 1);
147 #endif
148
149 ALLOC_HASHTABLE(result);
150 zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
151 zend_hash_apply_with_arguments(supported, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
152 zend_hash_destroy(&params);
153 zval_dtor(&arr);
154 zend_hash_sort(result, php_http_negotiate_sort, 0);
155 }
156
157 return result;
158 }
159
160
161
162 /*
163 * Local variables:
164 * tab-width: 4
165 * c-basic-offset: 4
166 * End:
167 * vim600: noet sw=4 ts=4 fdm=marker
168 * vim<600: noet sw=4 ts=4
169 */
170
171