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>