RetroZilla/embedding/browser/activex/tests/IEPatcher/IEPatcherDlg.cpp
2015-10-20 23:03:22 -04:00

623 lines
14 KiB
C++

// IEPatcherDlg.cpp : implementation file
//
#include "stdafx.h"
#include "IEPatcher.h"
#include "IEPatcherDlg.h"
#include "ScanForFilesDlg.h"
#include "ScannerThread.h"
#include "DlgProxy.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const CLSID CLSID_WebBrowser_V1 = { 0xEAB22AC3, 0x30C1, 0x11CF, { 0xA7, 0xEB, 0x00, 0x00, 0xC0, 0x5B, 0xAE, 0x0B } };
const CLSID CLSID_WebBrowser = { 0x8856F961, 0x340A, 0x11D0, { 0xA9, 0x6B, 0x00, 0xC0, 0x4F, 0xD7, 0x05, 0xA2 } };
const CLSID CLSID_InternetExplorer = { 0x0002DF01, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
const CLSID CLSID_MozillaBrowser = {0x1339B54C,0x3453,0x11D2,{0x93,0xB9,0x00,0x00,0x00,0x00,0x00,0x00}};
const IID IID_IWebBrowser = { 0xEAB22AC1, 0x30C1, 0x11CF, { 0xA7, 0xEB, 0x00, 0x00, 0xC0, 0x5B, 0xAE, 0x0B } };
const IID IID_IWebBrowser2 = { 0xD30C1661, 0xCDAF, 0x11d0, { 0x8A, 0x3E, 0x00, 0xC0, 0x4F, 0xC9, 0xE2, 0x6E } };
const IID IID_IWebBrowserApp = { 0x0002DF05, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
#define ITEM_PATCHSTATUS 1
#define IMG_DOESNTCONTAINIE 0
#define IMG_CONTAINSIE 1
#define IMG_CONTAINSMOZILLA 2
#define IMG_UNKNOWNSTATUS 3
/////////////////////////////////////////////////////////////////////////////
// CIEPatcherDlg dialog
IMPLEMENT_DYNAMIC(CIEPatcherDlg, CDialog);
CIEPatcherDlg::CIEPatcherDlg(CWnd* pParent /*=NULL*/)
: CDialog(CIEPatcherDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CIEPatcherDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pAutoProxy = NULL;
}
CIEPatcherDlg::~CIEPatcherDlg()
{
// If there is an automation proxy for this dialog, set
// its back pointer to this dialog to NULL, so it knows
// the dialog has been deleted.
if (m_pAutoProxy != NULL)
m_pAutoProxy->m_pDialog = NULL;
}
void CIEPatcherDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CIEPatcherDlg)
DDX_Control(pDX, IDC_PATCH, m_btnPatch);
DDX_Control(pDX, IDC_FILELIST, m_cFileList);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CIEPatcherDlg, CDialog)
//{{AFX_MSG_MAP(CIEPatcherDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_SCAN, OnScan)
ON_NOTIFY(NM_CLICK, IDC_FILELIST, OnClickFilelist)
ON_BN_CLICKED(IDC_PATCH, OnPatch)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_UPDATEFILESTATUS, OnUpdateFileStatus)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIEPatcherDlg message handlers
BOOL CIEPatcherDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Start the scanner thread
AfxBeginThread(RUNTIME_CLASS(CScannerThread), 0);
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Add icons to image list
m_cImageList.Create(16, 16, ILC_MASK, 0, 5);
m_cImageList.Add(AfxGetApp()->LoadIcon(IDI_DOESNTCONTAINIE));
m_cImageList.Add(AfxGetApp()->LoadIcon(IDI_CONTAINSIE));
m_cImageList.Add(AfxGetApp()->LoadIcon(IDI_CONTAINSMOZILLA));
m_cImageList.Add(AfxGetApp()->LoadIcon(IDI_UNKNOWNSTATUS));
// Associate image list with file list
m_cFileList.SetImageList(&m_cImageList, LVSIL_SMALL);
struct ColumnData
{
TCHAR *sColumnTitle;
int nPercentageWidth;
int nFormat;
};
ColumnData aColData[] =
{
{ _T("File"), 70, LVCFMT_LEFT },
{ _T("Patch Status"), 30, LVCFMT_LEFT }
};
// Get the size of the file list control and neatly
// divide it into columns
CRect rcFileList;
m_cFileList.GetClientRect(&rcFileList);
int nColTotal = sizeof(aColData) / sizeof(aColData[0]);
int nWidthRemaining = rcFileList.Width();
for (int nCol = 0; nCol < nColTotal; nCol++)
{
ColumnData *pData = &aColData[nCol];
int nColWidth = (rcFileList.Width() * pData->nPercentageWidth) / 100;
if (nCol == nColTotal - 1)
{
nColWidth = nWidthRemaining;
}
else if (nColWidth > nWidthRemaining)
{
nColWidth = nWidthRemaining;
}
m_cFileList.InsertColumn(nCol, pData->sColumnTitle, pData->nFormat, nColWidth);
nWidthRemaining -= nColWidth;
if (nColWidth <= 0)
{
break;
}
}
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CIEPatcherDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CIEPatcherDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
// Automation servers should not exit when a user closes the UI
// if a controller still holds on to one of its objects. These
// message handlers make sure that if the proxy is still in use,
// then the UI is hidden but the dialog remains around if it
// is dismissed.
void CIEPatcherDlg::OnClose()
{
if (CanExit())
CDialog::OnClose();
}
LONG CIEPatcherDlg::OnUpdateFileStatus(WPARAM wParam, LPARAM lParam)
{
PatchStatus ps = (PatchStatus) wParam;
TCHAR *pszFile = (TCHAR *) lParam;
UpdateFileStatus(pszFile, ps);
return 0;
}
void CIEPatcherDlg::OnScan()
{
CScanForFilesDlg dlg;
if (dlg.DoModal() == IDOK)
{
CWaitCursor wc;
CFileFind op;
if (op.FindFile(dlg.m_sFilePattern))
{
op.FindNextFile();
do {
if (!op.IsDirectory())
{
AddFileToList(op.GetFilePath());
}
} while (op.FindNextFile());
op.Close();
}
}
}
void CIEPatcherDlg::OnPatch()
{
int nItem = -1;
do {
nItem = m_cFileList.GetNextItem(nItem, LVNI_ALL | LVNI_SELECTED);
if (nItem != -1)
{
m_cQueuedFileDataList[nItem]->ps = psPatchPending;
UpdateFileStatus(nItem, m_cQueuedFileDataList[nItem]->sFileName, m_cQueuedFileDataList[nItem]->ps);
}
} while (nItem != -1);
}
void CIEPatcherDlg::OnClickFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
// Check if files are selected
*pResult = 0;
}
///////////////////////////////////////////////////////////////////////////////
BOOL CIEPatcherDlg::CanExit()
{
// If the proxy object is still around, then the automation
// controller is still holding on to this application. Leave
// the dialog around, but hide its UI.
if (m_pAutoProxy != NULL)
{
ShowWindow(SW_HIDE);
return FALSE;
}
return TRUE;
}
PatchStatus CIEPatcherDlg::ScanFile(const CString &sFileName)
{
char *pszBuffer = NULL;
long nBufferSize = 0;
if (!ReadFileToBuffer(sFileName, &pszBuffer, &nBufferSize))
{
return psFileError;
}
PatchStatus ps = ScanBuffer(pszBuffer, nBufferSize, FALSE);
delete []pszBuffer;
return ps;
}
BOOL CIEPatcherDlg::WriteBufferToFile(const CString &sFileName, char *pszBuffer, long nBufferSize)
{
CString sMessage;
sMessage.Format("Saving patched file \"%s\"", sFileName);
TraceProgress(sMessage);
FILE *fDest = fopen(sFileName, "wb");
if (fDest == NULL)
{
TraceProgress("Error: Could not open destination file");
return FALSE;
}
fwrite(pszBuffer, 1, nBufferSize, fDest);
fclose(fDest);
TraceProgress("File saved");
return TRUE;
}
BOOL CIEPatcherDlg::ReadFileToBuffer(const CString &sFileName, char **ppszBuffer, long *pnBufferSize)
{
CString sProcessing;
sProcessing.Format("Processing file \"%s\"", sFileName);
TraceProgress(sProcessing);
// Allocate a memory buffer to slurp up the whole file
struct _stat srcStat;
_stat(sFileName, &srcStat);
char *pszBuffer = new char[srcStat.st_size / sizeof(char)];
if (pszBuffer == NULL)
{
TraceProgress("Error: Could not allocate buffer for file");
return FALSE;
}
FILE *fSrc = fopen(sFileName, "rb");
if (fSrc == NULL)
{
TraceProgress("Error: Could not open file");
delete []pszBuffer;
return FALSE;
}
size_t sizeSrc = srcStat.st_size;
size_t sizeRead = 0;
// Dumb but effective
sizeRead = fread(pszBuffer, 1, sizeSrc, fSrc);
fclose(fSrc);
if (sizeRead != sizeSrc)
{
TraceProgress("Error: Could not read all of file");
delete []pszBuffer;
return FALSE;
}
*ppszBuffer = pszBuffer;
*pnBufferSize = sizeRead;
return TRUE;
}
PatchStatus CIEPatcherDlg::ScanBuffer(char *pszBuffer, long nBufferSize, BOOL bApplyPatch)
{
if (nBufferSize < sizeof(CLSID))
{
return psNotPatchable;
}
TraceProgress("Scanning for IE...");
BOOL bPatchApplied = FALSE;
PatchStatus ps = psUnknownStatus;
// Scan through buffer, one byte at a time doing a memcmp
for (size_t i = 0; i < nBufferSize - sizeof(CLSID); i++)
{
if (memcmp(&pszBuffer[i], &CLSID_MozillaBrowser, sizeof(CLSID)) == 0)
{
TraceProgress("Found CLSID_MozillaBrowser");
if (ps == psUnknownStatus)
{
ps = psAlreadyPatched;
}
}
else if (memcmp(&pszBuffer[i], &CLSID_WebBrowser_V1, sizeof(CLSID)) == 0)
{
TraceProgress("Found CLSID_WebBrowser_V1");
if (ps == psUnknownStatus)
{
ps = psPatchable;
}
if (bApplyPatch)
{
TraceProgress("Patching with CLSID_MozillaBrowser");
memcpy(&pszBuffer[i], &CLSID_MozillaBrowser, sizeof(CLSID));
bPatchApplied = TRUE;
}
}
else if (memcmp(&pszBuffer[i], &CLSID_WebBrowser, sizeof(CLSID)) == 0)
{
TraceProgress("Found CLSID_WebBrowser");
if (ps == psUnknownStatus)
{
ps = psPatchable;
}
if (bApplyPatch)
{
TraceProgress("Patching with CLSID_MozillaBrowser");
memcpy(&pszBuffer[i], &CLSID_MozillaBrowser, sizeof(CLSID));
TraceProgress("Patching with CLSID_MozillaBrowser");
bPatchApplied = TRUE;
}
}
else if (memcmp(&pszBuffer[i], &IID_IWebBrowser, sizeof(CLSID)) == 0)
{
TraceProgress("Found IID_IWebBrowser");
if (ps == psUnknownStatus)
{
ps = psPatchable;
}
}
else if (memcmp(&pszBuffer[i], &CLSID_InternetExplorer, sizeof(CLSID)) == 0)
{
TraceProgress("Warning: Found CLSID_InternetExplorer, patching might not work");
if (ps == psUnknownStatus)
{
ps = psMayBePatchable;
}
}
else if (memcmp(&pszBuffer[i], &IID_IWebBrowser2, sizeof(CLSID)) == 0)
{
TraceProgress("Found IID_IWebBrowser2");
if (ps == psUnknownStatus)
{
ps = psPatchable;
}
}
else if (memcmp(&pszBuffer[i], &IID_IWebBrowserApp, sizeof(CLSID)) == 0)
{
TraceProgress("Found IID_IWebBrowserApp");
if (ps == psUnknownStatus)
{
ps = psPatchable;
}
}
}
if (ps == psUnknownStatus)
{
ps = psNotPatchable;
}
if (bApplyPatch)
{
ps = (bPatchApplied) ? psAlreadyPatched : psNotPatchable;
}
TraceProgress("Scan completed");
return ps;
}
BOOL CIEPatcherDlg::PatchFile(const CString & sFileFrom, const CString & sFileTo, PatchStatus *pPatchStatus)
{
if (sFileTo.IsEmpty())
{
return FALSE;
}
if (sFileTo == sFileFrom)
{
TraceProgress("Warning: Patching disabled for safety reasons, source and destination names must differ");
return FALSE;
}
char *pszBuffer = NULL;
long nBufferSize = 0;
if (!ReadFileToBuffer(sFileFrom, &pszBuffer, &nBufferSize))
{
return FALSE;
}
// Scan and patch the buffer
*pPatchStatus = ScanBuffer(pszBuffer, nBufferSize, TRUE);
if (*pPatchStatus == psNotPatchable)
{
TraceProgress("Error: Nothing was found to patch");
}
else
{
// Write out the patch file
WriteBufferToFile(sFileTo, pszBuffer, nBufferSize);
}
delete []pszBuffer;
return FALSE;
}
void CIEPatcherDlg::TraceProgress(const CString &sProgress)
{
// TODO
}
void CIEPatcherDlg::AddFileToList(const CString & sFile)
{
CSingleLock sl(&m_csQueuedFileDataList, TRUE);
int nIndex = m_cFileList.GetItemCount();
m_cFileList.InsertItem(nIndex, sFile, IMG_UNKNOWNSTATUS);
QueuedFileData *pData = new QueuedFileData;
pData->ps = psUnknownStatus;
pData->sFileName = sFile;
pData->sPatchedFileName = sFile;
m_cQueuedFileDataList.Add(pData);
UpdateFileStatus(nIndex, pData->ps);
}
void CIEPatcherDlg::UpdateFileStatus(int nFileIndex, const CString &sFile, PatchStatus ps)
{
CString sStatus;
int nIconStatus = -1;
switch (ps)
{
case psFileError:
sStatus = _T("File error");
break;
case psNotPatchable:
sStatus = _T("Nothing to patch");
nIconStatus = IMG_DOESNTCONTAINIE;
break;
case psPatchable:
sStatus = _T("Patchable");
nIconStatus = IMG_CONTAINSIE;
break;
case psMayBePatchable:
sStatus = _T("Maybe patchable");
nIconStatus = IMG_CONTAINSIE;
break;
case psAlreadyPatched:
sStatus = _T("Already patched");
nIconStatus = IMG_CONTAINSMOZILLA;
break;
case psUnknownStatus:
sStatus = _T("Status pending");
nIconStatus = IMG_UNKNOWNSTATUS;
break;
case psPatchPending:
sStatus = _T("Patch pending");
break;
default:
sStatus = _T("*** ERROR ***");
break;
}
if (nIconStatus != -1)
{
m_cFileList.SetItem(nFileIndex, -1, LVIF_IMAGE, NULL, nIconStatus, 0, 0, 0);
}
m_cFileList.SetItemText(nFileIndex, ITEM_PATCHSTATUS, sStatus);
}
void CIEPatcherDlg::UpdateFileStatus(const CString &sFile, PatchStatus ps)
{
CSingleLock sl(&m_csQueuedFileDataList, TRUE);
for (int n = 0; n < m_cQueuedFileDataList.GetSize(); n++)
{
if (m_cQueuedFileDataList[n]->sFileName == sFile)
{
UpdateFileStatus(n, sFile, ps);
m_cQueuedFileDataList[n]->ps = ps;
return;
}
}
}
BOOL CIEPatcherDlg::GetPendingFileToScan(CString & sFileToScan)
{
CSingleLock sl(&m_csQueuedFileDataList, TRUE);
for (int n = 0; n < m_cQueuedFileDataList.GetSize(); n++)
{
if (m_cQueuedFileDataList[n]->ps == psUnknownStatus)
{
sFileToScan = m_cQueuedFileDataList[n]->sFileName;
return TRUE;
}
}
return FALSE;
}
BOOL CIEPatcherDlg::GetPendingFileToPatch(CString & sFileToPatch)
{
CSingleLock sl(&m_csQueuedFileDataList, TRUE);
for (int n = 0; n < m_cQueuedFileDataList.GetSize(); n++)
{
if (m_cQueuedFileDataList[n]->ps == psPatchPending)
{
sFileToPatch = m_cQueuedFileDataList[n]->sFileName;
return TRUE;
}
}
return FALSE;
}