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