quazip.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /*
  2. Copyright (C) 2005-2011 Sergey A. Tachenov
  3. This program is free software; you can redistribute it and/or modify it
  4. under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at
  6. your option) any later version.
  7. This program is distributed in the hope that it will be useful, but
  8. WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  10. General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software Foundation,
  13. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  14. See COPYING file for the full LGPL text.
  15. Original ZIP package is copyrighted by Gilles Vollant, see
  16. quazip/(un)zip.h files for details, basically it's zlib license.
  17. **/
  18. #include <QFile>
  19. #include <QFlags>
  20. #include "quazip.h"
  21. class QuaZipPrivate {
  22. friend class QuaZip;
  23. private:
  24. QuaZip *q;
  25. QTextCodec *fileNameCodec, *commentCodec;
  26. QString zipName;
  27. QIODevice *ioDevice;
  28. QString comment;
  29. QuaZip::Mode mode;
  30. union {
  31. unzFile unzFile_f;
  32. zipFile zipFile_f;
  33. };
  34. bool hasCurrentFile_f;
  35. int zipError;
  36. bool dataDescriptorWritingEnabled;
  37. inline QuaZipPrivate(QuaZip *q):
  38. q(q),
  39. fileNameCodec(QTextCodec::codecForLocale()),
  40. commentCodec(QTextCodec::codecForLocale()),
  41. ioDevice(NULL),
  42. mode(QuaZip::mdNotOpen),
  43. hasCurrentFile_f(false),
  44. zipError(UNZ_OK),
  45. dataDescriptorWritingEnabled(true) {}
  46. inline QuaZipPrivate(QuaZip *q, const QString &zipName):
  47. q(q),
  48. fileNameCodec(QTextCodec::codecForLocale()),
  49. commentCodec(QTextCodec::codecForLocale()),
  50. zipName(zipName),
  51. ioDevice(NULL),
  52. mode(QuaZip::mdNotOpen),
  53. hasCurrentFile_f(false),
  54. zipError(UNZ_OK),
  55. dataDescriptorWritingEnabled(true) {}
  56. inline QuaZipPrivate(QuaZip *q, QIODevice *ioDevice):
  57. q(q),
  58. fileNameCodec(QTextCodec::codecForLocale()),
  59. commentCodec(QTextCodec::codecForLocale()),
  60. ioDevice(ioDevice),
  61. mode(QuaZip::mdNotOpen),
  62. hasCurrentFile_f(false),
  63. zipError(UNZ_OK),
  64. dataDescriptorWritingEnabled(true) {}
  65. template<typename TFileInfo>
  66. bool getFileInfoList(QList<TFileInfo> *result) const;
  67. };
  68. QuaZip::QuaZip():
  69. p(new QuaZipPrivate(this))
  70. {
  71. }
  72. QuaZip::QuaZip(const QString& zipName):
  73. p(new QuaZipPrivate(this, zipName))
  74. {
  75. }
  76. QuaZip::QuaZip(QIODevice *ioDevice):
  77. p(new QuaZipPrivate(this, ioDevice))
  78. {
  79. }
  80. QuaZip::~QuaZip()
  81. {
  82. if(isOpen())
  83. close();
  84. delete p;
  85. }
  86. bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi)
  87. {
  88. p->zipError=UNZ_OK;
  89. if(isOpen()) {
  90. qWarning("QuaZip::open(): ZIP already opened");
  91. return false;
  92. }
  93. QIODevice *ioDevice = p->ioDevice;
  94. if (ioDevice == NULL) {
  95. if (p->zipName.isEmpty()) {
  96. qWarning("QuaZip::open(): set either ZIP file name or IO device first");
  97. return false;
  98. } else {
  99. ioDevice = new QFile(p->zipName);
  100. }
  101. }
  102. switch(mode) {
  103. case mdUnzip:
  104. p->unzFile_f=unzOpen2(ioDevice, ioApi);
  105. if(p->unzFile_f!=NULL) {
  106. p->mode=mode;
  107. p->ioDevice = ioDevice;
  108. return true;
  109. } else {
  110. p->zipError=UNZ_OPENERROR;
  111. if (!p->zipName.isEmpty())
  112. delete ioDevice;
  113. return false;
  114. }
  115. case mdCreate:
  116. case mdAppend:
  117. case mdAdd:
  118. p->zipFile_f=zipOpen2(ioDevice,
  119. mode==mdCreate?APPEND_STATUS_CREATE:
  120. mode==mdAppend?APPEND_STATUS_CREATEAFTER:
  121. APPEND_STATUS_ADDINZIP,
  122. NULL,
  123. ioApi);
  124. if(p->zipFile_f!=NULL) {
  125. p->mode=mode;
  126. p->ioDevice = ioDevice;
  127. return true;
  128. } else {
  129. p->zipError=UNZ_OPENERROR;
  130. if (!p->zipName.isEmpty())
  131. delete ioDevice;
  132. return false;
  133. }
  134. default:
  135. qWarning("QuaZip::open(): unknown mode: %d", (int)mode);
  136. if (!p->zipName.isEmpty())
  137. delete ioDevice;
  138. return false;
  139. break;
  140. }
  141. }
  142. void QuaZip::close()
  143. {
  144. p->zipError=UNZ_OK;
  145. switch(p->mode) {
  146. case mdNotOpen:
  147. qWarning("QuaZip::close(): ZIP is not open");
  148. return;
  149. case mdUnzip:
  150. p->zipError=unzClose(p->unzFile_f);
  151. break;
  152. case mdCreate:
  153. case mdAppend:
  154. case mdAdd:
  155. p->zipError=zipClose(p->zipFile_f, p->commentCodec->fromUnicode(p->comment).constData());
  156. break;
  157. default:
  158. qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode);
  159. return;
  160. }
  161. // opened by name, need to delete the internal IO device
  162. if (!p->zipName.isEmpty())
  163. delete p->ioDevice;
  164. if(p->zipError==UNZ_OK)
  165. p->mode=mdNotOpen;
  166. }
  167. void QuaZip::setZipName(const QString& zipName)
  168. {
  169. if(isOpen()) {
  170. qWarning("QuaZip::setZipName(): ZIP is already open!");
  171. return;
  172. }
  173. p->zipName=zipName;
  174. p->ioDevice = NULL;
  175. }
  176. void QuaZip::setIoDevice(QIODevice *ioDevice)
  177. {
  178. if(isOpen()) {
  179. qWarning("QuaZip::setIoDevice(): ZIP is already open!");
  180. return;
  181. }
  182. p->ioDevice = ioDevice;
  183. p->zipName = QString();
  184. }
  185. int QuaZip::getEntriesCount()const
  186. {
  187. QuaZip *fakeThis=(QuaZip*)this; // non-const
  188. fakeThis->p->zipError=UNZ_OK;
  189. if(p->mode!=mdUnzip) {
  190. qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode");
  191. return -1;
  192. }
  193. unz_global_info globalInfo;
  194. if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
  195. return p->zipError;
  196. return (int)globalInfo.number_entry;
  197. }
  198. QString QuaZip::getComment()const
  199. {
  200. QuaZip *fakeThis=(QuaZip*)this; // non-const
  201. fakeThis->p->zipError=UNZ_OK;
  202. if(p->mode!=mdUnzip) {
  203. qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode");
  204. return QString();
  205. }
  206. unz_global_info globalInfo;
  207. QByteArray comment;
  208. if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
  209. return QString();
  210. comment.resize(globalInfo.size_comment);
  211. if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0)
  212. return QString();
  213. fakeThis->p->zipError = UNZ_OK;
  214. return p->commentCodec->toUnicode(comment);
  215. }
  216. bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs)
  217. {
  218. p->zipError=UNZ_OK;
  219. if(p->mode!=mdUnzip) {
  220. qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode");
  221. return false;
  222. }
  223. if(fileName.isEmpty()) {
  224. p->hasCurrentFile_f=false;
  225. return true;
  226. }
  227. // Unicode-aware reimplementation of the unzLocateFile function
  228. if(p->unzFile_f==NULL) {
  229. p->zipError=UNZ_PARAMERROR;
  230. return false;
  231. }
  232. if(fileName.length()>MAX_FILE_NAME_LENGTH) {
  233. p->zipError=UNZ_PARAMERROR;
  234. return false;
  235. }
  236. bool sens;
  237. if(cs==csDefault) {
  238. #ifdef Q_WS_WIN
  239. sens=false;
  240. #else
  241. sens=true;
  242. #endif
  243. } else sens=cs==csSensitive;
  244. QString lower, current;
  245. if(!sens) lower=fileName.toLower();
  246. p->hasCurrentFile_f=false;
  247. for(bool more=goToFirstFile(); more; more=goToNextFile()) {
  248. current=getCurrentFileName();
  249. if(current.isEmpty()) return false;
  250. if(sens) {
  251. if(current==fileName) break;
  252. } else {
  253. if(current.toLower()==lower) break;
  254. }
  255. }
  256. return p->hasCurrentFile_f;
  257. }
  258. bool QuaZip::goToFirstFile()
  259. {
  260. p->zipError=UNZ_OK;
  261. if(p->mode!=mdUnzip) {
  262. qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
  263. return false;
  264. }
  265. p->zipError=unzGoToFirstFile(p->unzFile_f);
  266. p->hasCurrentFile_f=p->zipError==UNZ_OK;
  267. return p->hasCurrentFile_f;
  268. }
  269. bool QuaZip::goToNextFile()
  270. {
  271. p->zipError=UNZ_OK;
  272. if(p->mode!=mdUnzip) {
  273. qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
  274. return false;
  275. }
  276. p->zipError=unzGoToNextFile(p->unzFile_f);
  277. p->hasCurrentFile_f=p->zipError==UNZ_OK;
  278. if(p->zipError==UNZ_END_OF_LIST_OF_FILE)
  279. p->zipError=UNZ_OK;
  280. return p->hasCurrentFile_f;
  281. }
  282. bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const
  283. {
  284. QuaZip *fakeThis=(QuaZip*)this; // non-const
  285. fakeThis->p->zipError=UNZ_OK;
  286. if(p->mode!=mdUnzip) {
  287. qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode");
  288. return false;
  289. }
  290. unz_file_info info_z;
  291. QByteArray fileName;
  292. QByteArray extra;
  293. QByteArray comment;
  294. if(info==NULL) return false;
  295. if(!isOpen()||!hasCurrentFile()) return false;
  296. if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK)
  297. return false;
  298. fileName.resize(info_z.size_filename);
  299. extra.resize(info_z.size_file_extra);
  300. comment.resize(info_z.size_file_comment);
  301. if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL,
  302. fileName.data(), fileName.size(),
  303. extra.data(), extra.size(),
  304. comment.data(), comment.size()))!=UNZ_OK)
  305. return false;
  306. info->versionCreated=info_z.version;
  307. info->versionNeeded=info_z.version_needed;
  308. info->flags=info_z.flag;
  309. info->method=info_z.compression_method;
  310. info->crc=info_z.crc;
  311. info->compressedSize=info_z.compressed_size;
  312. info->uncompressedSize=info_z.uncompressed_size;
  313. info->diskNumberStart=info_z.disk_num_start;
  314. info->internalAttr=info_z.internal_fa;
  315. info->externalAttr=info_z.external_fa;
  316. info->name=p->fileNameCodec->toUnicode(fileName);
  317. info->comment=p->commentCodec->toUnicode(comment);
  318. info->extra=extra;
  319. info->dateTime=QDateTime(
  320. QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday),
  321. QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec));
  322. return true;
  323. }
  324. QString QuaZip::getCurrentFileName()const
  325. {
  326. QuaZip *fakeThis=(QuaZip*)this; // non-const
  327. fakeThis->p->zipError=UNZ_OK;
  328. if(p->mode!=mdUnzip) {
  329. qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode");
  330. return QString();
  331. }
  332. if(!isOpen()||!hasCurrentFile()) return QString();
  333. QByteArray fileName(MAX_FILE_NAME_LENGTH, 0);
  334. if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL, fileName.data(), fileName.size(),
  335. NULL, 0, NULL, 0))!=UNZ_OK)
  336. return QString();
  337. return p->fileNameCodec->toUnicode(fileName.constData());
  338. }
  339. void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec)
  340. {
  341. p->fileNameCodec=fileNameCodec;
  342. }
  343. void QuaZip::setFileNameCodec(const char *fileNameCodecName)
  344. {
  345. p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName);
  346. }
  347. QTextCodec *QuaZip::getFileNameCodec()const
  348. {
  349. return p->fileNameCodec;
  350. }
  351. void QuaZip::setCommentCodec(QTextCodec *commentCodec)
  352. {
  353. p->commentCodec=commentCodec;
  354. }
  355. void QuaZip::setCommentCodec(const char *commentCodecName)
  356. {
  357. p->commentCodec=QTextCodec::codecForName(commentCodecName);
  358. }
  359. QTextCodec *QuaZip::getCommentCodec()const
  360. {
  361. return p->commentCodec;
  362. }
  363. QString QuaZip::getZipName() const
  364. {
  365. return p->zipName;
  366. }
  367. QIODevice *QuaZip::getIoDevice() const
  368. {
  369. if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice
  370. return NULL;
  371. return p->ioDevice;
  372. }
  373. QuaZip::Mode QuaZip::getMode()const
  374. {
  375. return p->mode;
  376. }
  377. bool QuaZip::isOpen()const
  378. {
  379. return p->mode!=mdNotOpen;
  380. }
  381. int QuaZip::getZipError() const
  382. {
  383. return p->zipError;
  384. }
  385. void QuaZip::setComment(const QString& comment)
  386. {
  387. p->comment=comment;
  388. }
  389. bool QuaZip::hasCurrentFile()const
  390. {
  391. return p->hasCurrentFile_f;
  392. }
  393. unzFile QuaZip::getUnzFile()
  394. {
  395. return p->unzFile_f;
  396. }
  397. zipFile QuaZip::getZipFile()
  398. {
  399. return p->zipFile_f;
  400. }
  401. void QuaZip::setDataDescriptorWritingEnabled(bool enabled)
  402. {
  403. p->dataDescriptorWritingEnabled = enabled;
  404. }
  405. bool QuaZip::isDataDescriptorWritingEnabled() const
  406. {
  407. return p->dataDescriptorWritingEnabled;
  408. }
  409. template<typename TFileInfo>
  410. static TFileInfo getFileInfo(QuaZip *zip, bool *ok);
  411. template<>
  412. QuaZipFileInfo getFileInfo(QuaZip *zip, bool *ok)
  413. {
  414. QuaZipFileInfo info;
  415. *ok = zip->getCurrentFileInfo(&info);
  416. return info;
  417. }
  418. template<>
  419. QString getFileInfo(QuaZip *zip, bool *ok)
  420. {
  421. QString name = zip->getCurrentFileName();
  422. *ok = !name.isEmpty();
  423. return name;
  424. }
  425. template<typename TFileInfo>
  426. bool QuaZipPrivate::getFileInfoList(QList<TFileInfo> *result) const
  427. {
  428. QuaZipPrivate *fakeThis=const_cast<QuaZipPrivate*>(this);
  429. fakeThis->zipError=UNZ_OK;
  430. if (mode!=QuaZip::mdUnzip) {
  431. qWarning("QuaZip::getFileNameList/getFileInfoList(): "
  432. "ZIP is not open in mdUnzip mode");
  433. return false;
  434. }
  435. QString currentFile;
  436. if (q->hasCurrentFile()) {
  437. currentFile = q->getCurrentFileName();
  438. }
  439. if (q->goToFirstFile()) {
  440. do {
  441. bool ok;
  442. result->append(getFileInfo<TFileInfo>(q, &ok));
  443. if (!ok)
  444. return false;
  445. } while (q->goToNextFile());
  446. }
  447. if (zipError != UNZ_OK)
  448. return false;
  449. if (currentFile.isEmpty()) {
  450. if (!q->setCurrentFile(currentFile))
  451. return false;
  452. } else {
  453. if (!q->goToFirstFile())
  454. return false;
  455. }
  456. return true;
  457. }
  458. QStringList QuaZip::getFileNameList() const
  459. {
  460. QStringList list;
  461. if (p->getFileInfoList(&list))
  462. return list;
  463. else
  464. return QStringList();
  465. }
  466. QList<QuaZipFileInfo> QuaZip::getFileInfoList() const
  467. {
  468. QList<QuaZipFileInfo> list;
  469. if (p->getFileInfoList(&list))
  470. return list;
  471. else
  472. return QList<QuaZipFileInfo>();
  473. }