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