xref: /PHP-5.5/ext/standard/php_fopen_wrapper.c (revision 73c1be26)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 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    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Jim Winstead <jimw@php.net>                                 |
17    |          Hartmut Holzgraefe <hholzgra@php.net>                       |
18    +----------------------------------------------------------------------+
19  */
20 /* $Id$ */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 
28 #include "php.h"
29 #include "php_globals.h"
30 #include "php_standard.h"
31 #include "php_fopen_wrappers.h"
32 #include "SAPI.h"
33 
php_stream_output_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)34 static size_t php_stream_output_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
35 {
36 	PHPWRITE(buf, count);
37 	return count;
38 }
39 /* }}} */
40 
php_stream_output_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)41 static size_t php_stream_output_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
42 {
43 	stream->eof = 1;
44 	return 0;
45 }
46 /* }}} */
47 
php_stream_output_close(php_stream * stream,int close_handle TSRMLS_DC)48 static int php_stream_output_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
49 {
50 	return 0;
51 }
52 /* }}} */
53 
54 php_stream_ops php_stream_output_ops = {
55 	php_stream_output_write,
56 	php_stream_output_read,
57 	php_stream_output_close,
58 	NULL, /* flush */
59 	"Output",
60 	NULL, /* seek */
61 	NULL, /* cast */
62 	NULL, /* stat */
63 	NULL  /* set_option */
64 };
65 
php_stream_input_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)66 static size_t php_stream_input_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
67 {
68 	return -1;
69 }
70 /* }}} */
71 
php_stream_input_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)72 static size_t php_stream_input_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
73 {
74 	off_t *position = (off_t*)stream->abstract;
75 	size_t read_bytes = 0;
76 
77 	if (!stream->eof) {
78 		if (SG(request_info).raw_post_data) { /* data has already been read by a post handler */
79 			read_bytes = SG(request_info).raw_post_data_length - *position;
80 			if (read_bytes <= count) {
81 				stream->eof = 1;
82 			} else {
83 				read_bytes = count;
84 			}
85 			if (read_bytes) {
86 				memcpy(buf, SG(request_info).raw_post_data + *position, read_bytes);
87 			}
88 		} else if (sapi_module.read_post) {
89 			read_bytes = sapi_module.read_post(buf, count TSRMLS_CC);
90 			if (read_bytes <= 0) {
91 				stream->eof = 1;
92 				read_bytes = 0;
93 			}
94 			/* Increment SG(read_post_bytes) only when something was actually read. */
95 			SG(read_post_bytes) += read_bytes;
96 		} else {
97 			stream->eof = 1;
98 		}
99 	}
100 
101 	*position += read_bytes;
102 
103 	return read_bytes;
104 }
105 /* }}} */
106 
php_stream_input_close(php_stream * stream,int close_handle TSRMLS_DC)107 static int php_stream_input_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
108 {
109 	efree(stream->abstract);
110 
111 	return 0;
112 }
113 /* }}} */
114 
php_stream_input_flush(php_stream * stream TSRMLS_DC)115 static int php_stream_input_flush(php_stream *stream TSRMLS_DC) /* {{{ */
116 {
117 	return -1;
118 }
119 /* }}} */
120 
121 php_stream_ops php_stream_input_ops = {
122 	php_stream_input_write,
123 	php_stream_input_read,
124 	php_stream_input_close,
125 	php_stream_input_flush,
126 	"Input",
127 	NULL, /* seek */
128 	NULL, /* cast */
129 	NULL, /* stat */
130 	NULL  /* set_option */
131 };
132 
php_stream_apply_filter_list(php_stream * stream,char * filterlist,int read_chain,int write_chain TSRMLS_DC)133 static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain TSRMLS_DC) /* {{{ */
134 {
135 	char *p, *token;
136 	php_stream_filter *temp_filter;
137 
138 	p = php_strtok_r(filterlist, "|", &token);
139 	while (p) {
140 		php_url_decode(p, strlen(p));
141 		if (read_chain) {
142 			if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream) TSRMLS_CC))) {
143 				php_stream_filter_append(&stream->readfilters, temp_filter);
144 			} else {
145 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create filter (%s)", p);
146 			}
147 		}
148 		if (write_chain) {
149 			if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream) TSRMLS_CC))) {
150 				php_stream_filter_append(&stream->writefilters, temp_filter);
151 			} else {
152 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create filter (%s)", p);
153 			}
154 		}
155 		p = php_strtok_r(NULL, "|", &token);
156 	}
157 }
158 /* }}} */
159 
php_stream_url_wrap_php(php_stream_wrapper * wrapper,char * path,char * mode,int options,char ** opened_path,php_stream_context * context STREAMS_DC TSRMLS_DC)160 php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
161 {
162 	int fd = -1;
163 	int mode_rw = 0;
164 	php_stream * stream = NULL;
165 	char *p, *token, *pathdup;
166 	long max_memory;
167 	FILE *file = NULL;
168 
169 	if (!strncasecmp(path, "php://", 6)) {
170 		path += 6;
171 	}
172 
173 	if (!strncasecmp(path, "temp", 4)) {
174 		path += 4;
175 		max_memory = PHP_STREAM_MAX_MEM;
176 		if (!strncasecmp(path, "/maxmemory:", 11)) {
177 			path += 11;
178 			max_memory = strtol(path, NULL, 10);
179 			if (max_memory < 0) {
180 				php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Max memory must be >= 0");
181 				return NULL;
182 			}
183 		}
184 		if (strpbrk(mode, "wa+")) {
185 			mode_rw = TEMP_STREAM_DEFAULT;
186 		} else {
187 			mode_rw = TEMP_STREAM_READONLY;
188 		}
189 		return php_stream_temp_create(mode_rw, max_memory);
190 	}
191 
192 	if (!strcasecmp(path, "memory")) {
193 		if (strpbrk(mode, "wa+")) {
194 			mode_rw = TEMP_STREAM_DEFAULT;
195 		} else {
196 			mode_rw = TEMP_STREAM_READONLY;
197 		}
198 		return php_stream_memory_create(mode_rw);
199 	}
200 
201 	if (!strcasecmp(path, "output")) {
202 		return php_stream_alloc(&php_stream_output_ops, NULL, 0, "wb");
203 	}
204 
205 	if (!strcasecmp(path, "input")) {
206 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
207 			if (options & REPORT_ERRORS) {
208 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
209 			}
210 			return NULL;
211 		}
212 		return php_stream_alloc(&php_stream_input_ops, ecalloc(1, sizeof(off_t)), 0, "rb");
213 	}
214 
215 	if (!strcasecmp(path, "stdin")) {
216 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
217 			if (options & REPORT_ERRORS) {
218 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
219 			}
220 			return NULL;
221 		}
222 		if (!strcmp(sapi_module.name, "cli")) {
223 			static int cli_in = 0;
224 			fd = STDIN_FILENO;
225 			if (cli_in) {
226 				fd = dup(fd);
227 			} else {
228 				cli_in = 1;
229 				file = stdin;
230 			}
231 		} else {
232 			fd = dup(STDIN_FILENO);
233 		}
234 	} else if (!strcasecmp(path, "stdout")) {
235 		if (!strcmp(sapi_module.name, "cli")) {
236 			static int cli_out = 0;
237 			fd = STDOUT_FILENO;
238 			if (cli_out++) {
239 				fd = dup(fd);
240 			} else {
241 				cli_out = 1;
242 				file = stdout;
243 			}
244 		} else {
245 			fd = dup(STDOUT_FILENO);
246 		}
247 	} else if (!strcasecmp(path, "stderr")) {
248 		if (!strcmp(sapi_module.name, "cli")) {
249 			static int cli_err = 0;
250 			fd = STDERR_FILENO;
251 			if (cli_err++) {
252 				fd = dup(fd);
253 			} else {
254 				cli_err = 1;
255 				file = stderr;
256 			}
257 		} else {
258 			fd = dup(STDERR_FILENO);
259 		}
260 	} else if (!strncasecmp(path, "fd/", 3)) {
261 		char	   *start,
262 				   *end;
263 		long	   fildes_ori;
264 		int		   dtablesize;
265 
266 		if (strcmp(sapi_module.name, "cli")) {
267 			if (options & REPORT_ERRORS) {
268 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Direct access to file descriptors is only available from command-line PHP");
269 			}
270 			return NULL;
271 		}
272 
273 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
274 			if (options & REPORT_ERRORS) {
275 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
276 			}
277 			return NULL;
278 		}
279 
280 		start = &path[3];
281 		fildes_ori = strtol(start, &end, 10);
282 		if (end == start || *end != '\0') {
283 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
284 				"php://fd/ stream must be specified in the form php://fd/<orig fd>");
285 			return NULL;
286 		}
287 
288 #if HAVE_UNISTD_H
289 		dtablesize = getdtablesize();
290 #else
291 		dtablesize = INT_MAX;
292 #endif
293 
294 		if (fildes_ori < 0 || fildes_ori >= dtablesize) {
295 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
296 				"The file descriptors must be non-negative numbers smaller than %d", dtablesize);
297 			return NULL;
298 		}
299 
300 		fd = dup(fildes_ori);
301 		if (fd == -1) {
302 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
303 				"Error duping file descriptor %ld; possibly it doesn't exist: "
304 				"[%d]: %s", fildes_ori, errno, strerror(errno));
305 			return NULL;
306 		}
307 	} else if (!strncasecmp(path, "filter/", 7)) {
308 		/* Save time/memory when chain isn't specified */
309 		if (strchr(mode, 'r') || strchr(mode, '+')) {
310 			mode_rw |= PHP_STREAM_FILTER_READ;
311 		}
312 		if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {
313 			mode_rw |= PHP_STREAM_FILTER_WRITE;
314 		}
315 		pathdup = estrndup(path + 6, strlen(path + 6));
316 		p = strstr(pathdup, "/resource=");
317 		if (!p) {
318 			php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "No URL resource specified");
319 			efree(pathdup);
320 			return NULL;
321 		}
322 		if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {
323 			efree(pathdup);
324 			return NULL;
325 		}
326 
327 		*p = '\0';
328 
329 		p = php_strtok_r(pathdup + 1, "/", &token);
330 		while (p) {
331 			if (!strncasecmp(p, "read=", 5)) {
332 				php_stream_apply_filter_list(stream, p + 5, 1, 0 TSRMLS_CC);
333 			} else if (!strncasecmp(p, "write=", 6)) {
334 				php_stream_apply_filter_list(stream, p + 6, 0, 1 TSRMLS_CC);
335 			} else {
336 				php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE TSRMLS_CC);
337 			}
338 			p = php_strtok_r(NULL, "/", &token);
339 		}
340 		efree(pathdup);
341 
342 		return stream;
343 	} else {
344 		/* invalid php://thingy */
345 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid php:// URL specified");
346 		return NULL;
347 	}
348 
349 	/* must be stdin, stderr or stdout */
350 	if (fd == -1)	{
351 		/* failed to dup */
352 		return NULL;
353 	}
354 
355 #if defined(S_IFSOCK) && !defined(WIN32) && !defined(__BEOS__)
356 	do {
357 		struct stat st;
358 		memset(&st, 0, sizeof(st));
359 		if (fstat(fd, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
360 			stream = php_stream_sock_open_from_socket(fd, NULL);
361 			if (stream) {
362 				stream->ops = &php_stream_socket_ops;
363 				return stream;
364 			}
365 		}
366 	} while (0);
367 #endif
368 
369 	if (file) {
370 		stream = php_stream_fopen_from_file(file, mode);
371 	} else {
372 		stream = php_stream_fopen_from_fd(fd, mode, NULL);
373 		if (stream == NULL) {
374 			close(fd);
375 		}
376 	}
377 
378 	return stream;
379 }
380 /* }}} */
381 
382 static php_stream_wrapper_ops php_stdio_wops = {
383 	php_stream_url_wrap_php,
384 	NULL, /* close */
385 	NULL, /* fstat */
386 	NULL, /* stat */
387 	NULL, /* opendir */
388 	"PHP",
389 	NULL, /* unlink */
390 	NULL, /* rename */
391 	NULL, /* mkdir */
392 	NULL  /* rmdir */
393 };
394 
395 php_stream_wrapper php_stream_php_wrapper =	{
396 	&php_stdio_wops,
397 	NULL,
398 	0, /* is_url */
399 };
400 
401 
402 /*
403  * Local variables:
404  * tab-width: 4
405  * c-basic-offset: 4
406  * End:
407  * vim600: sw=4 ts=4 fdm=marker
408  * vim<600: sw=4 ts=4
409  */
410