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 | |
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 | |
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 | |
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 | |
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 | |
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 | 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 | |
106 | static 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 | |
115 | static int php_stream_input_flush(php_stream *stream TSRMLS_DC) /* {{{ */ |
116 | { |
117 | return -1; |
118 | } |
119 | /* }}} */ |
120 | |
121 | static 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 | |
135 | php_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 | |
147 | static 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 | |
174 | php_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 | |
408 | static 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 | |
421 | php_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 | |