xref: /PHP-5.6/ext/standard/php_fopen_wrapper.c (revision 07546496)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 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 
66 typedef struct php_stream_input { /* {{{ */
67 	php_stream *body;
68 	off_t position;
69 } php_stream_input_t;
70 /* }}} */
71 
php_stream_input_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)72 static size_t php_stream_input_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
73 {
74 	return -1;
75 }
76 /* }}} */
77 
php_stream_input_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)78 static size_t php_stream_input_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
79 {
80 	php_stream_input_t *input = stream->abstract;
81 	size_t read;
82 
83 	if (!SG(post_read) && SG(read_post_bytes) < input->position + count) {
84 		/* read requested data from SAPI */
85 		int read_bytes = sapi_read_post_block(buf, count TSRMLS_CC);
86 
87 		if (read_bytes > 0) {
88 			php_stream_seek(input->body, 0, SEEK_END);
89 			php_stream_write(input->body, buf, read_bytes);
90 		}
91 	}
92 
93 	if (!input->body->readfilters.head) {
94 		/* If the input stream contains filters, it's not really seekable. The
95 			input->position is likely to be wrong for unfiltered data. */
96 		php_stream_seek(input->body, input->position, SEEK_SET);
97 	}
98 	read = php_stream_read(input->body, buf, count);
99 
100 	if (!read || read == (size_t) -1) {
101 		stream->eof = 1;
102 	} else {
103 		input->position += read;
104 	}
105 
106 	return read;
107 }
108 /* }}} */
109 
php_stream_input_close(php_stream * stream,int close_handle TSRMLS_DC)110 static int php_stream_input_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
111 {
112 	efree(stream->abstract);
113 	stream->abstract = NULL;
114 
115 	return 0;
116 }
117 /* }}} */
118 
php_stream_input_flush(php_stream * stream TSRMLS_DC)119 static int php_stream_input_flush(php_stream *stream TSRMLS_DC) /* {{{ */
120 {
121 	return -1;
122 }
123 /* }}} */
124 
php_stream_input_seek(php_stream * stream,off_t offset,int whence,off_t * newoffset TSRMLS_DC)125 static int php_stream_input_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
126 {
127 	php_stream_input_t *input = stream->abstract;
128 
129 	if (input->body) {
130 		int sought = php_stream_seek(input->body, offset, whence);
131 		*newoffset = (input->body)->position;
132 		return sought;
133 	}
134 
135 	return -1;
136 }
137 /* }}} */
138 
139 php_stream_ops php_stream_input_ops = {
140 	php_stream_input_write,
141 	php_stream_input_read,
142 	php_stream_input_close,
143 	php_stream_input_flush,
144 	"Input",
145 	php_stream_input_seek,
146 	NULL, /* cast */
147 	NULL, /* stat */
148 	NULL  /* set_option */
149 };
150 
php_stream_apply_filter_list(php_stream * stream,char * filterlist,int read_chain,int write_chain TSRMLS_DC)151 static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain TSRMLS_DC) /* {{{ */
152 {
153 	char *p, *token;
154 	php_stream_filter *temp_filter;
155 
156 	p = php_strtok_r(filterlist, "|", &token);
157 	while (p) {
158 		php_url_decode(p, strlen(p));
159 		if (read_chain) {
160 			if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream) TSRMLS_CC))) {
161 				php_stream_filter_append(&stream->readfilters, temp_filter);
162 			} else {
163 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create filter (%s)", p);
164 			}
165 		}
166 		if (write_chain) {
167 			if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream) TSRMLS_CC))) {
168 				php_stream_filter_append(&stream->writefilters, temp_filter);
169 			} else {
170 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create filter (%s)", p);
171 			}
172 		}
173 		p = php_strtok_r(NULL, "|", &token);
174 	}
175 }
176 /* }}} */
177 
php_stream_url_wrap_php(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,char ** opened_path,php_stream_context * context STREAMS_DC TSRMLS_DC)178 php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
179 									 char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
180 {
181 	int fd = -1;
182 	int mode_rw = 0;
183 	php_stream * stream = NULL;
184 	char *p, *token, *pathdup;
185 	long max_memory;
186 	FILE *file = NULL;
187 
188 	if (!strncasecmp(path, "php://", 6)) {
189 		path += 6;
190 	}
191 
192 	if (!strncasecmp(path, "temp", 4)) {
193 		path += 4;
194 		max_memory = PHP_STREAM_MAX_MEM;
195 		if (!strncasecmp(path, "/maxmemory:", 11)) {
196 			path += 11;
197 			max_memory = strtol(path, NULL, 10);
198 			if (max_memory < 0) {
199 				php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Max memory must be >= 0");
200 				return NULL;
201 			}
202 		}
203 		if (strpbrk(mode, "wa+")) {
204 			mode_rw = TEMP_STREAM_DEFAULT;
205 		} else {
206 			mode_rw = TEMP_STREAM_READONLY;
207 		}
208 		return php_stream_temp_create(mode_rw, max_memory);
209 	}
210 
211 	if (!strcasecmp(path, "memory")) {
212 		if (strpbrk(mode, "wa+")) {
213 			mode_rw = TEMP_STREAM_DEFAULT;
214 		} else {
215 			mode_rw = TEMP_STREAM_READONLY;
216 		}
217 		return php_stream_memory_create(mode_rw);
218 	}
219 
220 	if (!strcasecmp(path, "output")) {
221 		return php_stream_alloc(&php_stream_output_ops, NULL, 0, "wb");
222 	}
223 
224 	if (!strcasecmp(path, "input")) {
225 		php_stream_input_t *input;
226 
227 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
228 			if (options & REPORT_ERRORS) {
229 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
230 			}
231 			return NULL;
232 		}
233 
234 		input = ecalloc(1, sizeof(*input));
235 		if ((input->body = SG(request_info).request_body)) {
236 			php_stream_rewind(input->body);
237 		} else {
238 			input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
239 			SG(request_info).request_body = input->body;
240 		}
241 
242 		return php_stream_alloc(&php_stream_input_ops, input, 0, "rb");
243 	}
244 
245 	if (!strcasecmp(path, "stdin")) {
246 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
247 			if (options & REPORT_ERRORS) {
248 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
249 			}
250 			return NULL;
251 		}
252 		if (!strcmp(sapi_module.name, "cli")) {
253 			static int cli_in = 0;
254 			fd = STDIN_FILENO;
255 			if (cli_in) {
256 				fd = dup(fd);
257 			} else {
258 				cli_in = 1;
259 				file = stdin;
260 			}
261 		} else {
262 			fd = dup(STDIN_FILENO);
263 		}
264 	} else if (!strcasecmp(path, "stdout")) {
265 		if (!strcmp(sapi_module.name, "cli")) {
266 			static int cli_out = 0;
267 			fd = STDOUT_FILENO;
268 			if (cli_out++) {
269 				fd = dup(fd);
270 			} else {
271 				cli_out = 1;
272 				file = stdout;
273 			}
274 		} else {
275 			fd = dup(STDOUT_FILENO);
276 		}
277 	} else if (!strcasecmp(path, "stderr")) {
278 		if (!strcmp(sapi_module.name, "cli")) {
279 			static int cli_err = 0;
280 			fd = STDERR_FILENO;
281 			if (cli_err++) {
282 				fd = dup(fd);
283 			} else {
284 				cli_err = 1;
285 				file = stderr;
286 			}
287 		} else {
288 			fd = dup(STDERR_FILENO);
289 		}
290 	} else if (!strncasecmp(path, "fd/", 3)) {
291 		const char *start;
292 		char       *end;
293 		long	   fildes_ori;
294 		int		   dtablesize;
295 
296 		if (strcmp(sapi_module.name, "cli")) {
297 			if (options & REPORT_ERRORS) {
298 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Direct access to file descriptors is only available from command-line PHP");
299 			}
300 			return NULL;
301 		}
302 
303 		if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
304 			if (options & REPORT_ERRORS) {
305 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
306 			}
307 			return NULL;
308 		}
309 
310 		start = &path[3];
311 		fildes_ori = strtol(start, &end, 10);
312 		if (end == start || *end != '\0') {
313 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
314 				"php://fd/ stream must be specified in the form php://fd/<orig fd>");
315 			return NULL;
316 		}
317 
318 #if HAVE_UNISTD_H
319 		dtablesize = getdtablesize();
320 #else
321 		dtablesize = INT_MAX;
322 #endif
323 
324 		if (fildes_ori < 0 || fildes_ori >= dtablesize) {
325 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
326 				"The file descriptors must be non-negative numbers smaller than %d", dtablesize);
327 			return NULL;
328 		}
329 
330 		fd = dup(fildes_ori);
331 		if (fd == -1) {
332 			php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
333 				"Error duping file descriptor %ld; possibly it doesn't exist: "
334 				"[%d]: %s", fildes_ori, errno, strerror(errno));
335 			return NULL;
336 		}
337 	} else if (!strncasecmp(path, "filter/", 7)) {
338 		/* Save time/memory when chain isn't specified */
339 		if (strchr(mode, 'r') || strchr(mode, '+')) {
340 			mode_rw |= PHP_STREAM_FILTER_READ;
341 		}
342 		if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {
343 			mode_rw |= PHP_STREAM_FILTER_WRITE;
344 		}
345 		pathdup = estrndup(path + 6, strlen(path + 6));
346 		p = strstr(pathdup, "/resource=");
347 		if (!p) {
348 			php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "No URL resource specified");
349 			efree(pathdup);
350 			return NULL;
351 		}
352 		if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {
353 			efree(pathdup);
354 			return NULL;
355 		}
356 
357 		*p = '\0';
358 
359 		p = php_strtok_r(pathdup + 1, "/", &token);
360 		while (p) {
361 			if (!strncasecmp(p, "read=", 5)) {
362 				php_stream_apply_filter_list(stream, p + 5, 1, 0 TSRMLS_CC);
363 			} else if (!strncasecmp(p, "write=", 6)) {
364 				php_stream_apply_filter_list(stream, p + 6, 0, 1 TSRMLS_CC);
365 			} else {
366 				php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE TSRMLS_CC);
367 			}
368 			p = php_strtok_r(NULL, "/", &token);
369 		}
370 		efree(pathdup);
371 
372 		return stream;
373 	} else {
374 		/* invalid php://thingy */
375 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid php:// URL specified");
376 		return NULL;
377 	}
378 
379 	/* must be stdin, stderr or stdout */
380 	if (fd == -1)	{
381 		/* failed to dup */
382 		return NULL;
383 	}
384 
385 #if defined(S_IFSOCK) && !defined(WIN32) && !defined(__BEOS__)
386 	do {
387 		struct stat st;
388 		memset(&st, 0, sizeof(st));
389 		if (fstat(fd, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
390 			stream = php_stream_sock_open_from_socket(fd, NULL);
391 			if (stream) {
392 				stream->ops = &php_stream_socket_ops;
393 				return stream;
394 			}
395 		}
396 	} while (0);
397 #endif
398 
399 	if (file) {
400 		stream = php_stream_fopen_from_file(file, mode);
401 	} else {
402 		stream = php_stream_fopen_from_fd(fd, mode, NULL);
403 		if (stream == NULL) {
404 			close(fd);
405 		}
406 	}
407 
408 	return stream;
409 }
410 /* }}} */
411 
412 static php_stream_wrapper_ops php_stdio_wops = {
413 	php_stream_url_wrap_php,
414 	NULL, /* close */
415 	NULL, /* fstat */
416 	NULL, /* stat */
417 	NULL, /* opendir */
418 	"PHP",
419 	NULL, /* unlink */
420 	NULL, /* rename */
421 	NULL, /* mkdir */
422 	NULL  /* rmdir */
423 };
424 
425 php_stream_wrapper php_stream_php_wrapper =	{
426 	&php_stdio_wops,
427 	NULL,
428 	0, /* is_url */
429 };
430 
431 
432 /*
433  * Local variables:
434  * tab-width: 4
435  * c-basic-offset: 4
436  * End:
437  * vim600: sw=4 ts=4 fdm=marker
438  * vim<600: sw=4 ts=4
439  */
440