1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: streams.c 290796 2009-11-15 20:30:57Z felipe $ */
20 :
21 : /* This file implements cURL based wrappers.
22 : * NOTE: If you are implementing your own streams that are intended to
23 : * work independently of wrappers, this is not a good example to follow!
24 : **/
25 :
26 : #ifdef HAVE_CONFIG_H
27 : #include "config.h"
28 : #endif
29 :
30 : #include "php.h"
31 : #include "php_memory_streams.h"
32 :
33 : #if HAVE_CURL
34 :
35 : #include <stdio.h>
36 : #include <string.h>
37 :
38 : #ifdef PHP_WIN32
39 : #include <winsock2.h>
40 : #include <sys/types.h>
41 : #endif
42 :
43 : #include <curl/curl.h>
44 : #include <curl/easy.h>
45 :
46 : #define SMART_STR_PREALLOC 4096
47 :
48 : #include "ext/standard/php_smart_str.h"
49 : #include "ext/standard/info.h"
50 : #include "ext/standard/file.h"
51 : #include "ext/standard/php_string.h"
52 : #include "php_curl.h"
53 :
54 : static size_t on_data_available(char *data, size_t size, size_t nmemb, void *ctx)
55 78 : {
56 78 : php_stream *stream = (php_stream *) ctx;
57 78 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
58 : size_t wrote;
59 : TSRMLS_FETCH();
60 :
61 : /* TODO: I'd like to deprecate this.
62 : * This code is here because until we start getting real data, we don't know
63 : * if we have had all of the headers
64 : * */
65 78 : if (curlstream->readbuffer.writepos == 0) {
66 : zval *sym;
67 :
68 24 : if (!EG(active_symbol_table)) {
69 8 : zend_rebuild_symbol_table(TSRMLS_C);
70 : }
71 24 : MAKE_STD_ZVAL(sym);
72 24 : *sym = *curlstream->headers;
73 24 : zval_copy_ctor(sym);
74 24 : ZEND_SET_SYMBOL(EG(active_symbol_table), "http_response_header", sym);
75 : }
76 :
77 78 : php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.writepos, SEEK_SET);
78 78 : wrote = php_stream_write(curlstream->readbuffer.buf, data, size * nmemb);
79 78 : curlstream->readbuffer.writepos = php_stream_tell(curlstream->readbuffer.buf);
80 :
81 78 : return wrote;
82 : }
83 :
84 : /* cURL guarantees that headers are written as complete lines, with this function
85 : * called once for each header */
86 : static size_t on_header_available(char *data, size_t size, size_t nmemb, void *ctx)
87 243 : {
88 243 : size_t length = size * nmemb;
89 : zval *header;
90 243 : php_stream *stream = (php_stream *) ctx;
91 243 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
92 : TSRMLS_FETCH();
93 :
94 243 : if (length < 2) {
95 : /* invalid header ? */
96 0 : return length;
97 : }
98 :
99 243 : if (!(length == 2 && data[0] == '\r' && data[1] == '\n')) {
100 210 : MAKE_STD_ZVAL(header);
101 210 : Z_STRLEN_P(header) = length;
102 210 : Z_STRVAL_P(header) = estrndup(data, length);
103 210 : if (Z_STRVAL_P(header)[length-1] == '\n') {
104 210 : Z_STRVAL_P(header)[length-1] = '\0';
105 210 : Z_STRLEN_P(header)--;
106 :
107 210 : if (Z_STRVAL_P(header)[length-2] == '\r') {
108 210 : Z_STRVAL_P(header)[length-2] = '\0';
109 210 : Z_STRLEN_P(header)--;
110 : }
111 : }
112 210 : Z_TYPE_P(header) = IS_STRING;
113 210 : zend_hash_next_index_insert(Z_ARRVAL_P(curlstream->headers), &header, sizeof(zval *), NULL);
114 :
115 : /* based on the header, we might need to trigger a notification */
116 210 : if (!strncasecmp(data, "Location: ", 10)) {
117 13 : php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_REDIRECTED, data + 10, 0);
118 197 : } else if (!strncasecmp(data, "Content-Type: ", 14)) {
119 13 : php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, data + 14, 0);
120 184 : } else if (!strncasecmp(data, "Context-Length: ", 16)) {
121 0 : php_stream_notify_file_size(stream->context, atoi(data + 16), data, 0);
122 0 : php_stream_notify_progress_init(stream->context, 0, 0);
123 : }
124 : }
125 243 : return length;
126 :
127 : }
128 :
129 : static int on_progress_avail(php_stream *stream, double dltotal, double dlnow, double ultotal, double ulnow)
130 73 : {
131 : TSRMLS_FETCH();
132 :
133 : /* our notification system only works in a single direction; we should detect which
134 : * direction is important and use the correct values in this call */
135 73 : php_stream_notify_progress(stream->context, (size_t) dlnow, (size_t) dltotal);
136 73 : return 0;
137 : }
138 :
139 : static size_t php_curl_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
140 0 : {
141 0 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
142 :
143 0 : if (curlstream->writebuffer.buf) {
144 0 : return php_stream_write(curlstream->writebuffer.buf, buf, count);
145 : }
146 :
147 0 : return 0;
148 : }
149 :
150 : static size_t php_curl_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
151 107 : {
152 107 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
153 107 : size_t didread = 0;
154 :
155 107 : if (curlstream->readbuffer.readpos >= curlstream->readbuffer.writepos && curlstream->pending) {
156 : /* we need to read some more data */
157 : struct timeval tv;
158 :
159 : /* fire up the connection */
160 75 : if (curlstream->readbuffer.writepos == 0) {
161 25 : while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlstream->multi, &curlstream->pending));
162 : }
163 :
164 : do {
165 : /* get the descriptors from curl */
166 57205 : curl_multi_fdset(curlstream->multi, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &curlstream->maxfd);
167 :
168 : /* if we are in blocking mode, set a timeout */
169 57205 : tv.tv_usec = 0;
170 57205 : tv.tv_sec = 15; /* TODO: allow this to be configured from the script */
171 :
172 : /* wait for data */
173 : switch ((curlstream->maxfd < 0) ? 1 :
174 57205 : select(curlstream->maxfd + 1, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &tv)) {
175 : case -1:
176 : /* error */
177 0 : return 0;
178 : case 0:
179 : /* no data yet: timed-out */
180 0 : return 0;
181 : default:
182 : /* fetch the data */
183 : do {
184 57273 : curlstream->mcode = curl_multi_perform(curlstream->multi, &curlstream->pending);
185 57273 : } while (curlstream->mcode == CURLM_CALL_MULTI_PERFORM);
186 : }
187 : } while (curlstream->maxfd >= 0 &&
188 57205 : curlstream->readbuffer.readpos >= curlstream->readbuffer.writepos && curlstream->pending > 0);
189 :
190 : }
191 :
192 : /* if there is data in the buffer, try and read it */
193 107 : if (curlstream->readbuffer.writepos > 0 && curlstream->readbuffer.readpos < curlstream->readbuffer.writepos) {
194 75 : php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.readpos, SEEK_SET);
195 75 : didread = php_stream_read(curlstream->readbuffer.buf, buf, count);
196 75 : curlstream->readbuffer.readpos = php_stream_tell(curlstream->readbuffer.buf);
197 : }
198 :
199 107 : if (didread == 0) {
200 32 : stream->eof = 1;
201 : }
202 :
203 107 : return didread;
204 : }
205 :
206 : static int php_curl_stream_close(php_stream *stream, int close_handle TSRMLS_DC)
207 30 : {
208 30 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
209 :
210 : /* TODO: respect the close_handle flag here, so that casting to a FILE* on
211 : * systems without fopencookie will work properly */
212 :
213 30 : curl_multi_remove_handle(curlstream->multi, curlstream->curl);
214 30 : curl_easy_cleanup(curlstream->curl);
215 30 : curl_multi_cleanup(curlstream->multi);
216 :
217 : /* we are not closing curlstream->readbuf here, because we export
218 : * it as a zval with the wrapperdata - the engine will garbage collect it */
219 :
220 30 : efree(curlstream->url);
221 30 : efree(curlstream);
222 :
223 30 : return 0;
224 : }
225 :
226 : static int php_curl_stream_flush(php_stream *stream TSRMLS_DC)
227 30 : {
228 : #ifdef ilia_0
229 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
230 : #endif
231 30 : return 0;
232 : }
233 :
234 : static int php_curl_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
235 15 : {
236 : /* TODO: fill in details based on Data: and Content-Length: headers, and/or data
237 : * from curl_easy_getinfo().
238 : * For now, return -1 to indicate that it doesn't make sense to stat this stream */
239 15 : return -1;
240 : }
241 :
242 : static int php_curl_stream_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
243 0 : {
244 0 : php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
245 : /* delegate to the readbuffer stream */
246 0 : return php_stream_cast(curlstream->readbuffer.buf, castas, ret, 0);
247 : }
248 :
249 : php_stream_ops php_curl_stream_ops = {
250 : php_curl_stream_write,
251 : php_curl_stream_read,
252 : php_curl_stream_close,
253 : php_curl_stream_flush,
254 : "cURL",
255 : NULL, /* seek */
256 : php_curl_stream_cast, /* cast */
257 : php_curl_stream_stat /* stat */
258 : };
259 :
260 :
261 : php_stream *php_curl_stream_opener(php_stream_wrapper *wrapper, char *filename, char *mode,
262 : int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
263 30 : {
264 : php_stream *stream;
265 : php_curl_stream *curlstream;
266 30 : zval *tmp, **ctx_opt = NULL;
267 30 : struct curl_slist *slist = NULL;
268 :
269 30 : curlstream = emalloc(sizeof(php_curl_stream));
270 30 : memset(curlstream, 0, sizeof(php_curl_stream));
271 :
272 30 : stream = php_stream_alloc(&php_curl_stream_ops, curlstream, 0, mode);
273 30 : php_stream_context_set(stream, context TSRMLS_CC);
274 :
275 30 : curlstream->curl = curl_easy_init();
276 30 : curlstream->multi = curl_multi_init();
277 30 : curlstream->pending = 1;
278 :
279 : /* if opening for an include statement, ensure that the local storage will
280 : * have a FILE* associated with it.
281 : * Otherwise, use the "smart" memory stream that will turn itself into a file
282 : * when it gets large */
283 : #if !HAVE_FOPENCOOKIE
284 : if (options & STREAM_WILL_CAST) {
285 : curlstream->readbuffer.buf = php_stream_fopen_tmpfile();
286 : } else
287 : #endif
288 : {
289 30 : curlstream->readbuffer.buf = php_stream_temp_new();
290 : }
291 :
292 : /* curl requires the URL to be valid throughout it's operation, so dup it */
293 30 : curlstream->url = estrdup(filename);
294 30 : curl_easy_setopt(curlstream->curl, CURLOPT_URL, curlstream->url);
295 :
296 : /* feed curl data into our read buffer */
297 30 : curl_easy_setopt(curlstream->curl, CURLOPT_WRITEFUNCTION, on_data_available);
298 30 : curl_easy_setopt(curlstream->curl, CURLOPT_FILE, stream);
299 :
300 : /* feed headers */
301 30 : curl_easy_setopt(curlstream->curl, CURLOPT_HEADERFUNCTION, on_header_available);
302 30 : curl_easy_setopt(curlstream->curl, CURLOPT_WRITEHEADER, stream);
303 :
304 30 : curl_easy_setopt(curlstream->curl, CURLOPT_ERRORBUFFER, curlstream->errstr);
305 30 : curl_easy_setopt(curlstream->curl, CURLOPT_VERBOSE, 0);
306 :
307 : /* enable progress notification */
308 30 : curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSFUNCTION, on_progress_avail);
309 30 : curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSDATA, stream);
310 30 : curl_easy_setopt(curlstream->curl, CURLOPT_NOPROGRESS, 0);
311 :
312 30 : curl_easy_setopt(curlstream->curl, CURLOPT_USERAGENT, FG(user_agent) ? FG(user_agent) : "PHP/" PHP_VERSION);
313 :
314 : /* TODO: read cookies and options from context */
315 51 : if (context && !strncasecmp(filename, "http", sizeof("http")-1)) {
316 : /* Protocol version */
317 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "protocol_version", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_DOUBLE) {
318 1 : if (Z_DVAL_PP(ctx_opt) == 1.1) {
319 1 : curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
320 : } else {
321 0 : curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
322 : }
323 : }
324 :
325 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "curl_verify_ssl_host", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_BOOL && Z_LVAL_PP(ctx_opt) == 1) {
326 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 1);
327 : } else {
328 21 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0);
329 : }
330 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "curl_verify_ssl_peer", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_BOOL && Z_LVAL_PP(ctx_opt) == 1) {
331 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1);
332 : } else {
333 21 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0);
334 : }
335 :
336 : /* HTTP(S) */
337 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "user_agent", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
338 0 : curl_easy_setopt(curlstream->curl, CURLOPT_USERAGENT, Z_STRVAL_PP(ctx_opt));
339 : }
340 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "header", &ctx_opt)) {
341 3 : if (Z_TYPE_PP(ctx_opt) == IS_ARRAY) {
342 : HashPosition pos;
343 1 : zval **header = NULL;
344 :
345 1 : for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(ctx_opt), &pos);
346 4 : SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(ctx_opt), (void *)&header, &pos);
347 : zend_hash_move_forward_ex(Z_ARRVAL_PP(ctx_opt), &pos)
348 2 : ) {
349 2 : if (Z_TYPE_PP(header) == IS_STRING) {
350 0 : slist = curl_slist_append(slist, Z_STRVAL_PP(header));
351 : }
352 : }
353 2 : } else if (Z_TYPE_PP(ctx_opt) == IS_STRING && Z_STRLEN_PP(ctx_opt)) {
354 : char *p, *token, *trimmed, *copy_ctx_opt;
355 :
356 1 : copy_ctx_opt = php_trim(Z_STRVAL_PP(ctx_opt), Z_STRLEN_PP(ctx_opt), NULL, 0, NULL, 3 TSRMLS_CC);
357 1 : p = php_strtok_r(copy_ctx_opt, "\r\n", &token);
358 3 : while (p) {
359 1 : trimmed = php_trim(p, strlen(p), NULL, 0, NULL, 3 TSRMLS_CC);
360 1 : slist = curl_slist_append(slist, trimmed);
361 1 : efree(trimmed);
362 1 : p = php_strtok_r(NULL, "\r\n", &token);
363 : }
364 1 : efree(copy_ctx_opt);
365 : }
366 3 : if (slist) {
367 1 : curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, slist);
368 : }
369 : }
370 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "method", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
371 0 : if (strcasecmp(Z_STRVAL_PP(ctx_opt), "get")) {
372 0 : if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "head")) {
373 0 : curl_easy_setopt(curlstream->curl, CURLOPT_NOBODY, 1);
374 : } else {
375 0 : if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "post")) {
376 0 : curl_easy_setopt(curlstream->curl, CURLOPT_POST, 1);
377 : } else {
378 0 : curl_easy_setopt(curlstream->curl, CURLOPT_CUSTOMREQUEST, Z_STRVAL_PP(ctx_opt));
379 : }
380 0 : if (SUCCESS == php_stream_context_get_option(context, "http", "content", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
381 0 : curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDS, Z_STRVAL_PP(ctx_opt));
382 0 : curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDSIZE, (long)Z_STRLEN_PP(ctx_opt));
383 : }
384 : }
385 : }
386 : }
387 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "proxy", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
388 0 : curl_easy_setopt(curlstream->curl, CURLOPT_PROXY, Z_STRVAL_PP(ctx_opt));
389 : }
390 21 : if (SUCCESS == php_stream_context_get_option(context, "http", "max_redirects", &ctx_opt)) {
391 6 : long mr = 20;
392 6 : if (Z_TYPE_PP(ctx_opt) != IS_STRING || !is_numeric_string(Z_STRVAL_PP(ctx_opt), Z_STRLEN_PP(ctx_opt), &mr, NULL, 1)) {
393 6 : if (Z_TYPE_PP(ctx_opt) == IS_LONG) {
394 6 : mr = Z_LVAL_PP(ctx_opt);
395 : }
396 : }
397 6 : if (mr > 1) {
398 2 : if (PG(open_basedir) && *PG(open_basedir)) {
399 0 : curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0);
400 : } else {
401 2 : curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1);
402 : }
403 2 : curl_easy_setopt(curlstream->curl, CURLOPT_MAXREDIRS, mr);
404 : }
405 : } else {
406 15 : if (PG(open_basedir) && *PG(open_basedir)) {
407 0 : curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0);
408 : } else {
409 15 : curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1);
410 : }
411 15 : curl_easy_setopt(curlstream->curl, CURLOPT_MAXREDIRS, 20L);
412 : }
413 9 : } else if (context && !strncasecmp(filename, "ftps", sizeof("ftps")-1)) {
414 0 : if (SUCCESS == php_stream_context_get_option(context, "ftp", "curl_verify_ssl_host", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_BOOL && Z_LVAL_PP(ctx_opt) == 1) {
415 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 1);
416 : } else {
417 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0);
418 : }
419 0 : if (SUCCESS == php_stream_context_get_option(context, "ftp", "curl_verify_ssl_peer", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_BOOL && Z_LVAL_PP(ctx_opt) == 1) {
420 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1);
421 : } else {
422 0 : curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0);
423 : }
424 : }
425 :
426 : /* prepare for "pull" mode */
427 30 : curl_multi_add_handle(curlstream->multi, curlstream->curl);
428 :
429 : /* Prepare stuff for file_get_wrapper_data: the data is an array:
430 : *
431 : * data = array(
432 : * "headers" => array("Content-Type: text/html", "Xxx: Yyy"),
433 : * "readbuf" => resource (equivalent to curlstream->readbuffer)
434 : * );
435 : * */
436 30 : MAKE_STD_ZVAL(stream->wrapperdata);
437 30 : array_init(stream->wrapperdata);
438 :
439 30 : MAKE_STD_ZVAL(curlstream->headers);
440 30 : array_init(curlstream->headers);
441 :
442 30 : add_assoc_zval(stream->wrapperdata, "headers", curlstream->headers);
443 :
444 30 : MAKE_STD_ZVAL(tmp);
445 30 : php_stream_to_zval(curlstream->readbuffer.buf, tmp);
446 30 : add_assoc_zval(stream->wrapperdata, "readbuf", tmp);
447 :
448 : #if !HAVE_FOPENCOOKIE
449 : if (options & STREAM_WILL_CAST) {
450 : /* we will need to download the whole resource now,
451 : * since we cannot get the actual FD for the download,
452 : * so we won't be able to drive curl via stdio. */
453 :
454 : /* TODO: this needs finishing */
455 :
456 : curl_easy_perform(curlstream->curl);
457 : }
458 : else
459 : #endif
460 : {
461 : /* fire up the connection; we need to detect a connection error here,
462 : * otherwise the curlstream we return ends up doing nothing useful. */
463 : CURLMcode m;
464 : CURLMsg *msg;
465 30 : int msgs_left, msg_found = 0;
466 :
467 110 : while (CURLM_CALL_MULTI_PERFORM == (m = curl_multi_perform(curlstream->multi, &curlstream->pending))) {
468 : ; /* spin */
469 : }
470 :
471 30 : if (m != CURLM_OK) {
472 : #if HAVE_CURL_MULTI_STRERROR
473 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(m));
474 : #else
475 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error mcode=%d", m);
476 : #endif
477 0 : goto exit_fail;
478 : }
479 :
480 : /* we have only one curl handle here, even though we use multi syntax,
481 : * so it's ok to fail on any error */
482 61 : while ((msg = curl_multi_info_read(curlstream->multi, &msgs_left))) {
483 1 : if (msg->data.result == CURLE_OK) {
484 1 : continue;
485 : } else {
486 : #if HAVE_CURL_EASY_STRERROR
487 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_easy_strerror(msg->data.result));
488 : #else
489 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error mcode=%d", msg->data.result);
490 : #endif
491 0 : msg_found++;
492 : }
493 : }
494 30 : if (msg_found) {
495 0 : goto exit_fail;
496 : }
497 : }
498 :
499 : /* context headers are not needed anymore */
500 30 : if (slist) {
501 1 : curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, NULL);
502 1 : curl_slist_free_all(slist);
503 : }
504 30 : return stream;
505 :
506 0 : exit_fail:
507 0 : php_stream_close(stream);
508 0 : if (slist) {
509 0 : curl_slist_free_all(slist);
510 : }
511 0 : return NULL;
512 : }
513 :
514 : static php_stream_wrapper_ops php_curl_wrapper_ops = {
515 : php_curl_stream_opener,
516 : NULL, /* stream_close: curl streams know how to clean themselves up */
517 : NULL, /* stream_stat: curl streams know how to stat themselves */
518 : NULL, /* stat url */
519 : NULL, /* opendir */
520 : "cURL", /* label */
521 : NULL, /* unlink */
522 : NULL, /* rename */
523 : NULL, /* mkdir */
524 : NULL /* rmdir */
525 : };
526 :
527 : php_stream_wrapper php_curl_wrapper = {
528 : &php_curl_wrapper_ops,
529 : NULL,
530 : 1 /* is_url */
531 : };
532 :
533 : #endif
534 :
535 : /*
536 : * Local variables:
537 : * tab-width: 4
538 : * c-basic-offset: 4
539 : * End:
540 : * vim600: noet sw=4 ts=4 fdm=marker
541 : * vim<600: noet sw=4 ts=4
542 : */
|