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