2 * PROJECT: ReactOS Zip Shell Extension
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Zip extraction
5 * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
15 bool m_DirectoryChanged
;
18 CZipExtract(PCWSTR Filename
)
19 :m_DirectoryChanged(false)
22 m_Filename
= Filename
;
23 m_Directory
= m_Filename
;
24 PWSTR Dir
= m_Directory
.GetBuffer();
25 PathRemoveExtensionW(Dir
);
26 m_Directory
.ReleaseBuffer();
33 DPRINT1("WARNING: uf not closed!\n");
45 // *** IZip methods ***
46 STDMETHODIMP
QueryInterface(REFIID riid
, void **ppvObject
)
48 if (riid
== IID_IUnknown
)
56 STDMETHODIMP_(ULONG
) AddRef(void)
60 STDMETHODIMP_(ULONG
) Release(void)
64 STDMETHODIMP_(unzFile
) getZip()
69 class CExtractSettingsPage
: public CPropertyPageImpl
<CExtractSettingsPage
>
72 CZipExtract
* m_pExtract
;
75 CExtractSettingsPage(CZipExtract
* extract
)
76 :CPropertyPageImpl
<CExtractSettingsPage
>(MAKEINTRESOURCE(IDS_WIZ_TITLE
))
79 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_WIZ_DEST_TITLE
);
80 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_WIZ_DEST_SUBTITLE
);
81 m_psp
.dwFlags
|= PSP_USETITLE
| PSP_USEHEADERTITLE
| PSP_USEHEADERSUBTITLE
;
86 SetDlgItemTextW(IDC_DIRECTORY
, m_pExtract
->m_Directory
);
87 m_pExtract
->m_DirectoryChanged
= false;
88 ::EnableWindow(GetDlgItem(IDC_PASSWORD
), FALSE
); /* Not supported for now */
89 GetParent().CenterWindow(::GetDesktopWindow());
90 SetWizardButtons(PSWIZB_NEXT
);
96 ::EnableWindow(GetDlgItem(IDC_BROWSE
), FALSE
);
97 ::EnableWindow(GetDlgItem(IDC_DIRECTORY
), FALSE
);
98 ::EnableWindow(GetDlgItem(IDC_PASSWORD
), FALSE
);
101 if (m_pExtract
->m_DirectoryChanged
)
104 if (!m_pExtract
->Extract(m_hWnd
, GetDlgItem(IDC_PROGRESS
)))
106 /* Extraction failed, do not go to the next page */
107 SetWindowLongPtr(DWLP_MSGRESULT
, -1);
109 ::EnableWindow(GetDlgItem(IDC_BROWSE
), TRUE
);
110 ::EnableWindow(GetDlgItem(IDC_DIRECTORY
), TRUE
);
111 ::EnableWindow(GetDlgItem(IDC_PASSWORD
), FALSE
); /* Not supported for now */
112 SetWizardButtons(PSWIZB_NEXT
);
125 static INT CALLBACK
s_BrowseCallbackProc(HWND hWnd
, UINT uMsg
, LPARAM lp
, LPARAM pData
)
127 if (uMsg
== BFFM_INITIALIZED
)
129 browse_info
* info
= (browse_info
*)pData
;
131 dlg
.SendMessage(BFFM_SETSELECTION
, TRUE
, (LPARAM
)info
->Directory
);
132 dlg
.CenterWindow(info
->hWnd
);
137 LRESULT
OnBrowse(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
& bHandled
)
139 BROWSEINFOW bi
= { m_hWnd
};
140 WCHAR path
[MAX_PATH
];
141 bi
.pszDisplayName
= path
;
142 bi
.lpfn
= s_BrowseCallbackProc
;
143 bi
.ulFlags
= BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
144 CStringW
title(MAKEINTRESOURCEW(IDS_WIZ_BROWSE_TITLE
));
145 bi
.lpszTitle
= title
;
147 if (m_pExtract
->m_DirectoryChanged
)
150 browse_info info
= { m_hWnd
, m_pExtract
->m_Directory
.GetString() };
151 bi
.lParam
= (LPARAM
)&info
;
153 CComHeapPtr
<ITEMIDLIST
> pidl
;
154 pidl
.Attach(SHBrowseForFolderW(&bi
));
156 WCHAR tmpPath
[MAX_PATH
];
157 if (pidl
&& SHGetPathFromIDListW(pidl
, tmpPath
))
159 m_pExtract
->m_Directory
= tmpPath
;
160 SetDlgItemTextW(IDC_DIRECTORY
, m_pExtract
->m_Directory
);
161 m_pExtract
->m_DirectoryChanged
= false;
166 LRESULT
OnEnChangeDirectory(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
& bHandled
)
168 m_pExtract
->m_DirectoryChanged
= true;
172 LRESULT
OnPassword(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
& bHandled
)
177 void UpdateDirectory()
179 GetDlgItemText(IDC_DIRECTORY
, m_pExtract
->m_Directory
);
180 m_pExtract
->m_DirectoryChanged
= false;
184 enum { IDD
= IDD_PROPPAGEDESTINATION
};
186 BEGIN_MSG_MAP(CCompleteSettingsPage
)
187 COMMAND_ID_HANDLER(IDC_BROWSE
, OnBrowse
)
188 COMMAND_ID_HANDLER(IDC_PASSWORD
, OnPassword
)
189 COMMAND_HANDLER(IDC_DIRECTORY
, EN_CHANGE
, OnEnChangeDirectory
)
190 CHAIN_MSG_MAP(CPropertyPageImpl
<CExtractSettingsPage
>)
195 class CCompleteSettingsPage
: public CPropertyPageImpl
<CCompleteSettingsPage
>
198 CZipExtract
* m_pExtract
;
201 CCompleteSettingsPage(CZipExtract
* extract
)
202 :CPropertyPageImpl
<CCompleteSettingsPage
>(MAKEINTRESOURCE(IDS_WIZ_TITLE
))
203 , m_pExtract(extract
)
205 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_WIZ_COMPL_TITLE
);
206 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_WIZ_COMPL_SUBTITLE
);
207 m_psp
.dwFlags
|= PSP_USETITLE
| PSP_USEHEADERTITLE
| PSP_USEHEADERSUBTITLE
;
213 SetWizardButtons(PSWIZB_FINISH
);
214 CStringW Path
= m_pExtract
->m_Directory
;
215 PWSTR Ptr
= Path
.GetBuffer();
217 ::GetWindowRect(GetDlgItem(IDC_DESTDIR
), &rc
);
219 PathCompactPathW(dc
, Ptr
, rc
.right
- rc
.left
);
221 Path
.ReleaseBuffer();
222 SetDlgItemTextW(IDC_DESTDIR
, Path
);
223 CheckDlgButton(IDC_SHOW_EXTRACTED
, BST_CHECKED
);
226 BOOL
OnWizardFinish()
228 if (IsDlgButtonChecked(IDC_SHOW_EXTRACTED
) == BST_CHECKED
)
230 ShellExecuteW(NULL
, L
"explore", m_pExtract
->m_Directory
, NULL
, NULL
, SW_SHOW
);
236 enum { IDD
= IDD_PROPPAGECOMPLETE
};
238 BEGIN_MSG_MAP(CCompleteSettingsPage
)
239 CHAIN_MSG_MAP(CPropertyPageImpl
<CCompleteSettingsPage
>)
246 PROPSHEETHEADERW psh
= { sizeof(psh
), 0 };
247 psh
.dwFlags
= PSH_WIZARD97
| PSH_HEADER
;
248 psh
.hInstance
= _AtlBaseModule
.GetResourceInstance();
250 CExtractSettingsPage
extractPage(this);
251 CCompleteSettingsPage
completePage(this);
252 HPROPSHEETPAGE hpsp
[] =
254 extractPage
.Create(),
255 completePage
.Create()
259 psh
.nPages
= _countof(hpsp
);
261 PropertySheetW(&psh
);
264 bool Extract(HWND hDlg
, HWND hProgress
)
266 unz_global_info64 gi
;
267 uf
= unzOpen2_64(m_Filename
.GetString(), &g_FFunc
);
268 int err
= unzGetGlobalInfo64(uf
, &gi
);
271 DPRINT1("ERROR, unzGetGlobalInfo64: 0x%x\n", err
);
276 CZipEnumerator zipEnum
;
277 if (!zipEnum
.initialize(this))
279 DPRINT1("ERROR, zipEnum.initialize\n");
284 CWindow
Progress(hProgress
);
285 Progress
.SendMessage(PBM_SETRANGE32
, 0, gi
.number_entry
);
286 Progress
.SendMessage(PBM_SETPOS
, 0, 0);
289 CStringA BaseDirectory
= m_Directory
;
291 unz_file_info64 Info
;
293 bool bOverwriteAll
= false;
294 while (zipEnum
.next(Name
, Info
))
296 bool is_dir
= Name
.GetLength() > 0 && Name
[Name
.GetLength()-1] == '/';
298 char CombinedPath
[MAX_PATH
* 2] = { 0 };
299 PathCombineA(CombinedPath
, BaseDirectory
, Name
);
300 CStringA FullPath
= CombinedPath
;
301 FullPath
.Replace('/', '\\'); /* SHPathPrepareForWriteA does not handle '/' */
302 DWORD dwFlags
= SHPPFW_DIRCREATE
| (is_dir
? SHPPFW_NONE
: SHPPFW_IGNOREFILENAME
);
303 HRESULT hr
= SHPathPrepareForWriteA(hDlg
, NULL
, FullPath
, dwFlags
);
304 if (FAILED_UNEXPECTEDLY(hr
))
313 const char* password
= NULL
;
314 /* FIXME: Process password, if required and not specified, prompt the user */
315 err
= unzOpenCurrentFilePassword(uf
, password
);
318 DPRINT1("ERROR, unzOpenCurrentFilePassword: 0x%x\n", err
);
323 HANDLE hFile
= CreateFileA(FullPath
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, NULL
);
324 if (hFile
== INVALID_HANDLE_VALUE
)
326 DWORD dwErr
= GetLastError();
327 if (dwErr
== ERROR_FILE_EXISTS
)
329 bool bOverwrite
= bOverwriteAll
;
332 eZipConfirmResponse Result
= _CZipAskReplace(hDlg
, FullPath
);
336 bOverwriteAll
= true;
343 unzCloseCurrentFile(uf
);
351 hFile
= CreateFileA(FullPath
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
352 if (hFile
== INVALID_HANDLE_VALUE
)
354 dwErr
= GetLastError();
359 unzCloseCurrentFile(uf
);
363 if (hFile
== INVALID_HANDLE_VALUE
)
365 unzCloseCurrentFile(uf
);
366 DPRINT1("ERROR, CreateFileA: 0x%x (%s)\n", dwErr
, bOverwriteAll
? "Y" : "N");
374 err
= unzReadCurrentFile(uf
, Buffer
, sizeof(Buffer
));
378 DPRINT1("ERROR, unzReadCurrentFile: 0x%x\n", err
);
384 if (!WriteFile(hFile
, Buffer
, err
, &dwWritten
, NULL
))
386 DPRINT1("ERROR, WriteFile: 0x%x\n", GetLastError());
389 if (dwWritten
!= (DWORD
)err
)
391 DPRINT1("ERROR, WriteFile: dwWritten:%d err:%d\n", dwWritten
, err
);
398 /* Update Filetime */
399 FILETIME LastAccessTime
;
400 GetFileTime(hFile
, NULL
, &LastAccessTime
, NULL
);
401 FILETIME LocalFileTime
;
402 DosDateTimeToFileTime((WORD
)(Info
.dosDate
>> 16), (WORD
)Info
.dosDate
, &LocalFileTime
);
404 LocalFileTimeToFileTime(&LocalFileTime
, &FileTime
);
405 SetFileTime(hFile
, &FileTime
, &LastAccessTime
, &FileTime
);
412 unzCloseCurrentFile(uf
);
413 DPRINT1("ERROR, unzReadCurrentFile2: 0x%x\n", err
);
419 err
= unzCloseCurrentFile(uf
);
422 DPRINT1("ERROR(non-fatal), unzCloseCurrentFile: 0x%x\n", err
);
425 Progress
.SendMessage(PBM_SETPOS
, CurrentFile
, 0);
434 void _CZipExtract_runWizard(PCWSTR Filename
)
436 CZipExtract
extractor(Filename
);
437 extractor
.runWizard();