zlib_help.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include <zip.h>
  9. #include <unzip.h>
  10. #include "logs.h"
  11. #ifdef small
  12. #undef small
  13. #endif // small
  14. namespace zlib {
  15. namespace internal {
  16. class InMemoryFile {
  17. public:
  18. InMemoryFile(const QByteArray &data = QByteArray()) : _data(data) {
  19. }
  20. zlib_filefunc_def funcs() {
  21. zlib_filefunc_def result;
  22. result.opaque = this;
  23. result.zopen_file = &InMemoryFile::Open;
  24. result.zerror_file = &InMemoryFile::Error;
  25. result.zread_file = &InMemoryFile::Read;
  26. result.zwrite_file = &InMemoryFile::Write;
  27. result.zclose_file = &InMemoryFile::Close;
  28. result.zseek_file = &InMemoryFile::Seek;
  29. result.ztell_file = &InMemoryFile::Tell;
  30. return result;
  31. }
  32. int error() const {
  33. return _error;
  34. }
  35. QByteArray result() const {
  36. return _data;
  37. }
  38. private:
  39. voidpf open(const char *filename, int mode) {
  40. if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
  41. if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
  42. _data.clear();
  43. }
  44. _position = _data.size();
  45. _data.reserve(2 * 1024 * 1024);
  46. } else if (mode & ZLIB_FILEFUNC_MODE_READ) {
  47. _position = 0;
  48. }
  49. _error = 0;
  50. return this;
  51. }
  52. uLong read(voidpf stream, void* buf, uLong size) {
  53. uLong toRead = 0;
  54. if (!_error) {
  55. if (_data.size() > int(_position)) {
  56. toRead = qMin(size, uLong(_data.size() - _position));
  57. memcpy(buf, _data.constData() + _position, toRead);
  58. _position += toRead;
  59. }
  60. if (toRead < size) {
  61. _error = -1;
  62. }
  63. }
  64. return toRead;
  65. }
  66. uLong write(voidpf stream, const void* buf, uLong size) {
  67. if (_data.size() < int(_position + size)) {
  68. _data.resize(_position + size);
  69. }
  70. memcpy(_data.data() + _position, buf, size);
  71. _position += size;
  72. return size;
  73. }
  74. int close(voidpf stream) {
  75. auto result = _error;
  76. _position = 0;
  77. _error = 0;
  78. return result;
  79. }
  80. int error(voidpf stream) const {
  81. return _error;
  82. }
  83. long tell(voidpf stream) const {
  84. return _position;
  85. }
  86. long seek(voidpf stream, uLong offset, int origin) {
  87. if (!_error) {
  88. switch (origin) {
  89. case ZLIB_FILEFUNC_SEEK_SET: _position = offset; break;
  90. case ZLIB_FILEFUNC_SEEK_CUR: _position += offset; break;
  91. case ZLIB_FILEFUNC_SEEK_END: _position = _data.size() + offset; break;
  92. }
  93. if (int(_position) > _data.size()) {
  94. _error = -1;
  95. }
  96. }
  97. return _error;
  98. }
  99. static voidpf Open(voidpf opaque, const char* filename, int mode) {
  100. return static_cast<InMemoryFile*>(opaque)->open(filename, mode);
  101. }
  102. static uLong Read(voidpf opaque, voidpf stream, void* buf, uLong size) {
  103. return static_cast<InMemoryFile*>(opaque)->read(stream, buf, size);
  104. }
  105. static uLong Write(voidpf opaque, voidpf stream, const void* buf, uLong size) {
  106. return static_cast<InMemoryFile*>(opaque)->write(stream, buf, size);
  107. }
  108. static int Close(voidpf opaque, voidpf stream) {
  109. return static_cast<InMemoryFile*>(opaque)->close(stream);
  110. }
  111. static int Error(voidpf opaque, voidpf stream) {
  112. return static_cast<InMemoryFile*>(opaque)->error(stream);
  113. }
  114. static long Tell(voidpf opaque, voidpf stream) {
  115. return static_cast<InMemoryFile*>(opaque)->tell(stream);
  116. }
  117. static long Seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
  118. return static_cast<InMemoryFile*>(opaque)->seek(stream, offset, origin);
  119. }
  120. uLong _position = 0;
  121. int _error = 0;
  122. QByteArray _data;
  123. };
  124. } // namespace internal
  125. constexpr int kCaseSensitive = 1;
  126. constexpr int kCaseInsensitive = 2;
  127. class FileToRead {
  128. public:
  129. FileToRead(const QByteArray &content) : _data(content) {
  130. auto funcs = _data.funcs();
  131. if (!(_handle = unzOpen2(nullptr, &funcs))) {
  132. _error = -1;
  133. }
  134. }
  135. int getGlobalInfo(unz_global_info *pglobal_info) {
  136. if (error() == UNZ_OK) {
  137. _error = _handle ? unzGetGlobalInfo(_handle, pglobal_info) : -1;
  138. }
  139. return _error;
  140. }
  141. int locateFile(const char *szFileName, int iCaseSensitivity) {
  142. if (error() == UNZ_OK) {
  143. _error = _handle ? unzLocateFile(_handle, szFileName, iCaseSensitivity) : -1;
  144. }
  145. return error();
  146. }
  147. int goToFirstFile() {
  148. if (error() == UNZ_OK) {
  149. _error = _handle ? unzGoToFirstFile(_handle) : -1;
  150. }
  151. return error();
  152. }
  153. int goToNextFile() {
  154. if (error() == UNZ_OK) {
  155. _error = _handle ? unzGoToNextFile(_handle) : -1;
  156. }
  157. return error();
  158. }
  159. int getCurrentFileInfo(
  160. unz_file_info *pfile_info,
  161. char *szFileName,
  162. uLong fileNameBufferSize,
  163. void *extraField,
  164. uLong extraFieldBufferSize,
  165. char *szComment,
  166. uLong commentBufferSize) {
  167. if (error() == UNZ_OK) {
  168. _error = _handle ? unzGetCurrentFileInfo(
  169. _handle,
  170. pfile_info,
  171. szFileName,
  172. fileNameBufferSize,
  173. extraField,
  174. extraFieldBufferSize,
  175. szComment,
  176. commentBufferSize
  177. ) : -1;
  178. }
  179. return error();
  180. }
  181. QString getCurrentFileName() {
  182. unz_file_info info = { 0 };
  183. constexpr auto kMaxName = 128;
  184. char name[kMaxName + 1] = { 0 };
  185. const auto result = getCurrentFileInfo(
  186. &info,
  187. name,
  188. kMaxName,
  189. nullptr,
  190. 0,
  191. nullptr,
  192. 0);
  193. return (result == UNZ_OK) ? QString::fromUtf8(name) : QString();
  194. }
  195. int openCurrentFile() {
  196. if (error() == UNZ_OK) {
  197. _error = _handle ? unzOpenCurrentFile(_handle) : -1;
  198. }
  199. return error();
  200. }
  201. int readCurrentFile(voidp buf, unsigned len) {
  202. if (error() == UNZ_OK) {
  203. auto result = _handle ? unzReadCurrentFile(_handle, buf, len) : -1;
  204. if (result >= 0) {
  205. return result;
  206. } else {
  207. _error = result;
  208. }
  209. }
  210. return error();
  211. }
  212. int closeCurrentFile() {
  213. if (error() == UNZ_OK) {
  214. _error = _handle ? unzCloseCurrentFile(_handle) : -1;
  215. }
  216. return error();
  217. }
  218. QByteArray readCurrentFileContent(int fileSizeLimit) {
  219. unz_file_info fileInfo = { 0 };
  220. if (getCurrentFileInfo(&fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK) {
  221. LOG(("Error: could not get current file info in a zip file."));
  222. return QByteArray();
  223. }
  224. auto size = fileInfo.uncompressed_size;
  225. if (size > static_cast<uint32>(fileSizeLimit)) {
  226. if (_error == UNZ_OK) _error = -1;
  227. LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size));
  228. return QByteArray();
  229. }
  230. if (openCurrentFile() != UNZ_OK) {
  231. LOG(("Error: could not open current file in a zip file."));
  232. return QByteArray();
  233. }
  234. QByteArray result;
  235. result.resize(size);
  236. auto couldRead = readCurrentFile(result.data(), size);
  237. if (couldRead != static_cast<int>(size)) {
  238. LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead));
  239. return QByteArray();
  240. }
  241. if (closeCurrentFile() != UNZ_OK) {
  242. LOG(("Error: could not close current file in a zip file."));
  243. return QByteArray();
  244. }
  245. return result;
  246. }
  247. QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) {
  248. if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) {
  249. LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName));
  250. return QByteArray();
  251. }
  252. return readCurrentFileContent(fileSizeLimit);
  253. }
  254. void close() {
  255. if (_handle && unzClose(_handle) != UNZ_OK && _error == UNZ_OK) {
  256. _error = -1;
  257. }
  258. _handle = nullptr;
  259. }
  260. int error() const {
  261. if (auto dataError = _data.error()) {
  262. return dataError;
  263. }
  264. return _error;
  265. }
  266. void clearError() {
  267. _error = UNZ_OK;
  268. }
  269. ~FileToRead() {
  270. close();
  271. }
  272. private:
  273. internal::InMemoryFile _data;
  274. unzFile _handle = nullptr;
  275. int _error = 0;
  276. };
  277. class FileToWrite {
  278. public:
  279. FileToWrite() {
  280. auto funcs = _data.funcs();
  281. if (!(_handle = zipOpen2(nullptr, APPEND_STATUS_CREATE, nullptr, &funcs))) {
  282. _error = -1;
  283. }
  284. }
  285. int openNewFile(
  286. const char *filename,
  287. const zip_fileinfo *zipfi,
  288. const void *extrafield_local,
  289. uInt size_extrafield_local,
  290. const void* extrafield_global,
  291. uInt size_extrafield_global,
  292. const char* comment,
  293. int method,
  294. int level
  295. ) {
  296. if (error() == ZIP_OK) {
  297. _error = _handle ? zipOpenNewFileInZip(
  298. _handle,
  299. filename,
  300. zipfi,
  301. extrafield_local,
  302. size_extrafield_local,
  303. extrafield_global,
  304. size_extrafield_global,
  305. comment,
  306. method,
  307. level
  308. ) : -1;
  309. }
  310. return error();
  311. }
  312. int writeInFile(const void* buf, unsigned len) {
  313. if (error() == ZIP_OK) {
  314. _error = _handle ? zipWriteInFileInZip(_handle, buf, len) : -1;
  315. }
  316. return error();
  317. }
  318. int closeFile() {
  319. if (error() == ZIP_OK) {
  320. _error = _handle ? zipCloseFileInZip(_handle) : -1;
  321. }
  322. return error();
  323. }
  324. void close() {
  325. if (_handle && zipClose(_handle, nullptr) != ZIP_OK && _error == ZIP_OK) {
  326. _error = -1;
  327. }
  328. _handle = nullptr;
  329. }
  330. int error() const {
  331. if (auto dataError = _data.error()) {
  332. return dataError;
  333. }
  334. return _error;
  335. }
  336. QByteArray result() const {
  337. return _data.result();
  338. }
  339. ~FileToWrite() {
  340. close();
  341. }
  342. private:
  343. internal::InMemoryFile _data;
  344. zipFile _handle = nullptr;
  345. int _error = 0;
  346. };
  347. } // namespace zlib