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