summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp')
-rw-r--r--comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp934
1 files changed, 934 insertions, 0 deletions
diff --git a/comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp b/comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp
new file mode 100644
index 0000000000..eb8c9b9fe8
--- /dev/null
+++ b/comm/mailnews/mapi/mapihook/src/msgMapiHook.cpp
@@ -0,0 +1,934 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define MAPI_STARTUP_ARG "/MAPIStartUp"
+
+#include <mapidefs.h>
+#include <mapi.h>
+#include <direct.h>
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsIPromptService.h"
+#include "nsIAppShellService.h"
+#include "mozIDOMWindow.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIStringBundle.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIMsgAttachment.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgComposeParams.h"
+#include "nsIMsgCompose.h"
+#include "nsIMsgSend.h"
+#include "nsIMsgComposeService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "msgMapi.h"
+#include "msgMapiHook.h"
+#include "msgMapiSupport.h"
+#include "msgMapiMain.h"
+#include "nsThreadUtils.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Components.h"
+#include "nsEmbedCID.h"
+#include "mozilla/Logging.h"
+#include "mozilla/SpinEventLoopUntil.h"
+
+using namespace mozilla::dom;
+
+extern mozilla::LazyLogModule MAPI; // defined in msgMapiImp.cpp
+
+class MAPISendListener : public nsIMsgSendListener,
+ public mozilla::ReentrantMonitor {
+ public:
+ MAPISendListener()
+ : ReentrantMonitor("MAPISendListener monitor"), m_done(false) {}
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* void OnStartSending (in string aMsgID, in uint32_t aMsgSize); */
+ NS_IMETHOD OnStartSending(const char* aMsgID, uint32_t aMsgSize) {
+ return NS_OK;
+ }
+
+ /* void OnProgress (in string aMsgID, in uint32_t aProgress, in uint32_t
+ * aProgressMax); */
+ NS_IMETHOD OnProgress(const char* aMsgID, uint32_t aProgress,
+ uint32_t aProgressMax) {
+ return NS_OK;
+ }
+
+ /* void OnStatus (in string aMsgID, in wstring aMsg); */
+ NS_IMETHOD OnStatus(const char* aMsgID, const char16_t* aMsg) {
+ return NS_OK;
+ }
+
+ /* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg,
+ * in nsIFile returnFile); */
+ NS_IMETHOD OnStopSending(const char* aMsgID, nsresult aStatus,
+ const char16_t* aMsg, nsIFile* returnFile) {
+ mozilla::ReentrantMonitorAutoEnter mon(*this);
+ m_done = true;
+ NotifyAll();
+ return NS_OK;
+ }
+
+ /* void OnTransportSecurityError( in string msgID, in nsresult status, in
+ * nsITransportSecurityInfo secInfo, in ACString location); */
+ NS_IMETHOD OnTransportSecurityError(const char* msgID, nsresult status,
+ nsITransportSecurityInfo* secInfo,
+ nsACString const& location) {
+ return NS_OK;
+ }
+
+ /* void OnSendNotPerformed */
+ NS_IMETHOD OnSendNotPerformed(const char* aMsgID, nsresult aStatus) {
+ return OnStopSending(aMsgID, aStatus, nullptr, nullptr);
+ }
+
+ /* void OnGetDraftFolderURI (); */
+ NS_IMETHOD OnGetDraftFolderURI(const char* aMsgID,
+ const nsACString& aFolderURI) {
+ return NS_OK;
+ }
+
+ bool IsDone() { return m_done; }
+
+ private:
+ bool m_done;
+ virtual ~MAPISendListener() {}
+};
+
+/// Helper for setting up the hidden window for blind MAPI.
+class MOZ_STACK_CLASS AutoHiddenWindow {
+ public:
+ explicit AutoHiddenWindow(nsresult& rv)
+ : mAppService(do_GetService("@mozilla.org/appshell/appShellService;1")) {
+ mCreatedHiddenWindow = false;
+ rv = mAppService->GetHiddenDOMWindow(getter_AddRefs(mHiddenWindow));
+ if (rv == NS_ERROR_FAILURE) {
+ // Try to get a hidden window. If it doesn't exist, create a hidden
+ // window for us to use.
+ rv = mAppService->CreateHiddenWindow();
+ NS_ENSURE_SUCCESS_VOID(rv);
+ mCreatedHiddenWindow = true;
+ rv = mAppService->GetHiddenDOMWindow(getter_AddRefs(mHiddenWindow));
+ }
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+ ~AutoHiddenWindow() {
+ if (mCreatedHiddenWindow) mAppService->DestroyHiddenWindow();
+ }
+ mozIDOMWindowProxy* operator->() { return mHiddenWindow; }
+ operator mozIDOMWindowProxy*() { return mHiddenWindow; }
+
+ private:
+ nsCOMPtr<nsIAppShellService> mAppService;
+ nsCOMPtr<mozIDOMWindowProxy> mHiddenWindow;
+ bool mCreatedHiddenWindow;
+};
+
+NS_IMPL_ISUPPORTS(MAPISendListener, nsIMsgSendListener)
+
+bool nsMapiHook::isMapiService = false;
+
+void nsMapiHook::CleanUp() {
+ // This routine will be fully implemented in future
+ // to cleanup mapi related stuff inside mozilla code.
+}
+
+bool nsMapiHook::DisplayLoginDialog(bool aLogin, char16_t** aUsername,
+ char16_t** aPassword) {
+ nsresult rv;
+ bool btnResult = false;
+
+ nsCOMPtr<nsIPromptService> dlgService(
+ do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && dlgService) {
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ if (!bundleService) return false;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(MAPI_PROPERTIES_CHROME,
+ getter_AddRefs(bundle));
+ if (NS_FAILED(rv) || !bundle) return false;
+
+ nsCOMPtr<nsIStringBundle> brandBundle;
+ rv =
+ bundleService->CreateBundle("chrome://branding/locale/brand.properties",
+ getter_AddRefs(brandBundle));
+ if (NS_FAILED(rv)) return false;
+
+ nsString brandName;
+ rv = brandBundle->GetStringFromName("brandFullName", brandName);
+ if (NS_FAILED(rv)) return false;
+
+ nsString loginTitle;
+ AutoTArray<nsString, 1> brandStrings = {brandName};
+ rv = bundle->FormatStringFromName("loginTitle", brandStrings, loginTitle);
+ if (NS_FAILED(rv)) return false;
+
+ if (aLogin) {
+ nsString loginText;
+ rv = bundle->GetStringFromName("loginTextwithName", loginText);
+ if (NS_FAILED(rv) || loginText.IsEmpty()) return false;
+
+ rv = dlgService->PromptUsernameAndPassword(nullptr, loginTitle.get(),
+ loginText.get(), aUsername,
+ aPassword, &btnResult);
+ } else {
+ // nsString loginString;
+ nsString loginText;
+ AutoTArray<nsString, 1> userNameStrings = {nsDependentString(*aUsername)};
+ rv =
+ bundle->FormatStringFromName("loginText", userNameStrings, loginText);
+ if (NS_FAILED(rv)) return false;
+
+ rv = dlgService->PromptPassword(nullptr, loginTitle.get(),
+ loginText.get(), aPassword, &btnResult);
+ }
+ }
+
+ return btnResult;
+}
+
+bool nsMapiHook::VerifyUserName(const nsCString& aUsername, nsCString& aIdKey) {
+ nsresult rv;
+
+ if (aUsername.IsEmpty()) return false;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager(
+ do_GetService("@mozilla.org/messenger/account-manager;1", &rv));
+ if (NS_FAILED(rv)) return false;
+ nsTArray<RefPtr<nsIMsgIdentity>> identities;
+ rv = accountManager->GetAllIdentities(identities);
+ if (NS_FAILED(rv)) return false;
+
+ for (auto thisIdentity : identities) {
+ if (thisIdentity) {
+ nsCString email;
+ rv = thisIdentity->GetEmail(email);
+ if (NS_FAILED(rv)) continue;
+
+ // get the username from the email and compare with the username
+ int32_t index = email.FindChar('@');
+ if (index != -1) email.SetLength(index);
+
+ if (aUsername.Equals(email))
+ return NS_SUCCEEDED(thisIdentity->GetKey(aIdKey));
+ }
+ }
+
+ return false;
+}
+
+bool nsMapiHook::IsBlindSendAllowed() {
+ bool enabled = false;
+ bool warn = true;
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefBranch) {
+ prefBranch->GetBoolPref(PREF_MAPI_WARN_PRIOR_TO_BLIND_SEND, &warn);
+ prefBranch->GetBoolPref(PREF_MAPI_BLIND_SEND_ENABLED, &enabled);
+ }
+ if (!enabled) return false;
+
+ if (!warn) return true; // Everything is okay.
+
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ if (!bundleService) return false;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(MAPI_PROPERTIES_CHROME,
+ getter_AddRefs(bundle));
+ if (NS_FAILED(rv) || !bundle) return false;
+
+ nsString warningMsg;
+ rv = bundle->GetStringFromName("mapiBlindSendWarning", warningMsg);
+ if (NS_FAILED(rv)) return false;
+
+ nsString dontShowAgainMessage;
+ rv = bundle->GetStringFromName("mapiBlindSendDontShowAgain",
+ dontShowAgainMessage);
+ if (NS_FAILED(rv)) return false;
+
+ nsCOMPtr<nsIPromptService> dlgService(
+ do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv) || !dlgService) return false;
+
+ bool continueToWarn = true;
+ bool okayToContinue = false;
+ dlgService->ConfirmCheck(nullptr, nullptr, warningMsg.get(),
+ dontShowAgainMessage.get(), &continueToWarn,
+ &okayToContinue);
+
+ if (!continueToWarn && okayToContinue && prefBranch)
+ prefBranch->SetBoolPref(PREF_MAPI_WARN_PRIOR_TO_BLIND_SEND, false);
+
+ return okayToContinue;
+}
+
+// this is used for Send without UI
+nsresult nsMapiHook::BlindSendMail(unsigned long aSession,
+ nsIMsgCompFields* aCompFields) {
+ nsresult rv = NS_OK;
+
+ if (!IsBlindSendAllowed()) return NS_ERROR_FAILURE;
+
+ // Get a hidden window to use for compose.
+ AutoHiddenWindow hiddenWindow(rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // smtp password and Logged in used IdKey from MapiConfig (session obj)
+ nsMAPIConfiguration* pMapiConfig =
+ nsMAPIConfiguration::GetMAPIConfiguration();
+ if (!pMapiConfig) return NS_ERROR_FAILURE; // get the singleton obj
+ char16_t* password = pMapiConfig->GetPassword(aSession);
+
+ // Id key
+ nsCString MsgIdKey;
+ pMapiConfig->GetIdKey(aSession, MsgIdKey);
+
+ // get the MsgIdentity for the above key using AccountManager
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService("@mozilla.org/messenger/account-manager;1");
+ if (NS_FAILED(rv) || (!accountManager)) return rv;
+
+ nsCOMPtr<nsIMsgIdentity> pMsgId;
+ rv = accountManager->GetIdentity(MsgIdKey, getter_AddRefs(pMsgId));
+ if (NS_FAILED(rv)) return rv;
+
+ // create a send listener to get back the send status
+ RefPtr<MAPISendListener> sendListener = new MAPISendListener;
+
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams(
+ do_CreateInstance("@mozilla.org/messengercompose/composeparams;1", &rv));
+ if (NS_FAILED(rv) || (!pMsgComposeParams)) return rv;
+
+ // populate the compose params
+ bool forcePlainText;
+ aCompFields->GetForcePlainText(&forcePlainText);
+ pMsgComposeParams->SetType(nsIMsgCompType::New);
+ pMsgComposeParams->SetFormat(forcePlainText ? nsIMsgCompFormat::PlainText
+ : nsIMsgCompFormat::HTML);
+ pMsgComposeParams->SetIdentity(pMsgId);
+ pMsgComposeParams->SetComposeFields(aCompFields);
+ pMsgComposeParams->SetSendListener(sendListener);
+ if (password) pMsgComposeParams->SetSmtpPassword(nsDependentString(password));
+
+ // create the nsIMsgCompose object to send the object
+ nsCOMPtr<nsIMsgCompose> pMsgCompose(
+ do_CreateInstance("@mozilla.org/messengercompose/compose;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = pMsgCompose->Initialize(pMsgComposeParams, hiddenWindow, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we're in offline mode, we'll need to queue it for later.
+ RefPtr<Promise> promise;
+ rv = pMsgCompose->SendMsg(WeAreOffline() ? nsIMsgSend::nsMsgQueueForLater
+ : nsIMsgSend::nsMsgDeliverNow,
+ pMsgId, nullptr, nullptr, nullptr,
+ getter_AddRefs(promise));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // When offline, the message is saved to Outbox and OnStopSending won't be
+ // called.
+ if (WeAreOffline()) return NS_OK;
+
+ // Wait for OnStopSending to be called.
+ mozilla::SpinEventLoopUntil("nsIMsgCompose::SendMsg is async"_ns,
+ [=]() { return sendListener->IsDone(); });
+
+ return rv;
+}
+
+nsresult nsMapiHook::HandleAttachments(nsIMsgCompFields* aCompFields,
+ int32_t aFileCount,
+ lpnsMapiFileDesc aFiles, bool aIsUTF8) {
+ nsresult rv = NS_OK;
+ // Do nothing if there are no files to process.
+ if (!aFiles || aFileCount <= 0) return NS_OK;
+
+ nsAutoCString Attachments;
+ nsAutoCString TempFiles;
+
+ nsCOMPtr<nsIFile> pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || (!pFile)) return rv;
+ nsCOMPtr<nsIFile> pTempDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || (!pTempDir)) return rv;
+
+ for (int i = 0; i < aFileCount; i++) {
+ if (aFiles[i].lpszPathName) {
+ // check if attachment exists
+ if (!aIsUTF8)
+ pFile->InitWithNativePath(nsDependentCString(aFiles[i].lpszPathName));
+ else
+ pFile->InitWithPath(NS_ConvertUTF8toUTF16(aFiles[i].lpszPathName));
+
+ bool bExist;
+ rv = pFile->Exists(&bExist);
+ MOZ_LOG(
+ MAPI, mozilla::LogLevel::Debug,
+ ("nsMapiHook::HandleAttachments: filename: %s path: %s exists = %s\n",
+ (const char*)aFiles[i].lpszFileName,
+ (const char*)aFiles[i].lpszPathName, bExist ? "true" : "false"));
+ if (NS_FAILED(rv) || (!bExist)) return NS_ERROR_FILE_NOT_FOUND;
+
+ // Temp Directory
+ nsCOMPtr<nsIFile> pTempDir;
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pTempDir));
+
+ // create a new sub directory called moz_mapi underneath the temp
+ // directory
+ pTempDir->AppendRelativePath(u"moz_mapi"_ns);
+ pTempDir->Exists(&bExist);
+ if (!bExist) {
+ rv = pTempDir->Create(nsIFile::DIRECTORY_TYPE, 777);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // rename or copy the existing temp file with the real file name
+
+ nsAutoString leafName;
+ // convert to Unicode using Platform charset
+ // leafName already contains a unicode leafName from lpszPathName. If we
+ // were given a value for lpszFileName, use it. Otherwise stick with
+ // leafName
+ if (aFiles[i].lpszFileName) {
+ nsAutoString wholeFileName;
+ if (!aIsUTF8)
+ NS_CopyNativeToUnicode(nsDependentCString(aFiles[i].lpszFileName),
+ wholeFileName);
+ else
+ wholeFileName.Append(NS_ConvertUTF8toUTF16(aFiles[i].lpszFileName));
+ // need to find the last '\' and find the leafname from that.
+ int32_t lastSlash = wholeFileName.RFindChar(char16_t('\\'));
+ if (lastSlash != kNotFound)
+ leafName.Assign(Substring(wholeFileName, lastSlash + 1));
+ else
+ leafName.Assign(wholeFileName);
+ } else
+ pFile->GetLeafName(leafName);
+
+ nsCOMPtr<nsIMsgAttachment> attachment =
+ do_CreateInstance("@mozilla.org/messengercompose/attachment;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ attachment->SetName(leafName);
+
+ nsCOMPtr<nsIFile> pTempFile;
+ rv = pTempDir->Clone(getter_AddRefs(pTempFile));
+ if (NS_FAILED(rv) || !pTempFile) return rv;
+
+ pTempFile->Append(leafName);
+ pTempFile->Exists(&bExist);
+ if (bExist) {
+ rv = pTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777);
+ NS_ENSURE_SUCCESS(rv, rv);
+ pTempFile->Remove(false); // remove so we can copy over it.
+ pTempFile->GetLeafName(leafName);
+ }
+ // copy the file to its new location and file name
+ pFile->CopyTo(pTempDir, leafName);
+ // point pFile to the new location of the attachment
+ pFile->InitWithFile(pTempDir);
+ pFile->Append(leafName);
+
+ // create MsgCompose attachment object
+ attachment->SetTemporary(
+ true); // this one is a temp file so set the flag for MsgCompose
+
+ // now set the attachment object
+ nsAutoCString pURL;
+ NS_GetURLSpecFromFile(pFile, pURL);
+ attachment->SetUrl(pURL);
+
+ // set the file size
+ int64_t fileSize;
+ pFile->GetFileSize(&fileSize);
+ attachment->SetSize(fileSize);
+
+ // add the attachment
+ rv = aCompFields->AddAttachment(attachment);
+ if (NS_FAILED(rv))
+ MOZ_LOG(
+ MAPI, mozilla::LogLevel::Debug,
+ ("nsMapiHook::HandleAttachments: AddAttachment rv = %x\n", rv));
+ }
+ }
+ return rv;
+}
+
+nsresult nsMapiHook::HandleAttachmentsW(nsIMsgCompFields* aCompFields,
+ int32_t aFileCount,
+ lpnsMapiFileDescW aFiles) {
+ nsresult rv = NS_OK;
+ // Do nothing if there are no files to process.
+ if (!aFiles || aFileCount <= 0) return NS_OK;
+
+ nsAutoCString Attachments;
+ nsAutoCString TempFiles;
+
+ nsCOMPtr<nsIFile> pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || (!pFile)) return rv;
+ nsCOMPtr<nsIFile> pTempDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || (!pTempDir)) return rv;
+
+ for (int i = 0; i < aFileCount; i++) {
+ if (aFiles[i].lpszPathName) {
+ // Check if attachment exists.
+ pFile->InitWithPath(nsDependentString(aFiles[i].lpszPathName));
+
+ bool bExist;
+ rv = pFile->Exists(&bExist);
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
+ ("nsMapiHook::HandleAttachmentsW: filename: %s path: %s exists = "
+ "%s \n",
+ NS_ConvertUTF16toUTF8(aFiles[i].lpszFileName).get(),
+ NS_ConvertUTF16toUTF8(aFiles[i].lpszPathName).get(),
+ bExist ? "true" : "false"));
+ if (NS_FAILED(rv) || (!bExist)) return NS_ERROR_FILE_NOT_FOUND;
+
+ // Temp Directory.
+ nsCOMPtr<nsIFile> pTempDir;
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pTempDir));
+
+ // Create a new sub directory called moz_mapi underneath the temp
+ // directory.
+ pTempDir->AppendRelativePath(u"moz_mapi"_ns);
+ pTempDir->Exists(&bExist);
+ if (!bExist) {
+ rv = pTempDir->Create(nsIFile::DIRECTORY_TYPE, 777);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // Rename or copy the existing temp file with the real file name.
+
+ nsAutoString leafName;
+ // leafName already contains a unicode leafName from lpszPathName. If we
+ // were given a value for lpszFileName, use it. Otherwise stick with
+ // leafName.
+ if (aFiles[i].lpszFileName) {
+ nsAutoString wholeFileName(aFiles[i].lpszFileName);
+ // Need to find the last '\' and find the leafname from that.
+ int32_t lastSlash = wholeFileName.RFindChar(char16_t('\\'));
+ if (lastSlash != kNotFound)
+ leafName.Assign(Substring(wholeFileName, lastSlash + 1));
+ else
+ leafName.Assign(wholeFileName);
+ } else
+ pFile->GetLeafName(leafName);
+
+ nsCOMPtr<nsIMsgAttachment> attachment =
+ do_CreateInstance("@mozilla.org/messengercompose/attachment;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ attachment->SetName(leafName);
+
+ nsCOMPtr<nsIFile> pTempFile;
+ rv = pTempDir->Clone(getter_AddRefs(pTempFile));
+ if (NS_FAILED(rv) || !pTempFile) return rv;
+
+ pTempFile->Append(leafName);
+ pTempFile->Exists(&bExist);
+ if (bExist) {
+ rv = pTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777);
+ NS_ENSURE_SUCCESS(rv, rv);
+ pTempFile->Remove(false); // remove so we can copy over it.
+ pTempFile->GetLeafName(leafName);
+ }
+ // Copy the file to its new location and file name.
+ pFile->CopyTo(pTempDir, leafName);
+ // Point pFile to the new location of the attachment.
+ pFile->InitWithFile(pTempDir);
+ pFile->Append(leafName);
+
+ // Create MsgCompose attachment object.
+ attachment->SetTemporary(
+ true); // this one is a temp file so set the flag for MsgCompose
+
+ // Now set the attachment object.
+ nsAutoCString pURL;
+ NS_GetURLSpecFromFile(pFile, pURL);
+ attachment->SetUrl(pURL);
+
+ // Set the file size.
+ int64_t fileSize;
+ pFile->GetFileSize(&fileSize);
+ attachment->SetSize(fileSize);
+
+ // Add the attachment.
+ rv = aCompFields->AddAttachment(attachment);
+ if (NS_FAILED(rv))
+ MOZ_LOG(
+ MAPI, mozilla::LogLevel::Debug,
+ ("nsMapiHook::HandleAttachmentsW: AddAttachment rv = %x\n", rv));
+ }
+ }
+ return rv;
+}
+
+// this is used to convert non Unicode data and then populate comp fields
+nsresult nsMapiHook::PopulateCompFieldsWithConversion(
+ lpnsMapiMessage aMessage, nsIMsgCompFields* aCompFields) {
+ bool isUTF8 = aMessage->ulReserved == CP_UTF8;
+
+ if (aMessage->lpOriginator && aMessage->lpOriginator->lpszAddress) {
+ nsAutoString From;
+ if (!isUTF8)
+ From.Append(NS_ConvertASCIItoUTF16(aMessage->lpOriginator->lpszAddress));
+ else
+ From.Append(NS_ConvertUTF8toUTF16(aMessage->lpOriginator->lpszAddress));
+ aCompFields->SetFrom(From);
+ }
+
+ nsAutoString To;
+ nsAutoString Cc;
+ nsAutoString Bcc;
+ constexpr auto Comma = u","_ns;
+ if (aMessage->lpRecips) {
+ for (int i = 0; i < (int)aMessage->nRecipCount; i++) {
+ if (aMessage->lpRecips[i].lpszAddress || aMessage->lpRecips[i].lpszName) {
+ const char* addressWithoutType = (aMessage->lpRecips[i].lpszAddress)
+ ? aMessage->lpRecips[i].lpszAddress
+ : aMessage->lpRecips[i].lpszName;
+ if (!PL_strncasecmp(addressWithoutType, "SMTP:", 5))
+ addressWithoutType += 5;
+
+ switch (aMessage->lpRecips[i].ulRecipClass) {
+ case MAPI_TO:
+ if (!To.IsEmpty()) To += Comma;
+ if (!isUTF8)
+ To.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ else
+ To.Append(NS_ConvertUTF8toUTF16(addressWithoutType));
+ break;
+
+ case MAPI_CC:
+ if (!Cc.IsEmpty()) Cc += Comma;
+ if (!isUTF8)
+ Cc.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ else
+ Cc.Append(NS_ConvertUTF8toUTF16(addressWithoutType));
+ break;
+
+ case MAPI_BCC:
+ if (!Bcc.IsEmpty()) Bcc += Comma;
+ if (!isUTF8)
+ Bcc.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ else
+ Bcc.Append(NS_ConvertUTF8toUTF16(addressWithoutType));
+ break;
+ }
+ }
+ }
+ }
+
+ // set To, Cc, Bcc
+ aCompFields->SetTo(To);
+ aCompFields->SetCc(Cc);
+ aCompFields->SetBcc(Bcc);
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
+ ("to: %s cc: %s bcc: %s \n", NS_ConvertUTF16toUTF8(To).get(),
+ NS_ConvertUTF16toUTF8(Cc).get(), NS_ConvertUTF16toUTF8(Bcc).get()));
+
+ // set subject
+ nsresult rv = NS_OK;
+ if (aMessage->lpszSubject) {
+ nsAutoString Subject;
+ if (!isUTF8)
+ rv = NS_CopyNativeToUnicode(nsDependentCString(aMessage->lpszSubject),
+ Subject);
+ else
+ Subject.Append(NS_ConvertUTF8toUTF16(aMessage->lpszSubject));
+ if (NS_FAILED(rv)) return rv;
+ aCompFields->SetSubject(Subject);
+ }
+
+ // handle attachments as File URL
+ rv = HandleAttachments(aCompFields, aMessage->nFileCount, aMessage->lpFiles,
+ isUTF8);
+ if (NS_FAILED(rv)) return rv;
+
+ // set body
+ if (aMessage->lpszNoteText) {
+ nsAutoString Body;
+ if (!isUTF8)
+ rv = NS_CopyNativeToUnicode(nsDependentCString(aMessage->lpszNoteText),
+ Body);
+ else
+ Body.Append(NS_ConvertUTF8toUTF16(aMessage->lpszNoteText));
+ if (NS_FAILED(rv)) return rv;
+ if (Body.IsEmpty() || Body.Last() != '\n') Body.AppendLiteral(CRLF);
+
+ // This is needed when Simple MAPI is used without a compose window.
+ // See bug 1366196.
+ if (Body.Find(u"<html>") == kNotFound) aCompFields->SetForcePlainText(true);
+
+ rv = aCompFields->SetBody(Body);
+ } else {
+ // No body: Assume that we can do plaintext. This will trigger the default
+ // compose format in ShowComposerWindow().
+ aCompFields->SetForcePlainText(true);
+ }
+
+#ifdef RAJIV_DEBUG
+ // testing what all was set in CompFields
+ printf("To : %S \n", To.get());
+ printf("CC : %S \n", Cc.get());
+ printf("BCC : %S \n", Bcc.get());
+#endif
+
+ return rv;
+}
+
+// This is used to populate comp fields with UTF-16 data from MAPISendMailW
+// function.
+nsresult nsMapiHook::PopulateCompFieldsW(lpnsMapiMessageW aMessage,
+ nsIMsgCompFields* aCompFields) {
+ if (aMessage->lpOriginator && aMessage->lpOriginator->lpszAddress)
+ aCompFields->SetFrom(
+ nsDependentString(aMessage->lpOriginator->lpszAddress));
+
+ nsAutoString To;
+ nsAutoString Cc;
+ nsAutoString Bcc;
+
+ constexpr auto Comma = u","_ns;
+
+ if (aMessage->lpRecips) {
+ for (int i = 0; i < (int)aMessage->nRecipCount; i++) {
+ if (aMessage->lpRecips[i].lpszAddress || aMessage->lpRecips[i].lpszName) {
+ const wchar_t* addressWithoutType =
+ (aMessage->lpRecips[i].lpszAddress)
+ ? aMessage->lpRecips[i].lpszAddress
+ : aMessage->lpRecips[i].lpszName;
+ if (_wcsnicmp(addressWithoutType, L"SMTP:", 5) == 0)
+ addressWithoutType += 5;
+ switch (aMessage->lpRecips[i].ulRecipClass) {
+ case MAPI_TO:
+ if (!To.IsEmpty()) To += Comma;
+ To.Append(nsDependentString(addressWithoutType));
+ break;
+
+ case MAPI_CC:
+ if (!Cc.IsEmpty()) Cc += Comma;
+ Cc.Append(nsDependentString(addressWithoutType));
+ break;
+
+ case MAPI_BCC:
+ if (!Bcc.IsEmpty()) Bcc += Comma;
+ Bcc.Append(nsDependentString(addressWithoutType));
+ break;
+ }
+ }
+ }
+ }
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
+ ("to: %s cc: %s bcc: %s \n", NS_ConvertUTF16toUTF8(To).get(),
+ NS_ConvertUTF16toUTF8(Cc).get(), NS_ConvertUTF16toUTF8(Bcc).get()));
+ // set To, Cc, Bcc
+ aCompFields->SetTo(To);
+ aCompFields->SetCc(Cc);
+ aCompFields->SetBcc(Bcc);
+
+ // Set subject.
+ if (aMessage->lpszSubject)
+ aCompFields->SetSubject(nsDependentString(aMessage->lpszSubject));
+
+ // handle attachments as File URL
+ nsresult rv =
+ HandleAttachmentsW(aCompFields, aMessage->nFileCount, aMessage->lpFiles);
+ if (NS_FAILED(rv)) return rv;
+
+ // Set body.
+ if (aMessage->lpszNoteText) {
+ nsString Body(aMessage->lpszNoteText);
+ if (Body.IsEmpty() || Body.Last() != '\n') Body.AppendLiteral(CRLF);
+
+ // This is needed when Simple MAPI is used without a compose window.
+ // See bug 1366196.
+ if (Body.Find(u"<html>") == kNotFound) aCompFields->SetForcePlainText(true);
+
+ rv = aCompFields->SetBody(Body);
+ } else {
+ // No body: Assume that we can do plaintext. This will trigger the default
+ // compose format in ShowComposerWindow().
+ aCompFields->SetForcePlainText(true);
+ }
+ return rv;
+}
+
+// this is used to populate the docs as attachments in the Comp fields for Send
+// Documents
+nsresult nsMapiHook::PopulateCompFieldsForSendDocs(
+ nsIMsgCompFields* aCompFields, ULONG aFlags, LPSTR aDelimChar,
+ LPSTR aFilePaths) {
+ nsAutoCString strDelimChars;
+ nsAutoCString strFilePaths;
+ nsresult rv = NS_OK;
+ bool bExist;
+
+ if (aDelimChar) strDelimChars.Assign(aDelimChar);
+ if (aFilePaths) strFilePaths.Assign(aFilePaths);
+
+ // check for comma in filename
+ if (strDelimChars.FindChar(',') ==
+ kNotFound) // if comma is not in the delimiter specified by user
+ {
+ if (strFilePaths.FindChar(',') !=
+ kNotFound) // if comma found in filenames return error
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ nsCString Attachments;
+
+ // only 1 file is to be sent, no delim specified
+ if (strDelimChars.IsEmpty()) strDelimChars.Assign(';');
+
+ int32_t offset = 0;
+ int32_t FilePathsLen = strFilePaths.Length();
+ if (FilePathsLen) {
+ nsAutoString Subject;
+
+ // multiple files to be sent, delim specified
+ nsCOMPtr<nsIFile> pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || (!pFile)) return rv;
+
+ char* newFilePaths = (char*)strFilePaths.get();
+ while (offset != kNotFound) {
+ // Temp Directory
+ nsCOMPtr<nsIFile> pTempDir;
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pTempDir));
+
+ // if not already existing, create another temp dir for mapi within Win
+ // temp dir this is windows only so we can do "\\"
+ pTempDir->AppendRelativePath(u"moz_mapi"_ns);
+ pTempDir->Exists(&bExist);
+ if (!bExist) {
+ rv = pTempDir->Create(nsIFile::DIRECTORY_TYPE, 777);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ nsAutoCString RemainingPaths;
+ RemainingPaths.Assign(newFilePaths);
+ offset = RemainingPaths.Find(strDelimChars);
+ if (offset != kNotFound) {
+ RemainingPaths.SetLength(offset);
+ if ((offset + (int32_t)strDelimChars.Length()) < FilePathsLen)
+ newFilePaths += offset + strDelimChars.Length();
+ else
+ offset = kNotFound;
+ FilePathsLen -= offset + strDelimChars.Length();
+ }
+
+ if (RemainingPaths[1] != ':' && RemainingPaths[1] != '\\') {
+ char cwd[MAX_PATH];
+ if (_getdcwd(_getdrive(), cwd, MAX_PATH)) {
+ nsAutoCString cwdStr;
+ cwdStr.Assign(cwd);
+ cwdStr.Append('\\');
+ RemainingPaths.Insert(cwdStr, 0);
+ }
+ }
+
+ pFile->InitWithNativePath(RemainingPaths);
+
+ rv = pFile->Exists(&bExist);
+ if (NS_FAILED(rv) || (!bExist)) return NS_ERROR_FILE_NOT_FOUND;
+
+ // filename of the file attachment
+ nsAutoString leafName;
+ pFile->GetLeafName(leafName);
+ if (NS_FAILED(rv) || leafName.IsEmpty()) return rv;
+
+ if (!Subject.IsEmpty()) Subject.AppendLiteral(", ");
+ Subject += leafName;
+
+ // create MsgCompose attachment object
+ nsCOMPtr<nsIMsgAttachment> attachment =
+ do_CreateInstance("@mozilla.org/messengercompose/attachment;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsDependentString fileNameNative(leafName.get());
+ rv = pFile->CopyTo(pTempDir, fileNameNative);
+ if (NS_FAILED(rv)) return rv;
+
+ // now turn pTempDir into a full file path to the temp file
+ pTempDir->Append(fileNameNative);
+
+ // this one is a temp file so set the flag for MsgCompose
+ attachment->SetTemporary(true);
+
+ // now set the attachment object
+ nsAutoCString pURL;
+ NS_GetURLSpecFromFile(pTempDir, pURL);
+ attachment->SetUrl(pURL);
+
+ // set the file size
+ int64_t fileSize;
+ pFile->GetFileSize(&fileSize);
+ attachment->SetSize(fileSize);
+
+ // add the attachment
+ rv = aCompFields->AddAttachment(attachment);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ rv = aCompFields->SetBody(Subject);
+ }
+
+ return rv;
+}
+
+// this used for Send with UI
+nsresult nsMapiHook::ShowComposerWindow(unsigned long aSession,
+ nsIMsgCompFields* aCompFields) {
+ nsresult rv = NS_OK;
+
+ // create a send listener to get back the send status
+ RefPtr<MAPISendListener> sendListener = new MAPISendListener;
+
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams(
+ do_CreateInstance("@mozilla.org/messengercompose/composeparams;1", &rv));
+ if (NS_FAILED(rv) || (!pMsgComposeParams)) return rv;
+
+ // If we found HTML, compose in HTML.
+ bool forcePlainText;
+ aCompFields->GetForcePlainText(&forcePlainText);
+ pMsgComposeParams->SetFormat(forcePlainText ? nsIMsgCompFormat::Default
+ : nsIMsgCompFormat::HTML);
+
+ // populate the compose params
+ pMsgComposeParams->SetType(nsIMsgCompType::New);
+
+ // Never force to plain text, the default format will take care of that.
+ // Undo the forcing that happened in
+ // PopulateCompFields/PopulateCompFieldsWithConversion. See bug 1095629 and
+ // bug 1366196.
+ aCompFields->SetForcePlainText(false);
+ pMsgComposeParams->SetComposeFields(aCompFields);
+ pMsgComposeParams->SetSendListener(sendListener);
+
+ /** get the nsIMsgComposeService object to open the compose window **/
+ nsCOMPtr<nsIMsgComposeService> compService =
+ do_GetService("@mozilla.org/messengercompose;1");
+ if (NS_FAILED(rv) || (!compService)) return rv;
+
+ rv = compService->OpenComposeWindowWithParams(nullptr, pMsgComposeParams);
+ if (NS_FAILED(rv)) return rv;
+
+ return rv;
+}