// VerifyHash.cpp : Implementation of CVerifyHash
#include "stdafx.h"
#include "VerifyHash.h"
// CVerifyHash
#pragma warning( push, 4 )
HRESULT CVerifyHash::QueryContextMenu( HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
uidLastCmd; // Suppress Warning C4100
// DebugBreak();
// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
if( uFlags & CMF_DEFAULTONLY )
{ return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 ); }
int count = (int)files.size();
if( count == 0 )
{
// We did not insert anything. We must inform as such,
// since other Context menu handlers downstream depend on
// our result.
return MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, 0);
}
else if( count == 1 )
{
InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
uidFirstCmd+1, _T("Verify Checksum") );
}
else
{
InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
uidFirstCmd+1, _T("Verify Checksums") );
}
//
// Composition Workaround
//
// Return that we consumed two IDs rather than 1.
// This is besuase we skipped ID 0 (uidFirstCmd), and
// added ID 1 (uidFirstCmd+1)
//
// This means we must handle Command 1 in InvokeCommand(),
// rather than Command 0
return MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, 2 );
}
HRESULT CVerifyHash::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
unsigned int i = 0, j = 0;
UINT style = MB_ICONINFORMATION;
bool found = false;
std::wstring ClipboardText;
std::vector< std::wstring > hashnames;
std::vector< std::wstring > hashvalues;
std::vector< std::wstring > verifiedfiles;
std::vector< std::wstring > verifiedhashes;
std::vector< std::wstring > unverifiedfiles;
std::wstring MsgBoxMessage;
// If lpVerb really points to a std::string,
// ignore this function call and bail out.
if ( 0 != HIWORD( pCmdInfo->lpVerb ) ) {return E_INVALIDARG; }
HWND hwnd = pCmdInfo->hwnd;
// If there is no text on the Clipboard,
// inform the user and bail out.
if( false == GetClipboardText( ClipboardText ) )
{
MessageBox( hwnd, _T("There is not text on the Clipboard."),
_T("File Checksum Verifier"), MB_ICONWARNING );
return S_OK;
}
// Get the command index - the only valid one is 0.
switch( LOWORD( pCmdInfo->lpVerb ) )
{
//
// See comment for the return value of QueryContextMenu()
// case 0:
//
case 1:
{
for( i = 0; i < files.size(); i++ )
{
// Hash found on the Clipboard?
found = false;
CalculateFileHashes( files[ i ], hashnames, hashvalues );
// Start at size(): this has the effect of
// verifying strongest (SHA-256) to weakest (MD4)
for( j = 0; j < hashvalues.size() ; j++ )
{
if( true == Find( hashvalues[ j ], ClipboardText ) )
{
found = true;
verifiedfiles.push_back( files[ i ] );
// Concatenate 'MD5: ' and
// '82A80BE6F1E0E7766FAC3CA661089EE4', etc
verifiedhashes.push_back( hashnames[ j ] + L": " + Snip( hashvalues[ j ] ) );
j = (unsigned)hashvalues.size(); // Break the loop
}
} // for( j = 0; j < hashvalues.size(); j++ )
if( false == found )
{
unverifiedfiles.push_back( files[ i ] );
}
} // for( i = 0; i < files.size(); i++ )
for( i = 0; i < verifiedfiles.size(); i++ )
{
MsgBoxMessage += L"Verified ";
MsgBoxMessage += verifiedfiles[ i ];
MsgBoxMessage += L"\r\n";
MsgBoxMessage += L" ";
MsgBoxMessage += verifiedhashes[ i ]; // verifiedhashes includes 'MD5', etc
MsgBoxMessage += L"\r\n";
if( i + 1 < verifiedfiles.size() )
{ MsgBoxMessage += L"\r\n"; }
}
if( unverifiedfiles.size() > 0 )
{
if( verifiedfiles.size() > 0 )
{
MsgBoxMessage += L"\r\n";
}
if( 1 == unverifiedfiles.size() )
{
MsgBoxMessage += L"Unverified Checksum:\r\n";
}
else
{
MsgBoxMessage += L"Unverified Checksums:\r\n";
}
for( i = 0; i < unverifiedfiles.size(); i++ )
{
MsgBoxMessage += L" ";
MsgBoxMessage += unverifiedfiles[ i ];
MsgBoxMessage += L"\r\n";
}
}
if( verifiedfiles.size() > 0 && unverifiedfiles.size() == 0 )
{ style = MB_ICONINFORMATION; }
else
if( verifiedfiles.size() == 0 && unverifiedfiles.size() > 0 )
{ style = MB_ICONERROR; }
else
if( verifiedfiles.size() > 0 && unverifiedfiles.size() > 0 )
{ style = MB_ICONASTERISK; }
#ifdef _UNICODE
MessageBox( hwnd, MsgBoxMessage.c_str(),
L"File Checksum Results", style );
#else
MessageBox( hwnd, StringNarrow( MsgBoxMessage ).c_str(),
"File Checksum Results", style );
#endif
return S_OK;
break;
}
default:
break;
}
return E_INVALIDARG;
}
HRESULT CVerifyHash::GetCommandString( UINT idCmd, UINT uFlags,
UINT* pwReserved, LPSTR pszName, UINT cchMax )
{
pwReserved; // Suppress Warning C4100
// DebugBreak();
// Check idCmd, it must be 0 since we have only one menu item.
if ( 0 != idCmd ) { return E_INVALIDARG; }
// If Explorer is asking for a help std::string, copy our std::string
// into the supplied buffer.
if( uFlags & GCS_HELPTEXT )
{
if ( uFlags & GCS_UNICODE )
{
LPCWSTR szText = L"File Checksum Extension by Jeffrey Walton";
// We need to cast pszName to a Unicode std::string, and then use the
// Unicode std::string copy API.
lstrcpynW ( reinterpret_cast<LPWSTR>( pszName ), szText, cchMax );
}
else
{
LPCSTR szText = "File Checksum Extension by Jeffrey Walton";
// Use the ANSI std::string copy API to return the help std::string.
lstrcpynA ( pszName, szText, cchMax );
}
return S_OK;
}
return E_INVALIDARG;
}
STDMETHODIMP CVerifyHash::Initialize( LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pDataObj, HKEY hProgID )
{
// DebugBreak();
hProgID; // Suppress Warning C4100
pidlFolder; // Suppress Warning C4100
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
// Look for CF_HDROP data in the data object. If there
// is no such data, return an error back to Explorer.
if( FAILED( pDataObj->GetData ( &fmt, &stg ) ))
{ return E_INVALIDARG; }
// Get a pointer to the actual data.
hDrop = static_cast<HDROP>( GlobalLock ( stg.hGlobal ) );
// Make sure it worked.
if( NULL == hDrop )
{ return E_INVALIDARG; }
// Sanity check - make sure there is at least one filename.
UINT uNumFiles = DragQueryFile( hDrop, static_cast<UINT>(-1), NULL, 0 );
HRESULT hr = S_OK;
if( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
files.clear();
TCHAR file[ MAX_PATH * 4 + 1 ];
// Loop through all the files that were selected.
for(UINT i = 0; i < uNumFiles; i++)
{
DragQueryFile( static_cast<HDROP>( stg.hGlobal ), i, file, MAX_PATH*4 );
// If the file name is a directory, silently skip
// We should not encounter this...
if (::GetFileAttributes( file ) & FILE_ATTRIBUTE_DIRECTORY)
{ continue; }
// Add the file name to the end of the list.
#ifdef UNICODE
files.push_back( file );
#else
files.push_back( StringWiden( file ) );
#endif
}
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
std::wstring CVerifyHash::Snip( const std::wstring& hash )
{
const unsigned int SIZE = 32;
std::wstring result;
if( hash.length() < SIZE + 4 ) { return hash; }
result = hash.substr( 0, SIZE/2 );
result += L"...";
result += hash.substr( hash.length() - SIZE/2 );
return result;
}
bool CVerifyHash::CalculateFileHashes( const std::wstring& filename,
std::vector< std::wstring >& hashnames,
std::vector< std::wstring >& hashvalues )
{
CryptoPP::CRC32 hashCRC;
CryptoPP::HashFilter filterCRC(hashCRC);
// MD2 is not a memebr of the Weak namespace.
// We took it from Crypto++ 5.2 (since it was removed
// prio to 5.5.2 due to weakness)
CryptoPP::MD2 hashMD2;
CryptoPP::HashFilter filterMD2(hashMD2);
CryptoPP::Weak::MD4 hashMD4;
CryptoPP::HashFilter filterMD4(hashMD4);
CryptoPP::Weak::MD5 hashMD5;
CryptoPP::HashFilter filterMD5(hashMD5);
CryptoPP::RIPEMD128 hashRIPEMD128;
CryptoPP::HashFilter filterRIPEMD128(hashRIPEMD128);
// Same strength as RIPEMD-256, hence placed here
CryptoPP::RIPEMD256 hashRIPEMD256;
CryptoPP::HashFilter filterRIPEMD256(hashRIPEMD256);
CryptoPP::SHA1 hashSHA1;
CryptoPP::HashFilter filterSHA1(hashSHA1);
CryptoPP::RIPEMD160 hashRIPEMD160;
CryptoPP::HashFilter filterRIPEMD160(hashRIPEMD160);
// Same strength as RIPEMD-160, hence placed here
CryptoPP::RIPEMD320 hashRIPEMD320;
CryptoPP::HashFilter filterRIPEMD320(hashRIPEMD320);
CryptoPP::SHA224 hashSHA224;
CryptoPP::HashFilter filterSHA224(hashSHA224);
CryptoPP::SHA256 hashSHA256;
CryptoPP::HashFilter filterSHA256(hashSHA256);
CryptoPP::SHA384 hashSHA384;
CryptoPP::HashFilter filterSHA384(hashSHA384);
CryptoPP::Whirlpool hashWhirlpool;
CryptoPP::HashFilter filterWhirlpool(hashWhirlpool);
CryptoPP::SHA512 hashSHA512;
CryptoPP::HashFilter filterSHA512(hashSHA512);
std::auto_ptr<CryptoPP::ChannelSwitch>
channelSwitch(new CryptoPP::ChannelSwitch);
channelSwitch->AddDefaultRoute(filterCRC);
channelSwitch->AddDefaultRoute(filterMD2);
channelSwitch->AddDefaultRoute(filterMD4);
channelSwitch->AddDefaultRoute(filterMD5);
channelSwitch->AddDefaultRoute(filterRIPEMD128);
channelSwitch->AddDefaultRoute(filterRIPEMD256);
channelSwitch->AddDefaultRoute(filterRIPEMD160);
channelSwitch->AddDefaultRoute(filterRIPEMD320);
channelSwitch->AddDefaultRoute(filterSHA1);
channelSwitch->AddDefaultRoute(filterSHA224);
channelSwitch->AddDefaultRoute(filterSHA256);
channelSwitch->AddDefaultRoute(filterSHA384);
channelSwitch->AddDefaultRoute(filterWhirlpool);
channelSwitch->AddDefaultRoute(filterSHA512);
try {
CryptoPP::FileSource( StringNarrow( filename ).c_str(),
true, channelSwitch.release() );
}
catch( CryptoPP::Exception& e ) { e; return false; } // e.what()
catch( std::exception& e ) { e; return false; } // e.what()
catch( ... ) { return false; }
std::string digest;
CryptoPP::HexEncoder encoder( new CryptoPP::StringSink( digest ),
true /* uppercase */ );
hashnames.clear(); hashvalues.clear();
// Order matters - place the hash for which you want to
// find first at the top of the list...
filterSHA512.TransferTo( encoder );
hashnames.push_back( StringWiden( filterSHA512.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterWhirlpool.TransferTo( encoder );
hashnames.push_back( StringWiden( filterWhirlpool.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterSHA384.TransferTo( encoder );
hashnames.push_back( StringWiden( filterSHA384.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterSHA256.TransferTo( encoder );
hashnames.push_back( StringWiden( filterSHA256.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterSHA224.TransferTo( encoder );
hashnames.push_back( StringWiden( filterSHA224.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
// Same cryptographic strength as RIPE MD-160
filterRIPEMD320.TransferTo( encoder );
hashnames.push_back( StringWiden( filterRIPEMD320.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterRIPEMD160.TransferTo( encoder );
hashnames.push_back( StringWiden( filterRIPEMD160.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterSHA1.TransferTo( encoder );
hashnames.push_back( StringWiden( filterSHA1.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
// Same cryptographic strength as RIPE MD-128
filterRIPEMD256.TransferTo( encoder );
hashnames.push_back( StringWiden( filterRIPEMD256.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterRIPEMD128.TransferTo( encoder );
hashnames.push_back( StringWiden( filterRIPEMD128.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterMD5.TransferTo( encoder );
hashnames.push_back( StringWiden( filterMD5.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterMD4.TransferTo( encoder );
hashnames.push_back( StringWiden( filterMD4.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterMD2.TransferTo( encoder );
hashnames.push_back( StringWiden( filterMD2.AlgorithmName() ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
filterCRC.TransferTo( encoder );
// hashnames.push_back( StringWiden( filterCRC.AlgorithmName() ) );
hashnames.push_back( StringWiden( "CRC32" ) );
hashvalues.push_back( StringWiden( digest ) );
digest.erase();
return true;
}
bool CVerifyHash::GetClipboardText( std::wstring& ClipboardText )
{
if( TRUE == OpenClipboard( NULL ) )
{
if( TRUE == IsClipboardFormatAvailable( CF_UNICODETEXT ) )
{
HGLOBAL hClipboardData = GetClipboardData( CF_UNICODETEXT );
if( NULL != hClipboardData )
{
wchar_t* pClipboardText = static_cast<wchar_t*>
( GlobalLock( hClipboardData ) );
if( NULL != pClipboardText )
{
try {
ClipboardText = StringUpper( pClipboardText );
} catch(...) { ; }
GlobalUnlock( hClipboardData );
CloseClipboard( );
return true;
}
}
} // CF_UNICODETEXT
else
if( TRUE == IsClipboardFormatAvailable( CF_LOCALE ) )
{
HGLOBAL hClipboardData = GetClipboardData( CF_LOCALE );
if( NULL != hClipboardData )
{
char* pClipboardText = static_cast<char*>( GlobalLock( hClipboardData ) );
if( NULL != pClipboardText )
{
try {
ClipboardText = StringWiden( StringUpper( pClipboardText ) );
} catch(...) { ; }
GlobalUnlock( hClipboardData );
CloseClipboard( );
return true;
}
}
} // CF_LOCALE
else
if( TRUE == IsClipboardFormatAvailable( CF_TEXT ) )
{
HGLOBAL hClipboardData = GetClipboardData( CF_TEXT );
if( NULL != hClipboardData )
{
char* pClipboardText = static_cast<char*>( GlobalLock( hClipboardData ) );
if( NULL != pClipboardText )
{
try {
ClipboardText = StringWiden( StringUpper( pClipboardText ) );
} catch(...) { ; }
GlobalUnlock( hClipboardData );
CloseClipboard( );
return true;
}
}
} // CF_TEXT
else
if( TRUE == IsClipboardFormatAvailable( CF_OEMTEXT ) )
{
HGLOBAL hClipboardData = GetClipboardData( CF_OEMTEXT );
if( NULL != hClipboardData )
{
char* pClipboardText = static_cast<char*>( GlobalLock( hClipboardData ) );
if( NULL != pClipboardText )
{
try {
ClipboardText = StringWiden( StringUpper( pClipboardText ) );
} catch(...) { ; }
GlobalUnlock( hClipboardData );
CloseClipboard( );
return true;
}
}
} // CF_OEMTEXT
CloseClipboard( );
} // TRUE == OpenClipboard( )
return false;
}
#pragma warning( pop )