windows-nt/Source/XPSP1/NT/base/fs/utils/cryptmnu/cryptmnu.cpp

573 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "priv.h"
#include "cryptmnu.h"
#include <shellapi.h>
#include "resource.h"
enum {
VERB_ERROR = -1,
VERB_ENCRYPT = 0,
VERB_DECRYPT,
};
LPTSTR szVerbs[] = {
TEXT("encrypt"),
TEXT("decrypt"),
};
bool Encryptable(LPCTSTR szFile);
CCryptMenuExt::CCryptMenuExt() {
InitCommonControls();
m_pDataObj = NULL;
m_ObjRefCount = 1;
g_DllRefCount++;
m_nFile = m_nFiles = m_nToDecrypt = m_nToEncrypt = 0;
m_cbToEncrypt = m_cbToDecrypt = 0;
m_cbFile = 256;
m_szFile = new TCHAR[m_cbFile];
m_fShutDown = false;
}
CCryptMenuExt::~CCryptMenuExt() {
ResetSelectedFileList();
if (m_pDataObj) {
m_pDataObj->Release();
}
if (m_szFile) {
delete[] m_szFile;
}
g_DllRefCount--;
}
//IUnknown methods
STDMETHODIMP
CCryptMenuExt::QueryInterface(REFIID riid, void **ppvObject) {
if (IsEqualIID(riid, IID_IUnknown)) {
*ppvObject = (LPUNKNOWN) (LPCONTEXTMENU) this;
AddRef();
return(S_OK);
} else if (IsEqualIID(riid, IID_IShellExtInit)) {
*ppvObject = (LPSHELLEXTINIT) this;
AddRef();
return(S_OK);
} else if (IsEqualIID(riid, IID_IContextMenu)) {
*ppvObject = (LPCONTEXTMENU) this;
AddRef();
return(S_OK);
}
*ppvObject = NULL;
return(E_NOINTERFACE);
}
STDMETHODIMP_(DWORD)
CCryptMenuExt::AddRef() {
return(++m_ObjRefCount);
}
STDMETHODIMP_(DWORD)
CCryptMenuExt::Release() {
if (--m_ObjRefCount == 0) {
m_fShutDown = true;
delete this;
}
return(m_ObjRefCount);
}
//Utility methods
HRESULT
CCryptMenuExt::GetNextSelectedFile(LPTSTR *szFile, __int64 *cbFile) {
FORMATETC fe;
STGMEDIUM med;
HRESULT hr;
DWORD cbNeeded;
WIN32_FIND_DATA w32fd;
DWORD dwAttributes;
if (!m_pDataObj) {
return E_UNEXPECTED;
}
if (!szFile) {
return E_INVALIDARG;
}
*szFile = NULL;
while (!*szFile) {
HANDLE hFile = INVALID_HANDLE_VALUE;
// get the next file out of m_pDataObj
fe.cfFormat = CF_HDROP;
fe.ptd = NULL;
fe.dwAspect = DVASPECT_CONTENT;
fe.lindex = -1;
fe.tymed = TYMED_HGLOBAL;
hr = m_pDataObj->GetData(&fe,&med);
if (FAILED(hr)) {
return(E_FAIL);
}
if (!m_nFiles) {
m_nFiles = DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),0xFFFFFFFF,NULL,0);
}
if (m_nFile >= m_nFiles) {
return E_FAIL;
}
cbNeeded = DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),m_nFile,NULL,0) + 1;
if (cbNeeded > m_cbFile) {
if (m_szFile) delete[] m_szFile;
m_szFile = new TCHAR[cbNeeded];
m_cbFile = cbNeeded;
}
DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),m_nFile++,m_szFile,m_cbFile);
*szFile = m_szFile;
if (!Encryptable(*szFile)) {
*szFile = NULL;
continue;
}
hFile = FindFirstFile(*szFile,&w32fd);
if (hFile != INVALID_HANDLE_VALUE)
{
*cbFile = MAXDWORD * w32fd.nFileSizeHigh + w32fd.nFileSizeLow +1;
FindClose(hFile);
}
else
{
*szFile = NULL;
continue;
}
dwAttributes = GetFileAttributes(*szFile);
// If we found a system file then skip it:
if ((FILE_ATTRIBUTE_SYSTEM & dwAttributes) ||
(FILE_ATTRIBUTE_TEMPORARY & dwAttributes)) {
*szFile = NULL;
continue;
}
}
return S_OK;
}
void
CCryptMenuExt::ResetSelectedFileList() {
m_nFile = 0;
}
// A file can be encrypted only if it is on an NTFS volume.
bool
Encryptable(LPCTSTR szFile) {
TCHAR szFSName[6]; // This just needs to be longer than "NTFS"
LPTSTR szRoot;
int cchFile;
int nWhack = 0;
if (!szFile || (cchFile = lstrlen(szFile)) < 3) return false;
szRoot = new TCHAR [ cchFile + 1 ];
lstrcpy(szRoot,szFile);
// GetVolumeInformation wants only the root path, so we need to
// strip off the rest. Yuck.
if ('\\' == szRoot[0] && '\\' == szRoot[1]) {
/* UNC Path: chop after the second '\': \\server\share\ */
for(int i=2;i<cchFile;i++) {
if ('\\' == szRoot[i]) nWhack++;
if (2 == nWhack) {
szRoot[i+1] = '\0';
break;
}
}
} else {
// Drive Letter
szRoot[3] = '\0';
}
if (!GetVolumeInformation(szRoot,NULL,0,NULL,NULL,NULL,
szFSName,sizeof(szFSName)/sizeof(szFSName[0]))) {
delete[] szRoot;
return false;
}
delete[] szRoot;
return 0 == lstrcmp(szFSName,TEXT("NTFS"));
}
BOOL CALLBACK
EncryptProgressDlg(HWND hdlg, UINT umsg, WPARAM wp, LPARAM lp) {
switch(umsg) {
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wp)) {
case IDCANCEL: {
DestroyWindow(hdlg);
}
}
break;
}
return FALSE;
}
//IShellExtInit methods
STDMETHODIMP
CCryptMenuExt::Initialize(LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pDataObj,
HKEY hkeyProgID)
{
DWORD dwAttributes;
LPTSTR szFile;
__int64 cbFile;
// Hang on to the data object for later.
// We'll want this information in QueryContextMenu and InvokeCommand
if (!m_pDataObj) {
m_pDataObj = pDataObj;
m_pDataObj->AddRef();
} else {
return(E_UNEXPECTED);
}
ResetSelectedFileList();
while(SUCCEEDED(GetNextSelectedFile(&szFile,&cbFile))) {
// is it encrypted? increment our count of decryptable files
// otherwise increment our count of encryptable files
dwAttributes = GetFileAttributes(szFile);
if (dwAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
m_nToDecrypt++;
m_cbToDecrypt += cbFile;
} else {
m_nToEncrypt++;
m_cbToEncrypt += cbFile;
}
//We need the actual values for the title of the progress dialog
//if ((m_nToEncrypt > 1) && (m_nToDecrypt > 1)) break;
}
return(NOERROR);
}
//IContextMenu methods
STDMETHODIMP
CCryptMenuExt::QueryContextMenu(HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
TCHAR szMenu[50];
UINT idCmd;
if (!m_pDataObj) {
return E_UNEXPECTED;
}
if ((CMF_EXPLORE != (0xF & uFlags)) &&
(CMF_NORMAL != (0xF & uFlags))) {
return(NOERROR);
}
idCmd = idCmdFirst;
if (1 < m_nToEncrypt) {
LoadString(g_hinst,IDS_ENCRYPTMANY,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
} else if (1 == m_nToEncrypt) {
LoadString(g_hinst,IDS_ENCRYPTONE,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
}
if (m_nToEncrypt) {
InsertMenu(hmenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd,szMenu);
}
idCmd++;
if (1 < m_nToDecrypt) {
LoadString(g_hinst,IDS_DECRYPTMANY,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
} else if (1 == m_nToDecrypt) {
LoadString(g_hinst,IDS_DECRYPTONE,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
}
if (m_nToDecrypt) {
InsertMenu(hmenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd,szMenu);
}
idCmd++;
return(MAKE_SCODE(SEVERITY_SUCCESS,0,idCmd-idCmdFirst));
}
DWORD WINAPI
DoEncryptFile(LPVOID szFile) {
return EncryptFile(reinterpret_cast<LPTSTR>(szFile));
}
DWORD WINAPI
DoDecryptFile(LPVOID szFile) {
return DecryptFile(reinterpret_cast<LPTSTR>(szFile),0);
}
STDMETHODIMP
CCryptMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) {
HRESULT hrRet;
LPCMINVOKECOMMANDINFO pici;
int nVerb;
LPTSTR szFile;
if (!m_pDataObj) {
return E_UNEXPECTED;
}
pici = reinterpret_cast<LPCMINVOKECOMMANDINFO>(lpici);
// If pici->lpVerb has 0 in the high word then the low word
// contains the offset to the menu as set in QueryContextMenu
if (HIWORD(pici->lpVerb) == 0) {
nVerb = LOWORD(pici->lpVerb);
} else {
// Initialize nVerb to an illegal value so we don't accidentally
// recognize an invalid verb as legitimate
nVerb = VERB_ERROR;
for(int i=0;i<sizeof(szVerbs)/sizeof(szVerbs[0]);i++) {
if (0 == lstrcmp(reinterpret_cast<LPCTSTR>(pici->lpVerb),szVerbs[i])) {
nVerb = i;
break;
}
}
}
switch(nVerb) {
case VERB_ENCRYPT:
case VERB_DECRYPT: {
HWND hDlg;
TCHAR szDlgTitle[50];
TCHAR szDlgFormat[50];
TCHAR szTimeLeft[50];
TCHAR szTimeFormat[50];
TCHAR szTimeFormatInMin[50];
DWORD nTimeStarted;
DWORD nTimeElapsed;
__int64 nTimeLeft;
__int64 cbDone; // How many bytes we've handled
__int64 cbToDo; // How many bytes total we have to do
__int64 cbFile; // How many bites in the current file
int nShifts; // How many right shifts we need to do to get cbToDo
// into a range handleable by the progress bar.
hDlg = CreateDialog(g_hinst,MAKEINTRESOURCE(IDD_ENCRYPTPROGRESS),GetForegroundWindow(),
reinterpret_cast<DLGPROC>(EncryptProgressDlg));
// Setup the dialog's title, progress bar & animation
if (VERB_ENCRYPT==nVerb) {
if (1 == m_nToEncrypt) {
LoadString(g_hinst,IDS_ENCRYPTINGONE,szDlgTitle,sizeof(szDlgTitle)/sizeof(szDlgTitle[0]));
} else {
LoadString(g_hinst,IDS_ENCRYPTINGMANY,szDlgFormat,sizeof(szDlgFormat)/sizeof(szDlgFormat[0]));
wsprintf(szDlgTitle,szDlgFormat,m_nToEncrypt);
}
SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,m_nToEncrypt));
SendDlgItemMessage(hDlg,IDC_ANIMATE,ACM_OPEN,0,reinterpret_cast<LPARAM>(MAKEINTRESOURCE(IDA_ENCRYPT)));
cbToDo = m_cbToEncrypt;
} else {
if (1 == m_nToDecrypt) {
LoadString(g_hinst,IDS_DECRYPTINGONE,szDlgTitle,sizeof(szDlgTitle)/sizeof(szDlgTitle[0]));
} else {
LoadString(g_hinst,IDS_DECRYPTINGMANY,szDlgFormat,sizeof(szDlgFormat)/sizeof(szDlgFormat[0]));
wsprintf(szDlgTitle,szDlgFormat,m_nToDecrypt);
}
SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,m_nToDecrypt));
SendDlgItemMessage(hDlg,IDC_ANIMATE,ACM_OPEN,0,reinterpret_cast<LPARAM>(MAKEINTRESOURCE(IDA_ENCRYPT)));
cbToDo = m_cbToDecrypt;
}
nShifts = 0;
cbDone = 0;
while((cbToDo >> nShifts) > 65535) {
nShifts++;
}
#ifdef DISPLAY_TIME_ESTIMATE
LoadString(g_hinst,IDS_TIMEEST,szTimeFormat,sizeof(szTimeFormat)/sizeof(szTimeFormat[0]));
LoadString(g_hinst,IDS_TIMEESTMIN,szTimeFormatInMin,sizeof(szTimeFormatInMin)/sizeof(szTimeFormatInMin[0]));
#endif // DISPLAY_TIME_ESTIMATE
SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,cbToDo >> nShifts));
SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETPOS,0,0);
SetWindowText(hDlg,szDlgTitle);
ShowWindow(hDlg,SW_NORMAL);
nTimeStarted = GetTickCount();
ResetSelectedFileList();
while(SUCCEEDED(GetNextSelectedFile(&szFile,&cbFile))) {
if (!IsWindow(hDlg)) {
break;
}
if (GetFileAttributes(szFile) & FILE_ATTRIBUTE_ENCRYPTED) {
if (VERB_ENCRYPT == nVerb) {
continue;
}
} else {
if (VERB_DECRYPT == nVerb) {
continue;
}
}
// Set the name of the file currently being encrypted
SetDlgItemText(hDlg,IDC_NAME,szFile);
HANDLE hThread;
if (VERB_ENCRYPT == nVerb) {
hThread = CreateThread(NULL,0,DoEncryptFile,szFile,0,NULL);
} else {
hThread = CreateThread(NULL,0,DoDecryptFile,szFile,0,NULL);
}
MSG msg;
DWORD dw;
do {
dw = MsgWaitForMultipleObjects(1,&hThread,0,INFINITE,QS_ALLINPUT);
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while (WAIT_OBJECT_0 != dw);
GetExitCodeThread(hThread,&dw);
if (0 == dw) {
// Encrypt or Decrypt Failed
TCHAR szFormat[512];
TCHAR szBody[512];
TCHAR szTitle[80];
int nResBody,nResTitle;
UINT uMBType;
uMBType = MB_OKCANCEL;
if (VERB_ENCRYPT == nVerb) {
nResTitle = IDS_ENCRYPTFAILEDTITLE;
if (1 == m_nToEncrypt) {
uMBType = MB_OK;
nResBody = IDS_ENCRYPTFAILEDONE;
} else {
nResBody = IDS_ENCRYPTFAILEDMANY;
}
} else {
nResTitle = IDS_DECRYPTFAILEDTITLE;
if (1 == m_nToDecrypt) {
uMBType = MB_OK;
nResBody = IDS_DECRYPTFAILEDONE;
} else {
nResBody = IDS_DECRYPTFAILEDMANY;
}
}
LoadString(g_hinst,nResBody,szFormat,sizeof(szFormat)/sizeof(szFormat[0]));
wsprintf(szBody,szFormat,szFile);
LoadString(g_hinst,nResTitle,szFormat,sizeof(szFormat)/sizeof(szFormat[0]));
wsprintf(szTitle,szFormat,szFile);
if (IDCANCEL == MessageBox(hDlg,szBody,szTitle,uMBType|MB_ICONWARNING)) {
if (IsWindow(hDlg)) {
DestroyWindow(hDlg);
}
}
}
CloseHandle(hThread);
if (!IsWindow(hDlg)) {
break;
}
// Advance the progress Bar
cbDone += cbFile;
SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETPOS,(DWORD)(cbDone >> nShifts),0);
#ifdef DISPLAY_TIME_ESTIMATE
nTimeElapsed = GetTickCount() - nTimeStarted;
nTimeLeft = (cbToDo * nTimeElapsed) / cbDone - nTimeElapsed;
nTimeLeft /= 1000; // Convert to seconds
if (nTimeLeft < 60) {
wsprintf(szTimeLeft,szTimeFormat,(DWORD)(nTimeLeft));
} else {
wsprintf(szTimeLeft,szTimeFormatInMin,(DWORD)(nTimeLeft / 60), (DWORD) (nTimeLeft % 60));
}
SetDlgItemText(hDlg,IDC_TIMEEST,szTimeLeft);
#endif // DISPLAY_TIME_ESTIMATE
}
if (IsWindow(hDlg)) {
DestroyWindow(hDlg);
}
hrRet = NOERROR;
}
break;
default:
hrRet = E_UNEXPECTED;
break;
}
return(hrRet);
}
STDMETHODIMP
CCryptMenuExt::GetCommandString(
UINT_PTR idCmd, //Menu item identifier offset
UINT uFlags, //Specifies information to retrieve
LPUINT pwReserved, //Reserved; must be NULL
LPSTR pszName, //Address of buffer to receive string
UINT cchMax //Size of the buffer that receives the string
)
{
LPTSTR wszName;
// On NT we get unicode here, even though the base IContextMenu class
// is hardcoded to ANSI.
wszName = reinterpret_cast<LPTSTR>(pszName);
if (idCmd >= sizeof(szVerbs)/sizeof(szVerbs[0])) {
return(E_INVALIDARG);
}
switch(uFlags) {
case GCS_HELPTEXT:
switch(idCmd) {
case VERB_ENCRYPT:
if (1 < m_nToEncrypt) {
LoadString(g_hinst,IDS_ENCRYPTMANYHELP,wszName,cchMax);
} else {
LoadString(g_hinst,IDS_ENCRYPTONEHELP,wszName,cchMax);
}
break;
case VERB_DECRYPT:
if (1 < m_nToDecrypt) {
LoadString(g_hinst,IDS_DECRYPTMANYHELP,wszName,cchMax);
} else {
LoadString(g_hinst,IDS_DECRYPTONEHELP,wszName,cchMax);
}
break;
default:
break;
}
break;
case GCS_VALIDATE: {
break;
}
case GCS_VERB:
lstrcpyn(wszName,szVerbs[idCmd],cchMax);
pszName[cchMax-1] = '\0';
break;
}
return(NOERROR);
}