1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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 : | Author: Thies C. Arntzen <thies@thieso.net> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: dir.c,v 1.147.2.3.2.4 2007/02/24 17:16:23 iliaa Exp $ */
20 :
21 : /* {{{ includes/startup/misc */
22 :
23 : #include "php.h"
24 : #include "fopen_wrappers.h"
25 : #include "file.h"
26 : #include "php_dir.h"
27 : #include "php_scandir.h"
28 :
29 : #ifdef HAVE_DIRENT_H
30 : #include <dirent.h>
31 : #endif
32 :
33 : #if HAVE_UNISTD_H
34 : #include <unistd.h>
35 : #endif
36 :
37 : #include <errno.h>
38 :
39 : #ifdef PHP_WIN32
40 : #include "win32/readdir.h"
41 : #endif
42 :
43 :
44 : #ifdef HAVE_GLOB
45 : #ifndef PHP_WIN32
46 : #include <glob.h>
47 : #else
48 : #include "win32/glob.h"
49 : #endif
50 : #endif
51 :
52 : typedef struct {
53 : int default_dir;
54 : } php_dir_globals;
55 :
56 : #ifdef ZTS
57 : #define DIRG(v) TSRMG(dir_globals_id, php_dir_globals *, v)
58 : int dir_globals_id;
59 : #else
60 : #define DIRG(v) (dir_globals.v)
61 : php_dir_globals dir_globals;
62 : #endif
63 :
64 : #if 0
65 : typedef struct {
66 : int id;
67 : DIR *dir;
68 : } php_dir;
69 :
70 : static int le_dirp;
71 : #endif
72 :
73 : static zend_class_entry *dir_class_entry_ptr;
74 :
75 : #define FETCH_DIRP() \
76 : if (ZEND_NUM_ARGS() == 0) { \
77 : myself = getThis(); \
78 : if (myself) { \
79 : if (zend_hash_find(Z_OBJPROP_P(myself), "handle", sizeof("handle"), (void **)&tmp) == FAILURE) { \
80 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find my handle property"); \
81 : RETURN_FALSE; \
82 : } \
83 : ZEND_FETCH_RESOURCE(dirp, php_stream *, tmp, -1, "Directory", php_file_le_stream()); \
84 : } else { \
85 : ZEND_FETCH_RESOURCE(dirp, php_stream *, 0, DIRG(default_dir), "Directory", php_file_le_stream()); \
86 : } \
87 : } else if ((ZEND_NUM_ARGS() != 1) || zend_get_parameters_ex(1, &id) == FAILURE) { \
88 : WRONG_PARAM_COUNT; \
89 : } else { \
90 : dirp = (php_stream *) zend_fetch_resource(id TSRMLS_CC, -1, "Directory", NULL, 1, php_file_le_stream()); \
91 : if (!dirp) \
92 : RETURN_FALSE; \
93 : }
94 :
95 : static zend_function_entry php_dir_class_functions[] = {
96 : PHP_FALIAS(close, closedir, NULL)
97 : PHP_FALIAS(rewind, rewinddir, NULL)
98 : PHP_NAMED_FE(read, php_if_readdir, NULL)
99 : {NULL, NULL, NULL}
100 : };
101 :
102 :
103 : static void php_set_default_dir(int id TSRMLS_DC)
104 24 : {
105 24 : if (DIRG(default_dir)!=-1) {
106 14 : zend_list_delete(DIRG(default_dir));
107 : }
108 :
109 24 : if (id != -1) {
110 14 : zend_list_addref(id);
111 : }
112 :
113 24 : DIRG(default_dir) = id;
114 24 : }
115 :
116 : PHP_RINIT_FUNCTION(dir)
117 219 : {
118 219 : DIRG(default_dir) = -1;
119 219 : return SUCCESS;
120 : }
121 :
122 : PHP_MINIT_FUNCTION(dir)
123 220 : {
124 : static char dirsep_str[2], pathsep_str[2];
125 : zend_class_entry dir_class_entry;
126 :
127 220 : INIT_CLASS_ENTRY(dir_class_entry, "Directory", php_dir_class_functions);
128 220 : dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry TSRMLS_CC);
129 :
130 : #ifdef ZTS
131 : ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
132 : #endif
133 :
134 220 : dirsep_str[0] = DEFAULT_SLASH;
135 220 : dirsep_str[1] = '\0';
136 220 : REGISTER_STRING_CONSTANT("DIRECTORY_SEPARATOR", dirsep_str, CONST_CS|CONST_PERSISTENT);
137 :
138 220 : pathsep_str[0] = ZEND_PATHS_SEPARATOR;
139 220 : pathsep_str[1] = '\0';
140 220 : REGISTER_STRING_CONSTANT("PATH_SEPARATOR", pathsep_str, CONST_CS|CONST_PERSISTENT);
141 :
142 : #ifdef HAVE_GLOB
143 : #ifdef GLOB_BRACE
144 220 : REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE, CONST_CS | CONST_PERSISTENT);
145 : #endif
146 : #ifdef GLOB_MARK
147 220 : REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK, CONST_CS | CONST_PERSISTENT);
148 : #endif
149 : #ifdef GLOB_NOSORT
150 220 : REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT, CONST_CS | CONST_PERSISTENT);
151 : #endif
152 : #ifdef GLOB_NOCHECK
153 220 : REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK, CONST_CS | CONST_PERSISTENT);
154 : #endif
155 : #ifdef GLOB_NOESCAPE
156 220 : REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE, CONST_CS | CONST_PERSISTENT);
157 : #endif
158 : #ifdef GLOB_ERR
159 220 : REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR, CONST_CS | CONST_PERSISTENT);
160 : #endif
161 :
162 : #ifndef GLOB_ONLYDIR
163 : #define GLOB_ONLYDIR (1<<30)
164 : #define GLOB_EMULATE_ONLYDIR
165 : #define GLOB_FLAGMASK (~GLOB_ONLYDIR)
166 : #else
167 : #define GLOB_FLAGMASK (~0)
168 : #endif
169 :
170 220 : REGISTER_LONG_CONSTANT("GLOB_ONLYDIR", GLOB_ONLYDIR, CONST_CS | CONST_PERSISTENT);
171 :
172 : #endif /* HAVE_GLOB */
173 :
174 220 : return SUCCESS;
175 : }
176 : /* }}} */
177 :
178 : /* {{{ internal functions */
179 : static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
180 14 : {
181 : char *dirname;
182 : int dir_len;
183 14 : zval *zcontext = NULL;
184 14 : php_stream_context *context = NULL;
185 : php_stream *dirp;
186 :
187 14 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &dirname, &dir_len, &zcontext) == FAILURE) {
188 0 : RETURN_NULL();
189 : }
190 :
191 14 : if (zcontext) {
192 0 : context = php_stream_context_from_zval(zcontext, 0);
193 : }
194 :
195 14 : dirp = php_stream_opendir(dirname, ENFORCE_SAFE_MODE|REPORT_ERRORS, context);
196 :
197 14 : if (dirp == NULL) {
198 0 : RETURN_FALSE;
199 : }
200 :
201 14 : php_set_default_dir(dirp->rsrc_id TSRMLS_CC);
202 :
203 14 : if (createobject) {
204 0 : object_init_ex(return_value, dir_class_entry_ptr);
205 0 : add_property_stringl(return_value, "path", dirname, dir_len, 1);
206 0 : add_property_resource(return_value, "handle", dirp->rsrc_id);
207 : php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
208 : } else {
209 14 : php_stream_to_zval(dirp, return_value);
210 : }
211 : }
212 : /* }}} */
213 :
214 : /* {{{ proto mixed opendir(string path[, resource context])
215 : Open a directory and return a dir_handle */
216 : PHP_FUNCTION(opendir)
217 14 : {
218 14 : _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
219 14 : }
220 : /* }}} */
221 :
222 : /* {{{ proto object dir(string directory[, resource context])
223 : Directory class with properties, handle and class and methods read, rewind and close */
224 : PHP_FUNCTION(getdir)
225 0 : {
226 0 : _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
227 0 : }
228 : /* }}} */
229 :
230 : /* {{{ proto void closedir([resource dir_handle])
231 : Close directory connection identified by the dir_handle */
232 : PHP_FUNCTION(closedir)
233 14 : {
234 : zval **id, **tmp, *myself;
235 : php_stream *dirp;
236 :
237 14 : FETCH_DIRP();
238 :
239 14 : if (dirp->rsrc_id == DIRG(default_dir)) {
240 10 : php_set_default_dir(-1 TSRMLS_CC);
241 : }
242 :
243 14 : zend_list_delete(dirp->rsrc_id);
244 : }
245 : /* }}} */
246 :
247 : #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
248 : /* {{{ proto bool chroot(string directory)
249 : Change root directory */
250 : PHP_FUNCTION(chroot)
251 0 : {
252 : char *str;
253 : int ret, str_len;
254 :
255 0 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
256 0 : RETURN_FALSE;
257 : }
258 :
259 0 : ret = chroot(str);
260 0 : if (ret != 0) {
261 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
262 0 : RETURN_FALSE;
263 : }
264 :
265 0 : realpath_cache_clean(TSRMLS_C);
266 :
267 0 : ret = chdir("/");
268 :
269 0 : if (ret != 0) {
270 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
271 0 : RETURN_FALSE;
272 : }
273 :
274 0 : RETURN_TRUE;
275 : }
276 : /* }}} */
277 : #endif
278 :
279 : /* {{{ proto bool chdir(string directory)
280 : Change the current directory */
281 : PHP_FUNCTION(chdir)
282 2 : {
283 : char *str;
284 : int ret, str_len;
285 :
286 2 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
287 0 : RETURN_FALSE;
288 : }
289 :
290 2 : if ((PG(safe_mode) && !php_checkuid(str, NULL, CHECKUID_CHECK_FILE_AND_DIR)) || php_check_open_basedir(str TSRMLS_CC)) {
291 0 : RETURN_FALSE;
292 : }
293 2 : ret = VCWD_CHDIR(str);
294 :
295 2 : if (ret != 0) {
296 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
297 0 : RETURN_FALSE;
298 : }
299 :
300 2 : RETURN_TRUE;
301 : }
302 : /* }}} */
303 :
304 : /* {{{ proto mixed getcwd(void)
305 : Gets the current directory */
306 : PHP_FUNCTION(getcwd)
307 2 : {
308 : char path[MAXPATHLEN];
309 2 : char *ret=NULL;
310 :
311 2 : if (ZEND_NUM_ARGS() != 0) {
312 0 : WRONG_PARAM_COUNT;
313 : }
314 :
315 : #if HAVE_GETCWD
316 2 : ret = VCWD_GETCWD(path, MAXPATHLEN);
317 : #elif HAVE_GETWD
318 : ret = VCWD_GETWD(path);
319 : #endif
320 :
321 2 : if (ret) {
322 2 : RETURN_STRING(path, 1);
323 : } else {
324 0 : RETURN_FALSE;
325 : }
326 : }
327 : /* }}} */
328 :
329 : /* {{{ proto void rewinddir([resource dir_handle])
330 : Rewind dir_handle back to the start */
331 : PHP_FUNCTION(rewinddir)
332 0 : {
333 : zval **id, **tmp, *myself;
334 : php_stream *dirp;
335 :
336 0 : FETCH_DIRP();
337 :
338 0 : php_stream_rewinddir(dirp);
339 : }
340 : /* }}} */
341 :
342 : /* {{{ proto string readdir([resource dir_handle])
343 : Read directory entry from dir_handle */
344 : PHP_NAMED_FUNCTION(php_if_readdir)
345 730 : {
346 : zval **id, **tmp, *myself;
347 : php_stream *dirp;
348 : php_stream_dirent entry;
349 :
350 730 : FETCH_DIRP();
351 :
352 730 : if (php_stream_readdir(dirp, &entry)) {
353 716 : RETURN_STRINGL(entry.d_name, strlen(entry.d_name), 1);
354 : }
355 14 : RETURN_FALSE;
356 : }
357 : /* }}} */
358 :
359 : #ifdef HAVE_GLOB
360 : /* {{{ proto array glob(string pattern [, int flags])
361 : Find pathnames matching a pattern */
362 : PHP_FUNCTION(glob)
363 1 : {
364 : char cwd[MAXPATHLEN];
365 1 : int cwd_skip = 0;
366 : #ifdef ZTS
367 : char work_pattern[MAXPATHLEN];
368 : char *result;
369 : #endif
370 1 : char *pattern = NULL;
371 : int pattern_len;
372 1 : long flags = 0;
373 : glob_t globbuf;
374 : int n;
375 : int ret;
376 :
377 1 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &pattern, &pattern_len, &flags) == FAILURE)
378 0 : return;
379 :
380 : #ifdef ZTS
381 : if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
382 : result = VCWD_GETCWD(cwd, MAXPATHLEN);
383 : if (!result) {
384 : cwd[0] = '\0';
385 : }
386 : #ifdef PHP_WIN32
387 : if (IS_SLASH(*pattern)) {
388 : cwd[2] = '\0';
389 : }
390 : #endif
391 : cwd_skip = strlen(cwd)+1;
392 :
393 : snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
394 : pattern = work_pattern;
395 : }
396 : #endif
397 :
398 1 : globbuf.gl_offs = 0;
399 1 : if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
400 : #ifdef GLOB_NOMATCH
401 0 : if (GLOB_NOMATCH == ret) {
402 : /* Some glob implementation simply return no data if no matches
403 : were found, others return the GLOB_NOMATCH error code.
404 : We don't want to treat GLOB_NOMATCH as an error condition
405 : so that PHP glob() behaves the same on both types of
406 : implementations and so that 'foreach (glob() as ...'
407 : can be used for simple glob() calls without further error
408 : checking.
409 : */
410 0 : array_init(return_value);
411 0 : return;
412 : }
413 : #endif
414 0 : RETURN_FALSE;
415 : }
416 :
417 : /* now catch the FreeBSD style of "no matches" */
418 1 : if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
419 0 : array_init(return_value);
420 0 : return;
421 : }
422 :
423 : /* we assume that any glob pattern will match files from one directory only
424 : so checking the dirname of the first match should be sufficient */
425 1 : strlcpy(cwd, globbuf.gl_pathv[0], MAXPATHLEN);
426 1 : if (PG(safe_mode) && (!php_checkuid(cwd, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
427 0 : RETURN_FALSE;
428 : }
429 1 : if (php_check_open_basedir(cwd TSRMLS_CC)) {
430 0 : RETURN_FALSE;
431 : }
432 :
433 1 : array_init(return_value);
434 111 : for (n = 0; n < globbuf.gl_pathc; n++) {
435 : /* we need to do this everytime since GLOB_ONLYDIR does not guarantee that
436 : * all directories will be filtered. GNU libc documentation states the
437 : * following:
438 : * If the information about the type of the file is easily available
439 : * non-directories will be rejected but no extra work will be done to
440 : * determine the information for each file. I.e., the caller must still be
441 : * able to filter directories out.
442 : */
443 110 : if (flags & GLOB_ONLYDIR) {
444 : struct stat s;
445 :
446 0 : if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
447 0 : continue;
448 : }
449 :
450 0 : if (S_IFDIR != (s.st_mode & S_IFMT)) {
451 0 : continue;
452 : }
453 : }
454 110 : add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip, 1);
455 : }
456 :
457 1 : globfree(&globbuf);
458 : }
459 : /* }}} */
460 : #endif
461 :
462 : /* {{{ proto array scandir(string dir [, int sorting_order [, resource context]])
463 : List files & directories inside the specified path */
464 : PHP_FUNCTION(scandir)
465 0 : {
466 : char *dirn;
467 : int dirn_len;
468 0 : long flags = 0;
469 : char **namelist;
470 : int n, i;
471 0 : zval *zcontext = NULL;
472 0 : php_stream_context *context = NULL;
473 :
474 0 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &dirn, &dirn_len, &flags, &zcontext) == FAILURE) {
475 0 : return;
476 : }
477 :
478 0 : if (zcontext) {
479 0 : context = php_stream_context_from_zval(zcontext, 0);
480 : }
481 :
482 0 : if (!flags) {
483 0 : n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
484 : } else {
485 0 : n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
486 : }
487 0 : if (n < 0) {
488 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "(errno %d): %s", errno, strerror(errno));
489 0 : RETURN_FALSE;
490 : }
491 :
492 0 : array_init(return_value);
493 :
494 0 : for (i = 0; i < n; i++) {
495 0 : add_next_index_string(return_value, namelist[i], 0);
496 : }
497 :
498 0 : if (n) {
499 0 : efree(namelist);
500 : }
501 : }
502 : /* }}} */
503 :
504 : /*
505 : * Local variables:
506 : * tab-width: 4
507 : * c-basic-offset: 4
508 : * End:
509 : * vim600: sw=4 ts=4 fdm=marker
510 : * vim<600: sw=4 ts=4
511 : */
|