* more modular file structure
[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 "php_output.h"
26 #include "ext/standard/md5.h"
27
28 #include "php_http.h"
29 #include "php_http_std_defs.h"
30 #include "php_http_api.h"
31 #include "php_http_send_api.h"
32 #include "php_http_cache_api.h"
33 #include "php_http_headers_api.h"
34
35 ZEND_EXTERN_MODULE_GLOBALS(http);
36
37 /* char *pretty_key(char *, size_t, zend_bool, zebd_bool) */
38 char *pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen)
39 {
40 if (key && key_len) {
41 unsigned i, wasalpha;
42 if (wasalpha = isalpha(key[0])) {
43 key[0] = uctitle ? toupper(key[0]) : tolower(key[0]);
44 }
45 for (i = 1; i < key_len; i++) {
46 if (isalpha(key[i])) {
47 key[i] = ((!wasalpha) && uctitle) ? toupper(key[i]) : tolower(key[i]);
48 wasalpha = 1;
49 } else {
50 if (xhyphen && (key[i] == '_')) {
51 key[i] = '-';
52 }
53 wasalpha = 0;
54 }
55 }
56 }
57 return key;
58 }
59 /* }}} */
60
61 /* {{{ static STATUS http_ob_stack_get(php_ob_buffer *, php_ob_buffer **) */
62 static STATUS http_ob_stack_get(php_ob_buffer *o, php_ob_buffer **s)
63 {
64 static int i = 0;
65 php_ob_buffer *b = emalloc(sizeof(php_ob_buffer));
66 b->handler_name = estrdup(o->handler_name);
67 b->buffer = estrndup(o->buffer, o->text_length);
68 b->text_length = o->text_length;
69 b->chunk_size = o->chunk_size;
70 b->erase = o->erase;
71 s[i++] = b;
72 return SUCCESS;
73 }
74 /* }}} */
75
76 /* {{{ zval *http_get_server_var_ex(char *, size_t) */
77 PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zend_bool check TSRMLS_DC)
78 {
79 zval **var;
80 if (SUCCESS == zend_hash_find(HTTP_SERVER_VARS, (char *) key, key_size, (void **) &var)) {
81 if (check) {
82 return Z_STRVAL_PP(var) && Z_STRLEN_PP(var) ? *var : NULL;
83 } else {
84 return *var;
85 }
86 }
87 return NULL;
88 }
89 /* }}} */
90
91 /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */
92 PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len,
93 char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
94 {
95 char etag[33] = { 0 };
96 unsigned char digest[16];
97
98 if (mode & PHP_OUTPUT_HANDLER_START) {
99 PHP_MD5Init(&HTTP_G(etag_md5));
100 }
101
102 PHP_MD5Update(&HTTP_G(etag_md5), output, output_len);
103
104 if (mode & PHP_OUTPUT_HANDLER_END) {
105 PHP_MD5Final(digest, &HTTP_G(etag_md5));
106
107 /* just do that if desired */
108 if (HTTP_G(etag_started)) {
109 make_digest(etag, digest);
110
111 if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) {
112 http_send_status(304);
113 zend_bailout();
114 } else {
115 http_send_etag(etag, 32);
116 }
117 }
118 }
119
120 *handled_output_len = output_len;
121 *handled_output = estrndup(output, output_len);
122 }
123 /* }}} */
124
125 /* {{{ STATUS http_start_ob_handler(php_output_handler_func_t, char *, uint, zend_bool) */
126 PHP_HTTP_API STATUS _http_start_ob_handler(php_output_handler_func_t handler_func,
127 char *handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
128 {
129 php_ob_buffer **stack;
130 int count, i;
131
132 if (count = OG(ob_nesting_level)) {
133 stack = ecalloc(count, sizeof(php_ob_buffer *));
134
135 if (count > 1) {
136 zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP,
137 (int (*)(void *elem, void *)) http_ob_stack_get, stack);
138 }
139
140 if (count > 0) {
141 http_ob_stack_get(&OG(active_ob_buffer), stack);
142 }
143
144 while (OG(ob_nesting_level)) {
145 php_end_ob_buffer(0, 0 TSRMLS_CC);
146 }
147 }
148
149 php_ob_set_internal_handler(handler_func, chunk_size, handler_name, erase TSRMLS_CC);
150
151 for (i = 0; i < count; i++) {
152 php_ob_buffer *s = stack[i];
153 if (strcmp(s->handler_name, "default output handler")) {
154 php_start_ob_buffer_named(s->handler_name, s->chunk_size, s->erase TSRMLS_CC);
155 }
156 php_body_write(s->buffer, s->text_length TSRMLS_CC);
157 efree(s->handler_name);
158 efree(s->buffer);
159 efree(s);
160 }
161 if (count) {
162 efree(stack);
163 }
164
165 return SUCCESS;
166 }
167 /* }}} */
168
169 /* {{{ STATUS http_chunked_decode(char *, size_t, char **, size_t *) */
170 PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, size_t encoded_len,
171 char **decoded, size_t *decoded_len TSRMLS_DC)
172 {
173 const char *e_ptr;
174 char *d_ptr;
175
176 *decoded_len = 0;
177 *decoded = ecalloc(1, encoded_len);
178 d_ptr = *decoded;
179 e_ptr = encoded;
180
181 while (((e_ptr - encoded) - encoded_len) > 0) {
182 char hex_len[9] = {0};
183 size_t chunk_len = 0;
184 int i = 0;
185
186 /* read in chunk size */
187 while (isxdigit(*e_ptr)) {
188 if (i == 9) {
189 php_error_docref(NULL TSRMLS_CC, E_WARNING,
190 "Chunk size is too long: 0x%s...", hex_len);
191 efree(*decoded);
192 return FAILURE;
193 }
194 hex_len[i++] = *e_ptr++;
195 }
196
197 /* reached the end */
198 if (!strcmp(hex_len, "0")) {
199 break;
200 }
201
202 /* new line */
203 if (strncmp(e_ptr, HTTP_CRLF, 2)) {
204 php_error_docref(NULL TSRMLS_CC, E_WARNING,
205 "Invalid character (expected 0x0D 0x0A; got: %x %x)",
206 *e_ptr, *(e_ptr + 1));
207 efree(*decoded);
208 return FAILURE;
209 }
210
211 /* hex to long */
212 {
213 char *error = NULL;
214 chunk_len = strtol(hex_len, &error, 16);
215 if (error == hex_len) {
216 php_error_docref(NULL TSRMLS_CC, E_WARNING,
217 "Invalid chunk size string: '%s'", hex_len);
218 efree(*decoded);
219 return FAILURE;
220 }
221 }
222
223 memcpy(d_ptr, e_ptr += 2, chunk_len);
224 d_ptr += chunk_len;
225 e_ptr += chunk_len + 2;
226 *decoded_len += chunk_len;
227 }
228
229 return SUCCESS;
230 }
231 /* }}} */
232
233 /* {{{ STATUS http_split_response(zval *, zval *, zval *) */
234 PHP_HTTP_API STATUS _http_split_response(zval *response, zval *headers, zval *body TSRMLS_DC)
235 {
236 char *b = NULL;
237 size_t l = 0;
238 STATUS status = http_split_response_ex(Z_STRVAL_P(response), Z_STRLEN_P(response), Z_ARRVAL_P(headers), &b, &l);
239 ZVAL_STRINGL(body, b, l, 0);
240 return status;
241 }
242 /* }}} */
243
244 /* {{{ STATUS http_split_response(char *, size_t, HashTable *, char **, size_t *) */
245 PHP_HTTP_API STATUS _http_split_response_ex(char *response, size_t response_len,
246 HashTable *headers, char **body, size_t *body_len TSRMLS_DC)
247 {
248 char *header = response, *real_body = NULL;
249
250 while (0 < (response_len - (response - header + 4))) {
251 if ( (*response++ == '\r') &&
252 (*response++ == '\n') &&
253 (*response++ == '\r') &&
254 (*response++ == '\n')) {
255 real_body = response;
256 break;
257 }
258 }
259
260 if (real_body && (*body_len = (response_len - (real_body - header)))) {
261 *body = ecalloc(1, *body_len + 1);
262 memcpy(*body, real_body, *body_len);
263 }
264
265 return http_parse_headers_ex(header, real_body ? response_len - *body_len : response_len, headers, 1);
266 }
267 /* }}} */
268
269 /*
270 * Local variables:
271 * tab-width: 4
272 * c-basic-offset: 4
273 * End:
274 * vim600: noet sw=4 ts=4 fdm=marker
275 * vim<600: noet sw=4 ts=4
276 */
277