#include "ArchiveExtractCallback.h" #include "PropVariant2.h" #include "FileSys.h" #include "OutStreamWrapper.h" #include #include "../CPP/Common/MyCom.h" namespace SevenZip { namespace intl { const TString EmptyFileAlias = _T("[Content]"); ArchiveExtractCallback::ArchiveExtractCallback( const CMyComPtr< IInArchive >& archiveHandler, const TString& directory, OverwriteModeEnum mode, ProgressCallback* callback) : m_refCount(0) , m_archiveHandler(archiveHandler) , m_directory(directory) , m_callback(callback) , m_overwriteMode(mode) , m_totalSize(0) , PasswordIsDefined(false) { if (*m_directory.rbegin() != L'\\' && *m_directory.rbegin() != L'/') m_directory +=L'\\'; } ArchiveExtractCallback::~ArchiveExtractCallback() { } STDMETHODIMP ArchiveExtractCallback::QueryInterface(REFIID iid, void** ppvObject) { if (iid == __uuidof(IUnknown)) { *ppvObject = reinterpret_cast(this); AddRef(); return S_OK; } if (iid == IID_IArchiveExtractCallback) { *ppvObject = static_cast(this); AddRef(); return S_OK; } if (iid == IID_ICryptoGetTextPassword) { *ppvObject = static_cast(this); AddRef(); return S_OK; } if (iid == IID_ICompressProgressInfo) { *ppvObject = static_cast(this); AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) ArchiveExtractCallback::AddRef() { return static_cast(InterlockedIncrement(&m_refCount)); } STDMETHODIMP_(ULONG) ArchiveExtractCallback::Release() { ULONG res = static_cast(InterlockedDecrement(&m_refCount)); if (res == 0) { delete this; } return res; } STDMETHODIMP ArchiveExtractCallback::SetTotal(UInt64 size) { #ifdef _DEBUG wprintf_s(L"SetTotal:%llu\n", size); #endif // _DEBUG m_totalSize = size; // - SetTotal is never called for ZIP and 7z formats if (m_callback) { m_callback->OnStart(m_absPath, size); } return S_OK; } STDMETHODIMP ArchiveExtractCallback::SetCompleted(const UInt64* completeValue) { #ifdef _DEBUG wprintf_s(L"SetCompleted:%llu\n", *completeValue); #endif // _DEBUG completeValue; //Callback Event calls /* NB: - For ZIP format SetCompleted only called once per 1000 files in central directory and once per 100 in local ones. - For 7Z format SetCompleted is never called. */ if (m_callback) { //Don't call this directly, it will be called per file which is more consistent across archive types //TODO: incorporate better progress tracking //m_callback->OnProgress(m_absPath, *completeValue); } return S_OK; } STDMETHODIMP ArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { #ifdef _DEBUG wprintf_s(L"SetRatioInfo:%llu-%llu\n", *inSize, *outSize); #endif // _DEBUG if (m_callback) m_callback->OnRadio(*inSize, *outSize); return S_OK; } STDMETHODIMP ArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode) { try { // Retrieve all the various properties for the file at this index. GetPropertyFilePath(index); if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) { return S_OK; } GetPropertyAttributes(index); GetPropertyIsDir(index); GetPropertyModifiedTime(index); GetPropertyCreatedTime(index); GetPropertyAccessedTime(index); GetPropertySize(index); } catch (_com_error& ex) { wprintf_s(L"获取数据失败\n"); return ex.Error(); } // TODO: m_directory could be a relative path as "..\" m_absPath = FileSys::AppendPath(m_directory, m_relPath); if (m_isDir) { wprintf_s(L"DirBegin:%s\n", m_absPath.c_str()); FileSys::CreateDirectoryTree(m_absPath); *outStream = NULL; return S_OK; } if (m_callback) { if (!m_callback->OnFileBegin(m_directory, m_relPath)) { //stop decompress return E_FAIL; } //Skip file if (m_relPath.empty()) { *outStream = NULL; return S_OK; } } wprintf_s(L"FileBegin:%s\n", m_absPath.c_str()); if (m_overwriteMode == OverwriteMode::kRollBack) { TString BackupPath; if (FileSys::PathExists(m_absPath)) { BackupPath = FileSys::GetUniquePath(m_absPath); if (!FileSys::BackupFile(m_absPath, BackupPath)) return HRESULT_FROM_WIN32(GetLastError()); } RollBack_Info info; info.backup_path = BackupPath; info.original_path = m_absPath; m_rollbacks.push_back(info); } else if (FileSys::PathExists(m_absPath)) { if (m_overwriteMode == OverwriteMode::kSkipExisting) { *outStream = NULL; return S_OK; } else if (m_overwriteMode == OverwriteMode::kAutoRename) { m_absPath = FileSys::GetUniquePath(m_absPath); } else if (m_overwriteMode == OverwriteMode::kAutoRenameExisting) { TString rename_path = FileSys::GetUniquePath(m_absPath); if (!FileSys::RenameFile(m_absPath, rename_path)) { wprintf_s(L"重命名文件失败:%s-%s\n", m_absPath.c_str(), rename_path.c_str()); return HRESULT_FROM_WIN32(GetLastError()); } } else if (m_overwriteMode == OverwriteMode::kWithoutPrompt) { if (!FileSys::RemovePath(m_absPath)) { wprintf_s(L"移除路径失败:%s\n", m_absPath.c_str()); return HRESULT_FROM_WIN32(GetLastError()); } } else { wprintf_s(L"文件已存在:%s\n", m_absPath.c_str()); return ERROR_FILE_EXISTS; } } TString absDir = FileSys::GetPath(m_absPath); if (!FileSys::CreateDirectoryTree(absDir)) { wprintf_s(L"创建目录失败:%s\n", absDir.c_str()); return ERROR_CREATE_FAILED; } CMyComPtr< IStream > fileStream = FileSys::OpenFileToWrite(m_absPath); if (fileStream == NULL) { wprintf_s(L"创建文件失败:%s\n", m_absPath.c_str()); m_absPath.clear(); return HRESULT_FROM_WIN32(GetLastError()); } OutStreamWrapper* stream = new OutStreamWrapper(fileStream); if (!stream) { wprintf_s(L"内存不足\n"); return E_OUTOFMEMORY; } CMyComPtr< OutStreamWrapper > wrapperStream = stream; *outStream = wrapperStream.Detach(); return S_OK; } STDMETHODIMP ArchiveExtractCallback::PrepareOperation(Int32 /*askExtractMode*/) { return S_OK; } STDMETHODIMP ArchiveExtractCallback::SetOperationResult(Int32 operationResult) { if (m_overwriteMode == OverwriteMode::kRollBack && operationResult != S_OK) { _ASSERT_EXPR(FALSE,L"begin rollback"); bool succ = ProcessRollBack(); succ; _ASSERT_EXPR(succ, L"rollback error!"); return E_FAIL; } if (m_absPath.empty()) { wprintf_s(L"AllDone\n"); if (m_callback) m_callback->OnEnd(m_directory); return S_OK; } if (m_hasModifiedTime || m_hasAccessedTime || m_hasCreatedTime) { HANDLE fileHandle = CreateFile(m_absPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle != INVALID_HANDLE_VALUE) { SetFileTime(fileHandle, m_hasCreatedTime ? &m_createdTime : NULL, m_hasAccessedTime ? &m_accessedTime : NULL, m_hasModifiedTime ? &m_modifiedTime : NULL); CloseHandle(fileHandle); } } if (m_hasAttrib) { SetFileAttributes(m_absPath.c_str(), m_attrib); } wprintf_s(L"FileDone:%s\n", m_absPath.c_str()); if (m_callback) { if(!m_callback->OnFileDone(m_absPath, m_newFileSize)) return E_FAIL; m_callback->OnProgress(m_absPath, m_newFileSize); } return S_OK; } STDMETHODIMP ArchiveExtractCallback::CryptoGetTextPassword(BSTR* password) { if (!PasswordIsDefined) { // You can ask real password here from user // Password = GetPassword(OutStream); // PasswordIsDefined = true; printf("Password is not defined"); return E_ABORT; } return StringToBstr(Password, password); } void ArchiveExtractCallback::GetPropertyFilePath(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidPath, &prop); if (hr != S_OK) { _com_issue_error(hr); } if ( prop.vt == VT_EMPTY ) { m_relPath = EmptyFileAlias; } else if (prop.vt != VT_BSTR) { _com_issue_error(E_FAIL); } else { _bstr_t bstr = prop.bstrVal; #ifdef _UNICODE m_relPath = bstr; #else char relPath[MAX_PATH]; int size = WideCharToMultiByte(CP_UTF8, 0, bstr, bstr.length(), relPath, MAX_PATH, NULL, NULL); m_relPath.assign(relPath, size); #endif } } void ArchiveExtractCallback::GetPropertyAttributes(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidAttrib, &prop); if (hr != S_OK) { _com_issue_error(hr); } if (prop.vt == VT_EMPTY) { m_attrib = 0; m_hasAttrib = false; } else if (prop.vt != VT_UI4) { _com_issue_error(E_FAIL); } else { m_attrib = prop.ulVal; m_hasAttrib = true; } } void ArchiveExtractCallback::GetPropertyIsDir(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidIsDir, &prop); if (hr != S_OK) { _com_issue_error(hr); } if (prop.vt == VT_EMPTY) { m_isDir = false; } else if (prop.vt != VT_BOOL) { _com_issue_error(E_FAIL); } else { m_isDir = prop.boolVal != VARIANT_FALSE; } } void ArchiveExtractCallback::GetPropertyModifiedTime(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidMTime, &prop); if (hr != S_OK) { _com_issue_error(hr); } if (prop.vt == VT_EMPTY) { m_hasModifiedTime = false; } else if (prop.vt != VT_FILETIME) { _com_issue_error(E_FAIL); } else { m_modifiedTime = prop.filetime; m_hasModifiedTime = true; } } void ArchiveExtractCallback::GetPropertyAccessedTime(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidATime, &prop); if (hr != S_OK) { _com_issue_error(hr); } if (prop.vt == VT_EMPTY) { m_hasAccessedTime = false; } else if (prop.vt != VT_FILETIME) { _com_issue_error(E_FAIL); } else { m_accessedTime = prop.filetime; m_hasAccessedTime = true; } } void ArchiveExtractCallback::GetPropertyCreatedTime(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidCTime, &prop); if (hr != S_OK) { _com_issue_error(hr); } if (prop.vt == VT_EMPTY) { m_hasCreatedTime = false; } else if (prop.vt != VT_FILETIME) { _com_issue_error(E_FAIL); } else { m_createdTime = prop.filetime; m_hasCreatedTime = true; } } void ArchiveExtractCallback::GetPropertySize(UInt32 index) { CPropVariant prop; HRESULT hr = m_archiveHandler->GetProperty(index, kpidSize, &prop); if (hr != S_OK) { _com_issue_error(hr); } switch (prop.vt) { case VT_EMPTY: m_hasNewFileSize = false; return; case VT_UI1: m_newFileSize = prop.bVal; break; case VT_UI2: m_newFileSize = prop.uiVal; break; case VT_UI4: m_newFileSize = prop.ulVal; break; case VT_UI8: m_newFileSize = (UInt64)prop.uhVal.QuadPart; break; default: _com_issue_error(E_FAIL); } m_hasNewFileSize = true; } bool ArchiveExtractCallback::ProcessRollBack() { bool succ = true; for (size_t i = 0;iOnRollBack(a.original_path); if (a.backup_path.empty()) FileSys::RemovePath(a.original_path); else succ = FileSys::RenameFile(a.backup_path, a.original_path); } return succ; } } }