From 4677d362982a38c6e2aabb667e33aaa7f921f018 Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Tue, 9 Jun 2015 16:04:24 +0200 Subject: Update source tree with version 9.38.beta of LZMA SDK. - Remove unused files. - Split in .pri files. - Add HEADERS section. - Adjust lib7z_facade. Change-Id: I31e7bafbfe1a9346364bd58c391601955f98ad3a Reviewed-by: Kai Koehne --- src/libs/installer/lib7z_facade.cpp | 1503 ++++++++++++++--------------------- 1 file changed, 613 insertions(+), 890 deletions(-) (limited to 'src/libs/installer/lib7z_facade.cpp') diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp index 3c1a190a3..cf4df52eb 100644 --- a/src/libs/installer/lib7z_facade.cpp +++ b/src/libs/installer/lib7z_facade.cpp @@ -31,101 +31,174 @@ ** $QT_END_LICENSE$ ** **************************************************************************/ + #include "lib7z_facade.h" #include "errors.h" #include "fileio.h" +#include "lib7z_guid.h" #ifndef Q_OS_WIN # include "StdAfx.h" #endif -#include "Common/MyInitGuid.h" +#include + +#include <7zCrc.h> -#include "7zip/Archive/IArchive.h" -#include "7zip/UI/Common/OpenArchive.h" -#include "7zip/UI/Common/Update.h" +#include <7zip/Archive/IArchive.h> -#include "Windows/FileIO.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" +#include <7zip/UI/Common/ArchiveCommandLine.h> +#include <7zip/UI/Common/OpenArchive.h> + +#include +#include +#include +#include #include #include #include #include -#include #include -#include #include +#include -#ifdef _MSC_VER -#pragma warning(disable:4297) -#endif +#include +#include #ifdef Q_OS_WIN +HINSTANCE g_hInstance = 0; -#include -#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) - -typedef BOOL(WINAPI *CREATEHARDLINK)(LPCSTR dst, LPCSTR str, LPSECURITY_ATTRIBUTES sa); - -bool CreateHardLinkWrapper(const QString &dest, const QString &file) -{ - static HMODULE module = 0; - static CREATEHARDLINK proc = 0; - - 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); -} - +# 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 */ #else #include #endif -#include +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; -#include +void registerCodecBCJ(); +void registerCodecBCJ2(); -using namespace Lib7z; -using namespace NWindows; +void registerCodecLZMA(); +void registerCodecLZMA2(); +void registerCodecCopy(); +void registerCodecDelta(); +void registerCodecBranch(); +void registerCodecByteSwap(); namespace Lib7z { - Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) - Q_GLOBAL_STATIC(QString, getLastErrorString) - QString lastError() - { - QReadLocker locker(lastErrorReadWriteLock()); - Q_UNUSED(locker) - return *getLastErrorString(); - } - void setLastError(const QString &errorString) - { - QWriteLocker locker(lastErrorReadWriteLock()); - Q_UNUSED(locker) - *getLastErrorString() = errorString; - } +// -- 7z init codecs, archives + +std::once_flag gOnceFlag; + +void initSevenZ() +{ + std::call_once(gOnceFlag, [] { + CrcGenerateTable(); + + registerCodecBCJ(); + registerCodecBCJ2(); + + registerCodecLZMA(); + registerCodecLZMA2(); + + registerCodecCopy(); + registerCodecDelta(); + registerCodecBranch(); + registerCodecByteSwap(); + + NArchive::N7z::registerArcDec7z(); + NArchive::NXz::registerArcxz(); + NArchive::NSplit::registerArcSplit(); + NArchive::NLzma::NLzmaAr::registerArcLzma(); + NArchive::NLzma::NLzma86Ar::registerArcLzma86(); + }); +} + + +// -- error handling + +Q_GLOBAL_STATIC(QString, getLastErrorString) +Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) + +QString lastError() +{ + QReadLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + return *getLastErrorString(); +} + +void setLastError(const QString &errorString) +{ + QWriteLocker locker(lastErrorReadWriteLock()); + Q_UNUSED(locker) + *getLastErrorString() = errorString; +} + +QString errorMessageFrom7zResult(const LONG &extractResult) +{ + if (!lastError().isEmpty()) + return 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; } -namespace { /*! RAII class to create a directory (tryCreate()) and delete it on destruction unless released. */ @@ -192,7 +265,6 @@ struct DirectoryGuard bool m_created; bool m_released; }; -} static UString QString2UString(const QString &str) { @@ -204,16 +276,6 @@ static QString UString2QString(const UString& str) return QString::fromStdWString(static_cast(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 NCOM::CPropVariant readProperty(IInArchive *archive, int index, int propId) { NCOM::CPropVariant prop; @@ -287,15 +349,15 @@ QDateTime getDateTimeProperty(IInArchive *archive, int index, int propId, const return result; } -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(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) @@ -303,7 +365,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui return static_cast(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; @@ -321,89 +383,56 @@ static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *h 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; - STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); - -private: - Behavior m_behavior; - QString m_errorString; - QPointer 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 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(data), size); + if (written == -1) { m_errorString = m_device->errorString(); return E_FAIL; } - } - const qint64 written = m_device->write(reinterpret_cast(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 m_device; +}; class QIODeviceInStream : public IInStream, public CMyUnknownImp { + Q_DISABLE_COPY(QIODeviceInStream) + public: MY_UNKNOWN_IMP @@ -412,14 +441,14 @@ public: , CMyUnknownImp() , m_device(device) { - assert(m_device); - assert(!m_device->isSequential()); + LIB7Z_ASSERTS(m_device, Readable) } 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(data), size); Q_ASSERT(actual != 0 || m_device->atEnd()); if (processedSize) @@ -429,9 +458,8 @@ public: 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; @@ -449,7 +477,7 @@ public: return STG_E_INVALIDFUNCTION; } - np = qBound(static_cast(0), np, static_cast(m_device->size() - 1)); + np = qBound(static_cast(0), np, static_cast(m_device->size())); const bool ok = m_device->seek(np); if (newPosition) *newPosition = np; @@ -459,936 +487,631 @@ public: private: QPointer m_device; }; -} - -File::File() - : permissions(0) - , uncompressedSize(0) - , compressedSize(0) - , isDirectory(false) -{ -} - -QVector File::subtreeInPreorder() const -{ - QVector 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 +bool operator==(const File &lhs, const File &rhs) { - 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(-1) - || other.permissions == static_cast(-1)); + return lhs.path == rhs.path + && lhs.mtime == rhs.mtime + && lhs.isDirectory == rhs.isDirectory + && lhs.compressedSize == rhs.compressedSize + && lhs.uncompressedSize == rhs.uncompressedSize + && (lhs.permissions == rhs.permissions + || lhs.permissions == static_cast(-1) + || rhs.permissions == static_cast(-1)); } -class OpenArchiveInfo +QVector listArchive(QFileDevice *archive) { -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")); - } - 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); - } + LIB7Z_ASSERTS(archive, Readable) - static OpenArchiveInfo *take(QFileDevice *device) - { - QMutexLocker _(&m_mutex); - if (instances.contains(device)) - return instances.take(device); - return 0; - } + const qint64 initialPos = archive->pos(); + try { + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs.")); - CArchiveLink archiveLink; + COpenOptions op; + op.codecs = &codecs; -private: - CIntVector formatIndices; - CMyComPtr stream; - QScopedPointer codecs; - OpenArchiveInfoCleaner *m_cleaner; + CObjectVector types; + op.types = &types; // Empty, because we use a stream. - static QMutex m_mutex; - static QHash< QIODevice*, OpenArchiveInfo *> instances; -}; + CIntVector excluded; + op.excludedFormats = &excluded; -QMutex OpenArchiveInfo::m_mutex; -QHash< QIODevice*, OpenArchiveInfo *> OpenArchiveInfo::instances; + const CMyComPtr stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). -void OpenArchiveInfoCleaner::deviceDestroyed(QObject *dev) -{ - QFileDevice *device = static_cast(dev); - Q_ASSERT(device); - delete OpenArchiveInfo::take(device); -} + CObjectVector properties; + op.props = &properties; -QVector Lib7z::listArchive(QFileDevice *archive) -{ - assert(archive); - try { - const OpenArchiveInfo *const openArchive = OpenArchiveInfo::value(archive); + CArchiveLink archiveLink; + if (archiveLink.Open2(op, nullptr) != S_OK) { + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Could not open archive '%1'.").arg(archive->fileName())); + } QVector 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")); + "Could not 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)); + "Could not 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); + Archive_IsItem_Folder(arch, item, f.isDirectory); + f.permissions = getPermissions(arch, item, 0); f.mtime = getDateTimeProperty(arch, item, kpidMTime, QDateTime()); 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(); // never reached } -class Lib7z::ExtractCallbackImpl : public IArchiveExtractCallback, public CMyUnknownImp + +// -- ExtractCallback + +STDMETHODIMP ExtractCallback::SetTotal(UInt64 t) { -public: - MY_UNKNOWN_IMP - explicit ExtractCallbackImpl(ExtractCallback *qq) - : q(qq) - , currentIndex(0) - , arc(0) - , total(0) - , completed(0) - , device(0) - { - } + total = t; + return S_OK; +} - void setTarget(QIODevice *dev) - { - device = dev; - } +STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c) +{ + completed = *c; + if (total > 0) + return setCompleted(completed, total); + return S_OK; +} - void setTarget(const QString &targetDirectory) - { - targetDir = targetDirectory; - } +// 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; - // this method will be called by CFolderOutStream::OpenFile to stream via - // CDecoder::CodeSpec extracted content to an output stream. - 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 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; - } + Q_ASSERT(arc); + currentIndex = index; - const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); - const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path)); + UString s; + if (arc->GetItemPath(index, s) != S_OK) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Could not retrieve path of archive item %1").arg(index)); + return E_FAIL; + } - DirectoryGuard guard(fi.absolutePath()); - const QStringList directories = guard.tryCreate(); + const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s))); - bool isDir = false; - IsArchiveItemFolder(arc->Archive, index, isDir); - if (isDir) - QDir(fi.absolutePath()).mkdir(fi.fileName()); + DirectoryGuard guard(fi.absolutePath()); + const QStringList directories = guard.tryCreate(); - // this makes sure that all directories created get removed as well - foreach (const QString &directory, directories) - q->setCurrentFile(directory); + bool isDir = false; + Archive_IsItem_Folder(arc->Archive, index, isDir); + if (isDir) + QDir(fi.absolutePath()).mkdir(fi.fileName()); - if (!isDir && !q->prepareForFile(fi.absoluteFilePath())) - return E_FAIL; + // this makes sure that all directories created get removed as well + foreach (const QString &directory, directories) + setCurrentFile(directory); - q->setCurrentFile(fi.absoluteFilePath()); + if (!isDir && !prepareForFile(fi.absoluteFilePath())) + return E_FAIL; - if (!isDir) { + setCurrentFile(fi.absoluteFilePath()); + + 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", + "Could not 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 stream = qOutStream; - *outStream = stream; - stream.Detach(); - } - - guard.release(); - return S_OK; + std::unique_ptr file(new QFile(fi.absoluteFilePath())); + if (!file->open(QIODevice::WriteOnly)) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Could not open " + "file '%1': %2").arg(fi.absoluteFilePath(), file->errorString())); + return E_FAIL; } - return E_FAIL; + CMyComPtr stream = + new QIODeviceSequentialOutStream(std::move(file)); + *outStream = stream.Detach(); // CMyComPtr is needed, otherwise it crashes in Write(). } - 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; + + UString s; + if (arc->GetItemPath(currentIndex, s) != S_OK) { + setLastError(QCoreApplication::translate("ExtractCallbackImpl", + "Could not retrieve path of archive item %1.").arg(currentIndex)); + return E_FAIL; } - STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) - { - Q_UNUSED(resultEOperationResult) - - if (!targetDir.isEmpty()) { - bool hasPerm = false; - const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, - &hasPerm); - - 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)) { + const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, + UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')))).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)) { #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", + "Could not 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", + "Could not 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", + "Could not 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; - } - - STDMETHOD(SetTotal)(UInt64 t) - { - total = t; - return S_OK; - } + bool hasPerm = false; + const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm); + if (hasPerm) + QFile::setPermissions(absFilePath, permissions); + return S_OK; +} - STDMETHOD(SetCompleted)(const UInt64 *c) - { - completed = *c; - if (total > 0) - return q->setCompleted(completed, total); - return S_OK; - } +/*! + \fn bool ExtractCallback::prepareForFile(const QString &filename) - void setArchive(const CArc *archive) - { - arc = archive; - } + 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. +*/ -private: - ExtractCallback *const q; - UInt32 currentIndex; - const CArc *arc; - UInt64 total; - UInt64 completed; - QPointer device; - QString targetDir; -}; +// -- UpdateCallback -class Lib7z::ExtractCallbackPrivate +HRESULT UpdateCallback::SetTotal(UInt64) { -public: - explicit ExtractCallbackPrivate(ExtractCallback *qq) - { - impl = new ExtractCallbackImpl(qq); - } - - CMyComPtr impl; -}; + return S_OK; +} -ExtractCallback::ExtractCallback() - : d(new ExtractCallbackPrivate(this)) +HRESULT UpdateCallback::SetCompleted(const UInt64*) { + return S_OK; } -ExtractCallback::~ExtractCallback() +HRESULT UpdateCallback::SetRatioInfo(const UInt64*, const UInt64*) { - delete d; + return S_OK; } -ExtractCallbackImpl *ExtractCallback::impl() +HRESULT UpdateCallback::CheckBreak() { - return d->impl; + return S_OK; } -const ExtractCallbackImpl *ExtractCallback::impl() const +HRESULT UpdateCallback::Finilize() { - return d->impl; + return S_OK; } -void ExtractCallback::setTarget(QFileDevice *dev) +HRESULT UpdateCallback::SetNumFiles(UInt64) { - d->impl->setTarget(dev); + return S_OK; } -void ExtractCallback::setTarget(const QString &dir) +HRESULT UpdateCallback::GetStream(const wchar_t*, bool) { - d->impl->setTarget(dir); + return S_OK; } -HRESULT ExtractCallback::setCompleted(quint64, quint64) +HRESULT UpdateCallback::OpenFileError(const wchar_t*, DWORD) { return S_OK; } -void ExtractCallback::setCurrentFile(const QString&) +HRESULT UpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { + *password = 0; + *passwordIsDefined = false; + return S_OK; } -/*! - Reimplement 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. Default implementation returns - \c true. -*/ -bool ExtractCallback::prepareForFile(const QString &filename) +HRESULT UpdateCallback::CryptoGetTextPassword(BSTR *password) { - Q_UNUSED(filename) - return true; + *password = 0; + return E_NOTIMPL; } -class Lib7z::UpdateCallbackImpl : public IUpdateCallbackUI2, public CMyUnknownImp +HRESULT UpdateCallback::OpenResult(const wchar_t*, HRESULT, const wchar_t*) { -public: - MY_UNKNOWN_IMP - UpdateCallbackImpl() - { - } - - virtual ~UpdateCallbackImpl() - { - } - - HRESULT SetTotal(UInt64) - { - return S_OK; - } - - 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; - } - - HRESULT SetOperationResult(Int32) - { - // TODO! What? - return S_OK; - } - - void setSourcePaths(const QStringList &paths) - { - sourcePaths = paths; - } - - void setTarget(QIODevice *archive) - { - target = archive; - } - -private: - QIODevice *target; - QStringList sourcePaths; -}; + return S_OK; +} -class Lib7z::UpdateCallbackPrivate +HRESULT UpdateCallback::StartScanning() { -public: - UpdateCallbackPrivate() - { - m_impl = new UpdateCallbackImpl; - } - - UpdateCallbackImpl *impl() - { - return m_impl; - } + return S_OK; +} -private: - CMyComPtr< UpdateCallbackImpl > m_impl; -}; +HRESULT UpdateCallback::ScanProgress(UInt64, UInt64, UInt64, const wchar_t*, bool) +{ + return S_OK; +} -UpdateCallback::UpdateCallback() - : d(new UpdateCallbackPrivate) +HRESULT UpdateCallback::CanNotFindError(const wchar_t*, DWORD) { + return S_OK; } -UpdateCallback::~UpdateCallback() +HRESULT UpdateCallback::FinishScanning() { - delete d; + return S_OK; } -UpdateCallbackImpl *UpdateCallback::impl() +HRESULT UpdateCallback::StartArchive(const wchar_t*, bool) { - return d->impl(); + return S_OK; } -void UpdateCallback::setSourcePaths(const QStringList &paths) +HRESULT UpdateCallback::FinishArchive() { - d->impl()->setSourcePaths(paths); + return S_OK; } -void UpdateCallback::setTarget(QFileDevice *target) +HRESULT UpdateCallback::SetOperationResult(Int32) { - d->impl()->setTarget(target); + return S_OK; } -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; +/*! + 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() +{ + QTemporaryFile file; + if (!file.open()) { + throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not create " + "temporary file. Error: %1").arg(file.errorString())); } + + 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 Lib7z::createArchive(QFileDevice *archive, const QStringList &sourcePaths, UpdateCallback *callback) +/*! + Creates an archive using the 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. Also the + \c * wildcard is supported.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 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, + UpdateCallback *callback) { - assert(archive); + LIB7Z_ASSERTS(archive, Writable) - QScopedPointer dummyCallback(callback ? 0 : new UpdateCallback); - if (!callback) - callback = dummyCallback.data(); + const QString tmpArchive = createTmp7z(); + Lib7z::createArchive(tmpArchive, sources, QTmpFile::No, callback); try { - callback->setTarget(archive); - - QScopedPointer 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... + QFile source(tmpArchive); + QInstaller::openForRead(&source); + QInstaller::blockingCopy(&source, archive, source.size()); + } catch (const QInstaller::Error &error) { + throw SevenZipException(error.message()); + } +} - const QString tempFile = generateTempFileName(); +/*! + 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 \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, + UpdateCallback *callback) +{ + try { + QString target = archive; + if (mode == QTmpFile::Yes) + target = createTmp7z(); - 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*/); + static const UString command = QString2UString( + // (mode: add) (type: 7z) (time: modified|creation|access) (threads: multi-threaded) + QLatin1String("a -t7z -mtm=on -mtc=on -mta=on -mmt=on ") +#ifdef Q_OS_WIN + + QLatin1String("-sccUTF-8 ") // (files: case-sensitive|UTF8) +#endif + + QDir::toNativeSeparators(target) + QLatin1Char(' ') + + QDir::toNativeSeparators(sources.join(QLatin1Char(' '))) + ); + + CArcCmdLineOptions options; + try { + UStringVector commandStrings; + NCommandLineParser::SplitCommandLine(command, commandStrings); + + 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; - - 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); + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs.")); - // preserve access time - CProperty ta; - ta.Name = UString(L"TA"); - ta.Value = UString(L"ON"); - options.MethodMode.Properties.Add(ta); + CObjectVector 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 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()) { + throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not create " + "archive '%1'. %2").arg(tempFile.fileName(), errorMessageFrom7zResult(res))); } - { - //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", "Could not remove " + "old archive '%1'. Error: %2").arg(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", "Could not rename " + "temporary archive '%1' to '%2'. Error: %3").arg(arc.fileName(), 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))); } } /*! - Extracts the given File \a file from \a archive into output device \a target using - the provided extract callback \a callback. Throws Lib7z::SevenZipException on error. + 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 Lib7z::extractFileFromArchive(QFileDevice *archive, const File &file, QFileDevice *target, - ExtractCallback *callback) +void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallback *callback) { - assert(archive); - assert(target); + LIB7Z_ASSERTS(archive, Readable) - std::auto_ptr dummyCallback(callback ? 0 : new ExtractCallback); - if (!callback) - callback = dummyCallback.get(); - - try { - const OpenArchiveInfo *const openArchive = OpenArchiveInfo::value(archive); + // Guard a given object against unwanted delete. + CMyComPtr externCallback = callback; - const int arcIdx = file.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; + CMyComPtr localCallback; + if (!externCallback) { + callback = new ExtractCallback; + localCallback = callback; + } - const UInt32 itemIdx = file.archiveIndex.y(); - UInt32 numItems = 0; - if (parchive->GetNumberOfItems(&numItems) != S_OK) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Could not retrieve number of items in archive")); - } + DirectoryGuard outDir(QFileInfo(directory).absolutePath()); + try { + outDir.tryCreate(); - if (itemIdx >= numItems) { - throw SevenZipException(QCoreApplication::translate("Lib7z", - "Item index %1 out of bounds [0, %2]").arg(itemIdx).arg(numItems - 1)); - } + CCodecs codecs; + if (codecs.Load() != S_OK) + throw SevenZipException(QCoreApplication::translate("Lib7z", "Could not load codecs.")); - 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(file.path == UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'))); + COpenOptions op; + op.codecs = &codecs; - callback->setTarget(target); - const LONG extractResult = parchive->Extract(&itemIdx, 1, /*testmode=*/0, callback->impl()); + CObjectVector types; + op.types = &types; // Empty, because we use a stream. - if (extractResult != S_OK) - throw SevenZipException(errorMessageFrom7zResult(extractResult)); + CIntVector excluded; + op.excludedFormats = &excluded; - } 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))); - } -} + const CMyComPtr stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). -/*! - Extracts the given File \a file from \a archive into target directory \a directory using the - provided extract callback \a callback. The output filename is deduced from the \a file path - name. Throws Lib7z::SevenZipException on error. -*/ + CObjectVector properties; + op.props = &properties; -void Lib7z::extractFileFromArchive(QFileDevice *archive, const File &file, const QString &directory, - ExtractCallback *callback) -{ - assert(archive); + CArchiveLink archiveLink; + if (archiveLink.Open2(op, nullptr) != S_OK) { + throw SevenZipException(QCoreApplication::translate("Lib7z", + "Could not open archive '%1'.").arg(archive->fileName())); + } - QScopedPointer 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(directory + QLatin1String("/") + file.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(-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, file, &out, callback); - if (file.permissions) - out.setPermissions(file.permissions); outDir.release(); + externCallback.Detach(); } /*! - 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. - Throws Lib7z::SevenZipException on error. -*/ - -void Lib7z::extractArchive(QFileDevice *archive, const QString &directory, ExtractCallback *callback) -{ - assert(archive); - - QScopedPointer dummyCallback(callback ? 0 : new ExtractCallback); - if (!callback) - callback = dummyCallback.data(); + Returns \c true if the given \a archive is supported; otherwise returns \c false. - callback->setTarget(directory); - - const QFileInfo fi(directory); - 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(-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. Throws - Lib7z::SevenZipException on error. + \note Throws SevenZipException on error. */ -bool Lib7z::isSupportedArchive(const QString &archive) +bool isSupportedArchive(QFileDevice *archive) { - QFile file(archive); - if (!file.open(QIODevice::ReadOnly)) - return false; - - return isSupportedArchive(&file); -} + LIB7Z_ASSERTS(archive, Readable) -/*! - Returns \c true if the given \a archive is supported; otherwise returns \c false. Throws - Lib7z::SevenZipException on error. -*/ -bool Lib7z::isSupportedArchive(QFileDevice *archive) -{ - assert(archive); - assert(!archive->isSequential()); const qint64 initialPos = archive->pos(); try { - QScopedPointer 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", "Could not load codecs.")); - CIntVector formatIndices; + COpenOptions op; + op.codecs = &codecs; + + CObjectVector 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 stream = new QIODeviceInStream(archive); + op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). - const HRESULT result = archiveLink.Open2(codecs.data(), formatIndices, /*stdInMode*/false, stream, - UString(), 0); + CObjectVector properties; + op.props = &properties; + + 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 } + +/*! + Returns \c true if the given \a archive is supported; otherwise returns \c false. + + \note Throws SevenZipException on error. +*/ +bool isSupportedArchive(const QString &archive) +{ + QFile file(archive); + if (!file.open(QIODevice::ReadOnly)) + return false; + return isSupportedArchive(&file); +} + +} // namespace Lib7z -- cgit v1.2.3