1 // PanelItemOpen.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/MyWindows.h"
6
7 #include <TlHelp32.h>
8
9 #include "../../../Common/IntToString.h"
10
11 #include "../../../Common/AutoPtr.h"
12
13 #include "../../../Windows/ProcessUtils.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17
18 #include "../../Common/FileStreams.h"
19 #include "../../Common/StreamObjects.h"
20
21 #include "../Common/ExtractingFilePath.h"
22
23 #include "App.h"
24
25 #include "FileFolderPluginOpen.h"
26 #include "FormatUtils.h"
27 #include "LangUtils.h"
28 #include "PropertyNameRes.h"
29 #include "RegistryUtils.h"
30 #include "UpdateCallback100.h"
31
32 #include "../GUI/ExtractRes.h"
33
34 #include "resource.h"
35
36 using namespace NWindows;
37 using namespace NSynchronization;
38 using namespace NFile;
39 using namespace NDir;
40
41 extern bool g_RAM_Size_Defined;
42 extern size_t g_RAM_Size;
43
44 #ifndef _UNICODE
45 extern bool g_IsNT;
46 #endif
47
48 #define kTempDirPrefix FTEXT("7zO")
49
50 // #define SHOW_DEBUG_INFO
51
52 #ifdef SHOW_DEBUG_INFO
53 #define DEBUG_PRINT(s) OutputDebugStringA(s);
54 #define DEBUG_PRINT_W(s) OutputDebugStringW(s);
55 #define DEBUG_PRINT_NUM(s, num) { char ttt[32]; ConvertUInt32ToString(num, ttt); OutputDebugStringA(s); OutputDebugStringA(ttt); }
56 #else
57 #define DEBUG_PRINT(s)
58 #define DEBUG_PRINT_W(s)
59 #define DEBUG_PRINT_NUM(s, num)
60 #endif
61
62
63
64 #ifndef UNDER_CE
65
66 class CProcessSnapshot
67 {
68 HANDLE _handle;
69 public:
CProcessSnapshot()70 CProcessSnapshot(): _handle(INVALID_HANDLE_VALUE) {}
~CProcessSnapshot()71 ~CProcessSnapshot() { Close(); }
72
Close()73 bool Close()
74 {
75 if (_handle == INVALID_HANDLE_VALUE)
76 return true;
77 if (!::CloseHandle(_handle))
78 return false;
79 _handle = INVALID_HANDLE_VALUE;
80 return true;
81 }
82
Create()83 bool Create()
84 {
85 _handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
86 return (_handle != INVALID_HANDLE_VALUE);
87 }
88
GetFirstProcess(PROCESSENTRY32 * pe)89 bool GetFirstProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32First(_handle, pe)); }
GetNextProcess(PROCESSENTRY32 * pe)90 bool GetNextProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32Next(_handle, pe)); }
91 };
92
93 #endif
94
95
96 /*
97 struct COpenExtProg
98 {
99 const char *Ext;
100 const char *Prog;
101 };
102
103 static const COpenExtProg g_Progs[] =
104 {
105 { "jpeg jpg png bmp gif", "Microsoft.Photos.exe" },
106 { "html htm pdf", "MicrosoftEdge.exe" },
107 // , { "rrr", "notepad.exe" }
108 };
109
110 static bool FindExtProg(const char *exts, const char *ext)
111 {
112 unsigned len = (unsigned)strlen(ext);
113 for (;;)
114 {
115 const char *p = exts;
116 for (;; p++)
117 {
118 const char c = *p;
119 if (c == 0 || c == ' ')
120 break;
121 }
122 if (len == (unsigned)(p - exts) && IsString1PrefixedByString2(exts, ext))
123 return true;
124 if (*p == 0)
125 return false;
126 exts = p + 1;
127 }
128 }
129
130 class CPossibleProgs
131 {
132 public:
133 AStringVector ProgNames;
134
135 void SetFromExtension(const char *ext) // ext must be low case
136 {
137 ProgNames.Clear();
138 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Progs); i++)
139 if (FindExtProg(g_Progs[i].Ext, ext))
140 {
141 ProgNames.Add(g_Progs[i].Prog);
142 }
143 }
144
145 bool IsFromList(const UString &progName) const
146 {
147 FOR_VECTOR (i, ProgNames)
148 if (progName.IsEqualTo_Ascii_NoCase(ProgNames[i]))
149 return true;
150 return false;
151 }
152 };
153 */
154
155
156 #ifndef UNDER_CE
157
158 EXTERN_C_BEGIN
159
160 /*
161 GetProcessImageFileName
162 returns the path in device form, rather than drive letters:
163 \Device\HarddiskVolume1\WINDOWS\SysWOW64\notepad.exe
164
165 GetModuleFileNameEx works only after Sleep(something). Why?
166 returns the path
167 C:\WINDOWS\system32\NOTEPAD.EXE
168 */
169
170 /* Kernel32.dll: Win7, Win2008R2;
171 Psapi.dll: (if PSAPI_VERSION=1) on Win7 and Win2008R2;
172 Psapi.dll: XP, Win2003, Vista, 2008;
173 */
174
175 typedef DWORD (WINAPI *Func_GetProcessImageFileNameW)(
176 HANDLE hProcess, LPWSTR lpFilename, DWORD nSize);
177
178 typedef DWORD (WINAPI *Func_GetModuleFileNameExW)(
179 HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
180
181 typedef DWORD (WINAPI *Func_GetProcessId)(HANDLE process);
182
183 EXTERN_C_END
184
185
186 static HMODULE g_Psapi_dll_module;
187
188 /*
189 static void My_GetProcessFileName_2(HANDLE hProcess, UString &path)
190 {
191 path.Empty();
192 const unsigned maxPath = 1024;
193 WCHAR temp[maxPath + 1];
194
195 const char *func_name = "GetModuleFileNameExW";
196 Func_GetModuleFileNameExW my_func = (Func_GetModuleFileNameExW)
197 ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), func_name);
198 if (!my_func)
199 {
200 if (!g_Psapi_dll_module)
201 g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll");
202 if (g_Psapi_dll_module)
203 my_func = (Func_GetModuleFileNameExW)::GetProcAddress(g_Psapi_dll_module, func_name);
204 }
205 if (my_func)
206 {
207 // DWORD num = GetModuleFileNameEx(hProcess, NULL, temp, maxPath);
208 DWORD num = my_func(hProcess, NULL, temp, maxPath);
209 if (num != 0)
210 path = temp;
211 }
212 // FreeLibrary(lib);
213 }
214 */
215
216 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
217
My_GetProcessFileName(HANDLE hProcess,UString & path)218 static void My_GetProcessFileName(HANDLE hProcess, UString &path)
219 {
220 path.Empty();
221 const unsigned maxPath = 1024;
222 WCHAR temp[maxPath + 1];
223
224 const char *func_name =
225 "GetProcessImageFileNameW";
226 Func_GetProcessImageFileNameW my_func = Z7_GET_PROC_ADDRESS(
227 Func_GetProcessImageFileNameW, ::GetModuleHandleA("kernel32.dll"), func_name);
228
229 if (!my_func)
230 {
231 if (!g_Psapi_dll_module)
232 g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll");
233 if (g_Psapi_dll_module)
234 my_func = Z7_GET_PROC_ADDRESS(
235 Func_GetProcessImageFileNameW, g_Psapi_dll_module, func_name);
236 }
237
238 if (my_func)
239 {
240 const DWORD num =
241 // GetProcessImageFileNameW(hProcess, temp, maxPath);
242 my_func(hProcess, temp, maxPath);
243 if (num != 0)
244 path = temp;
245 }
246 // FreeLibrary(lib);
247 }
248
249 struct CSnapshotProcess
250 {
251 DWORD Id;
252 DWORD ParentId;
253 UString Name;
254 };
255
GetSnapshot(CObjectVector<CSnapshotProcess> & items)256 static void GetSnapshot(CObjectVector<CSnapshotProcess> &items)
257 {
258 items.Clear();
259
260 CProcessSnapshot snapshot;
261 if (!snapshot.Create())
262 return;
263
264 DEBUG_PRINT("snapshot.Create() OK");
265 PROCESSENTRY32 pe;
266 CSnapshotProcess item;
267 memset(&pe, 0, sizeof(pe));
268 pe.dwSize = sizeof(pe);
269 BOOL res = snapshot.GetFirstProcess(&pe);
270 while (res)
271 {
272 item.Id = pe.th32ProcessID;
273 item.ParentId = pe.th32ParentProcessID;
274 item.Name = GetUnicodeString(pe.szExeFile);
275 items.Add(item);
276 res = snapshot.GetNextProcess(&pe);
277 }
278 }
279
280 #endif
281
282
283 class CChildProcesses
284 {
285 #ifndef UNDER_CE
286 CRecordVector<DWORD> _ids;
287 #endif
288
289 public:
290 // bool ProgsWereUsed;
291 CRecordVector<HANDLE> Handles;
292 CRecordVector<bool> NeedWait;
293 // UStringVector Names;
294
295 #ifndef UNDER_CE
296 UString Path;
297 #endif
298
299 // CChildProcesses(): ProgsWereUsed(false) {}
~CChildProcesses()300 ~CChildProcesses() { CloseAll(); }
DisableWait(unsigned index)301 void DisableWait(unsigned index) { NeedWait[index] = false; }
302
CloseAll()303 void CloseAll()
304 {
305 FOR_VECTOR (i, Handles)
306 {
307 HANDLE h = Handles[i];
308 if (h != NULL)
309 CloseHandle(h);
310 }
311
312 Handles.Clear();
313 NeedWait.Clear();
314 // Names.Clear();
315
316 #ifndef UNDER_CE
317 // Path.Empty();
318 _ids.Clear();
319 #endif
320 }
321
SetMainProcess(HANDLE h)322 void SetMainProcess(HANDLE h)
323 {
324 #ifndef UNDER_CE
325 const
326 Func_GetProcessId func = Z7_GET_PROC_ADDRESS(
327 Func_GetProcessId, ::GetModuleHandleA("kernel32.dll"),
328 "GetProcessId");
329 if (func)
330 {
331 const DWORD id = func(h);
332 if (id != 0)
333 _ids.AddToUniqueSorted(id);
334 }
335
336 My_GetProcessFileName(h, Path);
337 DEBUG_PRINT_W(Path);
338
339 #endif
340
341 Handles.Add(h);
342 NeedWait.Add(true);
343 }
344
345 #ifndef UNDER_CE
346
Update(bool needFindProcessByPath)347 void Update(bool needFindProcessByPath /* , const CPossibleProgs &progs */)
348 {
349 /*
350 if (_ids.IsEmpty())
351 return;
352 */
353
354 CObjectVector<CSnapshotProcess> sps;
355 GetSnapshot(sps);
356
357 const int separ = Path.ReverseFind_PathSepar();
358 const UString mainName = Path.Ptr((unsigned)(separ + 1));
359 if (mainName.IsEmpty())
360 needFindProcessByPath = false;
361
362 const DWORD currentProcessId = GetCurrentProcessId();
363
364 for (;;)
365 {
366 bool wasAdded = false;
367
368 FOR_VECTOR (i, sps)
369 {
370 const CSnapshotProcess &sp = sps[i];
371 const DWORD id = sp.Id;
372
373 if (id == currentProcessId)
374 continue;
375 if (_ids.FindInSorted(id) >= 0)
376 continue;
377
378 bool isSameName = false;
379 const UString &name = sp.Name;
380
381 if (needFindProcessByPath)
382 isSameName = mainName.IsEqualTo_NoCase(name);
383
384 bool needAdd = false;
385 // bool isFromProgs = false;
386
387 if (isSameName || _ids.FindInSorted(sp.ParentId) >= 0)
388 needAdd = true;
389 /*
390 else if (progs.IsFromList(name))
391 {
392 needAdd = true;
393 isFromProgs = true;
394 }
395 */
396
397 if (needAdd)
398 {
399 DEBUG_PRINT("----- OpenProcess -----");
400 DEBUG_PRINT_W(name);
401 HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, id);
402 if (hProcess)
403 {
404 DEBUG_PRINT("----- OpenProcess OK -----");
405 // if (!isFromProgs)
406 _ids.AddToUniqueSorted(id);
407 Handles.Add(hProcess);
408 NeedWait.Add(true);
409 // Names.Add(name);
410 wasAdded = true;
411 // ProgsWereUsed = isFromProgs;
412 }
413 }
414 }
415
416 if (!wasAdded)
417 break;
418 }
419 }
420
421 #endif
422 };
423
424
425 struct CTmpProcessInfo: public CTempFileInfo
426 {
427 CChildProcesses Processes;
428 HWND Window;
429 UString FullPathFolderPrefix;
430 bool UsePassword;
431 UString Password;
432
433 bool ReadOnly;
434
CTmpProcessInfoCTmpProcessInfo435 CTmpProcessInfo(): UsePassword(false), ReadOnly(false) {}
436 };
437
438
439 class CTmpProcessInfoRelease
440 {
441 CTmpProcessInfo *_tmpProcessInfo;
442 public:
443 bool _needDelete;
CTmpProcessInfoRelease(CTmpProcessInfo & tpi)444 CTmpProcessInfoRelease(CTmpProcessInfo &tpi):
445 _tmpProcessInfo(&tpi), _needDelete(true) {}
~CTmpProcessInfoRelease()446 ~CTmpProcessInfoRelease()
447 {
448 if (_needDelete)
449 _tmpProcessInfo->DeleteDirAndFile();
450 }
451 };
452
453 void GetFolderError(CMyComPtr<IFolderFolder> &folder, UString &s);
454
455
OpenAsArc(IInStream * inStream,const CTempFileInfo & tempFileInfo,const UString & virtualFilePath,const UString & arcFormat,COpenResult & openRes)456 HRESULT CPanel::OpenAsArc(IInStream *inStream,
457 const CTempFileInfo &tempFileInfo,
458 const UString &virtualFilePath,
459 const UString &arcFormat,
460 COpenResult &openRes)
461 {
462 openRes.Encrypted = false;
463 CFolderLink folderLink;
464 (CTempFileInfo &)folderLink = tempFileInfo;
465
466 if (inStream)
467 folderLink.IsVirtual = true;
468 else
469 {
470 if (!folderLink.FileInfo.Find(folderLink.FilePath))
471 return GetLastError_noZero_HRESULT();
472 if (folderLink.FileInfo.IsDir())
473 return S_FALSE;
474 folderLink.IsVirtual = false;
475 }
476
477 folderLink.VirtualPath = virtualFilePath;
478
479 CFfpOpen ffp;
480 const HRESULT res = ffp.OpenFileFolderPlugin(inStream,
481 folderLink.FilePath.IsEmpty() ? us2fs(virtualFilePath) : folderLink.FilePath,
482 arcFormat, GetParent());
483
484 openRes.Encrypted = ffp.Encrypted;
485 openRes.ErrorMessage = ffp.ErrorMessage;
486
487 RINOK(res)
488
489 folderLink.Password = ffp.Password;
490 folderLink.UsePassword = ffp.Encrypted;
491
492 if (_folder)
493 folderLink.ParentFolderPath = GetFolderPath(_folder);
494 else
495 folderLink.ParentFolderPath = _currentFolderPrefix;
496
497 if (!_parentFolders.IsEmpty())
498 folderLink.ParentFolder = _folder;
499
500 _parentFolders.Add(folderLink);
501 _parentFolders.Back().Library.Attach(_library.Detach());
502
503 ReleaseFolder();
504 _library.Free();
505 SetNewFolder(ffp.Folder);
506 _library.Attach(ffp.Library.Detach());
507
508 _flatMode = _flatModeForArc;
509
510 _thereAreDeletedItems = false;
511
512 if (!openRes.ErrorMessage.IsEmpty())
513 MessageBox_Error(openRes.ErrorMessage);
514 /*
515 UString s;
516 GetFolderError(_folder, s);
517 if (!s.IsEmpty())
518 MessageBox_Error(s);
519 */
520 // we don't show error here by some reasons:
521 // after MessageBox_Warning it throws exception in nested archives in Debug Mode. why ?.
522 // MessageBox_Warning(L"test error");
523
524 return S_OK;
525 }
526
527
OpenAsArc_Msg(IInStream * inStream,const CTempFileInfo & tempFileInfo,const UString & virtualFilePath,const UString & arcFormat)528 HRESULT CPanel::OpenAsArc_Msg(IInStream *inStream,
529 const CTempFileInfo &tempFileInfo,
530 const UString &virtualFilePath,
531 const UString &arcFormat
532 // , bool &encrypted
533 // , bool showErrorMessage
534 )
535 {
536 COpenResult opRes;
537
538 HRESULT res = OpenAsArc(inStream, tempFileInfo, virtualFilePath, arcFormat, opRes);
539
540 if (res == S_OK)
541 return res;
542 if (res == E_ABORT)
543 return res;
544
545 // if (showErrorMessage)
546 if (opRes.Encrypted || res != S_FALSE) // 17.01 : we show message also for (res != S_FALSE)
547 {
548 UString message;
549 if (res == S_FALSE)
550 {
551 message = MyFormatNew(
552 opRes.Encrypted ?
553 IDS_CANT_OPEN_ENCRYPTED_ARCHIVE :
554 IDS_CANT_OPEN_ARCHIVE,
555 virtualFilePath);
556 }
557 else
558 message = HResultToMessage(res);
559 MessageBox_Error(message);
560 }
561
562 return res;
563 }
564
565
OpenAsArc_Name(const UString & relPath,const UString & arcFormat)566 HRESULT CPanel::OpenAsArc_Name(const UString &relPath, const UString &arcFormat
567 // , bool &encrypted,
568 // , bool showErrorMessage
569 )
570 {
571 CTempFileInfo tfi;
572 tfi.RelPath = relPath;
573 tfi.FolderPath = us2fs(GetFsPath());
574 const UString fullPath = GetFsPath() + relPath;
575 tfi.FilePath = us2fs(fullPath);
576 return OpenAsArc_Msg(NULL, tfi, fullPath, arcFormat /* , encrypted, showErrorMessage */);
577 }
578
579
OpenAsArc_Index(unsigned index,const wchar_t * type)580 HRESULT CPanel::OpenAsArc_Index(unsigned index, const wchar_t *type
581 // , bool showErrorMessage
582 )
583 {
584 CDisableTimerProcessing disableTimerProcessing1(*this);
585 CDisableNotify disableNotify(*this);
586
587 HRESULT res = OpenAsArc_Name(GetItemRelPath2(index), type ? type : L"" /* , encrypted, showErrorMessage */);
588 if (res != S_OK)
589 {
590 RefreshTitle(true); // in case of error we must refresh changed title of 7zFM
591 return res;
592 }
593 RefreshListCtrl();
594 return S_OK;
595 }
596
597
OpenParentArchiveFolder()598 HRESULT CPanel::OpenParentArchiveFolder()
599 {
600 CDisableTimerProcessing disableTimerProcessing(*this);
601 CDisableNotify disableNotify(*this);
602 if (_parentFolders.Size() < 2)
603 return S_OK;
604 const CFolderLink &folderLinkPrev = _parentFolders[_parentFolders.Size() - 2];
605 const CFolderLink &folderLink = _parentFolders.Back();
606 NFind::CFileInfo newFileInfo;
607 if (newFileInfo.Find(folderLink.FilePath))
608 {
609 if (folderLink.WasChanged_from_FolderLink(newFileInfo))
610 {
611 const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, folderLink.RelPath);
612 if (::MessageBoxW((HWND)*this, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES)
613 {
614 if (OnOpenItemChanged(folderLink.FileIndex, fs2us(folderLink.FilePath),
615 folderLinkPrev.UsePassword, folderLinkPrev.Password) != S_OK)
616 {
617 ::MessageBoxW((HWND)*this, MyFormatNew(IDS_CANNOT_UPDATE_FILE,
618 fs2us(folderLink.FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP);
619 return S_OK;
620 }
621 }
622 }
623 }
624 folderLink.DeleteDirAndFile();
625 return S_OK;
626 }
627
628
629 static const char * const kExeExtensions =
630 " exe bat ps1 com lnk"
631 " ";
632
633 static const char * const kStartExtensions =
634 #ifdef UNDER_CE
635 " cab"
636 #endif
637 " exe bat ps1 com lnk"
638 " chm"
639 " msi doc dot xls ppt pps wps wpt wks xlr wdb vsd pub"
640
641 " docx docm dotx dotm xlsx xlsm xltx xltm xlsb xps"
642 " xlam pptx pptm potx potm ppam ppsx ppsm vsdx xsn"
643 " mpp"
644 " msg"
645 " dwf"
646
647 " flv swf"
648
649 " epub"
650 " odt ods"
651 " wb3"
652 " pdf"
653 " ps"
654 " txt"
655 " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
656 " h hpp hxx c cpp cxx m mm go swift"
657 " awk sed hta js json php php3 php4 php5 phptml pl pm py pyo rb tcl ts vbs"
658 " asm"
659 " mak clw csproj vcproj sln dsp dsw"
660 " ";
661
662 // bool FindExt(const char *p, const UString &name, AString &s);
663 bool FindExt(const char *p, const UString &name, CStringFinder &finder);
664
DoItemAlwaysStart(const UString & name)665 static bool DoItemAlwaysStart(const UString &name)
666 {
667 CStringFinder finder;
668 return FindExt(kStartExtensions, name, finder);
669 }
670
671 UString GetQuotedString(const UString &s);
672
673
674 void SplitCmdLineSmart(const UString &cmd, UString &prg, UString ¶ms);
SplitCmdLineSmart(const UString & cmd,UString & prg,UString & params)675 void SplitCmdLineSmart(const UString &cmd, UString &prg, UString ¶ms)
676 {
677 params.Empty();
678 prg = cmd;
679 prg.Trim();
680 if (prg.Len() >= 2 && prg[0] == L'"')
681 {
682 int pos = prg.Find(L'"', 1);
683 if (pos >= 0)
684 {
685 if ((unsigned)(pos + 1) == prg.Len() || prg[pos + 1] == ' ')
686 {
687 params = prg.Ptr((unsigned)(pos + 1));
688 params.Trim();
689 prg.DeleteFrom((unsigned)pos);
690 prg.DeleteFrontal(1);
691 }
692 }
693 }
694 }
695
696
StartAppWithParams(const UString & cmd,const UStringVector & paramVector,CProcess & process)697 static WRes StartAppWithParams(const UString &cmd, const UStringVector ¶mVector, CProcess &process)
698 {
699 UString param;
700 UString prg;
701
702 SplitCmdLineSmart(cmd, prg, param);
703
704 param.Trim();
705
706 // int pos = params.Find(L"%1");
707
708 FOR_VECTOR (i, paramVector)
709 {
710 if (!param.IsEmpty() && param.Back() != ' ')
711 param.Add_Space();
712 param += GetQuotedString(paramVector[i]);
713 }
714
715 return process.Create(prg, param, NULL);
716 }
717
718
StartEditApplication(const UString & path,bool useEditor,HWND window,CProcess & process)719 static HRESULT StartEditApplication(const UString &path, bool useEditor, HWND window, CProcess &process)
720 {
721 UString command;
722 ReadRegEditor(useEditor, command);
723 if (command.IsEmpty())
724 {
725 #ifdef UNDER_CE
726 command = "\\Windows\\";
727 #else
728 FString winDir;
729 if (!GetWindowsDir(winDir))
730 return 0;
731 NName::NormalizeDirPathPrefix(winDir);
732 command = fs2us(winDir);
733 #endif
734 command += "notepad.exe";
735 }
736
737 UStringVector params;
738 params.Add(path);
739
740 const WRes res = StartAppWithParams(command, params, process);
741 if (res != 0)
742 ::MessageBoxW(window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP);
743 return HRESULT_FROM_WIN32(res);
744 }
745
746
DiffFiles()747 void CApp::DiffFiles()
748 {
749 const CPanel &panel = GetFocusedPanel();
750
751 if (!panel.Is_IO_FS_Folder())
752 {
753 panel.MessageBox_Error_UnsupportOperation();
754 return;
755 }
756
757 CRecordVector<UInt32> indices;
758 panel.Get_ItemIndices_Selected(indices);
759
760 UString path1, path2;
761 if (indices.Size() == 2)
762 {
763 path1 = panel.GetItemFullPath(indices[0]);
764 path2 = panel.GetItemFullPath(indices[1]);
765 }
766 else if (indices.Size() == 1 && NumPanels >= 2)
767 {
768 const CPanel &destPanel = Panels[1 - LastFocusedPanel];
769
770 if (!destPanel.Is_IO_FS_Folder())
771 {
772 panel.MessageBox_Error_UnsupportOperation();
773 return;
774 }
775
776 path1 = panel.GetItemFullPath(indices[0]);
777 CRecordVector<UInt32> indices2;
778 destPanel.Get_ItemIndices_Selected(indices2);
779 if (indices2.Size() == 1)
780 path2 = destPanel.GetItemFullPath(indices2[0]);
781 else
782 {
783 UString relPath = panel.GetItemRelPath2(indices[0]);
784 if (panel._flatMode && !destPanel._flatMode)
785 relPath = panel.GetItemName(indices[0]);
786 path2 = destPanel._currentFolderPrefix + relPath;
787 }
788 }
789 else
790 return;
791
792 DiffFiles(path1, path2);
793 }
794
DiffFiles(const UString & path1,const UString & path2)795 void CApp::DiffFiles(const UString &path1, const UString &path2)
796 {
797 UString command;
798 ReadRegDiff(command);
799 if (command.IsEmpty())
800 return;
801
802 UStringVector params;
803 params.Add(path1);
804 params.Add(path2);
805
806 WRes res;
807 {
808 CProcess process;
809 res = StartAppWithParams(command, params, process);
810 }
811 if (res == 0)
812 return;
813 ::MessageBoxW(_window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP);
814 }
815
816
817 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process);
818 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window);
819
EditItem(unsigned index,bool useEditor)820 void CPanel::EditItem(unsigned index, bool useEditor)
821 {
822 if (!_parentFolders.IsEmpty())
823 {
824 OpenItemInArchive(index, false, true, true, useEditor);
825 return;
826 }
827 CProcess process;
828 StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this, process);
829 }
830
831
OpenFolderExternal(unsigned index)832 void CPanel::OpenFolderExternal(unsigned index)
833 {
834 UString prefix = GetFsPath();
835 UString path = prefix;
836
837 if (index == kParentIndex)
838 {
839 if (prefix.IsEmpty())
840 return;
841 const wchar_t c = prefix.Back();
842 if (!IS_PATH_SEPAR(c) && c != ':')
843 return;
844 prefix.DeleteBack();
845 int pos = prefix.ReverseFind_PathSepar();
846 if (pos < 0)
847 return;
848 prefix.DeleteFrom((unsigned)(pos + 1));
849 path = prefix;
850 }
851 else
852 {
853 path += GetItemRelPath(index);
854 path.Add_PathSepar();
855 }
856
857 StartApplicationDontWait(prefix, path, (HWND)*this);
858 }
859
860
IsVirus_Message(const UString & name)861 bool CPanel::IsVirus_Message(const UString &name)
862 {
863 UString name2;
864
865 const wchar_t cRLO = (wchar_t)0x202E;
866 bool isVirus = false;
867 bool isSpaceError = false;
868 name2 = name;
869
870 if (name2.Find(cRLO) >= 0)
871 {
872 const UString badString(cRLO);
873 name2.Replace(badString, L"[RLO]");
874 isVirus = true;
875 }
876 {
877 const wchar_t * const kVirusSpaces = L" ";
878 // const unsigned kNumSpaces = strlen(kVirusSpaces);
879 for (;;)
880 {
881 int pos = name2.Find(kVirusSpaces);
882 if (pos < 0)
883 break;
884 isVirus = true;
885 isSpaceError = true;
886 name2.Replace(kVirusSpaces, L" ");
887 }
888 }
889
890 #ifdef _WIN32
891 {
892 unsigned i;
893 for (i = name2.Len(); i != 0;)
894 {
895 wchar_t c = name2[i - 1];
896 if (c != '.' && c != ' ')
897 break;
898 i--;
899 name2.ReplaceOneCharAtPos(i, '_');
900 }
901 if (i != name2.Len())
902 {
903 CStringFinder finder;
904 UString name3 = name2;
905 name3.DeleteFrom(i);
906 if (FindExt(kExeExtensions, name3, finder))
907 isVirus = true;
908 }
909 }
910 #endif
911
912 if (!isVirus)
913 return false;
914
915 UString s = LangString(IDS_VIRUS);
916
917 if (!isSpaceError)
918 {
919 const int pos1 = s.Find(L'(');
920 if (pos1 >= 0)
921 {
922 const int pos2 = s.Find(L')', (unsigned)pos1 + 1);
923 if (pos2 >= 0)
924 {
925 s.Delete((unsigned)pos1, (unsigned)pos2 + 1 - (unsigned)pos1);
926 if (pos1 > 0 && s[pos1 - 1] == ' ' && s[pos1] == '.')
927 s.Delete(pos1 - 1);
928 }
929 }
930 }
931
932 UString name3 = name;
933 name3.Replace(L'\n', L'_');
934 name2.Replace(L'\n', L'_');
935
936 s.Add_LF(); s += name2;
937 s.Add_LF(); s += name3;
938
939 MessageBox_Error(s);
940 return true;
941 }
942
943
OpenItem(unsigned index,bool tryInternal,bool tryExternal,const wchar_t * type)944 void CPanel::OpenItem(unsigned index, bool tryInternal, bool tryExternal, const wchar_t *type)
945 {
946 CDisableTimerProcessing disableTimerProcessing(*this);
947 const UString name = GetItemRelPath2(index);
948
949 if (tryExternal)
950 if (IsVirus_Message(name))
951 return;
952
953 if (!_parentFolders.IsEmpty())
954 {
955 OpenItemInArchive(index, tryInternal, tryExternal, false, false, type);
956 return;
957 }
958
959 CDisableNotify disableNotify(*this);
960 UString prefix = GetFsPath();
961 UString fullPath = prefix + name;
962
963 if (tryInternal)
964 if (!tryExternal || !DoItemAlwaysStart(name))
965 {
966 HRESULT res = OpenAsArc_Index(index, type
967 // , true
968 );
969 disableNotify.Restore(); // we must restore to allow text notification update
970 InvalidateList();
971 if (res == S_OK || res == E_ABORT)
972 return;
973 if (res != S_FALSE)
974 {
975 MessageBox_Error_HRESULT(res);
976 return;
977 }
978 }
979
980 if (tryExternal)
981 {
982 // SetCurrentDirectory opens HANDLE to folder!!!
983 // NDirectory::MySetCurrentDirectory(prefix);
984 StartApplicationDontWait(prefix, fullPath, (HWND)*this);
985 }
986 }
987
988 class CThreadCopyFrom: public CProgressThreadVirt
989 {
990 HRESULT ProcessVirt() Z7_override;
991 public:
992 UString FullPath;
993 UInt32 ItemIndex;
994
995 CMyComPtr<IFolderOperations> FolderOperations;
996 CMyComPtr<IProgress> UpdateCallback;
997 CUpdateCallback100Imp *UpdateCallbackSpec;
998 };
999
ProcessVirt()1000 HRESULT CThreadCopyFrom::ProcessVirt()
1001 {
1002 return FolderOperations->CopyFromFile(ItemIndex, FullPath, UpdateCallback);
1003 }
1004
OnOpenItemChanged(UInt32 index,const wchar_t * fullFilePath,bool usePassword,const UString & password)1005 HRESULT CPanel::OnOpenItemChanged(UInt32 index, const wchar_t *fullFilePath,
1006 bool usePassword, const UString &password)
1007 {
1008 if (!_folderOperations)
1009 {
1010 MessageBox_Error_UnsupportOperation();
1011 return E_FAIL;
1012 }
1013
1014 CThreadCopyFrom t;
1015 t.UpdateCallbackSpec = new CUpdateCallback100Imp;
1016 t.UpdateCallback = t.UpdateCallbackSpec;
1017 t.UpdateCallbackSpec->ProgressDialog = &t;
1018 t.ItemIndex = index;
1019 t.FullPath = fullFilePath;
1020 t.FolderOperations = _folderOperations;
1021
1022 t.UpdateCallbackSpec->Init();
1023 t.UpdateCallbackSpec->PasswordIsDefined = usePassword;
1024 t.UpdateCallbackSpec->Password = password;
1025
1026
1027 RINOK(t.Create(GetItemName(index), (HWND)*this))
1028 return t.Result;
1029 }
1030
OnOpenItemChanged(LPARAM lParam)1031 LRESULT CPanel::OnOpenItemChanged(LPARAM lParam)
1032 {
1033 // DEBUG_PRINT_NUM("OnOpenItemChanged", GetCurrentThreadId());
1034
1035 CTmpProcessInfo &tpi = *(CTmpProcessInfo *)lParam;
1036 if (tpi.FullPathFolderPrefix != _currentFolderPrefix)
1037 return 0;
1038 UInt32 fileIndex = tpi.FileIndex;
1039 UInt32 numItems;
1040 _folder->GetNumberOfItems(&numItems);
1041
1042 // This code is not 100% OK for cases when there are several files with
1043 // tpi.RelPath name and there are changes in archive before update.
1044 // So tpi.FileIndex can point to another file.
1045
1046 if (fileIndex >= numItems || GetItemRelPath(fileIndex) != tpi.RelPath)
1047 {
1048 UInt32 i;
1049 for (i = 0; i < numItems; i++)
1050 if (GetItemRelPath(i) == tpi.RelPath)
1051 break;
1052 if (i == numItems)
1053 return 0;
1054 fileIndex = i;
1055 }
1056
1057 CSelectedState state;
1058 SaveSelectedState(state);
1059
1060 CDisableNotify disableNotify(*this); // do we need it??
1061
1062 HRESULT result = OnOpenItemChanged(fileIndex, fs2us(tpi.FilePath), tpi.UsePassword, tpi.Password);
1063 RefreshListCtrl(state);
1064 if (result != S_OK)
1065 return 0;
1066 return 1;
1067 }
1068
1069
1070 CExitEventLauncher g_ExitEventLauncher;
1071
Exit(bool hardExit)1072 void CExitEventLauncher::Exit(bool hardExit)
1073 {
1074 if (_needExit)
1075 {
1076 _exitEvent.Set();
1077 _needExit = false;
1078 }
1079
1080 if (_numActiveThreads == 0)
1081 return;
1082
1083 FOR_VECTOR (i, _threads)
1084 {
1085 ::CThread &th = _threads[i];
1086 if (Thread_WasCreated(&th))
1087 {
1088 const DWORD waitResult = WaitForSingleObject(th, hardExit ? 100 : INFINITE);
1089 // Thread_Wait(&th);
1090 // if (waitResult == WAIT_TIMEOUT) wait = 1;
1091 if (!hardExit && waitResult != WAIT_OBJECT_0)
1092 continue;
1093 Thread_Close(&th);
1094 _numActiveThreads--;
1095 }
1096 }
1097 }
1098
1099
1100
MyThreadFunction(void * param)1101 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1102 {
1103 DEBUG_PRINT("==== MyThreadFunction ====");
1104
1105 CMyUniquePtr<CTmpProcessInfo> tpi((CTmpProcessInfo *)param);
1106 CChildProcesses &processes = tpi->Processes;
1107
1108 const bool mainProcessWasSet = !processes.Handles.IsEmpty();
1109
1110 bool isComplexMode = true;
1111
1112 if (!processes.Handles.IsEmpty())
1113 {
1114
1115 const DWORD startTime = GetTickCount();
1116
1117 /*
1118 CPossibleProgs progs;
1119 {
1120 const UString &name = tpi->RelPath;
1121 int slashPos = name.ReverseFind_PathSepar();
1122 int dotPos = name.ReverseFind_Dot();
1123 if (dotPos > slashPos)
1124 {
1125 const UString ext = name.Ptr(dotPos + 1);
1126 AString extA = UnicodeStringToMultiByte(ext);
1127 extA.MakeLower_Ascii();
1128 progs.SetFromExtension(extA);
1129 }
1130 }
1131 */
1132
1133 bool firstPass = true;
1134
1135 for (;;)
1136 {
1137 CRecordVector<HANDLE> handles;
1138 CUIntVector indices;
1139
1140 FOR_VECTOR (i, processes.Handles)
1141 {
1142 if (handles.Size() > 60)
1143 break;
1144 if (processes.NeedWait[i])
1145 {
1146 handles.Add(processes.Handles[i]);
1147 indices.Add(i);
1148 }
1149 }
1150
1151 bool needFindProcessByPath = false;
1152
1153 if (handles.IsEmpty())
1154 {
1155 if (!firstPass)
1156 break;
1157 }
1158 else
1159 {
1160 handles.Add(g_ExitEventLauncher._exitEvent);
1161
1162 DWORD waitResult = WaitForMultiObj_Any_Infinite(handles.Size(), handles.ConstData());
1163
1164 waitResult -= WAIT_OBJECT_0;
1165
1166 if (waitResult >= handles.Size() - 1)
1167 {
1168 processes.CloseAll();
1169 /*
1170 if (waitResult == handles.Size() - 1)
1171 {
1172 // exit event
1173 // we want to delete temp files, if progs were used
1174 if (processes.ProgsWereUsed)
1175 break;
1176 }
1177 */
1178 return waitResult >= (DWORD)handles.Size() ? 1 : 0;
1179 }
1180
1181 if (firstPass && indices.Size() == 1)
1182 {
1183 const DWORD curTime = GetTickCount() - startTime;
1184
1185 /*
1186 if (curTime > 5 * 1000)
1187 progs.ProgNames.Clear();
1188 */
1189
1190 needFindProcessByPath = (curTime < 2 * 1000);
1191
1192 if (needFindProcessByPath)
1193 {
1194 NFind::CFileInfo newFileInfo;
1195 if (newFileInfo.Find(tpi->FilePath))
1196 if (tpi->WasChanged_from_TempFileInfo(newFileInfo))
1197 needFindProcessByPath = false;
1198 }
1199
1200 DEBUG_PRINT_NUM(" -- firstPass -- time = ", curTime)
1201 }
1202
1203 processes.DisableWait(indices[(unsigned)waitResult]);
1204 }
1205
1206 firstPass = false;
1207
1208 // Sleep(300);
1209 #ifndef UNDER_CE
1210 processes.Update(needFindProcessByPath /* , progs */);
1211 #endif
1212 }
1213
1214
1215 const DWORD curTime = GetTickCount() - startTime;
1216
1217 DEBUG_PRINT_NUM("after time = ", curTime)
1218
1219 processes.CloseAll();
1220
1221 isComplexMode = (curTime < 2 * 1000);
1222
1223 }
1224
1225 bool needCheckTimestamp = true;
1226
1227 for (;;)
1228 {
1229 NFind::CFileInfo newFileInfo;
1230
1231 if (!newFileInfo.Find(tpi->FilePath))
1232 break;
1233
1234 if (mainProcessWasSet)
1235 {
1236 if (tpi->WasChanged_from_TempFileInfo(newFileInfo))
1237 {
1238 UString m = MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(tpi->FilePath));
1239 if (tpi->ReadOnly)
1240 {
1241 m.Add_LF();
1242 AddLangString(m, IDS_PROP_READ_ONLY);
1243 m.Add_LF();
1244 m += tpi->FullPathFolderPrefix;
1245 ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1246 return 0;
1247 }
1248 {
1249 const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath);
1250 if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES)
1251 {
1252 // DEBUG_PRINT_NUM("SendMessage", GetCurrentThreadId());
1253 if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi.get()) != 1)
1254 {
1255 ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1256 return 0;
1257 }
1258 }
1259 needCheckTimestamp = false;
1260 break;
1261 }
1262 }
1263
1264 if (!isComplexMode)
1265 break;
1266 }
1267
1268 // DEBUG_PRINT("WaitForSingleObject");
1269 DWORD waitResult = ::WaitForSingleObject(g_ExitEventLauncher._exitEvent, INFINITE);
1270 // DEBUG_PRINT("---");
1271
1272 if (waitResult == WAIT_OBJECT_0)
1273 break;
1274
1275 return 1;
1276 }
1277
1278 {
1279 NFind::CFileInfo newFileInfo;
1280 const bool finded = newFileInfo.Find(tpi->FilePath);
1281 if (!needCheckTimestamp
1282 || !finded
1283 || !tpi->WasChanged_from_TempFileInfo(newFileInfo))
1284 {
1285 DEBUG_PRINT("Delete Temp file");
1286 tpi->DeleteDirAndFile();
1287 }
1288 }
1289
1290 return 0;
1291 }
1292
1293
1294 /*
1295 #if defined(_WIN32) && !defined(UNDER_CE)
1296 static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
1297 #endif
1298
1299
1300 #ifndef UNDER_CE
1301
1302 static void ReadZoneFile(CFSTR fileName, CByteBuffer &buf)
1303 {
1304 buf.Free();
1305 NIO::CInFile file;
1306 if (!file.Open(fileName))
1307 return;
1308 UInt64 fileSize;
1309 if (!file.GetLength(fileSize))
1310 return;
1311 if (fileSize == 0 || fileSize >= ((UInt32)1 << 20))
1312 return;
1313 buf.Alloc((size_t)fileSize);
1314 size_t processed;
1315 if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize)
1316 return;
1317 buf.Free();
1318 }
1319
1320 static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
1321 {
1322 NIO::COutFile file;
1323 if (!file.Create(fileName, true))
1324 return false;
1325 UInt32 processed;
1326 if (!file.Write(buf, (UInt32)buf.Size(), processed))
1327 return false;
1328 return processed == buf.Size();
1329 }
1330
1331 #endif
1332 */
1333
1334 /*
1335 Z7_CLASS_IMP_COM_1(
1336 CBufSeqOutStream_WithFile
1337 , ISequentialOutStream
1338 )
1339 Byte *_buffer;
1340 size_t _size;
1341 size_t _pos;
1342
1343 size_t _fileWritePos;
1344 bool fileMode;
1345 public:
1346
1347 bool IsStreamInMem() const { return !fileMode; }
1348 size_t GetMemStreamWrittenSize() const { return _pos; }
1349
1350 // ISequentialOutStream *FileStream;
1351 FString FilePath;
1352 COutFileStream *outFileStreamSpec;
1353 CMyComPtr<ISequentialOutStream> outFileStream;
1354
1355 CBufSeqOutStream_WithFile(): outFileStreamSpec(NULL) {}
1356
1357 void Init(Byte *buffer, size_t size)
1358 {
1359 fileMode = false;
1360 _buffer = buffer;
1361 _pos = 0;
1362 _size = size;
1363 _fileWritePos = 0;
1364 }
1365
1366 HRESULT FlushToFile();
1367 size_t GetPos() const { return _pos; }
1368 };
1369
1370 static const UInt32 kBlockSize = ((UInt32)1 << 31);
1371
1372 STDMETHODIMP CBufSeqOutStream_WithFile::Write(const void *data, UInt32 size, UInt32 *processedSize)
1373 {
1374 if (processedSize)
1375 *processedSize = 0;
1376 if (!fileMode)
1377 {
1378 if (_size - _pos >= size)
1379 {
1380 if (size != 0)
1381 {
1382 memcpy(_buffer + _pos, data, size);
1383 _pos += size;
1384 }
1385 if (processedSize)
1386 *processedSize = (UInt32)size;
1387 return S_OK;
1388 }
1389
1390 fileMode = true;
1391 }
1392 RINOK(FlushToFile());
1393 return outFileStream->Write(data, size, processedSize);
1394 }
1395
1396 HRESULT CBufSeqOutStream_WithFile::FlushToFile()
1397 {
1398 if (!outFileStream)
1399 {
1400 outFileStreamSpec = new COutFileStream;
1401 outFileStream = outFileStreamSpec;
1402 if (!outFileStreamSpec->Create(FilePath, false))
1403 {
1404 outFileStream.Release();
1405 return E_FAIL;
1406 // MessageBoxMyError(UString("Can't create file ") + fs2us(tempFilePath));
1407 }
1408 }
1409 while (_fileWritePos != _pos)
1410 {
1411 size_t cur = _pos - _fileWritePos;
1412 UInt32 curSize = (cur < kBlockSize) ? (UInt32)cur : kBlockSize;
1413 UInt32 processedSizeLoc = 0;
1414 HRESULT res = outFileStream->Write(_buffer + _fileWritePos, curSize, &processedSizeLoc);
1415 _fileWritePos += processedSizeLoc;
1416 RINOK(res);
1417 if (processedSizeLoc == 0)
1418 return E_FAIL;
1419 }
1420 return S_OK;
1421 }
1422 */
1423
1424 /*
1425 static HRESULT GetTime(IFolderFolder *folder, UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
1426 {
1427 filetimeIsDefined = false;
1428 NCOM::CPropVariant prop;
1429 RINOK(folder->GetProperty(index, propID, &prop));
1430 if (prop.vt == VT_FILETIME)
1431 {
1432 filetime = prop.filetime;
1433 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
1434 }
1435 else if (prop.vt != VT_EMPTY)
1436 return E_FAIL;
1437 return S_OK;
1438 }
1439 */
1440
1441
1442 /*
1443 tryInternal tryExternal
1444 false false : unused
1445 false true : external
1446 true false : internal
1447 true true : smart based on file extension:
1448 !alwaysStart(name) : both
1449 alwaysStart(name) : external
1450 */
1451
OpenItemInArchive(unsigned index,bool tryInternal,bool tryExternal,bool editMode,bool useEditor,const wchar_t * type)1452 void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExternal, bool editMode, bool useEditor, const wchar_t *type)
1453 {
1454 // we don't want to change hash data here
1455 if (IsHashFolder())
1456 return;
1457
1458 const UString name = GetItemName(index);
1459 const UString relPath = GetItemRelPath(index);
1460
1461 if (tryExternal)
1462 if (IsVirus_Message(name))
1463 return;
1464
1465 if (!_folderOperations)
1466 {
1467 MessageBox_Error_UnsupportOperation();
1468 return;
1469 }
1470
1471 bool tryAsArchive = tryInternal && (!tryExternal || !DoItemAlwaysStart(name));
1472
1473 const UString fullVirtPath = _currentFolderPrefix + relPath;
1474
1475 CTempDir tempDirectory;
1476 if (!tempDirectory.Create(kTempDirPrefix))
1477 {
1478 MessageBox_LastError();
1479 return;
1480 }
1481
1482 FString tempDir = tempDirectory.GetPath();
1483 FString tempDirNorm = tempDir;
1484 NName::NormalizeDirPathPrefix(tempDirNorm);
1485 const FString tempFilePath = tempDirNorm + us2fs(Get_Correct_FsFile_Name(name));
1486
1487 CTempFileInfo tempFileInfo;
1488 tempFileInfo.FileIndex = index;
1489 tempFileInfo.RelPath = relPath;
1490 tempFileInfo.FolderPath = tempDir;
1491 tempFileInfo.FilePath = tempFilePath;
1492 tempFileInfo.NeedDelete = true;
1493
1494 if (tryAsArchive)
1495 {
1496 CMyComPtr<IInArchiveGetStream> getStream;
1497 _folder.QueryInterface(IID_IInArchiveGetStream, &getStream);
1498 if (getStream)
1499 {
1500 CMyComPtr<ISequentialInStream> subSeqStream;
1501 getStream->GetStream(index, &subSeqStream);
1502 if (subSeqStream)
1503 {
1504 CMyComPtr<IInStream> subStream;
1505 subSeqStream.QueryInterface(IID_IInStream, &subStream);
1506 if (subStream)
1507 {
1508 HRESULT res = OpenAsArc_Msg(subStream, tempFileInfo, fullVirtPath, type ? type : L""
1509 // , true // showErrorMessage
1510 );
1511 if (res == S_OK)
1512 {
1513 tempDirectory.DisableDeleting();
1514 RefreshListCtrl();
1515 return;
1516 }
1517 if (res == E_ABORT || res != S_FALSE)
1518 return;
1519 if (!tryExternal)
1520 return;
1521 tryAsArchive = false;
1522 }
1523 }
1524 }
1525 }
1526
1527
1528 CRecordVector<UInt32> indices;
1529 indices.Add(index);
1530
1531 UStringVector messages;
1532
1533 bool usePassword = false;
1534 UString password;
1535 if (!_parentFolders.IsEmpty())
1536 {
1537 const CFolderLink &fl = _parentFolders.Back();
1538 usePassword = fl.UsePassword;
1539 password = fl.Password;
1540 }
1541
1542 /*
1543 #if defined(_WIN32) && !defined(UNDER_CE)
1544 CByteBuffer zoneBuf;
1545 #ifndef _UNICODE
1546 if (g_IsNT)
1547 #endif
1548 if (!_parentFolders.IsEmpty())
1549 {
1550 const CFolderLink &fl = _parentFolders.Front();
1551 if (!fl.IsVirtual && !fl.FilePath.IsEmpty())
1552 ReadZoneFile(fl.FilePath + k_ZoneId_StreamName, zoneBuf);
1553 }
1554 #endif
1555 */
1556
1557
1558 CVirtFileSystem *virtFileSystemSpec = NULL;
1559 CMyComPtr<ISequentialOutStream> virtFileSystem;
1560
1561 const bool isAltStream = IsItem_AltStream(index);
1562
1563 CCopyToOptions options;
1564 options.includeAltStreams = true;
1565 options.replaceAltStreamChars = isAltStream;
1566 {
1567 // CContextMenuInfo ci;
1568 // ci.Load();
1569 // if (ci.WriteZone != (UInt32)(Int32)-1)
1570 // we use kAll when we unpack just one file.
1571 options.ZoneIdMode = NExtract::NZoneIdMode::kAll;
1572 options.NeedRegistryZone = false;
1573 }
1574
1575 if (tryAsArchive)
1576 {
1577 // actually we want to get sum: size of main file plus sizes of altStreams.
1578 // but now there is no interface to get altStreams sizes.
1579 NCOM::CPropVariant prop;
1580 _folder->GetProperty(index, kpidSize, &prop);
1581 const size_t fileLimit = g_RAM_Size_Defined ?
1582 g_RAM_Size >> MyMax(_parentFolders.Size() + 1, 8u):
1583 1u << 22;
1584 UInt64 fileSize = 0;
1585 if (!ConvertPropVariantToUInt64(prop, fileSize))
1586 fileSize = fileLimit;
1587 #if 0 // 1 : for debug
1588 fileLimit = 1;
1589 #endif
1590
1591 if (fileSize <= fileLimit)
1592 {
1593 options.streamMode = true;
1594 virtFileSystemSpec = new CVirtFileSystem;
1595 virtFileSystem = virtFileSystemSpec;
1596 virtFileSystemSpec->FileName = name;
1597 virtFileSystemSpec->IsAltStreamFile = isAltStream;
1598
1599 #if defined(_WIN32) && !defined(UNDER_CE)
1600 #ifndef _UNICODE
1601 if (g_IsNT)
1602 #endif
1603 {
1604 Get_ZoneId_Stream_from_ParentFolders(virtFileSystemSpec->ZoneBuf);
1605 options.ZoneBuf = virtFileSystemSpec->ZoneBuf;
1606 }
1607 #endif
1608
1609 virtFileSystemSpec->MaxTotalAllocSize = (size_t)fileSize
1610 + (1 << 16); // we allow additional total size for small alt streams.
1611 virtFileSystemSpec->DirPrefix = tempDirNorm;
1612 // options.VirtFileSystem = virtFileSystem;
1613 options.VirtFileSystemSpec = virtFileSystemSpec;
1614 }
1615 }
1616
1617 options.folder = fs2us(tempDirNorm);
1618 options.showErrorMessages = true;
1619
1620 const HRESULT result = CopyTo(options, indices, &messages, usePassword, password);
1621
1622 if (!_parentFolders.IsEmpty())
1623 {
1624 CFolderLink &fl = _parentFolders.Back();
1625 fl.UsePassword = usePassword;
1626 fl.Password = password;
1627 }
1628
1629 if (!messages.IsEmpty())
1630 return;
1631 if (result != S_OK)
1632 {
1633 if (result != E_ABORT)
1634 MessageBox_Error_HRESULT(result);
1635 return;
1636 }
1637
1638 if (virtFileSystemSpec && !virtFileSystemSpec->WasStreamFlushedToFS())
1639 {
1640 int index_in_Files = virtFileSystemSpec->Index_of_MainExtractedFile_in_Files;
1641 if (index_in_Files < 0)
1642 {
1643 if (virtFileSystemSpec->Files.Size() != 1)
1644 {
1645 MessageBox_Error_HRESULT(E_FAIL);
1646 return;
1647 }
1648 // it's not expected case that index was not set, but we support that case
1649 index_in_Files = 0;
1650 }
1651 {
1652 const CVirtFile &file = virtFileSystemSpec->Files[index_in_Files];
1653 CMyComPtr2_Create<IInStream, CBufInStream> bufInStream;
1654 bufInStream->Init(file.Data, file.WrittenSize, virtFileSystem);
1655 const HRESULT res = OpenAsArc_Msg(bufInStream, tempFileInfo,
1656 fullVirtPath, type ? type : L""
1657 // , encrypted
1658 // , true // showErrorMessage
1659 );
1660 if (res == S_OK)
1661 {
1662 if (virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files >= 0
1663 && !_parentFolders.IsEmpty())
1664 {
1665 const CVirtFile &fileZone = virtFileSystemSpec->Files[
1666 virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files];
1667 _parentFolders.Back().ZoneBuf.CopyFrom(fileZone.Data, fileZone.WrittenSize);
1668 }
1669
1670 tempDirectory.DisableDeleting();
1671 RefreshListCtrl();
1672 return;
1673 }
1674 if (res == E_ABORT || res != S_FALSE)
1675 return;
1676 if (!tryExternal)
1677 return;
1678 tryAsArchive = false;
1679 if (virtFileSystemSpec->FlushToDisk(true) != S_OK)
1680 return;
1681 }
1682 }
1683
1684
1685 /*
1686 #if defined(_WIN32) && !defined(UNDER_CE)
1687 if (zoneBuf.Size() != 0)
1688 {
1689 if (NFind::DoesFileExist_Raw(tempFilePath))
1690 {
1691 WriteZoneFile(tempFilePath + k_ZoneId_StreamName, zoneBuf);
1692 }
1693 }
1694 #endif
1695 */
1696
1697
1698 if (tryAsArchive)
1699 {
1700 const HRESULT res = OpenAsArc_Msg(NULL, tempFileInfo, fullVirtPath, type ? type : L""
1701 // , encrypted
1702 // , true // showErrorMessage
1703 );
1704 if (res == S_OK)
1705 {
1706 tempDirectory.DisableDeleting();
1707 RefreshListCtrl();
1708 return;
1709 }
1710 if (res == E_ABORT || res != S_FALSE)
1711 return;
1712 }
1713
1714 if (!tryExternal)
1715 return;
1716
1717 CMyUniquePtr<CTmpProcessInfo> tpi(new CTmpProcessInfo());
1718 tpi->FolderPath = tempDir;
1719 tpi->FilePath = tempFilePath;
1720 tpi->NeedDelete = true;
1721 tpi->UsePassword = usePassword;
1722 tpi->Password = password;
1723 tpi->ReadOnly = IsThereReadOnlyFolder();
1724 if (IsHashFolder())
1725 tpi->ReadOnly = true;
1726
1727 if (!tpi->FileInfo.Find(tempFilePath))
1728 return;
1729
1730 CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi);
1731
1732 CProcess process;
1733 HRESULT res;
1734 if (editMode)
1735 res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process);
1736 else
1737 res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process);
1738
1739 if ((HANDLE)process == NULL)
1740 {
1741 // win7 / win10 work so for some extensions (pdf, html ..);
1742 DEBUG_PRINT("#### (HANDLE)process == 0");
1743 // return;
1744 if (res != S_OK)
1745 return;
1746 }
1747
1748 tpi->Window = (HWND)*this;
1749 tpi->FullPathFolderPrefix = _currentFolderPrefix;
1750 tpi->FileIndex = index;
1751 tpi->RelPath = relPath;
1752
1753 if ((HANDLE)process)
1754 tpi->Processes.SetMainProcess(process.Detach());
1755
1756 ::CThread th;
1757 if (Thread_Create(&th, MyThreadFunction, tpi.get()) != 0)
1758 throw 271824;
1759 g_ExitEventLauncher._threads.Add(th);
1760 g_ExitEventLauncher._numActiveThreads++;
1761
1762 tempDirectory.DisableDeleting();
1763 tpi.release();
1764 tmpProcessInfoRelease._needDelete = false;
1765 }
1766
1767
1768 /*
1769 static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24;
1770
1771 static bool CheckDeleteItem(UINT64 currentFileTime, UINT64 folderFileTime)
1772 {
1773 return (currentFileTime - folderFileTime > kTimeLimit &&
1774 folderFileTime - currentFileTime > kTimeLimit);
1775 }
1776
1777 void DeleteOldTempFiles()
1778 {
1779 UString tempPath;
1780 if (!MyGetTempPath(tempPath))
1781 throw 1;
1782
1783 UINT64 currentFileTime;
1784 NTime::GetCurUtcFileTime(currentFileTime);
1785 UString searchWildCard = tempPath + kTempDirPrefix + L"*.tmp";
1786 searchWildCard += WCHAR(NName::kAnyStringWildcard);
1787 NFind::CEnumeratorW enumerator(searchWildCard);
1788 NFind::CFileInfo fileInfo;
1789 while (enumerator.Next(fileInfo))
1790 {
1791 if (!fileInfo.IsDir())
1792 continue;
1793 const UINT64 &cTime = *(const UINT64 *)(&fileInfo.CTime);
1794 if (CheckDeleteItem(cTime, currentFileTime))
1795 RemoveDirectoryWithSubItems(tempPath + fileInfo.Name);
1796 }
1797 }
1798 */
1799