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