zip_stream.c 8.3 KB

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