* PROJECT: ReactOS Zip Shell Extension
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Zip extraction
- * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
+ * COPYRIGHT: Copyright 2017-2019 Mark Jansen (mark.jansen@reactos.org)
*/
#include "precomp.h"
{
CStringW m_Filename;
CStringW m_Directory;
+ CStringA m_Password;
bool m_DirectoryChanged;
unzFile uf;
public:
class CExtractSettingsPage : public CPropertyPageImpl<CExtractSettingsPage>
{
private:
+ HANDLE m_hExtractionThread;
+ bool m_bExtractionThreadCancel;
+
CZipExtract* m_pExtract;
+ CStringA* m_pPassword;
public:
- CExtractSettingsPage(CZipExtract* extract)
+ CExtractSettingsPage(CZipExtract* extract, CStringA* password)
:CPropertyPageImpl<CExtractSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
+ ,m_hExtractionThread(NULL)
+ ,m_bExtractionThreadCancel(false)
,m_pExtract(extract)
+ ,m_pPassword(password)
{
m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_TITLE);
m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_SUBTITLE);
{
SetDlgItemTextW(IDC_DIRECTORY, m_pExtract->m_Directory);
m_pExtract->m_DirectoryChanged = false;
- ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE); /* Not supported for now */
GetParent().CenterWindow(::GetDesktopWindow());
SetWizardButtons(PSWIZB_NEXT);
return 0;
int OnWizardNext()
{
+ if (m_hExtractionThread != NULL)
+ {
+ /* We enter here when extraction has finished, and go to next page if it succeeded */
+ WaitForSingleObject(m_hExtractionThread, INFINITE);
+ CloseHandle(m_hExtractionThread);
+ m_hExtractionThread = NULL;
+ m_pExtract->Release();
+ if (!m_bExtractionThreadCancel)
+ {
+ return 0;
+ }
+ else
+ {
+ SetWindowLongPtr(DWLP_MSGRESULT, -1);
+ return TRUE;
+ }
+ }
+
+ /* We end up here if the user manually clicks Next: start extraction */
+ m_bExtractionThreadCancel = false;
+
+ /* Grey out every control during extraction to prevent user interaction */
::EnableWindow(GetDlgItem(IDC_BROWSE), FALSE);
::EnableWindow(GetDlgItem(IDC_DIRECTORY), FALSE);
::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE);
SetWizardButtons(0);
+ CStringW strExtracting(MAKEINTRESOURCEW(IDS_EXTRACTING));
+ SetDlgItemTextW(IDC_STATUSTEXT, strExtracting);
+
if (m_pExtract->m_DirectoryChanged)
UpdateDirectory();
- if (!m_pExtract->Extract(m_hWnd, GetDlgItem(IDC_PROGRESS)))
+ m_pExtract->AddRef();
+
+ m_hExtractionThread = CreateThread(NULL, 0,
+ &CExtractSettingsPage::ExtractEntry,
+ this,
+ 0, NULL);
+ if (!m_hExtractionThread)
{
- /* Extraction failed, do not go to the next page */
+ /* Extraction thread creation failed, do not go to the next page */
+ DWORD err = GetLastError();
+ DPRINT1("ERROR, m_hExtractionThread: CreateThread failed: 0x%x\n", err);
+ m_pExtract->Release();
+
SetWindowLongPtr(DWLP_MSGRESULT, -1);
::EnableWindow(GetDlgItem(IDC_BROWSE), TRUE);
::EnableWindow(GetDlgItem(IDC_DIRECTORY), TRUE);
- ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE); /* Not supported for now */
+ ::EnableWindow(GetDlgItem(IDC_PASSWORD), TRUE);
SetWizardButtons(PSWIZB_NEXT);
+ }
+ return TRUE;
+ }
- return TRUE;
+ static DWORD WINAPI ExtractEntry(LPVOID lpParam)
+ {
+ CExtractSettingsPage* pPage = (CExtractSettingsPage*)lpParam;
+ bool res = pPage->m_pExtract->Extract(pPage->m_hWnd, pPage->GetDlgItem(IDC_PROGRESS), &(pPage->m_bExtractionThreadCancel));
+ /* Failing and cancelling extraction both mean we stay on the same property page */
+ pPage->m_bExtractionThreadCancel = !res;
+
+ pPage->SetWizardButtons(PSWIZB_NEXT);
+ if (!res)
+ {
+ /* Extraction failed/cancelled: the page becomes interactive again */
+ ::EnableWindow(pPage->GetDlgItem(IDC_BROWSE), TRUE);
+ ::EnableWindow(pPage->GetDlgItem(IDC_DIRECTORY), TRUE);
+ ::EnableWindow(pPage->GetDlgItem(IDC_PASSWORD), TRUE);
+
+ /* Reset the progress bar's appearance */
+ CWindow Progress(pPage->GetDlgItem(IDC_PROGRESS));
+ Progress.SendMessage(PBM_SETRANGE32, 0, 1);
+ Progress.SendMessage(PBM_SETPOS, 0, 0);
}
+ SendMessageCallback(pPage->GetParent().m_hWnd, PSM_PRESSBUTTON, PSBTN_NEXT, 0, NULL, NULL);
+
return 0;
}
+
+ BOOL OnQueryCancel()
+ {
+ if (m_hExtractionThread != NULL)
+ {
+ /* Extraction will check the value of m_bExtractionThreadCancel between each file in the archive */
+ m_bExtractionThreadCancel = true;
+ return TRUE;
+ }
+ return FALSE;
+ }
struct browse_info
{
LRESULT OnPassword(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
+ CStringA Password;
+ if (_CZipAskPassword(m_hWnd, NULL, Password) == eAccept)
+ {
+ *m_pPassword = Password;
+ }
return 0;
}
psh.dwFlags = PSH_WIZARD97 | PSH_HEADER;
psh.hInstance = _AtlBaseModule.GetResourceInstance();
- CExtractSettingsPage extractPage(this);
+ CExtractSettingsPage extractPage(this, &m_Password);
CCompleteSettingsPage completePage(this);
HPROPSHEETPAGE hpsp[] =
{
PropertySheetW(&psh);
}
- bool Extract(HWND hDlg, HWND hProgress)
+ bool Extract(HWND hDlg, HWND hProgress, const bool* bCancel)
{
unz_global_info64 gi;
uf = unzOpen2_64(m_Filename.GetString(), &g_FFunc);
BYTE Buffer[2048];
CStringA BaseDirectory = m_Directory;
CStringA Name;
+ CStringA Password = m_Password;
unz_file_info64 Info;
int CurrentFile = 0;
bool bOverwriteAll = false;
while (zipEnum.next(Name, Info))
{
+ if (*bCancel)
+ {
+ Close();
+ return false;
+ }
+
bool is_dir = Name.GetLength() > 0 && Name[Name.GetLength()-1] == '/';
char CombinedPath[MAX_PATH * 2] = { 0 };
if (is_dir)
continue;
- const char* password = NULL;
- /* FIXME: Process password, if required and not specified, prompt the user */
- err = unzOpenCurrentFilePassword(uf, password);
+ if (Info.flag & MINIZIP_PASSWORD_FLAG)
+ {
+ eZipPasswordResponse Response = eAccept;
+ do
+ {
+ /* If there is a password set, try it */
+ if (!Password.IsEmpty())
+ {
+ err = unzOpenCurrentFilePassword(uf, Password);
+ if (err == UNZ_OK)
+ {
+ /* Try to read some bytes, because unzOpenCurrentFilePassword does not return failure */
+ char Buf[10];
+ err = unzReadCurrentFile(uf, Buf, sizeof(Buf));
+ unzCloseCurrentFile(uf);
+ if (err >= UNZ_OK)
+ {
+ /* 're'-open the file so that we can begin to extract */
+ err = unzOpenCurrentFilePassword(uf, Password);
+ break;
+ }
+ }
+ }
+ Response = _CZipAskPassword(hDlg, Name, Password);
+ } while (Response == eAccept);
+
+ if (Response == eSkip)
+ {
+ Progress.SendMessage(PBM_SETPOS, CurrentFile, 0);
+ continue;
+ }
+ else if (Response == eAbort)
+ {
+ Close();
+ return false;
+ }
+ }
+ else
+ {
+ err = unzOpenCurrentFile(uf);
+ }
+
if (err != UNZ_OK)
{
DPRINT1("ERROR, unzOpenCurrentFilePassword: 0x%x\n", err);
do
{
+ if (*bCancel)
+ {
+ CloseHandle(hFile);
+ BOOL deleteResult = DeleteFileA(FullPath);
+ if (deleteResult == 0)
+ DPRINT1("ERROR, DeleteFileA: 0x%x\n", GetLastError());
+ Close();
+ return false;
+ }
+
err = unzReadCurrentFile(uf, Buffer, sizeof(Buffer));
if (err < 0)
LocalFileTimeToFileTime(&LocalFileTime, &FileTime);
SetFileTime(hFile, &FileTime, &LastAccessTime, &FileTime);
- /* Done.. */
+ /* Done */
CloseHandle(hFile);
if (err)