Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
17 KiB

  1. #include "priv.h"
  2. #include "cryptmnu.h"
  3. #include <shellapi.h>
  4. #include "resource.h"
  5. enum {
  6. VERB_ERROR = -1,
  7. VERB_ENCRYPT = 0,
  8. VERB_DECRYPT,
  9. };
  10. LPTSTR szVerbs[] = {
  11. TEXT("encrypt"),
  12. TEXT("decrypt"),
  13. };
  14. bool Encryptable(LPCTSTR szFile);
  15. CCryptMenuExt::CCryptMenuExt() {
  16. InitCommonControls();
  17. m_pDataObj = NULL;
  18. m_ObjRefCount = 1;
  19. g_DllRefCount++;
  20. m_nFile = m_nFiles = m_nToDecrypt = m_nToEncrypt = 0;
  21. m_cbToEncrypt = m_cbToDecrypt = 0;
  22. m_cbFile = 256;
  23. m_szFile = new TCHAR[m_cbFile];
  24. m_fShutDown = false;
  25. }
  26. CCryptMenuExt::~CCryptMenuExt() {
  27. ResetSelectedFileList();
  28. if (m_pDataObj) {
  29. m_pDataObj->Release();
  30. }
  31. if (m_szFile) {
  32. delete[] m_szFile;
  33. }
  34. g_DllRefCount--;
  35. }
  36. //IUnknown methods
  37. STDMETHODIMP
  38. CCryptMenuExt::QueryInterface(REFIID riid, void **ppvObject) {
  39. if (IsEqualIID(riid, IID_IUnknown)) {
  40. *ppvObject = (LPUNKNOWN) (LPCONTEXTMENU) this;
  41. AddRef();
  42. return(S_OK);
  43. } else if (IsEqualIID(riid, IID_IShellExtInit)) {
  44. *ppvObject = (LPSHELLEXTINIT) this;
  45. AddRef();
  46. return(S_OK);
  47. } else if (IsEqualIID(riid, IID_IContextMenu)) {
  48. *ppvObject = (LPCONTEXTMENU) this;
  49. AddRef();
  50. return(S_OK);
  51. }
  52. *ppvObject = NULL;
  53. return(E_NOINTERFACE);
  54. }
  55. STDMETHODIMP_(DWORD)
  56. CCryptMenuExt::AddRef() {
  57. return(++m_ObjRefCount);
  58. }
  59. STDMETHODIMP_(DWORD)
  60. CCryptMenuExt::Release() {
  61. if (--m_ObjRefCount == 0) {
  62. m_fShutDown = true;
  63. delete this;
  64. }
  65. return(m_ObjRefCount);
  66. }
  67. //Utility methods
  68. HRESULT
  69. CCryptMenuExt::GetNextSelectedFile(LPTSTR *szFile, __int64 *cbFile) {
  70. FORMATETC fe;
  71. STGMEDIUM med;
  72. HRESULT hr;
  73. DWORD cbNeeded;
  74. WIN32_FIND_DATA w32fd;
  75. DWORD dwAttributes;
  76. if (!m_pDataObj) {
  77. return E_UNEXPECTED;
  78. }
  79. if (!szFile) {
  80. return E_INVALIDARG;
  81. }
  82. *szFile = NULL;
  83. while (!*szFile) {
  84. HANDLE hFile = INVALID_HANDLE_VALUE;
  85. // get the next file out of m_pDataObj
  86. fe.cfFormat = CF_HDROP;
  87. fe.ptd = NULL;
  88. fe.dwAspect = DVASPECT_CONTENT;
  89. fe.lindex = -1;
  90. fe.tymed = TYMED_HGLOBAL;
  91. hr = m_pDataObj->GetData(&fe,&med);
  92. if (FAILED(hr)) {
  93. return(E_FAIL);
  94. }
  95. if (!m_nFiles) {
  96. m_nFiles = DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),0xFFFFFFFF,NULL,0);
  97. }
  98. if (m_nFile >= m_nFiles) {
  99. return E_FAIL;
  100. }
  101. cbNeeded = DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),m_nFile,NULL,0) + 1;
  102. if (cbNeeded > m_cbFile) {
  103. if (m_szFile) delete[] m_szFile;
  104. m_szFile = new TCHAR[cbNeeded];
  105. m_cbFile = cbNeeded;
  106. }
  107. DragQueryFile(reinterpret_cast<HDROP>(med.hGlobal),m_nFile++,m_szFile,m_cbFile);
  108. *szFile = m_szFile;
  109. if (!Encryptable(*szFile)) {
  110. *szFile = NULL;
  111. continue;
  112. }
  113. hFile = FindFirstFile(*szFile,&w32fd);
  114. if (hFile != INVALID_HANDLE_VALUE)
  115. {
  116. *cbFile = MAXDWORD * w32fd.nFileSizeHigh + w32fd.nFileSizeLow +1;
  117. FindClose(hFile);
  118. }
  119. else
  120. {
  121. *szFile = NULL;
  122. continue;
  123. }
  124. dwAttributes = GetFileAttributes(*szFile);
  125. // If we found a system file then skip it:
  126. if ((FILE_ATTRIBUTE_SYSTEM & dwAttributes) ||
  127. (FILE_ATTRIBUTE_TEMPORARY & dwAttributes)) {
  128. *szFile = NULL;
  129. continue;
  130. }
  131. }
  132. return S_OK;
  133. }
  134. void
  135. CCryptMenuExt::ResetSelectedFileList() {
  136. m_nFile = 0;
  137. }
  138. // A file can be encrypted only if it is on an NTFS volume.
  139. bool
  140. Encryptable(LPCTSTR szFile) {
  141. TCHAR szFSName[6]; // This just needs to be longer than "NTFS"
  142. LPTSTR szRoot;
  143. int cchFile;
  144. int nWhack = 0;
  145. if (!szFile || (cchFile = lstrlen(szFile)) < 3) return false;
  146. szRoot = new TCHAR [ cchFile + 1 ];
  147. lstrcpy(szRoot,szFile);
  148. // GetVolumeInformation wants only the root path, so we need to
  149. // strip off the rest. Yuck.
  150. if ('\\' == szRoot[0] && '\\' == szRoot[1]) {
  151. /* UNC Path: chop after the second '\': \\server\share\ */
  152. for(int i=2;i<cchFile;i++) {
  153. if ('\\' == szRoot[i]) nWhack++;
  154. if (2 == nWhack) {
  155. szRoot[i+1] = '\0';
  156. break;
  157. }
  158. }
  159. } else {
  160. // Drive Letter
  161. szRoot[3] = '\0';
  162. }
  163. if (!GetVolumeInformation(szRoot,NULL,0,NULL,NULL,NULL,
  164. szFSName,sizeof(szFSName)/sizeof(szFSName[0]))) {
  165. delete[] szRoot;
  166. return false;
  167. }
  168. delete[] szRoot;
  169. return 0 == lstrcmp(szFSName,TEXT("NTFS"));
  170. }
  171. BOOL CALLBACK
  172. EncryptProgressDlg(HWND hdlg, UINT umsg, WPARAM wp, LPARAM lp) {
  173. switch(umsg) {
  174. case WM_INITDIALOG:
  175. return TRUE;
  176. case WM_COMMAND:
  177. switch(LOWORD(wp)) {
  178. case IDCANCEL: {
  179. DestroyWindow(hdlg);
  180. }
  181. }
  182. break;
  183. }
  184. return FALSE;
  185. }
  186. //IShellExtInit methods
  187. STDMETHODIMP
  188. CCryptMenuExt::Initialize(LPCITEMIDLIST pidlFolder,
  189. LPDATAOBJECT pDataObj,
  190. HKEY hkeyProgID)
  191. {
  192. DWORD dwAttributes;
  193. LPTSTR szFile;
  194. __int64 cbFile;
  195. // Hang on to the data object for later.
  196. // We'll want this information in QueryContextMenu and InvokeCommand
  197. if (!m_pDataObj) {
  198. m_pDataObj = pDataObj;
  199. m_pDataObj->AddRef();
  200. } else {
  201. return(E_UNEXPECTED);
  202. }
  203. ResetSelectedFileList();
  204. while(SUCCEEDED(GetNextSelectedFile(&szFile,&cbFile))) {
  205. // is it encrypted? increment our count of decryptable files
  206. // otherwise increment our count of encryptable files
  207. dwAttributes = GetFileAttributes(szFile);
  208. if (dwAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
  209. m_nToDecrypt++;
  210. m_cbToDecrypt += cbFile;
  211. } else {
  212. m_nToEncrypt++;
  213. m_cbToEncrypt += cbFile;
  214. }
  215. //We need the actual values for the title of the progress dialog
  216. //if ((m_nToEncrypt > 1) && (m_nToDecrypt > 1)) break;
  217. }
  218. return(NOERROR);
  219. }
  220. //IContextMenu methods
  221. STDMETHODIMP
  222. CCryptMenuExt::QueryContextMenu(HMENU hmenu,
  223. UINT indexMenu,
  224. UINT idCmdFirst,
  225. UINT idCmdLast,
  226. UINT uFlags)
  227. {
  228. TCHAR szMenu[50];
  229. UINT idCmd;
  230. if (!m_pDataObj) {
  231. return E_UNEXPECTED;
  232. }
  233. if ((CMF_EXPLORE != (0xF & uFlags)) &&
  234. (CMF_NORMAL != (0xF & uFlags))) {
  235. return(NOERROR);
  236. }
  237. idCmd = idCmdFirst;
  238. if (1 < m_nToEncrypt) {
  239. LoadString(g_hinst,IDS_ENCRYPTMANY,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
  240. } else if (1 == m_nToEncrypt) {
  241. LoadString(g_hinst,IDS_ENCRYPTONE,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
  242. }
  243. if (m_nToEncrypt) {
  244. InsertMenu(hmenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd,szMenu);
  245. }
  246. idCmd++;
  247. if (1 < m_nToDecrypt) {
  248. LoadString(g_hinst,IDS_DECRYPTMANY,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
  249. } else if (1 == m_nToDecrypt) {
  250. LoadString(g_hinst,IDS_DECRYPTONE,szMenu,sizeof(szMenu)/sizeof(szMenu[0]));
  251. }
  252. if (m_nToDecrypt) {
  253. InsertMenu(hmenu,indexMenu++,MF_STRING|MF_BYPOSITION,idCmd,szMenu);
  254. }
  255. idCmd++;
  256. return(MAKE_SCODE(SEVERITY_SUCCESS,0,idCmd-idCmdFirst));
  257. }
  258. DWORD WINAPI
  259. DoEncryptFile(LPVOID szFile) {
  260. return EncryptFile(reinterpret_cast<LPTSTR>(szFile));
  261. }
  262. DWORD WINAPI
  263. DoDecryptFile(LPVOID szFile) {
  264. return DecryptFile(reinterpret_cast<LPTSTR>(szFile),0);
  265. }
  266. STDMETHODIMP
  267. CCryptMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) {
  268. HRESULT hrRet;
  269. LPCMINVOKECOMMANDINFO pici;
  270. int nVerb;
  271. LPTSTR szFile;
  272. if (!m_pDataObj) {
  273. return E_UNEXPECTED;
  274. }
  275. pici = reinterpret_cast<LPCMINVOKECOMMANDINFO>(lpici);
  276. // If pici->lpVerb has 0 in the high word then the low word
  277. // contains the offset to the menu as set in QueryContextMenu
  278. if (HIWORD(pici->lpVerb) == 0) {
  279. nVerb = LOWORD(pici->lpVerb);
  280. } else {
  281. // Initialize nVerb to an illegal value so we don't accidentally
  282. // recognize an invalid verb as legitimate
  283. nVerb = VERB_ERROR;
  284. for(int i=0;i<sizeof(szVerbs)/sizeof(szVerbs[0]);i++) {
  285. if (0 == lstrcmp(reinterpret_cast<LPCTSTR>(pici->lpVerb),szVerbs[i])) {
  286. nVerb = i;
  287. break;
  288. }
  289. }
  290. }
  291. switch(nVerb) {
  292. case VERB_ENCRYPT:
  293. case VERB_DECRYPT: {
  294. HWND hDlg;
  295. TCHAR szDlgTitle[50];
  296. TCHAR szDlgFormat[50];
  297. TCHAR szTimeLeft[50];
  298. TCHAR szTimeFormat[50];
  299. TCHAR szTimeFormatInMin[50];
  300. DWORD nTimeStarted;
  301. DWORD nTimeElapsed;
  302. __int64 nTimeLeft;
  303. __int64 cbDone; // How many bytes we've handled
  304. __int64 cbToDo; // How many bytes total we have to do
  305. __int64 cbFile; // How many bites in the current file
  306. int nShifts; // How many right shifts we need to do to get cbToDo
  307. // into a range handleable by the progress bar.
  308. hDlg = CreateDialog(g_hinst,MAKEINTRESOURCE(IDD_ENCRYPTPROGRESS),GetForegroundWindow(),
  309. reinterpret_cast<DLGPROC>(EncryptProgressDlg));
  310. // Setup the dialog's title, progress bar & animation
  311. if (VERB_ENCRYPT==nVerb) {
  312. if (1 == m_nToEncrypt) {
  313. LoadString(g_hinst,IDS_ENCRYPTINGONE,szDlgTitle,sizeof(szDlgTitle)/sizeof(szDlgTitle[0]));
  314. } else {
  315. LoadString(g_hinst,IDS_ENCRYPTINGMANY,szDlgFormat,sizeof(szDlgFormat)/sizeof(szDlgFormat[0]));
  316. wsprintf(szDlgTitle,szDlgFormat,m_nToEncrypt);
  317. }
  318. SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,m_nToEncrypt));
  319. SendDlgItemMessage(hDlg,IDC_ANIMATE,ACM_OPEN,0,reinterpret_cast<LPARAM>(MAKEINTRESOURCE(IDA_ENCRYPT)));
  320. cbToDo = m_cbToEncrypt;
  321. } else {
  322. if (1 == m_nToDecrypt) {
  323. LoadString(g_hinst,IDS_DECRYPTINGONE,szDlgTitle,sizeof(szDlgTitle)/sizeof(szDlgTitle[0]));
  324. } else {
  325. LoadString(g_hinst,IDS_DECRYPTINGMANY,szDlgFormat,sizeof(szDlgFormat)/sizeof(szDlgFormat[0]));
  326. wsprintf(szDlgTitle,szDlgFormat,m_nToDecrypt);
  327. }
  328. SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,m_nToDecrypt));
  329. SendDlgItemMessage(hDlg,IDC_ANIMATE,ACM_OPEN,0,reinterpret_cast<LPARAM>(MAKEINTRESOURCE(IDA_ENCRYPT)));
  330. cbToDo = m_cbToDecrypt;
  331. }
  332. nShifts = 0;
  333. cbDone = 0;
  334. while((cbToDo >> nShifts) > 65535) {
  335. nShifts++;
  336. }
  337. #ifdef DISPLAY_TIME_ESTIMATE
  338. LoadString(g_hinst,IDS_TIMEEST,szTimeFormat,sizeof(szTimeFormat)/sizeof(szTimeFormat[0]));
  339. LoadString(g_hinst,IDS_TIMEESTMIN,szTimeFormatInMin,sizeof(szTimeFormatInMin)/sizeof(szTimeFormatInMin[0]));
  340. #endif // DISPLAY_TIME_ESTIMATE
  341. SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETRANGE,0,MAKELPARAM(0,cbToDo >> nShifts));
  342. SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETPOS,0,0);
  343. SetWindowText(hDlg,szDlgTitle);
  344. ShowWindow(hDlg,SW_NORMAL);
  345. nTimeStarted = GetTickCount();
  346. ResetSelectedFileList();
  347. while(SUCCEEDED(GetNextSelectedFile(&szFile,&cbFile))) {
  348. if (!IsWindow(hDlg)) {
  349. break;
  350. }
  351. if (GetFileAttributes(szFile) & FILE_ATTRIBUTE_ENCRYPTED) {
  352. if (VERB_ENCRYPT == nVerb) {
  353. continue;
  354. }
  355. } else {
  356. if (VERB_DECRYPT == nVerb) {
  357. continue;
  358. }
  359. }
  360. // Set the name of the file currently being encrypted
  361. SetDlgItemText(hDlg,IDC_NAME,szFile);
  362. HANDLE hThread;
  363. if (VERB_ENCRYPT == nVerb) {
  364. hThread = CreateThread(NULL,0,DoEncryptFile,szFile,0,NULL);
  365. } else {
  366. hThread = CreateThread(NULL,0,DoDecryptFile,szFile,0,NULL);
  367. }
  368. MSG msg;
  369. DWORD dw;
  370. do {
  371. dw = MsgWaitForMultipleObjects(1,&hThread,0,INFINITE,QS_ALLINPUT);
  372. while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  373. TranslateMessage(&msg);
  374. DispatchMessage(&msg);
  375. }
  376. } while (WAIT_OBJECT_0 != dw);
  377. GetExitCodeThread(hThread,&dw);
  378. if (0 == dw) {
  379. // Encrypt or Decrypt Failed
  380. TCHAR szFormat[512];
  381. TCHAR szBody[512];
  382. TCHAR szTitle[80];
  383. int nResBody,nResTitle;
  384. UINT uMBType;
  385. uMBType = MB_OKCANCEL;
  386. if (VERB_ENCRYPT == nVerb) {
  387. nResTitle = IDS_ENCRYPTFAILEDTITLE;
  388. if (1 == m_nToEncrypt) {
  389. uMBType = MB_OK;
  390. nResBody = IDS_ENCRYPTFAILEDONE;
  391. } else {
  392. nResBody = IDS_ENCRYPTFAILEDMANY;
  393. }
  394. } else {
  395. nResTitle = IDS_DECRYPTFAILEDTITLE;
  396. if (1 == m_nToDecrypt) {
  397. uMBType = MB_OK;
  398. nResBody = IDS_DECRYPTFAILEDONE;
  399. } else {
  400. nResBody = IDS_DECRYPTFAILEDMANY;
  401. }
  402. }
  403. LoadString(g_hinst,nResBody,szFormat,sizeof(szFormat)/sizeof(szFormat[0]));
  404. wsprintf(szBody,szFormat,szFile);
  405. LoadString(g_hinst,nResTitle,szFormat,sizeof(szFormat)/sizeof(szFormat[0]));
  406. wsprintf(szTitle,szFormat,szFile);
  407. if (IDCANCEL == MessageBox(hDlg,szBody,szTitle,uMBType|MB_ICONWARNING)) {
  408. if (IsWindow(hDlg)) {
  409. DestroyWindow(hDlg);
  410. }
  411. }
  412. }
  413. CloseHandle(hThread);
  414. if (!IsWindow(hDlg)) {
  415. break;
  416. }
  417. // Advance the progress Bar
  418. cbDone += cbFile;
  419. SendDlgItemMessage(hDlg,IDC_PROBAR,PBM_SETPOS,(DWORD)(cbDone >> nShifts),0);
  420. #ifdef DISPLAY_TIME_ESTIMATE
  421. nTimeElapsed = GetTickCount() - nTimeStarted;
  422. nTimeLeft = (cbToDo * nTimeElapsed) / cbDone - nTimeElapsed;
  423. nTimeLeft /= 1000; // Convert to seconds
  424. if (nTimeLeft < 60) {
  425. wsprintf(szTimeLeft,szTimeFormat,(DWORD)(nTimeLeft));
  426. } else {
  427. wsprintf(szTimeLeft,szTimeFormatInMin,(DWORD)(nTimeLeft / 60), (DWORD) (nTimeLeft % 60));
  428. }
  429. SetDlgItemText(hDlg,IDC_TIMEEST,szTimeLeft);
  430. #endif // DISPLAY_TIME_ESTIMATE
  431. }
  432. if (IsWindow(hDlg)) {
  433. DestroyWindow(hDlg);
  434. }
  435. hrRet = NOERROR;
  436. }
  437. break;
  438. default:
  439. hrRet = E_UNEXPECTED;
  440. break;
  441. }
  442. return(hrRet);
  443. }
  444. STDMETHODIMP
  445. CCryptMenuExt::GetCommandString(
  446. UINT_PTR idCmd, //Menu item identifier offset
  447. UINT uFlags, //Specifies information to retrieve
  448. LPUINT pwReserved, //Reserved; must be NULL
  449. LPSTR pszName, //Address of buffer to receive string
  450. UINT cchMax //Size of the buffer that receives the string
  451. )
  452. {
  453. LPTSTR wszName;
  454. // On NT we get unicode here, even though the base IContextMenu class
  455. // is hardcoded to ANSI.
  456. wszName = reinterpret_cast<LPTSTR>(pszName);
  457. if (idCmd >= sizeof(szVerbs)/sizeof(szVerbs[0])) {
  458. return(E_INVALIDARG);
  459. }
  460. switch(uFlags) {
  461. case GCS_HELPTEXT:
  462. switch(idCmd) {
  463. case VERB_ENCRYPT:
  464. if (1 < m_nToEncrypt) {
  465. LoadString(g_hinst,IDS_ENCRYPTMANYHELP,wszName,cchMax);
  466. } else {
  467. LoadString(g_hinst,IDS_ENCRYPTONEHELP,wszName,cchMax);
  468. }
  469. break;
  470. case VERB_DECRYPT:
  471. if (1 < m_nToDecrypt) {
  472. LoadString(g_hinst,IDS_DECRYPTMANYHELP,wszName,cchMax);
  473. } else {
  474. LoadString(g_hinst,IDS_DECRYPTONEHELP,wszName,cchMax);
  475. }
  476. break;
  477. default:
  478. break;
  479. }
  480. break;
  481. case GCS_VALIDATE: {
  482. break;
  483. }
  484. case GCS_VERB:
  485. lstrcpyn(wszName,szVerbs[idCmd],cchMax);
  486. pszName[cchMax-1] = '\0';
  487. break;
  488. }
  489. return(NOERROR);
  490. }