File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / curl / streams.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:47:53 2012 UTC (12 years, 4 months ago) by misho
Branches: php, MAIN
CVS tags: v5_3_10, HEAD
php

    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,v 1.1.1.1 2012/02/21 23:47:53 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: 			/* 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:  */

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