2e6e5934b0414b0bdac7f8413e4bbb1a91b4b101
[m6w6/ext-http] / http.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 #define _WINSOCKAPI_
19 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "snprintf.h"
28 #include "ext/standard/info.h"
29 #include "ext/session/php_session.h"
30 #include "ext/standard/php_string.h"
31 #include "ext/standard/php_smart_str.h"
32
33 #include "SAPI.h"
34
35 #include "php_http.h"
36 #include "php_http_api.h"
37
38 #ifdef ZEND_ENGINE_2
39 # include "ext/standard/php_http.h"
40 #endif
41
42 #ifdef HTTP_HAVE_CURL
43
44 # ifdef PHP_WIN32
45 # include <winsock2.h>
46 # include <sys/types.h>
47 # endif
48
49 # include <curl/curl.h>
50
51 /* {{{ ARG_INFO */
52 # ifdef ZEND_BEGIN_ARG_INFO
53 ZEND_BEGIN_ARG_INFO(http_request_info_ref_3, 0)
54 ZEND_ARG_PASS_INFO(0)
55 ZEND_ARG_PASS_INFO(0)
56 ZEND_ARG_PASS_INFO(1)
57 ZEND_END_ARG_INFO();
58
59 ZEND_BEGIN_ARG_INFO(http_request_info_ref_4, 0)
60 ZEND_ARG_PASS_INFO(0)
61 ZEND_ARG_PASS_INFO(0)
62 ZEND_ARG_PASS_INFO(0)
63 ZEND_ARG_PASS_INFO(1)
64 ZEND_END_ARG_INFO();
65 # else
66 static unsigned char http_request_info_ref_3[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE};
67 static unsigned char http_request_info_ref_4[] = {4, BYREF_NONE, BYREF_NONE, BYREF_NONE, BYREF_FORCE};
68 # endif
69 /* }}} ARG_INFO */
70
71 #endif /* HTTP_HAVE_CURL */
72
73 ZEND_DECLARE_MODULE_GLOBALS(http)
74
75 #ifdef COMPILE_DL_HTTP
76 ZEND_GET_MODULE(http)
77 #endif
78
79 /* {{{ http_functions[] */
80 function_entry http_functions[] = {
81 PHP_FE(http_date, NULL)
82 PHP_FE(http_absolute_uri, NULL)
83 PHP_FE(http_negotiate_language, NULL)
84 PHP_FE(http_negotiate_charset, NULL)
85 PHP_FE(http_redirect, NULL)
86 PHP_FE(http_send_status, NULL)
87 PHP_FE(http_send_last_modified, NULL)
88 PHP_FE(http_send_content_type, NULL)
89 PHP_FE(http_send_content_disposition, NULL)
90 PHP_FE(http_match_modified, NULL)
91 PHP_FE(http_match_etag, NULL)
92 PHP_FE(http_cache_last_modified, NULL)
93 PHP_FE(http_cache_etag, NULL)
94 PHP_FE(http_send_data, NULL)
95 PHP_FE(http_send_file, NULL)
96 PHP_FE(http_send_stream, NULL)
97 PHP_FE(http_chunked_decode, NULL)
98 PHP_FE(http_split_response, NULL)
99 PHP_FE(http_parse_headers, NULL)
100 PHP_FE(http_get_request_headers, NULL)
101 #ifdef HTTP_HAVE_CURL
102 PHP_FE(http_get, http_request_info_ref_3)
103 PHP_FE(http_head, http_request_info_ref_3)
104 PHP_FE(http_post_data, http_request_info_ref_4)
105 PHP_FE(http_post_array, http_request_info_ref_4)
106 #endif
107 PHP_FE(http_auth_basic, NULL)
108 PHP_FE(http_auth_basic_cb, NULL)
109 #ifndef ZEND_ENGINE_2
110 PHP_FE(http_build_query, NULL)
111 #endif
112 PHP_FE(ob_httpetaghandler, NULL)
113 {NULL, NULL, NULL}
114 };
115 /* }}} */
116
117 #define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v))
118 #define HASH_ORNULL(z) ((z) ? Z_ARRVAL_P(z) : NULL)
119 #define NO_ARGS if (ZEND_NUM_ARGS()) WRONG_PARAM_COUNT
120 #define HTTP_URL_ARGSEP_OVERRIDE zend_alter_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, "&", 1, ZEND_INI_ALL, ZEND_INI_STAGE_RUNTIME)
121 #define HTTP_URL_ARGSEP_RESTORE zend_restore_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, ZEND_INI_STAGE_RUNTIME)
122
123 #define array_copy(src, dst) zend_hash_copy(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *))
124 #define array_merge(src, dst) zend_hash_merge(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1)
125
126 #ifdef ZEND_ENGINE_2
127
128 # define HTTP_REGISTER_CLASS_EX(classname, name, parent, flags) \
129 { \
130 zend_class_entry ce; \
131 INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \
132 ce.create_object = name## _new_object; \
133 name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \
134 name## _ce->ce_flags |= flags; \
135 memcpy(& name## _object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
136 name## _object_handlers.clone_obj = NULL; \
137 name## _declare_default_properties(name## _ce); \
138 }
139
140 # define HTTP_REGISTER_CLASS(classname, name, parent, flags) \
141 { \
142 zend_class_entry ce; \
143 INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \
144 ce.create_object = NULL; \
145 name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \
146 name## _ce->ce_flags |= flags; \
147 }
148
149 # define getObject(t, o) t * o = ((t *) zend_object_store_get_object(getThis() TSRMLS_CC))
150 # define OBJ_PROP(o) o->zo.properties
151 # define DCL_PROP(a, t, n, v) zend_declare_property_ ##t(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC)
152 # define DCL_PROP_Z(a, n, v) zend_declare_property(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC)
153 # define DCL_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n), (ZEND_ACC_ ##a) TSRMLS_CC)
154 # define UPD_PROP(o, t, n, v) zend_update_property_ ##t(o->zo.ce, getThis(), (#n), sizeof(#n), (v) TSRMLS_CC)
155 # define SET_PROP(o, n, z) zend_update_property(o->zo.ce, getThis(), (#n), sizeof(#n), (z) TSRMLS_CC)
156 # define GET_PROP(o, n) zend_read_property(o->zo.ce, getThis(), (#n), sizeof(#n), 0 TSRMLS_CC)
157
158 # define INIT_PARR(o, n) \
159 { \
160 zval *__tmp; \
161 MAKE_STD_ZVAL(__tmp); \
162 array_init(__tmp); \
163 SET_PROP(o, n, __tmp); \
164 o->n = __tmp; \
165 }
166
167 # define FREE_PARR(p) \
168 if (p) { \
169 zval_dtor(p); \
170 FREE_ZVAL(p); \
171 (p) = NULL; \
172 }
173
174 /* {{{ HTTPi */
175
176 zend_class_entry *httpi_ce;
177
178 #define HTTPi_ME(me, al, ai) ZEND_FENTRY(me, ZEND_FN(al), ai, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
179
180 zend_function_entry httpi_class_methods[] = {
181 HTTPi_ME(date, http_date, NULL)
182 HTTPi_ME(absoluteURI, http_absolute_uri, NULL)
183 HTTPi_ME(negotiateLanguage, http_negotiate_language, NULL)
184 HTTPi_ME(negotiateCharset, http_negotiate_charset, NULL)
185 HTTPi_ME(redirect, http_redirect, NULL)
186 HTTPi_ME(sendStatus, http_send_status, NULL)
187 HTTPi_ME(sendLastModified, http_send_last_modified, NULL)
188 HTTPi_ME(sendContentType, http_send_content_type, NULL)
189 HTTPi_ME(sendContentDisposition, http_send_content_disposition, NULL)
190 HTTPi_ME(matchModified, http_match_modified, NULL)
191 HTTPi_ME(matchEtag, http_match_etag, NULL)
192 HTTPi_ME(cacheLastModified, http_cache_last_modified, NULL)
193 HTTPi_ME(cacheEtag, http_cache_etag, NULL)
194 HTTPi_ME(chunkedDecode, http_chunked_decode, NULL)
195 HTTPi_ME(splitResponse, http_split_response, NULL)
196 HTTPi_ME(parseHeaders, http_parse_headers, NULL)
197 HTTPi_ME(getRequestHeaders, http_get_request_headers, NULL)
198 #ifdef HTTP_HAVE_CURL
199 HTTPi_ME(get, http_get, http_request_info_ref_3)
200 HTTPi_ME(head, http_head, http_request_info_ref_3)
201 HTTPi_ME(postData, http_post_data, http_request_info_ref_4)
202 HTTPi_ME(postArray, http_post_array, http_request_info_ref_4)
203 #endif
204 HTTPi_ME(authBasic, http_auth_basic, NULL)
205 HTTPi_ME(authBasicCallback, http_auth_basic_cb, NULL)
206 {NULL, NULL, NULL}
207 };
208 /* }}} HTTPi */
209
210 /* {{{ HTTPi_Response */
211
212 zend_class_entry *httpi_response_ce;
213 static zend_object_handlers httpi_response_object_handlers;
214
215 typedef struct {
216 zend_object zo;
217 } httpi_response_object;
218
219 #define httpi_response_declare_default_properties(ce) _httpi_response_declare_default_properties(ce TSRMLS_CC)
220 static inline void _httpi_response_declare_default_properties(zend_class_entry *ce TSRMLS_DC)
221 {
222 DCL_PROP(PROTECTED, string, contentType, "application/x-octetstream");
223 DCL_PROP(PROTECTED, string, eTag, "");
224 DCL_PROP(PROTECTED, string, dispoFile, "");
225 DCL_PROP(PROTECTED, string, cacheControl, "public");
226 DCL_PROP(PROTECTED, string, data, "");
227 DCL_PROP(PROTECTED, string, file, "");
228 DCL_PROP(PROTECTED, long, stream, 0);
229 DCL_PROP(PROTECTED, long, lastModified, 0);
230 DCL_PROP(PROTECTED, long, dispoInline, 0);
231 DCL_PROP(PROTECTED, long, cache, 0);
232 DCL_PROP(PROTECTED, long, gzip, 0);
233
234 DCL_PROP(PRIVATE, long, raw_cache_header, 0);
235 DCL_PROP(PRIVATE, long, send_mode, -1);
236 }
237
238 #define httpi_response_destroy_object _httpi_response_destroy_object
239 void _httpi_response_destroy_object(void *object, zend_object_handle handle TSRMLS_DC)
240 {
241 httpi_response_object *o = object;
242 if (OBJ_PROP(o)) {
243 zend_hash_destroy(OBJ_PROP(o));
244 FREE_HASHTABLE(OBJ_PROP(o));
245 }
246 efree(o);
247 }
248
249 #define httpi_response_new_object _httpi_response_new_object
250 zend_object_value _httpi_response_new_object(zend_class_entry *ce TSRMLS_DC)
251 {
252 zend_object_value ov;
253 httpi_response_object *o;
254
255 o = ecalloc(sizeof(httpi_response_object), 1);
256 o->zo.ce = ce;
257
258 ALLOC_HASHTABLE(OBJ_PROP(o));
259 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
260 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
261
262 ov.handle = zend_objects_store_put(o, httpi_response_destroy_object, NULL, NULL TSRMLS_CC);
263 ov.handlers = &httpi_response_object_handlers;
264
265 return ov;
266 }
267
268 zend_function_entry httpi_response_class_methods[] = {
269 PHP_ME(HTTPi_Response, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
270 /* PHP_ME(HTTPi_Response, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
271 */
272 PHP_ME(HTTPi_Response, setETag, NULL, ZEND_ACC_PUBLIC)
273 PHP_ME(HTTPi_Response, getETag, NULL, ZEND_ACC_PUBLIC)
274
275 PHP_ME(HTTPi_Response, setContentDisposition, NULL, ZEND_ACC_PUBLIC)
276 PHP_ME(HTTPi_Response, getContentDisposition, NULL, ZEND_ACC_PUBLIC)
277
278 PHP_ME(HTTPi_Response, setContentType, NULL, ZEND_ACC_PUBLIC)
279 PHP_ME(HTTPi_Response, getContentType, NULL, ZEND_ACC_PUBLIC)
280
281 PHP_ME(HTTPi_Response, setCache, NULL, ZEND_ACC_PUBLIC)
282 PHP_ME(HTTPi_Response, getCache, NULL, ZEND_ACC_PUBLIC)
283
284 PHP_ME(HTTPi_Response, setCacheControl, NULL, ZEND_ACC_PUBLIC)
285 PHP_ME(HTTPi_Response, getCacheControl, NULL, ZEND_ACC_PUBLIC)
286
287 PHP_ME(HTTPi_Response, setGzip, NULL, ZEND_ACC_PUBLIC)
288 PHP_ME(HTTPi_Response, getGzip, NULL, ZEND_ACC_PUBLIC)
289
290 PHP_ME(HTTPi_Response, setData, NULL, ZEND_ACC_PUBLIC)
291 PHP_ME(HTTPi_Response, getData, NULL, ZEND_ACC_PUBLIC)
292
293 PHP_ME(HTTPi_Response, setFile, NULL, ZEND_ACC_PUBLIC)
294 PHP_ME(HTTPi_Response, getFile, NULL, ZEND_ACC_PUBLIC)
295
296 PHP_ME(HTTPi_Response, setStream, NULL, ZEND_ACC_PUBLIC)
297 PHP_ME(HTTPi_Response, getStream, NULL, ZEND_ACC_PUBLIC)
298
299 PHP_ME(HTTPi_Response, send, NULL, ZEND_ACC_PUBLIC)
300
301 {NULL, NULL, NULL}
302 };
303
304 /* {{{ proto void HTTPi_Response::__construct(bool cache, bool gzip)
305 *
306 */
307 PHP_METHOD(HTTPi_Response, __construct)
308 {
309 zend_bool do_cache = 0, do_gzip = 0;
310 getObject(httpi_response_object, obj);
311
312 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &do_cache, &do_gzip)) {
313 // throw exception
314 return;
315 }
316
317 UPD_PROP(obj, long, cache, do_cache);
318 UPD_PROP(obj, long, gzip, do_gzip);
319 }
320 /* }}} */
321
322 /* {{{ proto bool HTTPi_Response::setCache(bool cache)
323 *
324 */
325 PHP_METHOD(HTTPi_Response, setCache)
326 {
327 zend_bool do_cache = 0;
328 getObject(httpi_response_object, obj);
329
330 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
331 RETURN_FALSE;
332 }
333
334 UPD_PROP(obj, long, cache, do_cache);
335 RETURN_TRUE;
336 }
337 /* }}} */
338
339 /* {{{ proto bool HTTPi_Response::getCache()
340 *
341 */
342 PHP_METHOD(HTTPi_Response, getCache)
343 {
344 zval *do_cache = NULL;
345 getObject(httpi_response_object, obj);
346
347 NO_ARGS;
348
349 do_cache = GET_PROP(obj, cache);
350 RETURN_BOOL(Z_LVAL_P(do_cache));
351 }
352 /* }}}*/
353
354 /* {{{ proto bool HTTPi_Response::setGzip(bool gzip)
355 *
356 */
357 PHP_METHOD(HTTPi_Response, setGzip)
358 {
359 zend_bool do_gzip = 0;
360 getObject(httpi_response_object, obj);
361
362 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
363 RETURN_FALSE;
364 }
365
366 UPD_PROP(obj, long, gzip, do_gzip);
367 RETURN_TRUE;
368 }
369 /* }}} */
370
371 /* {{{ proto bool HTTPi_Response::getGzip()
372 *
373 */
374 PHP_METHOD(HTTPi_Response, getGzip)
375 {
376 zval *do_gzip = NULL;
377 getObject(httpi_response_object, obj);
378
379 NO_ARGS;
380
381 do_gzip = GET_PROP(obj, gzip);
382 RETURN_BOOL(Z_LVAL_P(do_gzip));
383 }
384 /* }}} */
385
386 /* {{{ proto bool HTTPi_Response::setCacheControl(string control[, bool raw = false])
387 *
388 */
389 PHP_METHOD(HTTPi_Response, setCacheControl)
390 {
391 char *ccontrol;
392 int cc_len;
393 zend_bool raw = 0;
394 getObject(httpi_response_object, obj);
395
396 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &ccontrol, &cc_len, &raw)) {
397 RETURN_FALSE;
398 }
399
400 if ((!raw) && (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache"))) {
401 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
402 RETURN_FALSE;
403 }
404
405 UPD_PROP(obj, long, raw_cache_header, raw);
406 UPD_PROP(obj, string, cacheControl, ccontrol);
407 RETURN_TRUE;
408 }
409 /* }}} */
410
411 /* {{{ proto string HTTPi_Response::getCacheControl()
412 *
413 */
414 PHP_METHOD(HTTPi_Response, getCacheControl)
415 {
416 zval *ccontrol;
417 getObject(httpi_response_object, obj);
418
419 NO_ARGS;
420
421 ccontrol = GET_PROP(obj, cacheControl);
422 RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1);
423 }
424 /* }}} */
425
426 /* {{{ proto bool HTTPi::setContentType(string content_type)
427 *
428 */
429 PHP_METHOD(HTTPi_Response, setContentType)
430 {
431 char *ctype;
432 int ctype_len;
433 getObject(httpi_response_object, obj);
434
435 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
436 RETURN_FALSE;
437 }
438
439 if (!strchr(ctype, '/')) {
440 php_error_docref(NULL TSRMLS_CC, E_WARNING,
441 "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype);
442 RETURN_FALSE;
443 }
444
445 UPD_PROP(obj, string, contentType, ctype);
446
447 RETURN_TRUE;
448 }
449 /* }}} */
450
451 /* {{{ proto string HTTPi_Response::getContentType()
452 *
453 */
454 PHP_METHOD(HTTPi_Response, getContentType)
455 {
456 zval *ctype;
457 getObject(httpi_response_object, obj);
458
459 NO_ARGS;
460
461 ctype = GET_PROP(obj, contentType);
462 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
463 }
464 /* }}} */
465
466 /* {{{ proto bool HTTPi_Response::setContentDisposition(string filename[, bool inline = false])
467 *
468 */
469 PHP_METHOD(HTTPi_Response, setContentDisposition)
470 {
471 char *file;
472 int file_len;
473 zend_bool is_inline = 0;
474 getObject(httpi_response_object, obj);
475
476 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &is_inline)) {
477 RETURN_FALSE;
478 }
479
480 UPD_PROP(obj, string, dispoFile, file);
481 UPD_PROP(obj, long, dispoInline, is_inline);
482 RETURN_TRUE;
483 }
484 /* }}} */
485
486 /* {{{ proto array HTTPi_Response::getContentDisposition()
487 *
488 */
489 PHP_METHOD(HTTPi_Response, getContentDisposition)
490 {
491 zval *file;
492 zval *is_inline;
493 getObject(httpi_response_object, obj);
494
495 if (ZEND_NUM_ARGS()) {
496 WRONG_PARAM_COUNT;
497 }
498
499 file = GET_PROP(obj, dispoFile);
500 is_inline = GET_PROP(obj, dispoInline);
501
502 array_init(return_value);
503 add_assoc_stringl(return_value, "filename", Z_STRVAL_P(file), Z_STRLEN_P(file), 1);
504 add_assoc_bool(return_value, "inline", Z_LVAL_P(is_inline));
505 }
506 /* }}} */
507
508 /* {{{ proto bool HTTPi_Response::setETag(string etag)
509 *
510 */
511 PHP_METHOD(HTTPi_Response, setETag)
512 {
513 char *etag;
514 int etag_len;
515 getObject(httpi_response_object, obj);
516
517 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
518 RETURN_FALSE;
519 }
520
521 UPD_PROP(obj, string, eTag, etag);
522 RETURN_TRUE;
523 }
524 /* }}} */
525
526 /* {{{ proto string HTTPi_Response::getETag()
527 *
528 */
529 PHP_METHOD(HTTPi_Response, getETag)
530 {
531 zval *etag;
532 getObject(httpi_response_object, obj);
533
534 NO_ARGS;
535
536 etag = GET_PROP(obj, eTag);
537 RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1);
538 }
539 /* }}} */
540
541 /* {{{ proto bool HTTPi_Response::setData(string data)
542 *
543 */
544 PHP_METHOD(HTTPi_Response, setData)
545 {
546 zval *the_data;
547 char *etag;
548 getObject(httpi_response_object, obj);
549
550 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
551 RETURN_FALSE;
552 }
553
554 convert_to_string_ex(&the_data);
555 SET_PROP(obj, data, the_data);
556 UPD_PROP(obj, long, lastModified, http_lmod(the_data, SEND_DATA));
557 UPD_PROP(obj, long, send_mode, SEND_DATA);
558 RETURN_TRUE;
559 }
560 /* }}} */
561
562 /* {{{ proto string HTTPi_Response::getData()
563 *
564 */
565 PHP_METHOD(HTTPi_Response, getData)
566 {
567 zval *the_data;
568 getObject(httpi_response_object, obj);
569
570 NO_ARGS;
571
572 the_data = GET_PROP(obj, data);
573 RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1);
574 }
575 /* }}} */
576
577 /* {{{ proto bool HTTPi_Response::setStream(resource stream)
578 *
579 */
580 PHP_METHOD(HTTPi_Response, setStream)
581 {
582 zval *the_stream;
583 php_stream *the_real_stream;
584 char *etag;
585 getObject(httpi_response_object, obj);
586
587 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
588 RETURN_FALSE;
589 }
590
591 php_stream_from_zval(the_real_stream, &the_stream);
592
593 SET_PROP(obj, stream, the_stream);
594 UPD_PROP(obj, long, lastModified, http_lmod(the_real_stream, SEND_RSRC));
595 UPD_PROP(obj, long, send_mode, SEND_RSRC);
596 RETURN_TRUE;
597 }
598 /* }}} */
599
600 /* {{{ proto resource HTTPi_Response::getStream()
601 *
602 */
603 PHP_METHOD(HTTPi_Response, getStream)
604 {
605 zval *the_stream;
606 getObject(httpi_response_object, obj);
607
608 NO_ARGS;
609
610 the_stream = GET_PROP(obj, stream);
611 RETURN_RESOURCE(Z_LVAL_P(the_stream));
612 }
613 /* }}} */
614
615 /* {{{ proto bool HTTPi_Response::setFile(string file)
616 *
617 */
618 PHP_METHOD(HTTPi_Response, setFile)
619 {
620 zval *the_file;
621 getObject(httpi_response_object, obj);
622
623 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_file)) {
624 RETURN_FALSE;
625 }
626
627 convert_to_string_ex(&the_file);
628
629 UPD_PROP(obj, string, file, Z_STRVAL_P(the_file));
630 UPD_PROP(obj, long, lastModified, http_lmod(the_file, -1));
631 UPD_PROP(obj, long, send_mode, -1);
632 RETURN_TRUE;
633 }
634 /* }}} */
635
636 /* {{{ proto string HTTPi_Response::getFile()
637 *
638 */
639 PHP_METHOD(HTTPi_Response, getFile)
640 {
641 zval *the_file;
642 getObject(httpi_response_object, obj);
643
644 NO_ARGS;
645
646 the_file = GET_PROP(obj, file);
647 RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1);
648 }
649 /* }}} */
650
651 PHP_METHOD(HTTPi_Response, send)
652 {
653 zval *do_cache, *do_gzip;
654 getObject(httpi_response_object, obj);
655
656 do_cache = GET_PROP(obj, cache);
657 do_gzip = GET_PROP(obj, gzip);
658
659 /* caching */
660 if (Z_LVAL_P(do_cache)) {
661 zval *cctrl, *etag, *lmod, *ccraw;
662
663 etag = GET_PROP(obj, eTag);
664 lmod = GET_PROP(obj, lastModified);
665 cctrl = GET_PROP(obj, cacheControl);
666 ccraw = GET_PROP(obj, raw_cache_header);
667
668 if (Z_LVAL_P(ccraw)) {
669 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl));
670 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl));
671 } else {
672 char cc_header[42] = {0};
673 sprintf(cc_header, "%s, must-revalidate, max-age=0", Z_STRVAL_P(cctrl));
674 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), cc_header, strlen(cc_header));
675 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), cc_header, strlen(cc_header));
676 }
677 }
678
679 /* gzip */
680 if (Z_LVAL_P(do_gzip)) {
681 /* ... */
682 }
683
684 /* content type */
685 {
686 zval *ctype = GET_PROP(obj, contentType);
687 if (Z_STRLEN_P(ctype)) {
688 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
689 } else {
690 http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1);
691 }
692 }
693
694 /* content disposition */
695 {
696 zval *dispo_file = GET_PROP(obj, dispoFile);
697 if (Z_STRLEN_P(dispo_file)) {
698 zval *dispo_inline = GET_PROP(obj, dispoInline);
699 http_send_content_disposition(Z_STRVAL_P(dispo_file), Z_STRLEN_P(dispo_file), Z_LVAL_P(dispo_inline));
700 }
701 }
702
703 /* send */
704 {
705 zval *send_mode = GET_PROP(obj, send_mode);
706 switch (Z_LVAL_P(send_mode))
707 {
708 case SEND_DATA:
709 {
710 RETURN_SUCCESS(http_send_data(GET_PROP(obj, data)));
711 }
712
713 case SEND_RSRC:
714 {
715 php_stream *the_real_stream;
716 zval *the_stream = GET_PROP(obj, stream);
717 php_stream_from_zval(the_real_stream, &the_stream);
718 RETURN_SUCCESS(http_send_stream(the_real_stream));
719 }
720
721 default:
722 {
723 RETURN_SUCCESS(http_send_file(GET_PROP(obj, file)));
724 }
725 }
726 }
727 }
728 /* }}} */
729
730 /* {{{ HTTPi_Request */
731 #ifdef HTTP_HAVE_CURL
732
733 zend_class_entry *httpi_request_ce;
734 static zend_object_handlers httpi_request_object_handlers;
735
736 typedef struct {
737 zend_object zo;
738 CURL *ch;
739
740 zval *options;
741 zval *responseInfo;
742 zval *responseData;
743 } httpi_request_object;
744
745 #define httpi_request_declare_default_properties(ce) _httpi_request_declare_default_properties(ce TSRMLS_CC)
746 static inline void _httpi_request_declare_default_properties(zend_class_entry *ce TSRMLS_DC)
747 {
748 DCL_PROP_N(PROTECTED, options);
749 DCL_PROP_N(PROTECTED, responseInfo);
750 DCL_PROP_N(PROTECTED, responseData);
751
752 DCL_PROP(PROTECTED, long, method, HTTP_GET);
753
754 DCL_PROP(PROTECTED, string, url, "");
755 DCL_PROP(PROTECTED, string, contentType, "");
756 DCL_PROP(PROTECTED, string, queryData, "");
757 DCL_PROP(PROTECTED, string, postData, "");
758 }
759
760 #define httpi_request_destroy_object _httpi_request_destroy_object
761 void _httpi_request_destroy_object(void *object, zend_object_handle handle TSRMLS_DC)
762 {
763 httpi_request_object *o = object;
764
765 zend_objects_destroy_object(object, handle TSRMLS_CC);
766
767 FREE_PARR(o->options);
768 FREE_PARR(o->responseInfo);
769 FREE_PARR(o->responseData);
770
771 if (OBJ_PROP(o)) {
772 zend_hash_destroy(OBJ_PROP(o));
773 FREE_HASHTABLE(OBJ_PROP(o));
774 }
775 if (o->ch) {
776 curl_easy_cleanup(o->ch);
777 o->ch = NULL;
778 }
779 efree(o);
780 }
781
782 #define httpi_request_new_object _httpi_request_new_object
783 zend_object_value _httpi_request_new_object(zend_class_entry *ce TSRMLS_DC)
784 {
785 zend_object_value ov;
786 httpi_request_object *o;
787
788 o = ecalloc(sizeof(httpi_request_object), 1);
789 o->zo.ce = ce;
790 o->ch = curl_easy_init();
791
792 ALLOC_HASHTABLE(OBJ_PROP(o));
793 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
794 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
795
796 ov.handle = zend_objects_store_put(o, httpi_request_destroy_object, NULL, NULL TSRMLS_CC);
797 ov.handlers = &httpi_request_object_handlers;
798
799 return ov;
800 }
801
802 zend_function_entry httpi_request_class_methods[] = {
803 PHP_ME(HTTPi_Request, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
804 PHP_ME(HTTPi_Request, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
805
806 PHP_ME(HTTPi_Request, setOptions, NULL, ZEND_ACC_PUBLIC)
807 PHP_ME(HTTPi_Request, getOptions, NULL, ZEND_ACC_PUBLIC)
808
809 PHP_ME(HTTPi_Request, setMethod, NULL, ZEND_ACC_PUBLIC)
810 PHP_ME(HTTPi_Request, getMethod, NULL, ZEND_ACC_PUBLIC)
811
812 PHP_ME(HTTPi_Request, setURL, NULL, ZEND_ACC_PUBLIC)
813 PHP_ME(HTTPi_Request, getURL, NULL, ZEND_ACC_PUBLIC)
814
815 PHP_ME(HTTPi_Request, setContentType, NULL, ZEND_ACC_PUBLIC)
816 PHP_ME(HTTPi_Request, getContentType, NULL, ZEND_ACC_PUBLIC)
817
818 PHP_ME(HTTPi_Request, setQueryData, NULL, ZEND_ACC_PUBLIC)
819 PHP_ME(HTTPi_Request, getQueryData, NULL, ZEND_ACC_PUBLIC)
820 PHP_ME(HTTPi_Request, addQueryData, NULL, ZEND_ACC_PUBLIC)
821 PHP_ME(HTTPi_Request, unsetQueryData, NULL, ZEND_ACC_PUBLIC)
822 /*
823 PHP_ME(HTTPi_Request, setPostData, NULL, ZEND_ACC_PUBLIC)
824 PHP_ME(HTTPi_Request, addPostData, NULL, ZEND_ACC_PUBLIC)
825 PHP_ME(HTTPi_Request, unsetPostData, NULL, ZEND_ACC_PUBLIC)
826
827 PHP_ME(HTTPi_Request, addPostFile, NULL, ZEND_ACC_PUBLIC)
828 */
829 PHP_ME(HTTPi_Request, send, NULL, ZEND_ACC_PUBLIC)
830
831 PHP_ME(HTTPi_Request, getResponseData, NULL, ZEND_ACC_PUBLIC)
832 PHP_ME(HTTPi_Request, getResponseHeaders, NULL, ZEND_ACC_PUBLIC)
833 PHP_ME(HTTPi_Request, getResponseBody, NULL, ZEND_ACC_PUBLIC)
834 PHP_ME(HTTPi_Request, getResponseInfo, NULL, ZEND_ACC_PUBLIC)
835
836 {NULL, NULL, NULL}
837 };
838
839 /* {{{ proto void HTTPi_Request::__construct([string url[, long request_method = HTTP_GET]])
840 *
841 */
842 PHP_METHOD(HTTPi_Request, __construct)
843 {
844 char *URL = NULL;
845 int URL_len;
846 long meth = -1;
847 zval *info, *opts, *resp;
848 getObject(httpi_request_object, obj);
849
850 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) {
851 return;
852 }
853
854 INIT_PARR(obj, options);
855 INIT_PARR(obj, responseInfo);
856 INIT_PARR(obj, responseData);
857
858 if (URL) {
859 UPD_PROP(obj, string, url, URL);
860 }
861 if (meth > -1) {
862 UPD_PROP(obj, long, method, meth);
863 }
864 }
865 /* }}} */
866
867 PHP_METHOD(HTTPi_Request, __destruct)
868 {
869 zval *opts, *info, *resp;
870 getObject(httpi_request_object, obj);
871
872 /*
873 * this never happens ???
874 */
875
876 fprintf(stderr, "\n\n\nYAY, DESTRUCTOR CALLED!\n\n\n");
877
878 opts = GET_PROP(obj, options);
879 zval_dtor(opts);
880 FREE_ZVAL(opts);
881
882 info = GET_PROP(obj, responseInfo);
883 zval_dtor(info);
884 FREE_ZVAL(info);
885
886 resp = GET_PROP(obj, responseData);
887 zval_dtor(resp);
888 FREE_ZVAL(resp);
889 }
890
891 /* {{{ proto bool HTTPi_Request::setOptions(array options)
892 *
893 */
894 PHP_METHOD(HTTPi_Request, setOptions)
895 {
896 zval *opts, *old_opts, **opt;
897 getObject(httpi_request_object, obj);
898
899 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &opts)) {
900 RETURN_FALSE;
901 }
902
903 old_opts = GET_PROP(obj, options);
904
905 /* headers and cookies need extra attention -- thus cannot use zend_hash_merge() or php_array_merge() directly */
906 for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(opts));
907 zend_hash_get_current_data(Z_ARRVAL_P(opts), (void **) &opt) == SUCCESS;
908 zend_hash_move_forward(Z_ARRVAL_P(opts))) {
909 char *key;
910 long idx;
911 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(opts), &key, &idx, 0)) {
912 if (!strcmp(key, "headers")) {
913 zval **headers;
914 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) {
915 array_merge(*opt, *headers);
916 continue;
917 }
918 } else if (!strcmp(key, "cookies")) {
919 zval **cookies;
920 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) {
921 array_merge(*opt, *cookies);
922 continue;
923 }
924 }
925 zval_add_ref(opt);
926 add_assoc_zval(old_opts, key, *opt);
927 }
928 }
929 RETURN_TRUE;
930 }
931 /* }}} */
932
933 /* {{{ proto array HTTPi_Request::getOptions()
934 *
935 */
936 PHP_METHOD(HTTPi_Request, getOptions)
937 {
938 zval *opts;
939 getObject(httpi_request_object, obj);
940
941 NO_ARGS;
942
943 opts = GET_PROP(obj, options);
944 array_init(return_value);
945 array_copy(opts, return_value);
946 }
947 /* }}} */
948
949 /* {{{ proto bool HTTPi_Request::setURL(string url)
950 *
951 */
952 PHP_METHOD(HTTPi_Request, setURL)
953 {
954 char *URL = NULL;
955 int URL_len;
956 getObject(httpi_request_object, obj);
957
958 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) {
959 RETURN_FALSE;
960 }
961
962 UPD_PROP(obj, string, url, URL);
963 RETURN_TRUE;
964 }
965 /* }}} */
966
967 /* {{{ proto string HTTPi_Request::getUrl()
968 *
969 */
970 PHP_METHOD(HTTPi_Request, getURL)
971 {
972 zval *URL;
973 getObject(httpi_request_object, obj);
974
975 NO_ARGS;
976
977 URL = GET_PROP(obj, url);
978 RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1);
979 }
980 /* }}} */
981
982 /* {{{ proto bool HTTPi_Request::setMethod(long request_method)
983 *
984 */
985 PHP_METHOD(HTTPi_Request, setMethod)
986 {
987 long meth;
988 getObject(httpi_request_object, obj);
989
990 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) {
991 RETURN_FALSE;
992 }
993
994 UPD_PROP(obj, long, method, meth);
995 RETURN_TRUE;
996 }
997 /* }}} */
998
999 /* {{{ proto long HTTPi_Request::getMethod()
1000 *
1001 */
1002 PHP_METHOD(HTTPi_Request, getMethod)
1003 {
1004 zval *meth;
1005 getObject(httpi_request_object, obj);
1006
1007 NO_ARGS;
1008
1009 meth = GET_PROP(obj, method);
1010 RETURN_LONG(Z_LVAL_P(meth));
1011 }
1012 /* }}} */
1013
1014 /* {{{ proto bool HTTPi_Request::setContentType(string content_type)
1015 *
1016 */
1017 PHP_METHOD(HTTPi_Request, setContentType)
1018 {
1019 char *ctype;
1020 int ct_len;
1021 getObject(httpi_request_object, obj);
1022
1023 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) {
1024 RETURN_FALSE;
1025 }
1026
1027 if (!strchr(ctype, '/')) {
1028 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1029 "Content-Type '%s' doesn't seem to contain a primary and a secondary part",
1030 ctype);
1031 RETURN_FALSE;
1032 }
1033
1034 UPD_PROP(obj, string, contentType, ctype);
1035 RETURN_TRUE;
1036 }
1037 /* }}} */
1038
1039 /* {{{ proto string HTTPi_Request::getContentType()
1040 *
1041 */
1042 PHP_METHOD(HTTPi_Request, getContentType)
1043 {
1044 zval *ctype;
1045 getObject(httpi_request_object, obj);
1046
1047 NO_ARGS;
1048
1049 ctype = GET_PROP(obj, contentType);
1050 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
1051 }
1052 /* }}} */
1053
1054 /* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data)
1055 *
1056 */
1057 PHP_METHOD(HTTPi_Request, setQueryData)
1058 {
1059 zval *qdata;
1060 getObject(httpi_request_object, obj);
1061
1062 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) {
1063 RETURN_FALSE;
1064 }
1065
1066 if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) {
1067 smart_str qstr = {0};
1068 HTTP_URL_ARGSEP_OVERRIDE;
1069 if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) {
1070 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data");
1071 if (qstr.c) {
1072 efree(qstr.c);
1073 }
1074 HTTP_URL_ARGSEP_RESTORE;
1075 RETURN_FALSE;
1076 }
1077 HTTP_URL_ARGSEP_RESTORE;
1078 smart_str_0(&qstr);
1079 UPD_PROP(obj, string, queryData, qstr.c);
1080 efree(qstr.c);
1081 RETURN_TRUE;
1082 }
1083
1084 convert_to_string(qdata);
1085 UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata));
1086 RETURN_TRUE;
1087 }
1088 /* }}} */
1089
1090 /* {{{ proto string HTTPi_Request::getQueryData()
1091 *
1092 */
1093 PHP_METHOD(HTTPi_Request, getQueryData)
1094 {
1095 zval *qdata;
1096 getObject(httpi_request_object, obj);
1097
1098 NO_ARGS;
1099
1100 qdata = GET_PROP(obj, queryData);
1101 RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1);
1102 }
1103 /* }}} */
1104
1105 /* {{{ proto bool HTTPi_Request::addQueryData(array query_params)
1106 *
1107 */
1108 PHP_METHOD(HTTPi_Request, addQueryData)
1109 {
1110 zval *qdata, *old_qdata;
1111 smart_str qstr = {0};
1112 char *separator;
1113 getObject(httpi_request_object, obj);
1114
1115 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) {
1116 RETURN_FALSE;
1117 }
1118
1119 old_qdata = GET_PROP(obj, queryData);
1120 if (Z_STRLEN_P(old_qdata)) {
1121 smart_str_appendl(&qstr, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata));
1122 }
1123
1124 HTTP_URL_ARGSEP_OVERRIDE;
1125 if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) {
1126 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data");
1127 if (qstr.c) {
1128 efree(qstr.c);
1129 }
1130 HTTP_URL_ARGSEP_RESTORE;
1131 RETURN_FALSE;
1132 }
1133 HTTP_URL_ARGSEP_RESTORE;
1134
1135 smart_str_0(&qstr);
1136
1137 UPD_PROP(obj, string, queryData, qstr.c);
1138 efree(qstr.c);
1139 RETURN_TRUE;
1140 }
1141 /* }}} */
1142
1143 /* {{{ proto void HTTPi_Request::unsetQueryData()
1144 *
1145 */
1146 PHP_METHOD(HTTPi_Request, unsetQueryData)
1147 {
1148 getObject(httpi_request_object, obj);
1149
1150 NO_ARGS;
1151
1152 UPD_PROP(obj, string, queryData, "");
1153 }
1154 /* }}} */
1155
1156 /* {{{ proto array HTTPi_Request::getResponseData()
1157 *
1158 */
1159 PHP_METHOD(HTTPi_Request, getResponseData)
1160 {
1161 zval *data;
1162 getObject(httpi_request_object, obj);
1163
1164 NO_ARGS;
1165
1166 data = GET_PROP(obj, responseData);
1167 array_init(return_value);
1168 array_copy(data, return_value);
1169 }
1170 /* }}} */
1171
1172 /* {{{ proto array HTTPi_Request::getResponseHeaders()
1173 *
1174 */
1175 PHP_METHOD(HTTPi_Request, getResponseHeaders)
1176 {
1177 zval *data, **headers;
1178 getObject(httpi_request_object, obj);
1179
1180 NO_ARGS;
1181
1182 array_init(return_value);
1183 data = GET_PROP(obj, responseData);
1184 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) {
1185 array_copy(*headers, return_value);
1186 }
1187 }
1188 /* }}} */
1189
1190 /* {{{ proto string HTTPi_Request::getResponseBody()
1191 *
1192 */
1193 PHP_METHOD(HTTPi_Request, getResponseBody)
1194 {
1195 zval *data, **body;
1196 getObject(httpi_request_object, obj);
1197
1198 NO_ARGS;
1199
1200 data = GET_PROP(obj, responseData);
1201 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) {
1202 RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1);
1203 } else {
1204 Z_TYPE_P(return_value) = IS_NULL;
1205 }
1206 }
1207 /* }}} */
1208
1209 /* {{{ proto array HTTPi_Request::getResponseInfo()
1210 *
1211 */
1212 PHP_METHOD(HTTPi_Request, getResponseInfo)
1213 {
1214 zval *info;
1215 getObject(httpi_request_object, obj);
1216
1217 NO_ARGS;
1218
1219 info = GET_PROP(obj, responseInfo);
1220 array_init(return_value);
1221 array_copy(info, return_value);
1222 }
1223 /* }}}*/
1224
1225 /* {{{ proto bool HTTPi_Request::send()
1226 *
1227 */
1228 PHP_METHOD(HTTPi_Request, send)
1229 {
1230 STATUS status = FAILURE;
1231 zval *meth, *URL, *qdata, *opts, *info, *resp;
1232 char *response_data, *request_uri, *uri;
1233 size_t response_len;
1234 getObject(httpi_request_object, obj);
1235
1236 NO_ARGS;
1237
1238 if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) {
1239 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL");
1240 RETURN_FALSE;
1241 }
1242
1243 meth = GET_PROP(obj, method);
1244 URL = GET_PROP(obj, url);
1245 qdata = GET_PROP(obj, queryData);
1246 opts = GET_PROP(obj, options);
1247 info = GET_PROP(obj, responseInfo);
1248 resp = GET_PROP(obj, responseData);
1249
1250 uri = http_absolute_uri(Z_STRVAL_P(URL), NULL);
1251 request_uri = ecalloc(HTTP_URI_MAXLEN + 1, 1);
1252 strcpy(request_uri, uri);
1253 efree(uri);
1254
1255 if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) {
1256 if (!strchr(request_uri, '?')) {
1257 strcat(request_uri, "?");
1258 } else {
1259 strcat(request_uri, "&");
1260 }
1261 strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri));
1262 }
1263
1264 switch (Z_LVAL_P(meth))
1265 {
1266 case HTTP_GET:
1267 status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1268 break;
1269
1270 case HTTP_HEAD:
1271 status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1272 break;
1273
1274 case HTTP_POST:
1275 break;
1276
1277 default:
1278 break;
1279 }
1280
1281 efree(request_uri);
1282
1283 /* final data handling */
1284 if (status != SUCCESS) {
1285 RETURN_FALSE;
1286 } else {
1287 zval *zheaders, *zbody;
1288
1289 MAKE_STD_ZVAL(zbody);
1290 MAKE_STD_ZVAL(zheaders)
1291 array_init(zheaders);
1292
1293 if (SUCCESS != http_split_response_ex(response_data, response_len, zheaders, zbody)) {
1294 zval_dtor(zheaders);
1295 efree(zheaders),
1296 efree(zbody);
1297 efree(response_data);
1298 RETURN_FALSE;
1299 }
1300
1301 add_assoc_zval(resp, "headers", zheaders);
1302 add_assoc_zval(resp, "body", zbody);
1303
1304 efree(response_data);
1305
1306 RETURN_TRUE;
1307 }
1308 /* */
1309 }
1310 /* }}} */
1311
1312 #endif /* HTTP_HAVE_CURL */
1313 /* }}} */
1314
1315 #endif /* ZEND_ENGINE_2 */
1316
1317 /* {{{ http_module_entry */
1318 zend_module_entry http_module_entry = {
1319 #if ZEND_MODULE_API_NO >= 20010901
1320 STANDARD_MODULE_HEADER,
1321 #endif
1322 "http",
1323 http_functions,
1324 PHP_MINIT(http),
1325 PHP_MSHUTDOWN(http),
1326 PHP_RINIT(http),
1327 PHP_RSHUTDOWN(http),
1328 PHP_MINFO(http),
1329 #if ZEND_MODULE_API_NO >= 20010901
1330 PHP_EXT_HTTP_VERSION,
1331 #endif
1332 STANDARD_MODULE_PROPERTIES
1333 };
1334 /* }}} */
1335
1336 /* {{{ proto string http_date([int timestamp])
1337 *
1338 * This function returns a valid HTTP date regarding RFC 822/1123
1339 * looking like: "Wed, 22 Dec 2004 11:34:47 GMT"
1340 *
1341 */
1342 PHP_FUNCTION(http_date)
1343 {
1344 long t = -1;
1345
1346 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1347 RETURN_FALSE;
1348 }
1349
1350 if (t == -1) {
1351 t = (long) time(NULL);
1352 }
1353
1354 RETURN_STRING(http_date(t), 0);
1355 }
1356 /* }}} */
1357
1358 /* {{{ proto string http_absolute_uri(string url[, string proto])
1359 *
1360 * This function returns an absolute URI constructed from url.
1361 * If the url is already abolute but a different proto was supplied,
1362 * only the proto part of the URI will be updated. If url has no
1363 * path specified, the path of the current REQUEST_URI will be taken.
1364 * The host will be taken either from the Host HTTP header of the client
1365 * the SERVER_NAME or just localhost if prior are not available.
1366 *
1367 * Some examples:
1368 * <pre>
1369 * url = "page.php" => http://www.example.com/current/path/page.php
1370 * url = "/page.php" => http://www.example.com/page.php
1371 * url = "/page.php", proto = "https" => https://www.example.com/page.php
1372 * </pre>
1373 *
1374 */
1375 PHP_FUNCTION(http_absolute_uri)
1376 {
1377 char *url = NULL, *proto = NULL;
1378 int url_len = 0, proto_len = 0;
1379
1380 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &url, &url_len, &proto, &proto_len) != SUCCESS) {
1381 RETURN_FALSE;
1382 }
1383
1384 RETURN_STRING(http_absolute_uri(url, proto), 0);
1385 }
1386 /* }}} */
1387
1388 /* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US'])
1389 *
1390 * This function negotiates the clients preferred language based on its
1391 * Accept-Language HTTP header. It returns the negotiated language or
1392 * the default language if none match.
1393 *
1394 * The qualifier is recognized and languages without qualifier are rated highest.
1395 *
1396 * The supported parameter is expected to be an array having
1397 * the supported languages as array values.
1398 *
1399 * Example:
1400 * <pre>
1401 * <?php
1402 * $langs = array(
1403 * 'en-US',// default
1404 * 'fr',
1405 * 'fr-FR',
1406 * 'de',
1407 * 'de-DE',
1408 * 'de-AT',
1409 * 'de-CH',
1410 * );
1411 * include './langs/'. http_negotiate_language($langs) .'.php';
1412 * ?>
1413 * </pre>
1414 *
1415 */
1416 PHP_FUNCTION(http_negotiate_language)
1417 {
1418 zval *supported;
1419 char *def = NULL;
1420 int def_len = 0;
1421
1422 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
1423 RETURN_FALSE;
1424 }
1425
1426 if (!def) {
1427 def = "en-US";
1428 }
1429
1430 RETURN_STRING(http_negotiate_language(supported, def), 0);
1431 }
1432 /* }}} */
1433
1434 /* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1'])
1435 *
1436 * This function negotiates the clients preferred charset based on its
1437 * Accept-Charset HTTP header. It returns the negotiated charset or
1438 * the default charset if none match.
1439 *
1440 * The qualifier is recognized and charset without qualifier are rated highest.
1441 *
1442 * The supported parameter is expected to be an array having
1443 * the supported charsets as array values.
1444 *
1445 * Example:
1446 * <pre>
1447 * <?php
1448 * $charsets = array(
1449 * 'iso-8859-1', // default
1450 * 'iso-8859-2',
1451 * 'iso-8859-15',
1452 * 'utf-8'
1453 * );
1454 * $pref = http_negotiate_charset($charsets);
1455 * if (!strcmp($pref, 'iso-8859-1')) {
1456 * iconv_set_encoding('internal_encoding', 'iso-8859-1');
1457 * iconv_set_encoding('output_encoding', $pref);
1458 * ob_start('ob_iconv_handler');
1459 * }
1460 * ?>
1461 * </pre>
1462 */
1463 PHP_FUNCTION(http_negotiate_charset)
1464 {
1465 zval *supported;
1466 char *def = NULL;
1467 int def_len = 0;
1468
1469 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
1470 RETURN_FALSE;
1471 }
1472
1473 if (!def) {
1474 def = "iso-8859-1";
1475 }
1476
1477 RETURN_STRING(http_negotiate_charset(supported, def), 0);
1478 }
1479 /* }}} */
1480
1481 /* {{{ proto bool http_send_status(int status)
1482 *
1483 * Send HTTP status code.
1484 *
1485 */
1486 PHP_FUNCTION(http_send_status)
1487 {
1488 int status = 0;
1489
1490 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) {
1491 RETURN_FALSE;
1492 }
1493 if (status < 100 || status > 510) {
1494 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid HTTP status code (100-510): %d", status);
1495 RETURN_FALSE;
1496 }
1497
1498 RETURN_SUCCESS(http_send_status(status));
1499 }
1500 /* }}} */
1501
1502 /* {{{ proto bool http_send_last_modified([int timestamp])
1503 *
1504 * This converts the given timestamp to a valid HTTP date and
1505 * sends it as "Last-Modified" HTTP header. If timestamp is
1506 * omitted, current time is sent.
1507 *
1508 */
1509 PHP_FUNCTION(http_send_last_modified)
1510 {
1511 long t = -1;
1512
1513 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1514 RETURN_FALSE;
1515 }
1516
1517 if (t == -1) {
1518 t = (long) time(NULL);
1519 }
1520
1521 RETURN_SUCCESS(http_send_last_modified(t));
1522 }
1523 /* }}} */
1524
1525 /* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream'])
1526 *
1527 * Sets the content type.
1528 *
1529 */
1530 PHP_FUNCTION(http_send_content_type)
1531 {
1532 char *ct;
1533 int ct_len = 0;
1534
1535 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) {
1536 RETURN_FALSE;
1537 }
1538
1539 if (!ct_len) {
1540 RETURN_SUCCESS(http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1));
1541 }
1542 RETURN_SUCCESS(http_send_content_type(ct, ct_len));
1543 }
1544 /* }}} */
1545
1546 /* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false])
1547 *
1548 * Set the Content Disposition. The Content-Disposition header is very useful
1549 * if the data actually sent came from a file or something similar, that should
1550 * be "saved" by the client/user (i.e. by browsers "Save as..." popup window).
1551 *
1552 */
1553 PHP_FUNCTION(http_send_content_disposition)
1554 {
1555 char *filename;
1556 int f_len;
1557 zend_bool send_inline = 0;
1558
1559 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) {
1560 RETURN_FALSE;
1561 }
1562 RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline));
1563 }
1564 /* }}} */
1565
1566 /* {{{ proto bool http_match_modified([int timestamp])
1567 *
1568 * Matches the given timestamp against the clients "If-Modified-Since" resp.
1569 * "If-Unmodified-Since" HTTP headers.
1570 *
1571 */
1572 PHP_FUNCTION(http_match_modified)
1573 {
1574 long t = -1;
1575
1576 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1577 RETURN_FALSE;
1578 }
1579
1580 // current time if not supplied (senseless though)
1581 if (t == -1) {
1582 t = (long) time(NULL);
1583 }
1584
1585 RETURN_BOOL(http_modified_match("HTTP_IF_MODIFIED_SINCE", t) || http_modified_match("HTTP_IF_UNMODIFIED_SINCE", t));
1586 }
1587 /* }}} */
1588
1589 /* {{{ proto bool http_match_etag(string etag)
1590 *
1591 * This matches the given ETag against the clients
1592 * "If-Match" resp. "If-None-Match" HTTP headers.
1593 *
1594 */
1595 PHP_FUNCTION(http_match_etag)
1596 {
1597 int etag_len;
1598 char *etag;
1599
1600 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len) != SUCCESS) {
1601 RETURN_FALSE;
1602 }
1603
1604 RETURN_BOOL(http_etag_match("HTTP_IF_NONE_MATCH", etag) || http_etag_match("HTTP_IF_MATCH", etag));
1605 }
1606 /* }}} */
1607
1608 /* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]])
1609 *
1610 * If timestamp_or_exires is greater than 0, it is handled as timestamp
1611 * and will be sent as date of last modification. If it is 0 or omitted,
1612 * the current time will be sent as Last-Modified date. If it's negative,
1613 * it is handled as expiration time in seconds, which means that if the
1614 * requested last modification date is not between the calculated timespan,
1615 * the Last-Modified header is updated and the actual body will be sent.
1616 *
1617 */
1618 PHP_FUNCTION(http_cache_last_modified)
1619 {
1620 long last_modified = 0, send_modified = 0, t;
1621 zval *zlm;
1622
1623 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) {
1624 RETURN_FALSE;
1625 }
1626
1627 t = (long) time(NULL);
1628
1629 /* 0 or omitted */
1630 if (!last_modified) {
1631 /* does the client have? (att: caching "forever") */
1632 if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) {
1633 last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm));
1634 /* send current time */
1635 } else {
1636 send_modified = t;
1637 }
1638 /* negative value is supposed to be expiration time */
1639 } else if (last_modified < 0) {
1640 last_modified += t;
1641 send_modified = t;
1642 /* send supplied time explicitly */
1643 } else {
1644 send_modified = last_modified;
1645 }
1646
1647 RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1));
1648 }
1649 /* }}} */
1650
1651 /* {{{ proto bool http_cache_etag([string etag])
1652 *
1653 * This function attempts to cache the HTTP body based on an ETag,
1654 * either supplied or generated through calculation of the MD5
1655 * checksum of the output (uses output buffering).
1656 *
1657 * If clients "If-None-Match" header matches the supplied/calculated
1658 * ETag, the body is considered cached on the clients side and
1659 * a "304 Not Modified" status code is issued.
1660 *
1661 */
1662 PHP_FUNCTION(http_cache_etag)
1663 {
1664 char *etag;
1665 int etag_len = 0;
1666
1667 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) {
1668 RETURN_FALSE;
1669 }
1670
1671 RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1));
1672 }
1673 /* }}} */
1674
1675 /* {{{ proto string ob_httpetaghandler(string data, int mode)
1676 *
1677 * For use with ob_start().
1678 * Note that this has to be started as first output buffer.
1679 * WARNING: Don't use with http_send_*().
1680 */
1681 PHP_FUNCTION(ob_httpetaghandler)
1682 {
1683 char *data;
1684 int data_len;
1685 long mode;
1686
1687 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) {
1688 RETURN_FALSE;
1689 }
1690
1691 if (mode & PHP_OUTPUT_HANDLER_START) {
1692 if (HTTP_G(etag_started)) {
1693 php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler can only be used once");
1694 RETURN_STRINGL(data, data_len, 1);
1695 }
1696 http_send_header("Cache-Control: " HTTP_DEFAULT_CACHECONTROL);
1697 HTTP_G(etag_started) = 1;
1698 }
1699
1700 if (OG(ob_nesting_level) > 1) {
1701 php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler must be started prior to other output buffers");
1702 RETURN_STRINGL(data, data_len, 1);
1703 }
1704
1705 Z_TYPE_P(return_value) = IS_STRING;
1706 http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), mode);
1707 }
1708 /* }}} */
1709
1710 /* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]])
1711 *
1712 * Redirect to a given url.
1713 * The supplied url will be expanded with http_absolute_uri(), the params array will
1714 * be treated with http_build_query() and the session identification will be appended
1715 * if session is true.
1716 *
1717 * Depending on permanent the redirection will be issued with a permanent
1718 * ("301 Moved Permanently") or a temporary ("302 Found") redirection
1719 * status code.
1720 *
1721 * To be RFC compliant, "Redirecting to <a>URI</a>." will be displayed,
1722 * if the client doesn't redirect immediatly.
1723 */
1724 PHP_FUNCTION(http_redirect)
1725 {
1726 int url_len;
1727 zend_bool session = 0, permanent = 0;
1728 zval *params = NULL;
1729 smart_str qstr = {0};
1730 char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34];
1731
1732 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, &params, &session, &permanent) != SUCCESS) {
1733 RETURN_FALSE;
1734 }
1735
1736 /* append session info */
1737 if (session && (PS(session_status) == php_session_active)) {
1738 if (!params) {
1739 MAKE_STD_ZVAL(params);
1740 array_init(params);
1741 }
1742 if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
1743 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not append session information");
1744 }
1745 }
1746
1747 /* treat params array with http_build_query() */
1748 if (params) {
1749 if (php_url_encode_hash_ex(Z_ARRVAL_P(params), &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) {
1750 if (qstr.c) {
1751 efree(qstr.c);
1752 }
1753 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode query parameters");
1754 RETURN_FALSE;
1755 }
1756 smart_str_0(&qstr);
1757 }
1758
1759 URI = http_absolute_uri(url, NULL);
1760 if (qstr.c) {
1761 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c);
1762 sprintf(RED, "Redirecting to <a href=\"%s?%s\">%s?%s</a>.\n", URI, qstr.c, URI, qstr.c);
1763 efree(qstr.c);
1764 } else {
1765 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI);
1766 sprintf(RED, "Redirecting to <a href=\"%s\">%s</a>.\n", URI, URI);
1767 }
1768 efree(URI);
1769
1770 if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) {
1771 php_body_write(RED, strlen(RED) TSRMLS_CC);
1772 RETURN_TRUE;
1773 }
1774 RETURN_FALSE;
1775 }
1776 /* }}} */
1777
1778 /* {{{ proto bool http_send_data(string data)
1779 *
1780 * Sends raw data with support for (multiple) range requests.
1781 *
1782 */
1783 PHP_FUNCTION(http_send_data)
1784 {
1785 zval *zdata;
1786
1787 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) {
1788 RETURN_FALSE;
1789 }
1790
1791 convert_to_string_ex(&zdata);
1792 http_send_header("Accept-Ranges: bytes");
1793 RETURN_SUCCESS(http_send_data(zdata));
1794 }
1795 /* }}} */
1796
1797 /* {{{ proto bool http_send_file(string file)
1798 *
1799 * Sends a file with support for (multiple) range requests.
1800 *
1801 */
1802 PHP_FUNCTION(http_send_file)
1803 {
1804 zval *zfile;
1805
1806 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zfile) != SUCCESS) {
1807 RETURN_FALSE;
1808 }
1809
1810 convert_to_string_ex(&zfile);
1811 http_send_header("Accept-Ranges: bytes");
1812 RETURN_SUCCESS(http_send_file(zfile));
1813 }
1814 /* }}} */
1815
1816 /* {{{ proto bool http_send_stream(resource stream)
1817 *
1818 * Sends an already opened stream with support for (multiple) range requests.
1819 *
1820 */
1821 PHP_FUNCTION(http_send_stream)
1822 {
1823 zval *zstream;
1824 php_stream *file;
1825
1826 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) {
1827 RETURN_FALSE;
1828 }
1829
1830 php_stream_from_zval(file, &zstream);
1831 http_send_header("Accept-Ranges: bytes");
1832 RETURN_SUCCESS(http_send_stream(file));
1833 }
1834 /* }}} */
1835
1836 /* {{{ proto string http_chunked_decode(string encoded)
1837 *
1838 * This function decodes a string that was HTTP-chunked encoded.
1839 * Returns false on failure.
1840 */
1841 PHP_FUNCTION(http_chunked_decode)
1842 {
1843 char *encoded = NULL, *decoded = NULL;
1844 int encoded_len = 0, decoded_len = 0;
1845
1846 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) {
1847 RETURN_FALSE;
1848 }
1849
1850 if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
1851 RETURN_STRINGL(decoded, decoded_len, 0);
1852 } else {
1853 RETURN_FALSE;
1854 }
1855 }
1856 /* }}} */
1857
1858 /* {{{ proto array http_split_response(string http_response)
1859 *
1860 * This function splits an HTTP response into an array with headers and the
1861 * content body. The returned array may look simliar to the following example:
1862 *
1863 * <pre>
1864 * <?php
1865 * array(
1866 * 0 => array(
1867 * 'Status' => '200 Ok',
1868 * 'Content-Type' => 'text/plain',
1869 * 'Content-Language' => 'en-US'
1870 * ),
1871 * 1 => "Hello World!"
1872 * );
1873 * ?>
1874 * </pre>
1875 */
1876 PHP_FUNCTION(http_split_response)
1877 {
1878 zval *zresponse, *zbody, *zheaders;
1879
1880 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) {
1881 RETURN_FALSE;
1882 }
1883
1884 convert_to_string_ex(&zresponse);
1885
1886 MAKE_STD_ZVAL(zbody);
1887 MAKE_STD_ZVAL(zheaders);
1888 array_init(zheaders);
1889
1890 if (SUCCESS != http_split_response(zresponse, zheaders, zbody)) {
1891 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP response");
1892 RETURN_FALSE;
1893 }
1894
1895 array_init(return_value);
1896 add_index_zval(return_value, 0, zheaders);
1897 add_index_zval(return_value, 1, zbody);
1898 }
1899 /* }}} */
1900
1901 /* {{{ proto array http_parse_headers(string header)
1902 *
1903 */
1904 PHP_FUNCTION(http_parse_headers)
1905 {
1906 char *header, *rnrn;
1907 int header_len;
1908
1909 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) {
1910 RETURN_FALSE;
1911 }
1912
1913 array_init(return_value);
1914
1915 if (rnrn = strstr(header, HTTP_CRLF HTTP_CRLF)) {
1916 header_len = rnrn - header + 2;
1917 }
1918 if (SUCCESS != http_parse_headers(header, header_len, return_value)) {
1919 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP header");
1920 zval_dtor(return_value);
1921 RETURN_FALSE;
1922 }
1923 }
1924 /* }}}*/
1925
1926 /* {{{ proto array http_get_request_headers(void)
1927 *
1928 */
1929 PHP_FUNCTION(http_get_request_headers)
1930 {
1931 if (ZEND_NUM_ARGS()) {
1932 WRONG_PARAM_COUNT;
1933 }
1934
1935 array_init(return_value);
1936 http_get_request_headers(return_value);
1937 }
1938 /* }}} */
1939
1940 /* {{{ HAVE_CURL */
1941 #ifdef HTTP_HAVE_CURL
1942
1943 /* {{{ proto string http_get(string url[, array options[, array &info]])
1944 *
1945 * Performs an HTTP GET request on the supplied url.
1946 *
1947 * The second parameter is expected to be an associative
1948 * array where the following keys will be recognized:
1949 * <pre>
1950 * - redirect: int, whether and how many redirects to follow
1951 * - unrestrictedauth: bool, whether to continue sending credentials on
1952 * redirects to a different host
1953 * - proxyhost: string, proxy host in "host[:port]" format
1954 * - proxyport: int, use another proxy port as specified in proxyhost
1955 * - proxyauth: string, proxy credentials in "user:pass" format
1956 * - proxyauthtype: int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
1957 * - httpauth: string, http credentials in "user:pass" format
1958 * - httpauthtype: int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
1959 * - compress: bool, whether to allow gzip/deflate content encoding
1960 * (defaults to true)
1961 * - port: int, use another port as specified in the url
1962 * - referer: string, the referer to sends
1963 * - useragent: string, the user agent to send
1964 * (defaults to PECL::HTTP/version (PHP/version)))
1965 * - headers: array, list of custom headers as associative array
1966 * like array("header" => "value")
1967 * - cookies: array, list of cookies as associative array
1968 * like array("cookie" => "value")
1969 * - cookiestore: string, path to a file where cookies are/will be stored
1970 * </pre>
1971 *
1972 * The optional third parameter will be filled with some additional information
1973 * in form af an associative array, if supplied, like the following example:
1974 * <pre>
1975 * <?php
1976 * array (
1977 * 'effective_url' => 'http://localhost',
1978 * 'response_code' => 403,
1979 * 'total_time' => 0.017,
1980 * 'namelookup_time' => 0.013,
1981 * 'connect_time' => 0.014,
1982 * 'pretransfer_time' => 0.014,
1983 * 'size_upload' => 0,
1984 * 'size_download' => 202,
1985 * 'speed_download' => 11882,
1986 * 'speed_upload' => 0,
1987 * 'header_size' => 145,
1988 * 'request_size' => 62,
1989 * 'ssl_verifyresult' => 0,
1990 * 'filetime' => -1,
1991 * 'content_length_download' => 202,
1992 * 'content_length_upload' => 0,
1993 * 'starttransfer_time' => 0.017,
1994 * 'content_type' => 'text/html; charset=iso-8859-1',
1995 * 'redirect_time' => 0,
1996 * 'redirect_count' => 0,
1997 * 'private' => '',
1998 * 'http_connectcode' => 0,
1999 * 'httpauth_avail' => 0,
2000 * 'proxyauth_avail' => 0,
2001 * )
2002 * ?>
2003 * </pre>
2004 */
2005 PHP_FUNCTION(http_get)
2006 {
2007 char *URL, *data = NULL;
2008 size_t data_len = 0;
2009 int URL_len;
2010 zval *options = NULL, *info = NULL;
2011
2012 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
2013 RETURN_FALSE;
2014 }
2015
2016 if (info) {
2017 zval_dtor(info);
2018 array_init(info);
2019 }
2020
2021 if (SUCCESS == http_get(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2022 RETURN_STRINGL(data, data_len, 0);
2023 } else {
2024 RETURN_FALSE;
2025 }
2026 }
2027 /* }}} */
2028
2029 /* {{{ proto string http_head(string url[, array options[, array &info]])
2030 *
2031 * Performs an HTTP HEAD request on the suppied url.
2032 * Returns the HTTP response as string.
2033 * See http_get() for a full list of available options.
2034 */
2035 PHP_FUNCTION(http_head)
2036 {
2037 char *URL, *data = NULL;
2038 size_t data_len = 0;
2039 int URL_len;
2040 zval *options = NULL, *info = NULL;
2041
2042 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
2043 RETURN_FALSE;
2044 }
2045
2046 if (info) {
2047 zval_dtor(info);
2048 array_init(info);
2049 }
2050
2051 if (SUCCESS == http_head(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2052 RETURN_STRINGL(data, data_len, 0);
2053 } else {
2054 RETURN_FALSE;
2055 }
2056 }
2057 /* }}} */
2058
2059 /* {{{ proto string http_post_data(string url, string data[, array options[, &info]])
2060 *
2061 * Performs an HTTP POST request, posting data.
2062 * Returns the HTTP response as string.
2063 * See http_get() for a full list of available options.
2064 */
2065 PHP_FUNCTION(http_post_data)
2066 {
2067 char *URL, *postdata, *data = NULL;
2068 size_t data_len = 0;
2069 int postdata_len, URL_len;
2070 zval *options = NULL, *info = NULL;
2071
2072 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) {
2073 RETURN_FALSE;
2074 }
2075
2076 if (info) {
2077 zval_dtor(info);
2078 array_init(info);
2079 }
2080
2081 if (SUCCESS == http_post_data(URL, postdata, (size_t) postdata_len, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2082 RETURN_STRINGL(data, data_len, 0);
2083 } else {
2084 RETURN_FALSE;
2085 }
2086 }
2087 /* }}} */
2088
2089 /* {{{ proto string http_post_array(string url, array data[, array options[, array &info]])
2090 *
2091 * Performs an HTTP POST request, posting www-form-urlencoded array data.
2092 * Returns the HTTP response as string.
2093 * See http_get() for a full list of available options.
2094 */
2095 PHP_FUNCTION(http_post_array)
2096 {
2097 char *URL, *data = NULL;
2098 size_t data_len = 0;
2099 int URL_len;
2100 zval *options = NULL, *info = NULL, *postdata;
2101
2102 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a/!z", &URL, &URL_len, &postdata, &options, &info) != SUCCESS) {
2103 RETURN_FALSE;
2104 }
2105
2106 if (info) {
2107 zval_dtor(info);
2108 array_init(info);
2109 }
2110
2111 if (SUCCESS == http_post_array(URL, Z_ARRVAL_P(postdata), HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2112 RETURN_STRINGL(data, data_len, 0);
2113 } else {
2114 RETURN_FALSE;
2115 }
2116 }
2117 /* }}} */
2118
2119 #endif
2120 /* }}} HAVE_CURL */
2121
2122
2123 /* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"])
2124 *
2125 * Example:
2126 * <pre>
2127 * <?php
2128 * if (!http_auth_basic('mike', 's3c|r3t')) {
2129 * die('<h1>Authorization failed!</h1>');
2130 * }
2131 * ?>
2132 * </pre>
2133 */
2134 PHP_FUNCTION(http_auth_basic)
2135 {
2136 char *realm = NULL, *user, *pass, *suser, *spass;
2137 int r_len, u_len, p_len;
2138
2139 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) {
2140 RETURN_FALSE;
2141 }
2142
2143 if (!realm) {
2144 realm = "Restricted";
2145 }
2146
2147 if (SUCCESS != http_auth_credentials(&suser, &spass)) {
2148 http_auth_header("Basic", realm);
2149 RETURN_FALSE;
2150 }
2151
2152 if (strcasecmp(suser, user)) {
2153 http_auth_header("Basic", realm);
2154 RETURN_FALSE;
2155 }
2156
2157 if (strcmp(spass, pass)) {
2158 http_auth_header("Basic", realm);
2159 RETURN_FALSE;
2160 }
2161
2162 RETURN_TRUE;
2163 }
2164 /* }}} */
2165
2166 /* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"])
2167 *
2168 * Example:
2169 * <pre>
2170 * <?php
2171 * function auth_cb($user, $pass)
2172 * {
2173 * global $db;
2174 * $query = 'SELECT pass FROM users WHERE user='. $db->quoteSmart($user);
2175 * if (strlen($realpass = $db->getOne($query)) {
2176 * return $pass === $realpass;
2177 * }
2178 * return false;
2179 * }
2180 *
2181 * if (!http_auth_basic_cb('auth_cb')) {
2182 * die('<h1>Authorization failed</h1>');
2183 * }
2184 * ?>
2185 * </pre>
2186 */
2187 PHP_FUNCTION(http_auth_basic_cb)
2188 {
2189 zval *cb;
2190 char *realm = NULL, *user, *pass;
2191 int r_len;
2192
2193 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) {
2194 RETURN_FALSE;
2195 }
2196
2197 if (!realm) {
2198 realm = "Restricted";
2199 }
2200
2201 if (SUCCESS != http_auth_credentials(&user, &pass)) {
2202 http_auth_header("Basic", realm);
2203 RETURN_FALSE;
2204 }
2205 {
2206 zval *zparams[2] = {NULL, NULL}, retval;
2207 int result = 0;
2208
2209 MAKE_STD_ZVAL(zparams[0]);
2210 MAKE_STD_ZVAL(zparams[1]);
2211 ZVAL_STRING(zparams[0], user, 0);
2212 ZVAL_STRING(zparams[1], pass, 0);
2213
2214 if (SUCCESS == call_user_function(EG(function_table), NULL, cb,
2215 &retval, 2, zparams TSRMLS_CC)) {
2216 result = Z_LVAL(retval);
2217 }
2218
2219 efree(user);
2220 efree(pass);
2221 efree(zparams[0]);
2222 efree(zparams[1]);
2223
2224 if (!result) {
2225 http_auth_header("Basic", realm);
2226 }
2227
2228 RETURN_BOOL(result);
2229 }
2230 }
2231 /* }}}*/
2232
2233
2234 /* {{{ php_http_init_globals(zend_http_globals *) */
2235 static void php_http_init_globals(zend_http_globals *http_globals)
2236 {
2237 http_globals->etag_started = 0;
2238 http_globals->ctype = NULL;
2239 http_globals->etag = NULL;
2240 http_globals->lmod = 0;
2241 #ifdef HTTP_HAVE_CURL
2242 http_globals->curlbuf.body.data = NULL;
2243 http_globals->curlbuf.body.used = 0;
2244 http_globals->curlbuf.body.free = 0;
2245 http_globals->curlbuf.hdrs.data = NULL;
2246 http_globals->curlbuf.hdrs.used = 0;
2247 http_globals->curlbuf.hdrs.free = 0;
2248 #endif
2249 http_globals->allowed_methods = NULL;
2250 }
2251 /* }}} */
2252
2253 /* {{{ static inline STATUS http_check_allowed_methods(char *, int) */
2254 #define http_check_allowed_methods(m, l) _http_check_allowed_methods((m), (l) TSRMLS_CC)
2255 static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC)
2256 {
2257 if (length && SG(request_info).request_method && (!strstr(methods, SG(request_info).request_method))) {
2258 char *allow_header = emalloc(length + sizeof("Allow: "));
2259 sprintf(allow_header, "Allow: %s", methods);
2260 http_send_header(allow_header);
2261 efree(allow_header);
2262 http_send_status(405);
2263 zend_bailout();
2264 }
2265 }
2266 /* }}} */
2267
2268 /* {{{ PHP_INI */
2269 PHP_INI_MH(update_allowed_methods)
2270 {
2271 http_check_allowed_methods(new_value, new_value_length);
2272 return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
2273 }
2274
2275 PHP_INI_BEGIN()
2276 STD_PHP_INI_ENTRY("http.allowed_methods", "OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT", PHP_INI_ALL, update_allowed_methods, allowed_methods, zend_http_globals, http_globals)
2277 PHP_INI_END()
2278 /* }}} */
2279
2280 /* {{{ PHP_MINIT_FUNCTION */
2281 PHP_MINIT_FUNCTION(http)
2282 {
2283 ZEND_INIT_MODULE_GLOBALS(http, php_http_init_globals, NULL);
2284 REGISTER_INI_ENTRIES();
2285
2286 #ifdef HTTP_HAVE_CURL
2287 REGISTER_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC, CONST_CS | CONST_PERSISTENT);
2288 REGISTER_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS | CONST_PERSISTENT);
2289 REGISTER_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM, CONST_CS | CONST_PERSISTENT);
2290 #endif
2291
2292 #ifdef ZEND_ENGINE_2
2293 HTTP_REGISTER_CLASS(HTTPi, httpi, NULL, ZEND_ACC_FINAL_CLASS);
2294 HTTP_REGISTER_CLASS_EX(HTTPi_Response, httpi_response, NULL, 0);
2295 # ifdef HTTP_HAVE_CURL
2296 HTTP_REGISTER_CLASS_EX(HTTPi_Request, httpi_request, NULL, 0);
2297 REGISTER_LONG_CONSTANT("HTTP_GET", HTTP_GET, CONST_CS | CONST_PERSISTENT);
2298 REGISTER_LONG_CONSTANT("HTTP_HEAD", HTTP_HEAD, CONST_CS | CONST_PERSISTENT);
2299 REGISTER_LONG_CONSTANT("HTTP_POST", HTTP_POST, CONST_CS | CONST_PERSISTENT);
2300 # endif /* HTTP_HAVE_CURL */
2301 #endif /* ZEND_ENGINE_2 */
2302 return SUCCESS;
2303 }
2304 /* }}} */
2305
2306 /* {{{ PHP_MSHUTDOWN_FUNCTION */
2307 PHP_MSHUTDOWN_FUNCTION(http)
2308 {
2309 UNREGISTER_INI_ENTRIES();
2310 return SUCCESS;
2311 }
2312 /* }}} */
2313
2314 /* {{{ PHP_RINIT_FUNCTION */
2315 PHP_RINIT_FUNCTION(http)
2316 {
2317 char *allowed_methods = INI_STR("http.allowed_methods");
2318 http_check_allowed_methods(allowed_methods, strlen(allowed_methods));
2319 return SUCCESS;
2320 }
2321 /* }}} */
2322
2323 /* {{{ PHP_RSHUTDOWN_FUNCTION */
2324 PHP_RSHUTDOWN_FUNCTION(http)
2325 {
2326 HTTP_G(etag_started) = 0;
2327 HTTP_G(lmod) = 0;
2328
2329 if (HTTP_G(etag)) {
2330 efree(HTTP_G(etag));
2331 HTTP_G(etag) = NULL;
2332 }
2333
2334 if (HTTP_G(ctype)) {
2335 efree(HTTP_G(ctype));
2336 HTTP_G(ctype) = NULL;
2337 }
2338 #ifdef HTTP_HAVE_CURL
2339 if (HTTP_G(curlbuf).body.data) {
2340 efree(HTTP_G(curlbuf).body.data);
2341 HTTP_G(curlbuf).body.data = NULL;
2342 }
2343 if (HTTP_G(curlbuf).hdrs.data) {
2344 efree(HTTP_G(curlbuf).hdrs.data);
2345 HTTP_G(curlbuf).hdrs.data = NULL;
2346 }
2347 #endif
2348 return SUCCESS;
2349 }
2350 /* }}} */
2351
2352 /* {{{ PHP_MINFO_FUNCTION */
2353 PHP_MINFO_FUNCTION(http)
2354 {
2355 php_info_print_table_start();
2356 php_info_print_table_header(2, "Extended HTTP support", "enabled");
2357 php_info_print_table_row(2, "Version:", PHP_EXT_HTTP_VERSION);
2358 php_info_print_table_row(2, "cURL convenience functions:",
2359 #ifdef HTTP_HAVE_CURL
2360 "enabled"
2361 #else
2362 "disabled"
2363 #endif
2364 );
2365 php_info_print_table_end();
2366
2367 DISPLAY_INI_ENTRIES();
2368 }
2369 /* }}} */
2370
2371 /*
2372 * Local variables:
2373 * tab-width: 4
2374 * c-basic-offset: 4
2375 * End:
2376 * vim600: noet sw=4 ts=4 fdm=marker
2377 * vim<600: noet sw=4 ts=4
2378 */
2379