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(const void *first
, const void *second
)
21 Bucket
*b1
= (Bucket
*) first
, *b2
= (Bucket
*) second
;
22 int result
= numeric_compare_function(&b1
->val
, &b2
->val
);
24 return (result
> 0 ? -1 : (result
< 0 ? 1 : 0));
32 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 unsigned match
= M_NOT
;
36 if (param_len
== supported_len
&& !strncasecmp(param_str
, supported_str
, param_len
)) {
39 } else if (sep_str
&& sep_len
) {
40 const char *param_sec
= php_http_locate_str(param_str
, param_len
, sep_str
, sep_len
);
41 size_t param_pri_len
= param_sec
? param_sec
- param_str
: param_len
;
42 const char *supported_sec
= php_http_locate_str(supported_str
, supported_len
, sep_str
, sep_len
);
43 size_t supported_pri_len
= supported_sec
? supported_sec
- supported_str
: supported_len
;
44 size_t cmp_len
= MIN(param_pri_len
, supported_pri_len
);
46 if (((*param_str
== '*') || (*supported_str
== '*'))
47 || ((param_pri_len
== supported_pri_len
) && !strncasecmp(param_str
, supported_str
, param_pri_len
))
48 || ((!param_sec
|| !supported_sec
) && cmp_len
&& !strncasecmp(param_str
, supported_str
, cmp_len
))
53 if (param_sec
&& supported_sec
54 && ((*(param_sec
+ sep_len
) == '*' || *(supported_sec
+ sep_len
) == '*')
55 || !strcasecmp(param_sec
, supported_sec
)
61 if ((param_sec
&& *(param_sec
+ sep_len
) == '*')
62 || (supported_sec
&& *(supported_sec
+ sep_len
) == '*')
63 || ((*param_str
== '*') || (*supported_str
== '*'))
68 #if PHP_HTTP_DEBUG_NEG
69 fprintf(stderr
, "match: %s == %s => %u\n", supported_str
, param_str
, match
);
73 static int php_http_negotiate_reduce(zval
*p
, int num_args
, va_list args
, zend_hash_key
*hash_key
)
75 unsigned best_match
= 0;
77 php_http_arrkey_t key
;
79 zend_string
*supported
= zval_get_string(p
);
80 HashTable
*params
= va_arg(args
, HashTable
*);
81 HashTable
*result
= va_arg(args
, HashTable
*);
82 const char *sep_str
= va_arg(args
, const char *);
83 size_t sep_len
= va_arg(args
, size_t);
85 ZEND_HASH_FOREACH_KEY_VAL(params
, key
.h
, key
.key
, value
)
89 #if PHP_HTTP_DEBUG_NEG
90 fprintf(stderr
, "match(%u) > best_match(%u) = %u (q=%f)\n", match
, best_match
, match
>best_match
, Z_DVAL_PP(val
));
92 php_http_arrkey_stringify(&key
, NULL
);
93 match
= php_http_negotiate_match(key
.key
->val
, key
.key
->len
, supported
->val
, supported
->len
, sep_str
, sep_len
);
95 if (match
> best_match
) {
97 q
= Z_DVAL_P(value
) - 0.1 / match
;
99 php_http_arrkey_dtor(&key
);
101 ZEND_HASH_FOREACH_END();
106 ZVAL_DOUBLE(&tmp
, q
);
107 zend_hash_update(result
, supported
, &tmp
);
110 zend_string_release(supported
);
111 return ZEND_HASH_APPLY_KEEP
;
114 HashTable
*php_http_negotiate(const char *value_str
, size_t value_len
, HashTable
*supported
, const char *primary_sep_str
, size_t primary_sep_len
)
116 HashTable
*result
= NULL
;
118 if (value_str
&& value_len
) {
120 zval arr
, *val
, *arg
, *zq
;
122 php_http_arrkey_t key
;
123 php_http_params_opts_t opts
;
125 zend_hash_init(¶ms
, 10, NULL
, ZVAL_PTR_DTOR
, 0);
126 php_http_params_opts_default_get(&opts
);
127 opts
.input
.str
= estrndup(value_str
, value_len
);
128 opts
.input
.len
= value_len
;
129 opts
.flags
&= ~PHP_HTTP_PARAMS_RFC5987
;
130 php_http_params_parse(¶ms
, &opts
);
131 efree(opts
.input
.str
);
135 ZEND_HASH_FOREACH_KEY_VAL(¶ms
, key
.h
, key
.key
, val
)
139 if ((arg
= zend_hash_str_find(Z_ARRVAL_P(val
), ZEND_STRL("arguments")))
140 && (IS_ARRAY
== Z_TYPE_P(arg
))
141 && (zq
= zend_hash_str_find(Z_ARRVAL_P(arg
), ZEND_STRL("q")))) {
142 q
= zval_get_double(zq
);
144 q
= 1.0 - (((double) ++i
) / 100.0);
148 fprintf(stderr
, "Q: %s=%1.3f\n", key
.key
->val
, q
);
152 add_assoc_double_ex(&arr
, key
.key
->val
, key
.key
->len
, q
);
154 add_index_double(&arr
, key
.h
, q
);
158 ZEND_HASH_FOREACH_END();
160 #if PHP_HTTP_DEBUG_NEG
161 zend_print_zval_r(&arr
, 1);
164 ALLOC_HASHTABLE(result
);
165 zend_hash_init(result
, zend_hash_num_elements(supported
), NULL
, ZVAL_PTR_DTOR
, 0);
166 zend_hash_apply_with_arguments(supported
, php_http_negotiate_reduce
, 4, Z_ARRVAL(arr
), result
, primary_sep_str
, primary_sep_len
);
167 zend_hash_destroy(¶ms
);
169 zend_hash_sort(result
, php_http_negotiate_sort
, 0);
182 * vim600: noet sw=4 ts=4 fdm=marker
183 * vim<600: noet sw=4 ts=4