- highly experimental stream wrapper
[m6w6/ext-http] / http_wrapper_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #define HTTP_WANT_CURL
20 #include "php_http.h"
21
22 #if defined(HTTP_HAVE_CURL) && defined(HTTP_HAVE_WRAPPER)
23
24 #include "php_streams.h"
25
26 #include "php_http_api.h"
27 #include "php_http_wrapper_api.h"
28 #include "php_http_request_api.h"
29 #include "php_http_headers_api.h"
30 #include "php_http_message_api.h"
31 #include "php_http_url_api.h"
32
33 ZEND_EXTERN_MODULE_GLOBALS(http);
34
35 typedef struct {
36 phpstr b;
37 size_t p;
38 } http_wrapper_t;
39
40 static php_stream_ops http_stream_ops;
41
42 PHP_MINIT_FUNCTION(http_wrapper)
43 {
44 php_unregister_url_stream_wrapper("http" TSRMLS_CC);
45 return php_register_url_stream_wrapper("http", &http_wrapper TSRMLS_CC);
46 }
47
48 php_stream *http_wrapper_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
49 {
50 zval opt_array, **opt_elem, *ssl_opt;
51 http_request request;
52 php_stream *stream;
53
54 if (strpbrk(mode, "awx+")) {
55 php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP wrapper does not support writeable connections.");
56 return NULL;
57 }
58
59 INIT_PZVAL(&opt_array);
60 array_init(&opt_array);
61
62 http_request_init(&request);
63 request.url = estrdup(path);
64
65 /*
66 HTTP options
67 */
68
69 /* request method */
70 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "method", &opt_elem)) {
71 switch (Z_TYPE_PP(opt_elem))
72 {
73 case IS_LONG:
74 if (http_request_method_exists(0, Z_LVAL_PP(opt_elem), NULL)) {
75 request.meth = Z_LVAL_PP(opt_elem);
76 }
77 break;
78 default:
79 {
80 ulong method;
81 zval *orig = *opt_elem;
82 convert_to_string_ex(opt_elem);
83 if ((method = http_request_method_exists(1, 0, Z_STRVAL_PP(opt_elem)))) {
84 request.meth = method;
85 }
86 if (orig != *opt_elem) zval_ptr_dtor(opt_elem);
87 }
88 break;
89 }
90 }
91
92 /* header */
93 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "header", &opt_elem)) {
94 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
95 zval *headers;
96
97 MAKE_STD_ZVAL(headers);
98 array_init(headers);
99 if (SUCCESS == http_parse_headers(Z_STRVAL_PP(opt_elem), headers)) {
100 add_assoc_zval(&opt_array, "headers", headers);
101 } else {
102 zval_ptr_dtor(&headers);
103 }
104 }
105 }
106
107 /* user_agent */
108 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "user_agent", &opt_elem)) {
109 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
110 add_assoc_stringl(&opt_array, "useragent", Z_STRVAL_PP(opt_elem), Z_STRLEN_PP(opt_elem), 1);
111 }
112 }
113
114 /* proxy */
115 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "proxy", &opt_elem)) {
116 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
117 if (strstr(Z_STRVAL_PP(opt_elem), "://")) {
118 if (!strncasecmp(Z_STRVAL_PP(opt_elem), "tcp://", lenof("tcp://"))) {
119 phpstr proxy;
120
121 phpstr_init(&proxy);
122 phpstr_appends(&proxy, "http://");
123 phpstr_append(&proxy, Z_STRVAL_PP(opt_elem)+lenof("tcp://"), Z_STRLEN_PP(opt_elem)-lenof("tcp://"));
124 phpstr_fix(&proxy);
125
126 add_assoc_stringl(&opt_array, "proxy", PHPSTR_VAL(&proxy), PHPSTR_LEN(&proxy), 0);
127 }
128 } else {
129 ZVAL_ADDREF(*opt_elem);
130 add_assoc_zval(&opt_array, "proxy", *opt_elem);
131 }
132 }
133 }
134
135 /* redirect */
136 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "max_redirects", &opt_elem)) {
137 zval *orig = *opt_elem;
138
139 convert_to_long_ex(opt_elem);
140 if (0 < Z_LVAL_PP(opt_elem)) {
141 add_assoc_long(&opt_array, "redirect", Z_LVAL_PP(opt_elem)-1);
142 }
143 if (orig != *opt_elem) {
144 zval_ptr_dtor(opt_elem);
145 }
146 }
147
148 /* request body */
149 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "content", &opt_elem)) {
150 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
151 request.body = http_request_body_init_ex(request.body, HTTP_REQUEST_BODY_CSTRING,
152 estrndup(Z_STRVAL_PP(opt_elem), Z_STRLEN_PP(opt_elem)), Z_STRLEN_PP(opt_elem), 1);
153 }
154 }
155
156 /*
157 SSL options
158 */
159
160 MAKE_STD_ZVAL(ssl_opt);
161 array_init(ssl_opt);
162
163 /* verify_peer */
164 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "verify_peer", &opt_elem)) {
165 if (zval_is_true(*opt_elem)) {
166 add_assoc_bool(ssl_opt, "verifypeer", 1);
167 }
168 }
169
170 /* cafile */
171 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "cafile", &opt_elem)) {
172 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
173 ZVAL_ADDREF(*opt_elem);
174 add_assoc_zval(ssl_opt, "cainfo", *opt_elem);
175 }
176 }
177
178 /* capath */
179 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "capath", &opt_elem)) {
180 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
181 ZVAL_ADDREF(*opt_elem);
182 add_assoc_zval(ssl_opt, "capath", *opt_elem);
183 }
184 }
185
186 /* local_cert */
187 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "local_cert", &opt_elem)) {
188 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
189 zval **pass = NULL;
190
191 ZVAL_ADDREF(*opt_elem);
192 add_assoc_zval(ssl_opt, "cert", *opt_elem);
193
194 if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "passphrase", &opt_elem)) {
195 if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) {
196 ZVAL_ADDREF(*opt_elem);
197 add_assoc_zval(ssl_opt, "certpasswd", *opt_elem);
198 }
199 }
200 }
201 }
202
203 add_assoc_zval(&opt_array, "ssl", ssl_opt);
204
205 if (SUCCESS == http_request_prepare(&request, Z_ARRVAL(opt_array))) {
206 http_message *msg;
207
208 http_request_exec(&request);
209
210 if (opened_path) {
211 char *url;
212 if (CURLE_OK == curl_easy_getinfo(request.ch, CURLINFO_EFFECTIVE_URL, &url) && url) {
213 *opened_path = estrdup(url);
214 } else {
215 *opened_path = NULL;
216 }
217 }
218
219 if ((msg = http_message_parse(PHPSTR_VAL(&request.conv.response), PHPSTR_LEN(&request.conv.response)))) {
220 http_wrapper_t *w = emalloc(sizeof(http_wrapper_t));
221
222 w->p = 0;
223 phpstr_init(&w->b);
224 phpstr_append(&w->b, PHPSTR_VAL(&msg->body), PHPSTR_LEN(&msg->body));
225
226 stream = php_stream_alloc(&http_stream_ops, w, NULL, mode);
227
228 http_message_free(&msg);
229 }
230 }
231
232 http_request_dtor(&request);
233
234 zval_dtor(&opt_array);
235 return stream;
236 }
237
238 static size_t http_wrapper_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
239 {
240 return 0;
241 }
242
243 static size_t http_wrapper_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
244 {
245 http_wrapper_t *w = (http_wrapper_t *) stream->abstract;
246 size_t len = MIN(count, w->b.used - w->p);
247
248 if (len) {
249 memcpy(buf, w->b.data + w->p, len);
250 w->p += len;
251 }
252
253 return len;
254 }
255
256 static int http_wrapper_close(php_stream *stream, int close_handle TSRMLS_DC)
257 {
258 phpstr_dtor(&((http_wrapper_t *)stream->abstract)->b);
259 efree(stream->abstract);
260 stream->abstract = NULL;
261 return 0;
262 }
263
264 static int http_wrapper_flush(php_stream *stream TSRMLS_DC)
265 {
266 return 0;
267 }
268
269 static int http_wrapper_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
270 {
271 http_wrapper_t *w = (http_wrapper_t *) stream->abstract;
272 off_t o;
273
274 *newoffset = w->p;
275
276 switch (whence)
277 {
278 case SEEK_SET:
279 if (offset < 0 || offset > w->b.used) {
280 return -1;
281 }
282 w->p = offset;
283 break;
284
285 case SEEK_END:
286 o = w->b.used + offset;
287 if (o < 0 || o > w->b.used) {
288 return -1;
289 }
290 w->p = o;
291 break;
292
293 case SEEK_CUR:
294 o = w->p + offset;
295 if (o < 0 || o > w->b.used) {
296 return -1;
297 }
298 w->p = o;
299 break;
300
301 default:
302 return -1;
303 break;
304 }
305
306 *newoffset = w->p;
307
308 return 0;
309 }
310
311 static php_stream_ops http_stream_ops = {
312 http_wrapper_write,
313 http_wrapper_read,
314 http_wrapper_close,
315 http_wrapper_flush,
316 "http",
317 http_wrapper_seek,
318 NULL,
319 NULL,
320 NULL,
321 };
322
323 static php_stream_wrapper_ops http_wrapper_ops = {
324 http_wrapper_ex,
325 NULL,
326 NULL,
327 NULL,
328 NULL,
329 "http",
330 NULL,
331 NULL,
332 NULL,
333 NULL
334 };
335
336 PHP_HTTP_API php_stream_wrapper http_wrapper = {
337 &http_wrapper_ops,
338 NULL,
339 1
340 };
341
342 #endif /* HTTP_HAVE_CURL */
343
344 /*
345 * Local variables:
346 * tab-width: 4
347 * c-basic-offset: 4
348 * End:
349 * vim600: noet sw=4 ts=4 fdm=marker
350 * vim<600: noet sw=4 ts=4
351 */
352
353