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