Return to streams.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / curl |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z 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: { ! 56: php_stream *stream = (php_stream *) ctx; ! 57: 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: if (curlstream->readbuffer.writepos == 0) { ! 66: zval *sym; ! 67: ! 68: if (!EG(active_symbol_table)) { ! 69: zend_rebuild_symbol_table(TSRMLS_C); ! 70: } ! 71: MAKE_STD_ZVAL(sym); ! 72: *sym = *curlstream->headers; ! 73: zval_copy_ctor(sym); ! 74: ZEND_SET_SYMBOL(EG(active_symbol_table), "http_response_header", sym); ! 75: } ! 76: ! 77: php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.writepos, SEEK_SET); ! 78: wrote = php_stream_write(curlstream->readbuffer.buf, data, size * nmemb); ! 79: curlstream->readbuffer.writepos = php_stream_tell(curlstream->readbuffer.buf); ! 80: ! 81: 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: { ! 88: size_t length = size * nmemb; ! 89: zval *header; ! 90: php_stream *stream = (php_stream *) ctx; ! 91: php_curl_stream *curlstream = (php_curl_stream *) stream->abstract; ! 92: TSRMLS_FETCH(); ! 93: ! 94: if (length < 2) { ! 95: /* invalid header ? */ ! 96: return length; ! 97: } ! 98: ! 99: if (!(length == 2 && data[0] == '\r' && data[1] == '\n')) { ! 100: MAKE_STD_ZVAL(header); ! 101: Z_STRLEN_P(header) = length; ! 102: Z_STRVAL_P(header) = estrndup(data, length); ! 103: if (Z_STRVAL_P(header)[length-1] == '\n') { ! 104: Z_STRVAL_P(header)[length-1] = '\0'; ! 105: Z_STRLEN_P(header)--; ! 106: ! 107: if (Z_STRVAL_P(header)[length-2] == '\r') { ! 108: Z_STRVAL_P(header)[length-2] = '\0'; ! 109: Z_STRLEN_P(header)--; ! 110: } ! 111: } ! 112: Z_TYPE_P(header) = IS_STRING; ! 113: 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: if (!strncasecmp(data, "Location: ", 10)) { ! 117: php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_REDIRECTED, data + 10, 0); ! 118: } else if (!strncasecmp(data, "Content-Type: ", 14)) { ! 119: php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, data + 14, 0); ! 120: } else if (!strncasecmp(data, "Context-Length: ", 16)) { ! 121: php_stream_notify_file_size(stream->context, atoi(data + 16), data, 0); ! 122: php_stream_notify_progress_init(stream->context, 0, 0); ! 123: } ! 124: } ! 125: return length; ! 126: ! 127: } ! 128: ! 129: static int on_progress_avail(php_stream *stream, double dltotal, double dlnow, double ultotal, double ulnow) ! 130: { ! 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: php_stream_notify_progress(stream->context, (size_t) dlnow, (size_t) dltotal); ! 136: return 0; ! 137: } ! 138: ! 139: static size_t php_curl_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) ! 140: { ! 141: php_curl_stream *curlstream = (php_curl_stream *) stream->abstract; ! 142: ! 143: if (curlstream->writebuffer.buf) { ! 144: return php_stream_write(curlstream->writebuffer.buf, buf, count); ! 145: } ! 146: ! 147: return 0; ! 148: } ! 149: ! 150: static size_t php_curl_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) ! 151: { ! 152: php_curl_stream *curlstream = (php_curl_stream *) stream->abstract; ! 153: size_t didread = 0; ! 154: ! 155: 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: if (curlstream->readbuffer.writepos == 0) { ! 161: while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curlstream->multi, &curlstream->pending)); ! 162: } ! 163: ! 164: do { ! 165: /* get the descriptors from curl */ ! 166: 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: tv.tv_usec = 0; ! 170: 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: select(curlstream->maxfd + 1, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &tv)) { ! 175: case -1: ! 176: /* error */ ! 177: return 0; ! 178: case 0: ! 179: /* no data yet: timed-out */ ! 180: return 0; ! 181: default: ! 182: /* fetch the data */ ! 183: do { ! 184: curlstream->mcode = curl_multi_perform(curlstream->multi, &curlstream->pending); ! 185: } while (curlstream->mcode == CURLM_CALL_MULTI_PERFORM); ! 186: } ! 187: } while (curlstream->maxfd >= 0 && ! 188: 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: if (curlstream->readbuffer.writepos > 0 && curlstream->readbuffer.readpos < curlstream->readbuffer.writepos) { ! 194: php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.readpos, SEEK_SET); ! 195: didread = php_stream_read(curlstream->readbuffer.buf, buf, count); ! 196: curlstream->readbuffer.readpos = php_stream_tell(curlstream->readbuffer.buf); ! 197: } ! 198: ! 199: if (didread == 0) { ! 200: stream->eof = 1; ! 201: } ! 202: ! 203: return didread; ! 204: } ! 205: ! 206: static int php_curl_stream_close(php_stream *stream, int close_handle TSRMLS_DC) ! 207: { ! 208: 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: curl_multi_remove_handle(curlstream->multi, curlstream->curl); ! 214: curl_easy_cleanup(curlstream->curl); ! 215: 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: efree(curlstream->url); ! 221: efree(curlstream); ! 222: ! 223: return 0; ! 224: } ! 225: ! 226: static int php_curl_stream_flush(php_stream *stream TSRMLS_DC) ! 227: { ! 228: #ifdef ilia_0 ! 229: php_curl_stream *curlstream = (php_curl_stream *) stream->abstract; ! 230: #endif ! 231: return 0; ! 232: } ! 233: ! 234: static int php_curl_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) ! 235: { ! 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: return -1; ! 240: } ! 241: ! 242: static int php_curl_stream_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) ! 243: { ! 244: php_curl_stream *curlstream = (php_curl_stream *) stream->abstract; ! 245: /* delegate to the readbuffer stream */ ! 246: 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: { ! 264: php_stream *stream; ! 265: php_curl_stream *curlstream; ! 266: zval *tmp, **ctx_opt = NULL; ! 267: struct curl_slist *slist = NULL; ! 268: ! 269: curlstream = emalloc(sizeof(php_curl_stream)); ! 270: memset(curlstream, 0, sizeof(php_curl_stream)); ! 271: ! 272: stream = php_stream_alloc(&php_curl_stream_ops, curlstream, 0, mode); ! 273: php_stream_context_set(stream, context); ! 274: ! 275: curlstream->curl = curl_easy_init(); ! 276: curlstream->multi = curl_multi_init(); ! 277: 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: #ifndef HAVE_FOPENCOOKIE ! 284: if (options & STREAM_WILL_CAST) { ! 285: curlstream->readbuffer.buf = php_stream_fopen_tmpfile(); ! 286: } else ! 287: #endif ! 288: { ! 289: 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: curlstream->url = estrdup(filename); ! 294: curl_easy_setopt(curlstream->curl, CURLOPT_URL, curlstream->url); ! 295: ! 296: /* feed curl data into our read buffer */ ! 297: curl_easy_setopt(curlstream->curl, CURLOPT_WRITEFUNCTION, on_data_available); ! 298: curl_easy_setopt(curlstream->curl, CURLOPT_FILE, stream); ! 299: ! 300: /* feed headers */ ! 301: curl_easy_setopt(curlstream->curl, CURLOPT_HEADERFUNCTION, on_header_available); ! 302: curl_easy_setopt(curlstream->curl, CURLOPT_WRITEHEADER, stream); ! 303: ! 304: curl_easy_setopt(curlstream->curl, CURLOPT_ERRORBUFFER, curlstream->errstr); ! 305: curl_easy_setopt(curlstream->curl, CURLOPT_VERBOSE, 0); ! 306: ! 307: /* enable progress notification */ ! 308: curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSFUNCTION, on_progress_avail); ! 309: curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSDATA, stream); ! 310: curl_easy_setopt(curlstream->curl, CURLOPT_NOPROGRESS, 0); ! 311: ! 312: 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: if (context && !strncasecmp(filename, "http", sizeof("http")-1)) { ! 316: /* Protocol version */ ! 317: if (SUCCESS == php_stream_context_get_option(context, "http", "protocol_version", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_DOUBLE) { ! 318: if (Z_DVAL_PP(ctx_opt) == 1.1) { ! 319: curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); ! 320: } else { ! 321: curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); ! 322: } ! 323: } ! 324: ! 325: 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: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 1); ! 327: } else { ! 328: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0); ! 329: } ! 330: 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: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1); ! 332: } else { ! 333: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0); ! 334: } ! 335: ! 336: /* HTTP(S) */ ! 337: if (SUCCESS == php_stream_context_get_option(context, "http", "user_agent", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) { ! 338: curl_easy_setopt(curlstream->curl, CURLOPT_USERAGENT, Z_STRVAL_PP(ctx_opt)); ! 339: } ! 340: if (SUCCESS == php_stream_context_get_option(context, "http", "header", &ctx_opt)) { ! 341: if (Z_TYPE_PP(ctx_opt) == IS_ARRAY) { ! 342: HashPosition pos; ! 343: zval **header = NULL; ! 344: ! 345: for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(ctx_opt), &pos); ! 346: 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: ) { ! 349: if (Z_TYPE_PP(header) == IS_STRING) { ! 350: slist = curl_slist_append(slist, Z_STRVAL_PP(header)); ! 351: } ! 352: } ! 353: } else if (Z_TYPE_PP(ctx_opt) == IS_STRING && Z_STRLEN_PP(ctx_opt)) { ! 354: char *p, *token, *trimmed, *copy_ctx_opt; ! 355: ! 356: copy_ctx_opt = php_trim(Z_STRVAL_PP(ctx_opt), Z_STRLEN_PP(ctx_opt), NULL, 0, NULL, 3 TSRMLS_CC); ! 357: p = php_strtok_r(copy_ctx_opt, "\r\n", &token); ! 358: while (p) { ! 359: trimmed = php_trim(p, strlen(p), NULL, 0, NULL, 3 TSRMLS_CC); ! 360: slist = curl_slist_append(slist, trimmed); ! 361: efree(trimmed); ! 362: p = php_strtok_r(NULL, "\r\n", &token); ! 363: } ! 364: efree(copy_ctx_opt); ! 365: } ! 366: if (slist) { ! 367: curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, slist); ! 368: } ! 369: } ! 370: if (SUCCESS == php_stream_context_get_option(context, "http", "method", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) { ! 371: if (strcasecmp(Z_STRVAL_PP(ctx_opt), "get")) { ! 372: if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "head")) { ! 373: curl_easy_setopt(curlstream->curl, CURLOPT_NOBODY, 1); ! 374: } else { ! 375: if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "post")) { ! 376: curl_easy_setopt(curlstream->curl, CURLOPT_POST, 1); ! 377: } else { ! 378: curl_easy_setopt(curlstream->curl, CURLOPT_CUSTOMREQUEST, Z_STRVAL_PP(ctx_opt)); ! 379: } ! 380: if (SUCCESS == php_stream_context_get_option(context, "http", "content", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) { ! 381: curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDS, Z_STRVAL_PP(ctx_opt)); ! 382: curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDSIZE, (long)Z_STRLEN_PP(ctx_opt)); ! 383: } ! 384: } ! 385: } ! 386: } ! 387: if (SUCCESS == php_stream_context_get_option(context, "http", "proxy", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) { ! 388: curl_easy_setopt(curlstream->curl, CURLOPT_PROXY, Z_STRVAL_PP(ctx_opt)); ! 389: } ! 390: if (SUCCESS == php_stream_context_get_option(context, "http", "max_redirects", &ctx_opt)) { ! 391: long mr = 20; ! 392: 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: if (Z_TYPE_PP(ctx_opt) == IS_LONG) { ! 394: mr = Z_LVAL_PP(ctx_opt); ! 395: } ! 396: } ! 397: if (mr > 1) { ! 398: if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) { ! 399: curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0); ! 400: } else { ! 401: curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1); ! 402: } ! 403: curl_easy_setopt(curlstream->curl, CURLOPT_MAXREDIRS, mr); ! 404: } ! 405: } else { ! 406: if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) { ! 407: curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0); ! 408: } else { ! 409: curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1); ! 410: } ! 411: curl_easy_setopt(curlstream->curl, CURLOPT_MAXREDIRS, 20L); ! 412: } ! 413: } else if (context && !strncasecmp(filename, "ftps", sizeof("ftps")-1)) { ! 414: 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: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 1); ! 416: } else { ! 417: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0); ! 418: } ! 419: 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: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1); ! 421: } else { ! 422: curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0); ! 423: } ! 424: } ! 425: ! 426: /* prepare for "pull" mode */ ! 427: 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: MAKE_STD_ZVAL(stream->wrapperdata); ! 437: array_init(stream->wrapperdata); ! 438: ! 439: MAKE_STD_ZVAL(curlstream->headers); ! 440: array_init(curlstream->headers); ! 441: ! 442: add_assoc_zval(stream->wrapperdata, "headers", curlstream->headers); ! 443: ! 444: MAKE_STD_ZVAL(tmp); ! 445: php_stream_to_zval(curlstream->readbuffer.buf, tmp); ! 446: add_assoc_zval(stream->wrapperdata, "readbuf", tmp); ! 447: ! 448: #ifndef 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: int msgs_left, msg_found = 0; ! 466: ! 467: while (CURLM_CALL_MULTI_PERFORM == (m = curl_multi_perform(curlstream->multi, &curlstream->pending))) { ! 468: ; /* spin */ ! 469: } ! 470: ! 471: if (m != CURLM_OK) { ! 472: #if HAVE_CURL_MULTI_STRERROR ! 473: 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: 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: while ((msg = curl_multi_info_read(curlstream->multi, &msgs_left))) { ! 483: if (msg->data.result == CURLE_OK) { ! 484: continue; ! 485: } else { ! 486: #if HAVE_CURL_EASY_STRERROR ! 487: 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: msg_found++; ! 492: } ! 493: } ! 494: if (msg_found) { ! 495: goto exit_fail; ! 496: } ! 497: } ! 498: ! 499: /* context headers are not needed anymore */ ! 500: if (slist) { ! 501: curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, NULL); ! 502: curl_slist_free_all(slist); ! 503: } ! 504: return stream; ! 505: ! 506: exit_fail: ! 507: php_stream_close(stream); ! 508: if (slist) { ! 509: curl_slist_free_all(slist); ! 510: } ! 511: 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: */