unzip.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. /****************************************************************************
  2. ** Filename: unzip.cpp
  3. ** Last updated [dd/mm/yyyy]: 08/07/2010
  4. **
  5. ** pkzip 2.0 decompression.
  6. **
  7. ** Some of the code has been inspired by other open source projects,
  8. ** (mainly Info-Zip and Gilles Vollant's minizip).
  9. ** Compression and decompression actually uses the zlib library.
  10. **
  11. ** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved.
  12. **
  13. ** This file is part of the OSDaB project (http://osdab.42cows.org/).
  14. **
  15. ** This file may be distributed and/or modified under the terms of the
  16. ** GNU General Public License version 2 as published by the Free Software
  17. ** Foundation and appearing in the file LICENSE.GPL included in the
  18. ** packaging of this file.
  19. **
  20. ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
  21. ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  22. **
  23. ** See the file LICENSE.GPL that came with this software distribution or
  24. ** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
  25. **
  26. **********************************************************************/
  27. #include "unzip.h"
  28. #include "unzip_p.h"
  29. #include "zipentry_p.h"
  30. #include <QtCore/QCoreApplication>
  31. #include <QtCore/QDir>
  32. #include <QtCore/QFile>
  33. #include <QtCore/QString>
  34. #include <QtCore/QStringList>
  35. // You can remove this #include if you replace the qDebug() statements.
  36. #include <QtCore/QtDebug>
  37. /*!
  38. \class UnZip unzip.h
  39. \brief PKZip 2.0 file decompression.
  40. Compatibility with later versions is not ensured as they may use
  41. unsupported compression algorithms.
  42. Versions after 2.7 may have an incompatible header format and thus be
  43. completely incompatible.
  44. */
  45. /*! \enum UnZip::ErrorCode The result of a decompression operation.
  46. \value UnZip::Ok No error occurred.
  47. \value UnZip::ZlibInit Failed to init or load the zlib library.
  48. \value UnZip::ZlibError The zlib library returned some error.
  49. \value UnZip::OpenFailed Unable to create or open a device.
  50. \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted.
  51. \value UnZip::Corrupted Corrupted or invalid zip archive.
  52. \value UnZip::WrongPassword Unable to decrypt a password protected file.
  53. \value UnZip::NoOpenArchive No archive has been opened yet.
  54. \value UnZip::FileNotFound Unable to find the requested file in the archive.
  55. \value UnZip::ReadFailed Reading of a file failed.
  56. \value UnZip::WriteFailed Writing of a file failed.
  57. \value UnZip::SeekFailed Seek failed.
  58. \value UnZip::CreateDirFailed Could not create a directory.
  59. \value UnZip::InvalidDevice A null device has been passed as parameter.
  60. \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive.
  61. \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted.
  62. \value UnZip::Skip Internal use only.
  63. \value UnZip::SkipAll Internal use only.
  64. */
  65. /*! \enum UnZip::ExtractionOptions Some options for the file extraction methods.
  66. \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files.
  67. \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory.
  68. \value UnZip::VerifyOnly Doesn't actually extract files.
  69. */
  70. //! Local header size (excluding signature, excluding variable length fields)
  71. #define UNZIP_LOCAL_HEADER_SIZE 26
  72. //! Central Directory file entry size (excluding signature, excluding variable length fields)
  73. #define UNZIP_CD_ENTRY_SIZE_NS 42
  74. //! Data descriptor size (excluding signature)
  75. #define UNZIP_DD_SIZE 12
  76. //! End Of Central Directory size (including signature, excluding variable length fields)
  77. #define UNZIP_EOCD_SIZE 22
  78. //! Local header entry encryption header size
  79. #define UNZIP_LOCAL_ENC_HEADER_SIZE 12
  80. // Some offsets inside a CD record (excluding signature)
  81. #define UNZIP_CD_OFF_VERSION_MADE 0
  82. #define UNZIP_CD_OFF_VERSION 2
  83. #define UNZIP_CD_OFF_GPFLAG 4
  84. #define UNZIP_CD_OFF_CMETHOD 6
  85. #define UNZIP_CD_OFF_MODT 8
  86. #define UNZIP_CD_OFF_MODD 10
  87. #define UNZIP_CD_OFF_CRC32 12
  88. #define UNZIP_CD_OFF_CSIZE 16
  89. #define UNZIP_CD_OFF_USIZE 20
  90. #define UNZIP_CD_OFF_NAMELEN 24
  91. #define UNZIP_CD_OFF_XLEN 26
  92. #define UNZIP_CD_OFF_COMMLEN 28
  93. #define UNZIP_CD_OFF_LHOFFSET 38
  94. // Some offsets inside a local header record (excluding signature)
  95. #define UNZIP_LH_OFF_VERSION 0
  96. #define UNZIP_LH_OFF_GPFLAG 2
  97. #define UNZIP_LH_OFF_CMETHOD 4
  98. #define UNZIP_LH_OFF_MODT 6
  99. #define UNZIP_LH_OFF_MODD 8
  100. #define UNZIP_LH_OFF_CRC32 10
  101. #define UNZIP_LH_OFF_CSIZE 14
  102. #define UNZIP_LH_OFF_USIZE 18
  103. #define UNZIP_LH_OFF_NAMELEN 22
  104. #define UNZIP_LH_OFF_XLEN 24
  105. // Some offsets inside a data descriptor record (excluding signature)
  106. #define UNZIP_DD_OFF_CRC32 0
  107. #define UNZIP_DD_OFF_CSIZE 4
  108. #define UNZIP_DD_OFF_USIZE 8
  109. // Some offsets inside a EOCD record
  110. #define UNZIP_EOCD_OFF_ENTRIES 6
  111. #define UNZIP_EOCD_OFF_CDOFF 12
  112. #define UNZIP_EOCD_OFF_COMMLEN 16
  113. /*!
  114. Max version handled by this API.
  115. 0x14 = 2.0 --> full compatibility only up to this version;
  116. later versions use unsupported features
  117. */
  118. #define UNZIP_VERSION 0x14
  119. //! CRC32 routine
  120. #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
  121. OSDAB_BEGIN_NAMESPACE(Zip)
  122. /************************************************************************
  123. ZipEntry
  124. *************************************************************************/
  125. /*!
  126. ZipEntry constructor - initialize data. Type is set to File.
  127. */
  128. UnZip::ZipEntry::ZipEntry()
  129. {
  130. compressedSize = uncompressedSize = crc32 = 0;
  131. compression = NoCompression;
  132. type = File;
  133. encrypted = false;
  134. }
  135. /************************************************************************
  136. Private interface
  137. *************************************************************************/
  138. //! \internal
  139. UnzipPrivate::UnzipPrivate() :
  140. password(),
  141. skipAllEncrypted(false),
  142. headers(0),
  143. device(0),
  144. file(0),
  145. uBuffer(0),
  146. crcTable(0),
  147. cdOffset(0),
  148. eocdOffset(0),
  149. cdEntryCount(0),
  150. unsupportedEntryCount(0),
  151. comment()
  152. {
  153. uBuffer = (unsigned char*) buffer1;
  154. crcTable = (quint32*) get_crc_table();
  155. }
  156. //! \internal
  157. void UnzipPrivate::deviceDestroyed(QObject*)
  158. {
  159. qDebug("Unexpected device destruction detected.");
  160. do_closeArchive();
  161. }
  162. //! \internal Parses a Zip archive.
  163. UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev)
  164. {
  165. Q_ASSERT(!device);
  166. Q_ASSERT(dev);
  167. if (!(dev->isOpen() || dev->open(QIODevice::ReadOnly))) {
  168. qDebug() << "Unable to open device for reading";
  169. return UnZip::OpenFailed;
  170. }
  171. device = dev;
  172. if (device != file)
  173. connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*)));
  174. UnZip::ErrorCode ec;
  175. ec = seekToCentralDirectory();
  176. if (ec != UnZip::Ok) {
  177. closeArchive();
  178. return ec;
  179. }
  180. //! \todo Ignore CD entry count? CD may be corrupted.
  181. if (cdEntryCount == 0) {
  182. return UnZip::Ok;
  183. }
  184. bool continueParsing = true;
  185. while (continueParsing) {
  186. if (device->read(buffer1, 4) != 4) {
  187. if (headers) {
  188. qDebug() << "Corrupted zip archive. Some files might be extracted.";
  189. ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;
  190. break;
  191. } else {
  192. closeArchive();
  193. qDebug() << "Corrupted or invalid zip archive. Closing.";
  194. ec = UnZip::Corrupted;
  195. break;
  196. }
  197. }
  198. if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) )
  199. break;
  200. if ((ec = parseCentralDirectoryRecord()) != UnZip::Ok)
  201. break;
  202. }
  203. if (ec != UnZip::Ok)
  204. closeArchive();
  205. return ec;
  206. }
  207. /*
  208. \internal Parses a local header record and makes some consistency check
  209. with the information stored in the Central Directory record for this entry
  210. that has been previously parsed.
  211. \todo Optional consistency check (as a ExtractionOptions flag)
  212. local file header signature 4 bytes (0x04034b50)
  213. version needed to extract 2 bytes
  214. general purpose bit flag 2 bytes
  215. compression method 2 bytes
  216. last mod file time 2 bytes
  217. last mod file date 2 bytes
  218. crc-32 4 bytes
  219. compressed size 4 bytes
  220. uncompressed size 4 bytes
  221. file name length 2 bytes
  222. extra field length 2 bytes
  223. file name (variable size)
  224. extra field (variable size)
  225. */
  226. UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry)
  227. {
  228. Q_ASSERT(device);
  229. if (!device->seek(entry.lhOffset))
  230. return UnZip::SeekFailed;
  231. // Test signature
  232. if (device->read(buffer1, 4) != 4)
  233. return UnZip::ReadFailed;
  234. if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))
  235. return UnZip::InvalidArchive;
  236. if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE)
  237. return UnZip::ReadFailed;
  238. /*
  239. Check 3rd general purpose bit flag.
  240. "bit 3: If this bit is set, the fields crc-32, compressed size
  241. and uncompressed size are set to zero in the local
  242. header. The correct values are put in the data descriptor
  243. immediately following the compressed data."
  244. */
  245. bool hasDataDescriptor = entry.hasDataDescriptor();
  246. bool checkFailed = false;
  247. if (!checkFailed)
  248. checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD);
  249. if (!checkFailed)
  250. checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG];
  251. if (!checkFailed)
  252. checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1];
  253. if (!checkFailed)
  254. checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT];
  255. if (!checkFailed)
  256. checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1];
  257. if (!checkFailed)
  258. checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD];
  259. if (!checkFailed)
  260. checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1];
  261. if (!hasDataDescriptor)
  262. {
  263. if (!checkFailed)
  264. checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32);
  265. if (!checkFailed)
  266. checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE);
  267. if (!checkFailed)
  268. checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE);
  269. }
  270. if (checkFailed)
  271. return UnZip::HeaderConsistencyError;
  272. // Check filename
  273. quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN);
  274. if (szName == 0)
  275. return UnZip::HeaderConsistencyError;
  276. if (device->read(buffer2, szName) != szName)
  277. return UnZip::ReadFailed;
  278. QString filename = QString::fromAscii(buffer2, szName);
  279. if (filename != path) {
  280. qDebug() << "Filename in local header mismatches.";
  281. return UnZip::HeaderConsistencyError;
  282. }
  283. // Skip extra field
  284. quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN);
  285. if (szExtra != 0) {
  286. if (!device->seek(device->pos() + szExtra))
  287. return UnZip::SeekFailed;
  288. }
  289. entry.dataOffset = device->pos();
  290. if (hasDataDescriptor) {
  291. /*
  292. The data descriptor has this OPTIONAL signature: PK\7\8
  293. We try to skip the compressed data relying on the size set in the
  294. Central Directory record.
  295. */
  296. if (!device->seek(device->pos() + entry.szComp))
  297. return UnZip::SeekFailed;
  298. // Read 4 bytes and check if there is a data descriptor signature
  299. if (device->read(buffer2, 4) != 4)
  300. return UnZip::ReadFailed;
  301. bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;
  302. if (hasSignature) {
  303. if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE)
  304. return UnZip::ReadFailed;
  305. } else {
  306. if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4)
  307. return UnZip::ReadFailed;
  308. }
  309. // DD: crc, compressed size, uncompressed size
  310. if (
  311. entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) ||
  312. entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) ||
  313. entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE)
  314. )
  315. return UnZip::HeaderConsistencyError;
  316. }
  317. return UnZip::Ok;
  318. }
  319. /*! \internal Attempts to find the start of the central directory record.
  320. We seek the file back until we reach the "End Of Central Directory"
  321. signature PK\5\6.
  322. end of central dir signature 4 bytes (0x06054b50)
  323. number of this disk 2 bytes
  324. number of the disk with the
  325. start of the central directory 2 bytes
  326. total number of entries in the
  327. central directory on this disk 2 bytes
  328. total number of entries in
  329. the central directory 2 bytes
  330. size of the central directory 4 bytes
  331. offset of start of central
  332. directory with respect to
  333. the starting disk number 4 bytes
  334. .ZIP file comment length 2 bytes
  335. --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE ---
  336. .ZIP file comment (variable size)
  337. */
  338. UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()
  339. {
  340. Q_ASSERT(device);
  341. qint64 length = device->size();
  342. qint64 offset = length - UNZIP_EOCD_SIZE;
  343. if (length < UNZIP_EOCD_SIZE)
  344. return UnZip::InvalidArchive;
  345. if (!device->seek( offset ))
  346. return UnZip::SeekFailed;
  347. if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
  348. return UnZip::ReadFailed;
  349. bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);
  350. if (eocdFound) {
  351. // Zip file has no comment (the only variable length field in the EOCD record)
  352. eocdOffset = offset;
  353. } else {
  354. qint64 read;
  355. char* p = 0;
  356. offset -= UNZIP_EOCD_SIZE;
  357. if (offset <= 0)
  358. return UnZip::InvalidArchive;
  359. if (!device->seek( offset ))
  360. return UnZip::SeekFailed;
  361. while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0) {
  362. if ( (p = strstr(buffer1, "PK\5\6")) != 0) {
  363. // Seek to the start of the EOCD record so we can read it fully
  364. // Yes... we could simply read the missing bytes and append them to the buffer
  365. // but this is far easier so heck it!
  366. device->seek( offset + (p - buffer1) );
  367. eocdFound = true;
  368. eocdOffset = offset + (p - buffer1);
  369. // Read EOCD record
  370. if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
  371. return UnZip::ReadFailed;
  372. break;
  373. }
  374. // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here.
  375. offset -= 1 /*UNZIP_EOCD_SIZE*/;
  376. if (offset <= 0)
  377. return UnZip::InvalidArchive;
  378. if (!device->seek( offset ))
  379. return UnZip::SeekFailed;
  380. }
  381. }
  382. if (!eocdFound)
  383. return UnZip::InvalidArchive;
  384. // Parse EOCD to locate CD offset
  385. offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
  386. cdOffset = offset;
  387. cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);
  388. quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);
  389. if (commentLength != 0) {
  390. QByteArray c = device->read(commentLength);
  391. if (c.count() != commentLength)
  392. return UnZip::ReadFailed;
  393. comment = c;
  394. }
  395. // Seek to the start of the CD record
  396. if (!device->seek( cdOffset ))
  397. return UnZip::SeekFailed;
  398. return UnZip::Ok;
  399. }
  400. /*!
  401. \internal Parses a central directory record.
  402. Central Directory record structure:
  403. [file header 1]
  404. .
  405. .
  406. .
  407. [file header n]
  408. [digital signature] // PKZip 6.2 or later only
  409. File header:
  410. central file header signature 4 bytes (0x02014b50)
  411. version made by 2 bytes
  412. version needed to extract 2 bytes
  413. general purpose bit flag 2 bytes
  414. compression method 2 bytes
  415. last mod file time 2 bytes
  416. last mod file date 2 bytes
  417. crc-32 4 bytes
  418. compressed size 4 bytes
  419. uncompressed size 4 bytes
  420. file name length 2 bytes
  421. extra field length 2 bytes
  422. file comment length 2 bytes
  423. disk number start 2 bytes
  424. internal file attributes 2 bytes
  425. external file attributes 4 bytes
  426. relative offset of local header 4 bytes
  427. file name (variable size)
  428. extra field (variable size)
  429. file comment (variable size)
  430. */
  431. UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord()
  432. {
  433. Q_ASSERT(device);
  434. // Read CD record
  435. if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)
  436. return UnZip::ReadFailed;
  437. bool skipEntry = false;
  438. // Get compression type so we can skip non compatible algorithms
  439. quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);
  440. // Get variable size fields length so we can skip the whole record
  441. // if necessary
  442. quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);
  443. quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);
  444. quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);
  445. quint32 skipLength = szName + szExtra + szComment;
  446. UnZip::ErrorCode ec = UnZip::Ok;
  447. if ((compMethod != 0) && (compMethod != 8)) {
  448. qDebug() << "Unsupported compression method. Skipping file.";
  449. skipEntry = true;
  450. }
  451. if (!skipEntry && szName == 0) {
  452. qDebug() << "Skipping file with no name.";
  453. skipEntry = true;
  454. }
  455. QString filename;
  456. if (device->read(buffer2, szName) != szName) {
  457. ec = UnZip::ReadFailed;
  458. skipEntry = true;
  459. } else {
  460. filename = QString::fromAscii(buffer2, szName);
  461. }
  462. // Unsupported features if version is bigger than UNZIP_VERSION
  463. if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION) {
  464. QString v = QString::number(buffer1[UNZIP_CD_OFF_VERSION]);
  465. if (v.length() == 2)
  466. v.insert(1, QLatin1Char('.'));
  467. v = QString::fromLatin1("Unsupported PKZip version (%1). Skipping file: %2")
  468. .arg(v, filename.isEmpty() ? QString::fromLatin1("<undefined>") : filename);
  469. qDebug() << v.toLatin1().constData();
  470. skipEntry = true;
  471. }
  472. if (skipEntry) {
  473. if (ec == UnZip::Ok) {
  474. if (!device->seek( device->pos() + skipLength ))
  475. ec = UnZip::SeekFailed;
  476. unsupportedEntryCount++;
  477. }
  478. return ec;
  479. }
  480. ZipEntryP* h = new ZipEntryP;
  481. h->compMethod = compMethod;
  482. h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];
  483. h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];
  484. h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];
  485. h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];
  486. h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];
  487. h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];
  488. h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);
  489. h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);
  490. h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);
  491. // Skip extra field (if any)
  492. if (szExtra != 0) {
  493. if (!device->seek( device->pos() + szExtra )) {
  494. delete h;
  495. return UnZip::SeekFailed;
  496. }
  497. }
  498. // Read comment field (if any)
  499. if (szComment != 0) {
  500. if (device->read(buffer2, szComment) != szComment) {
  501. delete h;
  502. return UnZip::ReadFailed;
  503. }
  504. h->comment = QString::fromAscii(buffer2, szComment);
  505. }
  506. h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);
  507. if (!headers)
  508. headers = new QMap<QString, ZipEntryP*>();
  509. headers->insert(filename, h);
  510. return UnZip::Ok;
  511. }
  512. //! \internal Closes the archive and resets the internal status.
  513. void UnzipPrivate::closeArchive()
  514. {
  515. if (!device) {
  516. Q_ASSERT(!file);
  517. return;
  518. }
  519. if (device != file)
  520. disconnect(device, 0, this, 0);
  521. do_closeArchive();
  522. }
  523. //! \internal
  524. void UnzipPrivate::do_closeArchive()
  525. {
  526. skipAllEncrypted = false;
  527. if (headers) {
  528. if (headers)
  529. qDeleteAll(*headers);
  530. delete headers;
  531. headers = 0;
  532. }
  533. device = 0;
  534. if (file)
  535. delete file;
  536. file = 0;
  537. cdOffset = eocdOffset = 0;
  538. cdEntryCount = 0;
  539. unsupportedEntryCount = 0;
  540. comment.clear();
  541. }
  542. //! \internal
  543. UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry,
  544. const QDir& dir, UnZip::ExtractionOptions options)
  545. {
  546. QString name(path);
  547. QString dirname;
  548. QString directory;
  549. const bool verify = (options & UnZip::VerifyOnly);
  550. const int pos = name.lastIndexOf('/');
  551. // This entry is for a directory
  552. if (pos == name.length() - 1) {
  553. if (verify)
  554. return UnZip::Ok;
  555. if (options & UnZip::SkipPaths)
  556. return UnZip::Ok;
  557. directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
  558. if (!createDirectory(directory)) {
  559. qDebug() << QString("Unable to create directory: %1").arg(directory);
  560. return UnZip::CreateDirFailed;
  561. }
  562. return UnZip::Ok;
  563. }
  564. // Extract path from entry
  565. if (verify) {
  566. return extractFile(path, entry, 0, options);
  567. }
  568. if (pos > 0) {
  569. // get directory part
  570. dirname = name.left(pos);
  571. if (options & UnZip::SkipPaths) {
  572. directory = dir.absolutePath();
  573. } else {
  574. directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
  575. if (!createDirectory(directory)) {
  576. qDebug() << QString("Unable to create directory: %1").arg(directory);
  577. return UnZip::CreateDirFailed;
  578. }
  579. }
  580. name = name.right(name.length() - pos - 1);
  581. } else {
  582. directory = dir.absolutePath();
  583. }
  584. name = QString("%1/%2").arg(directory).arg(name);
  585. QFile outFile(name);
  586. if (!outFile.open(QIODevice::WriteOnly)) {
  587. qDebug() << QString("Unable to open %1 for writing").arg(name);
  588. return UnZip::OpenFailed;
  589. }
  590. UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);
  591. outFile.close();
  592. const QDateTime lastModified = convertDateTime(entry.modDate, entry.modTime);
  593. const bool setTimeOk = OSDAB_ZIP_MANGLE(setFileTimestamp)(name, lastModified);
  594. if (!setTimeOk) {
  595. qDebug() << QString("Unable to set last modified time on file: %1").arg(name);
  596. }
  597. if (ec != UnZip::Ok) {
  598. if (!outFile.remove())
  599. qDebug() << QString("Unable to remove corrupted file: %1").arg(name);
  600. }
  601. return ec;
  602. }
  603. //! \internal
  604. UnZip::ErrorCode UnzipPrivate::extractStoredFile(
  605. const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev,
  606. UnZip::ExtractionOptions options)
  607. {
  608. const bool verify = (options & UnZip::VerifyOnly);
  609. const bool isEncrypted = keys != 0;
  610. uInt rep = szComp / UNZIP_READ_BUFFER;
  611. uInt rem = szComp % UNZIP_READ_BUFFER;
  612. uInt cur = 0;
  613. // extract data
  614. qint64 read;
  615. quint64 tot = 0;
  616. while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 ) {
  617. if (isEncrypted)
  618. decryptBytes(*keys, buffer1, read);
  619. myCRC = crc32(myCRC, uBuffer, read);
  620. if (!verify) {
  621. if (outDev->write(buffer1, read) != read)
  622. return UnZip::WriteFailed;
  623. }
  624. cur++;
  625. tot += read;
  626. if (tot == szComp)
  627. break;
  628. }
  629. return (read < 0)
  630. ? UnZip::ReadFailed
  631. : UnZip::Ok;
  632. }
  633. //! \internal
  634. UnZip::ErrorCode UnzipPrivate::inflateFile(
  635. const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev,
  636. UnZip::ExtractionOptions options)
  637. {
  638. const bool verify = (options & UnZip::VerifyOnly);
  639. const bool isEncrypted = keys != 0;
  640. Q_ASSERT(verify ? true : outDev != 0);
  641. uInt rep = szComp / UNZIP_READ_BUFFER;
  642. uInt rem = szComp % UNZIP_READ_BUFFER;
  643. uInt cur = 0;
  644. // extract data
  645. qint64 read;
  646. quint64 tot = 0;
  647. /* Allocate inflate state */
  648. z_stream zstr;
  649. zstr.zalloc = Z_NULL;
  650. zstr.zfree = Z_NULL;
  651. zstr.opaque = Z_NULL;
  652. zstr.next_in = Z_NULL;
  653. zstr.avail_in = 0;
  654. int zret;
  655. // Use inflateInit2 with negative windowBits to get raw decompression
  656. if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )
  657. return UnZip::ZlibError;
  658. int szDecomp;
  659. // Decompress until deflate stream ends or end of file
  660. do {
  661. read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);
  662. if (!read)
  663. break;
  664. if (read < 0) {
  665. (void)inflateEnd(&zstr);
  666. return UnZip::ReadFailed;
  667. }
  668. if (isEncrypted)
  669. decryptBytes(*keys, buffer1, read);
  670. cur++;
  671. tot += read;
  672. zstr.avail_in = (uInt) read;
  673. zstr.next_in = (Bytef*) buffer1;
  674. // Run inflate() on input until output buffer not full
  675. do {
  676. zstr.avail_out = UNZIP_READ_BUFFER;
  677. zstr.next_out = (Bytef*) buffer2;;
  678. zret = inflate(&zstr, Z_NO_FLUSH);
  679. switch (zret) {
  680. case Z_NEED_DICT:
  681. case Z_DATA_ERROR:
  682. case Z_MEM_ERROR:
  683. inflateEnd(&zstr);
  684. return UnZip::WriteFailed;
  685. default:
  686. ;
  687. }
  688. szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;
  689. if (!verify) {
  690. if (outDev->write(buffer2, szDecomp) != szDecomp) {
  691. inflateEnd(&zstr);
  692. return UnZip::ZlibError;
  693. }
  694. }
  695. myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);
  696. } while (zstr.avail_out == 0);
  697. } while (zret != Z_STREAM_END);
  698. inflateEnd(&zstr);
  699. return UnZip::Ok;
  700. }
  701. //! \internal \p outDev is null if the VerifyOnly option is set
  702. UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry,
  703. QIODevice* outDev, UnZip::ExtractionOptions options)
  704. {
  705. const bool verify = (options & UnZip::VerifyOnly);
  706. Q_UNUSED(options);
  707. Q_ASSERT(device);
  708. Q_ASSERT(verify ? true : outDev != 0);
  709. if (!entry.lhEntryChecked) {
  710. UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);
  711. entry.lhEntryChecked = true;
  712. if (ec != UnZip::Ok)
  713. return ec;
  714. }
  715. if (!device->seek(entry.dataOffset))
  716. return UnZip::SeekFailed;
  717. // Encryption keys
  718. quint32 keys[3];
  719. quint32 szComp = entry.szComp;
  720. if (entry.isEncrypted()) {
  721. UnZip::ErrorCode e = testPassword(keys, path, entry);
  722. if (e != UnZip::Ok)
  723. {
  724. qDebug() << QString("Unable to decrypt %1").arg(path);
  725. return e;
  726. }//! Encryption header size
  727. szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size
  728. }
  729. if (szComp == 0) {
  730. if (entry.crc != 0)
  731. return UnZip::Corrupted;
  732. return UnZip::Ok;
  733. }
  734. quint32 myCRC = crc32(0L, Z_NULL, 0);
  735. quint32* k = keys;
  736. UnZip::ErrorCode ec = UnZip::Ok;
  737. if (entry.compMethod == 0) {
  738. ec = extractStoredFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options);
  739. } else if (entry.compMethod == 8) {
  740. ec = inflateFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options);
  741. }
  742. if (ec == UnZip::Ok && myCRC != entry.crc)
  743. return UnZip::Corrupted;
  744. return UnZip::Ok;
  745. }
  746. //! \internal Creates a new directory and all the needed parent directories.
  747. bool UnzipPrivate::createDirectory(const QString& path)
  748. {
  749. QDir d(path);
  750. if (!d.exists()) {
  751. int sep = path.lastIndexOf("/");
  752. if (sep <= 0) return true;
  753. if (!createDirectory(path.left(sep)))
  754. return false;
  755. if (!d.mkdir(path)) {
  756. qDebug() << QString("Unable to create directory: %1").arg(path);
  757. return false;
  758. }
  759. }
  760. return true;
  761. }
  762. /*!
  763. \internal Reads an quint32 (4 bytes) from a byte array starting at given offset.
  764. */
  765. quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const
  766. {
  767. quint32 res = (quint32) data[offset];
  768. res |= (((quint32)data[offset+1]) << 8);
  769. res |= (((quint32)data[offset+2]) << 16);
  770. res |= (((quint32)data[offset+3]) << 24);
  771. return res;
  772. }
  773. /*!
  774. \internal Reads an quint64 (8 bytes) from a byte array starting at given offset.
  775. */
  776. quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const
  777. {
  778. quint64 res = (quint64) data[offset];
  779. res |= (((quint64)data[offset+1]) << 8);
  780. res |= (((quint64)data[offset+2]) << 16);
  781. res |= (((quint64)data[offset+3]) << 24);
  782. res |= (((quint64)data[offset+1]) << 32);
  783. res |= (((quint64)data[offset+2]) << 40);
  784. res |= (((quint64)data[offset+3]) << 48);
  785. res |= (((quint64)data[offset+3]) << 56);
  786. return res;
  787. }
  788. /*!
  789. \internal Reads an quint16 (2 bytes) from a byte array starting at given offset.
  790. */
  791. quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const
  792. {
  793. return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
  794. }
  795. /*!
  796. \internal Return the next byte in the pseudo-random sequence
  797. */
  798. int UnzipPrivate::decryptByte(quint32 key2) const
  799. {
  800. quint16 temp = ((quint16)(key2) & 0xffff) | 2;
  801. return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
  802. }
  803. /*!
  804. \internal Update the encryption keys with the next byte of plain text
  805. */
  806. void UnzipPrivate::updateKeys(quint32* keys, int c) const
  807. {
  808. keys[0] = CRC32(keys[0], c);
  809. keys[1] += keys[0] & 0xff;
  810. keys[1] = keys[1] * 134775813L + 1;
  811. keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
  812. }
  813. /*!
  814. \internal Initialize the encryption keys and the random header according to
  815. the given password.
  816. */
  817. void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const
  818. {
  819. keys[0] = 305419896L;
  820. keys[1] = 591751049L;
  821. keys[2] = 878082192L;
  822. QByteArray pwdBytes = pwd.toAscii();
  823. int sz = pwdBytes.size();
  824. const char* ascii = pwdBytes.data();
  825. for (int i = 0; i < sz; ++i)
  826. updateKeys(keys, (int)ascii[i]);
  827. }
  828. /*!
  829. \internal Attempts to test a password without actually extracting a file.
  830. The \p file parameter can be used in the user interface or for debugging purposes
  831. as it is the name of the encrypted file for wich the password is being tested.
  832. */
  833. UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)
  834. {
  835. Q_UNUSED(file);
  836. Q_ASSERT(device);
  837. // read encryption keys
  838. if (device->read(buffer1, 12) != 12)
  839. return UnZip::Corrupted;
  840. // Replace this code if you want to i.e. call some dialog and ask the user for a password
  841. initKeys(password, keys);
  842. if (testKeys(header, keys))
  843. return UnZip::Ok;
  844. return UnZip::Skip;
  845. }
  846. /*!
  847. \internal Tests a set of keys on the encryption header.
  848. */
  849. bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)
  850. {
  851. char lastByte;
  852. // decrypt encryption header
  853. for (int i = 0; i < 11; ++i)
  854. updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
  855. updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
  856. // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time
  857. // with no extended header we have to check the crc high-order byte
  858. char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;
  859. return (lastByte == c);
  860. }
  861. /*!
  862. \internal Decrypts an array of bytes long \p read.
  863. */
  864. void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)
  865. {
  866. for (int i = 0; i < (int)read; ++i)
  867. updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
  868. }
  869. /*!
  870. \internal Converts date and time values from ZIP format to a QDateTime object.
  871. */
  872. QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
  873. {
  874. QDateTime dt;
  875. // Usual PKZip low-byte to high-byte order
  876. // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day
  877. quint16 year = (date[1] >> 1) & 127;
  878. quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
  879. quint16 day = date[0] & 31;
  880. // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision
  881. quint16 hour = (time[1] >> 3) & 31;
  882. quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
  883. quint16 seconds = (time[0] & 31) * 2;
  884. dt.setDate(QDate(1980 + year, month, day));
  885. dt.setTime(QTime(hour, minutes, seconds));
  886. return dt;
  887. }
  888. /************************************************************************
  889. Public interface
  890. *************************************************************************/
  891. /*!
  892. Creates a new Zip file decompressor.
  893. */
  894. UnZip::UnZip() : d(new UnzipPrivate)
  895. {
  896. }
  897. /*!
  898. Closes any open archive and releases used resources.
  899. */
  900. UnZip::~UnZip()
  901. {
  902. closeArchive();
  903. delete d;
  904. }
  905. /*!
  906. Returns true if there is an open archive.
  907. */
  908. bool UnZip::isOpen() const
  909. {
  910. return d->device;
  911. }
  912. /*!
  913. Opens a zip archive and reads the files list. Closes any previously opened archive.
  914. */
  915. UnZip::ErrorCode UnZip::openArchive(const QString& filename)
  916. {
  917. closeArchive();
  918. // closeArchive will destroy the file
  919. d->file = new QFile(filename);
  920. if (!d->file->exists()) {
  921. delete d->file;
  922. d->file = 0;
  923. return UnZip::FileNotFound;
  924. }
  925. if (!d->file->open(QIODevice::ReadOnly)) {
  926. delete d->file;
  927. d->file = 0;
  928. return UnZip::OpenFailed;
  929. }
  930. return d->openArchive(d->file);
  931. }
  932. /*!
  933. Opens a zip archive and reads the entries list.
  934. Closes any previously opened archive.
  935. \warning The class takes DOES NOT take ownership of the device.
  936. */
  937. UnZip::ErrorCode UnZip::openArchive(QIODevice* device)
  938. {
  939. closeArchive();
  940. if (!device) {
  941. qDebug() << "Invalid device.";
  942. return UnZip::InvalidDevice;
  943. }
  944. return d->openArchive(device);
  945. }
  946. /*!
  947. Closes the archive and releases all the used resources (like cached passwords).
  948. */
  949. void UnZip::closeArchive()
  950. {
  951. d->closeArchive();
  952. }
  953. QString UnZip::archiveComment() const
  954. {
  955. return d->comment;
  956. }
  957. /*!
  958. Returns a locale translated error string for a given error code.
  959. */
  960. QString UnZip::formatError(UnZip::ErrorCode c) const
  961. {
  962. switch (c)
  963. {
  964. case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break;
  965. case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break;
  966. case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break;
  967. case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break;
  968. case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break;
  969. case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break;
  970. case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break;
  971. case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break;
  972. case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break;
  973. case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break;
  974. case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break;
  975. case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break;
  976. case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break;
  977. case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break;
  978. case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break;
  979. case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break;
  980. default: ;
  981. }
  982. return QCoreApplication::translate("UnZip", "Unknown error.");
  983. }
  984. /*!
  985. Returns true if the archive contains a file with the given path and name.
  986. */
  987. bool UnZip::contains(const QString& file) const
  988. {
  989. return d->headers ? d->headers->contains(file) : false;
  990. }
  991. /*!
  992. Returns complete paths of files and directories in this archive.
  993. */
  994. QStringList UnZip::fileList() const
  995. {
  996. return d->headers ? d->headers->keys() : QStringList();
  997. }
  998. /*!
  999. Returns information for each (correctly parsed) entry of this archive.
  1000. */
  1001. QList<UnZip::ZipEntry> UnZip::entryList() const
  1002. {
  1003. QList<UnZip::ZipEntry> list;
  1004. if (!d->headers)
  1005. return list;
  1006. for (QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin();
  1007. it != d->headers->constEnd(); ++it) {
  1008. const ZipEntryP* entry = it.value();
  1009. Q_ASSERT(entry != 0);
  1010. ZipEntry z;
  1011. z.filename = it.key();
  1012. if (!entry->comment.isEmpty())
  1013. z.comment = entry->comment;
  1014. z.compressedSize = entry->szComp;
  1015. z.uncompressedSize = entry->szUncomp;
  1016. z.crc32 = entry->crc;
  1017. z.lastModified = d->convertDateTime(entry->modDate, entry->modTime);
  1018. z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression;
  1019. z.type = z.filename.endsWith("/") ? Directory : File;
  1020. z.encrypted = entry->isEncrypted();
  1021. list.append(z);
  1022. }
  1023. return list;
  1024. }
  1025. /*!
  1026. Extracts the whole archive to a directory.
  1027. */
  1028. UnZip::ErrorCode UnZip::verifyArchive()
  1029. {
  1030. return extractAll(QDir(), VerifyOnly);
  1031. }
  1032. /*!
  1033. Extracts the whole archive to a directory.
  1034. */
  1035. UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options)
  1036. {
  1037. return extractAll(QDir(dirname), options);
  1038. }
  1039. /*!
  1040. Extracts the whole archive to a directory.
  1041. Stops extraction at the first error.
  1042. */
  1043. UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options)
  1044. {
  1045. // this should only happen if we didn't call openArchive() yet
  1046. if (!d->device)
  1047. return NoOpenArchive;
  1048. if (!d->headers)
  1049. return Ok;
  1050. ErrorCode ec = Ok;
  1051. QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin();
  1052. const QMap<QString,ZipEntryP*>::ConstIterator end = d->headers->constEnd();
  1053. while (it != end) {
  1054. ZipEntryP* entry = it.value();
  1055. Q_ASSERT(entry != 0);
  1056. if ((entry->isEncrypted()) && d->skipAllEncrypted) {
  1057. ++it;
  1058. continue;
  1059. }
  1060. bool skip = false;
  1061. ec = d->extractFile(it.key(), *entry, dir, options);
  1062. switch (ec) {
  1063. case Corrupted:
  1064. qDebug() << "Corrupted entry" << it.key();
  1065. break;
  1066. case CreateDirFailed:
  1067. break;
  1068. case Skip:
  1069. skip = true;
  1070. break;
  1071. case SkipAll:
  1072. skip = true;
  1073. d->skipAllEncrypted = true;
  1074. break;
  1075. default:
  1076. ;
  1077. }
  1078. if (ec != Ok && !skip) {
  1079. break;
  1080. }
  1081. ++it;
  1082. }
  1083. return ec;
  1084. }
  1085. /*!
  1086. Extracts a single file to a directory.
  1087. */
  1088. UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options)
  1089. {
  1090. return extractFile(filename, QDir(dirname), options);
  1091. }
  1092. /*!
  1093. Extracts a single file to a directory.
  1094. */
  1095. UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options)
  1096. {
  1097. if (!d->device)
  1098. return NoOpenArchive;
  1099. if (!d->headers)
  1100. return FileNotFound;
  1101. QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
  1102. if (itr != d->headers->end()) {
  1103. ZipEntryP* entry = itr.value();
  1104. Q_ASSERT(entry != 0);
  1105. return d->extractFile(itr.key(), *entry, dir, options);
  1106. }
  1107. return FileNotFound;
  1108. }
  1109. /*!
  1110. Extracts a single file to a directory.
  1111. */
  1112. UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* outDev, ExtractionOptions options)
  1113. {
  1114. if (!d->device)
  1115. return NoOpenArchive;
  1116. if (!d->headers)
  1117. return FileNotFound;
  1118. if (!outDev)
  1119. return InvalidDevice;
  1120. QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
  1121. if (itr != d->headers->end()) {
  1122. ZipEntryP* entry = itr.value();
  1123. Q_ASSERT(entry != 0);
  1124. return d->extractFile(itr.key(), *entry, outDev, options);
  1125. }
  1126. return FileNotFound;
  1127. }
  1128. /*!
  1129. Extracts a list of files.
  1130. Stops extraction at the first error (but continues if a file does not exist in the archive).
  1131. */
  1132. UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options)
  1133. {
  1134. if (!d->device)
  1135. return NoOpenArchive;
  1136. if (!d->headers)
  1137. return Ok;
  1138. QDir dir(dirname);
  1139. ErrorCode ec;
  1140. for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) {
  1141. ec = extractFile(*itr, dir, options);
  1142. if (ec == FileNotFound)
  1143. continue;
  1144. if (ec != Ok)
  1145. return ec;
  1146. }
  1147. return Ok;
  1148. }
  1149. /*!
  1150. Extracts a list of files.
  1151. Stops extraction at the first error (but continues if a file does not exist in the archive).
  1152. */
  1153. UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options)
  1154. {
  1155. if (!d->device)
  1156. return NoOpenArchive;
  1157. if (!d->headers)
  1158. return Ok;
  1159. ErrorCode ec;
  1160. for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) {
  1161. ec = extractFile(*itr, dir, options);
  1162. if (ec == FileNotFound)
  1163. continue;
  1164. if (ec != Ok)
  1165. return ec;
  1166. }
  1167. return Ok;
  1168. }
  1169. /*!
  1170. Remove/replace this method to add your own password retrieval routine.
  1171. */
  1172. void UnZip::setPassword(const QString& pwd)
  1173. {
  1174. d->password = pwd;
  1175. }
  1176. OSDAB_END_NAMESPACE