2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #ifndef PHP_HTTP_DEBUG_NEG
16 # define PHP_HTTP_DEBUG_NEG 0
19 static int php_http_negotiate_sort(Bucket
*b1
, Bucket
*b2
)
21 int result
= numeric_compare_function(&b1
->val
, &b2
->val
);
23 return (result
> 0 ? -1 : (result
< 0 ? 1 : 0));
31 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
)
33 unsigned match
= M_NOT
;
35 if (param_len
== supported_len
&& !strncasecmp(param_str
, supported_str
, param_len
)) {
38 } else if (sep_str
&& sep_len
) {
39 const char *param_sec
= php_http_locate_str(param_str
, param_len
, sep_str
, sep_len
);
40 size_t param_pri_len
= param_sec
? param_sec
- param_str
: param_len
;
41 const char *supported_sec
= php_http_locate_str(supported_str
, supported_len
, sep_str
, sep_len
);
42 size_t supported_pri_len
= supported_sec
? supported_sec
- supported_str
: supported_len
;
43 size_t cmp_len
= MIN(param_pri_len
, supported_pri_len
);
45 if (((*param_str
== '*') || (*supported_str
== '*'))
46 || ((param_pri_len
== supported_pri_len
) && !strncasecmp(param_str
, supported_str
, param_pri_len
))
47 || ((!param_sec
|| !supported_sec
) && cmp_len
&& !strncasecmp(param_str
, supported_str
, cmp_len
))
52 if (param_sec
&& supported_sec
53 && ((*(param_sec
+ sep_len
) == '*' || *(supported_sec
+ sep_len
) == '*')
54 || !strcasecmp(param_sec
, supported_sec
)
60 if ((param_sec
&& *(param_sec
+ sep_len
) == '*')
61 || (supported_sec
&& *(supported_sec
+ sep_len
) == '*')
62 || ((*param_str
== '*') || (*supported_str
== '*'))
67 #if PHP_HTTP_DEBUG_NEG
68 fprintf(stderr
, "match: %s == %s => %u\n", supported_str
, param_str
, match
);
72 static int php_http_negotiate_reduce(zval
*p
, int num_args
, va_list args
, zend_hash_key
*hash_key
)
74 unsigned best_match
= 0;
76 php_http_arrkey_t key
;
78 zend_string
*supported
= zval_get_string(p
);
79 HashTable
*params
= va_arg(args
, HashTable
*);
80 HashTable
*result
= va_arg(args
, HashTable
*);
81 const char *sep_str
= va_arg(args
, const char *);
82 size_t sep_len
= va_arg(args
, size_t);
84 ZEND_HASH_FOREACH_KEY_VAL(params
, key
.h
, key
.key
, value
)
88 #if PHP_HTTP_DEBUG_NEG
89 fprintf(stderr
, "match(%u) > best_match(%u) = %u (q=%f)\n", match
, best_match
, match
>best_match
, Z_DVAL_PP(val
));
91 php_http_arrkey_stringify(&key
, NULL
);
92 match
= php_http_negotiate_match(key
.key
->val
, key
.key
->len
, supported
->val
, supported
->len
, sep_str
, sep_len
);
94 if (match
> best_match
) {
96 q
= Z_DVAL_P(value
) - 0.1 / match
;
98 php_http_arrkey_dtor(&key
);
100 ZEND_HASH_FOREACH_END();
105 ZVAL_DOUBLE(&tmp
, q
);
106 zend_hash_update(result
, supported
, &tmp
);
109 zend_string_release(supported
);
110 return ZEND_HASH_APPLY_KEEP
;
113 HashTable
*php_http_negotiate(const char *value_str
, size_t value_len
, HashTable
*supported
, const char *primary_sep_str
, size_t primary_sep_len
)
115 HashTable
*result
= NULL
;
117 if (value_str
&& value_len
) {
119 zval arr
, *val
, *arg
, *zq
;
121 php_http_arrkey_t key
;
122 php_http_params_opts_t opts
;
124 zend_hash_init(¶ms
, 10, NULL
, ZVAL_PTR_DTOR
, 0);
125 php_http_params_opts_default_get(&opts
);
126 opts
.input
.str
= estrndup(value_str
, value_len
);
127 opts
.input
.len
= value_len
;
128 opts
.flags
&= ~PHP_HTTP_PARAMS_RFC5987
;
129 php_http_params_parse(¶ms
, &opts
);
130 efree(opts
.input
.str
);
134 ZEND_HASH_FOREACH_KEY_VAL(¶ms
, key
.h
, key
.key
, val
)
138 if ((arg
= zend_hash_str_find(Z_ARRVAL_P(val
), ZEND_STRL("arguments")))
139 && (IS_ARRAY
== Z_TYPE_P(arg
))
140 && (zq
= zend_hash_str_find(Z_ARRVAL_P(arg
), ZEND_STRL("q")))) {
141 q
= zval_get_double(zq
);
143 q
= 1.0 - (((double) ++i
) / 100.0);
147 fprintf(stderr
, "Q: %s=%1.3f\n", key
.key
->val
, q
);
151 add_assoc_double_ex(&arr
, key
.key
->val
, key
.key
->len
, q
);
153 add_index_double(&arr
, key
.h
, q
);
157 ZEND_HASH_FOREACH_END();
159 #if PHP_HTTP_DEBUG_NEG
160 zend_print_zval_r(&arr
, 1);
163 ALLOC_HASHTABLE(result
);
164 zend_hash_init(result
, zend_hash_num_elements(supported
), NULL
, ZVAL_PTR_DTOR
, 0);
165 zend_hash_apply_with_arguments(supported
, php_http_negotiate_reduce
, 4, Z_ARRVAL(arr
), result
, primary_sep_str
, primary_sep_len
);
166 zend_hash_destroy(¶ms
);
168 zend_hash_sort(result
, php_http_negotiate_sort
, 0);
181 * vim600: noet sw=4 ts=4 fdm=marker
182 * vim<600: noet sw=4 ts=4