- move http_parse_cookie() code to an odd http_parse_key_list() function
[m6w6/ext-http] / http_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 #include "php_http_send_api.h"
31
32 #ifdef ZEND_ENGINE_2
33 # include "zend_exceptions.h"
34 # include "php_http_exception_object.h"
35 #endif
36
37 ZEND_EXTERN_MODULE_GLOBALS(http);
38
39 /* char *pretty_key(char *, size_t, zend_bool, zebd_bool) */
40 char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
41 {
42 if (key && key_len) {
43 unsigned i, wasalpha;
44 if (wasalpha = isalpha(key[0])) {
45 key[0] = uctitle ? toupper(key[0]) : tolower(key[0]);
46 }
47 for (i = 1; i < key_len; i++) {
48 if (isalpha(key[i])) {
49 key[i] = ((!wasalpha) && uctitle) ? toupper(key[i]) : tolower(key[i]);
50 wasalpha = 1;
51 } else {
52 if (xhyphen && (key[i] == '_')) {
53 key[i] = '-';
54 }
55 wasalpha = 0;
56 }
57 }
58 }
59 return key;
60 }
61 /* }}} */
62
63 /* {{{ */
64 void _http_key_list_default_decoder(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
65 {
66 *decoded = estrndup(encoded, encoded_len);
67 *decoded_len = (size_t) php_url_decode(*decoded, encoded_len);
68 }
69 /* }}} */
70
71 /* {{{ */
72 STATUS _http_parse_key_list(const char *list, HashTable *items, char separator, http_key_list_decode_t decode, zend_bool first_entry_is_name_value_pair TSRMLS_DC)
73 {
74 const char *key = list, *val = NULL;
75 int vallen = 0, keylen = 0, done = 0;
76 zval array;
77
78 Z_ARRVAL(array) = items;
79
80 if (!(val = strchr(list, '='))) {
81 return FAILURE;
82 }
83
84 #define HTTP_KEYLIST_VAL(array, k, str, len) \
85 { \
86 char *decoded; \
87 size_t decoded_len; \
88 if (decode) { \
89 decode(str, len, &decoded, &decoded_len TSRMLS_CC); \
90 } else { \
91 decoded = estrdup(str); \
92 decoded_len = len; \
93 } \
94 add_assoc_stringl(array, k, decoded, decoded_len, 0); \
95 }
96 #define HTTP_KEYLIST_FIXKEY() \
97 { \
98 while (isspace(*key)) ++key; \
99 keylen = val - key; \
100 while (isspace(key[keylen - 1])) --keylen; \
101 }
102 #define HTTP_KEYLIST_FIXVAL() \
103 { \
104 ++val; \
105 while (isspace(*val)) ++val; \
106 vallen = key - val; \
107 while (isspace(val[vallen - 1])) --vallen; \
108 }
109
110 HTTP_KEYLIST_FIXKEY();
111
112 if (first_entry_is_name_value_pair) {
113 HTTP_KEYLIST_VAL(&array, "name", key, keylen);
114
115 /* just one name=value */
116 if (!(key = strchr(val, separator))) {
117 key = val + strlen(val);
118 HTTP_KEYLIST_FIXVAL();
119 HTTP_KEYLIST_VAL(&array, "value", val, vallen);
120 goto list_done;
121 }
122 /* additional info appended */
123 else {
124 HTTP_KEYLIST_FIXVAL();
125 HTTP_KEYLIST_VAL(&array, "value", val, vallen);
126 }
127 }
128
129 do {
130 char *keydup = NULL;
131
132 if (!(val = strchr(key, '='))) {
133 break;
134 }
135
136 /* start at 0 if first_entry_is_name_value_pair==0 */
137 if (zend_hash_num_elements(items)) {
138 ++key;
139 }
140
141 HTTP_KEYLIST_FIXKEY();
142 keydup = estrndup(key, keylen);
143 if (!(key = strchr(val, separator))) {
144 done = 1;
145 key = val + strlen(val);
146 }
147 HTTP_KEYLIST_FIXVAL();
148 HTTP_KEYLIST_VAL(&array, keydup, val, vallen);
149 efree(keydup);
150 } while (!done);
151
152 list_done:
153 return SUCCESS;
154 }
155
156 /* {{{ void http_error(long, long, char*) */
157 void _http_error_ex(long type, long code, const char *format, ...)
158 {
159 va_list args;
160 TSRMLS_FETCH();
161
162 va_start(args, format);
163 if (type == E_THROW) {
164 #ifdef ZEND_ENGINE_2
165 char *message;
166 vspprintf(&message, 0, format, args);
167 zend_throw_exception(http_exception_get_for_code(code), message, code TSRMLS_CC);
168 #else
169 type = E_WARNING;
170 #endif
171 }
172 if (type != E_THROW) {
173 php_verror(NULL, "", type, format, args TSRMLS_CC);
174 }
175 va_end(args);
176 }
177 /* }}} */
178
179 /* {{{ STATUS http_exit(int, char*) */
180 STATUS _http_exit_ex(int status, char *header, zend_bool free_header TSRMLS_DC)
181 {
182 if (SUCCESS != http_send_status_header(status, header)) {
183 http_error_ex(E_WARNING, HTTP_E_HEADER, "Failed to exit with status/header: %d - %s", status, header ? header : "");
184 if (free_header && header) {
185 efree(header);
186 }
187 return FAILURE;
188 }
189 if (free_header && header) {
190 efree(header);
191 }
192 zend_bailout();
193 /* fake */
194 return SUCCESS;
195 }
196 /* }}} */
197
198 /* {{{ STATUS http_check_method(char *) */
199 STATUS _http_check_method_ex(const char *method, const char *methods)
200 {
201 const char *found;
202
203 if ( (found = strstr(methods, method)) &&
204 (found == method || !isalpha(found[-1])) &&
205 (!isalpha(found[strlen(method) + 1]))) {
206 return SUCCESS;
207 }
208 return FAILURE;
209 }
210 /* }}} */
211
212 /* {{{ zval *http_get_server_var_ex(char *, size_t) */
213 PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zend_bool check TSRMLS_DC)
214 {
215 zval **var;
216 if (SUCCESS == zend_hash_find(HTTP_SERVER_VARS, (char *) key, key_size, (void **) &var)) {
217 if (check) {
218 return Z_STRVAL_PP(var) && Z_STRLEN_PP(var) ? *var : NULL;
219 } else {
220 return *var;
221 }
222 }
223 return NULL;
224 }
225 /* }}} */
226
227
228 /* {{{ char *http_chunked_decode(char *, size_t, char **, size_t *) */
229 PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encoded_len,
230 char **decoded, size_t *decoded_len TSRMLS_DC)
231 {
232 const char *e_ptr;
233 char *d_ptr;
234
235 *decoded_len = 0;
236 *decoded = ecalloc(1, encoded_len);
237 d_ptr = *decoded;
238 e_ptr = encoded;
239
240 while (((e_ptr - encoded) - encoded_len) > 0) {
241 int no_crlf = 0;
242 char *n_ptr;
243 size_t chunk_len = 0;
244
245 chunk_len = strtol(e_ptr, &n_ptr, 16);
246
247 /* check if:
248 * - we could not read in chunk size
249 * - chunk size is not followed by HTTP_CRLF|NUL
250 */
251 if ((n_ptr == e_ptr) || (*n_ptr && (no_crlf = strncmp(n_ptr, HTTP_CRLF, lenof(HTTP_CRLF))))) {
252 /* don't fail on apperently not encoded data */
253 if (e_ptr == encoded) {
254 memcpy(*decoded, encoded, encoded_len);
255 *decoded_len = encoded_len;
256 return encoded + encoded_len;
257 } else {
258 efree(*decoded);
259 if (no_crlf) {
260 http_error_ex(E_WARNING, HTTP_E_PARSE, "Invalid character (expected 0x0D 0x0A; got: 0x%x 0x%x)", *n_ptr, *(n_ptr + 1));
261 } else {
262 char *error = estrndup(n_ptr, strcspn(n_ptr, "\r\n \0"));
263 http_error_ex(E_WARNING, HTTP_E_PARSE, "Invalid chunk size: '%s' at pos %d", error, n_ptr - encoded);
264 efree(error);
265 }
266
267 return NULL;
268 }
269 } else {
270 e_ptr = n_ptr;
271 }
272
273 /* reached the end */
274 if (!chunk_len) {
275 break;
276 }
277
278 memcpy(d_ptr, e_ptr += 2, chunk_len);
279 d_ptr += chunk_len;
280 e_ptr += chunk_len + 2;
281 *decoded_len += chunk_len;
282 }
283
284 return e_ptr;
285 }
286 /* }}} */
287
288 /* {{{ STATUS http_split_response(zval *, zval *, zval *) */
289 PHP_HTTP_API STATUS _http_split_response(zval *response, zval *headers, zval *body TSRMLS_DC)
290 {
291 char *b = NULL;
292 size_t l = 0;
293 STATUS status = http_split_response_ex(Z_STRVAL_P(response), Z_STRLEN_P(response), Z_ARRVAL_P(headers), &b, &l);
294 ZVAL_STRINGL(body, b, l, 0);
295 return status;
296 }
297 /* }}} */
298
299 /* {{{ STATUS http_split_response(char *, size_t, HashTable *, char **, size_t *) */
300 PHP_HTTP_API STATUS _http_split_response_ex(char *response, size_t response_len,
301 HashTable *headers, char **body, size_t *body_len TSRMLS_DC)
302 {
303 char *header = response, *real_body = NULL;
304
305 while (0 < (response_len - (response - header + 4))) {
306 if ( (*response++ == '\r') &&
307 (*response++ == '\n') &&
308 (*response++ == '\r') &&
309 (*response++ == '\n')) {
310 real_body = response;
311 break;
312 }
313 }
314
315 if (real_body && (*body_len = (response_len - (real_body - header)))) {
316 *body = ecalloc(1, *body_len + 1);
317 memcpy(*body, real_body, *body_len);
318 }
319
320 return http_parse_headers_ex(header, headers, 1);
321 }
322 /* }}} */
323
324 /*
325 * Local variables:
326 * tab-width: 4
327 * c-basic-offset: 4
328 * End:
329 * vim600: noet sw=4 ts=4 fdm=marker
330 * vim<600: noet sw=4 ts=4
331 */
332