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