1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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)34 static size_t php_stream_output_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
35 {
36 PHPWRITE(buf, count);
37 return count;
38 }
39 /* }}} */
40
php_stream_output_read(php_stream * stream,char * buf,size_t count)41 static size_t php_stream_output_read(php_stream *stream, char *buf, size_t count) /* {{{ */
42 {
43 stream->eof = 1;
44 return 0;
45 }
46 /* }}} */
47
php_stream_output_close(php_stream * stream,int close_handle)48 static int php_stream_output_close(php_stream *stream, int close_handle) /* {{{ */
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 zend_off_t position;
69 } php_stream_input_t;
70 /* }}} */
71
php_stream_input_write(php_stream * stream,const char * buf,size_t count)72 static size_t php_stream_input_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
73 {
74 return -1;
75 }
76 /* }}} */
77
php_stream_input_read(php_stream * stream,char * buf,size_t count)78 static size_t php_stream_input_read(php_stream *stream, char *buf, size_t count) /* {{{ */
79 {
80 php_stream_input_t *input = stream->abstract;
81 size_t read;
82
83 if (!SG(post_read) && SG(read_post_bytes) < (int64_t)(input->position + count)) {
84 /* read requested data from SAPI */
85 size_t read_bytes = sapi_read_post_block(buf, count);
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)110 static int php_stream_input_close(php_stream *stream, int close_handle) /* {{{ */
111 {
112 efree(stream->abstract);
113 stream->abstract = NULL;
114
115 return 0;
116 }
117 /* }}} */
118
php_stream_input_flush(php_stream * stream)119 static int php_stream_input_flush(php_stream *stream) /* {{{ */
120 {
121 return -1;
122 }
123 /* }}} */
124
php_stream_input_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)125 static int php_stream_input_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) /* {{{ */
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->position = (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)151 static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain) /* {{{ */
152 {
153 char *p, *token = NULL;
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)))) {
161 php_stream_filter_append(&stream->readfilters, temp_filter);
162 } else {
163 php_error_docref(NULL, 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)))) {
168 php_stream_filter_append(&stream->writefilters, temp_filter);
169 } else {
170 php_error_docref(NULL, 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,zend_string ** opened_path,php_stream_context * context STREAMS_DC)178 php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
179 zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
180 {
181 int fd = -1;
182 int mode_rw = 0;
183 php_stream * stream = NULL;
184 char *p, *token, *pathdup;
185 zend_long max_memory;
186 FILE *file = NULL;
187 #ifdef PHP_WIN32
188 int pipe_requested = 0;
189 #endif
190
191 if (!strncasecmp(path, "php://", 6)) {
192 path += 6;
193 }
194
195 if (!strncasecmp(path, "temp", 4)) {
196 path += 4;
197 max_memory = PHP_STREAM_MAX_MEM;
198 if (!strncasecmp(path, "/maxmemory:", 11)) {
199 path += 11;
200 max_memory = ZEND_STRTOL(path, NULL, 10);
201 if (max_memory < 0) {
202 zend_throw_error(NULL, "Max memory must be >= 0");
203 return NULL;
204 }
205 }
206 if (strpbrk(mode, "wa+")) {
207 mode_rw = TEMP_STREAM_DEFAULT;
208 } else {
209 mode_rw = TEMP_STREAM_READONLY;
210 }
211 return php_stream_temp_create(mode_rw, max_memory);
212 }
213
214 if (!strcasecmp(path, "memory")) {
215 if (strpbrk(mode, "wa+")) {
216 mode_rw = TEMP_STREAM_DEFAULT;
217 } else {
218 mode_rw = TEMP_STREAM_READONLY;
219 }
220 return php_stream_memory_create(mode_rw);
221 }
222
223 if (!strcasecmp(path, "output")) {
224 return php_stream_alloc(&php_stream_output_ops, NULL, 0, "wb");
225 }
226
227 if (!strcasecmp(path, "input")) {
228 php_stream_input_t *input;
229
230 if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
231 if (options & REPORT_ERRORS) {
232 php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
233 }
234 return NULL;
235 }
236
237 input = ecalloc(1, sizeof(*input));
238 if ((input->body = SG(request_info).request_body)) {
239 php_stream_rewind(input->body);
240 } else {
241 input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
242 SG(request_info).request_body = input->body;
243 }
244
245 return php_stream_alloc(&php_stream_input_ops, input, 0, "rb");
246 }
247
248 if (!strcasecmp(path, "stdin")) {
249 if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
250 if (options & REPORT_ERRORS) {
251 php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
252 }
253 return NULL;
254 }
255 if (!strcmp(sapi_module.name, "cli")) {
256 static int cli_in = 0;
257 fd = STDIN_FILENO;
258 if (cli_in) {
259 fd = dup(fd);
260 } else {
261 cli_in = 1;
262 file = stdin;
263 }
264 } else {
265 fd = dup(STDIN_FILENO);
266 }
267 #ifdef PHP_WIN32
268 pipe_requested = 1;
269 #endif
270 } else if (!strcasecmp(path, "stdout")) {
271 if (!strcmp(sapi_module.name, "cli")) {
272 static int cli_out = 0;
273 fd = STDOUT_FILENO;
274 if (cli_out++) {
275 fd = dup(fd);
276 } else {
277 cli_out = 1;
278 file = stdout;
279 }
280 } else {
281 fd = dup(STDOUT_FILENO);
282 }
283 #ifdef PHP_WIN32
284 pipe_requested = 1;
285 #endif
286 } else if (!strcasecmp(path, "stderr")) {
287 if (!strcmp(sapi_module.name, "cli")) {
288 static int cli_err = 0;
289 fd = STDERR_FILENO;
290 if (cli_err++) {
291 fd = dup(fd);
292 } else {
293 cli_err = 1;
294 file = stderr;
295 }
296 } else {
297 fd = dup(STDERR_FILENO);
298 }
299 #ifdef PHP_WIN32
300 pipe_requested = 1;
301 #endif
302 } else if (!strncasecmp(path, "fd/", 3)) {
303 const char *start;
304 char *end;
305 zend_long fildes_ori;
306 int dtablesize;
307
308 if (strcmp(sapi_module.name, "cli")) {
309 if (options & REPORT_ERRORS) {
310 php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP");
311 }
312 return NULL;
313 }
314
315 if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
316 if (options & REPORT_ERRORS) {
317 php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
318 }
319 return NULL;
320 }
321
322 start = &path[3];
323 fildes_ori = ZEND_STRTOL(start, &end, 10);
324 if (end == start || *end != '\0') {
325 php_stream_wrapper_log_error(wrapper, options,
326 "php://fd/ stream must be specified in the form php://fd/<orig fd>");
327 return NULL;
328 }
329
330 #if HAVE_UNISTD_H
331 dtablesize = getdtablesize();
332 #else
333 dtablesize = INT_MAX;
334 #endif
335
336 if (fildes_ori < 0 || fildes_ori >= dtablesize) {
337 php_stream_wrapper_log_error(wrapper, options,
338 "The file descriptors must be non-negative numbers smaller than %d", dtablesize);
339 return NULL;
340 }
341
342 fd = dup((int)fildes_ori);
343 if (fd == -1) {
344 php_stream_wrapper_log_error(wrapper, options,
345 "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: "
346 "[%d]: %s", fildes_ori, errno, strerror(errno));
347 return NULL;
348 }
349 } else if (!strncasecmp(path, "filter/", 7)) {
350 /* Save time/memory when chain isn't specified */
351 if (strchr(mode, 'r') || strchr(mode, '+')) {
352 mode_rw |= PHP_STREAM_FILTER_READ;
353 }
354 if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {
355 mode_rw |= PHP_STREAM_FILTER_WRITE;
356 }
357 pathdup = estrndup(path + 6, strlen(path + 6));
358 p = strstr(pathdup, "/resource=");
359 if (!p) {
360 zend_throw_error(NULL, "No URL resource specified");
361 efree(pathdup);
362 return NULL;
363 }
364
365 if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {
366 efree(pathdup);
367 return NULL;
368 }
369
370 *p = '\0';
371
372 p = php_strtok_r(pathdup + 1, "/", &token);
373 while (p) {
374 if (!strncasecmp(p, "read=", 5)) {
375 php_stream_apply_filter_list(stream, p + 5, 1, 0);
376 } else if (!strncasecmp(p, "write=", 6)) {
377 php_stream_apply_filter_list(stream, p + 6, 0, 1);
378 } else {
379 php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);
380 }
381 p = php_strtok_r(NULL, "/", &token);
382 }
383 efree(pathdup);
384
385 return stream;
386 } else {
387 /* invalid php://thingy */
388 php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified");
389 return NULL;
390 }
391
392 /* must be stdin, stderr or stdout */
393 if (fd == -1) {
394 /* failed to dup */
395 return NULL;
396 }
397
398 #if defined(S_IFSOCK) && !defined(WIN32) && !defined(__BEOS__)
399 do {
400 zend_stat_t st;
401 memset(&st, 0, sizeof(st));
402 if (zend_fstat(fd, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
403 stream = php_stream_sock_open_from_socket(fd, NULL);
404 if (stream) {
405 stream->ops = &php_stream_socket_ops;
406 return stream;
407 }
408 }
409 } while (0);
410 #endif
411
412 if (file) {
413 stream = php_stream_fopen_from_file(file, mode);
414 } else {
415 stream = php_stream_fopen_from_fd(fd, mode, NULL);
416 if (stream == NULL) {
417 close(fd);
418 }
419 }
420
421 #ifdef PHP_WIN32
422 if (pipe_requested && stream && context) {
423 zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
424 if (blocking_pipes) {
425 convert_to_long(blocking_pipes);
426 php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);
427 }
428 }
429 #endif
430 return stream;
431 }
432 /* }}} */
433
434 static php_stream_wrapper_ops php_stdio_wops = {
435 php_stream_url_wrap_php,
436 NULL, /* close */
437 NULL, /* fstat */
438 NULL, /* stat */
439 NULL, /* opendir */
440 "PHP",
441 NULL, /* unlink */
442 NULL, /* rename */
443 NULL, /* mkdir */
444 NULL, /* rmdir */
445 NULL
446 };
447
448 PHPAPI php_stream_wrapper php_stream_php_wrapper = {
449 &php_stdio_wops,
450 NULL,
451 0, /* is_url */
452 };
453
454
455 /*
456 * Local variables:
457 * tab-width: 4
458 * c-basic-offset: 4
459 * End:
460 * vim600: sw=4 ts=4 fdm=marker
461 * vim<600: sw=4 ts=4
462 */
463