|
|
// --------------------------------------------------------------------------------
// Exploder.cpp
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "resource.h"
// --------------------------------------------------------------------------------
// Constants
// --------------------------------------------------------------------------------
#define CCHMAX_RES 1024
#define CCHMAX_PATH_EXPLODER 1024
// --------------------------------------------------------------------------------
// String Consts
// --------------------------------------------------------------------------------
static const char c_szRegCmd[] = "/reg"; static const char c_szUnRegCmd[] = "/unreg"; static const char c_szReg[] = "Reg"; static const char c_szUnReg[] = "UnReg"; static const char c_szAdvPackDll[] = "ADVPACK.DLL"; static const char c_szSource[] = "/SOURCE:"; static const char c_szDest[] = "/DEST:";
// --------------------------------------------------------------------------------
// Globals
// --------------------------------------------------------------------------------
HINSTANCE g_hInst=NULL; CHAR g_szTitle[CCHMAX_RES]; IMalloc *g_pMalloc=NULL;
// --------------------------------------------------------------------------------
// BODYFILEINFO
// --------------------------------------------------------------------------------
typedef struct tagBODYFILEINFO { HBODY hBody; LPSTR pszCntId; LPSTR pszCntLoc; LPSTR pszFileName; LPSTR pszFilePath; BYTE fIsHtml; IStream *pStmFile; } BODYFILEINFO, *LPBODYFILEINFO;
// --------------------------------------------------------------------------------
// Prototypes
// --------------------------------------------------------------------------------
HRESULT CallRegInstall(LPCSTR szSection); HRESULT ReplaceContentIds(LPSTREAM pStmHtml, LPBODYFILEINFO prgBody, DWORD cBodies); int WinMainT(HINSTANCE hInst, HINSTANCE hInstPrev, LPTSTR pszCmdLine, int nCmdShow); HRESULT MimeOleExplodeMhtmlFile(LPCSTR pszSrcFile, LPSTR pszDstDir, INT *pnError);
// --------------------------------------------------------------------------------
// IF_FAILEXIT_ERROR
// --------------------------------------------------------------------------------
#define IF_FAILEXIT_ERROR(_nError, hrExp) \
if (FAILED(hrExp)) { \ TraceResult(hr); \ *pnError = _nError; \ goto exit; \ } else
// --------------------------------------------------------------------------------
// ModuleEntry - Stolen from the CRT, used to shirink our code
// --------------------------------------------------------------------------------
int _stdcall ModuleEntry(void) { // Locals
int i; STARTUPINFOA si; LPTSTR pszCmdLine;
// Get Malloc
CoGetMalloc(1, &g_pMalloc);
// Get the command line
pszCmdLine = GetCommandLine();
// We don't want the "No disk in drive X:" requesters, so we set the critical error mask such that calls will just silently fail
SetErrorMode(SEM_FAILCRITICALERRORS);
// Parse the command line
if (*pszCmdLine == TEXT('\"')) { // Scan, and skip over, subsequent characters until another double-quote or a null is encountered.
while ( *++pszCmdLine && (*pszCmdLine != TEXT('\"'))) {};
// If we stopped on a double-quote (usual case), skip over it.
if (*pszCmdLine == TEXT('\"')) pszCmdLine++; } else { while (*pszCmdLine > TEXT(' ')) pszCmdLine++; }
// Skip past any white space preceeding the second token.
while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) pszCmdLine++;
// Register
if (0 == lstrcmpi(c_szRegCmd, pszCmdLine)) { CallRegInstall(c_szReg); goto exit; }
// Unregister
else if (0 == lstrcmpi(c_szUnRegCmd, pszCmdLine)) { CallRegInstall(c_szUnReg); goto exit; }
// Get startup information...
si.dwFlags = 0; GetStartupInfoA(&si);
// Call the real winmain
i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine, si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
exit: // Cleanup
SafeRelease(g_pMalloc);
// Since we now have a way for an extension to tell us when it is finished, we will terminate all processes when the main thread goes away.
ExitProcess(i);
// Done
return i; }
// --------------------------------------------------------------------------------
// WinMainT
// --------------------------------------------------------------------------------
int WinMainT(HINSTANCE hInst, HINSTANCE hInstPrev, LPTSTR pszCmdLine, int nCmdShow) { // Locals
HRESULT hr; CHAR szRes[CCHMAX_RES]; CHAR szSource[CCHMAX_PATH_EXPLODER]; CHAR szDest[CCHMAX_PATH_EXPLODER]; LPSTR pszT; DWORD i; INT nError;
// Message
LoadString(hInst, IDS_TITLE, g_szTitle, ARRAYSIZE(g_szTitle));
// Message
LoadString(hInst, IDS_HELP, szRes, ARRAYSIZE(szRes));
// If Command Line is Empty...
if (NULL == pszCmdLine || StrStrA(pszCmdLine, szRes) || *pszCmdLine == '?' || lstrcmpi("\\?", pszCmdLine) == 0) { // Message
LoadString(hInst, IDS_CMDLINE_FORMAT, szRes, ARRAYSIZE(szRes));
// Message
MessageBox(NULL, szRes, g_szTitle, MB_OK | MB_ICONINFORMATION);
// Done
goto exit; }
// Null Out Source and Dest
*szSource = '\0'; *szDest = '\0';
// If pszCmdLine specifies a specific, existing file...
if (PathFileExists(pszCmdLine)) { // Copy To source
lstrcpyn(szSource, pszCmdLine, ARRAYSIZE(szSource));
// Pick a Temporary Location to store the thicket
GetTempPath(ARRAYSIZE(szDest), szDest); }
// Otherwise, try to get a source
else { // Lets Upper Case the Command Line
CharUpper(pszCmdLine);
// Try to locate /SOURCE:
pszT = StrStrA(pszCmdLine, c_szSource);
// If we found /SOURCE, then read the contents...
if (pszT) { // Step over /SOURCE:
pszT += lstrlen(c_szSource);
// Initialize
i = 0;
// Read into szSource, until I hit a / or end of string...
while ('\0' != *pszT && '/' != *pszT && i < CCHMAX_PATH_EXPLODER) szSource[i++] = *pszT++;
// Pound in a Null
szSource[i] = '\0';
// Strip Leading and Trailing Whitespace
UlStripWhitespace(szSource, TRUE, TRUE, NULL);
// See if file exists
if (FALSE == PathFileExists(szSource)) { // Locals
CHAR szError[CCHMAX_RES + CCHMAX_PATH_EXPLODER];
// Message
LoadString(hInst, IDS_FILE_NOEXIST, szRes, ARRAYSIZE(szRes));
// Format the error message
wsprintf(szError, szRes, szSource);
// Message
INT nAnswer = MessageBox(NULL, szError, g_szTitle, MB_YESNO | MB_ICONEXCLAMATION );
// Done
if (IDNO == nAnswer) goto exit;
// Otherwise, clear szSource
*szSource = '\0'; } }
// No Source File, lets browser for one
if (FIsEmptyA(szSource)) { // Locals
OPENFILENAME ofn; CHAR rgszFilter[CCHMAX_PATH_EXPLODER]; CHAR szDir[MAX_PATH];
// Copy in the source of exploder.exe
GetModuleFileName(hInst, szDir, ARRAYSIZE(szDir));
// Initialize szDest
PathRemoveFileSpecA(szDir);
// Initialize ofn
ZeroMemory(&ofn, sizeof(OPENFILENAME));
// Initialize the STring
*szSource ='\0';
// Load the MHTML File Filter
LoadString(hInst, IDS_MHTML_FILTER, rgszFilter, ARRAYSIZE(rgszFilter));
// Fixup the String
ReplaceChars(rgszFilter, '|', '\0');
// Initialize the Open File Structure
ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.hInstance = hInst; ofn.lpstrFilter = rgszFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = szSource; ofn.nMaxFile = CCHMAX_PATH_EXPLODER; ofn.lpstrInitialDir = szDir; ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
// Get Open File Name
if (FALSE == GetOpenFileName(&ofn)) goto exit; } }
// Do we have a valid destination...
if (FALSE == PathIsDirectoryA(szDest)) { // Try to locate /DEST:
pszT = StrStrA(pszCmdLine, c_szDest);
// If we found /DEST, then read the contents...
if (pszT) { // Step over /DEST:
pszT += lstrlen(c_szDest);
// Initialize
i = 0;
// Read into szSource, until I hit a / or end of string...
while ('\0' != *pszT && '/' != *pszT && i < CCHMAX_PATH_EXPLODER) szDest[i++] = *pszT++;
// Pound in a Null
szDest[i] = '\0';
// Strip Leading and Trailing Whitespace
UlStripWhitespace(szDest, TRUE, TRUE, NULL);
// See if file exists
if (FALSE == PathIsDirectoryA(szDest)) { // Locals
CHAR szError[CCHMAX_RES + CCHMAX_PATH_EXPLODER];
// Message
LoadString(hInst, IDS_DIRECTORY_NOEXIST, szRes, ARRAYSIZE(szRes));
// Format the error message
wsprintf(szError, szRes, szDest);
// Message
INT nAnswer = MessageBox(NULL, szError, g_szTitle, MB_YESNO | MB_ICONEXCLAMATION );
// Done
if (IDNO == nAnswer) goto exit;
// Try to create the directory
if (FALSE == CreateDirectory(szDest, NULL)) { // Message
LoadString(hInst, IDS_NOCREATE_DIRECTORY, szRes, ARRAYSIZE(szRes));
// Format the error message
wsprintf(szError, szRes, szDest);
// Message
INT nAnswer = MessageBox(NULL, szError, g_szTitle, MB_YESNO | MB_ICONEXCLAMATION );
// Done
if (IDNO == nAnswer) goto exit;
// Clear *szDest
*szDest = '\0'; } } }
// No Source File, lets browser for one
if (FIsEmptyA(szDest)) { // Copy in the source of exploder.exe
GetModuleFileName(hInst, szDest, ARRAYSIZE(szDest));
// Initialize szDest
PathRemoveFileSpecA(szDest);
// Failure
if (FALSE == BrowseForFolder(hInst, NULL, szDest, ARRAYSIZE(szDest), IDS_BROWSE_DEST, TRUE)) goto exit;
// Better be a directory
Assert(PathIsDirectoryA(szDest)); } }
// Validate the dest and source
Assert(PathIsDirectoryA(szDest) && PathFileExists(szSource));
// Explode the file
nError = 0; hr = MimeOleExplodeMhtmlFile(szSource, szDest, &nError);
// Failure ?
if (FAILED(hr) || 0 != nError) { // Locals
CHAR szError[CCHMAX_RES + CCHMAX_PATH_EXPLODER];
// Message
LoadString(hInst, nError, szRes, ARRAYSIZE(szRes));
// Need to format with file name ?
if (IDS_OPEN_FILE == nError || IDS_LOAD_FAILURE == nError || IDS_NO_HTML == nError) { // Format the error message
wsprintf(szError, szRes, szSource, hr); }
// Otherwise,
else { // Format the error message
wsprintf(szError, szRes, hr); }
// Message
MessageBox(NULL, szError, g_szTitle, MB_OK | MB_ICONEXCLAMATION); }
exit: // Done
return(1); }
// --------------------------------------------------------------------------------
// MimeOleExplodeMhtmlFile
// --------------------------------------------------------------------------------
HRESULT MimeOleExplodeMhtmlFile(LPCSTR pszSrcFile, LPSTR pszDstDir, INT *pnError) { // Locals
HRESULT hr=S_OK; IStream *pStmFile=NULL; IMimeMessage *pMessage=NULL; HBODY hRootHtml=NULL; DWORD cMaxBodies; DWORD cBodies=0; FINDBODY FindBody={0}; DWORD cchDstDir; DWORD iRootBody=0xffffffff; HBODY hBody; PROPVARIANT Variant; SHELLEXECUTEINFO ExecuteInfo; LPBODYFILEINFO prgBody=NULL; LPBODYFILEINFO pInfo; DWORD i; IMimeBody *pBody=NULL;
// Trace
TraceCall("MimeOleExplodeMhtmlFile");
// Invalid Args
if (FALSE == PathIsDirectoryA(pszDstDir) || FALSE == PathFileExists(pszSrcFile) || NULL == pnError) return TraceResult(E_INVALIDARG);
// Initialize
*pnError = 0;
// Get DstDir Length
cchDstDir = lstrlen(pszDstDir);
// Remove last \\ from pszDstDir
if (cchDstDir && pszDstDir[cchDstDir - 1] == '\\') { pszDstDir[cchDstDir - 1] = '\0'; cchDstDir--; }
// Create a Mime Message
IF_FAILEXIT_ERROR(IDS_MEMORY, hr = MimeOleCreateMessage(NULL, &pMessage));
// Initialize the message
IF_FAILEXIT_ERROR(IDS_GENERAL_ERROR, hr = pMessage->InitNew());
// Create a stream on the file
IF_FAILEXIT_ERROR(IDS_OPEN_FILE, hr = OpenFileStream((LPSTR)pszSrcFile, OPEN_EXISTING, GENERIC_READ, &pStmFile));
// Load the Message
IF_FAILEXIT_ERROR(IDS_LOAD_FAILURE, hr = pMessage->Load(pStmFile));
// Invalid Message
if (MIME_S_INVALID_MESSAGE == hr) { *pnError = IDS_LOAD_FAILURE; goto exit; }
// Count the Bodies
IF_FAILEXIT(hr = pMessage->CountBodies(NULL, TRUE, &cMaxBodies));
// Allocate
IF_FAILEXIT_ERROR(IDS_MEMORY, hr = HrAlloc((LPVOID *)&prgBody, sizeof(BODYFILEINFO) * cMaxBodies));
// Zero
ZeroMemory(prgBody, sizeof(BODYFILEINFO) * cMaxBodies);
// Get the root body...
IF_FAILEXIT_ERROR(IDS_NO_HTML, hr = pMessage->GetTextBody(TXT_HTML, IET_DECODED, NULL, &hRootHtml));
// Loop through all the bodies
hr = pMessage->FindFirst(&FindBody, &hBody);
// Loop
while(SUCCEEDED(hr)) { // Must have an hBody
Assert(hBody);
// Skip Multipart Bodies
if (S_FALSE == pMessage->IsContentType(hBody, STR_CNT_MULTIPART, NULL)) { // Is this the root ?
if (hBody == hRootHtml) iRootBody = cBodies;
// Readability
pInfo = &prgBody[cBodies];
// Better not over run prgBody
pInfo->hBody = hBody;
// Init the Variant
Variant.vt = VT_LPSTR;
// Get the Content Id
if (SUCCEEDED(pMessage->GetBodyProp(hBody, PIDTOSTR(PID_HDR_CNTID), 0, &Variant))) pInfo->pszCntId = Variant.pszVal;
// Get the Content Location
if (SUCCEEDED(pMessage->GetBodyProp(hBody, PIDTOSTR(PID_HDR_CNTLOC), 0, &Variant))) pInfo->pszCntLoc = Variant.pszVal;
// Generate a filename
if (SUCCEEDED(pMessage->GetBodyProp(hBody, PIDTOSTR(PID_ATT_GENFNAME), 0, &Variant))) pInfo->pszFileName = Variant.pszVal;
// If its html, lets make sure that the filename has a .html file extension
pInfo->fIsHtml = (S_OK == pMessage->IsContentType(hBody, STR_CNT_TEXT, STR_SUB_HTML)) ? TRUE : FALSE;
// Take the filename and build the file path
Assert(pInfo->pszFileName);
// Don't Crash
if (NULL == pInfo->pszFileName) { hr = TraceResult(E_UNEXPECTED); goto exit; }
// Validate Extension
if (pInfo->fIsHtml) { // Get Extension
LPSTR pszExt = PathFindExtensionA(pInfo->pszFileName);
// If Null or not .html..
if (NULL == pszExt || lstrcmpi(pszExt, ".html") != 0) { // Re-allocate pInfo->pszFileName...
IF_FAILEXIT_ERROR(IDS_MEMORY, hr = HrRealloc((LPVOID *)&pInfo->pszFileName, lstrlen(pInfo->pszFileName) + 10));
// Rename the Extension
PathRenameExtensionA(pInfo->pszFileName, ".html"); } }
// Build that full file path
IF_FAILEXIT_ERROR(IDS_MEMORY, hr = HrAlloc((LPVOID *)&pInfo->pszFilePath, lstrlen(pszDstDir) + lstrlen(pInfo->pszFileName) + 10));
// Formath the filepath
wsprintf(pInfo->pszFilePath, "%s\\%s", pszDstDir, pInfo->pszFileName);
// Save the body to the file
IF_FAILEXIT(hr = pMessage->BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody));
// Save to the file
IF_FAILEXIT(hr = pBody->SaveToFile(IET_DECODED, pInfo->pszFilePath));
// Open a file stream
if (pInfo->fIsHtml) { // Open it if its html
IF_FAILEXIT(hr = OpenFileStream(pInfo->pszFilePath, OPEN_ALWAYS, GENERIC_READ | GENERIC_WRITE, &pInfo->pStmFile)); }
// Increment cBodies
cBodies++; }
// Loop through all the bodies
hr = pMessage->FindNext(&FindBody, &hBody); }
// Reset hr
hr = S_OK;
// Root Body was not found
Assert(iRootBody != 0xffffffff);
// Bad News
if (0xffffffff == iRootBody) { hr = TraceResult(E_UNEXPECTED); goto exit; }
// Walk through and fixup all HTML files and close all streams
for (i=0; i<cBodies; i++) { // Readability
pInfo = &prgBody[i];
// If HTML...
if (pInfo->fIsHtml) { // Better have an open stream
Assert(pInfo->pStmFile);
// Failure
if (NULL == pInfo->pStmFile) { hr = TraceResult(E_UNEXPECTED); goto exit; }
// Replace all the CID references with file references...
ReplaceContentIds(pInfo->pStmFile, prgBody, cBodies); }
// Release this stream
SafeRelease(pInfo->pStmFile); }
// Launch the Currently Registered HTML Editor ontop of iRootBody pszFilePath
ZeroMemory(&ExecuteInfo, sizeof(SHELLEXECUTEINFO)); ExecuteInfo.fMask = SEE_MASK_NOCLOSEPROCESS; ExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO); ExecuteInfo.lpVerb = "Edit"; ExecuteInfo.lpFile = prgBody[iRootBody].pszFilePath; ExecuteInfo.lpParameters = NULL; ExecuteInfo.lpDirectory = pszDstDir; ExecuteInfo.nShow = SW_SHOWNORMAL;
// Compress szBlobFile
ShellExecuteEx(&ExecuteInfo);
exit: // General Error
if (FAILED(hr) && 0 == *pnError) *pnError = IDS_GENERAL_ERROR;
// Free prgBody
if (prgBody) { // Loop
for (i=0; i<cBodies; i++) { SafeMemFree(prgBody[i].pszCntId); SafeMemFree(prgBody[i].pszCntLoc); SafeMemFree(prgBody[i].pszFileName); SafeMemFree(prgBody[i].pszFilePath); SafeRelease(prgBody[i].pStmFile); }
// Free the Array
CoTaskMemFree(prgBody); }
// Cleanup
SafeRelease(pStmFile); SafeRelease(pBody); SafeRelease(pMessage);
// Done
return hr; }
// --------------------------------------------------------------------------------
// ReplaceContentIds
// --------------------------------------------------------------------------------
HRESULT ReplaceContentIds(LPSTREAM pStmHtml, LPBODYFILEINFO prgBody, DWORD cBodies) { // Locals
HRESULT hr=S_OK; DWORD cb; LPSTR pszFound; LPSTR pszT; LPSTR pszHtml=NULL; LPSTR pszCntId=NULL; DWORD i; DWORD cchCntId; ULARGE_INTEGER uliSize;
// Trac
TraceCall("ReplaceContentIds");
// Invalid Args
Assert(pStmHtml && prgBody && cBodies);
// Loop through the bodies
for (i=0; i<cBodies; i++) { // No Content-ID here...
if (NULL == prgBody[i].pszCntId) continue;
// Better have a filename
Assert(prgBody[i].pszFileName);
// Load the stream into memory...
IF_FAILEXIT(hr = HrGetStreamSize(pStmHtml, &cb));
// Allocate Memory
IF_FAILEXIT(hr = HrAlloc((LPVOID *)&pszHtml, cb + 1));
// Rewind
IF_FAILEXIT(hr = HrRewindStream(pStmHtml));
// Read the Stream
IF_FAILEXIT(hr = pStmHtml->Read(pszHtml, cb, NULL));
// Stuff Null terminator
pszHtml[cb] = '\0';
// Kill pStmHtml
uliSize.QuadPart = 0; IF_FAILEXIT(hr = pStmHtml->SetSize(uliSize));
// Allocate Memory
IF_FAILEXIT(hr = HrAlloc((LPVOID *)&pszCntId, lstrlen(prgBody[i].pszCntId) + lstrlen("cid:") + 5));
// Format
pszT = prgBody[i].pszCntId; if (*pszT == '<') pszT++; wsprintf(pszCntId, "cid:%s", pszT);
// Remove trailing >
cchCntId = lstrlen(pszCntId); if (pszCntId[cchCntId - 1] == '>') pszCntId[cchCntId - 1] = '\0';
// Set pszT
pszT = pszHtml;
// Begin replace loop
while(1) { // Find pszCntId
pszFound = StrStrA(pszT, pszCntId);
// Done
if (NULL == pszFound) { // Write from pszT to pszFound
IF_FAILEXIT(hr = pStmHtml->Write(pszT, (pszHtml + cb) - pszT, NULL));
// Done
break; }
// Write from pszT to pszFound
IF_FAILEXIT(hr = pStmHtml->Write(pszT, pszFound - pszT, NULL));
// Write
IF_FAILEXIT(hr = pStmHtml->Write(prgBody[i].pszFileName, lstrlen(prgBody[i].pszFileName), NULL));
// Set pszT
pszT = pszFound + lstrlen(pszCntId); }
// Commit
IF_FAILEXIT(hr = pStmHtml->Commit(STGC_DEFAULT));
// Cleanup
SafeMemFree(pszHtml); SafeMemFree(pszCntId); }
exit: // Cleanup
SafeMemFree(pszHtml); SafeMemFree(pszCntId);
// Done
return hr; }
// --------------------------------------------------------------------------------
// CallRegInstall
// --------------------------------------------------------------------------------
HRESULT CallRegInstall(LPCSTR szSection) { int cch; HRESULT hr; HINSTANCE hAdvPack, hinst; REGINSTALL pfnri; char szExploderDll[CCHMAX_PATH_EXPLODER], szDir[CCHMAX_PATH_EXPLODER]; STRENTRY seReg[2]; STRTABLE stReg; char c_szExploder[] = "EXPLODER"; char c_szExploderDir[] = "EXPLODER_DIR";
hr = E_FAIL;
hinst = GetModuleHandle(NULL);
hAdvPack = LoadLibraryA(c_szAdvPackDll); if (hAdvPack != NULL) { // Get Proc Address for registration util
pfnri = (REGINSTALL)GetProcAddress(hAdvPack, achREGINSTALL); if (pfnri != NULL) { stReg.cEntries = 0; stReg.pse = seReg;
GetModuleFileName(hinst, szExploderDll, ARRAYSIZE(szExploderDll)); seReg[stReg.cEntries].pszName = c_szExploder; seReg[stReg.cEntries].pszValue = szExploderDll; stReg.cEntries++;
lstrcpy(szDir, szExploderDll); cch = lstrlen(szDir); for ( ; cch > 0; cch--) { if (szDir[cch] == '\\') { szDir[cch] = 0; break; } } seReg[stReg.cEntries].pszName = c_szExploderDir; seReg[stReg.cEntries].pszValue = szDir; stReg.cEntries++;
// Call the self-reg routine
hr = pfnri(hinst, szSection, &stReg); }
FreeLibrary(hAdvPack); }
return(hr); }
|