summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/lib7z_facade.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/lib7z_facade.cpp')
-rw-r--r--src/libs/installer/lib7z_facade.cpp1856
1 files changed, 693 insertions, 1163 deletions
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index 8f21343db..86dc86e7a 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -30,106 +30,199 @@
** $QT_END_LICENSE$
**
**************************************************************************/
+
#include "lib7z_facade.h"
#include "errors.h"
#include "fileio.h"
+#include "lib7z_create.h"
+#include "lib7z_extract.h"
+#include "lib7z_list.h"
+#include "lib7z_guid.h"
+
#ifndef Q_OS_WIN
# include "StdAfx.h"
#endif
-#include "Common/MyInitGuid.h"
+#include <7zCrc.h>
+
+#include <7zip/Archive/IArchive.h>
-#include "7zip/Archive/IArchive.h"
-#include "7zip/UI/Common/OpenArchive.h"
-#include "7zip/UI/Common/Update.h"
+#include <7zip/UI/Common/ArchiveCommandLine.h>
+#include <7zip/UI/Common/OpenArchive.h>
-#include "Windows/FileIO.h"
-#include "Windows/PropVariant.h"
-#include "Windows/PropVariantConversions.h"
+#include <Windows/FileDir.h>
+#include <Windows/FileIO.h>
+#include <Windows/PropVariant.h>
+#include <Windows/PropVariantConv.h>
+#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QIODevice>
-#include <QtCore/QMutexLocker>
#include <QPointer>
-#include <QTemporaryFile>
#include <QReadWriteLock>
+#include <QTemporaryFile>
-#ifdef _MSC_VER
-#pragma warning(disable:4297)
-#endif
+#include <mutex>
+#include <memory>
#ifdef Q_OS_WIN
+HINSTANCE g_hInstance = 0;
+
+# define S_IFMT 00170000
+# define S_IFLNK 0120000
+# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+# define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
+# if !defined(Q_CC_MINGW)
+# include <time.h> // for localtime_s
+# endif
+#else
+extern "C" int global_use_utf16_conversion;
+
+#include <myWindows/config.h>
+#include <sys/stat.h>
+#endif
+
+namespace NArchive {
+ namespace N7z {
+ void registerArcDec7z();
+ }
+ namespace NXz {
+ void registerArcxz();
+ }
+ namespace NSplit {
+ void registerArcSplit();
+ }
+ namespace NLzma {
+ namespace NLzmaAr {
+ void registerArcLzma();
+ }
+ namespace NLzma86Ar {
+ void registerArcLzma86();
+ }
+ }
+}
+using namespace NWindows;
+
+void registerCodecBCJ();
+void registerCodecBCJ2();
+
+void registerCodecLZMA();
+void registerCodecLZMA2();
+
+void registerCodecCopy();
+void registerCodecDelta();
+void registerCodecBranch();
+void registerCodecByteSwap();
+
+namespace Lib7z {
+
-#include <time.h>
-#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
-#define S_IFMT 00170000
-#define S_IFLNK 0120000
-#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+// -- 7z init codecs, archives
-typedef BOOL (WINAPI *CREATEHARDLINK)(LPCSTR dst, LPCSTR str, LPSECURITY_ATTRIBUTES sa);
+std::once_flag gOnceFlag;
-bool CreateHardLinkWrapper(const QString &dest, const QString &file)
+void initSevenZ()
{
- static HMODULE module = 0;
- static CREATEHARDLINK proc = 0;
+ std::call_once(gOnceFlag, [] {
+ CrcGenerateTable();
- if (module == 0)
- module = LoadLibrary(L"kernel32.dll");
- if (module == 0)
- return false;
- if (proc == 0)
- proc = (CREATEHARDLINK) GetProcAddress(module, "CreateHardLinkA");
- if (proc == 0)
- return false;
- QString target = file;
- if (!QFileInfo(file).isAbsolute())
- {
- target = QFileInfo(dest).dir().absoluteFilePath(file);
- }
- const QString link = QDir::toNativeSeparators(dest);
- const QString existingFile = QDir::toNativeSeparators(target);
- return proc(link.toLocal8Bit(), existingFile.toLocal8Bit(), 0);
-}
+ registerCodecBCJ();
+ registerCodecBCJ2();
-#else
-#include <sys/stat.h>
+ registerCodecLZMA();
+ registerCodecLZMA2();
+
+ registerCodecCopy();
+ registerCodecDelta();
+ registerCodecBranch();
+ registerCodecByteSwap();
+
+ NArchive::N7z::registerArcDec7z();
+ NArchive::NXz::registerArcxz();
+ NArchive::NSplit::registerArcSplit();
+ NArchive::NLzma::NLzmaAr::registerArcLzma();
+ NArchive::NLzma::NLzma86Ar::registerArcLzma86();
+
+#ifndef Q_OS_WIN
+# ifdef ENV_HAVE_LOCALE
+ const QByteArray locale = qgetenv("LC_ALL").toUpper();
+ if (!locale.isEmpty() && (locale != "C") && (locale != "POSIX"))
+ global_use_utf16_conversion = 1;
+# elif defined(LOCALE_IS_UTF8)
+ global_use_utf16_conversion = 1;
+# else
+ global_use_utf16_conversion = 0;
+# endif
#endif
+ });
+}
-#include <memory>
-#include <cassert>
+// -- error handling
-using namespace Lib7z;
-using namespace NWindows;
+Q_GLOBAL_STATIC(QString, getLastErrorString)
+Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock)
+QString lastError()
+{
+ QReadLocker locker(lastErrorReadWriteLock());
+ Q_UNUSED(locker)
+ return *getLastErrorString();
+}
-namespace Lib7z {
- Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock)
- Q_GLOBAL_STATIC(QString, getLastErrorString)
+void setLastError(const QString &errorString)
+{
+ QWriteLocker locker(lastErrorReadWriteLock());
+ Q_UNUSED(locker)
+ *getLastErrorString() = errorString;
+}
- QString lastError()
- {
- QReadLocker locker(lastErrorReadWriteLock());
- Q_UNUSED(locker)
- return *getLastErrorString();
- }
+QString errorMessageFrom7zResult(const LONG &extractResult)
+{
+ if (!lastError().isEmpty())
+ return lastError();
- void setLastError(const QString &errorString)
- {
- QWriteLocker locker(lastErrorReadWriteLock());
- Q_UNUSED(locker)
- *getLastErrorString() = errorString;
+ QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1");
+ switch (extractResult) {
+ case S_OK:
+ qFatal("S_OK value is not a valid error code.");
+ break;
+ case E_NOTIMPL:
+ errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL"));
+ break;
+ case E_NOINTERFACE:
+ errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE"));
+ break;
+ case E_ABORT:
+ errorMessage = errorMessage.arg(QLatin1String("E_ABORT"));
+ break;
+ case E_FAIL:
+ errorMessage = errorMessage.arg(QLatin1String("E_FAIL"));
+ break;
+ case STG_E_INVALIDFUNCTION:
+ errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION"));
+ break;
+ case E_OUTOFMEMORY:
+ errorMessage = QCoreApplication::translate("Lib7z", "not enough memory");
+ break;
+ case E_INVALIDARG:
+ errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG"));
+ break;
+ default:
+ errorMessage = QCoreApplication::translate("Lib7z", "Error: %1").arg(extractResult);
+ break;
}
+ return errorMessage;
}
-namespace {
-/**
-* RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
+/*!
+ RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
*/
-struct DirectoryGuard {
+struct DirectoryGuard
+{
explicit DirectoryGuard(const QString &path)
: m_path(path)
, m_created(false)
@@ -144,14 +237,15 @@ struct DirectoryGuard {
return;
QDir dir(m_path);
if (!dir.rmdir(m_path))
- qWarning() << "Could not delete directory " << m_path;
+ qWarning() << "Cannot delete directory " << m_path;
}
- /**
- * Tries to create the directorie structure.
- * Returns a list of every directory created.
+ /*!
+ Tries to create the directory structure.
+ Returns a list of every directory created.
*/
- QStringList tryCreate() {
+ QStringList tryCreate()
+ {
if (m_path.isEmpty())
return QStringList();
@@ -160,13 +254,12 @@ struct DirectoryGuard {
return QStringList();
if (fi.exists() && !fi.isDir()) {
throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Path exists but is not a folder: %1").arg(m_path));
+ "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path)));
}
QStringList created;
QDir toCreate(m_path);
- while (!toCreate.exists())
- {
+ while (!toCreate.exists()) {
QString p = toCreate.absolutePath();
created.push_front(p);
p = p.section(QLatin1Char('/'), 0, -2);
@@ -177,12 +270,13 @@ struct DirectoryGuard {
m_created = dir.mkpath(m_path);
if (!m_created) {
throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Could not create folder: %1").arg(m_path));
+ "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
}
return created;
}
- void release() {
+ void release()
+ {
m_released = true;
}
@@ -190,7 +284,6 @@ struct DirectoryGuard {
bool m_created;
bool m_released;
};
-}
static UString QString2UString(const QString &str)
{
@@ -202,32 +295,12 @@ static QString UString2QString(const UString& str)
return QString::fromStdWString(static_cast<const wchar_t*>(str));
}
-static QString generateTempFileName()
-{
- QTemporaryFile tmp;
- if (!tmp.open()) {
- throw SevenZipException(QCoreApplication::translate("QInstaller",
- "Could not create temporary file"));
- }
- return QDir::toNativeSeparators(tmp.fileName());
-}
-
-/*
-static QStringList UStringVector2QStringList(const UStringVector& vec)
-{
-QStringList res;
-for (int i = 0; i < vec.Size(); ++i)
-res += UString2QString(vec[i]);
-return res;
-}
-*/
-
-static NCOM::CPropVariant readProperty(IInArchive* archive, int index, int propId)
+static NCOM::CPropVariant readProperty(IInArchive *archive, int index, int propId)
{
NCOM::CPropVariant prop;
if (archive->GetProperty(index, propId, &prop) != S_OK) {
- throw SevenZipException(QCoreApplication::translate("QInstaller",
- "Could not retrieve property %1 for item %2").arg(QString::number(propId),
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Cannot retrieve property %1 for item %2.").arg(QString::number(propId),
QString::number(index)));
}
return prop;
@@ -238,193 +311,134 @@ static bool IsFileTimeZero(const FILETIME *lpFileTime)
return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
}
-static bool IsDST(const QDateTime& datetime = QDateTime())
-{
- const time_t seconds = static_cast< time_t >(datetime.isValid() ? datetime.toTime_t()
- : QDateTime::currentDateTime().toTime_t());
-#if defined(Q_OS_WIN) && !defined(Q_CC_MINGW)
- struct tm t;
- localtime_s(&t, &seconds);
-#else
- const struct tm &t = *localtime(&seconds);
-#endif
- return t.tm_isdst;
-}
-
-static bool getFileTimeFromProperty(IInArchive* archive, int index, int propId, FILETIME *fileTime)
+static bool getFileTimeFromProperty(IInArchive* archive, int index, int propId, FILETIME *ft)
{
const NCOM::CPropVariant prop = readProperty(archive, index, propId);
if (prop.vt != VT_FILETIME) {
- throw SevenZipException(QCoreApplication::translate("QInstaller",
- "Property %1 for item %2 not of type VT_FILETIME but %3").arg(QString::number(propId),
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Property %1 for item %2 not of type VT_FILETIME but %3.").arg(QString::number(propId),
QString::number(index), QString::number(prop.vt)));
}
- *fileTime = prop.filetime;
-
- if (IsFileTimeZero(fileTime))
- return false;
- return true;
+ *ft = prop.filetime;
+ return !IsFileTimeZero(ft);
}
-static
-QDateTime getDateTimeProperty(IInArchive* archive, int index, int propId, const QDateTime& defaultValue)
+static bool getDateTimeProperty(IInArchive *arc, int index, int id, QDateTime *value)
{
- FILETIME fileTime;
- if (!getFileTimeFromProperty(archive, index, propId, &fileTime))
- return defaultValue;
+ FILETIME ft7z;
+ if (!getFileTimeFromProperty(arc, index, id, &ft7z))
+ return false;
- FILETIME localFileTime;
- if (!FileTimeToLocalFileTime(&fileTime, &localFileTime)) {
- throw SevenZipException(QCoreApplication::translate("QInstaller",
- "Could not convert file time to local time"));
- }
SYSTEMTIME st;
- if (!BOOLToBool(FileTimeToSystemTime(&localFileTime, &st))) {
- throw SevenZipException(QCoreApplication::translate("QInstaller",
- "Could not convert local file time to system time"));
+ if (!BOOLToBool(FileTimeToSystemTime(&ft7z, &st))) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Cannot convert UTC file time to system time."));
}
- const QDate date(st.wYear, st.wMonth, st.wDay);
- const QTime time(st.wHour, st.wMinute, st.wSecond);
- QDateTime result(date, time);
-
- // fix daylight saving time
- const bool dst = IsDST();
- if (dst != IsDST(result))
- result = result.addSecs(dst ? -3600 : 3600);
-
- return result;
+ *value = QDateTime(QDate(st.wYear, st.wMonth, st.wDay), QTime(st.wHour, st.wMinute,
+ st.wSecond), Qt::UTC);
+ return value->isValid();
}
-static quint64 getUInt64Property(IInArchive* archive, int index, int propId, quint64 defaultValue=0)
+static quint64 getUInt64Property(IInArchive *archive, int index, int propId, quint64 defaultValue)
{
- const NCOM::CPropVariant prop = readProperty(archive, index, propId);
- if (prop.vt == VT_EMPTY)
- return defaultValue;
- return static_cast<quint64>(ConvertPropVariantToUInt64(prop));
+ UInt64 value;
+ if (ConvertPropVariantToUInt64(readProperty(archive, index, propId), value))
+ return value;
+ return defaultValue;
}
-static quint32 getUInt32Property(IInArchive* archive, int index, int propId, quint32 defaultValue=0)
+static quint32 getUInt32Property(IInArchive *archive, int index, int propId, quint32 defaultValue)
{
const NCOM::CPropVariant prop = readProperty(archive, index, propId);
if (prop.vt == VT_EMPTY)
return defaultValue;
- return static_cast< quint32 >(prop.ulVal);
+ return static_cast<quint32>(prop.ulVal);
}
-static QFile::Permissions getPermissions(IInArchive* archive, int index, bool* hasPermissions = 0)
+static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions)
{
quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
QFile::Permissions permissions = 0;
if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
if (hasPermissions != 0)
*hasPermissions = true;
- // filter the unix permissions
+ // filter the Unix permissions
attributes = (attributes >> 16) & 0777;
- permissions |= static_cast< QFile::Permissions >((attributes & 0700) << 2); // owner rights
- permissions |= static_cast< QFile::Permissions >((attributes & 0070) << 1); // group
- permissions |= static_cast< QFile::Permissions >((attributes & 0007) << 0); // and world rights
+ permissions |= static_cast<QFile::Permissions>((attributes & 0700) << 2); // owner rights
+ permissions |= static_cast<QFile::Permissions>((attributes & 0070) << 1); // group
+ permissions |= static_cast<QFile::Permissions>((attributes & 0007) << 0); // and world rights
} else if (hasPermissions != 0) {
*hasPermissions = false;
}
return permissions;
}
-namespace Lib7z {
+#define LIB7Z_ASSERTS(X, MODE) \
+ Q_ASSERT(X); \
+ Q_ASSERT(X->isOpen()); \
+ Q_ASSERT(X->is ## MODE()); \
+ Q_ASSERT(!X->isSequential());
+
class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp
{
-public:
- enum Behavior {
- KeepDeviceUntouched,
- CloseAndDeleteDevice
- };
+ Q_DISABLE_COPY(QIODeviceSequentialOutStream)
+public:
MY_UNKNOWN_IMP
- explicit QIODeviceSequentialOutStream(QIODevice* device, Behavior behavior);
- ~QIODeviceSequentialOutStream();
- QString errorString() const;
- /* reimp */ STDMETHOD(Write)(const void* data, UInt32 size, UInt32* processedSize);
-
-private:
- Behavior m_behavior;
- QString m_errorString;
- QPointer<QIODevice> m_device;
-};
-
-QIODeviceSequentialOutStream::QIODeviceSequentialOutStream(QIODevice* device, Behavior behavior)
- : ISequentialOutStream()
- , CMyUnknownImp()
- , m_behavior(behavior)
- , m_device(device)
-{
- Q_ASSERT(m_device);
-
- if (!device->isOpen() && !m_device->open(QIODevice::WriteOnly))
- m_errorString = m_device->errorString();
-}
-
-QIODeviceSequentialOutStream::~QIODeviceSequentialOutStream()
-{
- if (m_behavior == CloseAndDeleteDevice) {
- m_device->close();
- delete m_device;
- m_device = 0;
+ explicit QIODeviceSequentialOutStream(std::unique_ptr<QIODevice> device)
+ : ISequentialOutStream()
+ , m_device(std::move(device))
+ {
+ LIB7Z_ASSERTS(m_device, Writable)
}
-}
-QString QIODeviceSequentialOutStream::errorString() const
-{
- return m_errorString;
-}
+ QString errorString() const {
+ return m_errorString;
+ }
-HRESULT QIODeviceSequentialOutStream::Write(const void* data, UInt32 size, UInt32* processedSize)
-{
- if (!m_device) {
+ STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize)
+ {
if (processedSize)
*processedSize = 0;
- m_errorString = QCoreApplication::translate("QIODeviceSequentialOutStream",
- "No device set for output stream");
- return E_FAIL;
- }
- if (!m_device->isOpen()) {
- const bool opened = m_device->open(QIODevice::WriteOnly);
- if (!opened) {
- if (processedSize)
- *processedSize = 0;
+
+ const qint64 written = m_device->write(reinterpret_cast<const char*>(data), size);
+ if (written == -1) {
m_errorString = m_device->errorString();
return E_FAIL;
}
- }
- const qint64 written = m_device->write(reinterpret_cast<const char*>(data), size);
- if (written == -1) {
if (processedSize)
- *processedSize = 0;
- m_errorString = m_device->errorString();
- return E_FAIL;
+ *processedSize = written;
+ m_errorString.clear();
+ return S_OK;
}
- if (processedSize)
- *processedSize = written;
- m_errorString = QString();
- return S_OK;
-}
+private:
+ QString m_errorString;
+ std::unique_ptr<QIODevice> m_device;
+};
class QIODeviceInStream : public IInStream, public CMyUnknownImp
{
+ Q_DISABLE_COPY(QIODeviceInStream)
+
public:
MY_UNKNOWN_IMP
- explicit QIODeviceInStream(QIODevice* device) : IInStream(), CMyUnknownImp(), m_device(device)
+ explicit QIODeviceInStream(QIODevice *device)
+ : IInStream()
+ , CMyUnknownImp()
+ , m_device(device)
{
- assert(m_device);
- assert(!m_device->isSequential());
+ LIB7Z_ASSERTS(m_device, Readable)
}
- /* reimp */ STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize)
+ STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize)
{
- assert(m_device);
- assert(m_device->isReadable());
+ if (m_device.isNull())
+ return E_FAIL;
+
const qint64 actual = m_device->read(reinterpret_cast<char*>(data), size);
Q_ASSERT(actual != 0 || m_device->atEnd());
if (processedSize)
@@ -432,29 +446,28 @@ public:
return actual >= 0 ? S_OK : E_FAIL;
}
- /* reimp */ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition)
+ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
- assert(m_device);
- assert(!m_device->isSequential());
- assert(m_device->isReadable());
+ if (m_device.isNull())
+ return E_FAIL;
if (seekOrigin > STREAM_SEEK_END)
return STG_E_INVALIDFUNCTION;
UInt64 np = 0;
- switch(seekOrigin) {
- case STREAM_SEEK_SET:
- np = offset;
- break;
- case STREAM_SEEK_CUR:
- np = m_device->pos() + offset;
- break;
- case STREAM_SEEK_END:
- np = m_device->size() + offset;
- break;
- default:
- return STG_E_INVALIDFUNCTION;
+ switch (seekOrigin) {
+ case STREAM_SEEK_SET:
+ np = offset;
+ break;
+ case STREAM_SEEK_CUR:
+ np = m_device->pos() + offset;
+ break;
+ case STREAM_SEEK_END:
+ np = m_device->size() + offset;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
}
- np = qBound(static_cast<UInt64>(0), np, static_cast<UInt64>(m_device->size() - 1));
+ np = qBound(static_cast<UInt64>(0), np, static_cast<UInt64>(m_device->size()));
const bool ok = m_device->seek(np);
if (newPosition)
*newPosition = np;
@@ -464,1129 +477,646 @@ public:
private:
QPointer<QIODevice> m_device;
};
-}
-
-File::File()
- : permissions(0)
- , uncompressedSize(0)
- , compressedSize(0)
- , isDirectory(false)
-{
-}
-
-QVector<File> File::subtreeInPreorder() const
-{
- QVector<File> res;
- res += *this;
- Q_FOREACH (const File& child, children)
- res += child.subtreeInPreorder();
- return res;
-}
-
-bool File::operator<(const File& other) const
-{
- if (path != other.path)
- return path < other.path;
- if (mtime != other.mtime)
- return mtime < other.mtime;
- if (uncompressedSize != other.uncompressedSize)
- return uncompressedSize < other.uncompressedSize;
- if (compressedSize != other.compressedSize)
- return compressedSize < other.compressedSize;
- if (isDirectory != other.isDirectory)
- return !isDirectory;
- if (permissions != other.permissions)
- return permissions < other.permissions;
- return false;
-}
-
-bool File::operator==(const File& other) const
-{
- return mtime == other.mtime
- && path == other.path
- && uncompressedSize == other.uncompressedSize
- && compressedSize == other.compressedSize
- && isDirectory == other.isDirectory
- && children == other.children
- && (permissions == other.permissions
- || permissions == static_cast< QFile::Permissions >(-1)
- || other.permissions == static_cast< QFile::Permissions >(-1));
-}
-
-QByteArray Lib7z::formatKeyValuePairs(const QVariantList& l)
-{
- assert(l.size() % 2 == 0);
- QByteArray res;
- for (QVariantList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it) {
- if (!res.isEmpty())
- res += ", ";
- res += qPrintable(it->toString()) + QByteArray(" = ");
- ++it;
- res += qPrintable(it->toString());
- }
- return res;
-}
-
-class Job::Private
-{
-public:
- Private()
- : error(Lib7z::NoError)
- {}
-
- int error;
- QString errorString;
-};
-
-Job::Job(QObject* parent)
- : QObject(parent)
- , QRunnable()
- , d(new Private)
-{
-}
-
-Job::~Job()
-{
- delete d;
-}
-
-void Job::emitResult()
-{
- emit finished(this);
-}
-
-void Job::setError(int code)
-{
- d->error = code;
-}
-void Job::setErrorString(const QString &str)
+bool operator==(const File &lhs, const File &rhs)
{
- d->errorString = str;
+ return lhs.path == rhs.path
+ && lhs.utcTime == rhs.utcTime
+ && lhs.isDirectory == rhs.isDirectory
+ && lhs.compressedSize == rhs.compressedSize
+ && lhs.uncompressedSize == rhs.uncompressedSize
+ && (lhs.permissions == rhs.permissions
+ || lhs.permissions == static_cast<QFile::Permissions>(-1)
+ || rhs.permissions == static_cast<QFile::Permissions>(-1));
}
-void Job::emitProgress(qint64 completed, qint64 total)
+QVector<File> listArchive(QFileDevice *archive)
{
- emit progress(completed, total);
-}
-
-int Job::error() const
-{
- return d->error;
-}
+ LIB7Z_ASSERTS(archive, Readable)
-bool Job::hasError() const
-{
- return d->error != NoError;
-}
-
-void Job::run()
-{
- doStart();
-}
-
-QString Job::errorString() const
-{
- return d->errorString;
-}
+ const qint64 initialPos = archive->pos();
+ try {
+ CCodecs codecs;
+ if (codecs.Load() != S_OK)
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs."));
-void Job::start()
-{
- QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
-}
+ COpenOptions op;
+ op.codecs = &codecs;
-class ListArchiveJob::Private
-{
-public:
- QPointer<QFileDevice> archive;
- QVector<File> files;
-};
+ CObjectVector<COpenType> types;
+ op.types = &types; // Empty, because we use a stream.
-ListArchiveJob::ListArchiveJob(QObject* parent)
- : Job(parent)
- , d(new Private)
-{
-}
+ CIntVector excluded;
+ op.excludedFormats = &excluded;
-ListArchiveJob::~ListArchiveJob()
-{
- delete d;
-}
-
-QFileDevice* ListArchiveJob::archive() const
-{
- return d->archive;
-}
-
-void ListArchiveJob::setArchive(QFileDevice* device)
-{
- d->archive = device;
-}
+ const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
+ op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream().
-QVector<File> ListArchiveJob::index() const
-{
- return d->files;
-}
+ CObjectVector<CProperty> properties;
+ op.props = &properties;
-class OpenArchiveInfo
-{
-private:
- OpenArchiveInfo(QFileDevice* device)
- : codecs(new CCodecs)
- {
- if (codecs->Load() != S_OK) {
- throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo",
- "Could not load codecs"));
- }
- if (!codecs->FindFormatForArchiveType(L"", formatIndices)) {
- throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo",
- "Could not retrieve default format"));
- }
- stream = new QIODeviceInStream(device);
- if (archiveLink.Open2(codecs.data(), formatIndices, false, stream, UString(), 0) != S_OK) {
- throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo",
- "Could not open archive"));
+ CArchiveLink archiveLink;
+ if (archiveLink.Open2(op, nullptr) != S_OK) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Cannot open archive \"%1\".").arg(archive->fileName()));
}
- if (archiveLink.Arcs.Size() == 0)
- throw SevenZipException(QCoreApplication::translate("OpenArchiveInfo", "No CArc found"));
-
- m_cleaner = new OpenArchiveInfoCleaner();
- m_cleaner->moveToThread(device->thread());
- QObject::connect(device, SIGNAL(destroyed(QObject*)), m_cleaner, SLOT(deviceDestroyed(QObject*)));
- }
-
-public:
- ~OpenArchiveInfo()
- {
- m_cleaner->deleteLater();
- }
-
- static OpenArchiveInfo* value(QFileDevice* device)
- {
- QMutexLocker _(&m_mutex);
- if (!instances.contains(device))
- instances.insert(device, new OpenArchiveInfo(device));
- return instances.value(device);
- }
-
- static OpenArchiveInfo* take(QFileDevice *device)
- {
- QMutexLocker _(&m_mutex);
- if (instances.contains(device))
- return instances.take(device);
- return 0;
- }
-
- CArchiveLink archiveLink;
-
-private:
- CIntVector formatIndices;
- CMyComPtr<IInStream> stream;
- QScopedPointer<CCodecs> codecs;
- OpenArchiveInfoCleaner *m_cleaner;
-
- static QMutex m_mutex;
- static QHash< QIODevice*, OpenArchiveInfo* > instances;
-};
-
-QMutex OpenArchiveInfo::m_mutex;
-QHash< QIODevice*, OpenArchiveInfo* > OpenArchiveInfo::instances;
-
-void OpenArchiveInfoCleaner::deviceDestroyed(QObject* dev)
-{
- QFileDevice* device = static_cast<QFileDevice*>(dev);
- Q_ASSERT(device);
- delete OpenArchiveInfo::take(device);
-}
-
-QVector<File> Lib7z::listArchive(QFileDevice* archive)
-{
- assert(archive);
- try {
- const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
QVector<File> flat;
-
- for (int i = 0; i < openArchive->archiveLink.Arcs.Size(); ++i) {
- const CArc& arc = openArchive->archiveLink.Arcs[i];
- IInArchive* const arch = arc.Archive;
-
+ for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) {
+ IInArchive *const arch = archiveLink.Arcs[i].Archive;
UInt32 numItems = 0;
if (arch->GetNumberOfItems(&numItems) != S_OK) {
throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve number of items in archive"));
+ "Cannot retrieve number of items in archive."));
}
flat.reserve(flat.size() + numItems);
for (uint item = 0; item < numItems; ++item) {
UString s;
- if (arc.GetItemPath(item, s) != S_OK) {
+ if (archiveLink.Arcs[i].GetItemPath(item, s) != S_OK) {
throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve path of archive item %1").arg(item));
+ "Cannot retrieve path of archive item \"%1\".").arg(item));
}
File f;
f.archiveIndex.setX(i);
f.archiveIndex.setY(item);
f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
- IsArchiveItemFolder(arch, item, f.isDirectory);
- f.permissions = getPermissions(arch, item);
- f.mtime = getDateTimeProperty(arch, item, kpidMTime, QDateTime());
+ Archive_IsItem_Folder(arch, item, f.isDirectory);
+ f.permissions = getPermissions(arch, item, 0);
+ getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime));
f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0);
f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0);
- flat.push_back(f);
- qApp->processEvents();
+ flat.append(f);
}
}
return flat;
- } catch (const SevenZipException& e) {
- throw e;
} catch (const char *err) {
+ archive->seek(initialPos);
throw SevenZipException(err);
+ } catch (const SevenZipException &e) {
+ archive->seek(initialPos);
+ throw e; // re-throw unmodified
} catch (...) {
+ archive->seek(initialPos);
throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)));
+ "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO)));
}
return QVector<File>(); // never reached
}
-void ListArchiveJob::doStart()
+
+// -- ExtractCallback
+
+STDMETHODIMP ExtractCallback::SetTotal(UInt64 t)
{
- try {
- if (!d->archive)
- throw SevenZipException(tr("Could not list archive: QIODevice already destroyed."));
- d->files = listArchive(d->archive);
- } catch (const SevenZipException& e) {
- setError(Failed);
- setErrorString(e.message());
- } catch (...) {
- setError(Failed);
- setErrorString(tr("Unknown exception caught (%1)").arg(tr("Failed")));
- }
- emitResult();
+ total = t;
+ return S_OK;
}
-class Lib7z::ExtractCallbackImpl : public IArchiveExtractCallback, public CMyUnknownImp
+STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c)
{
-public:
- MY_UNKNOWN_IMP
- explicit ExtractCallbackImpl(ExtractCallback* qq)
- : q(qq)
- , currentIndex(0)
- , arc(0)
- , total(0)
- , completed(0)
- , device(0)
- {
- }
+ completed = *c;
+ if (total > 0)
+ return setCompleted(completed, total);
+ return S_OK;
+}
- void setTarget(QIODevice* dev)
- {
- device = dev;
- }
+// this method will be called by CFolderOutStream::OpenFile to stream via
+// CDecoder::CodeSpec extracted content to an output stream.
+STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 /*askExtractMode*/)
+{
+ *outStream = 0;
+ if (targetDir.isEmpty())
+ return E_FAIL;
- void setTarget(const QString &targetDirectory)
- {
- targetDir = targetDirectory;
- }
+ Q_ASSERT(arc);
+ currentIndex = index;
- // this method will be called by CFolderOutStream::OpenFile to stream via
- // CDecoder::CodeSpec extracted content to an output stream.
- /* reimp */ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode)
- {
- Q_UNUSED(askExtractMode)
- *outStream = 0;
- if (device != 0) {
- QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream(device,
- QIODeviceSequentialOutStream::KeepDeviceUntouched);
- if (!qOutStream->errorString().isEmpty()) {
- Lib7z::setLastError(qOutStream->errorString());
- return E_FAIL;
- }
- CMyComPtr<ISequentialOutStream> stream = qOutStream;
- *outStream = stream.Detach();
- return S_OK;
- } else if (!targetDir.isEmpty()) {
- assert(arc);
-
- currentIndex = index;
-
- UString s;
- if (arc->GetItemPath(index, s) != S_OK) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not retrieve path of archive item %1").arg(index));
- return E_FAIL;
- }
+ UString s;
+ if (arc->GetItemPath(index, s) != S_OK) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot retrieve path of archive item %1.").arg(index));
+ return E_FAIL;
+ }
- const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
- const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path));
+ const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s)));
- DirectoryGuard guard(fi.absolutePath());
- const QStringList directories = guard.tryCreate();
+ DirectoryGuard guard(fi.absolutePath());
+ const QStringList directories = guard.tryCreate();
- bool isDir = false;
- IsArchiveItemFolder(arc->Archive, index, isDir);
- if (isDir)
- QDir(fi.absolutePath()).mkdir(fi.fileName());
+ bool isDir = false;
+ Archive_IsItem_Folder(arc->Archive, index, isDir);
+ if (isDir)
+ QDir(fi.absolutePath()).mkdir(fi.fileName());
- // this makes sure that all directories created get removed as well
- foreach (const QString &directory, directories)
- q->setCurrentFile(directory);
+ // this makes sure that all directories created get removed as well
+ foreach (const QString &directory, directories)
+ setCurrentFile(directory);
- if (!isDir && !q->prepareForFile(fi.absoluteFilePath()))
- return E_FAIL;
+ if (!isDir && !prepareForFile(fi.absoluteFilePath()))
+ return E_FAIL;
- q->setCurrentFile(fi.absoluteFilePath());
+ setCurrentFile(fi.absoluteFilePath());
- if (!isDir) {
+ if (!isDir) {
#ifndef Q_OS_WIN
- // do not follow symlinks, so we need to remove an existing one
- if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not remove already existing symlink. %1").arg(fi.absoluteFilePath()));
- return E_FAIL;
- }
+ // do not follow symlinks, so we need to remove an existing one
+ if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot remove already existing symlink %1.").arg(fi.absoluteFilePath()));
+ return E_FAIL;
+ }
#endif
- QIODeviceSequentialOutStream *qOutStream = new QIODeviceSequentialOutStream(
- new QFile(fi.absoluteFilePath()), QIODeviceSequentialOutStream::CloseAndDeleteDevice);
- if (!qOutStream->errorString().isEmpty()) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not open file: %1 (%2)").arg(fi.absoluteFilePath(),
- qOutStream->errorString()));
- return E_FAIL;
- }
- CMyComPtr<ISequentialOutStream> stream = qOutStream;
- *outStream = stream;
- stream.Detach();
- }
-
- guard.release();
- return S_OK;
+ std::unique_ptr<QFile> file(new QFile(fi.absoluteFilePath()));
+ if (!file->open(QIODevice::WriteOnly)) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot open file \"%1\" for writing: %2").arg(
+ QDir::toNativeSeparators(fi.absoluteFilePath()), file->errorString()));
+ return E_FAIL;
}
- return E_FAIL;
+ CMyComPtr<ISequentialOutStream> stream =
+ new QIODeviceSequentialOutStream(std::move(file));
+ *outStream = stream.Detach(); // CMyComPtr is needed, otherwise it crashes in Write().
}
- /* reimp */ STDMETHOD(PrepareOperation)(Int32 askExtractMode)
- {
- Q_UNUSED(askExtractMode)
+ guard.release();
+ return S_OK;
+}
+
+STDMETHODIMP ExtractCallback::PrepareOperation(Int32 /*askExtractMode*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*/)
+{
+ if (targetDir.isEmpty())
return S_OK;
- }
- /* reimp */ STDMETHOD(SetOperationResult)(Int32 resultEOperationResult)
- {
- Q_UNUSED(resultEOperationResult)
+ UString s;
+ if (arc->GetItemPath(currentIndex, s) != S_OK) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot retrieve path of archive item %1.").arg(currentIndex));
+ return E_FAIL;
+ }
- if (!targetDir.isEmpty()) {
- bool hasPerm = false;
- const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm);
+ const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir,
+ UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')))).absoluteFilePath();
- UString s;
- if (arc->GetItemPath(currentIndex, s) != S_OK) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not retrieve path of archive item %1").arg(currentIndex));
- return E_FAIL;
- }
- const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
- const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, path))
- .absoluteFilePath();
-
- // do we have a symlink?
- const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0);
- struct stat stat_info;
- stat_info.st_mode = attributes >> 16;
- if (S_ISLNK(stat_info.st_mode)) {
+ // do we have a symlink?
+ const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0);
+ struct stat stat_info;
+ stat_info.st_mode = attributes >> 16;
+ if (S_ISLNK(stat_info.st_mode)) {
#ifdef Q_OS_WIN
- qFatal(QString::fromLatin1("Creating a link from archive is not implemented for windows. "
- "Link filename: %1").arg(absFilePath).toLatin1());
- // TODO
-// if (!CreateHardLinkWrapper(absFilePath, QLatin1String(symlinkTarget))) {
-// return S_FALSE;
-// }
+ qFatal(QString::fromLatin1("Creating a link from archive is not implemented for "
+ "windows. Link filename: %1").arg(absFilePath).toLatin1());
+ // TODO
+ //if (!NFile::NDir::MyCreateHardLink(CFSTR(QDir::toNativeSeparators(absFilePath).utf16()),
+ // CFSTR(QDir::toNativeSeparators(symlinkTarget).utf16())) {
+ // return S_FALSE;
+ //}
#else
- QFileInfo symlinkPlaceHolderFileInfo(absFilePath);
- if (symlinkPlaceHolderFileInfo.isSymLink()) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not create symlink at '%1'. Another one is already existing.")
- .arg(absFilePath));
- return E_FAIL;
- }
- QFile symlinkPlaceHolderFile(absFilePath);
- if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not read symlink target from file '%1'.").arg(absFilePath));
- return E_FAIL;
- }
+ QFileInfo symlinkPlaceHolderFileInfo(absFilePath);
+ if (symlinkPlaceHolderFileInfo.isSymLink()) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot create symlink at \"%1\". Another one is already existing.")
+ .arg(absFilePath));
+ return E_FAIL;
+ }
+ QFile symlinkPlaceHolderFile(absFilePath);
+ if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot read symlink target from file \"%1\".").arg(absFilePath));
+ return E_FAIL;
+ }
- const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll();
- symlinkPlaceHolderFile.close();
- symlinkPlaceHolderFile.remove();
- QFile targetFile(QString::fromLatin1(symlinkTarget));
- if (!targetFile.link(absFilePath)) {
- Lib7z::setLastError(QCoreApplication::translate("ExtractCallbackImpl",
- "Could not create symlink at %1. %2").arg(absFilePath,
- targetFile.errorString()));
- return E_FAIL;
- }
- return S_OK;
+ const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll();
+ symlinkPlaceHolderFile.close();
+ symlinkPlaceHolderFile.remove();
+ QFile targetFile(QString::fromLatin1(symlinkTarget));
+ if (!targetFile.link(absFilePath)) {
+ setLastError(QCoreApplication::translate("ExtractCallbackImpl",
+ "Cannot create symlink at %1: %2").arg(absFilePath,
+ targetFile.errorString()));
+ return E_FAIL;
+ }
+ return S_OK;
#endif
- }
+ }
- try {
- if (!absFilePath.isEmpty()) {
- // This might fail for archives without all properties, we can only be sure about
- // modification time, as it's always stored by default in 7z archives. Also note that
- // we restore modification time on Unix only, as access time and change time are
- // supposed to be set to the time of installation.
- FILETIME mTime;
- if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidMTime, &mTime)) {
- NWindows::NFile::NIO::COutFile file;
- if (file.Open(QString2UString(absFilePath), 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL))
- file.SetTime(&mTime, &mTime, &mTime);
- }
+ try { // Note: This part might also fail while running a elevated installation.
+ if (!absFilePath.isEmpty()) {
+ // This might fail for archives without all properties, we can only be sure
+ // about modification time, as it's always stored by default in 7z archives.
+ // Also note that we restore modification time on Unix only, as access time
+ // and change time are supposed to be set to the time of installation.
+ FILETIME mTime;
+ const UString fileName = QString2UString(absFilePath);
+ if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidMTime, &mTime)) {
+ NWindows::NFile::NIO::COutFile file;
+ if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL))
+ file.SetTime(&mTime, &mTime, &mTime);
+ }
#ifdef Q_OS_WIN
- FILETIME cTime, aTime;
- bool success = getFileTimeFromProperty(arc->Archive, currentIndex, kpidCTime, &cTime);
- if (success && getFileTimeFromProperty(arc->Archive, currentIndex, kpidATime, &aTime)) {
- NWindows::NFile::NIO::COutFile file;
- if (file.Open(QString2UString(absFilePath), 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL))
- file.SetTime(&cTime, &aTime, &mTime);
- }
+ FILETIME cTime, aTime;
+ if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidCTime, &cTime)
+ && getFileTimeFromProperty(arc->Archive, currentIndex, kpidATime, &aTime)) {
+ NWindows::NFile::NIO::COutFile file;
+ if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL))
+ file.SetTime(&cTime, &aTime, &mTime);
+ }
#endif
- }
- } catch (...) {}
-
- if (hasPerm)
- QFile::setPermissions(absFilePath, permissions);
}
+ } catch (...) {}
- return S_OK;
- }
-
- /* reimp */ STDMETHOD(SetTotal)(UInt64 t)
- {
- total = t;
- return S_OK;
- }
-
- /* reimp */ STDMETHOD(SetCompleted)(const UInt64* c)
- {
- completed = *c;
- if (total > 0)
- return q->setCompleted(completed, total);
- return S_OK;
- }
-
- void setArchive(const CArc* archive)
- {
- arc = archive;
- }
-
-private:
- ExtractCallback* const q;
- UInt32 currentIndex;
- const CArc* arc;
- UInt64 total;
- UInt64 completed;
- QPointer<QIODevice> device;
- QString targetDir;
-};
+ bool hasPerm = false;
+ const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm);
+ if (hasPerm)
+ QFile::setPermissions(absFilePath, permissions);
+ return S_OK;
+}
+/*!
+ \fn bool ExtractCallback::prepareForFile(const QString &filename)
-class Lib7z::ExtractCallbackPrivate
-{
-public:
- explicit ExtractCallbackPrivate(ExtractCallback* qq)
- {
- impl = new ExtractCallbackImpl(qq);
- }
+ Implement to prepare for file \a filename to be extracted, e.g. by renaming existing files.
+ Return \c true if the preparation was successful and extraction can be continued. If \c false
+ is returned, the extraction will be aborted. The default implementation returns \c true.
+*/
- CMyComPtr<ExtractCallbackImpl> impl;
-};
-ExtractCallback::ExtractCallback()
- : d(new ExtractCallbackPrivate(this))
-{
-}
+// -- UpdateCallback
-ExtractCallback::~ExtractCallback()
+HRESULT UpdateCallback::SetTotal(UInt64)
{
- delete d;
+ return S_OK;
}
-ExtractCallbackImpl* ExtractCallback::impl()
+HRESULT UpdateCallback::SetCompleted(const UInt64*)
{
- return d->impl;
+ return S_OK;
}
-const ExtractCallbackImpl* ExtractCallback::impl() const
+HRESULT UpdateCallback::SetRatioInfo(const UInt64*, const UInt64*)
{
- return d->impl;
+ return S_OK;
}
-void ExtractCallback::setTarget(QFileDevice* dev)
+HRESULT UpdateCallback::CheckBreak()
{
- d->impl->setTarget(dev);
+ return S_OK;
}
-void ExtractCallback::setTarget(const QString &dir)
+HRESULT UpdateCallback::Finilize()
{
- d->impl->setTarget(dir);
+ return S_OK;
}
-HRESULT ExtractCallback::setCompleted(quint64, quint64)
+HRESULT UpdateCallback::SetNumFiles(UInt64)
{
return S_OK;
}
-void ExtractCallback::setCurrentFile(const QString&)
+HRESULT UpdateCallback::GetStream(const wchar_t*, bool)
{
+ return S_OK;
}
-bool ExtractCallback::prepareForFile(const QString&)
+HRESULT UpdateCallback::OpenFileError(const wchar_t*, DWORD)
{
- return true;
+ return S_OK;
}
-class Lib7z::ExtractCallbackJobImpl : public ExtractCallback
-{
-public:
- explicit ExtractCallbackJobImpl(ExtractItemJob* j)
- : ExtractCallback()
- , job(j)
- {}
-
-private:
- /* reimp */ HRESULT setCompleted(quint64 c, quint64 t)
- {
- emit job->progress(c, t);
- return S_OK;
- }
-
- ExtractItemJob* const job;
-};
-
-class Lib7z::UpdateCallbackImpl : public IUpdateCallbackUI2, public CMyUnknownImp
-{
-public:
- MY_UNKNOWN_IMP
- UpdateCallbackImpl()
- {
- }
- virtual ~UpdateCallbackImpl()
- {
- }
- /**
- * \reimp
- */
- HRESULT SetTotal(UInt64)
- {
- return S_OK;
- }
- /**
- * \reimp
- */
- HRESULT SetCompleted(const UInt64*)
- {
- return S_OK;
- }
- HRESULT SetRatioInfo(const UInt64*, const UInt64*)
- {
- return S_OK;
- }
- HRESULT CheckBreak()
- {
- return S_OK;
- }
- HRESULT Finilize()
- {
- return S_OK;
- }
- HRESULT SetNumFiles(UInt64)
- {
- return S_OK;
- }
- HRESULT GetStream(const wchar_t*, bool)
- {
- return S_OK;
- }
- HRESULT OpenFileError(const wchar_t*, DWORD)
- {
- return S_OK;
- }
- HRESULT CryptoGetTextPassword2(Int32* passwordIsDefined, OLECHAR** password)
- {
- *password = 0;
- *passwordIsDefined = false;
- return S_OK;
- }
- HRESULT CryptoGetTextPassword(OLECHAR**)
- {
- return E_NOTIMPL;
- }
- HRESULT OpenResult(const wchar_t*, LONG)
- {
- return S_OK;
- }
- HRESULT StartScanning()
- {
- return S_OK;
- }
- HRESULT ScanProgress(UInt64, UInt64, const wchar_t*)
- {
- return S_OK;
- }
- HRESULT CanNotFindError(const wchar_t*, DWORD)
- {
- return S_OK;
- }
- HRESULT FinishScanning()
- {
- return S_OK;
- }
- HRESULT StartArchive(const wchar_t*, bool)
- {
- return S_OK;
- }
- HRESULT FinishArchive()
- {
- return S_OK;
- }
-
- /**
- * \reimp
- */
- HRESULT SetOperationResult(Int32)
- {
- // TODO!
- return S_OK;
- }
- void setSourcePaths(const QStringList &paths)
- {
- sourcePaths = paths;
- }
- void setTarget(QIODevice* archive)
- {
- target = archive;
- }
-
-private:
- QIODevice* target;
- QStringList sourcePaths;
-};
-
-class Lib7z::UpdateCallbackPrivate
-{
-public:
- UpdateCallbackPrivate()
- {
- m_impl = new UpdateCallbackImpl;
- }
-
- UpdateCallbackImpl* impl()
- {
- return m_impl;
- }
-
-private:
- CMyComPtr< UpdateCallbackImpl > m_impl;
-};
-
-UpdateCallback::UpdateCallback()
- : d(new UpdateCallbackPrivate)
+HRESULT UpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
{
+ *password = 0;
+ *passwordIsDefined = false;
+ return S_OK;
}
-UpdateCallback::~UpdateCallback()
+HRESULT UpdateCallback::CryptoGetTextPassword(BSTR *password)
{
- delete d;
+ *password = 0;
+ return E_NOTIMPL;
}
-UpdateCallbackImpl* UpdateCallback::impl()
+HRESULT UpdateCallback::OpenResult(const wchar_t*, HRESULT, const wchar_t*)
{
- return d->impl();
+ return S_OK;
}
-void UpdateCallback::setSourcePaths(const QStringList &paths)
+HRESULT UpdateCallback::StartScanning()
{
- d->impl()->setSourcePaths(paths);
+ return S_OK;
}
-void UpdateCallback::setTarget(QFileDevice* target)
+HRESULT UpdateCallback::ScanProgress(UInt64, UInt64, UInt64, const wchar_t*, bool)
{
- d->impl()->setTarget(target);
+ return S_OK;
}
-class ExtractItemJob::Private
-{
-public:
- Private(ExtractItemJob* qq)
- : q(qq)
- , target(0)
- , callback(new ExtractCallbackJobImpl(q))
- {
- }
-
- ExtractItemJob* q;
- File item;
- QPointer<QFileDevice> archive;
- QString targetDirectory;
- QFileDevice* target;
- ExtractCallback* callback;
-};
-
-ExtractItemJob::ExtractItemJob(QObject* parent)
- : Job(parent)
- , d(new Private(this))
+HRESULT UpdateCallback::CanNotFindError(const wchar_t*, DWORD)
{
+ return S_OK;
}
-ExtractItemJob::~ExtractItemJob()
+HRESULT UpdateCallback::FinishScanning()
{
- delete d;
+ return S_OK;
}
-File ExtractItemJob::item() const
+HRESULT UpdateCallback::StartArchive(const wchar_t*, bool)
{
- return d->item;
+ return S_OK;
}
-void ExtractItemJob::setItem(const File& item)
+HRESULT UpdateCallback::FinishArchive()
{
- d->item = item;
+ return S_OK;
}
-QFileDevice* ExtractItemJob::archive() const
+HRESULT UpdateCallback::SetOperationResult(Int32)
{
- return d->archive;
+ return S_OK;
}
-void ExtractItemJob::setArchive(QFileDevice* archive)
+/*!
+ Function to create an empty 7z container. Using a temporary file only is not working, since
+ 7z checks the output file for a valid signature, otherwise it rejects overwriting the file.
+*/
+static QString createTmp7z()
{
- d->archive = archive;
-}
+ QTemporaryFile file;
+ if (!file.open()) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot create "
+ "temporary file: %1").arg(file.errorString()));
+ }
-QString ExtractItemJob::targetDirectory() const
-{
- return d->targetDirectory;
+ file.write(QByteArray::fromHex("377A.BCAF.271C" // Signature.
+ ".0003.8D9B.D50F.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000" // Crc + Data.
+ ));
+ file.setAutoRemove(false);
+ return file.fileName();
}
-void ExtractItemJob::setTargetDirectory(const QString &dir)
-{
- d->targetDirectory = dir;
- d->target = 0;
-}
+/*!
+ Creates an archive using the given file device \a archive. \a sourcePaths can contain one or
+ more files, one or more directories or a combination of files and folders. The \c * wildcard
+ is supported also. The value of \a level specifies the compression ratio, the default is set
+ to \c 5 (Normal compression). The \a callback can be used to get information about the archive
+ creation process. If no \a callback is given, an empty implementation is used.
-void ExtractItemJob::setTarget(QFileDevice* dev)
+ \note Throws SevenZipException on error.
+ \note Filenames are stored case-sensitive with UTF-8 encoding.
+ \note The ownership of \a callback is transferred to the function and gets delete on exit.
+*/
+void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources,
+ Compression level, UpdateCallback *callback)
{
- d->target = dev;
-}
+ LIB7Z_ASSERTS(archive, Writable)
-namespace{
- QString errorMessageFrom7zResult(const LONG & extractResult)
- {
- if (!Lib7z::lastError().isEmpty())
- return Lib7z::lastError();
-
- QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1");
- switch (extractResult) {
- case S_OK:
- qFatal("S_OK value is not a valid error code.");
- break;
- case E_NOTIMPL:
- errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL"));
- break;
- case E_NOINTERFACE:
- errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE"));
- break;
- case E_ABORT:
- errorMessage = errorMessage.arg(QLatin1String("E_ABORT"));
- break;
- case E_FAIL:
- errorMessage = errorMessage.arg(QLatin1String("E_FAIL"));
- break;
- case STG_E_INVALIDFUNCTION:
- errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION"));
- break;
- case E_OUTOFMEMORY:
- errorMessage = QCoreApplication::translate("Lib7z", "not enough memory");
- break;
- case E_INVALIDARG:
- errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG"));
- break;
- default:
- errorMessage = QCoreApplication::translate("Lib7z", "Error: %1").arg(extractResult);
- break;
- }
- return errorMessage;
- }
-}
+ const QString tmpArchive = createTmp7z();
+ Lib7z::createArchive(tmpArchive, sources, QTmpFile::No, level, callback);
-void Lib7z::createArchive(QFileDevice* archive, const QStringList &sourcePaths, UpdateCallback* callback)
+ try {
+ QFile source(tmpArchive);
+ QInstaller::openForRead(&source);
+ QInstaller::blockingCopy(&source, archive, source.size());
+ } catch (const QInstaller::Error &error) {
+ throw SevenZipException(error.message());
+ }
+}
+
+/*!
+ Creates an archive with the given filename \a archive. \a sourcePaths can contain one or more
+ files, one or more directories or a combination of files and folders. Also the \c * wildcard
+ is supported. To be able to use the function during an elevated installation, set \a mode to
+ \c QTmpFile::Yes. The value of \a level specifies the compression ratio, the default is set
+ to \c 5 (Normal compression). The \a callback can be used to get information about the archive
+ creation process. If no \a callback is given, an empty implementation is used.
+
+ \note Throws SevenZipException on error.
+ \note If \a archive exists, it will be overwritten.
+ \note Filenames are stored case-sensitive with UTF-8 encoding.
+ \note The ownership of \a callback is transferred to the function and gets delete on exit.
+*/
+void createArchive(const QString &archive, const QStringList &sources, QTmpFile mode,
+ Compression level, UpdateCallback *callback)
{
- assert(archive);
-
- QScopedPointer<UpdateCallback> dummyCallback(callback ? 0 : new UpdateCallback);
- if (!callback)
- callback = dummyCallback.data();
-
try {
- callback->setTarget(archive);
-
- QScopedPointer<CCodecs> codecs(new CCodecs);
- if (codecs->Load() != S_OK)
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs"));
-
- CIntVector formatIndices;
-
- if (!codecs.data()->FindFormatForArchiveType(L"", formatIndices)) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve default format"));
- }
-
- // yes this is crap, but there seems to be no streaming solution to this...
-
- const QString tempFile = generateTempFileName();
-
- NWildcard::CCensor censor;
- foreach (const QString &path, sourcePaths) {
- const QString cleanPath = QDir::toNativeSeparators(QDir::cleanPath(path));
- const UString nativePath = QString2UString(cleanPath);
- if (UString2QString(nativePath) != cleanPath) {
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not convert"
- "path: %1.").arg(path));
- }
- censor.AddItem(true /* always include item */, nativePath, false /* never recurse*/);
+ QString target = archive;
+ if (mode == QTmpFile::Yes)
+ target = createTmp7z();
+
+ CArcCmdLineOptions options;
+ try {
+ UStringVector commandStrings;
+ commandStrings.Add(L"a"); // mode: add
+ commandStrings.Add(L"-t7z"); // type: 7z
+ commandStrings.Add(L"-mtm=on"); // time: modeifier|creation|access
+ commandStrings.Add(L"-mtc=on");
+ commandStrings.Add(L"-mta=on");
+ commandStrings.Add(L"-mmt=on"); // threads: multi-threaded
+#ifdef Q_OS_WIN
+ commandStrings.Add(L"-sccUTF-8"); // files: case-sensitive|UTF8
+#endif
+ commandStrings.Add(QString2UString(QString::fromLatin1("-mx=%1").arg(int(level)))); // compression: level
+ commandStrings.Add(QString2UString(QDir::toNativeSeparators(target)));
+ foreach (const QString &source, sources)
+ commandStrings.Add(QString2UString(source));
+
+ CArcCmdLineParser parser;
+ parser.Parse1(commandStrings, options);
+ parser.Parse2(options);
+ } catch (const CArcCmdLineException &e) {
+ throw SevenZipException(UString2QString(e));
}
- callback->setSourcePaths(sourcePaths);
-
- CArchivePath archivePath;
- archivePath.ParseFromPath(QString2UString(tempFile));
- CUpdateArchiveCommand command;
- command.ArchivePath = archivePath;
- command.ActionSet = NUpdateArchive::kAddActionSet;
+ CCodecs codecs;
+ if (codecs.Load() != S_OK)
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs."));
- CUpdateOptions options;
- options.Commands.Add(command);
- options.ArchivePath = archivePath;
- options.MethodMode.FormatIndex = codecs->FindFormatForArchiveType(L"7z");
-
- // preserve creation time
- CProperty tc;
- tc.Name = UString(L"TC");
- tc.Value = UString(L"ON");
- options.MethodMode.Properties.Add(tc);
-
- // preserve access time
- CProperty ta;
- ta.Name = UString(L"TA");
- ta.Value = UString(L"ON");
- options.MethodMode.Properties.Add(ta);
+ CObjectVector<COpenType> types;
+ if (!ParseOpenTypes(codecs, options.ArcType, types))
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Unsupported archive type."));
CUpdateErrorInfo errorInfo;
- const HRESULT res = UpdateArchive(codecs.data(), censor, options, errorInfo, 0, callback->impl());
- if (res != S_OK || !QFile::exists(tempFile)) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not create archive %1. %2").arg(tempFile, errorMessageFrom7zResult(res)));
+ CMyComPtr<UpdateCallback> comCallback = callback == 0 ? new UpdateCallback : callback;
+ const HRESULT res = UpdateArchive(&codecs, types, options.ArchiveName, options.Censor,
+ options.UpdateOptions, errorInfo, nullptr, comCallback, true);
+
+ const QFile tempFile(UString2QString(options.ArchiveName));
+ if (res != S_OK || !tempFile.exists()) {
+ QString errorMsg;
+ if (res == S_OK) {
+ errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\"")
+ .arg(QDir::toNativeSeparators(tempFile.fileName()));
+ } else {
+ errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\": %2")
+ .arg(QDir::toNativeSeparators(tempFile.fileName()), errorMessageFrom7zResult(res));
+ }
+ throw SevenZipException(errorMsg);
}
- {
- //TODO remove temp file even if one the following throws
- QFile file(tempFile);
- QInstaller::openForRead(&file);
- QInstaller::blockingCopy(&file, archive, file.size());
- }
+ if (mode == QTmpFile::Yes) {
+ QFile org(archive);
+ if (org.exists() && !org.remove()) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot remove "
+ "old archive \"%1\": %2").arg(QDir::toNativeSeparators(org.fileName()),
+ org.errorString()));
+ }
- QFile file(tempFile);
- if (!file.remove()) {
- qWarning("%s: Could not remove temporary file %s: %s", Q_FUNC_INFO, qPrintable(tempFile),
- qPrintable(file.errorString()));
+ QFile arc(UString2QString(options.ArchiveName));
+ if(!arc.rename(archive)) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot rename "
+ "temporary archive \"%1\" to \"%2\": %3").arg(
+ QDir::toNativeSeparators(arc.fileName()),
+ QDir::toNativeSeparators(archive),
+ arc.errorString()));
+ }
}
} catch (const char *err) {
- qDebug() << err;
throw SevenZipException(err);
+ } catch (SevenZipException &e) {
+ throw e; // re-throw unmodified
} catch (const QInstaller::Error &err) {
throw SevenZipException(err.message());
} catch (...) {
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)")
- .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)));
}
}
-void Lib7z::extractFileFromArchive(QFileDevice* archive, const File& item, QFileDevice* target,
- ExtractCallback* callback)
+/*!
+ Extracts the given \a archive content into target directory \a directory using the provided
+ extract callback \a callback. The output filenames are deduced from the \a archive content.
+
+ \note Throws SevenZipException on error.
+ \note The ownership of \a callback is not transferred to the function.
+*/
+void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallback *callback)
{
- assert(archive);
- assert(target);
+ LIB7Z_ASSERTS(archive, Readable)
- std::auto_ptr<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
- if (!callback)
- callback = dummyCallback.get();
+ // Guard a given object against unwanted delete.
+ CMyComPtr<ExtractCallback> externCallback = callback;
+
+ CMyComPtr<ExtractCallback> localCallback;
+ if (!externCallback) {
+ callback = new ExtractCallback;
+ localCallback = callback;
+ }
+ DirectoryGuard outDir(QFileInfo(directory).absolutePath());
try {
- const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
+ outDir.tryCreate();
- const int arcIdx = item.archiveIndex.x();
- if (arcIdx < 0 || arcIdx >= openArchive->archiveLink.Arcs.Size()) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "CArc index %1 out of bounds [0, %2]").arg(openArchive->archiveLink.Arcs.Size() - 1));
- }
- const CArc& arc = openArchive->archiveLink.Arcs[arcIdx];
- IInArchive* const parchive = arc.Archive;
+ CCodecs codecs;
+ if (codecs.Load() != S_OK)
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs."));
- const UInt32 itemIdx = item.archiveIndex.y();
- UInt32 numItems = 0;
- if (parchive->GetNumberOfItems(&numItems) != S_OK) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve number of items in archive"));
- }
+ COpenOptions op;
+ op.codecs = &codecs;
- if (itemIdx >= numItems) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Item index %1 out of bounds [0, %2]").arg(itemIdx).arg(numItems - 1));
- }
+ CObjectVector<COpenType> types;
+ op.types = &types; // Empty, because we use a stream.
- UString s;
- if (arc.GetItemPath(itemIdx, s) != S_OK) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve path of archive item %1").arg(itemIdx));
- }
- assert(item.path == UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')));
+ CIntVector excluded;
+ op.excludedFormats = &excluded;
- callback->setTarget(target);
- const LONG extractResult = parchive->Extract(&itemIdx, 1, /*testmode=*/0, callback->impl());
+ const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
+ op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream().
- if (extractResult != S_OK)
- throw SevenZipException(errorMessageFrom7zResult(extractResult));
+ CObjectVector<CProperty> properties;
+ op.props = &properties;
- } catch (const char *err) {
- throw SevenZipException(err);
- } catch (const Lib7z::SevenZipException& e) {
- throw e;
- } catch (...) {
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)")
- .arg(QString::fromLatin1(Q_FUNC_INFO)));
- }
-}
-
-void Lib7z::extractFileFromArchive(QFileDevice* archive, const File& item,
- const QString &targetDirectory, ExtractCallback* callback)
-{
- assert(archive);
+ CArchiveLink archiveLink;
+ if (archiveLink.Open2(op, nullptr) != S_OK) {
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Cannot open archive \"%1\".").arg(archive->fileName()));
+ }
- QScopedPointer<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
- if (!callback)
- callback = dummyCallback.data();
+ callback->setTarget(directory);
+ for (unsigned a = 0; a < archiveLink.Arcs.Size(); ++a) {
+ callback->setArchive(&archiveLink.Arcs[a]);
+ IInArchive *const arch = archiveLink.Arcs[a].Archive;
- QFileInfo fi(targetDirectory + QLatin1String("/") + item.path);
- DirectoryGuard outDir(fi.absolutePath());
- outDir.tryCreate();
- QFile out(fi.absoluteFilePath());
- if (!out.open(QIODevice::WriteOnly)) { //TODO use tmp file
+ const LONG result = arch->Extract(0, static_cast<UInt32>(-1), false, callback);
+ if (result != S_OK)
+ throw SevenZipException(errorMessageFrom7zResult(result));
+ }
+ } catch (const SevenZipException &e) {
+ externCallback.Detach();
+ throw e; // re-throw unmodified
+ } catch (...) {
+ externCallback.Detach();
throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not create output file for writing: %1").arg(fi.absoluteFilePath()));
+ "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO)));
}
- callback->setTarget(&out);
- extractFileFromArchive(archive, item, &out, callback);
- if (item.permissions)
- out.setPermissions(item.permissions);
outDir.release();
+ externCallback.Detach();
}
-void Lib7z::extractArchive(QFileDevice* archive, const QString &targetDirectory,
- ExtractCallback* callback)
-{
- assert(archive);
-
- QScopedPointer<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
- if (!callback)
- callback = dummyCallback.data();
-
- callback->setTarget(targetDirectory);
-
- const QFileInfo fi(targetDirectory);
- DirectoryGuard outDir(fi.absolutePath());
- outDir.tryCreate();
-
- const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
-
- for (int a = 0; a < openArchive->archiveLink.Arcs.Size(); ++a)
- {
- const CArc& arc = openArchive->archiveLink.Arcs[a];
- IInArchive* const arch = arc.Archive;
- callback->impl()->setArchive(&arc);
- const LONG extractResult = arch->Extract(0, static_cast< UInt32 >(-1), false, callback->impl());
-
- if (extractResult != S_OK)
- throw SevenZipException(errorMessageFrom7zResult(extractResult));
- }
-
- outDir.release();
-}
+/*!
+ Returns \c true if the given \a archive is supported; otherwise returns \c false.
-bool Lib7z::isSupportedArchive(const QString &archive)
+ \note Throws SevenZipException on error.
+*/
+bool isSupportedArchive(QFileDevice *archive)
{
- QFile file(archive);
- if (!file.open(QIODevice::ReadOnly))
- return false;
+ LIB7Z_ASSERTS(archive, Readable)
- return isSupportedArchive(&file);
-}
-
-bool Lib7z::isSupportedArchive(QFileDevice* archive)
-{
- assert(archive);
- assert(!archive->isSequential());
const qint64 initialPos = archive->pos();
try {
- QScopedPointer<CCodecs> codecs(new CCodecs);
- if (codecs->Load() != S_OK)
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs"));
+ CCodecs codecs;
+ if (codecs.Load() != S_OK)
+ throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs."));
- CIntVector formatIndices;
+ COpenOptions op;
+ op.codecs = &codecs;
+
+ CObjectVector<COpenType> types;
+ op.types = &types; // Empty, because we use a stream.
+
+ CIntVector excluded;
+ op.excludedFormats = &excluded;
- if (!codecs->FindFormatForArchiveType(L"", formatIndices)) {
- throw SevenZipException(QCoreApplication::translate("Lib7z",
- "Could not retrieve default format"));
- }
- CArchiveLink archiveLink;
- //CMyComPtr is needed, otherwise it crashes in OpenStream()
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
+ op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream().
+
+ CObjectVector<CProperty> properties;
+ op.props = &properties;
- const HRESULT result = archiveLink.Open2(codecs.data(), formatIndices, /*stdInMode*/false, stream,
- UString(), 0);
+ CArchiveLink archiveLink;
+ const HRESULT result = archiveLink.Open2(op, nullptr);
archive->seek(initialPos);
return result == S_OK;
- } catch (const SevenZipException& e) {
- archive->seek(initialPos);
- throw e;
} catch (const char *err) {
archive->seek(initialPos);
throw SevenZipException(err);
+ } catch (const SevenZipException &e) {
+ archive->seek(initialPos);
+ throw e; // re-throw unmodified
} catch (...) {
archive->seek(initialPos);
- throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)")
- .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ throw SevenZipException(QCoreApplication::translate("Lib7z",
+ "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO)));
}
return false; // never reached
}
-void ExtractItemJob::doStart()
+/*!
+ Returns \c true if the given \a archive is supported; otherwise returns \c false.
+
+ \note Throws SevenZipException on error.
+*/
+bool isSupportedArchive(const QString &archive)
{
- try {
- if (!d->archive)
- throw SevenZipException(tr("Could not list archive: QIODevice not set or already destroyed."));
- if (d->target)
- extractFileFromArchive(d->archive, d->item, d->target, d->callback);
- else if (!d->item.path.isEmpty())
- extractFileFromArchive(d->archive, d->item, d->targetDirectory, d->callback);
- else
- extractArchive(d->archive, d->targetDirectory, d->callback);
- } catch (const SevenZipException& e) {
- setError(Failed);
- setErrorString(tr("Error while extracting '%1': %2").arg(d->item.path, e.message()));
- } catch (...) {
- setError(Failed);
- setErrorString(tr("Unknown exception caught (%1)").arg(tr("Failed")));
- }
- emitResult();
+ QFile file(archive);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+ return isSupportedArchive(&file);
}
+
+} // namespace Lib7z