/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "unziptask.h" #include "lib7z_facade.h" #ifdef Q_OS_UNIX # include "StdAfx.h" #endif #include "Common/ComTry.h" // TODO: include once we switch from lib7z_fascade.h //#include "Common/MyInitGuid.h" #include "7zip/Archive/IArchive.h" #include "7zip/Common/FileStreams.h" #include "7zip/UI/Common/OpenArchive.h" #include "Windows/FileDir.h" #include "Windows/PropVariant.h" #include namespace QInstaller { /*! \inmodule QtInstallerFramework \class QInstaller::UnzipTask \internal */ class ArchiveExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp { public: MY_UNKNOWN_IMP ArchiveExtractCallback(const QString &target, const CArc &arc, QFutureInterface &fi) : m_target(target) , m_arc(arc) { m_futureInterface = &fi; } // -- IProgress STDMETHOD(SetTotal)(UInt64 total) { COM_TRY_BEGIN m_totalUnpacked = 0; m_totalUnpackedExpected = total; return S_OK; COM_TRY_END } STDMETHOD(SetCompleted)(const UInt64 *completeValue) { COM_TRY_BEGIN Q_UNUSED(completeValue) return S_OK; // return S_OK here as we do not support sub archive extracting COM_TRY_END } // -- IArchiveExtractCallback STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { if (m_futureInterface->isCanceled()) return E_FAIL; if (m_futureInterface->isPaused()) m_futureInterface->waitForResume(); COM_TRY_BEGIN *outStream = nullptr; m_currentIndex = index; if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) return E_FAIL; bool isDir = false; if (Archive_IsItem_Folder(m_arc.Archive, m_currentIndex, isDir) != S_OK) return E_FAIL; bool isEncrypted = false; if (Archive_GetItemBoolProp(m_arc.Archive, m_currentIndex, kpidEncrypted, isEncrypted) != S_OK) return E_FAIL; if (isDir || isEncrypted) return E_FAIL; UString itemPath; if (m_arc.GetItemPath(m_currentIndex, itemPath) != S_OK) return E_FAIL; QDir().mkpath(m_target); m_currentTarget = m_target + QLatin1Char('/') + QString::fromStdWString((const wchar_t*)(itemPath)) .replace(QLatin1Char('\\'), QLatin1Char('/')); m_outFileStream = new COutFileStream; CMyComPtr scopedPointer(m_outFileStream); if (!m_outFileStream->Open((wchar_t*)(m_currentTarget.utf16()), CREATE_ALWAYS)) return E_FAIL; m_outFileStreamComPtr = scopedPointer; *outStream = scopedPointer.Detach(); return S_OK; COM_TRY_END } STDMETHOD(PrepareOperation)(Int32 askExtractMode) { COM_TRY_BEGIN Q_UNUSED(askExtractMode) m_futureInterface->setProgressValueAndText(0, QLatin1String("Started to extract archive.")); return S_OK; // return S_OK here as we do not need to prepare anything COM_TRY_END } STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) { COM_TRY_BEGIN switch (resultEOperationResult) { case NArchive::NExtract::NOperationResult::kOK: break; default: // fall through and bail case NArchive::NExtract::NOperationResult::kCRCError: case NArchive::NExtract::NOperationResult::kDataError: case NArchive::NExtract::NOperationResult::kUnsupportedMethod: m_outFileStream->Close(); m_outFileStreamComPtr.Release(); return E_FAIL; } UInt32 attributes; if (GetAttributes(&attributes)) NWindows::NFile::NDir::SetFileAttrib((wchar_t*)(m_currentTarget.utf16()), attributes); FILETIME accessTime, creationTime, modificationTime; const bool writeAccessTime = GetTime(kpidATime, &accessTime); const bool writeCreationTime = GetTime(kpidCTime, &creationTime); const bool writeModificationTime = GetTime(kpidMTime, &modificationTime); m_outFileStream->SetTime((writeCreationTime ? &creationTime : nullptr), (writeAccessTime ? &accessTime : nullptr), (writeModificationTime ? &modificationTime : nullptr)); m_totalUnpacked += m_outFileStream->ProcessedSize; m_outFileStream->Close(); m_outFileStreamComPtr.Release(); m_futureInterface->reportResult(m_currentTarget); m_futureInterface->setProgressValueAndText(100 * m_totalUnpacked / m_totalUnpackedExpected, m_currentTarget); return S_OK; COM_TRY_END } private: bool GetAttributes(UInt32 *attributes) const { *attributes = 0; NWindows::NCOM::CPropVariant property; if (m_arc.Archive->GetProperty(m_currentIndex, kpidAttrib, &property) != S_OK) return false; if (property.vt != VT_UI4) return false; *attributes = property.ulVal; return (property.vt == VT_UI4); } bool GetTime(PROPID propertyId, FILETIME *filetime) const { if (!filetime) return false; filetime->dwLowDateTime = 0; filetime->dwHighDateTime = 0; NWindows::NCOM::CPropVariant property; if (m_arc.Archive->GetProperty(m_currentIndex, propertyId, &property) != S_OK) return false; if (property.vt != VT_FILETIME) return false; *filetime = property.filetime; return (filetime->dwHighDateTime != 0 || filetime->dwLowDateTime != 0); } private: QString m_target; const CArc &m_arc; QFutureInterface *m_futureInterface; UInt32 m_currentIndex; QString m_currentTarget; UInt64 m_totalUnpacked; UInt64 m_totalUnpackedExpected; COutFileStream *m_outFileStream; CMyComPtr m_outFileStreamComPtr; }; // -- UnzipTask UnzipTask::UnzipTask(const QString &source, const QString &target) : m_source(source) , m_target(target) { Lib7z::initSevenZ(); } void UnzipTask::doTask(QFutureInterface &fi) { fi.reportStarted(); CCodecs codecs; if (codecs.Load() != S_OK) return; COpenOptions op; op.codecs = &codecs; CObjectVector types; op.types = &types; // Empty, because we use a stream. CIntVector excluded; op.excludedFormats = &excluded; const CMyComPtr fileStream = new CInFileStream; fileStream->Open((wchar_t*) (m_source.utf16())); op.stream = fileStream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). CObjectVector properties; op.props = &properties; CArchiveLink archiveLink; if (archiveLink.Open2(op, nullptr) != S_OK) return; UINT32 count = 0; for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { const CArc& arc = archiveLink.Arcs[i]; UInt32 numItems = 0; if (arc.Archive->GetNumberOfItems(&numItems) != S_OK) break; count += numItems; } fi.setExpectedResultCount(count); for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { if (fi.isCanceled()) break; if (fi.isPaused()) fi.waitForResume(); const UInt32 extractAll = UInt32(-1); const CArc &arc = archiveLink.Arcs[i]; arc.Archive->Extract(0, extractAll, NArchive::NExtract::NAskMode::kExtract, new ArchiveExtractCallback(m_target, arc, fi)); } fi.reportFinished(); } } // namespace QInstaller