File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / curl / streams.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:41 2014 UTC (10 years, 10 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | PHP Version 5                                                        |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1997-2014 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,v 1.1.1.4 2014/06/15 20:03:41 misho Exp $ */
   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: 			FD_ZERO(&curlstream->readfds);
  166: 			FD_ZERO(&curlstream->writefds);
  167: 			FD_ZERO(&curlstream->excfds);
  168: 
  169: 			/* get the descriptors from curl */
  170: 			curl_multi_fdset(curlstream->multi, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &curlstream->maxfd);
  171: 
  172: 			/* if we are in blocking mode, set a timeout */
  173: 			tv.tv_usec = 0;
  174: 			tv.tv_sec = 15; /* TODO: allow this to be configured from the script */
  175: 
  176: 			/* wait for data */
  177: 			switch ((curlstream->maxfd < 0) ? 1 : 
  178: 					select(curlstream->maxfd + 1, &curlstream->readfds, &curlstream->writefds, &curlstream->excfds, &tv)) {
  179: 				case -1:
  180: 					/* error */
  181: 					return 0;
  182: 				case 0:
  183: 					/* no data yet: timed-out */
  184: 					return 0;
  185: 				default:
  186: 					/* fetch the data */
  187: 					do {
  188: 						curlstream->mcode = curl_multi_perform(curlstream->multi, &curlstream->pending);
  189: 					} while (curlstream->mcode == CURLM_CALL_MULTI_PERFORM);
  190: 			}
  191: 		} while (curlstream->maxfd >= 0 &&
  192: 				curlstream->readbuffer.readpos >= curlstream->readbuffer.writepos && curlstream->pending > 0);
  193: 
  194: 	}
  195: 
  196: 	/* if there is data in the buffer, try and read it */
  197: 	if (curlstream->readbuffer.writepos > 0 && curlstream->readbuffer.readpos < curlstream->readbuffer.writepos) {
  198: 		php_stream_seek(curlstream->readbuffer.buf, curlstream->readbuffer.readpos, SEEK_SET);
  199: 		didread = php_stream_read(curlstream->readbuffer.buf, buf, count);
  200: 		curlstream->readbuffer.readpos = php_stream_tell(curlstream->readbuffer.buf);
  201: 	}
  202: 
  203: 	if (didread == 0) {
  204: 		stream->eof = 1;
  205: 	}
  206: 	
  207: 	return didread;
  208: }
  209: 
  210: static int php_curl_stream_close(php_stream *stream, int close_handle TSRMLS_DC)
  211: {
  212: 	php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
  213: 
  214: 	/* TODO: respect the close_handle flag here, so that casting to a FILE* on
  215: 	 * systems without fopencookie will work properly */
  216: 	
  217: 	curl_multi_remove_handle(curlstream->multi, curlstream->curl);
  218: 	curl_easy_cleanup(curlstream->curl);	
  219: 	curl_multi_cleanup(curlstream->multi);	
  220: 
  221: 	if (curlstream->headers_slist) {
  222: 		curl_slist_free_all(curlstream->headers_slist);
  223: 	}
  224: 	
  225: 	/* we are not closing curlstream->readbuf here, because we export
  226: 	 * it as a zval with the wrapperdata - the engine will garbage collect it */
  227: 
  228: 	efree(curlstream->url);
  229: 	efree(curlstream);
  230: 	
  231: 	return 0;
  232: }
  233: 
  234: static int php_curl_stream_flush(php_stream *stream TSRMLS_DC)
  235: {
  236: #ifdef ilia_0
  237: 	php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
  238: #endif
  239: 	return 0;
  240: }
  241: 
  242: static int php_curl_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
  243: {
  244: 	/* TODO: fill in details based on Data: and Content-Length: headers, and/or data
  245: 	 * from curl_easy_getinfo().
  246: 	 * For now, return -1 to indicate that it doesn't make sense to stat this stream */
  247: 	return -1;
  248: }
  249: 
  250: static int php_curl_stream_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
  251: {
  252: 	php_curl_stream *curlstream = (php_curl_stream *) stream->abstract;
  253: 	/* delegate to the readbuffer stream */
  254: 	return php_stream_cast(curlstream->readbuffer.buf, castas, ret, 0);
  255: }
  256: 
  257: php_stream_ops php_curl_stream_ops = {
  258: 	php_curl_stream_write,
  259: 	php_curl_stream_read,
  260: 	php_curl_stream_close,
  261: 	php_curl_stream_flush,
  262: 	"cURL",
  263: 	NULL, /* seek */
  264: 	php_curl_stream_cast, /* cast */
  265: 	php_curl_stream_stat  /* stat */
  266: };
  267: 
  268: 
  269: php_stream *php_curl_stream_opener(php_stream_wrapper *wrapper, char *filename, char *mode,
  270: 		int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
  271: {
  272: 	php_stream *stream;
  273: 	php_curl_stream *curlstream;
  274: 	zval *tmp, **ctx_opt = NULL;
  275: 
  276: 	curlstream = emalloc(sizeof(php_curl_stream));
  277: 	memset(curlstream, 0, sizeof(php_curl_stream));
  278: 
  279: 	stream = php_stream_alloc(&php_curl_stream_ops, curlstream, 0, mode);
  280: 	php_stream_context_set(stream, context);
  281: 
  282: 	curlstream->curl = curl_easy_init();
  283: 	curlstream->multi = curl_multi_init();
  284: 	curlstream->pending = 1;
  285: 	curlstream->headers_slist = NULL;
  286: 
  287: 	/* if opening for an include statement, ensure that the local storage will
  288: 	 * have a FILE* associated with it.
  289: 	 * Otherwise, use the "smart" memory stream that will turn itself into a file
  290: 	 * when it gets large */
  291: #ifndef HAVE_FOPENCOOKIE
  292: 	if (options & STREAM_WILL_CAST) {
  293: 		curlstream->readbuffer.buf = php_stream_fopen_tmpfile();
  294: 	} else
  295: #endif
  296: 	{
  297: 		curlstream->readbuffer.buf = php_stream_temp_new();
  298: 	}
  299: 	
  300: 	/* curl requires the URL to be valid throughout it's operation, so dup it */
  301: 	curlstream->url = estrdup(filename);
  302: 	curl_easy_setopt(curlstream->curl, CURLOPT_URL, curlstream->url);
  303: 
  304: 	/* feed curl data into our read buffer */	
  305: 	curl_easy_setopt(curlstream->curl, CURLOPT_WRITEFUNCTION, on_data_available);
  306: 	curl_easy_setopt(curlstream->curl, CURLOPT_FILE, stream);
  307: 	
  308: 	/* feed headers */
  309: 	curl_easy_setopt(curlstream->curl, CURLOPT_HEADERFUNCTION, on_header_available);
  310: 	curl_easy_setopt(curlstream->curl, CURLOPT_WRITEHEADER, stream);
  311: 
  312: 	curl_easy_setopt(curlstream->curl, CURLOPT_ERRORBUFFER, curlstream->errstr);
  313: 	curl_easy_setopt(curlstream->curl, CURLOPT_VERBOSE, 0);
  314: 
  315: 	/* enable progress notification */
  316: 	curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSFUNCTION, on_progress_avail);
  317: 	curl_easy_setopt(curlstream->curl, CURLOPT_PROGRESSDATA, stream);
  318: 	curl_easy_setopt(curlstream->curl, CURLOPT_NOPROGRESS, 0);
  319: 
  320: 	curl_easy_setopt(curlstream->curl, CURLOPT_USERAGENT, FG(user_agent) ? FG(user_agent) : "PHP/" PHP_VERSION);
  321: 	
  322: 	/* TODO: read cookies and options from context */
  323: 	if (context && !strncasecmp(filename, "http", sizeof("http")-1)) {
  324: 		/* Protocol version */
  325: 		if (SUCCESS == php_stream_context_get_option(context, "http", "protocol_version", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_DOUBLE) {
  326: 			if (Z_DVAL_PP(ctx_opt) == 1.1) {
  327: 				curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  328: 			} else {
  329: 				curl_easy_setopt(curlstream->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  330: 			}
  331: 		}
  332: 
  333: 		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) {
  334: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 2);
  335: 		} else {
  336: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0);
  337: 		}
  338: 		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) {
  339: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1);
  340: 		} else {
  341: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0);
  342: 		}
  343: 
  344: 		/* HTTP(S) */
  345: 		if (SUCCESS == php_stream_context_get_option(context, "http", "user_agent", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
  346: 			curl_easy_setopt(curlstream->curl, CURLOPT_USERAGENT, Z_STRVAL_PP(ctx_opt));
  347: 		}
  348: 		if (SUCCESS == php_stream_context_get_option(context, "http", "header", &ctx_opt)) {
  349: 			if (Z_TYPE_PP(ctx_opt) == IS_ARRAY) {
  350: 				HashPosition pos;
  351: 				zval **header = NULL;
  352: 			
  353: 				for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(ctx_opt), &pos);
  354: 					SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(ctx_opt), (void *)&header, &pos);
  355: 					zend_hash_move_forward_ex(Z_ARRVAL_PP(ctx_opt), &pos)
  356: 				) {
  357: 					if (Z_TYPE_PP(header) == IS_STRING) {
  358: 						curlstream->headers_slist = curl_slist_append(curlstream->headers_slist, Z_STRVAL_PP(header));
  359: 					}
  360: 				}
  361: 			} else if (Z_TYPE_PP(ctx_opt) == IS_STRING && Z_STRLEN_PP(ctx_opt)) {
  362: 				char *p, *token, *trimmed, *copy_ctx_opt;
  363: 
  364: 				copy_ctx_opt = php_trim(Z_STRVAL_PP(ctx_opt), Z_STRLEN_PP(ctx_opt), NULL, 0, NULL, 3 TSRMLS_CC);
  365: 				p = php_strtok_r(copy_ctx_opt, "\r\n", &token);
  366: 				while (p) {
  367: 					trimmed = php_trim(p, strlen(p), NULL, 0, NULL, 3 TSRMLS_CC);
  368: 					curlstream->headers_slist = curl_slist_append(curlstream->headers_slist, trimmed);
  369: 					efree(trimmed);
  370: 					p = php_strtok_r(NULL, "\r\n", &token);
  371: 				}
  372: 				efree(copy_ctx_opt);
  373: 			}
  374: 			if (curlstream->headers_slist) {
  375: 				curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, curlstream->headers_slist);
  376: 			}
  377: 		}
  378: 		if (SUCCESS == php_stream_context_get_option(context, "http", "method", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
  379: 			if (strcasecmp(Z_STRVAL_PP(ctx_opt), "get")) {
  380: 				if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "head")) {
  381: 					curl_easy_setopt(curlstream->curl, CURLOPT_NOBODY, 1);
  382: 				} else {
  383: 					if (!strcasecmp(Z_STRVAL_PP(ctx_opt), "post")) {
  384: 						curl_easy_setopt(curlstream->curl, CURLOPT_POST, 1);
  385: 					} else {
  386: 						curl_easy_setopt(curlstream->curl, CURLOPT_CUSTOMREQUEST, Z_STRVAL_PP(ctx_opt));
  387: 					}
  388: 					if (SUCCESS == php_stream_context_get_option(context, "http", "content", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
  389: 						curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDS, Z_STRVAL_PP(ctx_opt));
  390: 						curl_easy_setopt(curlstream->curl, CURLOPT_POSTFIELDSIZE, (long)Z_STRLEN_PP(ctx_opt));
  391: 					}
  392: 				}
  393: 			}
  394: 		}
  395: 		if (SUCCESS == php_stream_context_get_option(context, "http", "proxy", &ctx_opt) && Z_TYPE_PP(ctx_opt) == IS_STRING) {
  396: 			curl_easy_setopt(curlstream->curl, CURLOPT_PROXY, Z_STRVAL_PP(ctx_opt));
  397: 		}
  398: 		if (SUCCESS == php_stream_context_get_option(context, "http", "max_redirects", &ctx_opt)) {
  399: 			long mr = 20;
  400: 			if (Z_TYPE_PP(ctx_opt) != IS_STRING || !is_numeric_string(Z_STRVAL_PP(ctx_opt), Z_STRLEN_PP(ctx_opt), &mr, NULL, 1)) {
  401: 				if (Z_TYPE_PP(ctx_opt) == IS_LONG) {
  402: 					mr = Z_LVAL_PP(ctx_opt);
  403: 				}
  404: 			}
  405: 			if (mr > 1) {
  406: 				if (PG(open_basedir) && *PG(open_basedir)) {
  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, mr);
  412: 			}
  413: 		} else {
  414: 			if (PG(open_basedir) && *PG(open_basedir)) {
  415: 				curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 0);
  416: 			} else {
  417: 				curl_easy_setopt(curlstream->curl, CURLOPT_FOLLOWLOCATION, 1);
  418: 			}
  419: 			curl_easy_setopt(curlstream->curl, CURLOPT_MAXREDIRS, 20L);
  420: 		}
  421: 	} else if (context && !strncasecmp(filename, "ftps", sizeof("ftps")-1)) {
  422: 		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) {
  423: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 2);
  424: 		} else {
  425: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 0);
  426: 		}
  427: 		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) {
  428: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 1);
  429: 		} else {
  430: 			curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYPEER, 0);
  431: 		}
  432: 	}
  433: 
  434: 	/* prepare for "pull" mode */
  435: 	curl_multi_add_handle(curlstream->multi, curlstream->curl);
  436: 
  437: 	/* Prepare stuff for file_get_wrapper_data: the data is an array:
  438: 	 *
  439: 	 * data = array(
  440: 	 *   "headers" => array("Content-Type: text/html", "Xxx: Yyy"),
  441: 	 *   "readbuf" => resource (equivalent to curlstream->readbuffer)
  442: 	 * );
  443: 	 * */
  444: 	MAKE_STD_ZVAL(stream->wrapperdata);
  445: 	array_init(stream->wrapperdata);
  446: 	
  447: 	MAKE_STD_ZVAL(curlstream->headers);
  448: 	array_init(curlstream->headers);
  449: 	
  450: 	add_assoc_zval(stream->wrapperdata, "headers", curlstream->headers);
  451: 	
  452: 	MAKE_STD_ZVAL(tmp);
  453: 	php_stream_to_zval(curlstream->readbuffer.buf, tmp);
  454: 	add_assoc_zval(stream->wrapperdata, "readbuf", tmp);
  455: 
  456: #ifndef HAVE_FOPENCOOKIE
  457: 	if (options & STREAM_WILL_CAST) {
  458: 		/* we will need to download the whole resource now,
  459: 		 * since we cannot get the actual FD for the download,
  460: 		 * so we won't be able to drive curl via stdio. */
  461: 
  462: /* TODO: this needs finishing */
  463: 		
  464: 		curl_easy_perform(curlstream->curl);
  465: 	}
  466: 	else
  467: #endif
  468: 	{
  469: 		/* fire up the connection; we need to detect a connection error here,
  470: 		 * otherwise the curlstream we return ends up doing nothing useful. */
  471: 		CURLMcode m;
  472: 		CURLMsg *msg;
  473: 		int msgs_left, msg_found = 0;
  474: 
  475: 		while (CURLM_CALL_MULTI_PERFORM == (m = curl_multi_perform(curlstream->multi, &curlstream->pending))) {
  476: 			; /* spin */
  477: 		}
  478: 
  479: 		if (m != CURLM_OK) {
  480: #if HAVE_CURL_MULTI_STRERROR
  481: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(m));
  482: #else 
  483: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error mcode=%d", m);
  484: #endif
  485: 			goto exit_fail;
  486: 		}
  487: 		
  488: 		/* we have only one curl handle here, even though we use multi syntax, 
  489: 		 * so it's ok to fail on any error */
  490: 		while ((msg = curl_multi_info_read(curlstream->multi, &msgs_left))) {
  491: 			if (msg->data.result == CURLE_OK) {
  492: 				continue;
  493: 			} else {
  494: #if HAVE_CURL_EASY_STRERROR
  495: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_easy_strerror(msg->data.result));
  496: #else
  497: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error mcode=%d", msg->data.result);
  498: #endif
  499: 				msg_found++;
  500: 			}
  501: 		}
  502: 		if (msg_found) {
  503: 			goto exit_fail;
  504: 		}
  505: 	}
  506: 
  507: 	return stream;
  508: 
  509: exit_fail:
  510: 	php_stream_close(stream);
  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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>