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 /* 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 size_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 0;
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 < 1 ? 0 : (size_t)n);
79 }
80 /* }}} */
81
82 /* {{{ php_zip_ops_write */
php_zip_ops_write(php_stream * stream,const char * buf,size_t count)83 static size_t php_zip_ops_write(php_stream *stream, const char *buf, size_t count)
84 {
85 if (!stream) {
86 return 0;
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(file_basename);
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(file_basename);
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(file_basename);
198 return 0;
199 }
200 /* }}} */
201
202 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(file_basename);
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(file_basename);
342
343 if (!stream) {
344 return NULL;
345 } else {
346 return stream;
347 }
348 }
349 /* }}} */
350
351 static 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 php_stream_wrapper php_stream_zip_wrapper = {
366 &zip_stream_wops,
367 NULL,
368 0 /* is_url */
369 };
370 #endif /* HAVE_ZIP */
371