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