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.cpp1503
1 files changed, 613 insertions, 890 deletions
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 <Common/CommandLineParser.h>
+
+#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 <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;
-#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)
-
-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 <sys/stat.h>
#endif
-#include <memory>
+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 <cassert>
+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<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 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<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)
@@ -303,7 +365,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui
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;
@@ -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<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
@@ -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<char*>(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<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;
@@ -459,936 +487,631 @@ 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
+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<QFile::Permissions>(-1)
- || other.permissions == static_cast<QFile::Permissions>(-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<QFile::Permissions>(-1)
+ || rhs.permissions == static_cast<QFile::Permissions>(-1));
}
-class OpenArchiveInfo
+QVector<File> 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<IInStream> stream;
- QScopedPointer<CCodecs> codecs;
- OpenArchiveInfoCleaner *m_cleaner;
+ CObjectVector<COpenType> 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<IInStream> stream = new QIODeviceInStream(archive);
+ op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream().
-void OpenArchiveInfoCleaner::deviceDestroyed(QObject *dev)
-{
- QFileDevice *device = static_cast<QFileDevice*>(dev);
- Q_ASSERT(device);
- delete OpenArchiveInfo::take(device);
-}
+ CObjectVector<CProperty> properties;
+ op.props = &properties;
-QVector<File> 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<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"));
+ "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<File>(); // 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<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;
- }
+ 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<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", "Could not open "
+ "file '%1': %2").arg(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().
}
- 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<QIODevice> device;
- QString targetDir;
-};
+// -- UpdateCallback
-class Lib7z::ExtractCallbackPrivate
+HRESULT UpdateCallback::SetTotal(UInt64)
{
-public:
- explicit ExtractCallbackPrivate(ExtractCallback *qq)
- {
- impl = new ExtractCallbackImpl(qq);
- }
-
- CMyComPtr<ExtractCallbackImpl> 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<UpdateCallback> 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<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...
+ 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<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()) {
+ 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<ExtractCallback> 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<ExtractCallback> 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<ExtractCallback> 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<COpenType> 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<IInStream> 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<CProperty> 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<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(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<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, 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<ExtractCallback> 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<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. 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<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", "Could not 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().
- const HRESULT result = archiveLink.Open2(codecs.data(), formatIndices, /*stdInMode*/false, stream,
- UString(), 0);
+ CObjectVector<CProperty> 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