* compatibility with older libcurl versions, take 2
[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 FREE_PARR(o->options);
766 FREE_PARR(o->responseInfo);
767 FREE_PARR(o->responseData);
768
769 if (OBJ_PROP(o)) {
770 zend_hash_destroy(OBJ_PROP(o));
771 FREE_HASHTABLE(OBJ_PROP(o));
772 }
773 if (o->ch) {
774 curl_easy_cleanup(o->ch);
775 o->ch = NULL;
776 }
777 efree(o);
778 }
779
780 #define httpi_request_new_object _httpi_request_new_object
781 zend_object_value _httpi_request_new_object(zend_class_entry *ce TSRMLS_DC)
782 {
783 zend_object_value ov;
784 httpi_request_object *o;
785
786 o = ecalloc(sizeof(httpi_request_object), 1);
787 o->zo.ce = ce;
788 o->ch = curl_easy_init();
789
790 ALLOC_HASHTABLE(OBJ_PROP(o));
791 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
792 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
793
794 ov.handle = zend_objects_store_put(o, httpi_request_destroy_object, NULL, NULL TSRMLS_CC);
795 ov.handlers = &httpi_request_object_handlers;
796
797 return ov;
798 }
799
800 zend_function_entry httpi_request_class_methods[] = {
801 PHP_ME(HTTPi_Request, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
802 PHP_ME(HTTPi_Request, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
803
804 PHP_ME(HTTPi_Request, setOptions, NULL, ZEND_ACC_PUBLIC)
805 PHP_ME(HTTPi_Request, getOptions, NULL, ZEND_ACC_PUBLIC)
806
807 PHP_ME(HTTPi_Request, setMethod, NULL, ZEND_ACC_PUBLIC)
808 PHP_ME(HTTPi_Request, getMethod, NULL, ZEND_ACC_PUBLIC)
809
810 PHP_ME(HTTPi_Request, setURL, NULL, ZEND_ACC_PUBLIC)
811 PHP_ME(HTTPi_Request, getURL, NULL, ZEND_ACC_PUBLIC)
812
813 PHP_ME(HTTPi_Request, setContentType, NULL, ZEND_ACC_PUBLIC)
814 PHP_ME(HTTPi_Request, getContentType, NULL, ZEND_ACC_PUBLIC)
815
816 PHP_ME(HTTPi_Request, setQueryData, NULL, ZEND_ACC_PUBLIC)
817 PHP_ME(HTTPi_Request, getQueryData, NULL, ZEND_ACC_PUBLIC)
818 PHP_ME(HTTPi_Request, addQueryData, NULL, ZEND_ACC_PUBLIC)
819 PHP_ME(HTTPi_Request, unsetQueryData, NULL, ZEND_ACC_PUBLIC)
820 /*
821 PHP_ME(HTTPi_Request, setPostData, NULL, ZEND_ACC_PUBLIC)
822 PHP_ME(HTTPi_Request, addPostData, NULL, ZEND_ACC_PUBLIC)
823 PHP_ME(HTTPi_Request, unsetPostData, NULL, ZEND_ACC_PUBLIC)
824
825 PHP_ME(HTTPi_Request, addPostFile, NULL, ZEND_ACC_PUBLIC)
826 */
827 PHP_ME(HTTPi_Request, send, NULL, ZEND_ACC_PUBLIC)
828
829 PHP_ME(HTTPi_Request, getResponseData, NULL, ZEND_ACC_PUBLIC)
830 PHP_ME(HTTPi_Request, getResponseHeaders, NULL, ZEND_ACC_PUBLIC)
831 PHP_ME(HTTPi_Request, getResponseBody, NULL, ZEND_ACC_PUBLIC)
832 PHP_ME(HTTPi_Request, getResponseInfo, NULL, ZEND_ACC_PUBLIC)
833
834 {NULL, NULL, NULL}
835 };
836
837 /* {{{ proto void HTTPi_Request::__construct([string url[, long request_method = HTTP_GET]])
838 *
839 */
840 PHP_METHOD(HTTPi_Request, __construct)
841 {
842 char *URL = NULL;
843 int URL_len;
844 long meth = -1;
845 zval *info, *opts, *resp;
846 getObject(httpi_request_object, obj);
847
848 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) {
849 return;
850 }
851
852 INIT_PARR(obj, options);
853 INIT_PARR(obj, responseInfo);
854 INIT_PARR(obj, responseData);
855
856 if (URL) {
857 UPD_PROP(obj, string, url, URL);
858 }
859 if (meth > -1) {
860 UPD_PROP(obj, long, method, meth);
861 }
862 }
863 /* }}} */
864
865 PHP_METHOD(HTTPi_Request, __destruct)
866 {
867 zval *opts, *info, *resp;
868 getObject(httpi_request_object, obj);
869
870 /*
871 * this never happens ???
872 */
873
874 fprintf(stderr, "\n\n\nYAY, DESTRUCTOR CALLED!\n\n\n");
875
876 opts = GET_PROP(obj, options);
877 zval_dtor(opts);
878 FREE_ZVAL(opts);
879
880 info = GET_PROP(obj, responseInfo);
881 zval_dtor(info);
882 FREE_ZVAL(info);
883
884 resp = GET_PROP(obj, responseData);
885 zval_dtor(resp);
886 FREE_ZVAL(resp);
887 }
888
889 /* {{{ proto bool HTTPi_Request::setOptions(array options)
890 *
891 */
892 PHP_METHOD(HTTPi_Request, setOptions)
893 {
894 zval *opts, *old_opts, **opt;
895 getObject(httpi_request_object, obj);
896
897 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &opts)) {
898 RETURN_FALSE;
899 }
900
901 old_opts = GET_PROP(obj, options);
902
903 /* headers and cookies need extra attention -- thus cannot use zend_hash_merge() or php_array_merge() directly */
904 for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(opts));
905 zend_hash_get_current_data(Z_ARRVAL_P(opts), (void **) &opt) == SUCCESS;
906 zend_hash_move_forward(Z_ARRVAL_P(opts))) {
907 char *key;
908 long idx;
909 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(opts), &key, &idx, 0)) {
910 if (!strcmp(key, "headers")) {
911 zval **headers;
912 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) {
913 array_merge(*opt, *headers);
914 continue;
915 }
916 } else if (!strcmp(key, "cookies")) {
917 zval **cookies;
918 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) {
919 array_merge(*opt, *cookies);
920 continue;
921 }
922 }
923 zval_add_ref(opt);
924 add_assoc_zval(old_opts, key, *opt);
925 }
926 }
927 RETURN_TRUE;
928 }
929 /* }}} */
930
931 /* {{{ proto array HTTPi_Request::getOptions()
932 *
933 */
934 PHP_METHOD(HTTPi_Request, getOptions)
935 {
936 zval *opts;
937 getObject(httpi_request_object, obj);
938
939 NO_ARGS;
940
941 opts = GET_PROP(obj, options);
942 array_init(return_value);
943 array_copy(opts, return_value);
944 }
945 /* }}} */
946
947 /* {{{ proto bool HTTPi_Request::setURL(string url)
948 *
949 */
950 PHP_METHOD(HTTPi_Request, setURL)
951 {
952 char *URL = NULL;
953 int URL_len;
954 getObject(httpi_request_object, obj);
955
956 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) {
957 RETURN_FALSE;
958 }
959
960 UPD_PROP(obj, string, url, URL);
961 RETURN_TRUE;
962 }
963 /* }}} */
964
965 /* {{{ proto string HTTPi_Request::getUrl()
966 *
967 */
968 PHP_METHOD(HTTPi_Request, getURL)
969 {
970 zval *URL;
971 getObject(httpi_request_object, obj);
972
973 NO_ARGS;
974
975 URL = GET_PROP(obj, url);
976 RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1);
977 }
978 /* }}} */
979
980 /* {{{ proto bool HTTPi_Request::setMethod(long request_method)
981 *
982 */
983 PHP_METHOD(HTTPi_Request, setMethod)
984 {
985 long meth;
986 getObject(httpi_request_object, obj);
987
988 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) {
989 RETURN_FALSE;
990 }
991
992 UPD_PROP(obj, long, method, meth);
993 RETURN_TRUE;
994 }
995 /* }}} */
996
997 /* {{{ proto long HTTPi_Request::getMethod()
998 *
999 */
1000 PHP_METHOD(HTTPi_Request, getMethod)
1001 {
1002 zval *meth;
1003 getObject(httpi_request_object, obj);
1004
1005 NO_ARGS;
1006
1007 meth = GET_PROP(obj, method);
1008 RETURN_LONG(Z_LVAL_P(meth));
1009 }
1010 /* }}} */
1011
1012 /* {{{ proto bool HTTPi_Request::setContentType(string content_type)
1013 *
1014 */
1015 PHP_METHOD(HTTPi_Request, setContentType)
1016 {
1017 char *ctype;
1018 int ct_len;
1019 getObject(httpi_request_object, obj);
1020
1021 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) {
1022 RETURN_FALSE;
1023 }
1024
1025 if (!strchr(ctype, '/')) {
1026 php_error_docref(NULL TSRMLS_CC, E_WARNING,
1027 "Content-Type '%s' doesn't seem to contain a primary and a secondary part",
1028 ctype);
1029 RETURN_FALSE;
1030 }
1031
1032 UPD_PROP(obj, string, contentType, ctype);
1033 RETURN_TRUE;
1034 }
1035 /* }}} */
1036
1037 /* {{{ proto string HTTPi_Request::getContentType()
1038 *
1039 */
1040 PHP_METHOD(HTTPi_Request, getContentType)
1041 {
1042 zval *ctype;
1043 getObject(httpi_request_object, obj);
1044
1045 NO_ARGS;
1046
1047 ctype = GET_PROP(obj, contentType);
1048 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
1049 }
1050 /* }}} */
1051
1052 /* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data)
1053 *
1054 */
1055 PHP_METHOD(HTTPi_Request, setQueryData)
1056 {
1057 zval *qdata;
1058 getObject(httpi_request_object, obj);
1059
1060 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) {
1061 RETURN_FALSE;
1062 }
1063
1064 if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) {
1065 smart_str qstr = {0};
1066 HTTP_URL_ARGSEP_OVERRIDE;
1067 if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) {
1068 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data");
1069 if (qstr.c) {
1070 efree(qstr.c);
1071 }
1072 HTTP_URL_ARGSEP_RESTORE;
1073 RETURN_FALSE;
1074 }
1075 HTTP_URL_ARGSEP_RESTORE;
1076 smart_str_0(&qstr);
1077 UPD_PROP(obj, string, queryData, qstr.c);
1078 efree(qstr.c);
1079 RETURN_TRUE;
1080 }
1081
1082 convert_to_string(qdata);
1083 UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata));
1084 RETURN_TRUE;
1085 }
1086 /* }}} */
1087
1088 /* {{{ proto string HTTPi_Request::getQueryData()
1089 *
1090 */
1091 PHP_METHOD(HTTPi_Request, getQueryData)
1092 {
1093 zval *qdata;
1094 getObject(httpi_request_object, obj);
1095
1096 NO_ARGS;
1097
1098 qdata = GET_PROP(obj, queryData);
1099 RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1);
1100 }
1101 /* }}} */
1102
1103 /* {{{ proto bool HTTPi_Request::addQueryData(array query_params)
1104 *
1105 */
1106 PHP_METHOD(HTTPi_Request, addQueryData)
1107 {
1108 zval *qdata, *old_qdata;
1109 smart_str qstr = {0};
1110 char *separator;
1111 getObject(httpi_request_object, obj);
1112
1113 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) {
1114 RETURN_FALSE;
1115 }
1116
1117 old_qdata = GET_PROP(obj, queryData);
1118 if (Z_STRLEN_P(old_qdata)) {
1119 smart_str_appendl(&qstr, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata));
1120 }
1121
1122 HTTP_URL_ARGSEP_OVERRIDE;
1123 if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) {
1124 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data");
1125 if (qstr.c) {
1126 efree(qstr.c);
1127 }
1128 HTTP_URL_ARGSEP_RESTORE;
1129 RETURN_FALSE;
1130 }
1131 HTTP_URL_ARGSEP_RESTORE;
1132
1133 smart_str_0(&qstr);
1134
1135 UPD_PROP(obj, string, queryData, qstr.c);
1136 efree(qstr.c);
1137 RETURN_TRUE;
1138 }
1139 /* }}} */
1140
1141 /* {{{ proto void HTTPi_Request::unsetQueryData()
1142 *
1143 */
1144 PHP_METHOD(HTTPi_Request, unsetQueryData)
1145 {
1146 getObject(httpi_request_object, obj);
1147
1148 NO_ARGS;
1149
1150 UPD_PROP(obj, string, queryData, "");
1151 }
1152 /* }}} */
1153
1154 /* {{{ proto array HTTPi_Request::getResponseData()
1155 *
1156 */
1157 PHP_METHOD(HTTPi_Request, getResponseData)
1158 {
1159 zval *data;
1160 getObject(httpi_request_object, obj);
1161
1162 NO_ARGS;
1163
1164 data = GET_PROP(obj, responseData);
1165 array_init(return_value);
1166 array_copy(data, return_value);
1167 }
1168 /* }}} */
1169
1170 /* {{{ proto array HTTPi_Request::getResponseHeaders()
1171 *
1172 */
1173 PHP_METHOD(HTTPi_Request, getResponseHeaders)
1174 {
1175 zval *data, **headers;
1176 getObject(httpi_request_object, obj);
1177
1178 NO_ARGS;
1179
1180 array_init(return_value);
1181 data = GET_PROP(obj, responseData);
1182 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) {
1183 array_copy(*headers, return_value);
1184 }
1185 }
1186 /* }}} */
1187
1188 /* {{{ proto string HTTPi_Request::getResponseBody()
1189 *
1190 */
1191 PHP_METHOD(HTTPi_Request, getResponseBody)
1192 {
1193 zval *data, **body;
1194 getObject(httpi_request_object, obj);
1195
1196 NO_ARGS;
1197
1198 data = GET_PROP(obj, responseData);
1199 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) {
1200 RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1);
1201 } else {
1202 Z_TYPE_P(return_value) = IS_NULL;
1203 }
1204 }
1205 /* }}} */
1206
1207 /* {{{ proto array HTTPi_Request::getResponseInfo()
1208 *
1209 */
1210 PHP_METHOD(HTTPi_Request, getResponseInfo)
1211 {
1212 zval *info;
1213 getObject(httpi_request_object, obj);
1214
1215 NO_ARGS;
1216
1217 info = GET_PROP(obj, responseInfo);
1218 array_init(return_value);
1219 array_copy(info, return_value);
1220 }
1221 /* }}}*/
1222
1223 /* {{{ proto bool HTTPi_Request::send()
1224 *
1225 */
1226 PHP_METHOD(HTTPi_Request, send)
1227 {
1228 STATUS status = FAILURE;
1229 zval *meth, *URL, *qdata, *opts, *info, *resp;
1230 char *response_data, *request_uri, *uri;
1231 size_t response_len;
1232 getObject(httpi_request_object, obj);
1233
1234 NO_ARGS;
1235
1236 if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) {
1237 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL");
1238 RETURN_FALSE;
1239 }
1240
1241 meth = GET_PROP(obj, method);
1242 URL = GET_PROP(obj, url);
1243 qdata = GET_PROP(obj, queryData);
1244 opts = GET_PROP(obj, options);
1245 info = GET_PROP(obj, responseInfo);
1246 resp = GET_PROP(obj, responseData);
1247
1248 uri = http_absolute_uri(Z_STRVAL_P(URL), NULL);
1249 request_uri = ecalloc(HTTP_URI_MAXLEN + 1, 1);
1250 strcpy(request_uri, uri);
1251 efree(uri);
1252
1253 if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) {
1254 if (!strchr(request_uri, '?')) {
1255 strcat(request_uri, "?");
1256 } else {
1257 strcat(request_uri, "&");
1258 }
1259 strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri));
1260 }
1261
1262 switch (Z_LVAL_P(meth))
1263 {
1264 case HTTP_GET:
1265 status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1266 break;
1267
1268 case HTTP_HEAD:
1269 status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len);
1270 break;
1271
1272 case HTTP_POST:
1273 break;
1274
1275 default:
1276 break;
1277 }
1278
1279 efree(request_uri);
1280
1281 /* final data handling */
1282 if (status != SUCCESS) {
1283 RETURN_FALSE;
1284 } else {
1285 zval *zheaders, *zbody;
1286
1287 MAKE_STD_ZVAL(zbody);
1288 MAKE_STD_ZVAL(zheaders)
1289 array_init(zheaders);
1290
1291 if (SUCCESS != http_split_response_ex(response_data, response_len, zheaders, zbody)) {
1292 zval_dtor(zheaders);
1293 efree(zheaders),
1294 efree(zbody);
1295 efree(response_data);
1296 RETURN_FALSE;
1297 }
1298
1299 add_assoc_zval(resp, "headers", zheaders);
1300 add_assoc_zval(resp, "body", zbody);
1301
1302 efree(response_data);
1303
1304 RETURN_TRUE;
1305 }
1306 /* */
1307 }
1308 /* }}} */
1309
1310 #endif /* HTTP_HAVE_CURL */
1311 /* }}} */
1312
1313 #endif /* ZEND_ENGINE_2 */
1314
1315 /* {{{ http_module_entry */
1316 zend_module_entry http_module_entry = {
1317 #if ZEND_MODULE_API_NO >= 20010901
1318 STANDARD_MODULE_HEADER,
1319 #endif
1320 "http",
1321 http_functions,
1322 PHP_MINIT(http),
1323 PHP_MSHUTDOWN(http),
1324 PHP_RINIT(http),
1325 PHP_RSHUTDOWN(http),
1326 PHP_MINFO(http),
1327 #if ZEND_MODULE_API_NO >= 20010901
1328 PHP_EXT_HTTP_VERSION,
1329 #endif
1330 STANDARD_MODULE_PROPERTIES
1331 };
1332 /* }}} */
1333
1334 /* {{{ proto string http_date([int timestamp])
1335 *
1336 * This function returns a valid HTTP date regarding RFC 822/1123
1337 * looking like: "Wed, 22 Dec 2004 11:34:47 GMT"
1338 *
1339 */
1340 PHP_FUNCTION(http_date)
1341 {
1342 long t = -1;
1343
1344 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1345 RETURN_FALSE;
1346 }
1347
1348 if (t == -1) {
1349 t = (long) time(NULL);
1350 }
1351
1352 RETURN_STRING(http_date(t), 0);
1353 }
1354 /* }}} */
1355
1356 /* {{{ proto string http_absolute_uri(string url[, string proto])
1357 *
1358 * This function returns an absolute URI constructed from url.
1359 * If the url is already abolute but a different proto was supplied,
1360 * only the proto part of the URI will be updated. If url has no
1361 * path specified, the path of the current REQUEST_URI will be taken.
1362 * The host will be taken either from the Host HTTP header of the client
1363 * the SERVER_NAME or just localhost if prior are not available.
1364 *
1365 * Some examples:
1366 * <pre>
1367 * url = "page.php" => http://www.example.com/current/path/page.php
1368 * url = "/page.php" => http://www.example.com/page.php
1369 * url = "/page.php", proto = "https" => https://www.example.com/page.php
1370 * </pre>
1371 *
1372 */
1373 PHP_FUNCTION(http_absolute_uri)
1374 {
1375 char *url = NULL, *proto = NULL;
1376 int url_len = 0, proto_len = 0;
1377
1378 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &url, &url_len, &proto, &proto_len) != SUCCESS) {
1379 RETURN_FALSE;
1380 }
1381
1382 RETURN_STRING(http_absolute_uri(url, proto), 0);
1383 }
1384 /* }}} */
1385
1386 /* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US'])
1387 *
1388 * This function negotiates the clients preferred language based on its
1389 * Accept-Language HTTP header. It returns the negotiated language or
1390 * the default language if none match.
1391 *
1392 * The qualifier is recognized and languages without qualifier are rated highest.
1393 *
1394 * The supported parameter is expected to be an array having
1395 * the supported languages as array values.
1396 *
1397 * Example:
1398 * <pre>
1399 * <?php
1400 * $langs = array(
1401 * 'en-US',// default
1402 * 'fr',
1403 * 'fr-FR',
1404 * 'de',
1405 * 'de-DE',
1406 * 'de-AT',
1407 * 'de-CH',
1408 * );
1409 * include './langs/'. http_negotiate_language($langs) .'.php';
1410 * ?>
1411 * </pre>
1412 *
1413 */
1414 PHP_FUNCTION(http_negotiate_language)
1415 {
1416 zval *supported;
1417 char *def = NULL;
1418 int def_len = 0;
1419
1420 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
1421 RETURN_FALSE;
1422 }
1423
1424 if (!def) {
1425 def = "en-US";
1426 }
1427
1428 RETURN_STRING(http_negotiate_language(supported, def), 0);
1429 }
1430 /* }}} */
1431
1432 /* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1'])
1433 *
1434 * This function negotiates the clients preferred charset based on its
1435 * Accept-Charset HTTP header. It returns the negotiated charset or
1436 * the default charset if none match.
1437 *
1438 * The qualifier is recognized and charset without qualifier are rated highest.
1439 *
1440 * The supported parameter is expected to be an array having
1441 * the supported charsets as array values.
1442 *
1443 * Example:
1444 * <pre>
1445 * <?php
1446 * $charsets = array(
1447 * 'iso-8859-1', // default
1448 * 'iso-8859-2',
1449 * 'iso-8859-15',
1450 * 'utf-8'
1451 * );
1452 * $pref = http_negotiate_charset($charsets);
1453 * if (!strcmp($pref, 'iso-8859-1')) {
1454 * iconv_set_encoding('internal_encoding', 'iso-8859-1');
1455 * iconv_set_encoding('output_encoding', $pref);
1456 * ob_start('ob_iconv_handler');
1457 * }
1458 * ?>
1459 * </pre>
1460 */
1461 PHP_FUNCTION(http_negotiate_charset)
1462 {
1463 zval *supported;
1464 char *def = NULL;
1465 int def_len = 0;
1466
1467 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) {
1468 RETURN_FALSE;
1469 }
1470
1471 if (!def) {
1472 def = "iso-8859-1";
1473 }
1474
1475 RETURN_STRING(http_negotiate_charset(supported, def), 0);
1476 }
1477 /* }}} */
1478
1479 /* {{{ proto bool http_send_status(int status)
1480 *
1481 * Send HTTP status code.
1482 *
1483 */
1484 PHP_FUNCTION(http_send_status)
1485 {
1486 int status = 0;
1487
1488 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) {
1489 RETURN_FALSE;
1490 }
1491 if (status < 100 || status > 510) {
1492 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid HTTP status code (100-510): %d", status);
1493 RETURN_FALSE;
1494 }
1495
1496 RETURN_SUCCESS(http_send_status(status));
1497 }
1498 /* }}} */
1499
1500 /* {{{ proto bool http_send_last_modified([int timestamp])
1501 *
1502 * This converts the given timestamp to a valid HTTP date and
1503 * sends it as "Last-Modified" HTTP header. If timestamp is
1504 * omitted, current time is sent.
1505 *
1506 */
1507 PHP_FUNCTION(http_send_last_modified)
1508 {
1509 long t = -1;
1510
1511 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1512 RETURN_FALSE;
1513 }
1514
1515 if (t == -1) {
1516 t = (long) time(NULL);
1517 }
1518
1519 RETURN_SUCCESS(http_send_last_modified(t));
1520 }
1521 /* }}} */
1522
1523 /* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream'])
1524 *
1525 * Sets the content type.
1526 *
1527 */
1528 PHP_FUNCTION(http_send_content_type)
1529 {
1530 char *ct;
1531 int ct_len = 0;
1532
1533 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) {
1534 RETURN_FALSE;
1535 }
1536
1537 if (!ct_len) {
1538 RETURN_SUCCESS(http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1));
1539 }
1540 RETURN_SUCCESS(http_send_content_type(ct, ct_len));
1541 }
1542 /* }}} */
1543
1544 /* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false])
1545 *
1546 * Set the Content Disposition. The Content-Disposition header is very useful
1547 * if the data actually sent came from a file or something similar, that should
1548 * be "saved" by the client/user (i.e. by browsers "Save as..." popup window).
1549 *
1550 */
1551 PHP_FUNCTION(http_send_content_disposition)
1552 {
1553 char *filename;
1554 int f_len;
1555 zend_bool send_inline = 0;
1556
1557 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) {
1558 RETURN_FALSE;
1559 }
1560 RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline));
1561 }
1562 /* }}} */
1563
1564 /* {{{ proto bool http_match_modified([int timestamp])
1565 *
1566 * Matches the given timestamp against the clients "If-Modified-Since" resp.
1567 * "If-Unmodified-Since" HTTP headers.
1568 *
1569 */
1570 PHP_FUNCTION(http_match_modified)
1571 {
1572 long t = -1;
1573
1574 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) {
1575 RETURN_FALSE;
1576 }
1577
1578 // current time if not supplied (senseless though)
1579 if (t == -1) {
1580 t = (long) time(NULL);
1581 }
1582
1583 RETURN_BOOL(http_modified_match("HTTP_IF_MODIFIED_SINCE", t) || http_modified_match("HTTP_IF_UNMODIFIED_SINCE", t));
1584 }
1585 /* }}} */
1586
1587 /* {{{ proto bool http_match_etag(string etag)
1588 *
1589 * This matches the given ETag against the clients
1590 * "If-Match" resp. "If-None-Match" HTTP headers.
1591 *
1592 */
1593 PHP_FUNCTION(http_match_etag)
1594 {
1595 int etag_len;
1596 char *etag;
1597
1598 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len) != SUCCESS) {
1599 RETURN_FALSE;
1600 }
1601
1602 RETURN_BOOL(http_etag_match("HTTP_IF_NONE_MATCH", etag) || http_etag_match("HTTP_IF_MATCH", etag));
1603 }
1604 /* }}} */
1605
1606 /* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]])
1607 *
1608 * If timestamp_or_exires is greater than 0, it is handled as timestamp
1609 * and will be sent as date of last modification. If it is 0 or omitted,
1610 * the current time will be sent as Last-Modified date. If it's negative,
1611 * it is handled as expiration time in seconds, which means that if the
1612 * requested last modification date is not between the calculated timespan,
1613 * the Last-Modified header is updated and the actual body will be sent.
1614 *
1615 */
1616 PHP_FUNCTION(http_cache_last_modified)
1617 {
1618 long last_modified = 0, send_modified = 0, t;
1619 zval *zlm;
1620
1621 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) {
1622 RETURN_FALSE;
1623 }
1624
1625 t = (long) time(NULL);
1626
1627 /* 0 or omitted */
1628 if (!last_modified) {
1629 /* does the client have? (att: caching "forever") */
1630 if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) {
1631 last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm));
1632 /* send current time */
1633 } else {
1634 send_modified = t;
1635 }
1636 /* negative value is supposed to be expiration time */
1637 } else if (last_modified < 0) {
1638 last_modified += t;
1639 send_modified = t;
1640 /* send supplied time explicitly */
1641 } else {
1642 send_modified = last_modified;
1643 }
1644
1645 RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1));
1646 }
1647 /* }}} */
1648
1649 /* {{{ proto bool http_cache_etag([string etag])
1650 *
1651 * This function attempts to cache the HTTP body based on an ETag,
1652 * either supplied or generated through calculation of the MD5
1653 * checksum of the output (uses output buffering).
1654 *
1655 * If clients "If-None-Match" header matches the supplied/calculated
1656 * ETag, the body is considered cached on the clients side and
1657 * a "304 Not Modified" status code is issued.
1658 *
1659 */
1660 PHP_FUNCTION(http_cache_etag)
1661 {
1662 char *etag;
1663 int etag_len = 0;
1664
1665 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) {
1666 RETURN_FALSE;
1667 }
1668
1669 RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1));
1670 }
1671 /* }}} */
1672
1673 /* {{{ proto string ob_httpetaghandler(string data, int mode)
1674 *
1675 * For use with ob_start().
1676 * Note that this has to be started as first output buffer.
1677 * WARNING: Don't use with http_send_*().
1678 */
1679 PHP_FUNCTION(ob_httpetaghandler)
1680 {
1681 char *data;
1682 int data_len;
1683 long mode;
1684
1685 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) {
1686 RETURN_FALSE;
1687 }
1688
1689 if (mode & PHP_OUTPUT_HANDLER_START) {
1690 if (HTTP_G(etag_started)) {
1691 php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler can only be used once");
1692 RETURN_STRINGL(data, data_len, 1);
1693 }
1694 http_send_header("Cache-Control: " HTTP_DEFAULT_CACHECONTROL);
1695 HTTP_G(etag_started) = 1;
1696 }
1697
1698 if (OG(ob_nesting_level) > 1) {
1699 php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler must be started prior to other output buffers");
1700 RETURN_STRINGL(data, data_len, 1);
1701 }
1702
1703 Z_TYPE_P(return_value) = IS_STRING;
1704 http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), mode);
1705 }
1706 /* }}} */
1707
1708 /* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]])
1709 *
1710 * Redirect to a given url.
1711 * The supplied url will be expanded with http_absolute_uri(), the params array will
1712 * be treated with http_build_query() and the session identification will be appended
1713 * if session is true.
1714 *
1715 * Depending on permanent the redirection will be issued with a permanent
1716 * ("301 Moved Permanently") or a temporary ("302 Found") redirection
1717 * status code.
1718 *
1719 * To be RFC compliant, "Redirecting to <a>URI</a>." will be displayed,
1720 * if the client doesn't redirect immediatly.
1721 */
1722 PHP_FUNCTION(http_redirect)
1723 {
1724 int url_len;
1725 zend_bool session = 0, permanent = 0;
1726 zval *params = NULL;
1727 smart_str qstr = {0};
1728 char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34];
1729
1730 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, &params, &session, &permanent) != SUCCESS) {
1731 RETURN_FALSE;
1732 }
1733
1734 /* append session info */
1735 if (session && (PS(session_status) == php_session_active)) {
1736 if (!params) {
1737 MAKE_STD_ZVAL(params);
1738 array_init(params);
1739 }
1740 if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) {
1741 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not append session information");
1742 }
1743 }
1744
1745 /* treat params array with http_build_query() */
1746 if (params) {
1747 if (php_url_encode_hash_ex(Z_ARRVAL_P(params), &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) {
1748 if (qstr.c) {
1749 efree(qstr.c);
1750 }
1751 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode query parameters");
1752 RETURN_FALSE;
1753 }
1754 smart_str_0(&qstr);
1755 }
1756
1757 URI = http_absolute_uri(url, NULL);
1758 if (qstr.c) {
1759 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c);
1760 sprintf(RED, "Redirecting to <a href=\"%s?%s\">%s?%s</a>.\n", URI, qstr.c, URI, qstr.c);
1761 efree(qstr.c);
1762 } else {
1763 snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI);
1764 sprintf(RED, "Redirecting to <a href=\"%s\">%s</a>.\n", URI, URI);
1765 }
1766 efree(URI);
1767
1768 if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) {
1769 php_body_write(RED, strlen(RED) TSRMLS_CC);
1770 RETURN_TRUE;
1771 }
1772 RETURN_FALSE;
1773 }
1774 /* }}} */
1775
1776 /* {{{ proto bool http_send_data(string data)
1777 *
1778 * Sends raw data with support for (multiple) range requests.
1779 *
1780 */
1781 PHP_FUNCTION(http_send_data)
1782 {
1783 zval *zdata;
1784
1785 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) {
1786 RETURN_FALSE;
1787 }
1788
1789 convert_to_string_ex(&zdata);
1790 http_send_header("Accept-Ranges: bytes");
1791 RETURN_SUCCESS(http_send_data(zdata));
1792 }
1793 /* }}} */
1794
1795 /* {{{ proto bool http_send_file(string file)
1796 *
1797 * Sends a file with support for (multiple) range requests.
1798 *
1799 */
1800 PHP_FUNCTION(http_send_file)
1801 {
1802 zval *zfile;
1803
1804 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zfile) != SUCCESS) {
1805 RETURN_FALSE;
1806 }
1807
1808 convert_to_string_ex(&zfile);
1809 http_send_header("Accept-Ranges: bytes");
1810 RETURN_SUCCESS(http_send_file(zfile));
1811 }
1812 /* }}} */
1813
1814 /* {{{ proto bool http_send_stream(resource stream)
1815 *
1816 * Sends an already opened stream with support for (multiple) range requests.
1817 *
1818 */
1819 PHP_FUNCTION(http_send_stream)
1820 {
1821 zval *zstream;
1822 php_stream *file;
1823
1824 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) {
1825 RETURN_FALSE;
1826 }
1827
1828 php_stream_from_zval(file, &zstream);
1829 http_send_header("Accept-Ranges: bytes");
1830 RETURN_SUCCESS(http_send_stream(file));
1831 }
1832 /* }}} */
1833
1834 /* {{{ proto string http_chunked_decode(string encoded)
1835 *
1836 * This function decodes a string that was HTTP-chunked encoded.
1837 * Returns false on failure.
1838 */
1839 PHP_FUNCTION(http_chunked_decode)
1840 {
1841 char *encoded = NULL, *decoded = NULL;
1842 int encoded_len = 0, decoded_len = 0;
1843
1844 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) {
1845 RETURN_FALSE;
1846 }
1847
1848 if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
1849 RETURN_STRINGL(decoded, decoded_len, 0);
1850 } else {
1851 RETURN_FALSE;
1852 }
1853 }
1854 /* }}} */
1855
1856 /* {{{ proto array http_split_response(string http_response)
1857 *
1858 * This function splits an HTTP response into an array with headers and the
1859 * content body. The returned array may look simliar to the following example:
1860 *
1861 * <pre>
1862 * <?php
1863 * array(
1864 * 0 => array(
1865 * 'Status' => '200 Ok',
1866 * 'Content-Type' => 'text/plain',
1867 * 'Content-Language' => 'en-US'
1868 * ),
1869 * 1 => "Hello World!"
1870 * );
1871 * ?>
1872 * </pre>
1873 */
1874 PHP_FUNCTION(http_split_response)
1875 {
1876 zval *zresponse, *zbody, *zheaders;
1877
1878 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) {
1879 RETURN_FALSE;
1880 }
1881
1882 convert_to_string_ex(&zresponse);
1883
1884 MAKE_STD_ZVAL(zbody);
1885 MAKE_STD_ZVAL(zheaders);
1886 array_init(zheaders);
1887
1888 if (SUCCESS != http_split_response(zresponse, zheaders, zbody)) {
1889 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP response");
1890 RETURN_FALSE;
1891 }
1892
1893 array_init(return_value);
1894 add_index_zval(return_value, 0, zheaders);
1895 add_index_zval(return_value, 1, zbody);
1896 }
1897 /* }}} */
1898
1899 /* {{{ proto array http_parse_headers(string header)
1900 *
1901 */
1902 PHP_FUNCTION(http_parse_headers)
1903 {
1904 char *header, *rnrn;
1905 int header_len;
1906
1907 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) {
1908 RETURN_FALSE;
1909 }
1910
1911 array_init(return_value);
1912
1913 if (rnrn = strstr(header, HTTP_CRLF HTTP_CRLF)) {
1914 header_len = rnrn - header + 2;
1915 }
1916 if (SUCCESS != http_parse_headers(header, header_len, return_value)) {
1917 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP header");
1918 zval_dtor(return_value);
1919 RETURN_FALSE;
1920 }
1921 }
1922 /* }}}*/
1923
1924 /* {{{ proto array http_get_request_headers(void)
1925 *
1926 */
1927 PHP_FUNCTION(http_get_request_headers)
1928 {
1929 if (ZEND_NUM_ARGS()) {
1930 WRONG_PARAM_COUNT;
1931 }
1932
1933 array_init(return_value);
1934 http_get_request_headers(return_value);
1935 }
1936 /* }}} */
1937
1938 /* {{{ HAVE_CURL */
1939 #ifdef HTTP_HAVE_CURL
1940
1941 /* {{{ proto string http_get(string url[, array options[, array &info]])
1942 *
1943 * Performs an HTTP GET request on the supplied url.
1944 *
1945 * The second parameter is expected to be an associative
1946 * array where the following keys will be recognized:
1947 * <pre>
1948 * - redirect: int, whether and how many redirects to follow
1949 * - unrestrictedauth: bool, whether to continue sending credentials on
1950 * redirects to a different host
1951 * - proxyhost: string, proxy host in "host[:port]" format
1952 * - proxyport: int, use another proxy port as specified in proxyhost
1953 * - proxyauth: string, proxy credentials in "user:pass" format
1954 * - proxyauthtype: int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
1955 * - httpauth: string, http credentials in "user:pass" format
1956 * - httpauthtype: int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
1957 * - compress: bool, whether to allow gzip/deflate content encoding
1958 * (defaults to true)
1959 * - port: int, use another port as specified in the url
1960 * - referer: string, the referer to sends
1961 * - useragent: string, the user agent to send
1962 * (defaults to PECL::HTTP/version (PHP/version)))
1963 * - headers: array, list of custom headers as associative array
1964 * like array("header" => "value")
1965 * - cookies: array, list of cookies as associative array
1966 * like array("cookie" => "value")
1967 * - cookiestore: string, path to a file where cookies are/will be stored
1968 * </pre>
1969 *
1970 * The optional third parameter will be filled with some additional information
1971 * in form af an associative array, if supplied, like the following example:
1972 * <pre>
1973 * <?php
1974 * array (
1975 * 'effective_url' => 'http://localhost',
1976 * 'response_code' => 403,
1977 * 'total_time' => 0.017,
1978 * 'namelookup_time' => 0.013,
1979 * 'connect_time' => 0.014,
1980 * 'pretransfer_time' => 0.014,
1981 * 'size_upload' => 0,
1982 * 'size_download' => 202,
1983 * 'speed_download' => 11882,
1984 * 'speed_upload' => 0,
1985 * 'header_size' => 145,
1986 * 'request_size' => 62,
1987 * 'ssl_verifyresult' => 0,
1988 * 'filetime' => -1,
1989 * 'content_length_download' => 202,
1990 * 'content_length_upload' => 0,
1991 * 'starttransfer_time' => 0.017,
1992 * 'content_type' => 'text/html; charset=iso-8859-1',
1993 * 'redirect_time' => 0,
1994 * 'redirect_count' => 0,
1995 * 'private' => '',
1996 * 'http_connectcode' => 0,
1997 * 'httpauth_avail' => 0,
1998 * 'proxyauth_avail' => 0,
1999 * )
2000 * ?>
2001 * </pre>
2002 */
2003 PHP_FUNCTION(http_get)
2004 {
2005 char *URL, *data = NULL;
2006 size_t data_len = 0;
2007 int URL_len;
2008 zval *options = NULL, *info = NULL;
2009
2010 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
2011 RETURN_FALSE;
2012 }
2013
2014 if (info) {
2015 zval_dtor(info);
2016 array_init(info);
2017 }
2018
2019 if (SUCCESS == http_get(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2020 RETURN_STRINGL(data, data_len, 0);
2021 } else {
2022 RETURN_FALSE;
2023 }
2024 }
2025 /* }}} */
2026
2027 /* {{{ proto string http_head(string url[, array options[, array &info]])
2028 *
2029 * Performs an HTTP HEAD request on the suppied url.
2030 * Returns the HTTP response as string.
2031 * See http_get() for a full list of available options.
2032 */
2033 PHP_FUNCTION(http_head)
2034 {
2035 char *URL, *data = NULL;
2036 size_t data_len = 0;
2037 int URL_len;
2038 zval *options = NULL, *info = NULL;
2039
2040 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) {
2041 RETURN_FALSE;
2042 }
2043
2044 if (info) {
2045 zval_dtor(info);
2046 array_init(info);
2047 }
2048
2049 if (SUCCESS == http_head(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2050 RETURN_STRINGL(data, data_len, 0);
2051 } else {
2052 RETURN_FALSE;
2053 }
2054 }
2055 /* }}} */
2056
2057 /* {{{ proto string http_post_data(string url, string data[, array options[, &info]])
2058 *
2059 * Performs an HTTP POST request, posting data.
2060 * Returns the HTTP response as string.
2061 * See http_get() for a full list of available options.
2062 */
2063 PHP_FUNCTION(http_post_data)
2064 {
2065 char *URL, *postdata, *data = NULL;
2066 size_t data_len = 0;
2067 int postdata_len, URL_len;
2068 zval *options = NULL, *info = NULL;
2069
2070 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) {
2071 RETURN_FALSE;
2072 }
2073
2074 if (info) {
2075 zval_dtor(info);
2076 array_init(info);
2077 }
2078
2079 if (SUCCESS == http_post_data(URL, postdata, (size_t) postdata_len, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2080 RETURN_STRINGL(data, data_len, 0);
2081 } else {
2082 RETURN_FALSE;
2083 }
2084 }
2085 /* }}} */
2086
2087 /* {{{ proto string http_post_array(string url, array data[, array options[, array &info]])
2088 *
2089 * Performs an HTTP POST request, posting www-form-urlencoded array data.
2090 * Returns the HTTP response as string.
2091 * See http_get() for a full list of available options.
2092 */
2093 PHP_FUNCTION(http_post_array)
2094 {
2095 char *URL, *data = NULL;
2096 size_t data_len = 0;
2097 int URL_len;
2098 zval *options = NULL, *info = NULL, *postdata;
2099
2100 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a/!z", &URL, &URL_len, &postdata, &options, &info) != SUCCESS) {
2101 RETURN_FALSE;
2102 }
2103
2104 if (info) {
2105 zval_dtor(info);
2106 array_init(info);
2107 }
2108
2109 if (SUCCESS == http_post_array(URL, Z_ARRVAL_P(postdata), HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) {
2110 RETURN_STRINGL(data, data_len, 0);
2111 } else {
2112 RETURN_FALSE;
2113 }
2114 }
2115 /* }}} */
2116
2117 #endif
2118 /* }}} HAVE_CURL */
2119
2120
2121 /* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"])
2122 *
2123 * Example:
2124 * <pre>
2125 * <?php
2126 * if (!http_auth_basic('mike', 's3c|r3t')) {
2127 * die('<h1>Authorization failed!</h1>');
2128 * }
2129 * ?>
2130 * </pre>
2131 */
2132 PHP_FUNCTION(http_auth_basic)
2133 {
2134 char *realm = NULL, *user, *pass, *suser, *spass;
2135 int r_len, u_len, p_len;
2136
2137 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) {
2138 RETURN_FALSE;
2139 }
2140
2141 if (!realm) {
2142 realm = "Restricted";
2143 }
2144
2145 if (SUCCESS != http_auth_credentials(&suser, &spass)) {
2146 http_auth_header("Basic", realm);
2147 RETURN_FALSE;
2148 }
2149
2150 if (strcasecmp(suser, user)) {
2151 http_auth_header("Basic", realm);
2152 RETURN_FALSE;
2153 }
2154
2155 if (strcmp(spass, pass)) {
2156 http_auth_header("Basic", realm);
2157 RETURN_FALSE;
2158 }
2159
2160 RETURN_TRUE;
2161 }
2162 /* }}} */
2163
2164 /* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"])
2165 *
2166 * Example:
2167 * <pre>
2168 * <?php
2169 * function auth_cb($user, $pass)
2170 * {
2171 * global $db;
2172 * $query = 'SELECT pass FROM users WHERE user='. $db->quoteSmart($user);
2173 * if (strlen($realpass = $db->getOne($query)) {
2174 * return $pass === $realpass;
2175 * }
2176 * return false;
2177 * }
2178 *
2179 * if (!http_auth_basic_cb('auth_cb')) {
2180 * die('<h1>Authorization failed</h1>');
2181 * }
2182 * ?>
2183 * </pre>
2184 */
2185 PHP_FUNCTION(http_auth_basic_cb)
2186 {
2187 zval *cb;
2188 char *realm = NULL, *user, *pass;
2189 int r_len;
2190
2191 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) {
2192 RETURN_FALSE;
2193 }
2194
2195 if (!realm) {
2196 realm = "Restricted";
2197 }
2198
2199 if (SUCCESS != http_auth_credentials(&user, &pass)) {
2200 http_auth_header("Basic", realm);
2201 RETURN_FALSE;
2202 }
2203 {
2204 zval *zparams[2] = {NULL, NULL}, retval;
2205 int result = 0;
2206
2207 MAKE_STD_ZVAL(zparams[0]);
2208 MAKE_STD_ZVAL(zparams[1]);
2209 ZVAL_STRING(zparams[0], user, 0);
2210 ZVAL_STRING(zparams[1], pass, 0);
2211
2212 if (SUCCESS == call_user_function(EG(function_table), NULL, cb,
2213 &retval, 2, zparams TSRMLS_CC)) {
2214 result = Z_LVAL(retval);
2215 }
2216
2217 efree(user);
2218 efree(pass);
2219 efree(zparams[0]);
2220 efree(zparams[1]);
2221
2222 if (!result) {
2223 http_auth_header("Basic", realm);
2224 }
2225
2226 RETURN_BOOL(result);
2227 }
2228 }
2229 /* }}}*/
2230
2231
2232 /* {{{ php_http_init_globals(zend_http_globals *) */
2233 static void php_http_init_globals(zend_http_globals *http_globals)
2234 {
2235 http_globals->etag_started = 0;
2236 http_globals->ctype = NULL;
2237 http_globals->etag = NULL;
2238 http_globals->lmod = 0;
2239 #ifdef HTTP_HAVE_CURL
2240 http_globals->curlbuf.body.data = NULL;
2241 http_globals->curlbuf.body.used = 0;
2242 http_globals->curlbuf.body.free = 0;
2243 http_globals->curlbuf.hdrs.data = NULL;
2244 http_globals->curlbuf.hdrs.used = 0;
2245 http_globals->curlbuf.hdrs.free = 0;
2246 #endif
2247 http_globals->allowed_methods = NULL;
2248 }
2249 /* }}} */
2250
2251 /* {{{ static inline STATUS http_check_allowed_methods(char *, int) */
2252 #define http_check_allowed_methods(m, l) _http_check_allowed_methods((m), (l) TSRMLS_CC)
2253 static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC)
2254 {
2255 if (length && SG(request_info).request_method && (!strstr(methods, SG(request_info).request_method))) {
2256 char *allow_header = emalloc(length + sizeof("Allow: "));
2257 sprintf(allow_header, "Allow: %s", methods);
2258 http_send_header(allow_header);
2259 efree(allow_header);
2260 http_send_status(405);
2261 zend_bailout();
2262 }
2263 }
2264 /* }}} */
2265
2266 /* {{{ PHP_INI */
2267 PHP_INI_MH(update_allowed_methods)
2268 {
2269 http_check_allowed_methods(new_value, new_value_length);
2270 return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
2271 }
2272
2273 PHP_INI_BEGIN()
2274 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)
2275 PHP_INI_END()
2276 /* }}} */
2277
2278 /* {{{ PHP_MINIT_FUNCTION */
2279 PHP_MINIT_FUNCTION(http)
2280 {
2281 ZEND_INIT_MODULE_GLOBALS(http, php_http_init_globals, NULL);
2282 REGISTER_INI_ENTRIES();
2283
2284 #ifdef HTTP_HAVE_CURL
2285 REGISTER_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC, CONST_CS | CONST_PERSISTENT);
2286 REGISTER_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS | CONST_PERSISTENT);
2287 REGISTER_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM, CONST_CS | CONST_PERSISTENT);
2288 #endif
2289
2290 #ifdef ZEND_ENGINE_2
2291 HTTP_REGISTER_CLASS(HTTPi, httpi, NULL, ZEND_ACC_FINAL_CLASS);
2292 HTTP_REGISTER_CLASS_EX(HTTPi_Response, httpi_response, NULL, 0);
2293 # ifdef HTTP_HAVE_CURL
2294 HTTP_REGISTER_CLASS_EX(HTTPi_Request, httpi_request, NULL, 0);
2295 REGISTER_LONG_CONSTANT("HTTP_GET", HTTP_GET, CONST_CS | CONST_PERSISTENT);
2296 REGISTER_LONG_CONSTANT("HTTP_HEAD", HTTP_HEAD, CONST_CS | CONST_PERSISTENT);
2297 REGISTER_LONG_CONSTANT("HTTP_POST", HTTP_POST, CONST_CS | CONST_PERSISTENT);
2298 # endif /* HTTP_HAVE_CURL */
2299 #endif /* ZEND_ENGINE_2 */
2300 return SUCCESS;
2301 }
2302 /* }}} */
2303
2304 /* {{{ PHP_MSHUTDOWN_FUNCTION */
2305 PHP_MSHUTDOWN_FUNCTION(http)
2306 {
2307 UNREGISTER_INI_ENTRIES();
2308 return SUCCESS;
2309 }
2310 /* }}} */
2311
2312 /* {{{ PHP_RINIT_FUNCTION */
2313 PHP_RINIT_FUNCTION(http)
2314 {
2315 char *allowed_methods = INI_STR("http.allowed_methods");
2316 http_check_allowed_methods(allowed_methods, strlen(allowed_methods));
2317 return SUCCESS;
2318 }
2319 /* }}} */
2320
2321 /* {{{ PHP_RSHUTDOWN_FUNCTION */
2322 PHP_RSHUTDOWN_FUNCTION(http)
2323 {
2324 HTTP_G(etag_started) = 0;
2325 HTTP_G(lmod) = 0;
2326
2327 if (HTTP_G(etag)) {
2328 efree(HTTP_G(etag));
2329 HTTP_G(etag) = NULL;
2330 }
2331
2332 if (HTTP_G(ctype)) {
2333 efree(HTTP_G(ctype));
2334 HTTP_G(ctype) = NULL;
2335 }
2336 #ifdef HTTP_HAVE_CURL
2337 if (HTTP_G(curlbuf).body.data) {
2338 efree(HTTP_G(curlbuf).body.data);
2339 HTTP_G(curlbuf).body.data = NULL;
2340 }
2341 if (HTTP_G(curlbuf).hdrs.data) {
2342 efree(HTTP_G(curlbuf).hdrs.data);
2343 HTTP_G(curlbuf).hdrs.data = NULL;
2344 }
2345 #endif
2346 return SUCCESS;
2347 }
2348 /* }}} */
2349
2350 /* {{{ PHP_MINFO_FUNCTION */
2351 PHP_MINFO_FUNCTION(http)
2352 {
2353 php_info_print_table_start();
2354 php_info_print_table_header(2, "Extended HTTP support", "enabled");
2355 php_info_print_table_row(2, "Version:", PHP_EXT_HTTP_VERSION);
2356 php_info_print_table_row(2, "cURL convenience functions:",
2357 #ifdef HTTP_HAVE_CURL
2358 "enabled"
2359 #else
2360 "disabled"
2361 #endif
2362 );
2363 php_info_print_table_end();
2364
2365 DISPLAY_INI_ENTRIES();
2366 }
2367 /* }}} */
2368
2369 /*
2370 * Local variables:
2371 * tab-width: 4
2372 * c-basic-offset: 4
2373 * End:
2374 * vim600: noet sw=4 ts=4 fdm=marker
2375 * vim<600: noet sw=4 ts=4
2376 */
2377