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: Marcus Boerger <helly@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "php_streams_int.h"
19
20 #ifdef HAVE_GLOB
21 # ifndef PHP_WIN32
22 # include <glob.h>
23 # else
24 # include "win32/glob.h"
25 # endif
26 #endif
27
28 #ifdef HAVE_GLOB
29 #ifndef GLOB_ONLYDIR
30 #define GLOB_ONLYDIR (1<<30)
31 #define GLOB_FLAGMASK (~GLOB_ONLYDIR)
32 #else
33 #define GLOB_FLAGMASK (~0)
34 #endif
35
36 typedef struct {
37 glob_t glob;
38 size_t index;
39 int flags;
40 char *path;
41 size_t path_len;
42 char *pattern;
43 size_t pattern_len;
44 size_t *open_basedir_indexmap;
45 size_t open_basedir_indexmap_size;
46 bool open_basedir_used;
47 } glob_s_t;
48
_php_glob_stream_get_path(php_stream * stream,size_t * plen STREAMS_DC)49 PHPAPI char* _php_glob_stream_get_path(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
50 {
51 glob_s_t *pglob = (glob_s_t *)stream->abstract;
52
53 if (pglob && pglob->path) {
54 if (plen) {
55 *plen = pglob->path_len;
56 }
57 return pglob->path;
58 } else {
59 if (plen) {
60 *plen = 0;
61 }
62 return NULL;
63 }
64 }
65 /* }}} */
66
_php_glob_stream_get_pattern(php_stream * stream,size_t * plen STREAMS_DC)67 PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
68 {
69 glob_s_t *pglob = (glob_s_t *)stream->abstract;
70
71 if (pglob && pglob->pattern) {
72 if (plen) {
73 *plen = pglob->pattern_len;
74 }
75 return pglob->pattern;
76 } else {
77 if (plen) {
78 *plen = 0;
79 }
80 return NULL;
81 }
82 }
83 /* }}} */
84
php_glob_stream_get_result_count(glob_s_t * pglob)85 static inline int php_glob_stream_get_result_count(glob_s_t *pglob)
86 {
87 return pglob->open_basedir_used ? (int) pglob->open_basedir_indexmap_size : pglob->glob.gl_pathc;
88 }
89
_php_glob_stream_get_count(php_stream * stream,int * pflags STREAMS_DC)90 PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC) /* {{{ */
91 {
92 glob_s_t *pglob = (glob_s_t *)stream->abstract;
93
94 if (pglob) {
95 if (pflags) {
96 *pflags = pglob->flags;
97 }
98 return php_glob_stream_get_result_count(pglob);
99 } else {
100 if (pflags) {
101 *pflags = 0;
102 }
103 return 0;
104 }
105 }
106 /* }}} */
107
php_glob_stream_path_split(glob_s_t * pglob,const char * path,int get_path,const char ** p_file)108 static void php_glob_stream_path_split(glob_s_t *pglob, const char *path, int get_path, const char **p_file) /* {{{ */
109 {
110 const char *pos, *gpath = path;
111
112 if ((pos = strrchr(path, '/')) != NULL) {
113 path = pos+1;
114 }
115 #ifdef PHP_WIN32
116 if ((pos = strrchr(path, '\\')) != NULL) {
117 path = pos+1;
118 }
119 #endif
120
121 *p_file = path;
122
123 if (get_path) {
124 if (pglob->path) {
125 efree(pglob->path);
126 }
127 if ((path - gpath) > 1) {
128 path--;
129 }
130 pglob->path_len = path - gpath;
131 pglob->path = estrndup(gpath, pglob->path_len);
132 }
133 }
134 /* }}} */
135
php_glob_stream_read(php_stream * stream,char * buf,size_t count)136 static ssize_t php_glob_stream_read(php_stream *stream, char *buf, size_t count) /* {{{ */
137 {
138 glob_s_t *pglob = (glob_s_t *)stream->abstract;
139 php_stream_dirent *ent = (php_stream_dirent*)buf;
140 const char *path;
141 int glob_result_count;
142 size_t index;
143
144 /* avoid problems if someone mis-uses the stream */
145 if (count == sizeof(php_stream_dirent) && pglob) {
146 glob_result_count = php_glob_stream_get_result_count(pglob);
147 if (pglob->index < (size_t) glob_result_count) {
148 index = pglob->open_basedir_used && pglob->open_basedir_indexmap ?
149 pglob->open_basedir_indexmap[pglob->index] : pglob->index;
150 php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[index], pglob->flags & GLOB_APPEND, &path);
151 ++pglob->index;
152 PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path));
153 ent->d_type = DT_UNKNOWN;
154 return sizeof(php_stream_dirent);
155 }
156 pglob->index = glob_result_count;
157 if (pglob->path) {
158 efree(pglob->path);
159 pglob->path = NULL;
160 }
161 }
162
163 return -1;
164 }
165 /* }}} */
166
php_glob_stream_close(php_stream * stream,int close_handle)167 static int php_glob_stream_close(php_stream *stream, int close_handle) /* {{{ */
168 {
169 glob_s_t *pglob = (glob_s_t *)stream->abstract;
170
171 if (pglob) {
172 pglob->index = 0;
173 globfree(&pglob->glob);
174 if (pglob->path) {
175 efree(pglob->path);
176 }
177 if (pglob->pattern) {
178 efree(pglob->pattern);
179 }
180 if (pglob->open_basedir_indexmap) {
181 efree(pglob->open_basedir_indexmap);
182 }
183 }
184 efree(stream->abstract);
185 return 0;
186 }
187 /* {{{ */
188
php_glob_stream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)189 static int php_glob_stream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) /* {{{ */
190 {
191 glob_s_t *pglob = (glob_s_t *)stream->abstract;
192
193 if (pglob) {
194 pglob->index = 0;
195 if (pglob->path) {
196 efree(pglob->path);
197 pglob->path = NULL;
198 }
199 }
200 return 0;
201 }
202 /* }}} */
203
204 const php_stream_ops php_glob_stream_ops = {
205 NULL, php_glob_stream_read,
206 php_glob_stream_close, NULL,
207 "glob",
208 php_glob_stream_rewind,
209 NULL, /* cast */
210 NULL, /* stat */
211 NULL /* set_option */
212 };
213
214 /* {{{ php_glob_stream_opener */
php_glob_stream_opener(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)215 static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
216 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
217 {
218 glob_s_t *pglob;
219 int ret, i;
220 const char *tmp, *pos;
221
222 if (!strncmp(path, "glob://", sizeof("glob://")-1)) {
223 path += sizeof("glob://")-1;
224 if (opened_path) {
225 *opened_path = zend_string_init(path, strlen(path), 0);
226 }
227 }
228
229 pglob = ecalloc(1, sizeof(*pglob));
230
231 if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) {
232 #ifdef GLOB_NOMATCH
233 if (GLOB_NOMATCH != ret)
234 #endif
235 {
236 efree(pglob);
237 return NULL;
238 }
239 }
240
241 /* if open_basedir in use, check and filter restricted paths */
242 if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) {
243 pglob->open_basedir_used = true;
244 for (i = 0; i < pglob->glob.gl_pathc; i++) {
245 if (!php_check_open_basedir_ex(pglob->glob.gl_pathv[i], 0)) {
246 if (!pglob->open_basedir_indexmap) {
247 pglob->open_basedir_indexmap = (size_t *) safe_emalloc(
248 pglob->glob.gl_pathc, sizeof(size_t), 0);
249 }
250 pglob->open_basedir_indexmap[pglob->open_basedir_indexmap_size++] = i;
251 }
252 }
253 }
254
255 pos = path;
256 if ((tmp = strrchr(pos, '/')) != NULL) {
257 pos = tmp+1;
258 }
259 #ifdef PHP_WIN32
260 if ((tmp = strrchr(pos, '\\')) != NULL) {
261 pos = tmp+1;
262 }
263 #endif
264
265 pglob->pattern_len = strlen(pos);
266 pglob->pattern = estrndup(pos, pglob->pattern_len);
267
268 pglob->flags |= GLOB_APPEND;
269
270 if (pglob->glob.gl_pathc) {
271 php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp);
272 } else {
273 php_glob_stream_path_split(pglob, path, 1, &tmp);
274 }
275
276 return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode);
277 }
278 /* }}} */
279
280 static const php_stream_wrapper_ops php_glob_stream_wrapper_ops = {
281 NULL,
282 NULL,
283 NULL,
284 NULL,
285 php_glob_stream_opener,
286 "glob",
287 NULL,
288 NULL,
289 NULL,
290 NULL,
291 NULL
292 };
293
294 const php_stream_wrapper php_glob_stream_wrapper = {
295 &php_glob_stream_wrapper_ops,
296 NULL,
297 0
298 };
299 #endif /* HAVE_GLOB */
300