Annotation of embedaddon/php/ext/curl/streams.c, revision 1.1.1.4

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.4 ! misho       5:    | Copyright (c) 1997-2014 The PHP Group                                |
1.1       misho       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: 
1.1.1.2   misho      19: /* $Id$ */
1.1       misho      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 {
1.1.1.3   misho     165:                        FD_ZERO(&curlstream->readfds);
                    166:                        FD_ZERO(&curlstream->writefds);
                    167:                        FD_ZERO(&curlstream->excfds);
                    168: 
1.1       misho     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: 
1.1.1.3   misho     221:        if (curlstream->headers_slist) {
                    222:                curl_slist_free_all(curlstream->headers_slist);
                    223:        }
                    224:        
1.1       misho     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;
1.1.1.3   misho     285:        curlstream->headers_slist = NULL;
1.1       misho     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) {
1.1.1.3   misho     334:                        curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 2);
1.1       misho     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) {
1.1.1.3   misho     358:                                                curlstream->headers_slist = curl_slist_append(curlstream->headers_slist, Z_STRVAL_PP(header));
1.1       misho     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);
1.1.1.3   misho     368:                                        curlstream->headers_slist = curl_slist_append(curlstream->headers_slist, trimmed);
1.1       misho     369:                                        efree(trimmed);
                    370:                                        p = php_strtok_r(NULL, "\r\n", &token);
                    371:                                }
                    372:                                efree(copy_ctx_opt);
                    373:                        }
1.1.1.3   misho     374:                        if (curlstream->headers_slist) {
                    375:                                curl_easy_setopt(curlstream->curl, CURLOPT_HTTPHEADER, curlstream->headers_slist);
1.1       misho     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) {
1.1.1.2   misho     406:                                if (PG(open_basedir) && *PG(open_basedir)) {
1.1       misho     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 {
1.1.1.2   misho     414:                        if (PG(open_basedir) && *PG(open_basedir)) {
1.1       misho     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) {
1.1.1.3   misho     423:                        curl_easy_setopt(curlstream->curl, CURLOPT_SSL_VERIFYHOST, 2);
1.1       misho     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>