10 #ifndef QT_NO_TEXTODFWRITER
12 #include "MarbleZipReader.h"
13 #include "MarbleZipWriter.h"
15 #include <qplatformdefs.h>
24 # define S_IFREG 0100000
26 # define S_IFDIR 0040000
29 # define S_ISDIR(x) ((x) & S_IFDIR) > 0
32 # define S_ISREG(x) ((x) & 0170000) == S_IFREG
34 # define S_IFLNK 020000
35 # define S_ISLNK(x) ((x) & S_IFLNK) > 0
56 #define ZDEBUG if (0) qDebug
61 static inline uint readUInt(
const uchar *data)
63 return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
66 static inline ushort readUShort(
const uchar *data)
68 return (data[0]) + (data[1]<<8);
71 static inline void writeUInt(uchar *data, uint i)
74 data[1] = (i>>8) & 0xff;
75 data[2] = (i>>16) & 0xff;
76 data[3] = (i>>24) & 0xff;
79 static inline void writeUShort(uchar *data, ushort i)
82 data[1] = (i>>8) & 0xff;
85 static inline void copyUInt(uchar *dest,
const uchar *src)
93 static inline void copyUShort(uchar *dest,
const uchar *src)
99 static void writeMSDosDate(uchar *dest,
const QDateTime& dt)
107 dest[0] = time & 0xff;
115 dest[2] = char(date);
116 dest[3] = char(date >> 8);
155 static int inflate(Bytef *dest, ulong *destLen,
const Bytef *source, ulong sourceLen)
160 stream.next_in = (Bytef*)source;
161 stream.avail_in = (uInt)sourceLen;
162 if ((uLong)stream.avail_in != sourceLen)
165 stream.next_out = dest;
166 stream.avail_out = (uInt)*destLen;
167 if ((uLong)stream.avail_out != *destLen)
170 stream.zalloc = (alloc_func)
nullptr;
171 stream.zfree = (free_func)
nullptr;
173 err = inflateInit2(&stream, -MAX_WBITS);
177 err = inflate(&stream, Z_FINISH);
178 if (err != Z_STREAM_END) {
180 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
184 *destLen = stream.total_out;
186 err = inflateEnd(&stream);
190 static int deflate (Bytef *dest, ulong *destLen,
const Bytef *source, ulong sourceLen)
195 stream.next_in = (Bytef*)source;
196 stream.avail_in = (uInt)sourceLen;
197 stream.next_out = dest;
198 stream.avail_out = (uInt)*destLen;
199 if ((uLong)stream.avail_out != *destLen)
return Z_BUF_ERROR;
201 stream.zalloc = (alloc_func)
nullptr;
202 stream.zfree = (free_func)
nullptr;
203 stream.opaque = (voidpf)
nullptr;
205 err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
206 if (err != Z_OK)
return err;
208 err = deflate(&stream, Z_FINISH);
209 if (err != Z_STREAM_END) {
211 return err == Z_OK ? Z_BUF_ERROR : err;
213 *destLen = stream.total_out;
215 err = deflateEnd(&stream);
249 static QDateTime readMSDosDate(
const uchar *src)
251 uint dosDate = readUInt(src);
253 uDate = (quint64)(dosDate >> 16);
254 uint tm_mday = (uDate & 0x1f);
255 uint tm_mon = ((uDate & 0x1E0) >> 5);
256 uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
257 uint tm_hour = ((dosDate & 0xF800) >> 11);
258 uint tm_min = ((dosDate & 0x7E0) >> 5);
259 uint tm_sec = ((dosDate & 0x1f) << 1);
264 struct LocalFileHeader
267 uchar version_needed[2];
268 uchar general_purpose_bits[2];
269 uchar compression_method[2];
270 uchar last_mod_file[4];
272 uchar compressed_size[4];
273 uchar uncompressed_size[4];
274 uchar file_name_length[2];
275 uchar extra_field_length[2];
278 struct DataDescriptor
281 uchar compressed_size[4];
282 uchar uncompressed_size[4];
285 struct CentralFileHeader
288 uchar version_made[2];
289 uchar version_needed[2];
290 uchar general_purpose_bits[2];
291 uchar compression_method[2];
292 uchar last_mod_file[4];
294 uchar compressed_size[4];
295 uchar uncompressed_size[4];
296 uchar file_name_length[2];
297 uchar extra_field_length[2];
298 uchar file_comment_length[2];
300 uchar internal_file_attributes[2];
301 uchar external_file_attributes[4];
302 uchar offset_local_header[4];
303 LocalFileHeader toLocalHeader()
const;
306 struct EndOfDirectory
310 uchar start_of_directory_disk[2];
311 uchar num_dir_entries_this_disk[2];
312 uchar num_dir_entries[2];
313 uchar directory_size[4];
314 uchar dir_start_offset[4];
315 uchar comment_length[2];
326 MarbleZipReader::FileInfo::FileInfo()
327 : isDir(false), isFile(false), isSymLink(false), crc32(0), size(0)
331 MarbleZipReader::FileInfo::~FileInfo()
335 MarbleZipReader::FileInfo::FileInfo(
const FileInfo &other)
340 MarbleZipReader::FileInfo& MarbleZipReader::FileInfo::operator=(
const FileInfo &other)
342 filePath = other.filePath;
344 isFile = other.isFile;
345 isSymLink = other.isSymLink;
346 permissions = other.permissions;
349 lastModified = other.lastModified;
353 bool MarbleZipReader::FileInfo::isValid()
const
355 return isDir || isFile || isSymLink;
361 QZipPrivate(
QIODevice *device,
bool ownDev)
362 : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
372 void fillFileInfo(
int index, MarbleZipReader::FileInfo &fileInfo)
const;
379 uint start_of_directory;
382 void QZipPrivate::fillFileInfo(
int index, MarbleZipReader::FileInfo &fileInfo)
const
384 FileHeader header = fileHeaders.
at(index);
386 const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
388 fileInfo.isDir =
false;
389 fileInfo.isFile =
true;
390 fileInfo.isSymLink =
false;
393 fileInfo.isDir = S_ISDIR(mode);
394 fileInfo.isFile = S_ISREG(mode);
395 fileInfo.isSymLink = S_ISLNK(mode);
396 fileInfo.permissions = modeToPermissions(mode);
398 fileInfo.crc32 = readUInt(header.h.crc_32);
399 fileInfo.size = readUInt(header.h.uncompressed_size);
400 fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
403 class MarbleZipReaderPrivate :
public QZipPrivate
406 MarbleZipReaderPrivate(
QIODevice *device,
bool ownDev)
407 : QZipPrivate(device, ownDev),
status(MarbleZipReader::
NoError)
413 MarbleZipReader::Status
status;
416 class MarbleZipWriterPrivate :
public QZipPrivate
419 MarbleZipWriterPrivate(
QIODevice *device,
bool ownDev)
420 : QZipPrivate(device, ownDev),
422 permissions(
QFile::ReadOwner |
QFile::WriteOwner),
423 compressionPolicy(MarbleZipWriter::AlwaysCompress)
427 MarbleZipWriter::Status
status;
429 MarbleZipWriter::CompressionPolicy compressionPolicy;
431 enum EntryType { Directory,
File, Symlink };
433 void addEntry(EntryType type,
const QString &fileName,
const QByteArray &contents);
436 LocalFileHeader CentralFileHeader::toLocalHeader()
const
439 writeUInt(h.signature, 0x04034b50);
440 copyUShort(h.version_needed, version_needed);
441 copyUShort(h.general_purpose_bits, general_purpose_bits);
442 copyUShort(h.compression_method, compression_method);
443 copyUInt(h.last_mod_file, last_mod_file);
444 copyUInt(h.crc_32, crc_32);
445 copyUInt(h.compressed_size, compressed_size);
446 copyUInt(h.uncompressed_size, uncompressed_size);
447 copyUShort(h.file_name_length, file_name_length);
448 copyUShort(h.extra_field_length, extra_field_length);
452 void MarbleZipReaderPrivate::scanFiles()
458 status = MarbleZipReader::FileOpenError;
463 status = MarbleZipReader::FileReadError;
467 dirtyFileTree =
false;
469 device->read((
char *)tmp, 4);
470 if (readUInt(tmp) != 0x04034b50) {
471 qWarning() <<
"QZip: not a zip file!";
477 int start_of_directory = -1;
478 int num_dir_entries = 0;
480 while (start_of_directory == -1) {
481 int pos = device->size() -
sizeof(EndOfDirectory) - i;
482 if (pos < 0 || i > 65535) {
483 qWarning() <<
"QZip: EndOfDirectory not found";
488 device->read((
char *)&eod,
sizeof(EndOfDirectory));
489 if (readUInt(eod.signature) == 0x06054b50)
495 start_of_directory = readUInt(eod.dir_start_offset);
496 num_dir_entries = readUShort(eod.num_dir_entries);
497 ZDEBUG(
"start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
498 int comment_length = readUShort(eod.comment_length);
499 if (comment_length != i)
500 qWarning() <<
"QZip: failed to parse zip file.";
501 comment = device->read(qMin(comment_length, i));
504 device->seek(start_of_directory);
505 for (i = 0; i < num_dir_entries; ++i) {
507 int read = device->read((
char *) &header.h,
sizeof(CentralFileHeader));
508 if (read < (
int)
sizeof(CentralFileHeader)) {
509 qWarning() <<
"QZip: Failed to read complete header, index may be incomplete";
512 if (readUInt(header.h.signature) != 0x02014b50) {
513 qWarning() <<
"QZip: invalid header signature, index may be incomplete";
517 int l = readUShort(header.h.file_name_length);
518 header.file_name = device->read(l);
519 if (header.file_name.length() != l) {
520 qWarning() <<
"QZip: Failed to read filename from zip index, index may be incomplete";
523 l = readUShort(header.h.extra_field_length);
524 header.extra_field = device->read(l);
525 if (header.extra_field.length() != l) {
526 qWarning() <<
"QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
529 l = readUShort(header.h.file_comment_length);
530 header.file_comment = device->read(l);
531 if (header.file_comment.length() != l) {
532 qWarning() <<
"QZip: Failed to read file comment, index may be incomplete";
536 ZDEBUG(
"found file '%s'", header.file_name.data());
537 fileHeaders.append(header);
541 void MarbleZipWriterPrivate::addEntry(EntryType type,
const QString &fileName,
const QByteArray &contents)
544 static const char *entryTypes[] = {
552 status = MarbleZipWriter::FileOpenError;
555 device->seek(start_of_directory);
558 MarbleZipWriter::CompressionPolicy compression = compressionPolicy;
559 if (compressionPolicy == MarbleZipWriter::AutoCompress) {
560 if (contents.
length() < 64)
561 compression = MarbleZipWriter::NeverCompress;
563 compression = MarbleZipWriter::AlwaysCompress;
567 memset(&header.h, 0,
sizeof(CentralFileHeader));
568 writeUInt(header.h.signature, 0x02014b50);
570 writeUShort(header.h.version_needed, 0x14);
571 writeUInt(header.h.uncompressed_size, contents.
length());
574 if (compression == MarbleZipWriter::AlwaysCompress) {
575 writeUShort(header.h.compression_method, 8);
577 ulong len = contents.
length();
579 len += (len >> 12) + (len >> 14) + 11;
583 res = deflate((uchar*)data.
data(), &len, (
const uchar*)contents.
constData(), contents.
length());
590 qWarning(
"QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
597 }
while (res == Z_BUF_ERROR);
600 writeUInt(header.h.compressed_size, data.
length());
601 uint crc_32 = ::crc32(0,
nullptr, 0);
602 crc_32 = ::crc32(crc_32, (
const uchar *)contents.
constData(), contents.
length());
603 writeUInt(header.h.crc_32, crc_32);
606 if (header.file_name.size() > 0xffff) {
607 qWarning(
"QZip: Filename too long, chopping it to 65535 characters");
608 header.file_name = header.file_name.left(0xffff);
610 writeUShort(header.h.file_name_length, header.file_name.length());
613 writeUShort(header.h.version_made, 3 << 8);
616 quint32 mode = permissionsToMode(permissions);
618 case File: mode |= S_IFREG;
break;
619 case Directory: mode |= S_IFDIR;
break;
620 case Symlink: mode |= S_IFLNK;
break;
622 writeUInt(header.h.external_file_attributes, mode << 16);
623 writeUInt(header.h.offset_local_header, start_of_directory);
626 fileHeaders.append(header);
628 LocalFileHeader h = header.h.toLocalHeader();
629 device->write((
const char *)&h,
sizeof(LocalFileHeader));
630 device->write(header.file_name);
632 start_of_directory = device->pos();
633 dirtyFileTree =
true;
708 MarbleZipReader::Status
status;
717 status = FilePermissionsError;
722 d =
new MarbleZipReaderPrivate(f.data(),
true);
732 MarbleZipReader::MarbleZipReader(
QIODevice *device)
733 : d(new MarbleZipReaderPrivate(device, false))
741 MarbleZipReader::~MarbleZipReader()
750 QIODevice* MarbleZipReader::device()
const
758 bool MarbleZipReader::isReadable()
const
766 bool MarbleZipReader::exists()
const
768 QFile *f = qobject_cast<QFile*> (d->device);
781 for (
int i = 0; i < d->fileHeaders.size(); ++i) {
782 MarbleZipReader::FileInfo fi;
783 d->fillFileInfo(i, fi);
793 int MarbleZipReader::count()
const
796 return d->fileHeaders.
count();
806 MarbleZipReader::FileInfo MarbleZipReader::entryInfoAt(
int index)
const
809 MarbleZipReader::FileInfo fi;
810 if (index >= 0 && index < d->fileHeaders.count())
811 d->fillFileInfo(index, fi);
822 for (i = 0; i < d->fileHeaders.size(); ++i) {
826 if (i == d->fileHeaders.size())
829 FileHeader header = d->fileHeaders.at(i);
831 int compressed_size = readUInt(header.h.compressed_size);
832 int uncompressed_size = readUInt(header.h.uncompressed_size);
833 int start = readUInt(header.h.offset_local_header);
836 d->device->seek(
start);
838 d->device->read((
char *)&lh,
sizeof(LocalFileHeader));
839 uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
840 d->device->seek(d->device->pos() + skip);
842 int compression_method = readUShort(lh.compression_method);
846 QByteArray compressed = d->device->read(compressed_size);
847 if (compression_method == 0) {
849 compressed.
truncate(uncompressed_size);
851 }
else if (compression_method == 8) {
854 compressed.
truncate(compressed_size);
856 ulong len = qMax(uncompressed_size, 1);
860 res = inflate((uchar*)baunzip.
data(), &len,
861 (uchar*)compressed.
constData(), compressed_size);
865 if ((
int)len != baunzip.
size())
869 qWarning(
"QZip: Z_MEM_ERROR: Not enough memory");
875 qWarning(
"QZip: Z_DATA_ERROR: Input data is corrupted");
878 }
while (res == Z_BUF_ERROR);
881 qWarning() <<
"QZip: Unknown compression method";
890 bool MarbleZipReader::extractAll(
const QString &destinationDir)
const
892 QDir baseDir(destinationDir);
896 for (
const FileInfo& fi: allFiles) {
899 if (!baseDir.mkpath(fi.filePath))
907 for (
const FileInfo& fi: allFiles) {
925 for (
const FileInfo& fi: allFiles) {
932 f.
write(fileData(fi.filePath));
965 void MarbleZipReader::close()
994 MarbleZipWriter::Status
status;
996 status = MarbleZipWriter::NoError;
999 status = MarbleZipWriter::FileWriteError;
1001 status = MarbleZipWriter::FileOpenError;
1003 status = MarbleZipWriter::FilePermissionsError;
1005 status = MarbleZipWriter::FileError;
1008 d =
new MarbleZipWriterPrivate(f.data(),
true);
1018 MarbleZipWriter::MarbleZipWriter(
QIODevice *device)
1019 : d(new MarbleZipWriterPrivate(device, false))
1024 MarbleZipWriter::~MarbleZipWriter()
1033 QIODevice* MarbleZipWriter::device()
const
1041 bool MarbleZipWriter::isWritable()
const
1049 bool MarbleZipWriter::exists()
const
1051 QFile *f = qobject_cast<QFile*> (d->device);
1102 void MarbleZipWriter::setCompressionPolicy(CompressionPolicy policy)
1104 d->compressionPolicy = policy;
1112 MarbleZipWriter::CompressionPolicy MarbleZipWriter::compressionPolicy()
const
1114 return d->compressionPolicy;
1127 d->permissions = permissions;
1138 return d->permissions;
1169 bool opened =
false;
1173 d->status = FileOpenError;
1186 void MarbleZipWriter::addDirectory(
const QString &dirName)
1192 d->addEntry(MarbleZipWriterPrivate::Directory, name,
QByteArray());
1200 void MarbleZipWriter::addSymLink(
const QString &fileName,
const QString &destination)
1208 void MarbleZipWriter::close()
1216 d->device->seek(d->start_of_directory);
1218 for (
int i = 0; i < d->fileHeaders.size(); ++i) {
1219 const FileHeader &header = d->fileHeaders.at(i);
1220 d->device->write((
const char *)&header.h,
sizeof(CentralFileHeader));
1221 d->device->write(header.file_name);
1222 d->device->write(header.extra_field);
1223 d->device->write(header.file_comment);
1225 int dir_size = d->device->pos() - d->start_of_directory;
1228 memset(&eod, 0,
sizeof(EndOfDirectory));
1229 writeUInt(eod.signature, 0x06054b50);
1232 writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
1233 writeUShort(eod.num_dir_entries, d->fileHeaders.size());
1234 writeUInt(eod.directory_size, dir_size);
1235 writeUInt(eod.dir_start_offset, d->start_of_directory);
1236 writeUShort(eod.comment_length, d->comment.length());
1238 d->device->write((
const char *)&eod,
sizeof(EndOfDirectory));
1239 d->device->write(d->comment);
1245 #endif // QT_NO_TEXTODFWRITER