1 /*
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-2019 Mark Jansen (mark.jansen@reactos.org)
6 * Copyright 2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 */
8
9 #include "precomp.h"
10 #include <atlpath.h>
11
12 class CZipExtract :
13 public IZip
14 {
15 CStringW m_Filename;
16 CStringW m_Directory;
17 CStringA m_Password;
18 bool m_DirectoryChanged;
19 unzFile uf;
20 public:
CZipExtract(PCWSTR Filename)21 CZipExtract(PCWSTR Filename)
22 :m_DirectoryChanged(false)
23 ,uf(NULL)
24 {
25 m_Filename = Filename;
26 m_Directory = m_Filename;
27 PWSTR Dir = m_Directory.GetBuffer();
28 PathRemoveExtensionW(Dir);
29 m_Directory.ReleaseBuffer();
30 }
31
~CZipExtract()32 ~CZipExtract()
33 {
34 if (uf)
35 {
36 DPRINT1("WARNING: uf not closed!\n");
37 Close();
38 }
39 }
40
Close()41 void Close()
42 {
43 if (uf)
44 unzClose(uf);
45 uf = NULL;
46 }
47
48 // *** IZip methods ***
QueryInterface(REFIID riid,void ** ppvObject)49 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
50 {
51 if (riid == IID_IUnknown)
52 {
53 *ppvObject = this;
54 AddRef();
55 return S_OK;
56 }
57 return E_NOINTERFACE;
58 }
AddRef(void)59 STDMETHODIMP_(ULONG) AddRef(void)
60 {
61 return 2;
62 }
Release(void)63 STDMETHODIMP_(ULONG) Release(void)
64 {
65 return 1;
66 }
getZip()67 STDMETHODIMP_(unzFile) getZip()
68 {
69 return uf;
70 }
71
72 class CExtractSettingsPage : public CPropertyPageImpl<CExtractSettingsPage>
73 {
74 private:
75 HANDLE m_hExtractionThread;
76 bool m_bExtractionThreadCancel;
77
78 CZipExtract* m_pExtract;
79 CStringA* m_pPassword;
80 CStringW m_OldStatus;
81
82 public:
CExtractSettingsPage(CZipExtract * extract,CStringA * password)83 CExtractSettingsPage(CZipExtract* extract, CStringA* password)
84 :CPropertyPageImpl<CExtractSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
85 ,m_hExtractionThread(NULL)
86 ,m_bExtractionThreadCancel(false)
87 ,m_pExtract(extract)
88 ,m_pPassword(password)
89 {
90 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_TITLE);
91 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_WIZ_DEST_SUBTITLE);
92 m_psp.dwFlags |= PSP_USETITLE | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
93 }
94
OnSetActive()95 int OnSetActive()
96 {
97 SetDlgItemTextW(IDC_DIRECTORY, m_pExtract->m_Directory);
98 m_pExtract->m_DirectoryChanged = false;
99 GetParent().CenterWindow(::GetDesktopWindow());
100 SetWizardButtons(PSWIZB_NEXT);
101 return 0;
102 }
103
OnWizardNext()104 int OnWizardNext()
105 {
106 if (m_hExtractionThread != NULL)
107 {
108 /* We enter here when extraction has finished, and go to next page if it succeeded */
109 WaitForSingleObject(m_hExtractionThread, INFINITE);
110 CloseHandle(m_hExtractionThread);
111 m_hExtractionThread = NULL;
112 m_pExtract->Release();
113 if (!m_bExtractionThreadCancel)
114 {
115 return 0;
116 }
117 else
118 {
119 SetWindowLongPtr(DWLP_MSGRESULT, -1);
120 return TRUE;
121 }
122 }
123
124 /* We end up here if the user manually clicks Next: start extraction */
125 m_bExtractionThreadCancel = false;
126
127 /* Grey out every control during extraction to prevent user interaction */
128 ::EnableWindow(GetDlgItem(IDC_BROWSE), FALSE);
129 ::EnableWindow(GetDlgItem(IDC_DIRECTORY), FALSE);
130 ::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE);
131 SetWizardButtons(0);
132
133 ::GetWindowTextW(GetDlgItem(IDC_STATUSTEXT), m_OldStatus.GetBuffer(MAX_PATH), MAX_PATH);
134 m_OldStatus.ReleaseBuffer();
135 CStringW strExtracting(MAKEINTRESOURCEW(IDS_EXTRACTING));
136 SetDlgItemTextW(IDC_STATUSTEXT, strExtracting);
137
138 if (m_pExtract->m_DirectoryChanged)
139 UpdateDirectory();
140
141 m_pExtract->AddRef();
142
143 m_hExtractionThread = CreateThread(NULL, 0,
144 &CExtractSettingsPage::ExtractEntry,
145 this,
146 0, NULL);
147 if (!m_hExtractionThread)
148 {
149 /* Extraction thread creation failed, do not go to the next page */
150 DWORD err = GetLastError();
151 DPRINT1("ERROR, m_hExtractionThread: CreateThread failed: 0x%x\n", err);
152 m_pExtract->Release();
153
154 SetWindowLongPtr(DWLP_MSGRESULT, -1);
155
156 ::EnableWindow(GetDlgItem(IDC_BROWSE), TRUE);
157 ::EnableWindow(GetDlgItem(IDC_DIRECTORY), TRUE);
158 ::EnableWindow(GetDlgItem(IDC_PASSWORD), TRUE);
159 SetWizardButtons(PSWIZB_NEXT);
160 }
161 return TRUE;
162 }
163
WizardReset()164 void WizardReset()
165 {
166 SetDlgItemTextW(IDC_STATUSTEXT, m_OldStatus);
167 }
168
ExtractEntry(LPVOID lpParam)169 static DWORD WINAPI ExtractEntry(LPVOID lpParam)
170 {
171 CExtractSettingsPage* pPage = (CExtractSettingsPage*)lpParam;
172 bool res = pPage->m_pExtract->Extract(pPage->m_hWnd, pPage->GetDlgItem(IDC_PROGRESS), &(pPage->m_bExtractionThreadCancel));
173 /* Failing and cancelling extraction both mean we stay on the same property page */
174 pPage->m_bExtractionThreadCancel = !res;
175
176 pPage->SetWizardButtons(PSWIZB_NEXT);
177 if (!res)
178 {
179 /* Extraction failed/cancelled: the page becomes interactive again */
180 ::EnableWindow(pPage->GetDlgItem(IDC_BROWSE), TRUE);
181 ::EnableWindow(pPage->GetDlgItem(IDC_DIRECTORY), TRUE);
182 ::EnableWindow(pPage->GetDlgItem(IDC_PASSWORD), TRUE);
183
184 /* Reset the progress bar's appearance */
185 CWindow Progress(pPage->GetDlgItem(IDC_PROGRESS));
186 Progress.SendMessage(PBM_SETRANGE32, 0, 1);
187 Progress.SendMessage(PBM_SETPOS, 0, 0);
188 pPage->WizardReset();
189 }
190 SendMessageCallback(pPage->GetParent().m_hWnd, PSM_PRESSBUTTON, PSBTN_NEXT, 0, NULL, NULL);
191
192 return 0;
193 }
194
OnQueryCancel()195 BOOL OnQueryCancel()
196 {
197 if (m_hExtractionThread != NULL)
198 {
199 /* Extraction will check the value of m_bExtractionThreadCancel between each file in the archive */
200 m_bExtractionThreadCancel = true;
201 return TRUE;
202 }
203 return FALSE;
204 }
205
206 struct browse_info
207 {
208 HWND hWnd;
209 PCWSTR Directory;
210 };
211
s_BrowseCallbackProc(HWND hWnd,UINT uMsg,LPARAM lp,LPARAM pData)212 static INT CALLBACK s_BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lp, LPARAM pData)
213 {
214 if (uMsg == BFFM_INITIALIZED)
215 {
216 browse_info* info = (browse_info*)pData;
217 CWindow dlg(hWnd);
218 dlg.SendMessage(BFFM_SETSELECTION, TRUE, (LPARAM)info->Directory);
219 dlg.CenterWindow(info->hWnd);
220 }
221 return 0;
222 }
223
OnBrowse(WORD wNotifyCode,WORD wID,HWND hWndCtl,BOOL & bHandled)224 LRESULT OnBrowse(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
225 {
226 BROWSEINFOW bi = { m_hWnd };
227 WCHAR path[MAX_PATH];
228 bi.pszDisplayName = path;
229 bi.lpfn = s_BrowseCallbackProc;
230 bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
231 CStringW title(MAKEINTRESOURCEW(IDS_WIZ_BROWSE_TITLE));
232 bi.lpszTitle = title;
233
234 if (m_pExtract->m_DirectoryChanged)
235 UpdateDirectory();
236
237 browse_info info = { m_hWnd, m_pExtract->m_Directory.GetString() };
238 bi.lParam = (LPARAM)&info;
239
240 CComHeapPtr<ITEMIDLIST> pidl;
241 pidl.Attach(SHBrowseForFolderW(&bi));
242
243 WCHAR tmpPath[MAX_PATH];
244 if (pidl && SHGetPathFromIDListW(pidl, tmpPath))
245 {
246 m_pExtract->m_Directory = tmpPath;
247 SetDlgItemTextW(IDC_DIRECTORY, m_pExtract->m_Directory);
248 m_pExtract->m_DirectoryChanged = false;
249 }
250 return 0;
251 }
252
OnEnChangeDirectory(WORD wNotifyCode,WORD wID,HWND hWndCtl,BOOL & bHandled)253 LRESULT OnEnChangeDirectory(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
254 {
255 m_pExtract->m_DirectoryChanged = true;
256 return 0;
257 }
258
OnPassword(WORD wNotifyCode,WORD wID,HWND hWndCtl,BOOL & bHandled)259 LRESULT OnPassword(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
260 {
261 CStringA Password;
262 if (_CZipAskPassword(m_hWnd, NULL, Password) == eAccept)
263 {
264 *m_pPassword = Password;
265 }
266 return 0;
267 }
268
UpdateDirectory()269 void UpdateDirectory()
270 {
271 GetDlgItemText(IDC_DIRECTORY, m_pExtract->m_Directory);
272 m_pExtract->m_DirectoryChanged = false;
273 }
274
275 public:
276 enum { IDD = IDD_PROPPAGEDESTINATION };
277
278 BEGIN_MSG_MAP(CCompleteSettingsPage)
279 COMMAND_ID_HANDLER(IDC_BROWSE, OnBrowse)
280 COMMAND_ID_HANDLER(IDC_PASSWORD, OnPassword)
281 COMMAND_HANDLER(IDC_DIRECTORY, EN_CHANGE, OnEnChangeDirectory)
282 CHAIN_MSG_MAP(CPropertyPageImpl<CExtractSettingsPage>)
283 END_MSG_MAP()
284 };
285
286
287 class CCompleteSettingsPage : public CPropertyPageImpl<CCompleteSettingsPage>
288 {
289 private:
290 CZipExtract* m_pExtract;
291
292 public:
CCompleteSettingsPage(CZipExtract * extract)293 CCompleteSettingsPage(CZipExtract* extract)
294 :CPropertyPageImpl<CCompleteSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
295 , m_pExtract(extract)
296 {
297 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_WIZ_COMPL_TITLE);
298 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_WIZ_COMPL_SUBTITLE);
299 m_psp.dwFlags |= PSP_USETITLE | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
300 }
301
302
OnSetActive()303 int OnSetActive()
304 {
305 SetWizardButtons(PSWIZB_FINISH);
306 CStringW Path = m_pExtract->m_Directory;
307 PWSTR Ptr = Path.GetBuffer(MAX_PATH);
308 RECT rc;
309 ::GetWindowRect(GetDlgItem(IDC_DESTDIR), &rc);
310 HDC dc = GetDC();
311 PathCompactPathW(dc, Ptr, rc.right - rc.left);
312 ReleaseDC(dc);
313 Path.ReleaseBuffer();
314 SetDlgItemTextW(IDC_DESTDIR, Path);
315 CheckDlgButton(IDC_SHOW_EXTRACTED, BST_CHECKED);
316 return 0;
317 }
OnWizardFinish()318 BOOL OnWizardFinish()
319 {
320 if (IsDlgButtonChecked(IDC_SHOW_EXTRACTED) == BST_CHECKED)
321 {
322 ShellExecuteW(NULL, L"explore", m_pExtract->m_Directory, NULL, NULL, SW_SHOW);
323 }
324 return FALSE;
325 }
326
327 public:
328 enum { IDD = IDD_PROPPAGECOMPLETE };
329
330 BEGIN_MSG_MAP(CCompleteSettingsPage)
331 CHAIN_MSG_MAP(CPropertyPageImpl<CCompleteSettingsPage>)
332 END_MSG_MAP()
333 };
334
335
336 /* NOTE: This callback is needed to set large icon correctly. */
s_PropSheetCallbackProc(HWND hwndDlg,UINT uMsg,LPARAM lParam)337 static INT CALLBACK s_PropSheetCallbackProc(HWND hwndDlg, UINT uMsg, LPARAM lParam)
338 {
339 if (uMsg == PSCB_INITIALIZED)
340 {
341 HICON hIcon = LoadIconW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDI_ZIPFLDR));
342 CWindow dlg(hwndDlg);
343 dlg.SetIcon(hIcon, TRUE);
344 }
345
346 return 0;
347 }
348
runWizard()349 void runWizard()
350 {
351 PROPSHEETHEADERW psh = { sizeof(psh), 0 };
352 psh.dwFlags = PSH_WIZARD97 | PSH_HEADER | PSH_USEICONID | PSH_USECALLBACK;
353 psh.hInstance = _AtlBaseModule.GetResourceInstance();
354
355 CExtractSettingsPage extractPage(this, &m_Password);
356 CCompleteSettingsPage completePage(this);
357 HPROPSHEETPAGE hpsp[] =
358 {
359 extractPage.Create(),
360 completePage.Create()
361 };
362
363 psh.phpage = hpsp;
364 psh.nPages = _countof(hpsp);
365 psh.pszIcon = MAKEINTRESOURCE(IDI_ZIPFLDR);
366 psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
367 psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
368 psh.pfnCallback = s_PropSheetCallbackProc;
369
370 PropertySheetW(&psh);
371 }
372
ExtractSingle(HWND hDlg,PCWSTR FullPath,bool is_dir,unz_file_info64 * Info,CStringW Name,CStringA Password,bool * bOverwriteAll,const bool * bCancel,int * ErrorCode)373 eZipExtractError ExtractSingle(
374 HWND hDlg,
375 PCWSTR FullPath,
376 bool is_dir,
377 unz_file_info64* Info,
378 CStringW Name,
379 CStringA Password,
380 bool* bOverwriteAll,
381 const bool* bCancel,
382 int* ErrorCode
383 )
384 {
385 int err;
386 BYTE Buffer[2048];
387 DWORD dwFlags = SHPPFW_DIRCREATE | (is_dir ? SHPPFW_NONE : SHPPFW_IGNOREFILENAME);
388 HRESULT hr = SHPathPrepareForWriteW(hDlg, NULL, FullPath, dwFlags);
389 if (FAILED_UNEXPECTEDLY(hr))
390 {
391 *ErrorCode = hr;
392 return eDirectoryError;
393 }
394 if (is_dir)
395 return eNoError;
396
397 if (Info->flag & MINIZIP_PASSWORD_FLAG)
398 {
399 eZipPasswordResponse Response = eAccept;
400 do
401 {
402 /* If there is a password set, try it */
403 if (!Password.IsEmpty())
404 {
405 err = unzOpenCurrentFilePassword(uf, Password);
406 if (err == UNZ_OK)
407 {
408 /* Try to read some bytes, because unzOpenCurrentFilePassword does not return failure */
409 char Buf[10];
410 err = unzReadCurrentFile(uf, Buf, sizeof(Buf));
411 unzCloseCurrentFile(uf);
412 if (err >= UNZ_OK)
413 {
414 /* 're'-open the file so that we can begin to extract */
415 err = unzOpenCurrentFilePassword(uf, Password);
416 break;
417 }
418 }
419 }
420 Response = _CZipAskPassword(hDlg, Name, Password);
421 } while (Response == eAccept);
422
423 if (Response == eSkip)
424 {
425 return eNoError;
426 }
427 else if (Response == eAbort)
428 {
429 return eExtractAbort;
430 }
431 }
432 else
433 {
434 err = unzOpenCurrentFile(uf);
435 }
436
437 if (err != UNZ_OK)
438 {
439 DPRINT1("ERROR, unzOpenCurrentFilePassword: 0x%x\n", err);
440 *ErrorCode = err;
441 return eOpenError;
442 }
443
444 HANDLE hFile = CreateFileW(FullPath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
445 if (hFile == INVALID_HANDLE_VALUE)
446 {
447 DWORD dwErr = GetLastError();
448 if (dwErr == ERROR_FILE_EXISTS)
449 {
450 bool bOverwrite = *bOverwriteAll;
451 if (!*bOverwriteAll)
452 {
453 eZipConfirmResponse Result = _CZipAskReplace(hDlg, FullPath);
454 switch (Result)
455 {
456 case eYesToAll:
457 *bOverwriteAll = true;
458 /* fall through */
459 case eYes:
460 bOverwrite = true;
461 break;
462 case eNo:
463 break;
464 case eCancel:
465 unzCloseCurrentFile(uf);
466 return eExtractAbort;
467 }
468 }
469
470 if (bOverwrite)
471 {
472 hFile = CreateFileW(FullPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
473 if (hFile == INVALID_HANDLE_VALUE)
474 {
475 dwErr = GetLastError();
476 }
477 }
478 else
479 {
480 unzCloseCurrentFile(uf);
481 return eNoError;
482 }
483 }
484 if (hFile == INVALID_HANDLE_VALUE)
485 {
486 unzCloseCurrentFile(uf);
487 DPRINT1("ERROR, CreateFile: 0x%x (%s)\n", dwErr, *bOverwriteAll ? "Y" : "N");
488 *ErrorCode = dwErr;
489 return eFileError;
490 }
491 }
492
493 do
494 {
495 if (*bCancel)
496 {
497 CloseHandle(hFile);
498 BOOL deleteResult = DeleteFileW(FullPath);
499 if (!deleteResult)
500 DPRINT1("ERROR, DeleteFile: 0x%x\n", GetLastError());
501 return eExtractAbort;
502 }
503
504 err = unzReadCurrentFile(uf, Buffer, sizeof(Buffer));
505
506 if (err < 0)
507 {
508 DPRINT1("ERROR, unzReadCurrentFile: 0x%x\n", err);
509 break;
510 }
511 else if (err > 0)
512 {
513 DWORD dwWritten;
514 if (!WriteFile(hFile, Buffer, err, &dwWritten, NULL))
515 {
516 DPRINT1("ERROR, WriteFile: 0x%x\n", GetLastError());
517 break;
518 }
519 if (dwWritten != (DWORD)err)
520 {
521 DPRINT1("ERROR, WriteFile: dwWritten:%d err:%d\n", dwWritten, err);
522 break;
523 }
524 }
525
526 } while (err > 0);
527
528 /* Update Filetime */
529 FILETIME LocalFileTime;
530 DosDateTimeToFileTime((WORD)(Info->dosDate >> 16), (WORD)Info->dosDate, &LocalFileTime);
531 FILETIME FileTime;
532 LocalFileTimeToFileTime(&LocalFileTime, &FileTime);
533 SetFileTime(hFile, &FileTime, &FileTime, &FileTime);
534
535 /* Done */
536 CloseHandle(hFile);
537
538 if (err)
539 {
540 unzCloseCurrentFile(uf);
541 DPRINT1("ERROR, unzReadCurrentFile2: 0x%x\n", err);
542 *ErrorCode = err;
543 return eUnpackError;
544 }
545 else
546 {
547 err = unzCloseCurrentFile(uf);
548 if (err != UNZ_OK)
549 {
550 DPRINT1("ERROR(non-fatal), unzCloseCurrentFile: 0x%x\n", err);
551 }
552 }
553 return eNoError;
554 }
555
Extract(HWND hDlg,HWND hProgress,const bool * bCancel)556 bool Extract(HWND hDlg, HWND hProgress, const bool* bCancel)
557 {
558 unz_global_info64 gi;
559 uf = unzOpen2_64(m_Filename.GetString(), &g_FFunc);
560 int err = unzGetGlobalInfo64(uf, &gi);
561 if (err != UNZ_OK)
562 {
563 DPRINT1("ERROR, unzGetGlobalInfo64: 0x%x\n", err);
564 Close();
565 return false;
566 }
567
568 CZipEnumerator zipEnum;
569 if (!zipEnum.initialize(this))
570 {
571 DPRINT1("ERROR, zipEnum.initialize\n");
572 Close();
573 return false;
574 }
575
576 CWindow Progress(hProgress);
577 Progress.SendMessage(PBM_SETRANGE32, 0, gi.number_entry);
578 Progress.SendMessage(PBM_SETPOS, 0, 0);
579
580 CStringW BaseDirectory = m_Directory;
581 CStringW Name;
582 CStringA Password = m_Password;
583 unz_file_info64 Info;
584 int CurrentFile = 0;
585 bool bOverwriteAll = false;
586 while (zipEnum.next(Name, Info))
587 {
588 if (*bCancel)
589 {
590 Close();
591 return false;
592 }
593
594 bool is_dir = Name.GetLength() > 0 && Name[Name.GetLength()-1] == '/';
595
596 // Build a combined path
597 CPathW FullPath(BaseDirectory);
598 FullPath += Name;
599
600 // We use SHPathPrepareForWrite for this path.
601 // SHPathPrepareForWrite will prepare the necessary directories.
602 // Windows and ReactOS SHPathPrepareForWrite do not support '/'.
603 FullPath.m_strPath.Replace(L'/', L'\\');
604
605 Retry:
606 eZipExtractError Result = ExtractSingle(hDlg, FullPath, is_dir, &Info, Name, Password, &bOverwriteAll, bCancel, &err);
607 if (Result != eDirectoryError)
608 CurrentFile++;
609 switch (Result)
610 {
611 case eNoError:
612 break;
613
614 case eExtractAbort:
615 case eUnpackError:
616 {
617 Close();
618 return false;
619 }
620
621 case eDirectoryError:
622 {
623 WCHAR StrippedPath[MAX_PATH] = { 0 };
624
625 StrCpyNW(StrippedPath, FullPath, _countof(StrippedPath));
626 if (!is_dir)
627 PathRemoveFileSpecW(StrippedPath);
628 PathStripPathW(StrippedPath);
629 if (ShowExtractError(hDlg, StrippedPath, err, eDirectoryError) == IDRETRY)
630 goto Retry;
631 Close();
632 return false;
633 }
634
635 case eFileError:
636 {
637 int Result = ShowExtractError(hDlg, FullPath, err, eFileError);
638 switch (Result)
639 {
640 case IDABORT:
641 Close();
642 return false;
643 case IDRETRY:
644 CurrentFile--;
645 goto Retry;
646 case IDIGNORE:
647 break;
648 }
649 break;
650 }
651
652 case eOpenError:
653 {
654 if (err == UNZ_BADZIPFILE &&
655 Info.compression_method != 0 &&
656 Info.compression_method != Z_DEFLATED &&
657 Info.compression_method != Z_BZIP2ED)
658 {
659 if (ShowExtractError(hDlg, FullPath, Info.compression_method, eOpenError) == IDYES)
660 break;
661 }
662 Close();
663 return false;
664 }
665 }
666 if (Result == eNoError && is_dir)
667 continue;
668 Progress.SendMessage(PBM_SETPOS, CurrentFile, 0);
669 }
670
671 Close();
672 return true;
673 }
674
ShowExtractError(HWND hDlg,PCWSTR path,int Error,eZipExtractError ErrorType)675 int ShowExtractError(HWND hDlg, PCWSTR path, int Error, eZipExtractError ErrorType)
676 {
677 CStringW strTitle(MAKEINTRESOURCEW(IDS_ERRORTITLE));
678 CStringW strErr, strText;
679 PWSTR Win32ErrorString;
680
681 if (ErrorType == eFileError || ErrorType == eOpenError)
682 strText.LoadString(IDS_CANTEXTRACTFILE);
683 else
684 strText.LoadString(GetModuleHandleA("shell32.dll"), 128); // IDS_CREATEFOLDER_DENIED
685
686 strText.FormatMessage(strText.GetString(), path);
687
688 if (ErrorType == eFileError || HRESULT_FACILITY(Error) == FACILITY_WIN32)
689 {
690 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
691 NULL, ErrorType == eFileError ? Error : HRESULT_CODE(Error), 0,
692 (PWSTR)&Win32ErrorString, 0, NULL) != 0)
693 {
694 strErr.SetString(Win32ErrorString);
695 LocalFree(Win32ErrorString);
696 }
697 }
698 if (ErrorType == eOpenError)
699 strErr.Format(IDS_DECOMPRESSERROR, Error);
700 else if (strErr.GetLength() == 0)
701 strErr.Format(IDS_UNKNOWNERROR, Error);
702
703 strText.Append(L"\r\n\r\n" + strErr);
704
705 UINT mbFlags = MB_ICONWARNING;
706 if (ErrorType == eDirectoryError)
707 mbFlags |= MB_RETRYCANCEL;
708 else if (ErrorType == eFileError)
709 mbFlags |= MB_ABORTRETRYIGNORE;
710 else if (ErrorType == eOpenError)
711 mbFlags |= MB_YESNO;
712
713 return MessageBoxW(hDlg, strText, strTitle, mbFlags);
714 }
715 };
716
717
_CZipExtract_runWizard(PCWSTR Filename)718 void _CZipExtract_runWizard(PCWSTR Filename)
719 {
720 CZipExtract extractor(Filename);
721 extractor.runWizard();
722 }
723
724