xref: /PHP-5.4/ext/zip/zip_stream.c (revision 5ca051bf)
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