1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Stig Bakken <ssb@php.net> |
15 | Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | PHP 4.0 patches by Thies C. Arntzen (thies@thieso.net) |
18 | PHP streams by Wez Furlong (wez@thebrainroom.com) |
19 +----------------------------------------------------------------------+
20 */
21
22 /* {{{ includes */
23
24 #include "php.h"
25 #include "php_globals.h"
26 #include "ext/standard/flock_compat.h"
27 #include "ext/standard/exec.h"
28 #include "ext/standard/php_filestat.h"
29 #include "php_open_temporary_file.h"
30 #include "ext/standard/basic_functions.h"
31 #include "php_ini.h"
32 #include "zend_smart_str.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <wchar.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41
42 #ifdef PHP_WIN32
43 # include <io.h>
44 # define O_RDONLY _O_RDONLY
45 # include "win32/param.h"
46 # include "win32/winutil.h"
47 # include "win32/fnmatch.h"
48 # include "win32/ioutil.h"
49 #else
50 # if HAVE_SYS_PARAM_H
51 # include <sys/param.h>
52 # endif
53 # if HAVE_SYS_SELECT_H
54 # include <sys/select.h>
55 # endif
56 # include <sys/socket.h>
57 # include <netinet/in.h>
58 # include <netdb.h>
59 # if HAVE_ARPA_INET_H
60 # include <arpa/inet.h>
61 # endif
62 #endif
63
64 #include "ext/standard/head.h"
65 #include "php_string.h"
66 #include "file.h"
67
68 #ifdef HAVE_PWD_H
69 # ifdef PHP_WIN32
70 # include "win32/pwd.h"
71 # else
72 # include <pwd.h>
73 # endif
74 #endif
75
76 #ifdef HAVE_SYS_TIME_H
77 # include <sys/time.h>
78 #endif
79
80 #include "fsock.h"
81 #include "fopen_wrappers.h"
82 #include "streamsfuncs.h"
83 #include "php_globals.h"
84
85 #ifdef HAVE_SYS_FILE_H
86 # include <sys/file.h>
87 #endif
88
89 #ifdef HAVE_SYS_MMAN_H
90 # include <sys/mman.h>
91 #endif
92
93 #include "scanf.h"
94 #include "zend_API.h"
95
96 #ifdef ZTS
97 int file_globals_id;
98 #else
99 php_file_globals file_globals;
100 #endif
101
102 #if defined(HAVE_FNMATCH) && !defined(PHP_WIN32)
103 # ifndef _GNU_SOURCE
104 # define _GNU_SOURCE
105 # endif
106 # include <fnmatch.h>
107 #endif
108
109 #include "file_arginfo.h"
110
111 /* }}} */
112
113 #define PHP_STREAM_FROM_ZVAL(stream, arg) \
114 ZEND_ASSERT(Z_TYPE_P(arg) == IS_RESOURCE); \
115 php_stream_from_res(stream, Z_RES_P(arg));
116
117 /* {{{ ZTS-stuff / Globals / Prototypes */
118
119 /* sharing globals is *evil* */
120 static int le_stream_context = FAILURE;
121
php_le_stream_context(void)122 PHPAPI int php_le_stream_context(void)
123 {
124 return le_stream_context;
125 }
126 /* }}} */
127
128 /* {{{ Module-Stuff */
ZEND_RSRC_DTOR_FUNC(file_context_dtor)129 static ZEND_RSRC_DTOR_FUNC(file_context_dtor)
130 {
131 php_stream_context *context = (php_stream_context*)res->ptr;
132 if (Z_TYPE(context->options) != IS_UNDEF) {
133 zval_ptr_dtor(&context->options);
134 ZVAL_UNDEF(&context->options);
135 }
136 php_stream_context_free(context);
137 }
138
file_globals_ctor(php_file_globals * file_globals_p)139 static void file_globals_ctor(php_file_globals *file_globals_p)
140 {
141 memset(file_globals_p, 0, sizeof(php_file_globals));
142 file_globals_p->def_chunk_size = PHP_SOCK_CHUNK_SIZE;
143 }
144
file_globals_dtor(php_file_globals * file_globals_p)145 static void file_globals_dtor(php_file_globals *file_globals_p)
146 {
147 #if defined(HAVE_GETHOSTBYNAME_R)
148 if (file_globals_p->tmp_host_buf) {
149 free(file_globals_p->tmp_host_buf);
150 }
151 #endif
152 }
153
PHP_INI_MH(OnUpdateAutoDetectLineEndings)154 static PHP_INI_MH(OnUpdateAutoDetectLineEndings)
155 {
156 if (zend_ini_parse_bool(new_value)) {
157 zend_error(E_DEPRECATED, "auto_detect_line_endings is deprecated");
158 }
159 return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
160 }
161
162 PHP_INI_BEGIN()
163 STD_PHP_INI_ENTRY("user_agent", NULL, PHP_INI_ALL, OnUpdateString, user_agent, php_file_globals, file_globals)
164 STD_PHP_INI_ENTRY("from", NULL, PHP_INI_ALL, OnUpdateString, from_address, php_file_globals, file_globals)
165 STD_PHP_INI_ENTRY("default_socket_timeout", "60", PHP_INI_ALL, OnUpdateLong, default_socket_timeout, php_file_globals, file_globals)
166 STD_PHP_INI_BOOLEAN("auto_detect_line_endings", "0", PHP_INI_ALL, OnUpdateAutoDetectLineEndings, auto_detect_line_endings, php_file_globals, file_globals)
PHP_INI_END()167 PHP_INI_END()
168
169 PHP_MINIT_FUNCTION(file)
170 {
171 le_stream_context = zend_register_list_destructors_ex(file_context_dtor, NULL, "stream-context", module_number);
172
173 #ifdef ZTS
174 ts_allocate_id(&file_globals_id, sizeof(php_file_globals), (ts_allocate_ctor) file_globals_ctor, (ts_allocate_dtor) file_globals_dtor);
175 #else
176 file_globals_ctor(&file_globals);
177 #endif
178
179 REGISTER_INI_ENTRIES();
180
181 register_file_symbols(module_number);
182
183 return SUCCESS;
184 }
185 /* }}} */
186
PHP_MSHUTDOWN_FUNCTION(file)187 PHP_MSHUTDOWN_FUNCTION(file) /* {{{ */
188 {
189 #ifndef ZTS
190 file_globals_dtor(&file_globals);
191 #endif
192 return SUCCESS;
193 }
194 /* }}} */
195
php_flock_common(php_stream * stream,zend_long operation,uint32_t operation_arg_num,zval * wouldblock,zval * return_value)196 PHPAPI void php_flock_common(php_stream *stream, zend_long operation,
197 uint32_t operation_arg_num, zval *wouldblock, zval *return_value)
198 {
199 int flock_values[] = { LOCK_SH, LOCK_EX, LOCK_UN };
200 int act;
201
202 act = operation & PHP_LOCK_UN;
203 if (act < 1 || act > 3) {
204 zend_argument_value_error(operation_arg_num, "must be one of LOCK_SH, LOCK_EX, or LOCK_UN");
205 RETURN_THROWS();
206 }
207
208 if (wouldblock) {
209 ZEND_TRY_ASSIGN_REF_LONG(wouldblock, 0);
210 }
211
212 /* flock_values contains all possible actions if (operation & PHP_LOCK_NB) we won't block on the lock */
213 act = flock_values[act - 1] | (operation & PHP_LOCK_NB ? LOCK_NB : 0);
214 if (php_stream_lock(stream, act)) {
215 if (operation && errno == EWOULDBLOCK && wouldblock) {
216 ZEND_TRY_ASSIGN_REF_LONG(wouldblock, 1);
217 }
218 RETURN_FALSE;
219 }
220 RETURN_TRUE;
221 }
222
223 /* {{{ Portable file locking */
PHP_FUNCTION(flock)224 PHP_FUNCTION(flock)
225 {
226 zval *res, *wouldblock = NULL;
227 php_stream *stream;
228 zend_long operation = 0;
229
230 ZEND_PARSE_PARAMETERS_START(2, 3)
231 Z_PARAM_RESOURCE(res)
232 Z_PARAM_LONG(operation)
233 Z_PARAM_OPTIONAL
234 Z_PARAM_ZVAL(wouldblock)
235 ZEND_PARSE_PARAMETERS_END();
236
237 PHP_STREAM_FROM_ZVAL(stream, res);
238
239 php_flock_common(stream, operation, 2, wouldblock, return_value);
240 }
241 /* }}} */
242
243 #define PHP_META_UNSAFE ".\\+*?[^]$() "
244
245 /* {{{ Extracts all meta tag content attributes from a file and returns an array */
PHP_FUNCTION(get_meta_tags)246 PHP_FUNCTION(get_meta_tags)
247 {
248 char *filename;
249 size_t filename_len;
250 bool use_include_path = 0;
251 int in_tag = 0, done = 0;
252 int looking_for_val = 0, have_name = 0, have_content = 0;
253 int saw_name = 0, saw_content = 0;
254 char *name = NULL, *value = NULL, *temp = NULL;
255 php_meta_tags_token tok, tok_last;
256 php_meta_tags_data md;
257
258 /* Initialize our structure */
259 memset(&md, 0, sizeof(md));
260
261 /* Parse arguments */
262 ZEND_PARSE_PARAMETERS_START(1, 2)
263 Z_PARAM_PATH(filename, filename_len)
264 Z_PARAM_OPTIONAL
265 Z_PARAM_BOOL(use_include_path)
266 ZEND_PARSE_PARAMETERS_END();
267
268 md.stream = php_stream_open_wrapper(filename, "rb",
269 (use_include_path ? USE_PATH : 0) | REPORT_ERRORS,
270 NULL);
271 if (!md.stream) {
272 RETURN_FALSE;
273 }
274
275 array_init(return_value);
276
277 tok_last = TOK_EOF;
278
279 while (!done && (tok = php_next_meta_token(&md)) != TOK_EOF) {
280 if (tok == TOK_ID) {
281 if (tok_last == TOK_OPENTAG) {
282 md.in_meta = !strcasecmp("meta", md.token_data);
283 } else if (tok_last == TOK_SLASH && in_tag) {
284 if (strcasecmp("head", md.token_data) == 0) {
285 /* We are done here! */
286 done = 1;
287 }
288 } else if (tok_last == TOK_EQUAL && looking_for_val) {
289 if (saw_name) {
290 if (name) efree(name);
291 /* Get the NAME attr (Single word attr, non-quoted) */
292 temp = name = estrndup(md.token_data, md.token_len);
293
294 while (temp && *temp) {
295 if (strchr(PHP_META_UNSAFE, *temp)) {
296 *temp = '_';
297 }
298 temp++;
299 }
300
301 have_name = 1;
302 } else if (saw_content) {
303 if (value) efree(value);
304 value = estrndup(md.token_data, md.token_len);
305 have_content = 1;
306 }
307
308 looking_for_val = 0;
309 } else {
310 if (md.in_meta) {
311 if (strcasecmp("name", md.token_data) == 0) {
312 saw_name = 1;
313 saw_content = 0;
314 looking_for_val = 1;
315 } else if (strcasecmp("content", md.token_data) == 0) {
316 saw_name = 0;
317 saw_content = 1;
318 looking_for_val = 1;
319 }
320 }
321 }
322 } else if (tok == TOK_STRING && tok_last == TOK_EQUAL && looking_for_val) {
323 if (saw_name) {
324 if (name) efree(name);
325 /* Get the NAME attr (Quoted single/double) */
326 temp = name = estrndup(md.token_data, md.token_len);
327
328 while (temp && *temp) {
329 if (strchr(PHP_META_UNSAFE, *temp)) {
330 *temp = '_';
331 }
332 temp++;
333 }
334
335 have_name = 1;
336 } else if (saw_content) {
337 if (value) efree(value);
338 value = estrndup(md.token_data, md.token_len);
339 have_content = 1;
340 }
341
342 looking_for_val = 0;
343 } else if (tok == TOK_OPENTAG) {
344 if (looking_for_val) {
345 looking_for_val = 0;
346 have_name = saw_name = 0;
347 have_content = saw_content = 0;
348 }
349 in_tag = 1;
350 } else if (tok == TOK_CLOSETAG) {
351 if (have_name) {
352 /* For BC */
353 zend_str_tolower(name, strlen(name));
354 if (have_content) {
355 add_assoc_string(return_value, name, value);
356 } else {
357 add_assoc_string(return_value, name, "");
358 }
359
360 efree(name);
361 if (value) efree(value);
362 } else if (have_content) {
363 efree(value);
364 }
365
366 name = value = NULL;
367
368 /* Reset all of our flags */
369 in_tag = looking_for_val = 0;
370 have_name = saw_name = 0;
371 have_content = saw_content = 0;
372 md.in_meta = 0;
373 }
374
375 tok_last = tok;
376
377 if (md.token_data)
378 efree(md.token_data);
379
380 md.token_data = NULL;
381 }
382
383 if (value) efree(value);
384 if (name) efree(name);
385 php_stream_close(md.stream);
386 }
387 /* }}} */
388
389 /* {{{ Read the entire file into a string */
PHP_FUNCTION(file_get_contents)390 PHP_FUNCTION(file_get_contents)
391 {
392 char *filename;
393 size_t filename_len;
394 bool use_include_path = 0;
395 php_stream *stream;
396 zend_long offset = 0;
397 zend_long maxlen;
398 bool maxlen_is_null = 1;
399 zval *zcontext = NULL;
400 php_stream_context *context = NULL;
401 zend_string *contents;
402
403 /* Parse arguments */
404 ZEND_PARSE_PARAMETERS_START(1, 5)
405 Z_PARAM_PATH(filename, filename_len)
406 Z_PARAM_OPTIONAL
407 Z_PARAM_BOOL(use_include_path)
408 Z_PARAM_RESOURCE_OR_NULL(zcontext)
409 Z_PARAM_LONG(offset)
410 Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
411 ZEND_PARSE_PARAMETERS_END();
412
413 if (maxlen_is_null) {
414 maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
415 } else if (maxlen < 0) {
416 zend_argument_value_error(5, "must be greater than or equal to 0");
417 RETURN_THROWS();
418 }
419
420 context = php_stream_context_from_zval(zcontext, 0);
421
422 stream = php_stream_open_wrapper_ex(filename, "rb",
423 (use_include_path ? USE_PATH : 0) | REPORT_ERRORS,
424 NULL, context);
425 if (!stream) {
426 RETURN_FALSE;
427 }
428
429 /* disabling the read buffer allows doing the whole transfer
430 in just one read() system call */
431 if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
432 php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
433 }
434
435 if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) {
436 php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
437 php_stream_close(stream);
438 RETURN_FALSE;
439 }
440
441 if ((contents = php_stream_copy_to_mem(stream, maxlen, 0)) != NULL) {
442 RETVAL_STR(contents);
443 } else {
444 RETVAL_EMPTY_STRING();
445 }
446
447 php_stream_close(stream);
448 }
449 /* }}} */
450
451 /* {{{ Write/Create a file with contents data and return the number of bytes written */
PHP_FUNCTION(file_put_contents)452 PHP_FUNCTION(file_put_contents)
453 {
454 php_stream *stream;
455 char *filename;
456 size_t filename_len;
457 zval *data;
458 ssize_t numbytes = 0;
459 zend_long flags = 0;
460 zval *zcontext = NULL;
461 php_stream_context *context = NULL;
462 php_stream *srcstream = NULL;
463 char mode[3] = "wb";
464
465 ZEND_PARSE_PARAMETERS_START(2, 4)
466 Z_PARAM_PATH(filename, filename_len)
467 Z_PARAM_ZVAL(data)
468 Z_PARAM_OPTIONAL
469 Z_PARAM_LONG(flags)
470 Z_PARAM_RESOURCE_OR_NULL(zcontext)
471 ZEND_PARSE_PARAMETERS_END();
472
473 if (Z_TYPE_P(data) == IS_RESOURCE) {
474 php_stream_from_zval(srcstream, data);
475 }
476
477 context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
478
479 if (flags & PHP_FILE_APPEND) {
480 mode[0] = 'a';
481 } else if (flags & LOCK_EX) {
482 /* check to make sure we are dealing with a regular file */
483 if (php_memnstr(filename, "://", sizeof("://") - 1, filename + filename_len)) {
484 if (strncasecmp(filename, "file://", sizeof("file://") - 1)) {
485 php_error_docref(NULL, E_WARNING, "Exclusive locks may only be set for regular files");
486 RETURN_FALSE;
487 }
488 }
489 mode[0] = 'c';
490 }
491 mode[2] = '\0';
492
493 stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
494 if (stream == NULL) {
495 RETURN_FALSE;
496 }
497
498 if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) {
499 php_stream_close(stream);
500 php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream");
501 RETURN_FALSE;
502 }
503
504 if (mode[0] == 'c') {
505 php_stream_truncate_set_size(stream, 0);
506 }
507
508 switch (Z_TYPE_P(data)) {
509 case IS_RESOURCE: {
510 size_t len;
511 if (php_stream_copy_to_stream_ex(srcstream, stream, PHP_STREAM_COPY_ALL, &len) != SUCCESS) {
512 numbytes = -1;
513 } else {
514 if (len > ZEND_LONG_MAX) {
515 php_error_docref(NULL, E_WARNING, "content truncated from %zu to " ZEND_LONG_FMT " bytes", len, ZEND_LONG_MAX);
516 len = ZEND_LONG_MAX;
517 }
518 numbytes = len;
519 }
520 break;
521 }
522 case IS_NULL:
523 case IS_LONG:
524 case IS_DOUBLE:
525 case IS_FALSE:
526 case IS_TRUE:
527 convert_to_string(data);
528 ZEND_FALLTHROUGH;
529 case IS_STRING:
530 if (Z_STRLEN_P(data)) {
531 numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data));
532 if (numbytes != -1 && numbytes != Z_STRLEN_P(data)) {
533 php_error_docref(NULL, E_WARNING, "Only %zd of %zd bytes written, possibly out of free disk space", numbytes, Z_STRLEN_P(data));
534 numbytes = -1;
535 }
536 }
537 break;
538
539 case IS_ARRAY:
540 if (zend_hash_num_elements(Z_ARRVAL_P(data))) {
541 ssize_t bytes_written;
542 zval *tmp;
543
544 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), tmp) {
545 zend_string *t;
546 zend_string *str = zval_get_tmp_string(tmp, &t);
547 if (ZSTR_LEN(str)) {
548 numbytes += ZSTR_LEN(str);
549 bytes_written = php_stream_write(stream, ZSTR_VAL(str), ZSTR_LEN(str));
550 if (bytes_written != ZSTR_LEN(str)) {
551 php_error_docref(NULL, E_WARNING, "Failed to write %zd bytes to %s", ZSTR_LEN(str), filename);
552 zend_tmp_string_release(t);
553 numbytes = -1;
554 break;
555 }
556 }
557 zend_tmp_string_release(t);
558 } ZEND_HASH_FOREACH_END();
559 }
560 break;
561
562 case IS_OBJECT:
563 if (Z_OBJ_HT_P(data) != NULL) {
564 zval out;
565
566 if (zend_std_cast_object_tostring(Z_OBJ_P(data), &out, IS_STRING) == SUCCESS) {
567 numbytes = php_stream_write(stream, Z_STRVAL(out), Z_STRLEN(out));
568 if (numbytes != -1 && numbytes != Z_STRLEN(out)) {
569 php_error_docref(NULL, E_WARNING, "Only %zd of %zd bytes written, possibly out of free disk space", numbytes, Z_STRLEN(out));
570 numbytes = -1;
571 }
572 zval_ptr_dtor_str(&out);
573 break;
574 }
575 }
576 ZEND_FALLTHROUGH;
577 default:
578 numbytes = -1;
579 break;
580 }
581 php_stream_close(stream);
582
583 if (numbytes < 0) {
584 RETURN_FALSE;
585 }
586
587 RETURN_LONG(numbytes);
588 }
589 /* }}} */
590
591 #define PHP_FILE_BUF_SIZE 80
592
593 /* {{{ Read entire file into an array */
PHP_FUNCTION(file)594 PHP_FUNCTION(file)
595 {
596 char *filename;
597 size_t filename_len;
598 char *p, *s, *e;
599 int i = 0;
600 char eol_marker = '\n';
601 zend_long flags = 0;
602 bool use_include_path;
603 bool include_new_line;
604 bool skip_blank_lines;
605 php_stream *stream;
606 zval *zcontext = NULL;
607 php_stream_context *context = NULL;
608 zend_string *target_buf;
609
610 /* Parse arguments */
611 ZEND_PARSE_PARAMETERS_START(1, 3)
612 Z_PARAM_PATH(filename, filename_len)
613 Z_PARAM_OPTIONAL
614 Z_PARAM_LONG(flags)
615 Z_PARAM_RESOURCE_OR_NULL(zcontext)
616 ZEND_PARSE_PARAMETERS_END();
617
618 if ((flags & ~(PHP_FILE_USE_INCLUDE_PATH | PHP_FILE_IGNORE_NEW_LINES | PHP_FILE_SKIP_EMPTY_LINES | PHP_FILE_NO_DEFAULT_CONTEXT)) != 0) {
619 zend_argument_value_error(2, "must be a valid flag value");
620 RETURN_THROWS();
621 }
622
623 use_include_path = flags & PHP_FILE_USE_INCLUDE_PATH;
624 include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES);
625 skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES;
626
627 context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
628
629 stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
630 if (!stream) {
631 RETURN_FALSE;
632 }
633
634 /* Initialize return array */
635 array_init(return_value);
636
637 if ((target_buf = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
638 s = ZSTR_VAL(target_buf);
639 e = ZSTR_VAL(target_buf) + ZSTR_LEN(target_buf);
640
641 if (!(p = (char*)php_stream_locate_eol(stream, target_buf))) {
642 p = e;
643 goto parse_eol;
644 }
645
646 if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
647 eol_marker = '\r';
648 }
649
650 /* for performance reasons the code is duplicated, so that the if (include_new_line)
651 * will not need to be done for every single line in the file. */
652 if (include_new_line) {
653 do {
654 p++;
655 parse_eol:
656 add_index_stringl(return_value, i++, s, p-s);
657 s = p;
658 } while ((p = memchr(p, eol_marker, (e-p))));
659 } else {
660 do {
661 int windows_eol = 0;
662 if (p != ZSTR_VAL(target_buf) && eol_marker == '\n' && *(p - 1) == '\r') {
663 windows_eol++;
664 }
665 if (skip_blank_lines && !(p-s-windows_eol)) {
666 s = ++p;
667 continue;
668 }
669 add_index_stringl(return_value, i++, s, p-s-windows_eol);
670 s = ++p;
671 } while ((p = memchr(p, eol_marker, (e-p))));
672 }
673
674 /* handle any leftovers of files without new lines */
675 if (s != e) {
676 p = e;
677 goto parse_eol;
678 }
679 }
680
681 if (target_buf) {
682 zend_string_free(target_buf);
683 }
684 php_stream_close(stream);
685 }
686 /* }}} */
687
688 /* {{{ Create a unique filename in a directory */
PHP_FUNCTION(tempnam)689 PHP_FUNCTION(tempnam)
690 {
691 char *dir, *prefix;
692 size_t dir_len, prefix_len;
693 zend_string *opened_path;
694 int fd;
695 zend_string *p;
696
697 ZEND_PARSE_PARAMETERS_START(2, 2)
698 Z_PARAM_PATH(dir, dir_len)
699 Z_PARAM_PATH(prefix, prefix_len)
700 ZEND_PARSE_PARAMETERS_END();
701
702 p = php_basename(prefix, prefix_len, NULL, 0);
703 if (ZSTR_LEN(p) >= 64) {
704 ZSTR_VAL(p)[63] = '\0';
705 }
706
707 RETVAL_FALSE;
708
709 if ((fd = php_open_temporary_fd_ex(dir, ZSTR_VAL(p), &opened_path, PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ALWAYS)) >= 0) {
710 close(fd);
711 RETVAL_STR(opened_path);
712 }
713 zend_string_release_ex(p, 0);
714 }
715 /* }}} */
716
717 /* {{{ Create a temporary file that will be deleted automatically after use */
PHP_FUNCTION(tmpfile)718 PHP_FUNCTION(tmpfile)
719 {
720 php_stream *stream;
721
722 ZEND_PARSE_PARAMETERS_NONE();
723
724 stream = php_stream_fopen_tmpfile();
725
726 if (stream) {
727 php_stream_to_zval(stream, return_value);
728 } else {
729 RETURN_FALSE;
730 }
731 }
732 /* }}} */
733
734 /* {{{ Open a file or a URL and return a file pointer */
PHP_FUNCTION(fopen)735 PHP_FUNCTION(fopen)
736 {
737 char *filename, *mode;
738 size_t filename_len, mode_len;
739 bool use_include_path = 0;
740 zval *zcontext = NULL;
741 php_stream *stream;
742 php_stream_context *context = NULL;
743
744 ZEND_PARSE_PARAMETERS_START(2, 4)
745 Z_PARAM_PATH(filename, filename_len)
746 Z_PARAM_STRING(mode, mode_len)
747 Z_PARAM_OPTIONAL
748 Z_PARAM_BOOL(use_include_path)
749 Z_PARAM_RESOURCE_OR_NULL(zcontext)
750 ZEND_PARSE_PARAMETERS_END();
751
752 context = php_stream_context_from_zval(zcontext, 0);
753
754 stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
755
756 if (stream == NULL) {
757 RETURN_FALSE;
758 }
759
760 php_stream_to_zval(stream, return_value);
761 }
762 /* }}} */
763
764 /* {{{ Close an open file pointer */
PHP_FUNCTION(fclose)765 PHPAPI PHP_FUNCTION(fclose)
766 {
767 zval *res;
768 php_stream *stream;
769
770 ZEND_PARSE_PARAMETERS_START(1, 1)
771 Z_PARAM_RESOURCE(res)
772 ZEND_PARSE_PARAMETERS_END();
773
774 PHP_STREAM_FROM_ZVAL(stream, res);
775
776 if ((stream->flags & PHP_STREAM_FLAG_NO_FCLOSE) != 0) {
777 php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a valid stream resource", stream->res->handle);
778 RETURN_FALSE;
779 }
780
781 php_stream_free(stream,
782 PHP_STREAM_FREE_KEEP_RSRC |
783 (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE));
784
785 RETURN_TRUE;
786 }
787 /* }}} */
788
789 /* {{{ Execute a command and open either a read or a write pipe to it */
PHP_FUNCTION(popen)790 PHP_FUNCTION(popen)
791 {
792 char *command, *mode;
793 size_t command_len, mode_len;
794 FILE *fp;
795 php_stream *stream;
796 char *posix_mode;
797
798 ZEND_PARSE_PARAMETERS_START(2, 2)
799 Z_PARAM_PATH(command, command_len)
800 Z_PARAM_STRING(mode, mode_len)
801 ZEND_PARSE_PARAMETERS_END();
802
803 posix_mode = estrndup(mode, mode_len);
804 #ifndef PHP_WIN32
805 {
806 char *z = memchr(posix_mode, 'b', mode_len);
807 if (z) {
808 memmove(z, z + 1, mode_len - (z - posix_mode));
809 mode_len--;
810 }
811 }
812 #endif
813
814 /* Musl only partially validates the mode. Manually check it to ensure consistent behavior. */
815 if (mode_len > 2 ||
816 (mode_len == 1 && (*posix_mode != 'r' && *posix_mode != 'w')) ||
817 (mode_len == 2 && (memcmp(posix_mode, "rb", 2) && memcmp(posix_mode, "wb", 2)))
818 ) {
819 zend_argument_value_error(2, "must be one of \"r\", \"rb\", \"w\", or \"wb\"");
820 efree(posix_mode);
821 RETURN_THROWS();
822 }
823
824 fp = VCWD_POPEN(command, posix_mode);
825 if (!fp) {
826 php_error_docref2(NULL, command, posix_mode, E_WARNING, "%s", strerror(errno));
827 efree(posix_mode);
828 RETURN_FALSE;
829 }
830
831 stream = php_stream_fopen_from_pipe(fp, mode);
832
833 if (stream == NULL) {
834 php_error_docref2(NULL, command, mode, E_WARNING, "%s", strerror(errno));
835 RETVAL_FALSE;
836 } else {
837 php_stream_to_zval(stream, return_value);
838 }
839
840 efree(posix_mode);
841 }
842 /* }}} */
843
844 /* {{{ Close a file pointer opened by popen() */
PHP_FUNCTION(pclose)845 PHP_FUNCTION(pclose)
846 {
847 zval *res;
848 php_stream *stream;
849
850 ZEND_PARSE_PARAMETERS_START(1, 1)
851 Z_PARAM_RESOURCE(res)
852 ZEND_PARSE_PARAMETERS_END();
853
854 PHP_STREAM_FROM_ZVAL(stream, res);
855
856 FG(pclose_wait) = 1;
857 zend_list_close(stream->res);
858 FG(pclose_wait) = 0;
859 RETURN_LONG(FG(pclose_ret));
860 }
861 /* }}} */
862
863 /* {{{ Test for end-of-file on a file pointer */
PHP_FUNCTION(feof)864 PHPAPI PHP_FUNCTION(feof)
865 {
866 zval *res;
867 php_stream *stream;
868
869 ZEND_PARSE_PARAMETERS_START(1, 1)
870 Z_PARAM_RESOURCE(res)
871 ZEND_PARSE_PARAMETERS_END();
872
873 PHP_STREAM_FROM_ZVAL(stream, res);
874
875 if (php_stream_eof(stream)) {
876 RETURN_TRUE;
877 } else {
878 RETURN_FALSE;
879 }
880 }
881 /* }}} */
882
883 /* {{{ Get a line from file pointer */
PHP_FUNCTION(fgets)884 PHPAPI PHP_FUNCTION(fgets)
885 {
886 zval *res;
887 zend_long len = 1024;
888 bool len_is_null = 1;
889 char *buf = NULL;
890 size_t line_len = 0;
891 zend_string *str;
892 php_stream *stream;
893
894 ZEND_PARSE_PARAMETERS_START(1, 2)
895 Z_PARAM_RESOURCE(res)
896 Z_PARAM_OPTIONAL
897 Z_PARAM_LONG_OR_NULL(len, len_is_null)
898 ZEND_PARSE_PARAMETERS_END();
899
900 PHP_STREAM_FROM_ZVAL(stream, res);
901
902 if (len_is_null) {
903 /* ask streams to give us a buffer of an appropriate size */
904 buf = php_stream_get_line(stream, NULL, 0, &line_len);
905 if (buf == NULL) {
906 RETURN_FALSE;
907 }
908 // TODO: avoid reallocation ???
909 RETVAL_STRINGL(buf, line_len);
910 efree(buf);
911 } else {
912 if (len <= 0) {
913 zend_argument_value_error(2, "must be greater than 0");
914 RETURN_THROWS();
915 }
916
917 str = zend_string_alloc(len, 0);
918 if (php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len) == NULL) {
919 zend_string_efree(str);
920 RETURN_FALSE;
921 }
922 /* resize buffer if it's much larger than the result.
923 * Only needed if the user requested a buffer size. */
924 if (line_len < (size_t)len / 2) {
925 str = zend_string_truncate(str, line_len, 0);
926 } else {
927 ZSTR_LEN(str) = line_len;
928 }
929 RETURN_NEW_STR(str);
930 }
931 }
932 /* }}} */
933
934 /* {{{ Get a character from file pointer */
PHP_FUNCTION(fgetc)935 PHPAPI PHP_FUNCTION(fgetc)
936 {
937 zval *res;
938 php_stream *stream;
939
940 ZEND_PARSE_PARAMETERS_START(1, 1)
941 Z_PARAM_RESOURCE(res)
942 ZEND_PARSE_PARAMETERS_END();
943
944 PHP_STREAM_FROM_ZVAL(stream, res);
945
946 int result = php_stream_getc(stream);
947
948 if (result == EOF) {
949 RETVAL_FALSE;
950 } else {
951 RETURN_CHAR(result);
952 }
953 }
954 /* }}} */
955
956 /* {{{ Implements a mostly ANSI compatible fscanf() */
PHP_FUNCTION(fscanf)957 PHP_FUNCTION(fscanf)
958 {
959 int result, argc = 0;
960 size_t format_len;
961 zval *args = NULL;
962 zval *file_handle;
963 char *buf, *format;
964 size_t len;
965 void *what;
966
967 ZEND_PARSE_PARAMETERS_START(2, -1)
968 Z_PARAM_RESOURCE(file_handle)
969 Z_PARAM_STRING(format, format_len)
970 Z_PARAM_VARIADIC('*', args, argc)
971 ZEND_PARSE_PARAMETERS_END();
972
973 what = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream());
974
975 /* we can't do a ZEND_VERIFY_RESOURCE(what), otherwise we end up
976 * with a leak if we have an invalid filehandle. This needs changing
977 * if the code behind ZEND_VERIFY_RESOURCE changed. - cc */
978 if (!what) {
979 RETURN_THROWS();
980 }
981
982 buf = php_stream_get_line((php_stream *) what, NULL, 0, &len);
983 if (buf == NULL) {
984 RETURN_FALSE;
985 }
986
987 result = php_sscanf_internal(buf, format, argc, args, 0, return_value);
988
989 efree(buf);
990
991 if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
992 WRONG_PARAM_COUNT;
993 }
994 }
995 /* }}} */
996
997 /* {{{ Binary-safe file write */
PHP_FUNCTION(fwrite)998 PHPAPI PHP_FUNCTION(fwrite)
999 {
1000 zval *res;
1001 char *input;
1002 size_t inputlen;
1003 ssize_t ret;
1004 size_t num_bytes;
1005 zend_long maxlen = 0;
1006 bool maxlen_is_null = 1;
1007 php_stream *stream;
1008
1009 ZEND_PARSE_PARAMETERS_START(2, 3)
1010 Z_PARAM_RESOURCE(res)
1011 Z_PARAM_STRING(input, inputlen)
1012 Z_PARAM_OPTIONAL
1013 Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
1014 ZEND_PARSE_PARAMETERS_END();
1015
1016 if (maxlen_is_null) {
1017 num_bytes = inputlen;
1018 } else if (maxlen <= 0) {
1019 num_bytes = 0;
1020 } else {
1021 num_bytes = MIN((size_t) maxlen, inputlen);
1022 }
1023
1024 if (!num_bytes) {
1025 RETURN_LONG(0);
1026 }
1027
1028 PHP_STREAM_FROM_ZVAL(stream, res);
1029
1030 ret = php_stream_write(stream, input, num_bytes);
1031 if (ret < 0) {
1032 RETURN_FALSE;
1033 }
1034
1035 RETURN_LONG(ret);
1036 }
1037 /* }}} */
1038
1039 /* {{{ Flushes output */
PHP_FUNCTION(fflush)1040 PHPAPI PHP_FUNCTION(fflush)
1041 {
1042 zval *res;
1043 int ret;
1044 php_stream *stream;
1045
1046 ZEND_PARSE_PARAMETERS_START(1, 1)
1047 Z_PARAM_RESOURCE(res)
1048 ZEND_PARSE_PARAMETERS_END();
1049
1050 PHP_STREAM_FROM_ZVAL(stream, res);
1051
1052 ret = php_stream_flush(stream);
1053 if (ret) {
1054 RETURN_FALSE;
1055 }
1056 RETURN_TRUE;
1057 }
1058 /* }}} */
1059
1060 /* {{{ Rewind the position of a file pointer */
PHP_FUNCTION(rewind)1061 PHPAPI PHP_FUNCTION(rewind)
1062 {
1063 zval *res;
1064 php_stream *stream;
1065
1066 ZEND_PARSE_PARAMETERS_START(1, 1)
1067 Z_PARAM_RESOURCE(res)
1068 ZEND_PARSE_PARAMETERS_END();
1069
1070 PHP_STREAM_FROM_ZVAL(stream, res);
1071
1072 if (-1 == php_stream_rewind(stream)) {
1073 RETURN_FALSE;
1074 }
1075 RETURN_TRUE;
1076 }
1077 /* }}} */
1078
1079 /* {{{ Get file pointer's read/write position */
PHP_FUNCTION(ftell)1080 PHPAPI PHP_FUNCTION(ftell)
1081 {
1082 zval *res;
1083 zend_long ret;
1084 php_stream *stream;
1085
1086 ZEND_PARSE_PARAMETERS_START(1, 1)
1087 Z_PARAM_RESOURCE(res)
1088 ZEND_PARSE_PARAMETERS_END();
1089
1090 PHP_STREAM_FROM_ZVAL(stream, res);
1091
1092 ret = php_stream_tell(stream);
1093 if (ret == -1) {
1094 RETURN_FALSE;
1095 }
1096 RETURN_LONG(ret);
1097 }
1098 /* }}} */
1099
1100 /* {{{ Seek on a file pointer */
PHP_FUNCTION(fseek)1101 PHPAPI PHP_FUNCTION(fseek)
1102 {
1103 zval *res;
1104 zend_long offset, whence = SEEK_SET;
1105 php_stream *stream;
1106
1107 ZEND_PARSE_PARAMETERS_START(2, 3)
1108 Z_PARAM_RESOURCE(res)
1109 Z_PARAM_LONG(offset)
1110 Z_PARAM_OPTIONAL
1111 Z_PARAM_LONG(whence)
1112 ZEND_PARSE_PARAMETERS_END();
1113
1114 PHP_STREAM_FROM_ZVAL(stream, res);
1115
1116 RETURN_LONG(php_stream_seek(stream, offset, (int) whence));
1117 }
1118 /* }}} */
1119
1120 /* {{{ php_mkdir */
1121
1122 /* DEPRECATED APIs: Use php_stream_mkdir() instead */
php_mkdir_ex(const char * dir,zend_long mode,int options)1123 PHPAPI int php_mkdir_ex(const char *dir, zend_long mode, int options)
1124 {
1125 int ret;
1126
1127 if (php_check_open_basedir(dir)) {
1128 return -1;
1129 }
1130
1131 if ((ret = VCWD_MKDIR(dir, (mode_t)mode)) < 0 && (options & REPORT_ERRORS)) {
1132 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1133 }
1134
1135 return ret;
1136 }
1137
php_mkdir(const char * dir,zend_long mode)1138 PHPAPI int php_mkdir(const char *dir, zend_long mode)
1139 {
1140 return php_mkdir_ex(dir, mode, REPORT_ERRORS);
1141 }
1142 /* }}} */
1143
1144 /* {{{ Create a directory */
PHP_FUNCTION(mkdir)1145 PHP_FUNCTION(mkdir)
1146 {
1147 char *dir;
1148 size_t dir_len;
1149 zval *zcontext = NULL;
1150 zend_long mode = 0777;
1151 bool recursive = 0;
1152 php_stream_context *context;
1153
1154 ZEND_PARSE_PARAMETERS_START(1, 4)
1155 Z_PARAM_PATH(dir, dir_len)
1156 Z_PARAM_OPTIONAL
1157 Z_PARAM_LONG(mode)
1158 Z_PARAM_BOOL(recursive)
1159 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1160 ZEND_PARSE_PARAMETERS_END();
1161
1162 context = php_stream_context_from_zval(zcontext, 0);
1163
1164 RETURN_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context));
1165 }
1166 /* }}} */
1167
1168 /* {{{ Remove a directory */
PHP_FUNCTION(rmdir)1169 PHP_FUNCTION(rmdir)
1170 {
1171 char *dir;
1172 size_t dir_len;
1173 zval *zcontext = NULL;
1174 php_stream_context *context;
1175
1176 ZEND_PARSE_PARAMETERS_START(1, 2)
1177 Z_PARAM_PATH(dir, dir_len)
1178 Z_PARAM_OPTIONAL
1179 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1180 ZEND_PARSE_PARAMETERS_END();
1181
1182 context = php_stream_context_from_zval(zcontext, 0);
1183
1184 RETURN_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context));
1185 }
1186 /* }}} */
1187
1188 /* {{{ Output a file or a URL */
PHP_FUNCTION(readfile)1189 PHP_FUNCTION(readfile)
1190 {
1191 char *filename;
1192 size_t filename_len;
1193 size_t size = 0;
1194 bool use_include_path = 0;
1195 zval *zcontext = NULL;
1196 php_stream *stream;
1197 php_stream_context *context = NULL;
1198
1199 ZEND_PARSE_PARAMETERS_START(1, 3)
1200 Z_PARAM_PATH(filename, filename_len)
1201 Z_PARAM_OPTIONAL
1202 Z_PARAM_BOOL(use_include_path)
1203 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1204 ZEND_PARSE_PARAMETERS_END();
1205
1206 context = php_stream_context_from_zval(zcontext, 0);
1207
1208 stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
1209 if (stream) {
1210 size = php_stream_passthru(stream);
1211 php_stream_close(stream);
1212 RETURN_LONG(size);
1213 }
1214
1215 RETURN_FALSE;
1216 }
1217 /* }}} */
1218
1219 /* {{{ Return or change the umask */
PHP_FUNCTION(umask)1220 PHP_FUNCTION(umask)
1221 {
1222 zend_long mask = 0;
1223 bool mask_is_null = 1;
1224 int oldumask;
1225
1226 ZEND_PARSE_PARAMETERS_START(0, 1)
1227 Z_PARAM_OPTIONAL
1228 Z_PARAM_LONG_OR_NULL(mask, mask_is_null)
1229 ZEND_PARSE_PARAMETERS_END();
1230
1231 oldumask = umask(077);
1232
1233 if (BG(umask) == -1) {
1234 BG(umask) = oldumask;
1235 }
1236
1237 if (mask_is_null) {
1238 umask(oldumask);
1239 } else {
1240 umask((int) mask);
1241 }
1242
1243 RETURN_LONG(oldumask);
1244 }
1245 /* }}} */
1246
1247 /* {{{ Output all remaining data from a file pointer */
PHP_FUNCTION(fpassthru)1248 PHPAPI PHP_FUNCTION(fpassthru)
1249 {
1250 zval *res;
1251 size_t size;
1252 php_stream *stream;
1253
1254 ZEND_PARSE_PARAMETERS_START(1, 1)
1255 Z_PARAM_RESOURCE(res)
1256 ZEND_PARSE_PARAMETERS_END();
1257
1258 PHP_STREAM_FROM_ZVAL(stream, res);
1259
1260 size = php_stream_passthru(stream);
1261 RETURN_LONG(size);
1262 }
1263 /* }}} */
1264
1265 /* {{{ Rename a file */
PHP_FUNCTION(rename)1266 PHP_FUNCTION(rename)
1267 {
1268 char *old_name, *new_name;
1269 size_t old_name_len, new_name_len;
1270 zval *zcontext = NULL;
1271 php_stream_wrapper *wrapper;
1272 php_stream_context *context;
1273
1274 ZEND_PARSE_PARAMETERS_START(2, 3)
1275 Z_PARAM_PATH(old_name, old_name_len)
1276 Z_PARAM_PATH(new_name, new_name_len)
1277 Z_PARAM_OPTIONAL
1278 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1279 ZEND_PARSE_PARAMETERS_END();
1280
1281 wrapper = php_stream_locate_url_wrapper(old_name, NULL, 0);
1282
1283 if (!wrapper || !wrapper->wops) {
1284 php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper");
1285 RETURN_FALSE;
1286 }
1287
1288 if (!wrapper->wops->rename) {
1289 php_error_docref(NULL, E_WARNING, "%s wrapper does not support renaming", wrapper->wops->label ? wrapper->wops->label : "Source");
1290 RETURN_FALSE;
1291 }
1292
1293 if (wrapper != php_stream_locate_url_wrapper(new_name, NULL, 0)) {
1294 php_error_docref(NULL, E_WARNING, "Cannot rename a file across wrapper types");
1295 RETURN_FALSE;
1296 }
1297
1298 context = php_stream_context_from_zval(zcontext, 0);
1299
1300 RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context));
1301 }
1302 /* }}} */
1303
1304 /* {{{ Delete a file */
PHP_FUNCTION(unlink)1305 PHP_FUNCTION(unlink)
1306 {
1307 char *filename;
1308 size_t filename_len;
1309 php_stream_wrapper *wrapper;
1310 zval *zcontext = NULL;
1311 php_stream_context *context = NULL;
1312
1313 ZEND_PARSE_PARAMETERS_START(1, 2)
1314 Z_PARAM_PATH(filename, filename_len)
1315 Z_PARAM_OPTIONAL
1316 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1317 ZEND_PARSE_PARAMETERS_END();
1318
1319 context = php_stream_context_from_zval(zcontext, 0);
1320
1321 wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
1322
1323 if (!wrapper || !wrapper->wops) {
1324 php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper");
1325 RETURN_FALSE;
1326 }
1327
1328 if (!wrapper->wops->unlink) {
1329 php_error_docref(NULL, E_WARNING, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
1330 RETURN_FALSE;
1331 }
1332 RETURN_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context));
1333 }
1334 /* }}} */
1335
PHP_FUNCTION(fsync)1336 PHP_FUNCTION(fsync)
1337 {
1338 zval *res;
1339 php_stream *stream;
1340
1341 ZEND_PARSE_PARAMETERS_START(1, 1)
1342 Z_PARAM_RESOURCE(res)
1343 ZEND_PARSE_PARAMETERS_END();
1344
1345 PHP_STREAM_FROM_ZVAL(stream, res);
1346
1347 if (!php_stream_sync_supported(stream)) {
1348 php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1349 RETURN_FALSE;
1350 }
1351
1352 RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0);
1353 }
1354
PHP_FUNCTION(fdatasync)1355 PHP_FUNCTION(fdatasync)
1356 {
1357 zval *res;
1358 php_stream *stream;
1359
1360 ZEND_PARSE_PARAMETERS_START(1, 1)
1361 Z_PARAM_RESOURCE(res)
1362 ZEND_PARSE_PARAMETERS_END();
1363
1364 PHP_STREAM_FROM_ZVAL(stream, res);
1365
1366 if (!php_stream_sync_supported(stream)) {
1367 php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1368 RETURN_FALSE;
1369 }
1370
1371 RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0);
1372 }
1373
1374 /* {{{ Truncate file to 'size' length */
PHP_FUNCTION(ftruncate)1375 PHP_FUNCTION(ftruncate)
1376 {
1377 zval *fp;
1378 zend_long size;
1379 php_stream *stream;
1380
1381 ZEND_PARSE_PARAMETERS_START(2, 2)
1382 Z_PARAM_RESOURCE(fp)
1383 Z_PARAM_LONG(size)
1384 ZEND_PARSE_PARAMETERS_END();
1385
1386 if (size < 0) {
1387 zend_argument_value_error(2, "must be greater than or equal to 0");
1388 RETURN_THROWS();
1389 }
1390
1391 PHP_STREAM_FROM_ZVAL(stream, fp);
1392
1393 if (!php_stream_truncate_supported(stream)) {
1394 php_error_docref(NULL, E_WARNING, "Can't truncate this stream!");
1395 RETURN_FALSE;
1396 }
1397
1398 RETURN_BOOL(0 == php_stream_truncate_set_size(stream, size));
1399 }
1400 /* }}} */
php_fstat(php_stream * stream,zval * return_value)1401 PHPAPI void php_fstat(php_stream *stream, zval *return_value)
1402 {
1403 php_stream_statbuf stat_ssb;
1404 zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev,
1405 stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks;
1406 char *stat_sb_names[13] = {
1407 "dev", "ino", "mode", "nlink", "uid", "gid", "rdev",
1408 "size", "atime", "mtime", "ctime", "blksize", "blocks"
1409 };
1410
1411 if (php_stream_stat(stream, &stat_ssb)) {
1412 RETURN_FALSE;
1413 }
1414
1415 array_init(return_value);
1416
1417 ZVAL_LONG(&stat_dev, stat_ssb.sb.st_dev);
1418 ZVAL_LONG(&stat_ino, stat_ssb.sb.st_ino);
1419 ZVAL_LONG(&stat_mode, stat_ssb.sb.st_mode);
1420 ZVAL_LONG(&stat_nlink, stat_ssb.sb.st_nlink);
1421 ZVAL_LONG(&stat_uid, stat_ssb.sb.st_uid);
1422 ZVAL_LONG(&stat_gid, stat_ssb.sb.st_gid);
1423 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1424 ZVAL_LONG(&stat_rdev, stat_ssb.sb.st_rdev);
1425 #else
1426 ZVAL_LONG(&stat_rdev, -1);
1427 #endif
1428 ZVAL_LONG(&stat_size, stat_ssb.sb.st_size);
1429 ZVAL_LONG(&stat_atime, stat_ssb.sb.st_atime);
1430 ZVAL_LONG(&stat_mtime, stat_ssb.sb.st_mtime);
1431 ZVAL_LONG(&stat_ctime, stat_ssb.sb.st_ctime);
1432 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1433 ZVAL_LONG(&stat_blksize, stat_ssb.sb.st_blksize);
1434 #else
1435 ZVAL_LONG(&stat_blksize,-1);
1436 #endif
1437 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1438 ZVAL_LONG(&stat_blocks, stat_ssb.sb.st_blocks);
1439 #else
1440 ZVAL_LONG(&stat_blocks,-1);
1441 #endif
1442 /* Store numeric indexes in proper order */
1443 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_dev);
1444 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ino);
1445 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mode);
1446 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_nlink);
1447 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_uid);
1448 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_gid);
1449 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_rdev);
1450 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_size);
1451 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_atime);
1452 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mtime);
1453 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ctime);
1454 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blksize);
1455 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks);
1456
1457 /* Store string indexes referencing the same zval*/
1458 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev);
1459 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino);
1460 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode);
1461 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink);
1462 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid);
1463 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid);
1464 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev);
1465 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size);
1466 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime);
1467 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime);
1468 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime);
1469 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize);
1470 zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks);
1471 }
1472
1473 /* {{{ Stat() on a filehandle */
PHP_FUNCTION(fstat)1474 PHP_FUNCTION(fstat)
1475 {
1476 zval *fp;
1477 php_stream *stream;
1478
1479 ZEND_PARSE_PARAMETERS_START(1, 1)
1480 Z_PARAM_RESOURCE(fp)
1481 ZEND_PARSE_PARAMETERS_END();
1482
1483 PHP_STREAM_FROM_ZVAL(stream, fp);
1484
1485 php_fstat(stream, return_value);
1486 }
1487 /* }}} */
1488
1489 /* {{{ Copy a file */
PHP_FUNCTION(copy)1490 PHP_FUNCTION(copy)
1491 {
1492 char *source, *target;
1493 size_t source_len, target_len;
1494 zval *zcontext = NULL;
1495 php_stream_context *context;
1496
1497 ZEND_PARSE_PARAMETERS_START(2, 3)
1498 Z_PARAM_PATH(source, source_len)
1499 Z_PARAM_PATH(target, target_len)
1500 Z_PARAM_OPTIONAL
1501 Z_PARAM_RESOURCE_OR_NULL(zcontext)
1502 ZEND_PARSE_PARAMETERS_END();
1503
1504 if (php_stream_locate_url_wrapper(source, NULL, 0) == &php_plain_files_wrapper && php_check_open_basedir(source)) {
1505 RETURN_FALSE;
1506 }
1507
1508 context = php_stream_context_from_zval(zcontext, 0);
1509
1510 if (php_copy_file_ctx(source, target, 0, context) == SUCCESS) {
1511 RETURN_TRUE;
1512 } else {
1513 RETURN_FALSE;
1514 }
1515 }
1516 /* }}} */
1517
1518 /* {{{ php_copy_file */
php_copy_file(const char * src,const char * dest)1519 PHPAPI int php_copy_file(const char *src, const char *dest)
1520 {
1521 return php_copy_file_ctx(src, dest, 0, NULL);
1522 }
1523 /* }}} */
1524
1525 /* {{{ php_copy_file_ex */
php_copy_file_ex(const char * src,const char * dest,int src_flg)1526 PHPAPI int php_copy_file_ex(const char *src, const char *dest, int src_flg)
1527 {
1528 return php_copy_file_ctx(src, dest, src_flg, NULL);
1529 }
1530 /* }}} */
1531
1532 /* {{{ php_copy_file_ctx */
php_copy_file_ctx(const char * src,const char * dest,int src_flg,php_stream_context * ctx)1533 PHPAPI int php_copy_file_ctx(const char *src, const char *dest, int src_flg, php_stream_context *ctx)
1534 {
1535 php_stream *srcstream = NULL, *deststream = NULL;
1536 int ret = FAILURE;
1537 php_stream_statbuf src_s, dest_s;
1538 int src_stat_flags = (src_flg & STREAM_DISABLE_OPEN_BASEDIR) ? PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR : 0;
1539
1540 switch (php_stream_stat_path_ex(src, src_stat_flags, &src_s, ctx)) {
1541 case -1:
1542 /* non-statable stream */
1543 goto safe_to_copy;
1544 break;
1545 case 0:
1546 break;
1547 default: /* failed to stat file, does not exist? */
1548 return ret;
1549 }
1550 if (S_ISDIR(src_s.sb.st_mode)) {
1551 php_error_docref(NULL, E_WARNING, "The first argument to copy() function cannot be a directory");
1552 return FAILURE;
1553 }
1554
1555 switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET, &dest_s, ctx)) {
1556 case -1:
1557 /* non-statable stream */
1558 goto safe_to_copy;
1559 break;
1560 case 0:
1561 break;
1562 default: /* failed to stat file, does not exist? */
1563 return ret;
1564 }
1565 if (S_ISDIR(dest_s.sb.st_mode)) {
1566 php_error_docref(NULL, E_WARNING, "The second argument to copy() function cannot be a directory");
1567 return FAILURE;
1568 }
1569 if (!src_s.sb.st_ino || !dest_s.sb.st_ino) {
1570 goto no_stat;
1571 }
1572 if (src_s.sb.st_ino == dest_s.sb.st_ino && src_s.sb.st_dev == dest_s.sb.st_dev) {
1573 return ret;
1574 } else {
1575 goto safe_to_copy;
1576 }
1577 no_stat:
1578 {
1579 char *sp, *dp;
1580 int res;
1581
1582 if ((sp = expand_filepath(src, NULL)) == NULL) {
1583 return ret;
1584 }
1585 if ((dp = expand_filepath(dest, NULL)) == NULL) {
1586 efree(sp);
1587 goto safe_to_copy;
1588 }
1589
1590 res =
1591 #ifndef PHP_WIN32
1592 !strcmp(sp, dp);
1593 #else
1594 !strcasecmp(sp, dp);
1595 #endif
1596
1597 efree(sp);
1598 efree(dp);
1599 if (res) {
1600 return ret;
1601 }
1602 }
1603 safe_to_copy:
1604
1605 srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx);
1606
1607 if (!srcstream) {
1608 return ret;
1609 }
1610
1611 deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
1612
1613 if (deststream) {
1614 ret = php_stream_copy_to_stream_ex(srcstream, deststream, PHP_STREAM_COPY_ALL, NULL);
1615 }
1616 php_stream_close(srcstream);
1617 if (deststream) {
1618 php_stream_close(deststream);
1619 }
1620 return ret;
1621 }
1622 /* }}} */
1623
1624 /* {{{ Binary-safe file read */
PHP_FUNCTION(fread)1625 PHPAPI PHP_FUNCTION(fread)
1626 {
1627 zval *res;
1628 zend_long len;
1629 php_stream *stream;
1630 zend_string *str;
1631
1632 ZEND_PARSE_PARAMETERS_START(2, 2)
1633 Z_PARAM_RESOURCE(res)
1634 Z_PARAM_LONG(len)
1635 ZEND_PARSE_PARAMETERS_END();
1636
1637 PHP_STREAM_FROM_ZVAL(stream, res);
1638
1639 if (len <= 0) {
1640 zend_argument_value_error(2, "must be greater than 0");
1641 RETURN_THROWS();
1642 }
1643
1644 str = php_stream_read_to_str(stream, len);
1645 if (!str) {
1646 zval_ptr_dtor_str(return_value);
1647 RETURN_FALSE;
1648 }
1649
1650 RETURN_STR(str);
1651 }
1652 /* }}} */
1653
php_fgetcsv_lookup_trailing_spaces(const char * ptr,size_t len)1654 static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t len) /* {{{ */
1655 {
1656 int inc_len;
1657 unsigned char last_chars[2] = { 0, 0 };
1658
1659 while (len > 0) {
1660 inc_len = (*ptr == '\0' ? 1 : php_mblen(ptr, len));
1661 switch (inc_len) {
1662 case -2:
1663 case -1:
1664 inc_len = 1;
1665 php_mb_reset();
1666 break;
1667 case 0:
1668 goto quit_loop;
1669 case 1:
1670 default:
1671 last_chars[0] = last_chars[1];
1672 last_chars[1] = *ptr;
1673 break;
1674 }
1675 ptr += inc_len;
1676 len -= inc_len;
1677 }
1678 quit_loop:
1679 switch (last_chars[1]) {
1680 case '\n':
1681 if (last_chars[0] == '\r') {
1682 return ptr - 2;
1683 }
1684 ZEND_FALLTHROUGH;
1685 case '\r':
1686 return ptr - 1;
1687 }
1688 return ptr;
1689 }
1690 /* }}} */
1691
1692 #define FPUTCSV_FLD_CHK(c) memchr(ZSTR_VAL(field_str), c, ZSTR_LEN(field_str))
1693
1694 /* {{{ Format line as CSV and write to file pointer */
PHP_FUNCTION(fputcsv)1695 PHP_FUNCTION(fputcsv)
1696 {
1697 char delimiter = ','; /* allow this to be set as parameter */
1698 char enclosure = '"'; /* allow this to be set as parameter */
1699 int escape_char = (unsigned char) '\\'; /* allow this to be set as parameter */
1700 php_stream *stream;
1701 zval *fp = NULL, *fields = NULL;
1702 ssize_t ret;
1703 char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
1704 size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
1705 zend_string *eol_str = NULL;
1706
1707 ZEND_PARSE_PARAMETERS_START(2, 6)
1708 Z_PARAM_RESOURCE(fp)
1709 Z_PARAM_ARRAY(fields)
1710 Z_PARAM_OPTIONAL
1711 Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1712 Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1713 Z_PARAM_STRING(escape_str, escape_str_len)
1714 Z_PARAM_STR_OR_NULL(eol_str)
1715 ZEND_PARSE_PARAMETERS_END();
1716
1717 if (delimiter_str != NULL) {
1718 /* Make sure that there is at least one character in string */
1719 if (delimiter_str_len != 1) {
1720 zend_argument_value_error(3, "must be a single character");
1721 RETURN_THROWS();
1722 }
1723
1724 /* use first character from string */
1725 delimiter = *delimiter_str;
1726 }
1727
1728 if (enclosure_str != NULL) {
1729 if (enclosure_str_len != 1) {
1730 zend_argument_value_error(4, "must be a single character");
1731 RETURN_THROWS();
1732 }
1733 /* use first character from string */
1734 enclosure = *enclosure_str;
1735 }
1736
1737 if (escape_str != NULL) {
1738 if (escape_str_len > 1) {
1739 zend_argument_value_error(5, "must be empty or a single character");
1740 RETURN_THROWS();
1741 }
1742 if (escape_str_len < 1) {
1743 escape_char = PHP_CSV_NO_ESCAPE;
1744 } else {
1745 /* use first character from string */
1746 escape_char = (unsigned char) *escape_str;
1747 }
1748 }
1749
1750 PHP_STREAM_FROM_ZVAL(stream, fp);
1751
1752 ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str);
1753 if (ret < 0) {
1754 RETURN_FALSE;
1755 }
1756 RETURN_LONG(ret);
1757 }
1758 /* }}} */
1759
1760 /* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str) */
php_fputcsv(php_stream * stream,zval * fields,char delimiter,char enclosure,int escape_char,zend_string * eol_str)1761 PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str)
1762 {
1763 uint32_t count, i = 0;
1764 size_t ret;
1765 zval *field_tmp;
1766 smart_str csvline = {0};
1767
1768 ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1769 count = zend_hash_num_elements(Z_ARRVAL_P(fields));
1770 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(fields), field_tmp) {
1771 zend_string *tmp_field_str;
1772 zend_string *field_str = zval_get_tmp_string(field_tmp, &tmp_field_str);
1773
1774 /* enclose a field that contains a delimiter, an enclosure character, or a newline */
1775 if (FPUTCSV_FLD_CHK(delimiter) ||
1776 FPUTCSV_FLD_CHK(enclosure) ||
1777 (escape_char != PHP_CSV_NO_ESCAPE && FPUTCSV_FLD_CHK(escape_char)) ||
1778 FPUTCSV_FLD_CHK('\n') ||
1779 FPUTCSV_FLD_CHK('\r') ||
1780 FPUTCSV_FLD_CHK('\t') ||
1781 FPUTCSV_FLD_CHK(' ')
1782 ) {
1783 char *ch = ZSTR_VAL(field_str);
1784 char *end = ch + ZSTR_LEN(field_str);
1785 int escaped = 0;
1786
1787 smart_str_appendc(&csvline, enclosure);
1788 while (ch < end) {
1789 if (escape_char != PHP_CSV_NO_ESCAPE && *ch == escape_char) {
1790 escaped = 1;
1791 } else if (!escaped && *ch == enclosure) {
1792 smart_str_appendc(&csvline, enclosure);
1793 } else {
1794 escaped = 0;
1795 }
1796 smart_str_appendc(&csvline, *ch);
1797 ch++;
1798 }
1799 smart_str_appendc(&csvline, enclosure);
1800 } else {
1801 smart_str_append(&csvline, field_str);
1802 }
1803
1804 if (++i != count) {
1805 smart_str_appendl(&csvline, &delimiter, 1);
1806 }
1807 zend_tmp_string_release(tmp_field_str);
1808 } ZEND_HASH_FOREACH_END();
1809
1810 if (eol_str) {
1811 smart_str_append(&csvline, eol_str);
1812 } else {
1813 smart_str_appendc(&csvline, '\n');
1814 }
1815 smart_str_0(&csvline);
1816
1817 ret = php_stream_write(stream, ZSTR_VAL(csvline.s), ZSTR_LEN(csvline.s));
1818
1819 smart_str_free(&csvline);
1820
1821 return ret;
1822 }
1823 /* }}} */
1824
1825 /* {{{ Get line from file pointer and parse for CSV fields */
PHP_FUNCTION(fgetcsv)1826 PHP_FUNCTION(fgetcsv)
1827 {
1828 char delimiter = ','; /* allow this to be set as parameter */
1829 char enclosure = '"'; /* allow this to be set as parameter */
1830 int escape = (unsigned char) '\\';
1831
1832 zend_long len = 0;
1833 size_t buf_len;
1834 char *buf;
1835 php_stream *stream;
1836
1837 {
1838 zval *fd;
1839 bool len_is_null = 1;
1840 char *delimiter_str = NULL;
1841 size_t delimiter_str_len = 0;
1842 char *enclosure_str = NULL;
1843 size_t enclosure_str_len = 0;
1844 char *escape_str = NULL;
1845 size_t escape_str_len = 0;
1846
1847 ZEND_PARSE_PARAMETERS_START(1, 5)
1848 Z_PARAM_RESOURCE(fd)
1849 Z_PARAM_OPTIONAL
1850 Z_PARAM_LONG_OR_NULL(len, len_is_null)
1851 Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1852 Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1853 Z_PARAM_STRING(escape_str, escape_str_len)
1854 ZEND_PARSE_PARAMETERS_END();
1855
1856 if (delimiter_str != NULL) {
1857 /* Make sure that there is at least one character in string */
1858 if (delimiter_str_len != 1) {
1859 zend_argument_value_error(3, "must be a single character");
1860 RETURN_THROWS();
1861 }
1862
1863 /* use first character from string */
1864 delimiter = delimiter_str[0];
1865 }
1866
1867 if (enclosure_str != NULL) {
1868 if (enclosure_str_len != 1) {
1869 zend_argument_value_error(4, "must be a single character");
1870 RETURN_THROWS();
1871 }
1872
1873 /* use first character from string */
1874 enclosure = enclosure_str[0];
1875 }
1876
1877 if (escape_str != NULL) {
1878 if (escape_str_len > 1) {
1879 zend_argument_value_error(5, "must be empty or a single character");
1880 RETURN_THROWS();
1881 }
1882
1883 if (escape_str_len < 1) {
1884 escape = PHP_CSV_NO_ESCAPE;
1885 } else {
1886 escape = (unsigned char) escape_str[0];
1887 }
1888 }
1889
1890 if (len_is_null || len == 0) {
1891 len = -1;
1892 } else if (len < 0) {
1893 zend_argument_value_error(2, "must be a greater than or equal to 0");
1894 RETURN_THROWS();
1895 }
1896
1897 PHP_STREAM_FROM_ZVAL(stream, fd);
1898 }
1899
1900 if (len < 0) {
1901 if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) {
1902 RETURN_FALSE;
1903 }
1904 } else {
1905 buf = emalloc(len + 1);
1906 if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) {
1907 efree(buf);
1908 RETURN_FALSE;
1909 }
1910 }
1911
1912 HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape, buf_len, buf);
1913 if (values == NULL) {
1914 values = php_bc_fgetcsv_empty_line();
1915 }
1916 RETURN_ARR(values);
1917 }
1918 /* }}} */
1919
php_bc_fgetcsv_empty_line(void)1920 PHPAPI HashTable *php_bc_fgetcsv_empty_line(void)
1921 {
1922 HashTable *values = zend_new_array(1);
1923 zval tmp;
1924 ZVAL_NULL(&tmp);
1925 zend_hash_next_index_insert(values, &tmp);
1926 return values;
1927 }
1928
php_fgetcsv(php_stream * stream,char delimiter,char enclosure,int escape_char,size_t buf_len,char * buf)1929 PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure, int escape_char, size_t buf_len, char *buf) /* {{{ */
1930 {
1931 char *temp, *bptr, *line_end, *limit;
1932 size_t temp_len, line_end_len;
1933 int inc_len;
1934 bool first_field = true;
1935
1936 ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1937
1938 /* initialize internal state */
1939 php_mb_reset();
1940
1941 /* Now into new section that parses buf for delimiter/enclosure fields */
1942
1943 /* Strip trailing space from buf, saving end of line in case required for enclosure field */
1944
1945 bptr = buf;
1946 line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
1947 line_end_len = buf_len - (size_t)(limit - buf);
1948
1949 /* reserve workspace for building each individual field */
1950 temp_len = buf_len;
1951 temp = emalloc(temp_len + line_end_len + 1);
1952
1953 /* Initialize values HashTable */
1954 HashTable *values = zend_new_array(0);
1955
1956 /* Main loop to read CSV fields */
1957 /* NB this routine will return NULL for a blank line */
1958 do {
1959 char *comp_end, *hunk_begin;
1960 char *tptr = temp;
1961
1962 inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
1963 if (inc_len == 1) {
1964 char *tmp = bptr;
1965 while ((*tmp != delimiter) && isspace((int)*(unsigned char *)tmp)) {
1966 tmp++;
1967 }
1968 if (*tmp == enclosure && tmp < limit) {
1969 bptr = tmp;
1970 }
1971 }
1972
1973 if (first_field && bptr == line_end) {
1974 zend_array_destroy(values);
1975 values = NULL;
1976 break;
1977 }
1978 first_field = false;
1979 /* 2. Read field, leaving bptr pointing at start of next field */
1980 if (inc_len != 0 && *bptr == enclosure) {
1981 int state = 0;
1982
1983 bptr++; /* move on to first character in field */
1984 hunk_begin = bptr;
1985
1986 /* 2A. handle enclosure delimited field */
1987 for (;;) {
1988 switch (inc_len) {
1989 case 0:
1990 switch (state) {
1991 case 2:
1992 tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin - 1));
1993 hunk_begin = bptr;
1994 goto quit_loop_2;
1995
1996 case 1:
1997 tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin));
1998 hunk_begin = bptr;
1999 ZEND_FALLTHROUGH;
2000
2001 case 0: {
2002 if (hunk_begin != line_end) {
2003 tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin));
2004 hunk_begin = bptr;
2005 }
2006
2007 /* add the embedded line end to the field */
2008 tptr = zend_mempcpy(tptr, line_end, line_end_len);
2009
2010 /* nothing can be fetched if stream is NULL (e.g. str_getcsv()) */
2011 if (stream == NULL) {
2012 /* the enclosure is unterminated */
2013 if (bptr > limit) {
2014 /* if the line ends with enclosure, we need to go back by
2015 * one character so the \0 character is not copied. */
2016 if (hunk_begin == bptr) {
2017 --hunk_begin;
2018 }
2019 --bptr;
2020 }
2021 goto quit_loop_2;
2022 }
2023
2024 size_t new_len;
2025 char *new_buf = php_stream_get_line(stream, NULL, 0, &new_len);
2026 if (!new_buf) {
2027 /* we've got an unterminated enclosure,
2028 * assign all the data from the start of
2029 * the enclosure to end of data to the
2030 * last element */
2031 if (bptr > limit) {
2032 /* if the line ends with enclosure, we need to go back by
2033 * one character so the \0 character is not copied. */
2034 if (hunk_begin == bptr) {
2035 --hunk_begin;
2036 }
2037 --bptr;
2038 }
2039 goto quit_loop_2;
2040 }
2041
2042 temp_len += new_len;
2043 char *new_temp = erealloc(temp, temp_len);
2044 tptr = new_temp + (size_t)(tptr - temp);
2045 temp = new_temp;
2046
2047 efree(buf);
2048 buf_len = new_len;
2049 bptr = buf = new_buf;
2050 hunk_begin = buf;
2051
2052 line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
2053 line_end_len = buf_len - (size_t)(limit - buf);
2054
2055 state = 0;
2056 } break;
2057 }
2058 break;
2059
2060 case -2:
2061 case -1:
2062 php_mb_reset();
2063 ZEND_FALLTHROUGH;
2064 case 1:
2065 /* we need to determine if the enclosure is
2066 * 'real' or is it escaped */
2067 switch (state) {
2068 case 1: /* escaped */
2069 bptr++;
2070 state = 0;
2071 break;
2072 case 2: /* embedded enclosure ? let's check it */
2073 if (*bptr != enclosure) {
2074 /* real enclosure */
2075 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2076 hunk_begin = bptr;
2077 goto quit_loop_2;
2078 }
2079 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2080 bptr++;
2081 hunk_begin = bptr;
2082 state = 0;
2083 break;
2084 default:
2085 if (*bptr == enclosure) {
2086 state = 2;
2087 } else if (escape_char != PHP_CSV_NO_ESCAPE && *bptr == escape_char) {
2088 state = 1;
2089 }
2090 bptr++;
2091 break;
2092 }
2093 break;
2094
2095 default:
2096 switch (state) {
2097 case 2:
2098 /* real enclosure */
2099 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2100 hunk_begin = bptr;
2101 goto quit_loop_2;
2102 case 1:
2103 bptr += inc_len;
2104 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2105 hunk_begin = bptr;
2106 state = 0;
2107 break;
2108 default:
2109 bptr += inc_len;
2110 break;
2111 }
2112 break;
2113 }
2114 inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2115 }
2116
2117 quit_loop_2:
2118 /* look up for a delimiter */
2119 for (;;) {
2120 switch (inc_len) {
2121 case 0:
2122 goto quit_loop_3;
2123
2124 case -2:
2125 case -1:
2126 inc_len = 1;
2127 php_mb_reset();
2128 ZEND_FALLTHROUGH;
2129 case 1:
2130 if (*bptr == delimiter) {
2131 goto quit_loop_3;
2132 }
2133 break;
2134 default:
2135 break;
2136 }
2137 bptr += inc_len;
2138 inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2139 }
2140
2141 quit_loop_3:
2142 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2143 bptr += inc_len;
2144 comp_end = tptr;
2145 } else {
2146 /* 2B. Handle non-enclosure field */
2147
2148 hunk_begin = bptr;
2149
2150 for (;;) {
2151 switch (inc_len) {
2152 case 0:
2153 goto quit_loop_4;
2154 case -2:
2155 case -1:
2156 inc_len = 1;
2157 php_mb_reset();
2158 ZEND_FALLTHROUGH;
2159 case 1:
2160 if (*bptr == delimiter) {
2161 goto quit_loop_4;
2162 }
2163 break;
2164 default:
2165 break;
2166 }
2167 bptr += inc_len;
2168 inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2169 }
2170 quit_loop_4:
2171 tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2172
2173 comp_end = (char *)php_fgetcsv_lookup_trailing_spaces(temp, tptr - temp);
2174 if (*bptr == delimiter) {
2175 bptr++;
2176 }
2177 }
2178
2179 /* 3. Now pass our field back to php */
2180 *comp_end = '\0';
2181
2182 zval z_tmp;
2183 ZVAL_STRINGL(&z_tmp, temp, comp_end - temp);
2184 zend_hash_next_index_insert(values, &z_tmp);
2185 } while (inc_len > 0);
2186
2187 efree(temp);
2188 if (stream) {
2189 efree(buf);
2190 }
2191
2192 return values;
2193 }
2194 /* }}} */
2195
2196 /* {{{ Return the resolved path */
PHP_FUNCTION(realpath)2197 PHP_FUNCTION(realpath)
2198 {
2199 char *filename;
2200 size_t filename_len;
2201 char resolved_path_buff[MAXPATHLEN];
2202
2203 ZEND_PARSE_PARAMETERS_START(1, 1)
2204 Z_PARAM_PATH(filename, filename_len)
2205 ZEND_PARSE_PARAMETERS_END();
2206
2207 if (VCWD_REALPATH(filename, resolved_path_buff)) {
2208 if (php_check_open_basedir(resolved_path_buff)) {
2209 RETURN_FALSE;
2210 }
2211
2212 #ifdef ZTS
2213 if (VCWD_ACCESS(resolved_path_buff, F_OK)) {
2214 RETURN_FALSE;
2215 }
2216 #endif
2217 RETURN_STRING(resolved_path_buff);
2218 } else {
2219 RETURN_FALSE;
2220 }
2221 }
2222 /* }}} */
2223
2224 /* See http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.2 */
2225 #define PHP_META_HTML401_CHARS "-_.:"
2226
2227 /* {{{ php_next_meta_token
2228 Tokenizes an HTML file for get_meta_tags */
php_next_meta_token(php_meta_tags_data * md)2229 php_meta_tags_token php_next_meta_token(php_meta_tags_data *md)
2230 {
2231 int ch = 0, compliment;
2232 char buff[META_DEF_BUFSIZE + 1];
2233
2234 memset((void *)buff, 0, META_DEF_BUFSIZE + 1);
2235
2236 while (md->ulc || (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)))) {
2237 if (php_stream_eof(md->stream)) {
2238 break;
2239 }
2240
2241 if (md->ulc) {
2242 ch = md->lc;
2243 md->ulc = 0;
2244 }
2245
2246 switch (ch) {
2247 case '<':
2248 return TOK_OPENTAG;
2249 break;
2250
2251 case '>':
2252 return TOK_CLOSETAG;
2253 break;
2254
2255 case '=':
2256 return TOK_EQUAL;
2257 break;
2258 case '/':
2259 return TOK_SLASH;
2260 break;
2261
2262 case '\'':
2263 case '"':
2264 compliment = ch;
2265 md->token_len = 0;
2266 while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && ch != compliment && ch != '<' && ch != '>') {
2267 buff[(md->token_len)++] = ch;
2268
2269 if (md->token_len == META_DEF_BUFSIZE) {
2270 break;
2271 }
2272 }
2273
2274 if (ch == '<' || ch == '>') {
2275 /* Was just an apostrophe */
2276 md->ulc = 1;
2277 md->lc = ch;
2278 }
2279
2280 /* We don't need to alloc unless we are in a meta tag */
2281 if (md->in_meta) {
2282 md->token_data = (char *) emalloc(md->token_len + 1);
2283 memcpy(md->token_data, buff, md->token_len+1);
2284 }
2285
2286 return TOK_STRING;
2287 break;
2288
2289 case '\n':
2290 case '\r':
2291 case '\t':
2292 break;
2293
2294 case ' ':
2295 return TOK_SPACE;
2296 break;
2297
2298 default:
2299 if (isalnum(ch)) {
2300 md->token_len = 0;
2301 buff[(md->token_len)++] = ch;
2302 while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && (isalnum(ch) || strchr(PHP_META_HTML401_CHARS, ch))) {
2303 buff[(md->token_len)++] = ch;
2304
2305 if (md->token_len == META_DEF_BUFSIZE) {
2306 break;
2307 }
2308 }
2309
2310 /* This is ugly, but we have to replace ungetc */
2311 if (!isalpha(ch) && ch != '-') {
2312 md->ulc = 1;
2313 md->lc = ch;
2314 }
2315
2316 md->token_data = (char *) emalloc(md->token_len + 1);
2317 memcpy(md->token_data, buff, md->token_len+1);
2318
2319 return TOK_ID;
2320 } else {
2321 return TOK_OTHER;
2322 }
2323 break;
2324 }
2325 }
2326
2327 return TOK_EOF;
2328 }
2329 /* }}} */
2330
2331 #ifdef HAVE_FNMATCH
2332 /* {{{ Match filename against pattern */
PHP_FUNCTION(fnmatch)2333 PHP_FUNCTION(fnmatch)
2334 {
2335 char *pattern, *filename;
2336 size_t pattern_len, filename_len;
2337 zend_long flags = 0;
2338
2339 ZEND_PARSE_PARAMETERS_START(2, 3)
2340 Z_PARAM_PATH(pattern, pattern_len)
2341 Z_PARAM_PATH(filename, filename_len)
2342 Z_PARAM_OPTIONAL
2343 Z_PARAM_LONG(flags)
2344 ZEND_PARSE_PARAMETERS_END();
2345
2346 if (filename_len >= MAXPATHLEN) {
2347 php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2348 RETURN_FALSE;
2349 }
2350 if (pattern_len >= MAXPATHLEN) {
2351 php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2352 RETURN_FALSE;
2353 }
2354
2355 RETURN_BOOL( ! fnmatch( pattern, filename, (int)flags ));
2356 }
2357 /* }}} */
2358 #endif
2359
2360 /* {{{ Returns directory path used for temporary files */
PHP_FUNCTION(sys_get_temp_dir)2361 PHP_FUNCTION(sys_get_temp_dir)
2362 {
2363 ZEND_PARSE_PARAMETERS_NONE();
2364
2365 RETURN_STRING((char *)php_get_temporary_directory());
2366 }
2367 /* }}} */
2368