1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 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