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