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 *a
, const void *b TSRMLS_DC
)
21 zval result
, *first
, *second
;
23 first
= *((zval
**) (*((Bucket
**) a
))->pData
);
24 second
= *((zval
**) (*((Bucket
**) b
))->pData
);
26 if (numeric_compare_function(&result
, first
, second TSRMLS_CC
) != SUCCESS
) {
29 return (Z_LVAL(result
) > 0 ? -1 : (Z_LVAL(result
) < 0 ? 1 : 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
)
41 if (param_len
== supported_len
&& !strncasecmp(param_str
, supported_str
, param_len
)) {
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
);
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
))
58 if (param_sec
&& supported_sec
59 && ((*(param_sec
+ sep_len
) == '*' || *(supported_sec
+ sep_len
) == '*')
60 || !strcasecmp(param_sec
, supported_sec
)
66 if ((param_sec
&& *(param_sec
+ sep_len
) == '*')
67 || (supported_sec
&& *(supported_sec
+ sep_len
) == '*')
68 || ((*param_str
== '*') || (*supported_str
== '*'))
73 #if PHP_HTTP_DEBUG_NEG
74 fprintf(stderr
, "match: %s == %s => %u\n", supported_str
, param_str
, match
);
79 static int php_http_negotiate_reduce(void *p TSRMLS_DC
, int num_args
, va_list args
, zend_hash_key
*hash_key
)
81 unsigned best_match
= 0;
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);
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
));
97 if (match
> best_match
) {
99 q
= Z_DVAL_PP(val
) - 0.1 / match
;
109 zend_hash_update(result
, Z_STRVAL_P(supported
), Z_STRLEN_P(supported
) + 1, (void *) &tmp
, sizeof(zval
*), NULL
);
112 zval_ptr_dtor(&supported
);
113 return ZEND_HASH_APPLY_KEEP
;
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
)
118 HashTable
*result
= NULL
;
120 if (value_str
&& value_len
) {
122 zval arr
, **val
, **arg
, **zq
;
125 php_http_array_hashkey_t key
= php_http_array_hashkey_init(1);
126 php_http_params_opts_t opts
;
128 zend_hash_init(¶ms
, 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(¶ms
, &opts TSRMLS_CC
);
134 efree(opts
.input
.str
);
139 FOREACH_HASH_KEYVAL(pos
, ¶ms
, key
, val
) {
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
);
150 q
= 1.0 - (((double) ++i
) / 100.0);
153 if (key
.type
== HASH_KEY_IS_STRING
) {
154 add_assoc_double_ex(&arr
, key
.str
, key
.len
, q
);
156 add_index_double(&arr
, key
.num
, q
);
162 #if PHP_HTTP_DEBUG_NEG
163 zend_print_zval_r(&arr
, 1 TSRMLS_CC
);
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(¶ms
);
171 zend_hash_sort(result
, zend_qsort
, php_http_negotiate_sort
, 0 TSRMLS_CC
);
184 * vim600: noet sw=4 ts=4 fdm=marker
185 * vim<600: noet sw=4 ts=4