mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-18 12:04:13 +02:00
Merge pull request #1467 from ispyisail/fix/qetz-zip-entry-date
Fix the dates of the files inside a .qetz project
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void setError(QString *error, const QString &text)
|
void setError(QString *error, const QString &text)
|
||||||
@@ -44,6 +45,42 @@ bool safeName(const QString &name)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Built with MINIZ_NO_TIME, miniz leaves each entry's DOS date at 0,
|
||||||
|
//day 0 of month 0, which is no date: unzip shows 30 Nov 1979, other
|
||||||
|
//tools 13 Dec 1901. Give every entry 1 Jan 1980 00:00 instead, the
|
||||||
|
//first valid DOS date, so the bytes still do not depend on the time.
|
||||||
|
constexpr quint16 EntryDosDate = (1 << 5) | 1;
|
||||||
|
|
||||||
|
bool setEntryDates(QByteArray *bytes)
|
||||||
|
{
|
||||||
|
char *data = bytes->data();
|
||||||
|
mz_zip_archive zip;
|
||||||
|
mz_zip_zero_struct(&zip);
|
||||||
|
if (!mz_zip_reader_init_mem(&zip, data, size_t(bytes->size()), 0)) return false;
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
const mz_uint count = mz_zip_reader_get_num_files(&zip);
|
||||||
|
for (mz_uint i = 0 ; i < count && ok ; ++i)
|
||||||
|
{
|
||||||
|
mz_zip_archive_file_stat stat;
|
||||||
|
if (!mz_zip_reader_file_stat(&zip, i, &stat)) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//date at offset 12 of the local header, 14 of the central one
|
||||||
|
const quint64 local = stat.m_local_header_ofs + 12;
|
||||||
|
const quint64 central = zip.m_central_directory_file_ofs + stat.m_central_dir_ofs + 14;
|
||||||
|
if (local + 2 > quint64(bytes->size()) || central + 2 > quint64(bytes->size())) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qToLittleEndian<quint16>(EntryDosDate, data + local);
|
||||||
|
qToLittleEndian<quint16>(EntryDosDate, data + central);
|
||||||
|
}
|
||||||
|
mz_zip_reader_end(&zip);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,8 +124,12 @@ QByteArray QetZip::toBytes(const QList<Entry> &entries, QString *error)
|
|||||||
mz_zip_writer_end(&zip);
|
mz_zip_writer_end(&zip);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const QByteArray bytes(static_cast<const char *>(buffer), qsizetype(size));
|
QByteArray bytes(static_cast<const char *>(buffer), qsizetype(size));
|
||||||
mz_zip_writer_end(&zip); //frees buffer
|
mz_zip_writer_end(&zip); //frees buffer
|
||||||
|
if (!setEntryDates(&bytes)) {
|
||||||
|
setError(error, QStringLiteral("cannot date the zip entries"));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
megabytes, not gigabytes -- and written through QSaveFile, so a failed
|
megabytes, not gigabytes -- and written through QSaveFile, so a failed
|
||||||
save leaves the previous file as it was.
|
save leaves the previous file as it was.
|
||||||
|
|
||||||
Entries carry no timestamp: saving the same project twice gives the
|
Every entry is dated 1 Jan 1980 00:00, not the time of saving: saving
|
||||||
same bytes.
|
the same project twice gives the same bytes.
|
||||||
*/
|
*/
|
||||||
namespace QetZip
|
namespace QetZip
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
#include "container/qetzip.h"
|
#include "container/qetzip.h"
|
||||||
#include "container/miniz/miniz.h"
|
#include "container/miniz/miniz.h"
|
||||||
@@ -72,6 +73,33 @@ private slots:
|
|||||||
QCOMPARE(QetZip::toBytes(sample()), QetZip::toBytes(sample()));
|
QCOMPARE(QetZip::toBytes(sample()), QetZip::toBytes(sample()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// every entry carries a valid date in both of its headers, not the
|
||||||
|
// 0 miniz writes without a clock (shown as 1979 or 1901 by unzip tools)
|
||||||
|
void entriesHaveValidDate()
|
||||||
|
{
|
||||||
|
const QByteArray zip = QetZip::toBytes(sample());
|
||||||
|
QVERIFY(!zip.isEmpty());
|
||||||
|
mz_zip_archive archive;
|
||||||
|
mz_zip_zero_struct(&archive);
|
||||||
|
QVERIFY(mz_zip_reader_init_mem(&archive, zip.constData(), size_t(zip.size()), 0));
|
||||||
|
const mz_uint count = mz_zip_reader_get_num_files(&archive);
|
||||||
|
QCOMPARE(int(count), sample().size());
|
||||||
|
for (mz_uint i = 0 ; i < count ; ++i) {
|
||||||
|
mz_zip_archive_file_stat stat;
|
||||||
|
QVERIFY(mz_zip_reader_file_stat(&archive, i, &stat));
|
||||||
|
const char *local = zip.constData() + stat.m_local_header_ofs;
|
||||||
|
const char *central = zip.constData() + archive.m_central_directory_file_ofs + stat.m_central_dir_ofs;
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(local), quint16(0x50 | 0x4b << 8)); // "PK"
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(central), quint16(0x50 | 0x4b << 8));
|
||||||
|
// 1 Jan 1980: day 1, month 1, year 0 since 1980; time 00:00:00
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(local + 12), quint16(0x21));
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(central + 14), quint16(0x21));
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(local + 10), quint16(0));
|
||||||
|
QCOMPARE(qFromLittleEndian<quint16>(central + 12), quint16(0));
|
||||||
|
}
|
||||||
|
mz_zip_reader_end(&archive);
|
||||||
|
}
|
||||||
|
|
||||||
void refusesUnsafeNamesOnWrite_data()
|
void refusesUnsafeNamesOnWrite_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("name");
|
QTest::addColumn<QString>("name");
|
||||||
|
|||||||
Reference in New Issue
Block a user