* more modular file structure
[m6w6/ext-http] / http_headers_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include <ctype.h>
23
24 #include "php.h"
25
26 #include "php_http.h"
27 #include "php_http_std_defs.h"
28 #include "php_http_api.h"
29 #include "php_http_headers_api.h"
30
31 /* {{{ static int http_sort_q(const void *, const void *) */
32 static int http_sort_q(const void *a, const void *b TSRMLS_DC)
33 {
34 Bucket *f, *s;
35 zval result, *first, *second;
36
37 f = *((Bucket **) a);
38 s = *((Bucket **) b);
39
40 first = *((zval **) f->pData);
41 second= *((zval **) s->pData);
42
43 if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
44 return 0;
45 }
46 return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
47 }
48 /* }}} */
49
50 /* {{{ char *http_negotiate_q(char *, HashTable *, char *) */
51 PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC)
52 {
53 zval *zaccept, zdelim, zarray, zentries, **zentry, **zsupp;
54 char *q_ptr = NULL, *key = NULL;
55 int i = 0;
56 ulong idx = 0;
57 double qual;
58
59 HTTP_GSC(zaccept, entry, estrdup(def));
60
61 array_init(&zarray);
62 array_init(&zentries);
63
64 Z_STRVAL(zdelim) = ",";
65 Z_STRLEN(zdelim) = 1;
66
67 php_explode(&zdelim, zaccept, &zarray, -1);
68
69 FOREACH_HASH_VAL(Z_ARRVAL(zarray), zentry) {
70 if (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';')) {
71 qual = strtod(q_ptr + 3, NULL);
72 *q_ptr = 0;
73 q_ptr = NULL;
74 } else {
75 qual = 1000.0 - i++;
76 }
77 FOREACH_HASH_VAL((HashTable *)supported, zsupp) {
78 if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) {
79 add_assoc_double(&zentries, Z_STRVAL_PP(zsupp), qual);
80 break;
81 }
82 }
83 }
84 zval_dtor(&zarray);
85
86 zend_hash_sort(Z_ARRVAL(zentries), zend_qsort, http_sort_q, 0 TSRMLS_CC);
87
88 FOREACH_HASH_KEY(Z_ARRVAL(zentries), key, idx) {
89 if (key) {
90 key = estrdup(key);
91 zval_dtor(&zentries);
92 return key;
93 }
94 }
95 zval_dtor(&zentries);
96
97 return estrdup(def);
98 }
99 /* }}} */
100
101 /* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */
102 PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC)
103 {
104 zval *zrange;
105 char *range, c;
106 long begin = -1, end = -1, *ptr;
107
108 HTTP_GSC(zrange, "HTTP_RANGE", RANGE_NO);
109 range = Z_STRVAL_P(zrange);
110
111 if (strncmp(range, "bytes=", sizeof("bytes=") - 1)) {
112 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Range header misses bytes=");
113 return RANGE_NO;
114 }
115
116 ptr = &begin;
117 range += sizeof("bytes=") - 1;
118
119 do {
120 switch (c = *(range++))
121 {
122 case '0':
123 *ptr *= 10;
124 break;
125
126 case '1': case '2': case '3':
127 case '4': case '5': case '6':
128 case '7': case '8': case '9':
129 /*
130 * If the value of the pointer is already set (non-negative)
131 * then multiply its value by ten and add the current value,
132 * else initialise the pointers value with the current value
133 * --
134 * This let us recognize empty fields when validating the
135 * ranges, i.e. a "-10" for begin and "12345" for the end
136 * was the following range request: "Range: bytes=0-12345";
137 * While a "-1" for begin and "12345" for the end would
138 * have been: "Range: bytes=-12345".
139 */
140 if (*ptr > 0) {
141 *ptr *= 10;
142 *ptr += c - '0';
143 } else {
144 *ptr = c - '0';
145 }
146 break;
147
148 case '-':
149 ptr = &end;
150 break;
151
152 case ' ':
153 /* IE - ignore for now */
154 break;
155
156 case 0:
157 case ',':
158
159 if (length) {
160 /* validate ranges */
161 switch (begin)
162 {
163 /* "0-12345" */
164 case -10:
165 /* "0-" */
166 if (end == -1) {
167 return RANGE_NO;
168 }
169 /* "0-0" or overflow */
170 if (end == -10 || length <= end) {
171 return RANGE_ERR;
172 }
173 begin = 0;
174 break;
175
176 /* "-12345" */
177 case -1:
178 /* "-", "-0" or overflow */
179 if (end == -1 || end == -10 || length <= end) {
180 return RANGE_ERR;
181 }
182 begin = length - end;
183 end = length - 1;
184 break;
185
186 /* "12345-(xxx)" */
187 default:
188 switch (end)
189 {
190 /* "12345-0" */
191 case -10:
192 return RANGE_ERR;
193 break;
194
195 /* "12345-" */
196 case -1:
197 if (length <= begin) {
198 return RANGE_ERR;
199 }
200 end = length - 1;
201 break;
202
203 /* "12345-67890" */
204 default:
205 if ( (length <= begin) ||
206 (length <= end) ||
207 (end < begin)) {
208 return RANGE_ERR;
209 }
210 break;
211 }
212 break;
213 }
214 }
215 {
216 zval *zentry;
217 MAKE_STD_ZVAL(zentry);
218 array_init(zentry);
219 add_index_long(zentry, 0, begin);
220 add_index_long(zentry, 1, end);
221 zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
222
223 begin = -1;
224 end = -1;
225 ptr = &begin;
226 }
227 break;
228
229 default:
230 return RANGE_NO;
231 break;
232 }
233 } while (c != 0);
234
235 return RANGE_OK;
236 }
237 /* }}} */
238
239 /* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */
240 PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len,
241 HashTable *headers, zend_bool prettify TSRMLS_DC)
242 {
243 const char *colon = NULL, *line = NULL, *begin = header;
244 zval array;
245
246 Z_ARRVAL(array) = headers;
247
248 if (header_len < 2) {
249 return FAILURE;
250 }
251
252 /* status code */
253 if (!strncmp(header, "HTTP/1.", 7)) {
254 char *end = strstr(header, HTTP_CRLF);
255 size_t len = end - (header + lenof("HTTP/1.x "));
256 char *val = estrndup(header + lenof("HTTP/1.x "), len);
257
258 add_assoc_stringl(&array, "Status", val, len, 0);
259 header = end + 2;
260 }
261
262 line = header;
263
264 while (header_len >= (line - begin)) {
265 int value_len = 0;
266
267 switch (*line++)
268 {
269 case 0:
270 --value_len; /* we don't have CR so value length is one char less */
271 case '\n':
272 if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) {
273
274 /* skip empty key */
275 if (header != colon) {
276 zval **previous = NULL;
277 char *value = empty_string;
278 int keylen = colon - header;
279 char *key = estrndup(header, keylen);
280
281 if (prettify) {
282 key = pretty_key(key, keylen, 1, 1);
283 }
284
285 value_len += line - colon - 1;
286
287 /* skip leading ws */
288 while (isspace(*(++colon))) --value_len;
289 /* skip trailing ws */
290 while (isspace(colon[value_len - 1])) --value_len;
291
292 if (value_len > 0) {
293 value = estrndup(colon, value_len);
294 } else {
295 value_len = 0;
296 }
297
298 /* if we already have got such a header make an array of those */
299 if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
300 /* already an array? - just add */
301 if (Z_TYPE_PP(previous) == IS_ARRAY) {
302 add_next_index_stringl(*previous, value, value_len, 0);
303 } else {
304 /* create the array */
305 zval *new_array;
306 MAKE_STD_ZVAL(new_array);
307 array_init(new_array);
308
309 add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1);
310 add_next_index_stringl(new_array, value, value_len, 0);
311 add_assoc_zval(&array, key, new_array);
312 }
313
314 previous = NULL;
315 } else {
316 add_assoc_stringl(&array, key, value, value_len, 0);
317 }
318 efree(key);
319 }
320
321 colon = NULL;
322 value_len = 0;
323 header += line - header;
324 }
325 break;
326
327 case ':':
328 if (!colon) {
329 colon = line - 1;
330 }
331 break;
332 }
333 }
334 return SUCCESS;
335 }
336 /* }}} */
337
338 /* {{{ */
339 PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC)
340 {
341 const char *key = cookie, *val = NULL;
342 int vallen = 0, keylen = 0, done = 0;
343 zval array;
344
345 Z_ARRVAL(array) = values;
346
347 if (!(val = strchr(cookie, '='))) {
348 return FAILURE;
349 }
350
351 #define HTTP_COOKIE_VAL(array, k, str, len) \
352 { \
353 const char *encoded = str; \
354 char *decoded = NULL; \
355 int decoded_len = 0, encoded_len = len; \
356 decoded = estrndup(encoded, encoded_len); \
357 decoded_len = php_url_decode(decoded, encoded_len); \
358 add_assoc_stringl(array, k, decoded, decoded_len, 0); \
359 }
360 #define HTTP_COOKIE_FIXKEY() \
361 { \
362 while (isspace(*key)) ++key; \
363 keylen = val - key; \
364 while (isspace(key[keylen - 1])) --keylen; \
365 }
366 #define HTTP_COOKIE_FIXVAL() \
367 { \
368 ++val; \
369 while (isspace(*val)) ++val; \
370 vallen = key - val; \
371 while (isspace(val[vallen - 1])) --vallen; \
372 }
373
374 HTTP_COOKIE_FIXKEY();
375 HTTP_COOKIE_VAL(&array, "name", key, keylen);
376
377 /* just a name=value cookie */
378 if (!(key = strchr(val, ';'))) {
379 key = val + strlen(val);
380 HTTP_COOKIE_FIXVAL();
381 HTTP_COOKIE_VAL(&array, "value", val, vallen);
382 }
383 /* additional info appended */
384 else {
385 char *keydup = NULL;
386
387 HTTP_COOKIE_FIXVAL();
388 HTTP_COOKIE_VAL(&array, "value", val, vallen);
389
390 do {
391 if (!(val = strchr(key, '='))) {
392 break;
393 }
394 ++key;
395 HTTP_COOKIE_FIXKEY();
396 keydup = estrndup(key, keylen);
397 if (!(key = strchr(val, ';'))) {
398 done = 1;
399 key = val + strlen(val);
400 }
401 HTTP_COOKIE_FIXVAL();
402 HTTP_COOKIE_VAL(&array, keydup, val, vallen);
403 efree(keydup);
404 } while (!done);
405 }
406 return SUCCESS;
407 }
408 /* }}} */
409
410 /* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */
411 PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC)
412 {
413 char *key = NULL;
414 long idx = 0;
415 zval array;
416
417 Z_ARRVAL(array) = headers;
418
419 FOREACH_HASH_KEY(HTTP_SERVER_VARS, key, idx) {
420 if (key && !strncmp(key, "HTTP_", 5)) {
421 zval **header;
422
423 key += 5;
424 if (prettify) {
425 key = pretty_key(key, strlen(key), 1, 1);
426 }
427
428 zend_hash_get_current_data(HTTP_SERVER_VARS, (void **) &header);
429 add_assoc_stringl(&array, key, Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
430 key = NULL;
431 }
432 }
433 }
434 /* }}} */
435
436
437 /*
438 * Local variables:
439 * tab-width: 4
440 * c-basic-offset: 4
441 * End:
442 * vim600: noet sw=4 ts=4 fdm=marker
443 * vim<600: noet sw=4 ts=4
444 */
445