zip_stream.c 7.8 KB

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