xref: /PHP-7.4/ext/zip/zip_stream.c (revision d59aac58)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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: Piere-Alain Joye <pierre@php.net>                            |
16   +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #   include "config.h"
21 #endif
22 #include "php.h"
23 #if HAVE_ZIP
24 
25 #include "php_streams.h"
26 #include "ext/standard/file.h"
27 #include "ext/standard/php_string.h"
28 #include "fopen_wrappers.h"
29 #include "php_zip.h"
30 
31 #include "ext/standard/url.h"
32 
33 /* needed for ssize_t definition */
34 #include <sys/types.h>
35 
36 struct php_zip_stream_data_t {
37 	struct zip *za;
38 	struct zip_file *zf;
39 	size_t cursor;
40 	php_stream *stream;
41 };
42 
43 #define STREAM_DATA_FROM_STREAM() \
44 	struct php_zip_stream_data_t *self = (struct php_zip_stream_data_t *) stream->abstract;
45 
46 
47 /* {{{ php_zip_ops_read */
php_zip_ops_read(php_stream * stream,char * buf,size_t count)48 static ssize_t php_zip_ops_read(php_stream *stream, char *buf, size_t count)
49 {
50 	ssize_t n = 0;
51 	STREAM_DATA_FROM_STREAM();
52 
53 	if (self->za && self->zf) {
54 		n = zip_fread(self->zf, buf, count);
55 		if (n < 0) {
56 #if LIBZIP_VERSION_MAJOR < 1
57 			int ze, se;
58 			zip_file_error_get(self->zf, &ze, &se);
59 			stream->eof = 1;
60 			php_error_docref(NULL, E_WARNING, "Zip stream error: %s", zip_file_strerror(self->zf));
61 #else
62 			zip_error_t *err;
63 			err = zip_file_get_error(self->zf);
64 			stream->eof = 1;
65 			php_error_docref(NULL, E_WARNING, "Zip stream error: %s", zip_error_strerror(err));
66 			zip_error_fini(err);
67 #endif
68 			return -1;
69 		}
70 		/* cast count to signed value to avoid possibly negative n
71 		 * being cast to unsigned value */
72 		if (n == 0 || n < (ssize_t)count) {
73 			stream->eof = 1;
74 		} else {
75 			self->cursor += n;
76 		}
77 	}
78 	return n;
79 }
80 /* }}} */
81 
82 /* {{{ php_zip_ops_write */
php_zip_ops_write(php_stream * stream,const char * buf,size_t count)83 static ssize_t php_zip_ops_write(php_stream *stream, const char *buf, size_t count)
84 {
85 	if (!stream) {
86 		return -1;
87 	}
88 
89 	return count;
90 }
91 /* }}} */
92 
93 /* {{{ php_zip_ops_close */
php_zip_ops_close(php_stream * stream,int close_handle)94 static int php_zip_ops_close(php_stream *stream, int close_handle)
95 {
96 	STREAM_DATA_FROM_STREAM();
97 	if (close_handle) {
98 		if (self->zf) {
99 			zip_fclose(self->zf);
100 			self->zf = NULL;
101 		}
102 
103 		if (self->za) {
104 			zip_close(self->za);
105 			self->za = NULL;
106 		}
107 	}
108 	efree(self);
109 	stream->abstract = NULL;
110 	return EOF;
111 }
112 /* }}} */
113 
114 /* {{{ php_zip_ops_flush */
php_zip_ops_flush(php_stream * stream)115 static int php_zip_ops_flush(php_stream *stream)
116 {
117 	if (!stream) {
118 		return 0;
119 	}
120 
121 	return 0;
122 }
123 /* }}} */
124 
php_zip_ops_stat(php_stream * stream,php_stream_statbuf * ssb)125 static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
126 {
127 	struct zip_stat sb;
128 	const char *path = stream->orig_path;
129 	size_t path_len = strlen(stream->orig_path);
130 	char file_dirname[MAXPATHLEN];
131 	struct zip *za;
132 	char *fragment;
133 	size_t fragment_len;
134 	int err;
135 	zend_string *file_basename;
136 
137 	fragment = strchr(path, '#');
138 	if (!fragment) {
139 		return -1;
140 	}
141 
142 
143 	if (strncasecmp("zip://", path, 6) == 0) {
144 		path += 6;
145 	}
146 
147 	fragment_len = strlen(fragment);
148 
149 	if (fragment_len < 1) {
150 		return -1;
151 	}
152 	path_len = strlen(path);
153 	if (path_len >= MAXPATHLEN) {
154 		return -1;
155 	}
156 
157 	memcpy(file_dirname, path, path_len - fragment_len);
158 	file_dirname[path_len - fragment_len] = '\0';
159 
160 	file_basename = php_basename((char *)path, path_len - fragment_len, NULL, 0);
161 	fragment++;
162 
163 	if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) {
164 		zend_string_release_ex(file_basename, 0);
165 		return -1;
166 	}
167 
168 	za = zip_open(file_dirname, ZIP_CREATE, &err);
169 	if (za) {
170 		memset(ssb, 0, sizeof(php_stream_statbuf));
171 		if (zip_stat(za, fragment, ZIP_FL_NOCASE, &sb) != 0) {
172 			zip_close(za);
173 			zend_string_release_ex(file_basename, 0);
174 			return -1;
175 		}
176 		zip_close(za);
177 
178 		if (path[path_len-1] != '/') {
179 			ssb->sb.st_size = sb.size;
180 			ssb->sb.st_mode |= S_IFREG; /* regular file */
181 		} else {
182 			ssb->sb.st_size = 0;
183 			ssb->sb.st_mode |= S_IFDIR; /* regular directory */
184 		}
185 
186 		ssb->sb.st_mtime = sb.mtime;
187 		ssb->sb.st_atime = sb.mtime;
188 		ssb->sb.st_ctime = sb.mtime;
189 		ssb->sb.st_nlink = 1;
190 		ssb->sb.st_rdev = -1;
191 #ifndef PHP_WIN32
192 		ssb->sb.st_blksize = -1;
193 		ssb->sb.st_blocks = -1;
194 #endif
195 		ssb->sb.st_ino = -1;
196 	}
197 	zend_string_release_ex(file_basename, 0);
198 	return 0;
199 }
200 /* }}} */
201 
202 const php_stream_ops php_stream_zipio_ops = {
203 	php_zip_ops_write, php_zip_ops_read,
204 	php_zip_ops_close, php_zip_ops_flush,
205 	"zip",
206 	NULL, /* seek */
207 	NULL, /* cast */
208 	php_zip_ops_stat, /* stat */
209 	NULL  /* set_option */
210 };
211 
212 /* {{{ php_stream_zip_open */
php_stream_zip_open(const char * filename,const char * path,const char * mode STREAMS_DC)213 php_stream *php_stream_zip_open(const char *filename, const char *path, const char *mode STREAMS_DC)
214 {
215 	struct zip_file *zf = NULL;
216 	int err = 0;
217 
218 	php_stream *stream = NULL;
219 	struct php_zip_stream_data_t *self;
220 	struct zip *stream_za;
221 
222 	if (strncmp(mode,"r", strlen("r")) != 0) {
223 		return NULL;
224 	}
225 
226 	if (filename) {
227 		if (ZIP_OPENBASEDIR_CHECKPATH(filename)) {
228 			return NULL;
229 		}
230 
231 		/* duplicate to make the stream za independent (esp. for MSHUTDOWN) */
232 		stream_za = zip_open(filename, ZIP_CREATE, &err);
233 		if (!stream_za) {
234 			return NULL;
235 		}
236 
237 		zf = zip_fopen(stream_za, path, 0);
238 		if (zf) {
239 			self = emalloc(sizeof(*self));
240 
241 			self->za = stream_za;
242 			self->zf = zf;
243 			self->stream = NULL;
244 			self->cursor = 0;
245 			stream = php_stream_alloc(&php_stream_zipio_ops, self, NULL, mode);
246 			stream->orig_path = estrdup(path);
247 		} else {
248 			zip_close(stream_za);
249 		}
250 	}
251 
252 	if (!stream) {
253 		return NULL;
254 	} else {
255 		return stream;
256 	}
257 
258 }
259 /* }}} */
260 
261 /* {{{ php_stream_zip_opener */
php_stream_zip_opener(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)262 php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper,
263 											const char *path,
264 											const char *mode,
265 											int options,
266 											zend_string **opened_path,
267 											php_stream_context *context STREAMS_DC)
268 {
269 	size_t path_len;
270 
271 	zend_string *file_basename;
272 	char file_dirname[MAXPATHLEN];
273 
274 	struct zip *za;
275 	struct zip_file *zf = NULL;
276 	char *fragment;
277 	size_t fragment_len;
278 	int err;
279 
280 	php_stream *stream = NULL;
281 	struct php_zip_stream_data_t *self;
282 
283 	fragment = strchr(path, '#');
284 	if (!fragment) {
285 		return NULL;
286 	}
287 
288 	if (strncasecmp("zip://", path, 6) == 0) {
289 		path += 6;
290 	}
291 
292 	fragment_len = strlen(fragment);
293 
294 	if (fragment_len < 1) {
295 		return NULL;
296 	}
297 	path_len = strlen(path);
298 	if (path_len >= MAXPATHLEN || mode[0] != 'r') {
299 		return NULL;
300 	}
301 
302 	memcpy(file_dirname, path, path_len - fragment_len);
303 	file_dirname[path_len - fragment_len] = '\0';
304 
305 	file_basename = php_basename(path, path_len - fragment_len, NULL, 0);
306 	fragment++;
307 
308 	if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) {
309 		zend_string_release_ex(file_basename, 0);
310 		return NULL;
311 	}
312 
313 	za = zip_open(file_dirname, ZIP_CREATE, &err);
314 	if (za) {
315 		zval *tmpzval;
316 
317 		if (context && NULL != (tmpzval = php_stream_context_get_option(context, "zip", "password"))) {
318 			if (Z_TYPE_P(tmpzval) != IS_STRING || zip_set_default_password(za, Z_STRVAL_P(tmpzval))) {
319 				php_error_docref(NULL, E_WARNING, "Can't set zip password");
320 			}
321 		}
322 
323 		zf = zip_fopen(za, fragment, 0);
324 		if (zf) {
325 			self = emalloc(sizeof(*self));
326 
327 			self->za = za;
328 			self->zf = zf;
329 			self->stream = NULL;
330 			self->cursor = 0;
331 			stream = php_stream_alloc(&php_stream_zipio_ops, self, NULL, mode);
332 
333 			if (opened_path) {
334 				*opened_path = zend_string_init(path, strlen(path), 0);
335 			}
336 		} else {
337 			zip_close(za);
338 		}
339 	}
340 
341 	zend_string_release_ex(file_basename, 0);
342 
343 	if (!stream) {
344 		return NULL;
345 	} else {
346 		return stream;
347 	}
348 }
349 /* }}} */
350 
351 static const php_stream_wrapper_ops zip_stream_wops = {
352 	php_stream_zip_opener,
353 	NULL,	/* close */
354 	NULL,	/* fstat */
355 	NULL,	/* stat */
356 	NULL,	/* opendir */
357 	"zip wrapper",
358 	NULL,	/* unlink */
359 	NULL,	/* rename */
360 	NULL,	/* mkdir */
361 	NULL,	/* rmdir */
362 	NULL	/* metadata */
363 };
364 
365 const php_stream_wrapper php_stream_zip_wrapper = {
366 	&zip_stream_wops,
367 	NULL,
368 	0 /* is_url */
369 };
370 #endif /* HAVE_ZIP */
371