p7zip-rar/CPP/7zip/UI/FileManager/PanelItemOpen.cpp

1270 lines
31 KiB
C++
Raw Normal View History

2017-10-11 12:40:22 +02:00
// PanelItemOpen.cpp
#include "StdAfx.h"
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/mimetype.h"
#undef _WIN32
// #include <tlhelp32.h>
#include "../../../Common/AutoPtr.h"
#include "../../../Common/StringConvert.h"
// #include "../../../Windows/ProcessUtils.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/PropVariant.h"
#include "../../../Windows/PropVariantConv.h"
#include "../../Common/FileStreams.h"
#include "../../Common/StreamObjects.h"
#include "../Common/ExtractingFilePath.h"
#include "App.h"
#include "FileFolderPluginOpen.h"
#include "FormatUtils.h"
#include "LangUtils.h"
#include "RegistryUtils.h"
#include "UpdateCallback100.h"
#include "resource.h"
using namespace NWindows;
using namespace NSynchronization;
using namespace NFile;
using namespace NDir;
extern UInt64 g_RAM_Size;
#ifndef _UNICODE
extern bool g_IsNT;
#endif
static CFSTR kTempDirPrefix = FTEXT("7zO");
#if 0 // ifndef UNDER_CE
class CProcessSnapshot
{
HANDLE _handle;
public:
CProcessSnapshot(): _handle(INVALID_HANDLE_VALUE) {};
~CProcessSnapshot() { Close(); }
bool Close()
{
if (_handle == INVALID_HANDLE_VALUE)
return true;
if (!::CloseHandle(_handle))
return false;
_handle = INVALID_HANDLE_VALUE;
return true;
}
bool Create()
{
_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
return (_handle != INVALID_HANDLE_VALUE);
}
bool GetFirstProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32First(_handle, pe)); }
bool GetNextProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32Next(_handle, pe)); }
};
typedef DWORD (WINAPI *GetProcessIdFunc)(HANDLE process);
class CChildProcesses
{
#ifndef UNDER_CE
CRecordVector<DWORD> _ids;
#endif
public:
CRecordVector<HANDLE> Handles;
CRecordVector<bool> NeedWait;
~CChildProcesses() { CloseAll(); }
void DisableWait(int index) { NeedWait[index] = false; }
void CloseAll()
{
FOR_VECTOR (i, Handles)
{
HANDLE h = Handles[i];
if (h != NULL)
CloseHandle(h);
}
Handles.Clear();
NeedWait.Clear();
}
void AddProcess(HANDLE h)
{
#ifndef UNDER_CE
GetProcessIdFunc func = (GetProcessIdFunc)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "GetProcessId");
if (func)
_ids.AddToUniqueSorted(func(h));
#endif
Handles.Add(h);
NeedWait.Add(true);
}
void Update()
{
#ifndef UNDER_CE
CRecordVector<DWORD> ids, parents;
{
CProcessSnapshot snapshot;
if (snapshot.Create())
{
PROCESSENTRY32 pe;
memset(&pe, 0, sizeof(pe));
pe.dwSize = sizeof(pe);
BOOL res = snapshot.GetFirstProcess(&pe);
while (res)
{
ids.Add(pe.th32ProcessID);
parents.Add(pe.th32ParentProcessID);
res = snapshot.GetNextProcess(&pe);
}
}
}
for (;;)
{
unsigned i;
for (i = 0; i < ids.Size(); i++)
{
DWORD id = ids[i];
if (_ids.FindInSorted(parents[i]) >= 0 &&
_ids.FindInSorted(id) < 0)
{
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, id);
if (hProcess)
{
_ids.AddToUniqueSorted(id);
Handles.Add(hProcess);
NeedWait.Add(true);
break;
}
}
}
if (i == ids.Size())
break;
}
#endif
}
};
#endif
struct CTmpProcessInfo: public CTempFileInfo
{
HANDLE ProcessHandle; // CChildProcesses Processes;
HWND Window;
UString FullPathFolderPrefix;
bool UsePassword;
UString Password;
CTmpProcessInfo(): UsePassword(false) {}
};
class CTmpProcessInfoRelease
{
CTmpProcessInfo *_tmpProcessInfo;
public:
bool _needDelete;
CTmpProcessInfoRelease(CTmpProcessInfo &tpi):
_tmpProcessInfo(&tpi), _needDelete(true) {}
~CTmpProcessInfoRelease()
{
if (_needDelete)
_tmpProcessInfo->DeleteDirAndFile();
}
};
HRESULT CPanel::OpenItemAsArchive(IInStream *inStream,
const CTempFileInfo &tempFileInfo,
const UString &virtualFilePath,
const UString &arcFormat,
bool &encrypted)
{
encrypted = false;
CFolderLink folderLink;
(CTempFileInfo &)folderLink = tempFileInfo;
if (inStream)
folderLink.IsVirtual = true;
else
{
if (!folderLink.FileInfo.Find(folderLink.FilePath))
return ::GetLastError();
if (folderLink.FileInfo.IsDir())
return S_FALSE;
folderLink.IsVirtual = false;
}
folderLink.VirtualPath = virtualFilePath;
CMyComPtr<IFolderFolder> newFolder;
// _passwordIsDefined = false;
// _password.Empty();
NDLL::CLibrary library;
UString password;
RINOK(OpenFileFolderPlugin(inStream,
folderLink.FilePath.IsEmpty() ? us2fs(virtualFilePath) : folderLink.FilePath,
arcFormat,
&library, &newFolder, GetParent(), encrypted, password));
folderLink.Password = password;
folderLink.UsePassword = encrypted;
if (_folder)
folderLink.ParentFolderPath = GetFolderPath(_folder);
else
folderLink.ParentFolderPath = _currentFolderPrefix;
if (!_parentFolders.IsEmpty())
folderLink.ParentFolder = _folder;
_parentFolders.Add(folderLink);
_parentFolders.Back().Library.Attach(_library.Detach());
ReleaseFolder();
_library.Free();
SetNewFolder(newFolder);
_library.Attach(library.Detach());
_flatMode = _flatModeForArc;
CMyComPtr<IGetFolderArcProps> getFolderArcProps;
_folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
_thereAreDeletedItems = false;
if (getFolderArcProps)
{
CMyComPtr<IFolderArcProps> arcProps;
getFolderArcProps->GetFolderArcProps(&arcProps);
if (arcProps)
{
/*
UString s;
UInt32 numLevels;
if (arcProps->GetArcNumLevels(&numLevels) != S_OK)
numLevels = 0;
for (UInt32 level2 = 0; level2 <= numLevels; level2++)
{
UInt32 level = numLevels - level2;
PROPID propIDs[] = { kpidError, kpidPath, kpidType, kpidErrorType } ;
UString values[4];
for (Int32 i = 0; i < 4; i++)
{
CMyComBSTR name;
NCOM::CPropVariant prop;
if (arcProps->GetArcProp(level, propIDs[i], &prop) != S_OK)
continue;
if (prop.vt != VT_EMPTY)
values[i] = (prop.vt == VT_BSTR) ? prop.bstrVal : L"?";
}
UString s2;
if (!values[3].IsEmpty())
{
s2 = L"Can not open the file as [" + values[3] + L"] archive";
if (level2 != 0)
s2 += L"\nThe file is open as [" + values[2] + L"] archive";
}
if (!values[0].IsEmpty())
{
if (!s2.IsEmpty())
s2.Add_LF();
s2 += L"[";
s2 += values[2];
s2 += L"] Error: ";
s2 += values[0];
}
if (!s2.IsEmpty())
{
if (!s.IsEmpty())
s += L"--------------------\n";
s += values[1];
s.Add_LF();
s += s2;
}
}
*/
/*
if (!s.IsEmpty())
MessageBoxWarning(s);
else
*/
// after MessageBoxWarning it throws exception in nested archives in Debug Mode. why ?.
// MessageBoxWarning(L"test error");
}
}
return S_OK;
}
HRESULT CPanel::OpenItemAsArchive(const UString &relPath, const UString &arcFormat, bool &encrypted)
{
CTempFileInfo tfi;
tfi.RelPath = relPath;
tfi.FolderPath = us2fs(GetFsPath());
const UString fullPath = GetFsPath() + relPath;
tfi.FilePath = us2fs(fullPath);
return OpenItemAsArchive(NULL, tfi, fullPath, arcFormat, encrypted);
}
HRESULT CPanel::OpenItemAsArchive(int index, const wchar_t *type)
{
CDisableTimerProcessing disableTimerProcessing1(*this);
CDisableNotify disableNotify(*this);
bool encrypted;
HRESULT res = OpenItemAsArchive(GetItemRelPath2(index), type ? type : L"", encrypted);
if (res != S_OK)
{
RefreshTitle(true); // in case of error we must refresh changed title of 7zFM
return res;
}
RefreshListCtrl();
return S_OK;
}
HRESULT CPanel::OpenParentArchiveFolder()
{
CDisableTimerProcessing disableTimerProcessing(*this);
CDisableNotify disableNotify(*this);
if (_parentFolders.Size() < 2)
return S_OK;
const CFolderLink &folderLinkPrev = _parentFolders[_parentFolders.Size() - 2];
const CFolderLink &folderLink = _parentFolders.Back();
NFind::CFileInfo newFileInfo;
if (newFileInfo.Find(folderLink.FilePath))
{
if (folderLink.WasChanged(newFileInfo))
{
UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, folderLink.RelPath);
if (::MessageBoxW((HWND)*this, message, L"7-Zip", MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
{
if (OnOpenItemChanged(folderLink.FileIndex, fs2us(folderLink.FilePath),
folderLinkPrev.UsePassword, folderLinkPrev.Password) != S_OK)
{
::MessageBoxW((HWND)*this, MyFormatNew(IDS_CANNOT_UPDATE_FILE,
fs2us(folderLink.FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP);
return S_OK;
}
}
}
}
folderLink.DeleteDirAndFile();
return S_OK;
}
static const char *kStartExtensions =
#ifdef UNDER_CE
" cab"
#endif
" exe bat com"
" chm"
" msi doc xls ppt pps wps wpt wks xlr wdb vsd pub"
" docx docm dotx dotm xlsx xlsm xltx xltm xlsb xps"
" xlam pptx pptm potx potm ppam ppsx ppsm xsn"
" mpp"
" msg"
" dwf"
" flv swf"
" odt ods"
" wb3"
" pdf"
" ";
static bool FindExt(const char *p, const UString &name)
{
int dotPos = name.ReverseFind_Dot();
if (dotPos < 0 || dotPos == (int)name.Len() - 1)
return false;
AString s;
for (unsigned pos = dotPos + 1;; pos++)
{
wchar_t c = name[pos];
if (c == 0)
break;
if (c >= 0x80)
return false;
s += (char)MyCharLower_Ascii((char)c);
}
for (unsigned i = 0; p[i] != 0;)
{
unsigned j;
for (j = i; p[j] != ' '; j++);
if (s.Len() == j - i && memcmp(p + i, (const char *)s, s.Len()) == 0)
return true;
i = j + 1;
}
return false;
}
static bool DoItemAlwaysStart(const UString &name)
{
return FindExt(kStartExtensions, name);
}
UString GetQuotedString(const UString &s);
static void StartEditApplication(const UString &path, bool useEditor, HWND window /* , CProcess &process */ )
{
UString command;
ReadRegEditor(useEditor, command);
if (command.IsEmpty())
{
#ifdef _WIN32
#ifdef UNDER_CE
command = L"\\Windows\\";
#else
FString winDir;
if (!GetWindowsDir(winDir))
return 0;
NName::NormalizeDirPathPrefix(winDir);
command = fs2us(winDir);
#endif
command += L"notepad.exe";
#else
command += L"vi";
#endif
}
#ifdef _WIN32
HRESULT res = process.Create(command, GetQuotedString(path), NULL);
if (res != SZ_OK)
::MessageBoxW(window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP);
return res;
#else
wxString cmd = (const wchar_t *)command;
long pid = wxExecute(cmd, wxEXEC_ASYNC);
if (pid) return ;
::MessageBoxW(window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP);
#endif
}
void CApp::DiffFiles()
{
const CPanel &panel = GetFocusedPanel();
CRecordVector<UInt32> indices;
panel.GetSelectedItemsIndices(indices);
UString path1, path2;
if (indices.Size() == 2)
{
path1 = panel.GetItemFullPath(indices[0]);
path2 = panel.GetItemFullPath(indices[1]);
}
else if (indices.Size() == 1 && NumPanels >= 2)
{
const CPanel &destPanel = Panels[1 - LastFocusedPanel];
path1 = panel.GetItemFullPath(indices[0]);
CRecordVector<UInt32> indices2;
destPanel.GetSelectedItemsIndices(indices2);
if (indices2.Size() == 1)
path2 = destPanel.GetItemFullPath(indices2[0]);
else
{
UString relPath = panel.GetItemRelPath2(indices[0]);
if (panel._flatMode && !destPanel._flatMode)
relPath = panel.GetItemName(indices[0]);
path2 = destPanel._currentFolderPrefix + relPath;
}
}
else
return;
UString command;
ReadRegDiff(command);
if (command.IsEmpty())
return;
UString param = GetQuotedString(path1);
param.Add_Space();
param += GetQuotedString(path2);
#ifdef _WIN32
HRESULT res = MyCreateProcess(command, param);
if (res == SZ_OK)
return;
#else
wxString cmd = (const wchar_t *)command;
cmd += L" ";
cmd += (const wchar_t *)param;
long pid = wxExecute(cmd, wxEXEC_ASYNC);
if (pid) return ;
#endif
::MessageBoxW(_window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP);
}
#ifndef _UNICODE
typedef BOOL (WINAPI * ShellExecuteExWP)(LPSHELLEXECUTEINFOW lpExecInfo);
#endif
static void StartApplication(const UString &dir, const UString &path, HWND window /* , CProcess &process */ )
{
// FIXME
extern const TCHAR * nameWindowToUnix(const TCHAR * lpFileName);
UString tmpPath = path;
wxString filename(nameWindowToUnix(tmpPath));
wxString ext = filename.AfterLast(_T('.'));
printf("StartApplication(%ls) ext='%ls'\n",(const wchar_t *)filename,(const wchar_t *)ext);
if ( ! ext.empty() )
{
wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
// printf("StartApplication(%ls) ft=%p\n",(const wchar_t *)filename,ft);
if (ft)
{
wxString cmd;
// wxString type; ft->GetMimeType(&type);
wxFileType::MessageParameters params(filename); // , type);
bool ok = ft->GetOpenCommand(&cmd, params);
// printf("StartApplication(%ls) ok=%d\n",(const wchar_t *)filename,(int)ok);
delete ft;
if ( ok )
{
printf("StartApplication(%ls) cmd='%ls'\n",(const wchar_t *)filename,(const wchar_t *)cmd);
long pid = wxExecute(cmd, wxEXEC_ASYNC);
if (pid) return ;
}
}
}
::MessageBoxW(window,
// NError::MyFormatMessageW(::GetLastError()),
L"There is no application associated with the given file name extension",
L"7-Zip", MB_OK | MB_ICONSTOP);
}
static void StartApplicationDontWait(const UString &dir, const UString &path, HWND window)
{
// CProcess process;
StartApplication(dir, path, window /* , process */ );
}
void CPanel::EditItem(int index, bool useEditor)
{
if (!_parentFolders.IsEmpty())
{
OpenItemInArchive(index, false, true, true, useEditor);
return;
}
// CProcess process;
StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this /* , process */ );
}
void CPanel::OpenFolderExternal(int index)
{
UString fsPrefix = GetFsPath();
UString name;
if (index == kParentIndex)
{
int pos = fsPrefix.ReverseFind_PathSepar();
if (pos >= 0 && pos == (int)fsPrefix.Len() - 1)
{
UString s = fsPrefix.Left(pos);
pos = s.ReverseFind_PathSepar();
if (pos >= 0)
fsPrefix.SetFrom(s, pos + 1);
}
name = fsPrefix;
}
else
name = fsPrefix + GetItemRelPath(index) + WCHAR_PATH_SEPARATOR;
StartApplicationDontWait(fsPrefix, name, (HWND)*this);
}
bool CPanel::IsVirus_Message(const UString &name)
{
UString name2;
const wchar_t cRLO = (wchar_t)0x202E;
bool isVirus = false;
bool isSpaceError = false;
name2 = name;
if (name2.Find(cRLO) >= 0)
{
UString badString = cRLO;
name2.Replace(badString, L"[RLO]");
isVirus = true;
}
{
const wchar_t *kVirusSpaces = L" ";
// const unsigned kNumSpaces = strlen(kVirusSpaces);
for (;;)
{
int pos = name2.Find(kVirusSpaces);
if (pos < 0)
break;
isVirus = true;
isSpaceError = true;
name2.Replace(kVirusSpaces, L" ");
}
}
if (!isVirus)
return false;
UString s = LangString(IDS_VIRUS);
if (!isSpaceError)
{
int pos1 = s.Find(L'(');
if (pos1 >= 0)
{
int pos2 = s.Find(L')', pos1 + 1);
if (pos2 >= 0)
{
s.Delete(pos1, pos2 + 1 - pos1);
if (pos1 > 0 && s[pos1 - 1] == ' ' && s[pos1] == '.')
s.Delete(pos1 - 1);
}
}
}
UString name3 = name;
name3.Replace(L'\n', L'_');
name2.Replace(L'\n', L'_');
s.Add_LF(); s += name2;
s.Add_LF(); s += name3;
MessageBoxMyError(s);
return true;
}
void CPanel::OpenItem(int index, bool tryInternal, bool tryExternal, const wchar_t *type)
{
CDisableTimerProcessing disableTimerProcessing(*this);
UString name = GetItemRelPath2(index);
if (IsVirus_Message(name))
return;
if (!_parentFolders.IsEmpty())
{
OpenItemInArchive(index, tryInternal, tryExternal, false, false, type);
return;
}
CDisableNotify disableNotify(*this);
UString prefix = GetFsPath();
UString fullPath = prefix + name;
if (tryInternal)
if (!tryExternal || !DoItemAlwaysStart(name))
{
HRESULT res = OpenItemAsArchive(index, type);
disableNotify.Restore(); // we must restore to allow text notification update
InvalidateList();
if (res == S_OK || res == E_ABORT)
return;
if (res != S_FALSE)
{
MessageBoxError(res);
return;
}
}
if (tryExternal)
{
// SetCurrentDirectory opens HANDLE to folder!!!
// NDirectory::MySetCurrentDirectory(prefix);
StartApplicationDontWait(prefix, fullPath, (HWND)*this);
}
}
class CThreadCopyFrom: public CProgressThreadVirt
{
HRESULT ProcessVirt();
public:
UString FullPath;
UInt32 ItemIndex;
CMyComPtr<IFolderOperations> FolderOperations;
CMyComPtr<IProgress> UpdateCallback;
CUpdateCallback100Imp *UpdateCallbackSpec;
};
HRESULT CThreadCopyFrom::ProcessVirt()
{
return FolderOperations->CopyFromFile(ItemIndex, FullPath, UpdateCallback);
}
HRESULT CPanel::OnOpenItemChanged(UInt32 index, const wchar_t *fullFilePath,
bool usePassword, const UString &password)
{
if (!_folderOperations)
{
MessageBoxErrorLang(IDS_OPERATION_IS_NOT_SUPPORTED);
return E_FAIL;
}
CThreadCopyFrom t;
t.UpdateCallbackSpec = new CUpdateCallback100Imp;
t.UpdateCallback = t.UpdateCallbackSpec;
t.UpdateCallbackSpec->ProgressDialog = &t.ProgressDialog;
t.ItemIndex = index;
t.FullPath = fullFilePath;
t.FolderOperations = _folderOperations;
t.UpdateCallbackSpec->Init();
t.UpdateCallbackSpec->PasswordIsDefined = usePassword;
t.UpdateCallbackSpec->Password = password;
RINOK(t.Create(GetItemName(index), (HWND)*this));
return t.Result;
}
LRESULT CPanel::OnOpenItemChanged(LPARAM lParam)
{
CTmpProcessInfo &tpi = *(CTmpProcessInfo *)lParam;
if (tpi.FullPathFolderPrefix != _currentFolderPrefix)
return 0;
UInt32 fileIndex = tpi.FileIndex;
UInt32 numItems;
_folder->GetNumberOfItems(&numItems);
// This code is not 100% OK for cases when there are several files with
// tpi.RelPath name and there are changes in archive before update.
// So tpi.FileIndex can point to another file.
if (fileIndex >= numItems || GetItemRelPath(fileIndex) != tpi.RelPath)
{
UInt32 i;
for (i = 0; i < numItems; i++)
if (GetItemRelPath(i) == tpi.RelPath)
break;
if (i == numItems)
return 0;
fileIndex = i;
}
CSelectedState state;
SaveSelectedState(state);
CDisableNotify disableNotify(*this); // do we need it??
HRESULT result = OnOpenItemChanged(fileIndex, fs2us(tpi.FilePath), tpi.UsePassword, tpi.Password);
RefreshListCtrl(state);
if (result != S_OK)
return 0;
return 1;
}
class CExitEventLauncher
{
public:
NWindows::NSynchronization::CManualResetEvent _exitEvent;
CExitEventLauncher()
{
if (_exitEvent.Create(false) != S_OK)
throw 9387173;
};
~CExitEventLauncher() { _exitEvent.Set(); }
} g_ExitEventLauncher;
#ifdef _WIN32
static THREAD_FUNC_DECL MyThreadFunction(void *param)
{
CMyAutoPtr<CTmpProcessInfo> tmpProcessInfoPtr((CTmpProcessInfo *)param);
CTmpProcessInfo *tpi = tmpProcessInfoPtr.get();
CChildProcesses &processes = tpi->Processes;
for (;;)
{
CRecordVector<HANDLE> handles;
CRecordVector<int> indices;
FOR_VECTOR (i, processes.Handles)
{
if (processes.NeedWait[i])
{
handles.Add(processes.Handles[i]);
indices.Add(i);
}
}
if (handles.IsEmpty())
break;
handles.Add(g_ExitEventLauncher._exitEvent);
DWORD waitResult = ::WaitForMultipleObjects(handles.Size(), &handles.Front(), FALSE, INFINITE);
if (waitResult >= (DWORD)handles.Size() - 1)
{
processes.CloseAll();
return waitResult >= (DWORD)handles.Size() ? 1 : 0;
}
processes.Update();
processes.DisableWait(indices[waitResult]);
}
NFind::CFileInfo newFileInfo;
if (newFileInfo.Find(tpi->FilePath))
{
if (tpi->WasChanged(newFileInfo))
{
UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath);
if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
{
if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi) != 1)
{
::MessageBoxW(g_HWND, MyFormatNew(IDS_CANNOT_UPDATE_FILE,
fs2us(tpi->FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP);
return 0;
}
}
}
}
tpi->DeleteDirAndFile();
return 0;
}
#endif
#if defined(_WIN32) && !defined(UNDER_CE)
static const FChar *k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
#endif
#ifndef UNDER_CE
static void ReadZoneFile(CFSTR fileName, CByteBuffer &buf)
{
buf.Free();
NIO::CInFile file;
if (!file.Open(fileName))
return;
UInt64 fileSize;
if (!file.GetLength(fileSize))
return;
if (fileSize == 0 || fileSize >= ((UInt32)1 << 20))
return;
buf.Alloc((size_t)fileSize);
UInt32 processed;
if (file.Read(buf, (UInt32)fileSize, processed) && processed == fileSize)
return;
buf.Free();
}
static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
{
NIO::COutFile file;
if (!file.Create(fileName, true))
return false;
UInt32 processed;
if (!file.Write(buf, (UInt32)buf.Size(), processed))
return false;
return processed == buf.Size();
}
#endif
/*
class CBufSeqOutStream_WithFile:
public ISequentialOutStream,
public CMyUnknownImp
{
Byte *_buffer;
size_t _size;
size_t _pos;
size_t _fileWritePos;
bool fileMode;
public:
bool IsStreamInMem() const { return !fileMode; }
size_t GetMemStreamWrittenSize() const { return _pos; }
// ISequentialOutStream *FileStream;
FString FilePath;
COutFileStream *outFileStreamSpec;
CMyComPtr<ISequentialOutStream> outFileStream;
CBufSeqOutStream_WithFile(): outFileStreamSpec(NULL) {}
void Init(Byte *buffer, size_t size)
{
fileMode = false;
_buffer = buffer;
_pos = 0;
_size = size;
_fileWritePos = 0;
}
HRESULT FlushToFile();
size_t GetPos() const { return _pos; }
MY_UNKNOWN_IMP
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
};
static const UInt32 kBlockSize = ((UInt32)1 << 31);
STDMETHODIMP CBufSeqOutStream_WithFile::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
if (processedSize)
*processedSize = 0;
if (!fileMode)
{
if (_size - _pos >= size)
{
if (size != 0)
{
memcpy(_buffer + _pos, data, size);
_pos += size;
}
if (processedSize)
*processedSize = (UInt32)size;
return S_OK;
}
fileMode = true;
}
RINOK(FlushToFile());
return outFileStream->Write(data, size, processedSize);
}
HRESULT CBufSeqOutStream_WithFile::FlushToFile()
{
if (!outFileStream)
{
outFileStreamSpec = new COutFileStream;
outFileStream = outFileStreamSpec;
if (!outFileStreamSpec->Create(FilePath, false))
{
outFileStream.Release();
return E_FAIL;
// MessageBoxMyError(UString(L"Can't create file ") + fs2us(tempFilePath));
}
}
while (_fileWritePos != _pos)
{
size_t cur = _pos - _fileWritePos;
UInt32 curSize = (cur < kBlockSize) ? (UInt32)cur : kBlockSize;
UInt32 processedSizeLoc = 0;
HRESULT res = outFileStream->Write(_buffer + _fileWritePos, curSize, &processedSizeLoc);
_fileWritePos += processedSizeLoc;
RINOK(res);
if (processedSizeLoc == 0)
return E_FAIL;
}
return S_OK;
}
*/
/*
static HRESULT GetTime(IFolderFolder *folder, UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
{
filetimeIsDefined = false;
NCOM::CPropVariant prop;
RINOK(folder->GetProperty(index, propID, &prop));
if (prop.vt == VT_FILETIME)
{
filetime = prop.filetime;
filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
}
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
*/
void CPanel::OpenItemInArchive(int index, bool tryInternal, bool tryExternal, bool editMode, bool useEditor, const wchar_t *type)
{
const UString name = GetItemName(index);
const UString relPath = GetItemRelPath(index);
if (IsVirus_Message(name))
return;
if (!_folderOperations)
{
MessageBoxErrorLang(IDS_OPERATION_IS_NOT_SUPPORTED);
return;
}
bool tryAsArchive = tryInternal && (!tryExternal || !DoItemAlwaysStart(name));
UString fullVirtPath = _currentFolderPrefix + relPath;
CTempDir tempDirectory;
if (!tempDirectory.Create(kTempDirPrefix))
{
MessageBoxLastError();
return;
}
FString tempDir = tempDirectory.GetPath();
FString tempDirNorm = tempDir;
NName::NormalizeDirPathPrefix(tempDirNorm);
const FString tempFilePath = tempDirNorm + us2fs(Get_Correct_FsFile_Name(name));
CTempFileInfo tempFileInfo;
tempFileInfo.FileIndex = index;
tempFileInfo.RelPath = relPath;
tempFileInfo.FolderPath = tempDir;
tempFileInfo.FilePath = tempFilePath;
tempFileInfo.NeedDelete = true;
if (tryAsArchive)
{
CMyComPtr<IInArchiveGetStream> getStream;
_folder.QueryInterface(IID_IInArchiveGetStream, &getStream);
if (getStream)
{
CMyComPtr<ISequentialInStream> subSeqStream;
getStream->GetStream(index, &subSeqStream);
if (subSeqStream)
{
CMyComPtr<IInStream> subStream;
subSeqStream.QueryInterface(IID_IInStream, &subStream);
if (subStream)
{
bool encrypted;
HRESULT res = OpenItemAsArchive(subStream, tempFileInfo, fullVirtPath, type ? type : L"", encrypted);
if (res == S_OK)
{
tempDirectory.DisableDeleting();
RefreshListCtrl();
return;
}
if (res == E_ABORT)
return;
if (res != S_FALSE)
{
// probably we must show some message here
// return;
}
}
}
}
}
CRecordVector<UInt32> indices;
indices.Add(index);
UStringVector messages;
bool usePassword = false;
UString password;
if (_parentFolders.Size() > 0)
{
const CFolderLink &fl = _parentFolders.Back();
usePassword = fl.UsePassword;
password = fl.Password;
}
#if defined(_WIN32) && !defined(UNDER_CE)
CByteBuffer zoneBuf;
#ifndef _UNICODE
if (g_IsNT)
#endif
if (_parentFolders.Size() > 0)
{
const CFolderLink &fl = _parentFolders.Front();
if (!fl.IsVirtual && !fl.FilePath.IsEmpty())
ReadZoneFile(fl.FilePath + k_ZoneId_StreamName, zoneBuf);
}
#endif
CVirtFileSystem *virtFileSystemSpec = NULL;
CMyComPtr<ISequentialOutStream> virtFileSystem;
bool isAltStream = IsItem_AltStream(index);
CCopyToOptions options;
options.includeAltStreams = true;
options.replaceAltStreamChars = isAltStream;
if (tryAsArchive)
{
NCOM::CPropVariant prop;
_folder->GetProperty(index, kpidSize, &prop);
UInt64 fileLimit = g_RAM_Size / 4;
UInt64 fileSize = 0;
if (!ConvertPropVariantToUInt64(prop, fileSize))
fileSize = fileLimit;
if (fileSize <= fileLimit && fileSize > 0)
{
options.streamMode = true;
virtFileSystemSpec = new CVirtFileSystem;
virtFileSystem = virtFileSystemSpec;
// we allow additional total size for small alt streams;
virtFileSystemSpec->MaxTotalAllocSize = fileSize + (1 << 10);
virtFileSystemSpec->DirPrefix = tempDirNorm;
virtFileSystemSpec->Init();
options.VirtFileSystem = virtFileSystem;
options.VirtFileSystemSpec = virtFileSystemSpec;
}
}
options.folder = fs2us(tempDirNorm);
options.showErrorMessages = true;
HRESULT result = CopyTo(options, indices, &messages, usePassword, password);
if (_parentFolders.Size() > 0)
{
CFolderLink &fl = _parentFolders.Back();
fl.UsePassword = usePassword;
fl.Password = password;
}
if (!messages.IsEmpty())
return;
if (result != S_OK)
{
if (result != E_ABORT)
MessageBoxError(result);
return;
}
if (options.VirtFileSystem)
{
if (virtFileSystemSpec->IsStreamInMem())
{
const CVirtFile &file = virtFileSystemSpec->Files[0];
size_t streamSize = (size_t)file.Size;
CBufInStream *bufInStreamSpec = new CBufInStream;
CMyComPtr<IInStream> bufInStream = bufInStreamSpec;
bufInStreamSpec->Init(file.Data, streamSize, virtFileSystem);
bool encrypted;
if (OpenItemAsArchive(bufInStream, tempFileInfo, fullVirtPath, type ? type : L"", encrypted) == S_OK)
{
tempDirectory.DisableDeleting();
RefreshListCtrl();
return;
}
if (virtFileSystemSpec->FlushToDisk(true) != S_OK)
return;
}
}
#if defined(_WIN32) && !defined(UNDER_CE)
if (zoneBuf.Size() != 0)
{
if (NFind::DoesFileExist(tempFilePath))
{
WriteZoneFile(tempFilePath + k_ZoneId_StreamName, zoneBuf);
}
}
#endif
if (tryAsArchive)
{
bool encrypted;
if (OpenItemAsArchive(NULL, tempFileInfo, fullVirtPath, type ? type : L"", encrypted) == S_OK)
{
tempDirectory.DisableDeleting();
RefreshListCtrl();
return;
}
}
CMyAutoPtr<CTmpProcessInfo> tmpProcessInfoPtr(new CTmpProcessInfo());
CTmpProcessInfo *tpi = tmpProcessInfoPtr.get();
tpi->FolderPath = tempDir;
tpi->FilePath = tempFilePath;
tpi->NeedDelete = true;
tpi->UsePassword = usePassword;
tpi->Password = password;
if (!tpi->FileInfo.Find(tempFilePath))
return;
CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi);
if (!tryExternal)
return;
// CProcess process;
// HRESULT res;
if (editMode)
/* res = */ StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this /* , process */ );
else
/* res = */ StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this /* , process */ );
#ifdef _WIN32
if ((HANDLE)process == 0)
return;
tpi->Window = (HWND)(*this);
tpi->FullPathFolderPrefix = _currentFolderPrefix;
tpi->FileIndex = index;
tpi->RelPath = relPath;
tpi->Processes.AddProcess(process.Detach());
NWindows::CThread thread;
if (thread.Create(MyThreadFunction, tpi) != S_OK)
throw 271824;
#endif
tempDirectory.DisableDeleting();
tmpProcessInfoPtr.release();
tmpProcessInfoRelease._needDelete = false;
}
/*
static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24;
static bool CheckDeleteItem(UINT64 currentFileTime, UINT64 folderFileTime)
{
return (currentFileTime - folderFileTime > kTimeLimit &&
folderFileTime - currentFileTime > kTimeLimit);
}
void DeleteOldTempFiles()
{
UString tempPath;
if(!MyGetTempPath(tempPath))
throw 1;
UINT64 currentFileTime;
NTime::GetCurUtcFileTime(currentFileTime);
UString searchWildCard = tempPath + kTempDirPrefix + L"*.tmp";
searchWildCard += WCHAR(NName::kAnyStringWildcard);
NFind::CEnumeratorW enumerator(searchWildCard);
NFind::CFileInfo fileInfo;
while(enumerator.Next(fileInfo))
{
if (!fileInfo.IsDir())
continue;
const UINT64 &cTime = *(const UINT64 *)(&fileInfo.CTime);
if(CheckDeleteItem(cTime, currentFileTime))
RemoveDirectoryWithSubItems(tempPath + fileInfo.Name);
}
}
*/