37 #include <QStringList> 41 #include <QCoreApplication> 47 #define ZIP_LOCAL_HEADER_SIZE 30 49 #define ZIP_LOCAL_ENC_HEADER_SIZE 12 51 #define ZIP_DD_SIZE_WS 16 53 #define ZIP_CD_SIZE 46 55 #define ZIP_EOCD_SIZE 22 58 #define ZIP_LH_OFF_VERS 4 59 #define ZIP_LH_OFF_GPFLAG 6 60 #define ZIP_LH_OFF_CMET 8 61 #define ZIP_LH_OFF_MODT 10 62 #define ZIP_LH_OFF_MODD 12 63 #define ZIP_LH_OFF_CRC 14 64 #define ZIP_LH_OFF_CSIZE 18 65 #define ZIP_LH_OFF_USIZE 22 66 #define ZIP_LH_OFF_NAMELEN 26 67 #define ZIP_LH_OFF_XLEN 28 70 #define ZIP_DD_OFF_CRC32 4 71 #define ZIP_DD_OFF_CSIZE 8 72 #define ZIP_DD_OFF_USIZE 12 75 #define ZIP_CD_OFF_MADEBY 4 76 #define ZIP_CD_OFF_VERSION 6 77 #define ZIP_CD_OFF_GPFLAG 8 78 #define ZIP_CD_OFF_CMET 10 79 #define ZIP_CD_OFF_MODT 12 80 #define ZIP_CD_OFF_MODD 14 81 #define ZIP_CD_OFF_CRC 16 82 #define ZIP_CD_OFF_CSIZE 20 83 #define ZIP_CD_OFF_USIZE 24 84 #define ZIP_CD_OFF_NAMELEN 28 85 #define ZIP_CD_OFF_XLEN 30 86 #define ZIP_CD_OFF_COMMLEN 32 87 #define ZIP_CD_OFF_DISKSTART 34 88 #define ZIP_CD_OFF_IATTR 36 89 #define ZIP_CD_OFF_EATTR 38 90 #define ZIP_CD_OFF_LHOFF 42 93 #define ZIP_EOCD_OFF_DISKNUM 4 94 #define ZIP_EOCD_OFF_CDDISKNUM 6 95 #define ZIP_EOCD_OFF_ENTRIES 8 96 #define ZIP_EOCD_OFF_CDENTRIES 10 97 #define ZIP_EOCD_OFF_CDSIZE 12 98 #define ZIP_EOCD_OFF_CDOFF 16 99 #define ZIP_EOCD_OFF_COMMLEN 20 102 #define ZIP_VERSION 0x14 105 #define ZIP_COMPRESSION_THRESHOLD 60 108 #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) 270 QFile* file =
new QFile(filename);
272 if (file->exists() && !overwrite) {
277 if (!file->open(QIODevice::WriteOnly)) {
298 qDebug() <<
"Invalid device.";
389 QString actualRoot = root.trimmed();
392 if (actualRoot !=
"/")
394 while (actualRoot.endsWith(
"/") || actualRoot.endsWith(
"\\"))
395 actualRoot.truncate(actualRoot.length() - 1);
399 QFileInfo current(QDir::cleanPath(path));
401 if (!actualRoot.isEmpty() && actualRoot !=
"/")
402 actualRoot.append(
"/");
409 if (!absolutePath.isEmpty() && absolutePath !=
"/")
410 absolutePath.append(
"/");
411 actualRoot.append(absolutePath);
416 actualRoot = actualRoot.append(QDir(current.absoluteFilePath()).dirName());
417 actualRoot.append(
"/");
423 QFileInfoList list = dir.entryInfoList(
426 QDir::NoDotAndDotDot |
430 bool filesAdded =
false;
432 CompressionOptions recursionOptions;
437 for (
int i = 0; i < list.size() && ec ==
Zip::Ok; ++i)
439 QFileInfo info = list.at(i);
444 ec =
addDirectory(info.absoluteFilePath(), actualRoot, recursionOptions, level);
479 case Ok:
return QCoreApplication::translate(
"Zip",
"ZIP operation completed successfully.");
break;
480 case ZlibInit:
return QCoreApplication::translate(
"Zip",
"Failed to initialize or load zlib library.");
break;
481 case ZlibError:
return QCoreApplication::translate(
"Zip",
"zlib library error.");
break;
482 case OpenFailed:
return QCoreApplication::translate(
"Zip",
"Unable to create or open file.");
break;
483 case NoOpenArchive:
return QCoreApplication::translate(
"Zip",
"No archive has been created yet.");
break;
484 case FileNotFound:
return QCoreApplication::translate(
"Zip",
"File or directory does not exist.");
break;
485 case ReadFailed:
return QCoreApplication::translate(
"Zip",
"File read error.");
break;
486 case WriteFailed:
return QCoreApplication::translate(
"Zip",
"File write error.");
break;
487 case SeekFailed:
return QCoreApplication::translate(
"Zip",
"File seek error.");
break;
491 return QCoreApplication::translate(
"Zip",
"Unknown error.");
506 uBuffer = (
unsigned char*) buffer1;
507 crcTable = (quint32*) get_crc_table();
526 if (!device->isOpen())
528 if (!device->open(QIODevice::ReadOnly)) {
531 qDebug() <<
"Unable to open device for writing.";
536 headers =
new QMap<QString,ZipEntryP*>;
549 bool isPNGFile =
false;
550 bool dirOnly = file.isDir();
552 QString entryName = root;
559 entryName.append(file.fileName());
561 QString ext = file.completeSuffix().toLower();
562 isPNGFile = ext ==
"png";
573 level = detectCompressionByMime(ext);
576 level = detectCompressionByMime(ext);
594 bool encrypt = !dirOnly && !
password.isEmpty();
598 QDateTime dt = file.lastModified();
600 h->
modDate[1] = ((d.year() - 1980) << 1) & 254;
601 h->
modDate[1] |= ((d.month() >> 3) & 1);
602 h->
modDate[0] = ((d.month() & 7) << 5) & 224;
606 h->
modTime[1] = (t.hour() << 3) & 248;
607 h->
modTime[1] |= ((t.minute() >> 3) & 7);
608 h->
modTime[0] = ((t.minute() & 7) << 5) & 224;
609 h->
modTime[0] |= t.second() / 2;
611 h->
szUncomp = dirOnly ? 0 : file.size();
616 buffer1[0] =
'P'; buffer1[1] =
'K';
617 buffer1[2] = 0x3; buffer1[3] = 0x4;
654 QByteArray entryNameBytes = entryName.toLatin1();
655 int sz = entryNameBytes.size();
674 if (device->write(entryNameBytes) != sz)
681 quint32 keys[3] = { 0, 0, 0 };
689 srand(time(NULL) ^ 3141592654UL);
693 for (
int i=0; i<10; ++i)
695 randByte = (rand() >> 7) & 0xff;
696 buffer1[i] = decryptByte(keys[2]) ^ randByte;
697 updateKeys(keys, randByte);
702 for (
int i=0; i<10; ++i)
704 randByte = decryptByte(keys[2]);
705 updateKeys(keys, buffer1[i]);
706 buffer1[i] ^= randByte;
711 randByte = decryptByte(keys[2]);
712 updateKeys(keys, h->
modTime[0]);
713 buffer1[10] ^= randByte;
715 randByte = decryptByte(keys[2]);
716 updateKeys(keys, h->
modTime[1]);
717 buffer1[11] ^= randByte;
728 quint32 crc = crc32(0L, Z_NULL, 0);
732 QFile actualFile(file.absoluteFilePath());
733 if (!actualFile.open(QIODevice::ReadOnly))
735 qDebug() << QString(
"An error occurred while opening %1").arg(file.absoluteFilePath());
742 qint64 toRead = actualFile.size();
748 crc = crc32(crc, uBuffer, read);
751 encryptBytes(keys, buffer1, read);
753 if ( (written = device->write(buffer1, read)) != read )
766 zstr.zalloc = Z_NULL;
768 zstr.opaque = Z_NULL;
773 if ((zret = deflateInit2_(
779 isPNGFile ? Z_RLE : Z_DEFAULT_STRATEGY,
785 qDebug() <<
"Could not initialize zlib for compression";
792 int flush = Z_NO_FLUSH;
805 qDebug() << QString(
"Error while reading %1").arg(file.absoluteFilePath());
810 crc = crc32(crc, uBuffer, read);
812 zstr.next_in = (Bytef*) buffer1;
813 zstr.avail_in = (uInt)read;
817 flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH;
823 zstr.next_out = (Bytef*) buffer2;
826 zret = deflate(&zstr, flush);
828 Q_ASSERT(zret != Z_STREAM_ERROR);
834 encryptBytes(keys, buffer2, compressed);
836 if (device->write(buffer2, compressed) != compressed)
840 qDebug() << QString(
"Error while writing %1").arg(file.absoluteFilePath());
845 written += compressed;
847 }
while (zstr.avail_out == 0);
850 Q_ASSERT(zstr.avail_in == 0);
852 }
while (flush != Z_FINISH);
855 Q_ASSERT(zret == Z_STREAM_END);
865 quint32 current = device->pos();
868 if (!device->seek(crcOffset))
874 h->
crc = dirOnly ? 0 : crc;
877 setULong(h->
crc, buffer1, 0);
878 setULong(h->
szComp, buffer1, 4);
879 if ( device->write(buffer1, 8) != 8)
886 if (!device->seek(current))
892 if ((h->
gpFlag[0] & 8) == 8)
918 headers->insert(entryName, h);
925 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
926 return (
int)(((temp * (temp ^ 1)) >> 8) & 0xff);
932 buffer[offset+3] = ((v >> 24) & 0xFF);
933 buffer[offset+2] = ((v >> 16) & 0xFF);
934 buffer[offset+1] = ((v >> 8) & 0xFF);
935 buffer[offset] = (v & 0xFF);
943 keys[0] = 305419896L;
944 keys[1] = 591751049L;
945 keys[2] = 878082192L;
948 QByteArray pwdBytes =
password.toLatin1();
949 int sz = pwdBytes.size();
950 const char* ascii = pwdBytes.data();
952 for (
int i=0; i<sz; ++i)
953 updateKeys(keys, (
int)ascii[i]);
959 keys[0] =
CRC32(keys[0], c);
960 keys[1] += keys[0] & 0xff;
961 keys[1] = keys[1] * 134775813L + 1;
962 keys[2] =
CRC32(keys[2], ((
int)keys[1]) >> 24);
970 for (
int i=0; i<(int)read; ++i)
973 buffer[i] ^= decryptByte(keys[2]);
982 if ((ext ==
"png") ||
1002 if ((ext ==
"exe") ||
1028 quint32 szCentralDir = 0;
1029 quint32 offCentralDir = device->pos();
1031 for (QMap<QString,ZipEntryP*>::ConstIterator itr = headers->constBegin(); itr != headers->constEnd(); ++itr)
1075 QByteArray fileNameBytes = itr.key().toLatin1();
1076 sz = fileNameBytes.size();
1112 if ((
unsigned int)device->write(fileNameBytes) != sz)
1142 sz = headers->count();
1158 QByteArray commentBytes = comment.toLatin1();
1159 quint16 commentLength = commentBytes.size();
1161 if (commentLength == 0)
1181 if (commentLength != 0)
1183 if ((
unsigned int)device->write(commentBytes) != commentLength)
1204 qDeleteAll(*headers);
1209 delete device; device = 0;
1215 QDir
d(QDir::cleanPath(p));
1222 return d.absolutePath();
ErrorCode createArchive(const QString &file, bool overwrite=true)
#define ZIP_EOCD_OFF_CDDISKNUM
void initKeys(quint32 *keys) const
void setPassword(const QString &pwd)
void updateKeys(quint32 *keys, int c) const
Zip::ErrorCode createEntry(const QFileInfo &file, const QString &root, Zip::CompressionLevel level)
#define ZIP_EOCD_OFF_DISKNUM
void setArchiveComment(const QString &comment)
#define ZIP_EOCD_OFF_CDENTRIES
#define ZIP_CD_OFF_MADEBY
QString archiveComment() const
#define ZIP_EOCD_OFF_CDSIZE
#define ZIP_COMPRESSION_THRESHOLD
Do not store very small files as the compression headers overhead would be to big.
Do not store paths. All the files are put in the (evtl. user defined) root of the zip file...
#define ZIP_EOCD_OFF_ENTRIES
#define ZIP_EOCD_OFF_CDOFF
#define ZIP_LH_OFF_GPFLAG
Zip::ErrorCode createArchive(QIODevice *device)
#define ZIP_EOCD_SIZE
End of Central Directory record size (signature included)
#define ZIP_LOCAL_ENC_HEADER_SIZE
Encryption header size.
#define ZIP_VERSION
PKZip version for archives created by this API.
QString password() const
Returns the currently used password.
#define ZIP_CD_SIZE
Central Directory record size (signature included)
QString extractRoot(const QString &p)
#define ZIP_EOCD_OFF_COMMLEN
#define CRC32(c, b)
This macro updates a one-char-only CRC; it's the Info-Zip macro re-adapted.
Zip::CompressionLevel detectCompressionByMime(const QString &ext)
#define ZIP_CD_OFF_DISKSTART
#define ZIP_CD_OFF_VERSION
void encryptBytes(quint32 *keys, char *buffer, qint64 read)
ErrorCode addDirectory(const QString &path, CompressionOptions options=RelativePaths, CompressionLevel level=AutoFull)
int decryptByte(quint32 key2) const
ErrorCode addDirectoryContents(const QString &path, CompressionLevel level=AutoFull)
#define ZIP_CD_OFF_GPFLAG
Zip::ErrorCode closeArchive()
#define ZIP_LOCAL_HEADER_SIZE
Local header size (including signature, excluding variable length fields)
void setULong(quint32 v, char *buffer, unsigned int offset)
#define ZIP_CD_OFF_NAMELEN
#define ZIP_LH_OFF_NAMELEN
#define ZIP_CD_OFF_COMMLEN
void clearPassword()
Convenience method, clears the current password.
#define ZIP_DD_SIZE_WS
Data descriptor size (signature included)
QString formatError(ErrorCode c) const
Does not preserve absolute paths in the zip file when adding a file/directory (default) ...