diff --git a/UI/file/exporter.cpp b/UI/file/exporter.cpp index 65e20ecff..288360b1b 100644 --- a/UI/file/exporter.cpp +++ b/UI/file/exporter.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,15 +24,15 @@ namespace file dialog::editor::CEditor MyData( pParentWnd, IDS_SAVEMESSAGETOFILE, IDS_SAVEMESSAGETOFILEPROMPT, CEDITOR_BUTTON_OK | CEDITOR_BUTTON_CANCEL); - UINT uidDropDown[] = { - IDS_DDTEXTFILE, - IDS_DDMSGFILEANSI, - IDS_DDMSGFILEUNICODE, - IDS_DDEMLFILE, - IDS_DDEMLFILEUSINGICONVERTERSESSION, - IDS_DDTNEFFILE}; - MyData.AddPane( - viewpane::DropDownPane::Create(0, IDS_FORMATTOSAVEMESSAGE, _countof(uidDropDown), uidDropDown, true)); + std::vector uidDropDown{IDS_DDTEXTFILE, IDS_DDMSGFILEANSI, IDS_DDMSGFILEUNICODE}; + if (registry::enableLegacyFeatures) + { + uidDropDown.push_back(IDS_DDEMLFILEUSINGICONVERTERSESSION); + } + uidDropDown.push_back(IDS_DDTNEFFILE); + + MyData.AddPane(viewpane::DropDownPane::Create( + 0, IDS_FORMATTOSAVEMESSAGE, static_cast(uidDropDown.size()), uidDropDown.data(), true)); if (bMultiSelect) { MyData.AddPane(viewpane::CheckPane::Create(1, IDS_EXPORTPROMPTLOCATION, false, false)); @@ -39,7 +40,25 @@ namespace file if (!MyData.DisplayDialog()) return false; - exportType = static_cast(MyData.GetDropDown(0)); + switch (MyData.GetDropDownValue(0)) + { + case IDS_DDTEXTFILE: + exportType = exportType::text; + break; + case IDS_DDMSGFILEANSI: + exportType = exportType::msgAnsi; + break; + case IDS_DDMSGFILEUNICODE: + exportType = exportType::msgUnicode; + break; + case IDS_DDEMLFILEUSINGICONVERTERSESSION: + exportType = exportType::emlIConverter; + break; + case IDS_DDTNEFFILE: + exportType = exportType::tnef; + break; + } + bPrompt = !bMultiSelect || MyData.GetCheck(1); switch (exportType) @@ -55,7 +74,6 @@ namespace file szDotExt = L".msg"; szFilter = strings::loadstring(IDS_MSGFILES); break; - case exportType::eml: case exportType::emlIConverter: szExt = L"eml"; szDotExt = L".eml"; @@ -110,9 +128,6 @@ namespace file case exportType::msgUnicode: return EC_H(file::SaveToMSG(lpMessage, filename, true, pParentWnd->GetSafeHwnd(), true)); break; - case exportType::eml: - return EC_H(file::SaveToEML(lpMessage, filename)); - break; case exportType::emlIConverter: { auto ulConvertFlags = CCSF_SMTP; diff --git a/UI/file/exporter.h b/UI/file/exporter.h index f02d16b42..2bc0c155e 100644 --- a/UI/file/exporter.h +++ b/UI/file/exporter.h @@ -7,9 +7,8 @@ namespace file text = 0, msgAnsi = 1, msgUnicode = 2, - eml = 3, - emlIConverter = 4, - tnef = 5 + emlIConverter = 3, + tnef = 4 }; class exporter diff --git a/core/mapi/mapiFile.cpp b/core/mapi/mapiFile.cpp index 4a20a0a14..a14b827f6 100644 --- a/core/mapi/mapiFile.cpp +++ b/core/mapi/mapiFile.cpp @@ -326,41 +326,6 @@ namespace file return hRes; } - _Check_return_ HRESULT SaveToEML(_In_ LPMESSAGE lpMessage, _In_ const std::wstring& szFileName) - { - LPSTREAM pStrmSrc = nullptr; - - if (!lpMessage || szFileName.empty()) return MAPI_E_INVALID_PARAMETER; - output::DebugPrint(output::dbgLevel::Generic, L"SaveToEML: Saving message to \"%ws\"\n", szFileName.c_str()); - - // Open the property of the attachment - // containing the file data - auto hRes = EC_MAPI(lpMessage->OpenProperty( - PR_INTERNET_CONTENT, // TODO: There's a modern property for this... - const_cast(&IID_IStream), - 0, - NULL, // MAPI_MODIFY is not needed - reinterpret_cast(&pStrmSrc))); - if (FAILED(hRes)) - { - if (hRes == MAPI_E_NOT_FOUND) - { - output::DebugPrint(output::dbgLevel::Generic, L"No internet content found\n"); - } - } - else - { - if (pStrmSrc) - { - hRes = WC_H(WriteStreamToFile(pStrmSrc, szFileName)); - - pStrmSrc->Release(); - } - } - - return hRes; - } - _Check_return_ HRESULT STDAPICALLTYPE MyStgCreateStorageEx( _In_ const std::wstring& pName, DWORD grfMode, diff --git a/core/mapi/mapiFile.h b/core/mapi/mapiFile.h index 2ab593617..14b019cef 100644 --- a/core/mapi/mapiFile.h +++ b/core/mapi/mapiFile.h @@ -15,7 +15,6 @@ namespace file bool bAssoc, bool bUnicode, HWND hWnd); - _Check_return_ HRESULT SaveToEML(_In_ LPMESSAGE lpMessage, _In_ const std::wstring& szFileName); _Check_return_ HRESULT CreateNewMSG( _In_ const std::wstring& szFileName, bool bUnicode, diff --git a/core/res/MFCMapi.rc2 b/core/res/MFCMapi.rc2 index 6858b92b3..185cbe53d 100644 --- a/core/res/MFCMapi.rc2 +++ b/core/res/MFCMapi.rc2 @@ -995,6 +995,7 @@ IDS_REGKEY_FORCEOUTLOOKMAPI "Force the use of Outlook's MAPI files" IDS_REGKEY_FORCESYSTEMMAPI "Force the use of the system's MAPI files" IDS_REGKEY_PREFER_OLMAPI32 "Prefer olmapi32.dll over other MAPI files" IDS_REGKEY_UIDIAG "Show UI diagnostics" +IDS_REGKEY_ENABLE_LEGACY_FEATURES "Enable legacy features" // RestrictEditor.cpp IDS_RELOP "relop" @@ -1200,10 +1201,9 @@ IDS_DDDISPLAYPROPSONLY "Only display properties from MSG file" IDS_DDENTERFORMCLASS "Manually enter form class" IDS_DDFOLDERFORMLIBRARY "Folder Form library" IDS_DDORGFORMLIBRARY "Organization Forms and Application Forms libraries" -IDS_DDTEXTFILE "Text file (saves all properties of message to a text file)" +IDS_DDTEXTFILE "XML file (saves all properties of message to an XML file)" IDS_DDMSGFILEANSI "MSG file (ANSI)" IDS_DDMSGFILEUNICODE "MSG file (UNICODE)" -IDS_DDEMLFILE "EML file (using PR_INTERNET_CONTENT)" IDS_DDEMLFILEUSINGICONVERTERSESSION "EML file (using IConverterSession)" IDS_DDTNEFFILE "TNEF file (same format as winmail.dat)" IDS_DDCOPYPROPS "IMAPIProp::CopyProps" diff --git a/core/res/Resource.h b/core/res/Resource.h index d6007cc2c..6a2c79487 100644 --- a/core/res/Resource.h +++ b/core/res/Resource.h @@ -807,7 +807,6 @@ #define IDS_DDORGFORMLIBRARY 35246 #define IDS_DDTEXTFILE 35247 #define IDS_DDMSGFILEANSI 35248 -#define IDS_DDEMLFILE 35249 #define IDS_DDTNEFFILE 35250 #define IDS_DDCOPYPROPS 35251 #define IDS_DDGETSETPROPS 35252 @@ -1235,3 +1234,4 @@ #define IDS_CAPABILITIES_FOLDER 35872 #define IDS_CAPABILITIES_RESTRICTION 35873 #define IDS_REGKEY_PREFER_OLMAPI32 35874 +#define IDS_REGKEY_ENABLE_LEGACY_FEATURES 35875 diff --git a/core/utility/output.cpp b/core/utility/output.cpp index 28563e97c..93c184705 100644 --- a/core/utility/output.cpp +++ b/core/utility/output.cpp @@ -15,7 +15,10 @@ namespace output { - std::wstring g_szXMLHeader = L"\n"; + // Files are opened with the CRT "ccs=UNICODE" mode, which writes a UTF-16 LE BOM + // and encodes each character as two little-endian bytes. The declared encoding + // must match, otherwise XML parsers reject the file with an encoding mismatch. + std::wstring g_szXMLHeader = L"\n"; std::function outputToDbgView; FILE* g_fDebugFile = nullptr; diff --git a/core/utility/registry.cpp b/core/utility/registry.cpp index 09f268c1f..0aefa4dc2 100644 --- a/core/utility/registry.cpp +++ b/core/utility/registry.cpp @@ -67,6 +67,7 @@ namespace registry boolRegKey preferOlmapi32{L"PreferOlmapi32", true, false, IDS_REGKEY_PREFER_OLMAPI32}; boolRegKey uiDiag{L"UIDiag", false, false, IDS_REGKEY_UIDIAG}; boolRegKey displayAboutDialog{L"DisplayAboutDialog", true, false, NULL}; + boolRegKey enableLegacyFeatures{L"EnableLegacyFeatures", false, false, IDS_REGKEY_ENABLE_LEGACY_FEATURES}; wstringRegKey propertyColumnOrder{L"PropertyColumnOrder", L"", false, NULL}; dwordRegKey namedPropBatchSize{L"NamedPropBatchSize", regOptionType::stringDec, 400, false, NULL}; @@ -101,6 +102,7 @@ namespace registry &preferOlmapi32, &uiDiag, &displayAboutDialog, + &enableLegacyFeatures, &propertyColumnOrder, &namedPropBatchSize}; diff --git a/core/utility/registry.h b/core/utility/registry.h index 338ffe57c..14fcd11fd 100644 --- a/core/utility/registry.h +++ b/core/utility/registry.h @@ -198,6 +198,7 @@ namespace registry extern boolRegKey preferOlmapi32; extern boolRegKey uiDiag; extern boolRegKey displayAboutDialog; + extern boolRegKey enableLegacyFeatures; extern wstringRegKey propertyColumnOrder; extern dwordRegKey namedPropBatchSize; } // namespace registry \ No newline at end of file