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