// -------------------------------------------------------------------------------- // 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; ifIsHtml) { // 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; iRead(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); }