summaryrefslogtreecommitdiffstats
path: root/mailnews/mapi/mapihook/src
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mapi/mapihook/src')
-rw-r--r--mailnews/mapi/mapihook/src/Makefile.in6
-rw-r--r--mailnews/mapi/mapihook/src/Registry.cpp291
-rw-r--r--mailnews/mapi/mapihook/src/Registry.h23
-rw-r--r--mailnews/mapi/mapihook/src/moz.build25
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiFactory.cpp85
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiFactory.h39
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiHook.cpp829
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiHook.h33
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiImp.cpp878
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiImp.h77
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiMain.cpp306
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiMain.h86
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiSupport.cpp151
-rw-r--r--mailnews/mapi/mapihook/src/msgMapiSupport.h34
14 files changed, 2863 insertions, 0 deletions
diff --git a/mailnews/mapi/mapihook/src/Makefile.in b/mailnews/mapi/mapihook/src/Makefile.in
new file mode 100644
index 000000000..10e6172bf
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/Makefile.in
@@ -0,0 +1,6 @@
+#
+# 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/.
+
+CSRCS += ../build/msgMapi_i.c
diff --git a/mailnews/mapi/mapihook/src/Registry.cpp b/mailnews/mapi/mapihook/src/Registry.cpp
new file mode 100644
index 000000000..31db7520b
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/Registry.cpp
@@ -0,0 +1,291 @@
+/* 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/. */
+
+#undef _UNICODE
+#undef UNICODE
+
+#include <objbase.h>
+#include "nsStringGlue.h"
+#include "Registry.h"
+
+#define MAPI_PROXY_DLL_NAME "MapiProxy.dll"
+#define MAPI_STARTUP_ARG " /MAPIStartUp"
+#define MAX_SIZE 2048
+
+// Size of a CLSID as a string
+const int CLSID_STRING_SIZE = 39;
+
+// Proxy/Stub Dll Routines
+
+typedef HRESULT (__stdcall ProxyServer)();
+
+
+// Convert a CLSID to a char string.
+
+BOOL CLSIDtochar(const CLSID& clsid, char* szCLSID,
+ int length)
+{
+ LPOLESTR wszCLSID = NULL;
+
+ // Get CLSID
+ HRESULT hr = StringFromCLSID(clsid, &wszCLSID);
+ if (FAILED(hr))
+ return FALSE;
+
+ // Covert from wide characters to non-wide.
+ wcstombs(szCLSID, wszCLSID, length);
+
+ // Free memory.
+ CoTaskMemFree(wszCLSID);
+
+ return TRUE;
+}
+
+// Create a key and set its value.
+
+BOOL setKeyAndValue(nsAutoCString keyName, const char* subKey,
+ const char* theValue)
+{
+ HKEY hKey;
+ BOOL retValue = TRUE;
+
+ nsAutoCString theKey(keyName);
+ if (subKey != NULL)
+ {
+ theKey += "\\";
+ theKey += subKey;
+ }
+
+ // Create and open key and subkey.
+ long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, theKey.get(),
+ 0, NULL, REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, NULL, &hKey, NULL);
+ if (lResult != ERROR_SUCCESS)
+ return FALSE ;
+
+ // Set the Value.
+ if (theValue != NULL)
+ {
+ lResult = RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)theValue,
+ strlen(theValue)+1);
+ if (lResult != ERROR_SUCCESS)
+ retValue = FALSE;
+ }
+
+ RegCloseKey(hKey);
+ return TRUE;
+}
+
+// Delete a key and all of its descendents.
+
+LONG recursiveDeleteKey(HKEY hKeyParent, // Parent of key to delete
+ const char* lpszKeyChild) // Key to delete
+{
+ // Open the child.
+ HKEY hKeyChild ;
+ LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
+ KEY_ALL_ACCESS, &hKeyChild) ;
+ if (lRes != ERROR_SUCCESS)
+ {
+ return lRes ;
+ }
+
+ // Enumerate all of the decendents of this child.
+ FILETIME time ;
+ char szBuffer[MAX_SIZE] ;
+ DWORD dwSize = MAX_SIZE ;
+ while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
+ NULL, NULL, &time) == S_OK)
+ {
+ // Delete the decendents of this child.
+ lRes = recursiveDeleteKey(hKeyChild, szBuffer) ;
+ if (lRes != ERROR_SUCCESS)
+ {
+ // Cleanup before exiting.
+ RegCloseKey(hKeyChild) ;
+ return lRes;
+ }
+ dwSize = MAX_SIZE;
+ }
+
+ // Close the child.
+ RegCloseKey(hKeyChild) ;
+
+ // Delete this child.
+ return RegDeleteKey(hKeyParent, lpszKeyChild) ;
+}
+
+void RegisterProxy()
+{
+ HINSTANCE h = NULL;
+ ProxyServer *RegisterFunc = NULL;
+
+ char szModule[MAX_SIZE];
+ char *pTemp = NULL;
+
+ HMODULE hModule = GetModuleHandle(NULL);
+ DWORD dwResult = ::GetModuleFileName(hModule, szModule,
+ sizeof(szModule)/sizeof(char));
+ if (dwResult == 0)
+ return;
+
+ pTemp = strrchr(szModule, '\\');
+ if (pTemp == NULL)
+ return;
+
+ *pTemp = '\0';
+ nsAutoCString proxyPath(szModule);
+
+ proxyPath += "\\";
+ proxyPath += MAPI_PROXY_DLL_NAME;
+
+ h = LoadLibrary(proxyPath.get());
+ if (h == NULL)
+ return;
+
+ RegisterFunc = (ProxyServer *) GetProcAddress(h, "DllRegisterServer");
+ if (RegisterFunc)
+ RegisterFunc();
+
+ FreeLibrary(h);
+}
+
+void UnRegisterProxy()
+{
+ HINSTANCE h = NULL;
+ ProxyServer *UnRegisterFunc = NULL;
+
+ char szModule[MAX_SIZE];
+ char *pTemp = NULL;
+
+ HMODULE hModule = GetModuleHandle(NULL);
+ DWORD dwResult = ::GetModuleFileName(hModule, szModule,
+ sizeof(szModule)/sizeof(char));
+ if (dwResult == 0)
+ return;
+
+ pTemp = strrchr(szModule, '\\');
+ if (pTemp == NULL)
+ return;
+
+ *pTemp = '\0';
+ nsAutoCString proxyPath(szModule);
+
+ proxyPath += "\\";
+ proxyPath += MAPI_PROXY_DLL_NAME;
+
+ h = LoadLibrary(proxyPath.get());
+ if (h == NULL)
+ return;
+
+ UnRegisterFunc = (ProxyServer *) GetProcAddress(h, "DllUnregisterServer");
+ if (UnRegisterFunc)
+ UnRegisterFunc();
+
+ FreeLibrary(h);
+}
+
+// Register the component in the registry.
+
+HRESULT RegisterServer(const CLSID& clsid, // Class ID
+ const char* szFriendlyName, // Friendly Name
+ const char* szVerIndProgID, // Programmatic
+ const char* szProgID) // IDs
+{
+ HMODULE hModule = GetModuleHandle(NULL);
+ char szModuleName[MAX_SIZE];
+ char szCLSID[CLSID_STRING_SIZE];
+
+ nsAutoCString independentProgId(szVerIndProgID);
+ nsAutoCString progId(szProgID);
+
+ DWORD dwResult = ::GetModuleFileName(hModule, szModuleName,
+ sizeof(szModuleName)/sizeof(char));
+
+ if (dwResult == 0)
+ return S_FALSE;
+
+ nsAutoCString moduleName(szModuleName);
+ nsAutoCString registryKey("CLSID\\");
+
+ moduleName += MAPI_STARTUP_ARG;
+
+ // Convert the CLSID into a char.
+
+ if (!CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)))
+ return S_FALSE;
+ registryKey += szCLSID;
+
+ // Add the CLSID to the registry.
+ if (!setKeyAndValue(registryKey, NULL, szFriendlyName))
+ return S_FALSE;
+
+ if (!setKeyAndValue(registryKey, "LocalServer32", moduleName.get()))
+ return S_FALSE;
+
+ // Add the ProgID subkey under the CLSID key.
+ if (!setKeyAndValue(registryKey, "ProgID", szProgID))
+ return S_FALSE;
+
+ // Add the version-independent ProgID subkey under CLSID key.
+ if (!setKeyAndValue(registryKey, "VersionIndependentProgID", szVerIndProgID))
+ return S_FALSE;
+
+ // Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
+ if (!setKeyAndValue(independentProgId, NULL, szFriendlyName))
+ return S_FALSE;
+ if (!setKeyAndValue(independentProgId, "CLSID", szCLSID))
+ return S_FALSE;
+ if (!setKeyAndValue(independentProgId, "CurVer", szProgID))
+ return S_FALSE;
+
+ // Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
+ if (!setKeyAndValue(progId, NULL, szFriendlyName))
+ return S_FALSE;
+ if (!setKeyAndValue(progId, "CLSID", szCLSID))
+ return S_FALSE;
+
+ RegisterProxy();
+
+ return S_OK;
+}
+
+LONG UnregisterServer(const CLSID& clsid, // Class ID
+ const char* szVerIndProgID, // Programmatic
+ const char* szProgID) // IDs
+{
+ LONG lResult = S_OK;
+
+ // Convert the CLSID into a char.
+
+ char szCLSID[CLSID_STRING_SIZE];
+ if (!CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)))
+ return S_FALSE;
+
+ UnRegisterProxy();
+
+ nsAutoCString registryKey("CLSID\\");
+ registryKey += szCLSID;
+
+ lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, registryKey.get());
+ if (lResult == ERROR_SUCCESS || lResult == ERROR_FILE_NOT_FOUND)
+ return lResult;
+
+ registryKey += "\\LocalServer32";
+
+ // Delete only the path for this server.
+
+ lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, registryKey.get());
+ if (lResult != ERROR_SUCCESS && lResult != ERROR_FILE_NOT_FOUND)
+ return lResult;
+
+ // Delete the version-independent ProgID Key.
+ lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID);
+ if (lResult != ERROR_SUCCESS && lResult != ERROR_FILE_NOT_FOUND)
+ return lResult;
+
+ lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID);
+
+ return lResult;
+}
diff --git a/mailnews/mapi/mapihook/src/Registry.h b/mailnews/mapi/mapihook/src/Registry.h
new file mode 100644
index 000000000..34a4efca7
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/Registry.h
@@ -0,0 +1,23 @@
+/* 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/. */
+
+#ifndef _REGISTRY_H_
+#define _REGISTRY_H_
+
+#include <objbase.h>
+
+// This function will register a component in the Registry.
+
+HRESULT RegisterServer(const CLSID& clsid,
+ const char* szFriendlyName,
+ const char* szVerIndProgID,
+ const char* szProgID) ;
+
+// This function will unregister a component.
+
+HRESULT UnregisterServer(const CLSID& clsid,
+ const char* szVerIndProgID,
+ const char* szProgID) ;
+
+#endif
diff --git a/mailnews/mapi/mapihook/src/moz.build b/mailnews/mapi/mapihook/src/moz.build
new file mode 100644
index 000000000..3e8cd3450
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/moz.build
@@ -0,0 +1,25 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'msgMapiFactory.cpp',
+ 'msgMapiHook.cpp',
+ 'msgMapiImp.cpp',
+ 'msgMapiMain.cpp',
+ 'msgMapiSupport.cpp',
+ 'Registry.cpp',
+]
+
+if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']:
+ XPCOMBinaryComponent('msgMapi')
+else:
+ FINAL_LIBRARY = 'xul'
+
+OS_LIBS += [
+ 'ole32',
+]
+
+DEFINES['UNICODE'] = True
+DEFINES['_UNICODE'] = True
diff --git a/mailnews/mapi/mapihook/src/msgMapiFactory.cpp b/mailnews/mapi/mapihook/src/msgMapiFactory.cpp
new file mode 100644
index 000000000..829279f62
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiFactory.cpp
@@ -0,0 +1,85 @@
+/* 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/. */
+
+#undef UNICODE
+#undef _UNICODE
+
+#include "msgMapiFactory.h"
+#include "msgMapiImp.h"
+#include "msgMapi.h"
+
+CMapiFactory ::CMapiFactory()
+: m_cRef(1)
+{
+}
+
+CMapiFactory::~CMapiFactory()
+{
+}
+
+STDMETHODIMP CMapiFactory::QueryInterface(const IID& aIid, void** aPpv)
+{
+ if ((aIid == IID_IUnknown) || (aIid == IID_IClassFactory))
+ {
+ *aPpv = static_cast<IClassFactory*>(this);
+ }
+ else
+ {
+ *aPpv = nullptr;
+ return E_NOINTERFACE;
+ }
+ reinterpret_cast<IUnknown*>(*aPpv)->AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) CMapiFactory::AddRef()
+{
+ return ++m_cRef;
+}
+
+STDMETHODIMP_(ULONG) CMapiFactory::Release()
+{
+ int32_t temp = --m_cRef;
+ if (m_cRef == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return temp;
+}
+
+STDMETHODIMP CMapiFactory::CreateInstance(IUnknown* aUnknownOuter,
+ const IID& aIid,
+ void** aPpv)
+{
+ // Cannot aggregate.
+
+ if (aUnknownOuter != nullptr)
+ {
+ return CLASS_E_NOAGGREGATION ;
+ }
+
+ // Create component.
+
+ CMapiImp* pImp = new CMapiImp();
+ if (pImp == nullptr)
+ {
+ return E_OUTOFMEMORY ;
+ }
+
+ // Get the requested interface.
+ HRESULT hr = pImp->QueryInterface(aIid, aPpv);
+
+ // Release the IUnknown pointer.
+ // (If QueryInterface failed, component will delete itself.)
+
+ pImp->Release();
+ return hr;
+}
+
+STDMETHODIMP CMapiFactory::LockServer(BOOL aLock)
+{
+ return S_OK ;
+}
diff --git a/mailnews/mapi/mapihook/src/msgMapiFactory.h b/mailnews/mapi/mapihook/src/msgMapiFactory.h
new file mode 100644
index 000000000..7ae2f2ed3
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiFactory.h
@@ -0,0 +1,39 @@
+/* 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/. */
+
+#ifndef MSG_MAPI_FACTORY_H
+#define MSG_MAPI_FACTORY_H
+
+#include <windows.h>
+#include <objbase.h>
+#include "nspr.h"
+#include "nsISupportsImpl.h" // ThreadSafeAutoRefCnt
+#include <stdint.h>
+
+
+class CMapiFactory : public IClassFactory
+{
+public :
+
+ // IUnknown
+
+ STDMETHODIMP QueryInterface (REFIID aIid, void** aPpv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ // IClassFactory
+
+ STDMETHODIMP CreateInstance (LPUNKNOWN aUnkOuter, REFIID aIid, void **aPpv);
+ STDMETHODIMP LockServer (BOOL aLock);
+
+ CMapiFactory ();
+
+private:
+ mozilla::ThreadSafeAutoRefCnt m_cRef;
+
+ ~CMapiFactory ();
+};
+
+#endif // MSG_MAPI_FACTORY_H
+
diff --git a/mailnews/mapi/mapihook/src/msgMapiHook.cpp b/mailnews/mapi/mapihook/src/msgMapiHook.cpp
new file mode 100644
index 000000000..02696fe94
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiHook.cpp
@@ -0,0 +1,829 @@
+/* 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 <tchar.h>
+#include <direct.h>
+#include "nsCOMPtr.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsISupports.h"
+#include "nsIPromptService.h"
+#include "nsIAppStartup.h"
+#include "nsIAppShellService.h"
+#include "mozIDOMWindow.h"
+#include "nsINativeAppSupport.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsIStringBundle.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsStringGlue.h"
+#include "nsUnicharUtils.h"
+#include "nsIMsgAttachment.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgComposeParams.h"
+#include "nsIMsgCompose.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgSend.h"
+#include "nsIMsgComposeService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIDirectoryService.h"
+#include "nsMsgI18N.h"
+#include "msgMapi.h"
+#include "msgMapiHook.h"
+#include "msgMapiSupport.h"
+#include "msgMapiMain.h"
+#include "nsThreadUtils.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "mozilla/Services.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+#include "nsEmbedCID.h"
+#include "mozilla/Logging.h"
+
+extern PRLogModuleInfo *MAPI;
+
+class nsMAPISendListener : public nsIMsgSendListener
+{
+public:
+
+ // 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) {
+ PR_CEnterMonitor(this);
+ PR_CNotifyAll(this);
+ m_done = true;
+ PR_CExitMonitor(this);
+ 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 *aFolderURI) {return NS_OK;}
+
+ static nsresult CreateMAPISendListener( nsIMsgSendListener **ppListener);
+
+ bool IsDone() { return m_done ; }
+
+protected :
+ nsMAPISendListener() {
+ m_done = false;
+ }
+
+ bool m_done;
+private:
+ virtual ~nsMAPISendListener() { }
+
+};
+
+
+NS_IMPL_ISUPPORTS(nsMAPISendListener, nsIMsgSendListener)
+
+nsresult nsMAPISendListener::CreateMAPISendListener( nsIMsgSendListener **ppListener)
+{
+ NS_ENSURE_ARG_POINTER(ppListener) ;
+
+ *ppListener = new nsMAPISendListener();
+ if (! *ppListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*ppListener);
+ return NS_OK;
+}
+
+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::services::GetStringBundleService();
+ 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(
+ u"brandFullName",
+ getter_Copies(brandName));
+ if (NS_FAILED(rv)) return false;
+
+ nsString loginTitle;
+ const char16_t *brandStrings[] = { brandName.get() };
+ NS_NAMED_LITERAL_STRING(loginTitlePropertyTag, "loginTitle");
+ const char16_t *dTitlePropertyTag = loginTitlePropertyTag.get();
+ rv = bundle->FormatStringFromName(dTitlePropertyTag, brandStrings, 1,
+ getter_Copies(loginTitle));
+ if (NS_FAILED(rv)) return false;
+
+ if (aLogin)
+ {
+ nsString loginText;
+ rv = bundle->GetStringFromName(u"loginTextwithName",
+ getter_Copies(loginText));
+ if (NS_FAILED(rv) || loginText.IsEmpty()) return false;
+
+ bool dummyValue = false;
+ rv = dlgService->PromptUsernameAndPassword(nullptr, loginTitle.get(),
+ loginText.get(), aUsername, aPassword,
+ nullptr, &dummyValue, &btnResult);
+ }
+ else
+ {
+ //nsString loginString;
+ nsString loginText;
+ const char16_t *userNameStrings[] = { *aUsername };
+
+ NS_NAMED_LITERAL_STRING(loginTextPropertyTag, "loginText");
+ const char16_t *dpropertyTag = loginTextPropertyTag.get();
+ rv = bundle->FormatStringFromName(dpropertyTag, userNameStrings, 1,
+ getter_Copies(loginText));
+ if (NS_FAILED(rv)) return false;
+
+ bool dummyValue = false;
+ rv = dlgService->PromptPassword(nullptr, loginTitle.get(), loginText.get(),
+ aPassword, nullptr, &dummyValue, &btnResult);
+ }
+ }
+
+ return btnResult;
+}
+
+bool nsMapiHook::VerifyUserName(const nsString& aUsername, nsCString& aIdKey)
+{
+ nsresult rv;
+
+ if (aUsername.IsEmpty())
+ return false;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager(do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return false;
+ nsCOMPtr<nsIArray> identities;
+ rv = accountManager->GetAllIdentities(getter_AddRefs(identities));
+ if (NS_FAILED(rv)) return false;
+
+ uint32_t numIndentities = 0;
+ identities->GetLength(&numIndentities);
+
+ for (uint32_t i = 0; i < numIndentities; i++)
+ {
+ nsCOMPtr<nsIMsgIdentity> thisIdentity(do_QueryElementAt(identities, i, &rv));
+ if (NS_SUCCEEDED(rv) && 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(NS_ConvertASCIItoUTF16(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::services::GetStringBundleService();
+ 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(u"mapiBlindSendWarning",
+ getter_Copies(warningMsg));
+ if (NS_FAILED(rv)) return false;
+
+ nsString dontShowAgainMessage;
+ rv = bundle->GetStringFromName(u"mapiBlindSendDontShowAgain",
+ getter_Copies(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;
+
+ /** create nsIMsgComposeParams obj and other fields to populate it **/
+
+ nsCOMPtr<mozIDOMWindowProxy> hiddenWindow;
+ // get parent window
+ nsCOMPtr<nsIAppShellService> appService = do_GetService( "@mozilla.org/appshell/appShellService;1", &rv);
+ if (NS_FAILED(rv)|| (!appService) ) return rv ;
+
+ rv = appService->GetHiddenDOMWindow(getter_AddRefs(hiddenWindow));
+ if ( NS_FAILED(rv) ) return rv ;
+ // smtp password and Logged in used IdKey from MapiConfig (session obj)
+ nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
+ if (!pMapiConfig) return NS_ERROR_FAILURE ; // get the singelton obj
+ char16_t * password = pMapiConfig->GetPassword(aSession) ;
+ // password
+ nsAutoCString smtpPassword;
+ LossyCopyUTF16toASCII(password, smtpPassword);
+
+ // Id key
+ nsCString MsgIdKey;
+ pMapiConfig->GetIdKey(aSession, MsgIdKey);
+
+ // get the MsgIdentity for the above key using AccountManager
+ nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService (NS_MSGACCOUNTMANAGER_CONTRACTID) ;
+ 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
+ nsCOMPtr <nsIMsgSendListener> sendListener ;
+ rv = nsMAPISendListener::CreateMAPISendListener(getter_AddRefs(sendListener)) ;
+ if (NS_FAILED(rv) || (!sendListener) ) return rv;
+
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &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) ;
+ pMsgComposeParams->SetSmtpPassword(smtpPassword.get());
+
+ // create the nsIMsgCompose object to send the object
+ nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv));
+ if (NS_FAILED(rv) || (!pMsgCompose) ) return rv ;
+
+ /** initialize nsIMsgCompose, Send the message, wait for send completion response **/
+
+ rv = pMsgCompose->Initialize(pMsgComposeParams, hiddenWindow, nullptr);
+ if (NS_FAILED(rv)) return rv ;
+
+ // If we're in offline mode, we'll need to queue it for later. No point in trying to send it.
+ return pMsgCompose->SendMsg(WeAreOffline() ? nsIMsgSend::nsMsgQueueForLater : nsIMsgSend::nsMsgDeliverNow,
+ pMsgId, nullptr, nullptr, nullptr);
+ if (NS_FAILED(rv)) return rv ;
+
+ // assign to interface pointer from nsCOMPtr to facilitate typecast below
+ nsIMsgSendListener * pSendListener = sendListener ;
+
+ // we need to wait here to make sure that we return only after send is completed
+ // so we will have a event loop here which will process the events till the Send IsDone.
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+ while ( !((nsMAPISendListener *) pSendListener)->IsDone() )
+ {
+ PR_CEnterMonitor(pSendListener);
+ PR_CWait(pSendListener, PR_MicrosecondsToInterval(1000UL));
+ PR_CExitMonitor(pSendListener);
+ NS_ProcessPendingEvents(thread);
+ }
+
+ return rv ;
+}
+
+// this is used to populate comp fields with Unicode data
+nsresult nsMapiHook::PopulateCompFields(lpnsMapiMessage aMessage,
+ nsIMsgCompFields * aCompFields)
+{
+ nsresult rv = NS_OK ;
+
+ if (aMessage->lpOriginator)
+ aCompFields->SetFrom (NS_ConvertASCIItoUTF16((char *) aMessage->lpOriginator->lpszAddress));
+
+ nsAutoString To ;
+ nsAutoString Cc ;
+ nsAutoString Bcc ;
+
+ NS_NAMED_LITERAL_STRING(Comma, ",");
+
+ 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;
+ To.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ break;
+
+ case MAPI_CC :
+ if (!Cc.IsEmpty())
+ Cc += Comma;
+ Cc.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ break;
+
+ case MAPI_BCC :
+ if (!Bcc.IsEmpty())
+ Bcc += Comma;
+ Bcc.Append(NS_ConvertASCIItoUTF16(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(NS_ConvertASCIItoUTF16(aMessage->lpszSubject));
+
+ // handle attachments as File URL
+ rv = HandleAttachments (aCompFields, aMessage->nFileCount, aMessage->lpFiles, true) ;
+ if (NS_FAILED(rv)) return rv ;
+
+ // set body
+ if (aMessage->lpszNoteText)
+ {
+ nsString Body;
+ CopyASCIItoUTF16(aMessage->lpszNoteText, Body);
+ 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("<html>") == kNotFound)
+ aCompFields->SetForcePlainText(true);
+
+ rv = aCompFields->SetBody(Body) ;
+ }
+ return rv ;
+}
+
+nsresult nsMapiHook::HandleAttachments (nsIMsgCompFields * aCompFields, int32_t aFileCount,
+ lpnsMapiFileDesc aFiles, BOOL aIsUnicode)
+{
+ nsresult rv = 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 (aIsUnicode)
+ pFile->InitWithPath (nsDependentString(aFiles[i].lpszPathName));
+ else
+ pFile->InitWithNativePath (nsDependentCString((const char*)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_TARGET_DOES_NOT_EXIST ;
+
+ //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(NS_LITERAL_STRING("moz_mapi"));
+ 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 (aIsUnicode)
+ wholeFileName.Assign(aFiles[i].lpszFileName);
+ else
+ ConvertToUnicode(nsMsgI18NFileSystemCharset(), (char *) aFiles[i].lpszFileName, wholeFileName);
+ // 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(NS_MSGATTACHMENT_CONTRACTID, &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 = %lx\n", rv));
+ }
+ }
+ return rv ;
+}
+
+
+// this is used to convert non Unicode data and then populate comp fields
+nsresult nsMapiHook::PopulateCompFieldsWithConversion(lpnsMapiMessage aMessage,
+ nsIMsgCompFields * aCompFields)
+{
+ nsresult rv = NS_OK;
+
+ if (aMessage->lpOriginator)
+ {
+ nsAutoString From;
+ From.Append(NS_ConvertASCIItoUTF16((char *) aMessage->lpOriginator->lpszAddress));
+ aCompFields->SetFrom (From);
+ }
+
+ nsAutoString To;
+ nsAutoString Cc;
+ nsAutoString Bcc;
+ NS_NAMED_LITERAL_STRING(Comma, ",");
+ 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 ;
+ To.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ break ;
+
+ case MAPI_CC :
+ if (!Cc.IsEmpty())
+ Cc += Comma ;
+ Cc.Append(NS_ConvertASCIItoUTF16(addressWithoutType));
+ break ;
+
+ case MAPI_BCC :
+ if (!Bcc.IsEmpty())
+ Bcc += Comma ;
+ Bcc.Append(NS_ConvertASCIItoUTF16(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()));
+
+ nsAutoCString platformCharSet;
+ // set subject
+ if (aMessage->lpszSubject)
+ {
+ nsAutoString Subject ;
+ if (platformCharSet.IsEmpty())
+ platformCharSet.Assign(nsMsgI18NFileSystemCharset());
+ rv = ConvertToUnicode(platformCharSet.get(), (char *) aMessage->lpszSubject, Subject);
+ if (NS_FAILED(rv)) return rv;
+ aCompFields->SetSubject(Subject);
+ }
+
+ // handle attachments as File URL
+ rv = HandleAttachments (aCompFields, aMessage->nFileCount, aMessage->lpFiles, false) ;
+ if (NS_FAILED(rv)) return rv ;
+
+ // set body
+ if (aMessage->lpszNoteText)
+ {
+ nsAutoString Body ;
+ if (platformCharSet.IsEmpty())
+ platformCharSet.Assign(nsMsgI18NFileSystemCharset());
+ rv = ConvertToUnicode(platformCharSet.get(), (char *) aMessage->lpszNoteText, Body);
+ 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("<html>") == kNotFound)
+ aCompFields->SetForcePlainText(true);
+
+ rv = aCompFields->SetBody(Body) ;
+ }
+
+#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 the docs as attachments in the Comp fields for Send Documents
+nsresult nsMapiHook::PopulateCompFieldsForSendDocs(nsIMsgCompFields * aCompFields, ULONG aFlags,
+ LPTSTR aDelimChar, LPTSTR aFilePaths)
+{
+ nsAutoString strDelimChars ;
+ nsString 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.AssignLiteral(";");
+
+ 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 ;
+
+ char16_t * newFilePaths = (char16_t *) 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 (NS_LITERAL_STRING("moz_mapi"));
+ pTempDir->Exists(&bExist) ;
+ if (!bExist)
+ {
+ rv = pTempDir->Create(nsIFile::DIRECTORY_TYPE, 777) ;
+ if (NS_FAILED(rv)) return rv ;
+ }
+
+ nsString 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))
+ {
+ nsAutoString cwdStr;
+ CopyASCIItoUTF16(cwd, cwdStr);
+ cwdStr.Append('\\');
+ RemainingPaths.Insert(cwdStr, 0);
+ }
+ }
+
+ pFile->InitWithPath (RemainingPaths) ;
+
+ rv = pFile->Exists(&bExist) ;
+ if (NS_FAILED(rv) || (!bExist) ) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST ;
+
+ // 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(NS_MSGATTACHMENT_CONTRACTID, &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
+ nsCOMPtr <nsIMsgSendListener> sendListener ;
+ rv = nsMAPISendListener::CreateMAPISendListener(getter_AddRefs(sendListener)) ;
+ if (NS_FAILED(rv) || (!sendListener) ) return rv ;
+
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &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 (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
+ if (NS_FAILED(rv)|| (!compService) ) return rv ;
+
+ rv = compService->OpenComposeWindowWithParams(nullptr, pMsgComposeParams) ;
+ if (NS_FAILED(rv)) return rv ;
+
+ return rv ;
+}
diff --git a/mailnews/mapi/mapihook/src/msgMapiHook.h b/mailnews/mapi/mapihook/src/msgMapiHook.h
new file mode 100644
index 000000000..8860d5227
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiHook.h
@@ -0,0 +1,33 @@
+/* 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/. */
+
+#ifndef MSG_MAPI_HOOK_H_
+#define MSG_MAPI_HOOK_H_
+
+#include "prtypes.h"
+
+class nsMapiHook
+{
+ public :
+
+ static bool DisplayLoginDialog(bool aLogin, char16_t **aUsername,
+ char16_t **aPassword);
+ static bool VerifyUserName(const nsString& aUsername, nsCString& aIdKey);
+
+ static bool IsBlindSendAllowed () ;
+ static nsresult BlindSendMail (unsigned long aSession, nsIMsgCompFields * aCompFields) ;
+ static nsresult ShowComposerWindow (unsigned long aSession, nsIMsgCompFields * aCompFields) ;
+ static nsresult PopulateCompFields(lpnsMapiMessage aMessage, nsIMsgCompFields * aCompFields) ;
+ static nsresult PopulateCompFieldsWithConversion(lpnsMapiMessage aMessage,
+ nsIMsgCompFields * aCompFields) ;
+ static nsresult PopulateCompFieldsForSendDocs(nsIMsgCompFields * aCompFields,
+ ULONG aFlags, LPTSTR aDelimChar, LPTSTR aFilePaths) ;
+ static nsresult HandleAttachments (nsIMsgCompFields * aCompFields, int32_t aFileCount,
+ lpnsMapiFileDesc aFiles, BOOL aIsUnicode) ;
+ static void CleanUp();
+
+ static bool isMapiService;
+};
+
+#endif // MSG_MAPI_HOOK_H_
diff --git a/mailnews/mapi/mapihook/src/msgMapiImp.cpp b/mailnews/mapi/mapihook/src/msgMapiImp.cpp
new file mode 100644
index 000000000..9139d68c6
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiImp.cpp
@@ -0,0 +1,878 @@
+/* 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/. */
+
+#include <mapidefs.h>
+#include <mapi.h>
+#include "msgMapi.h"
+#include "msgMapiImp.h"
+#include "msgMapiFactory.h"
+#include "msgMapiMain.h"
+
+#include "nsIMsgCompFields.h"
+#include "msgMapiHook.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgDatabase.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgHdr.h"
+#include "MailNewsTypes.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgImapMailFolder.h"
+#include <time.h>
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsNetCID.h"
+#include "nsMsgMessageFlags.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "mozilla/Logging.h"
+
+using namespace mozilla::mailnews;
+
+PRLogModuleInfo *MAPI;
+
+CMapiImp::CMapiImp()
+: m_cRef(1)
+{
+ m_Lock = PR_NewLock();
+ if (!MAPI)
+ MAPI = PR_NewLogModule("MAPI");
+}
+
+CMapiImp::~CMapiImp()
+{
+ if (m_Lock)
+ PR_DestroyLock(m_Lock);
+}
+
+STDMETHODIMP CMapiImp::QueryInterface(const IID& aIid, void** aPpv)
+{
+ if (aIid == IID_IUnknown)
+ {
+ *aPpv = static_cast<nsIMapi*>(this);
+ }
+ else if (aIid == IID_nsIMapi)
+ {
+ *aPpv = static_cast<nsIMapi*>(this);
+ }
+ else
+ {
+ *aPpv = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ reinterpret_cast<IUnknown*>(*aPpv)->AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) CMapiImp::AddRef()
+{
+ return ++m_cRef;
+}
+
+STDMETHODIMP_(ULONG) CMapiImp::Release()
+{
+ int32_t temp = --m_cRef;
+ if (m_cRef == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return temp;
+}
+
+STDMETHODIMP CMapiImp::IsValid()
+{
+ return S_OK;
+}
+
+STDMETHODIMP CMapiImp::IsValidSession(unsigned long aSession)
+{
+ nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
+ if (pConfig && pConfig->IsSessionValid(aSession))
+ return S_OK;
+
+ return E_FAIL;
+}
+
+STDMETHODIMP CMapiImp::Initialize()
+{
+ HRESULT hr = E_FAIL;
+
+ if (!m_Lock)
+ return E_FAIL;
+
+ PR_Lock(m_Lock);
+
+ // Initialize MAPI Configuration
+
+ nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
+ if (pConfig != nullptr)
+ hr = S_OK;
+
+ PR_Unlock(m_Lock);
+
+ return hr;
+}
+
+STDMETHODIMP CMapiImp::Login(unsigned long aUIArg, LOGIN_PW_TYPE aLogin, LOGIN_PW_TYPE aPassWord,
+ unsigned long aFlags, unsigned long *aSessionId)
+{
+ HRESULT hr = E_FAIL;
+ bool bNewSession = false;
+ nsCString id_key;
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::Login using flags %d\n", aFlags));
+ if (aFlags & MAPI_NEW_SESSION)
+ bNewSession = true;
+
+ // Check For Profile Name
+ if (aLogin != nullptr && aLogin[0] != '\0')
+ {
+ if (!nsMapiHook::VerifyUserName(nsString(aLogin), id_key))
+ {
+ *aSessionId = MAPI_E_LOGIN_FAILURE;
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::Login failed for username %s\n", aLogin));
+ NS_ASSERTION(false, "failed verifying user name");
+ return hr;
+ }
+ }
+ else
+ {
+ // get default account
+ nsresult rv;
+ nsCOMPtr <nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
+ nsCOMPtr <nsIMsgAccount> account;
+ nsCOMPtr <nsIMsgIdentity> identity;
+ rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
+ rv = account->GetDefaultIdentity(getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
+ if (!identity)
+ return MAPI_E_LOGIN_FAILURE;
+ identity->GetKey(id_key);
+ }
+
+ // finally register(create) the session.
+ uint32_t nSession_Id;
+ int16_t nResult = 0;
+
+ nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
+ if (pConfig != nullptr)
+ nResult = pConfig->RegisterSession(aUIArg, wwc(aLogin), wwc(aPassWord),
+ (aFlags & MAPI_FORCE_DOWNLOAD), bNewSession,
+ &nSession_Id, id_key.get());
+ switch (nResult)
+ {
+ case -1 :
+ {
+ *aSessionId = MAPI_E_TOO_MANY_SESSIONS;
+ return hr;
+ }
+ case 0 :
+ {
+ *aSessionId = MAPI_E_INSUFFICIENT_MEMORY;
+ return hr;
+ }
+ default :
+ {
+ *aSessionId = nSession_Id;
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::Login succeeded\n"));
+ break;
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP CMapiImp::SendMail( unsigned long aSession, lpnsMapiMessage aMessage,
+ short aRecipCount, lpnsMapiRecipDesc aRecips , short aFileCount, lpnsMapiFileDesc aFiles ,
+ unsigned long aFlags, unsigned long aReserved)
+{
+ nsresult rv = NS_OK ;
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendMail using flags %d\n", aFlags));
+ // Assign the pointers in the aMessage struct to the array of Recips and Files
+ // received here from MS COM. These are used in BlindSendMail and ShowCompWin fns
+ aMessage->lpRecips = aRecips ;
+ aMessage->lpFiles = aFiles ;
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendMail flags=%x subject: %s sender: %s\n",
+ aFlags, (char *) aMessage->lpszSubject, (aMessage->lpOriginator) ? aMessage->lpOriginator->lpszAddress : ""));
+
+ /** create nsIMsgCompFields obj and populate it **/
+ nsCOMPtr<nsIMsgCompFields> pCompFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ;
+ if (NS_FAILED(rv) || (!pCompFields) ) return MAPI_E_INSUFFICIENT_MEMORY ;
+
+ if (aFlags & MAPI_UNICODE)
+ rv = nsMapiHook::PopulateCompFields(aMessage, pCompFields) ;
+ else
+ rv = nsMapiHook::PopulateCompFieldsWithConversion(aMessage, pCompFields) ;
+
+ if (NS_SUCCEEDED (rv))
+ {
+ // see flag to see if UI needs to be brought up
+ if (!(aFlags & MAPI_DIALOG))
+ {
+ rv = nsMapiHook::BlindSendMail(aSession, pCompFields);
+ }
+ else
+ {
+ rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
+ }
+ }
+
+ return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
+}
+
+
+STDMETHODIMP CMapiImp::SendDocuments( unsigned long aSession, LPTSTR aDelimChar,
+ LPTSTR aFilePaths, LPTSTR aFileNames, ULONG aFlags)
+{
+ nsresult rv = NS_OK ;
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendDocument using flags %d\n", aFlags));
+ /** create nsIMsgCompFields obj and populate it **/
+ nsCOMPtr<nsIMsgCompFields> pCompFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ;
+ if (NS_FAILED(rv) || (!pCompFields) ) return MAPI_E_INSUFFICIENT_MEMORY ;
+
+ if (aFilePaths)
+ {
+ rv = nsMapiHook::PopulateCompFieldsForSendDocs(pCompFields, aFlags, aDelimChar, aFilePaths) ;
+ }
+
+ if (NS_SUCCEEDED (rv))
+ rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
+ else
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendDocument error rv = %lx, paths = %s names = %s\n", rv, aFilePaths, aFileNames));
+
+ return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
+}
+
+nsresult CMapiImp::GetDefaultInbox(nsIMsgFolder **inboxFolder)
+{
+ // get default account
+ nsresult rv;
+ nsCOMPtr <nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIMsgAccount> account;
+ rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // get incoming server
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ rv = account->GetIncomingServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString type;
+ rv = server->GetType(type);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // we only care about imap and pop3
+ if (type.EqualsLiteral("imap") || type.EqualsLiteral("pop3"))
+ {
+ // imap and pop3 account should have an Inbox
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ rv = server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!rootMsgFolder)
+ return NS_ERROR_FAILURE;
+
+ rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox, inboxFolder);
+ if (!*inboxFolder)
+ return NS_ERROR_FAILURE;
+
+ }
+ return NS_OK;
+}
+
+//*****************************************************************************
+// Encapsulate the XP DB stuff required to enumerate messages
+
+class MsgMapiListContext
+{
+public:
+ MsgMapiListContext () {}
+ ~MsgMapiListContext ();
+
+ nsresult OpenDatabase (nsIMsgFolder *folder);
+
+ nsMsgKey GetNext ();
+ nsresult MarkRead (nsMsgKey key, bool read);
+
+ lpnsMapiMessage GetMessage (nsMsgKey, unsigned long flFlags);
+ bool IsIMAPHost(void);
+ bool DeleteMessage(nsMsgKey key);
+
+protected:
+
+ char *ConvertDateToMapiFormat (time_t);
+ char *ConvertBodyToMapiFormat (nsIMsgDBHdr *hdr);
+ void ConvertRecipientsToMapiFormat(const nsCOMArray<msgIAddressObject> &ourRecips,
+ lpnsMapiRecipDesc mapiRecips,
+ int mapiRecipClass);
+
+ nsCOMPtr <nsIMsgFolder> m_folder;
+ nsCOMPtr <nsIMsgDatabase> m_db;
+ nsCOMPtr <nsISimpleEnumerator> m_msgEnumerator;
+};
+
+
+LONG CMapiImp::InitContext(unsigned long session, MsgMapiListContext **listContext)
+{
+ nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
+ if (!pMapiConfig)
+ return MAPI_E_FAILURE ; // get the singelton obj
+ *listContext = (MsgMapiListContext *) pMapiConfig->GetMapiListContext(session);
+ // This is the first message
+ if (!*listContext)
+ {
+ nsCOMPtr <nsIMsgFolder> inboxFolder;
+ nsresult rv = GetDefaultInbox(getter_AddRefs(inboxFolder));
+ if (NS_FAILED(rv))
+ {
+ NS_ASSERTION(false, "in init context, no inbox");
+ return(MAPI_E_NO_MESSAGES);
+ }
+
+ *listContext = new MsgMapiListContext;
+ if (!*listContext)
+ return MAPI_E_INSUFFICIENT_MEMORY;
+
+ rv = (*listContext)->OpenDatabase(inboxFolder);
+ if (NS_FAILED(rv))
+ {
+ pMapiConfig->SetMapiListContext(session, NULL);
+ delete *listContext;
+ NS_ASSERTION(false, "in init context, unable to open db");
+ return MAPI_E_NO_MESSAGES;
+ }
+ else
+ pMapiConfig->SetMapiListContext(session, *listContext);
+ }
+ return SUCCESS_SUCCESS;
+}
+
+STDMETHODIMP CMapiImp::FindNext(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageType,
+ LPTSTR lpszSeedMessageID, unsigned long flFlags, unsigned long ulReserved,
+ unsigned char lpszMessageID[64])
+
+{
+ //
+ // If this is true, then this is the first call to this FindNext function
+ // and we should start the enumeration operation.
+ //
+
+ *lpszMessageID = '\0';
+ nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
+ if (!pMapiConfig)
+ {
+ NS_ASSERTION(false, "failed to get config in findnext");
+ return MAPI_E_FAILURE ; // get the singelton obj
+ }
+ MsgMapiListContext *listContext;
+ LONG ret = InitContext(aSession, &listContext);
+ if (ret != SUCCESS_SUCCESS)
+ {
+ NS_ASSERTION(false, "init context failed");
+ return ret;
+ }
+ NS_ASSERTION(listContext, "initContext returned null context");
+ if (listContext)
+ {
+// NS_ASSERTION(false, "find next init context succeeded");
+ nsMsgKey nextKey = listContext->GetNext();
+ if (nextKey == nsMsgKey_None)
+ {
+ pMapiConfig->SetMapiListContext(aSession, NULL);
+ delete listContext;
+ return(MAPI_E_NO_MESSAGES);
+ }
+
+// TRACE("MAPI: ProcessMAPIFindNext() Found message id = %d\n", nextKey);
+
+ sprintf((char *) lpszMessageID, "%d", nextKey);
+ }
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::FindNext returning key %s\n", (char *) lpszMessageID));
+ return(SUCCESS_SUCCESS);
+}
+
+STDMETHODIMP CMapiImp::ReadMail(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
+ unsigned long flFlags, unsigned long ulReserved, lpnsMapiMessage *lppMessage)
+{
+ nsresult irv;
+ nsAutoCString keyString((char *) lpszMessageID);
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::ReadMail asking for key %s\n", (char *) lpszMessageID));
+ nsMsgKey msgKey = keyString.ToInteger(&irv);
+ if (NS_FAILED(irv))
+ {
+ NS_ASSERTION(false, "invalid lpszMessageID");
+ return MAPI_E_INVALID_MESSAGE;
+ }
+ MsgMapiListContext *listContext;
+ LONG ret = InitContext(aSession, &listContext);
+ if (ret != SUCCESS_SUCCESS)
+ {
+ NS_ASSERTION(false, "init context failed in ReadMail");
+ return ret;
+ }
+ *lppMessage = listContext->GetMessage (msgKey, flFlags);
+ NS_ASSERTION(*lppMessage, "get message failed");
+
+ return (*lppMessage) ? SUCCESS_SUCCESS : E_FAIL;
+}
+
+
+STDMETHODIMP CMapiImp::DeleteMail(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
+ unsigned long flFlags, unsigned long ulReserved)
+{
+ nsresult irv;
+ nsAutoCString keyString((char *) lpszMessageID);
+ nsMsgKey msgKey = keyString.ToInteger(&irv);
+ // XXX Why do we return success on failure?
+ if (NS_FAILED(irv))
+ return SUCCESS_SUCCESS;
+ MsgMapiListContext *listContext;
+ LONG ret = InitContext(aSession, &listContext);
+ if (ret != SUCCESS_SUCCESS)
+ return ret;
+ return (listContext->DeleteMessage(msgKey)) ? SUCCESS_SUCCESS : MAPI_E_INVALID_MESSAGE;
+}
+
+STDMETHODIMP CMapiImp::SaveMail(unsigned long aSession, unsigned long ulUIParam, lpnsMapiMessage lppMessage,
+ unsigned long flFlags, unsigned long ulReserved, LPTSTR lpszMessageID)
+{
+ MsgMapiListContext *listContext;
+ LONG ret = InitContext(aSession, &listContext);
+ if (ret != SUCCESS_SUCCESS)
+ return ret;
+ return S_OK;
+}
+
+
+STDMETHODIMP CMapiImp::Logoff (unsigned long aSession)
+{
+ nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
+
+ if (pConfig->UnRegisterSession((uint32_t)aSession))
+ return S_OK;
+
+ return E_FAIL;
+}
+
+STDMETHODIMP CMapiImp::CleanUp()
+{
+ nsMapiHook::CleanUp();
+ return S_OK;
+}
+
+
+#define MAX_NAME_LEN 256
+
+
+MsgMapiListContext::~MsgMapiListContext ()
+{
+ if (m_db)
+ m_db->Close(false);
+}
+
+
+nsresult MsgMapiListContext::OpenDatabase (nsIMsgFolder *folder)
+{
+ nsresult dbErr = NS_ERROR_FAILURE;
+ if (folder)
+ {
+ m_folder = folder;
+ dbErr = folder->GetMsgDatabase(getter_AddRefs(m_db));
+ if (m_db)
+ dbErr = m_db->EnumerateMessages(getter_AddRefs(m_msgEnumerator));
+ }
+ return dbErr;
+}
+
+bool
+MsgMapiListContext::IsIMAPHost(void)
+{
+ if (!m_folder)
+ return FALSE;
+ nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
+
+ return imapFolder != nullptr;
+}
+
+nsMsgKey MsgMapiListContext::GetNext ()
+{
+ nsMsgKey key = nsMsgKey_None;
+ bool keepTrying = TRUE;
+
+// NS_ASSERTION (m_msgEnumerator && m_db, "need enumerator and db");
+ if (m_msgEnumerator && m_db)
+ {
+
+ do
+ {
+ keepTrying = FALSE;
+ nsCOMPtr <nsISupports> hdrISupports;
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ if (NS_SUCCEEDED(m_msgEnumerator->GetNext(getter_AddRefs(hdrISupports))) && hdrISupports)
+ {
+ msgHdr = do_QueryInterface(hdrISupports);
+ msgHdr->GetMessageKey(&key);
+
+ // Check here for IMAP message...if not, just return...
+ if (!IsIMAPHost())
+ return key;
+
+ // If this is an IMAP message, we have to make sure we have a valid
+ // body to work with.
+ uint32_t flags = 0;
+
+ (void) msgHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::Offline)
+ return key;
+
+ // Ok, if we get here, we have an IMAP message without a body!
+ // We need to keep trying by calling the GetNext member recursively...
+ keepTrying = TRUE;
+ }
+ } while (keepTrying);
+ }
+
+ return key;
+}
+
+
+nsresult MsgMapiListContext::MarkRead (nsMsgKey key, bool read)
+{
+ nsresult err = NS_ERROR_FAILURE;
+ NS_ASSERTION(m_db, "no db");
+ if (m_db)
+ err = m_db->MarkRead (key, read, nullptr);
+ return err;
+}
+
+
+lpnsMapiMessage MsgMapiListContext::GetMessage (nsMsgKey key, unsigned long flFlags)
+{
+ lpnsMapiMessage message = (lpnsMapiMessage) CoTaskMemAlloc (sizeof(nsMapiMessage));
+ memset(message, 0, sizeof(nsMapiMessage));
+ if (message)
+ {
+ nsCString subject;
+ nsCString author;
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+
+ nsresult rv = m_db->GetMsgHdrForKey (key, getter_AddRefs(msgHdr));
+ if (msgHdr)
+ {
+ msgHdr->GetSubject (getter_Copies(subject));
+ message->lpszSubject = (char *) CoTaskMemAlloc(subject.Length() + 1);
+ strcpy((char *) message->lpszSubject, subject.get());
+ uint32_t date;
+ (void) msgHdr->GetDateInSeconds(&date);
+ message->lpszDateReceived = ConvertDateToMapiFormat (date);
+
+ // Pull out the flags info
+ // anything to do with MAPI_SENT? Since we're only reading the Inbox, I guess not
+ uint32_t ourFlags;
+ (void) msgHdr->GetFlags(&ourFlags);
+ if (!(ourFlags & nsMsgMessageFlags::Read))
+ message->flFlags |= MAPI_UNREAD;
+ if (ourFlags & (nsMsgMessageFlags::MDNReportNeeded | nsMsgMessageFlags::MDNReportSent))
+ message->flFlags |= MAPI_RECEIPT_REQUESTED;
+
+ // Pull out the author/originator info
+ message->lpOriginator = (lpnsMapiRecipDesc) CoTaskMemAlloc (sizeof(nsMapiRecipDesc));
+ memset(message->lpOriginator, 0, sizeof(nsMapiRecipDesc));
+ if (message->lpOriginator)
+ {
+ msgHdr->GetAuthor (getter_Copies(author));
+ ConvertRecipientsToMapiFormat(EncodedHeader(author),
+ message->lpOriginator, MAPI_ORIG);
+ }
+ // Pull out the To/CC info
+ nsCString recipients, ccList;
+ msgHdr->GetRecipients(getter_Copies(recipients));
+ msgHdr->GetCcList(getter_Copies(ccList));
+
+ nsCOMArray<msgIAddressObject> parsedToRecips = EncodedHeader(recipients);
+ nsCOMArray<msgIAddressObject> parsedCCRecips = EncodedHeader(ccList);
+ uint32_t numToRecips = parsedToRecips.Length();
+ uint32_t numCCRecips = parsedCCRecips.Length();
+
+ message->lpRecips = (lpnsMapiRecipDesc) CoTaskMemAlloc ((numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
+ memset(message->lpRecips, 0, (numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
+ if (message->lpRecips)
+ {
+ ConvertRecipientsToMapiFormat(parsedToRecips, message->lpRecips,
+ MAPI_TO);
+ ConvertRecipientsToMapiFormat(parsedCCRecips,
+ &message->lpRecips[numToRecips], MAPI_CC);
+ }
+
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("MsgMapiListContext::GetMessage flags=%x subject %s date %s sender %s\n",
+ flFlags, (char *) message->lpszSubject,(char *) message->lpszDateReceived, author.get()) );
+
+ // Convert any body text that we have locally
+ if (!(flFlags & MAPI_ENVELOPE_ONLY))
+ message->lpszNoteText = (char *) ConvertBodyToMapiFormat (msgHdr);
+
+ }
+ if (! (flFlags & (MAPI_PEEK | MAPI_ENVELOPE_ONLY)))
+ m_db->MarkRead(key, true, nullptr);
+ }
+ return message;
+}
+
+
+char *MsgMapiListContext::ConvertDateToMapiFormat (time_t ourTime)
+{
+ char *date = (char*) CoTaskMemAlloc(32);
+ if (date)
+ {
+ // MAPI time format is YYYY/MM/DD HH:MM
+ // Note that we're not using XP_StrfTime because that localizes the time format,
+ // and the way I read the MAPI spec is that their format is canonical, not localized.
+ struct tm *local = localtime (&ourTime);
+ if (local)
+ strftime (date, 32, "%Y/%m/%d %I:%M", local); //use %H if hours should be 24 hour format
+ }
+ return date;
+}
+
+
+void MsgMapiListContext::ConvertRecipientsToMapiFormat(
+ const nsCOMArray<msgIAddressObject> &recipients,
+ lpnsMapiRecipDesc mapiRecips, int mapiRecipClass)
+{
+ nsTArray<nsCString> names, addresses;
+ ExtractAllAddresses(recipients, UTF16ArrayAdapter<>(names),
+ UTF16ArrayAdapter<>(addresses));
+
+ size_t numAddresses = names.Length();
+ for (size_t i = 0; i < numAddresses; i++)
+ {
+ if (!names[i].IsEmpty())
+ {
+ mapiRecips[i].lpszName = (char *) CoTaskMemAlloc(names[i].Length() + 1);
+ if (mapiRecips[i].lpszName)
+ strcpy((char *)mapiRecips[i].lpszName, names[i].get());
+ }
+ if (!addresses[i].IsEmpty())
+ {
+ mapiRecips[i].lpszName = (char *) CoTaskMemAlloc(addresses[i].Length() + 1);
+ if (mapiRecips[i].lpszName)
+ strcpy((char *)mapiRecips[i].lpszName, addresses[i].get());
+ }
+ mapiRecips[i].ulRecipClass = mapiRecipClass;
+ }
+}
+
+
+char *MsgMapiListContext::ConvertBodyToMapiFormat (nsIMsgDBHdr *hdr)
+{
+ const int kBufLen = 64000; // I guess we only return the first 64K of a message.
+#define EMPTY_MESSAGE_LINE(buf) (buf[0] == '\r' || buf[0] == '\n' || buf[0] == '\0')
+
+ nsCOMPtr <nsIMsgFolder> folder;
+ hdr->GetFolder(getter_AddRefs(folder));
+ if (!folder)
+ return nullptr;
+
+ nsCOMPtr <nsIInputStream> inputStream;
+ nsCOMPtr <nsIFile> localFile;
+ folder->GetFilePath(getter_AddRefs(localFile));
+
+ nsresult rv;
+ nsCOMPtr<nsIFileInputStream> fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = fileStream->Init(localFile, PR_RDONLY, 0664, false); //just have to read the messages
+ inputStream = do_QueryInterface(fileStream);
+
+ if (inputStream)
+ {
+ nsCOMPtr <nsILineInputStream> fileLineStream = do_QueryInterface(inputStream);
+ if (!fileLineStream)
+ return nullptr;
+ // ### really want to skip past headers...
+ uint64_t messageOffset;
+ uint32_t lineCount;
+ hdr->GetMessageOffset(&messageOffset);
+ hdr->GetLineCount(&lineCount);
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream);
+ seekableStream->Seek(PR_SEEK_SET, messageOffset);
+ bool hasMore = true;
+ nsAutoCString curLine;
+ nsresult rv = NS_OK;
+ while (hasMore) // advance past message headers
+ {
+ nsresult rv = fileLineStream->ReadLine(curLine, &hasMore);
+ if (NS_FAILED(rv) || EMPTY_MESSAGE_LINE(curLine))
+ break;
+ }
+ uint32_t msgSize;
+ hdr->GetMessageSize(&msgSize);
+ if (msgSize > kBufLen)
+ msgSize = kBufLen - 1;
+ // this is too big, since it includes the msg hdr size...oh well
+ char *body = (char*) CoTaskMemAlloc (msgSize + 1);
+
+ if (!body)
+ return nullptr;
+ int32_t bytesCopied = 0;
+ for (hasMore = TRUE; lineCount > 0 && hasMore && NS_SUCCEEDED(rv); lineCount--)
+ {
+ rv = fileLineStream->ReadLine(curLine, &hasMore);
+ if (NS_FAILED(rv))
+ break;
+ curLine.Append(CRLF);
+ // make sure we have room left
+ if (bytesCopied + curLine.Length() < msgSize)
+ {
+ strcpy(body + bytesCopied, curLine.get());
+ bytesCopied += curLine.Length();
+ }
+ }
+ MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("ConvertBodyToMapiFormat size=%x allocated size %x body = %100.100s\n",
+ bytesCopied, msgSize + 1, (char *) body) );
+ body[bytesCopied] = '\0'; // rhp - fix last line garbage...
+ return body;
+ }
+ return nullptr;
+}
+
+
+//*****************************************************************************
+// MSGMAPI API implementation
+
+
+
+static void msg_FreeMAPIFile(lpMapiFileDesc f)
+{
+ if (f)
+ {
+ CoTaskMemFree(f->lpszPathName);
+ CoTaskMemFree(f->lpszFileName);
+ }
+}
+
+static void msg_FreeMAPIRecipient(lpMapiRecipDesc rd)
+{
+ if (rd)
+ {
+ if (rd->lpszName)
+ CoTaskMemFree(rd->lpszName);
+ if (rd->lpszAddress)
+ CoTaskMemFree(rd->lpszAddress);
+ // CoTaskMemFree(rd->lpEntryID);
+ }
+}
+
+extern "C" void MSG_FreeMapiMessage (lpMapiMessage msg)
+{
+ ULONG i;
+
+ if (msg)
+ {
+ CoTaskMemFree(msg->lpszSubject);
+ CoTaskMemFree(msg->lpszNoteText);
+ CoTaskMemFree(msg->lpszMessageType);
+ CoTaskMemFree(msg->lpszDateReceived);
+ CoTaskMemFree(msg->lpszConversationID);
+
+ if (msg->lpOriginator)
+ msg_FreeMAPIRecipient(msg->lpOriginator);
+
+ for (i=0; i<msg->nRecipCount; i++)
+ if (&(msg->lpRecips[i]) != nullptr)
+ msg_FreeMAPIRecipient(&(msg->lpRecips[i]));
+
+ CoTaskMemFree(msg->lpRecips);
+
+ for (i=0; i<msg->nFileCount; i++)
+ if (&(msg->lpFiles[i]) != nullptr)
+ msg_FreeMAPIFile(&(msg->lpFiles[i]));
+
+ CoTaskMemFree(msg->lpFiles);
+
+ CoTaskMemFree(msg);
+ }
+}
+
+
+extern "C" bool MsgMarkMapiMessageRead (nsIMsgFolder *folder, nsMsgKey key, bool read)
+{
+ bool success = FALSE;
+ MsgMapiListContext *context = new MsgMapiListContext();
+ if (context)
+ {
+ if (NS_SUCCEEDED(context->OpenDatabase(folder)))
+ {
+ if (NS_SUCCEEDED(context->MarkRead (key, read)))
+ success = TRUE;
+ }
+ delete context;
+ }
+ return success;
+}
+
+bool
+MsgMapiListContext::DeleteMessage(nsMsgKey key)
+{
+ if (!m_db)
+ return FALSE;
+
+ if ( !IsIMAPHost() )
+ {
+ return NS_SUCCEEDED((m_db->DeleteMessages(1, &key, nullptr)));
+ }
+#if 0
+ else if ( m_folder->GetIMAPFolderInfoMail() )
+ {
+ AutoTArray<nsMsgKey, 1> messageKeys;
+ messageKeys.AppendElement(key);
+
+ (m_folder->GetIMAPFolderInfoMail())->DeleteSpecifiedMessages(pane, messageKeys, nsMsgKey_None);
+ m_db->DeleteMessage(key, nullptr, FALSE);
+ return TRUE;
+ }
+#endif
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* Return TRUE on success, FALSE on failure */
+extern "C" bool MSG_DeleteMapiMessage(nsIMsgFolder *folder, nsMsgKey key)
+{
+ bool success = FALSE;
+ MsgMapiListContext *context = new MsgMapiListContext();
+ if (context)
+ {
+ if (NS_SUCCEEDED(context->OpenDatabase(folder)))
+ {
+ success = context->DeleteMessage(key);
+ }
+
+ delete context;
+ }
+
+ return success;
+}
+
diff --git a/mailnews/mapi/mapihook/src/msgMapiImp.h b/mailnews/mapi/mapihook/src/msgMapiImp.h
new file mode 100644
index 000000000..875e9c6cf
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiImp.h
@@ -0,0 +1,77 @@
+/* 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/. */
+
+#ifndef MSG_MAPI_IMP_H
+#define MSG_MAPI_IMP_H
+
+#include <windows.h>
+#include <mapi.h>
+#include "msgMapi.h"
+#include "nspr.h"
+#include "nscore.h"
+#include "nsISupportsImpl.h" // ThreadSafeAutoRefCnt
+
+class nsIMsgFolder;
+class MsgMapiListContext;
+
+const CLSID CLSID_CMapiImp = {0x29f458be, 0x8866, 0x11d5, {0xa3, 0xdd, 0x0, 0xb0, 0xd0, 0xf3, 0xba, 0xa7}};
+
+// this class implements the MS COM interface nsIMapi that provides the methods
+// called by mapi32.dll to perform the mail operations as specified by MAPI.
+// These class methods in turn use the Mozilla Mail XPCOM interfaces to do so.
+class CMapiImp : public nsIMapi
+{
+
+public :
+
+ // IUnknown
+
+ STDMETHODIMP QueryInterface(const IID& aIid, void** aPpv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // Interface INsMapi
+
+ STDMETHODIMP Login(unsigned long aUIArg, LOGIN_PW_TYPE aLogin,
+ LOGIN_PW_TYPE aPassWord, unsigned long aFlags,
+ unsigned long *aSessionId);
+
+ STDMETHODIMP SendMail( unsigned long aSession, lpnsMapiMessage aMessage,
+ short aRecipCount, lpnsMapiRecipDesc aRecips ,
+ short aFileCount, lpnsMapiFileDesc aFiles ,
+ unsigned long aFlags, unsigned long aReserved) ;
+
+ STDMETHODIMP SendDocuments( unsigned long aSession, LPTSTR aDelimChar,
+ LPTSTR aFilePaths, LPTSTR aFileNames, ULONG aFlags);
+
+ STDMETHODIMP FindNext( unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageType,
+ LPTSTR lpszSeedMessageID, unsigned long flFlags, unsigned long ulReserved,
+ unsigned char lpszMessageID[64] );
+
+ STDMETHODIMP ReadMail(unsigned long lhSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
+ unsigned long flFlags, unsigned long ulReserved, lpnsMapiMessage *lppMessage);
+ STDMETHODIMP DeleteMail(unsigned long lhSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
+ unsigned long flFlags, unsigned long ulReserved);
+ STDMETHODIMP SaveMail(unsigned long lhSession, unsigned long ulUIParam, lpnsMapiMessage lppMessage,
+ unsigned long flFlags, unsigned long ulReserved, LPTSTR lpszMessageID);
+
+ STDMETHODIMP Initialize();
+ STDMETHODIMP IsValid();
+ STDMETHODIMP IsValidSession(unsigned long aSession);
+
+ STDMETHODIMP Logoff (unsigned long aSession);
+ STDMETHODIMP CleanUp();
+
+ CMapiImp();
+ ~CMapiImp();
+
+ LONG InitContext(unsigned long session, MsgMapiListContext **listContext);
+ nsresult GetDefaultInbox(nsIMsgFolder **inboxFolder);
+
+private :
+ PRLock *m_Lock;
+ mozilla::ThreadSafeAutoRefCnt m_cRef;
+};
+
+#endif // MSG_MAPI_IMP_H
diff --git a/mailnews/mapi/mapihook/src/msgMapiMain.cpp b/mailnews/mapi/mapihook/src/msgMapiMain.cpp
new file mode 100644
index 000000000..adb5cc14c
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiMain.cpp
@@ -0,0 +1,306 @@
+/* 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/. */
+
+#include <mapidefs.h>
+#include <mapi.h>
+
+#include "msgCore.h"
+#include "nsComposeStrings.h"
+#include "msgMapiMain.h"
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+
+nsMAPIConfiguration *nsMAPIConfiguration::m_pSelfRef = nullptr;
+uint32_t nsMAPIConfiguration::session_generator = 0;
+uint32_t nsMAPIConfiguration::sessionCount = 0;
+
+nsMAPIConfiguration *nsMAPIConfiguration::GetMAPIConfiguration()
+{
+ if (m_pSelfRef == nullptr)
+ m_pSelfRef = new nsMAPIConfiguration();
+
+ return m_pSelfRef;
+}
+
+nsMAPIConfiguration::nsMAPIConfiguration()
+: m_nMaxSessions(MAX_SESSIONS)
+{
+ m_Lock = PR_NewLock();
+}
+
+nsMAPIConfiguration::~nsMAPIConfiguration()
+{
+ if (m_Lock)
+ PR_DestroyLock(m_Lock);
+}
+
+void nsMAPIConfiguration::OpenConfiguration()
+{
+ // No. of max. sessions is set to MAX_SESSIONS. In future
+ // if it is decided to have configuration (registry)
+ // parameter, this function can be used to set the
+ // max sessions;
+
+ return;
+}
+
+int16_t nsMAPIConfiguration::RegisterSession(uint32_t aHwnd,
+ const char16_t *aUserName, const char16_t *aPassword,
+ bool aForceDownLoad, bool aNewSession,
+ uint32_t *aSession, const char *aIdKey)
+{
+ int16_t nResult = 0;
+ uint32_t n_SessionId = 0;
+
+ PR_Lock(m_Lock);
+
+ // Check whether max sessions is exceeded
+
+ if (sessionCount >= m_nMaxSessions)
+ {
+ PR_Unlock(m_Lock);
+ return -1;
+ }
+
+ if (aUserName != nullptr && aUserName[0] != '\0')
+ m_ProfileMap.Get(nsDependentString(aUserName), &n_SessionId);
+
+ // try to share a session; if not create a session
+ if (n_SessionId > 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(n_SessionId, &pTemp);
+ if (pTemp != nullptr)
+ {
+ pTemp->IncrementSession();
+ *aSession = n_SessionId;
+ nResult = 1;
+ }
+ }
+ else if (aNewSession || n_SessionId == 0) // checking for n_SessionId is a concession
+ {
+ // create a new session; if new session is specified OR there is no session
+ nsMAPISession *pTemp = nullptr;
+ pTemp = new nsMAPISession(aHwnd, aUserName,
+ aPassword, aForceDownLoad, aIdKey);
+
+ if (pTemp != nullptr)
+ {
+ session_generator++;
+
+ // I don't think there will be (2 power 32) sessions alive
+ // in a cycle. This is an assumption
+
+ if (session_generator == 0)
+ session_generator++;
+ m_SessionMap.Put(session_generator, pTemp);
+ if (aUserName != nullptr && aUserName[0] != '\0')
+ m_ProfileMap.Put(nsDependentString(aUserName), session_generator);
+ *aSession = session_generator;
+ sessionCount++;
+ nResult = 1;
+ }
+ }
+
+ PR_Unlock(m_Lock);
+ return nResult;
+}
+
+bool nsMAPIConfiguration::UnRegisterSession(uint32_t aSessionID)
+{
+ bool bResult = false;
+
+ PR_Lock(m_Lock);
+
+ if (aSessionID != 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(aSessionID, &pTemp);
+
+ if (pTemp != nullptr)
+ {
+ if (pTemp->DecrementSession() == 0)
+ {
+ if (pTemp->m_pProfileName.get() != nullptr)
+ m_ProfileMap.Remove(pTemp->m_pProfileName);
+ m_SessionMap.Remove(aSessionID);
+ sessionCount--;
+ bResult = true;
+ }
+ }
+ }
+
+ PR_Unlock(m_Lock);
+ return bResult;
+}
+
+bool nsMAPIConfiguration::IsSessionValid(uint32_t aSessionID)
+{
+ if (aSessionID == 0)
+ return false;
+ bool retValue = false;
+ PR_Lock(m_Lock);
+ retValue = m_SessionMap.Get(aSessionID, NULL);
+ PR_Unlock(m_Lock);
+ return retValue;
+}
+
+char16_t *nsMAPIConfiguration::GetPassword(uint32_t aSessionID)
+{
+ char16_t *pResult = nullptr;
+
+ PR_Lock(m_Lock);
+
+ if (aSessionID != 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(aSessionID, &pTemp);
+
+ if (pTemp)
+ pResult = pTemp->GetPassword();
+ }
+ PR_Unlock(m_Lock);
+ return pResult;
+}
+
+void *nsMAPIConfiguration::GetMapiListContext(uint32_t aSessionID)
+{
+ void *pResult = nullptr;
+
+ PR_Lock(m_Lock);
+
+ if (aSessionID != 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(aSessionID, &pTemp);
+ if (pTemp)
+ pResult = pTemp->GetMapiListContext();
+ }
+
+ PR_Unlock(m_Lock);
+ return pResult;
+}
+
+void nsMAPIConfiguration::SetMapiListContext(uint32_t aSessionID, void *mapiListContext)
+{
+ PR_Lock(m_Lock);
+
+ if (aSessionID != 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(aSessionID, &pTemp);
+ if (pTemp)
+ pTemp->SetMapiListContext(mapiListContext);
+ }
+
+ PR_Unlock(m_Lock);
+}
+
+void nsMAPIConfiguration::GetIdKey(uint32_t aSessionID, nsCString& aKey)
+{
+ PR_Lock(m_Lock);
+ if (aSessionID != 0)
+ {
+ nsMAPISession *pTemp = nullptr;
+ m_SessionMap.Get(aSessionID, &pTemp);
+ if (pTemp)
+ pTemp->GetIdKey(aKey);
+ }
+ PR_Unlock(m_Lock);
+ return;
+}
+
+// util func
+HRESULT nsMAPIConfiguration::GetMAPIErrorFromNSError (nsresult res)
+{
+ HRESULT hr = SUCCESS_SUCCESS;
+
+ if (NS_SUCCEEDED (res)) return hr;
+
+ // if failure return the related MAPI failure code
+ switch (res)
+ {
+ case NS_MSG_NO_RECIPIENTS :
+ hr = MAPI_E_BAD_RECIPTYPE;
+ break;
+ case NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS :
+ hr = MAPI_E_INVALID_RECIPS;
+ break;
+ case NS_ERROR_SMTP_AUTH_FAILURE :
+ case NS_ERROR_SMTP_AUTH_GSSAPI :
+ case NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED :
+ case NS_ERROR_SMTP_AUTH_NOT_SUPPORTED :
+ case NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL :
+ case NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL :
+ case NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT :
+ hr = MAPI_E_LOGIN_FAILURE;
+ break;
+ case NS_MSG_UNABLE_TO_OPEN_FILE :
+ case NS_MSG_UNABLE_TO_OPEN_TMP_FILE :
+ case NS_MSG_COULDNT_OPEN_FCC_FOLDER :
+ case NS_ERROR_FILE_INVALID_PATH :
+ hr = MAPI_E_ATTACHMENT_OPEN_FAILURE;
+ break;
+ case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST :
+ hr = MAPI_E_ATTACHMENT_NOT_FOUND;
+ break;
+ case NS_MSG_CANCELLING :
+ hr = MAPI_E_USER_ABORT;
+ break;
+ case NS_MSG_ERROR_WRITING_FILE :
+ case NS_MSG_UNABLE_TO_SAVE_TEMPLATE :
+ case NS_MSG_UNABLE_TO_SAVE_DRAFT :
+ hr = MAPI_E_ATTACHMENT_WRITE_FAILURE;
+ break;
+ default:
+ hr = MAPI_E_FAILURE;
+ break;
+ }
+
+ return hr;
+}
+
+
+nsMAPISession::nsMAPISession(uint32_t aHwnd, const char16_t *aUserName,
+ const char16_t *aPassword,
+ bool aForceDownLoad, const char *aKey)
+: m_bIsForcedDownLoad(aForceDownLoad),
+ m_hAppHandle(aHwnd),
+ m_nShared(1),
+ m_pIdKey(aKey)
+{
+ m_listContext = NULL;
+ m_pProfileName.Assign(aUserName);
+ m_pPassword.Assign(aPassword);
+}
+
+nsMAPISession::~nsMAPISession()
+{
+}
+
+uint32_t nsMAPISession::IncrementSession()
+{
+ return ++m_nShared;
+}
+
+uint32_t nsMAPISession::DecrementSession()
+{
+ return --m_nShared;
+}
+
+uint32_t nsMAPISession::GetSessionCount()
+{
+ return m_nShared;
+}
+
+char16_t *nsMAPISession::GetPassword()
+{
+ return (char16_t *)m_pPassword.get();
+}
+
+void nsMAPISession::GetIdKey(nsCString& aKey)
+{
+ aKey = m_pIdKey;
+ return;
+}
diff --git a/mailnews/mapi/mapihook/src/msgMapiMain.h b/mailnews/mapi/mapihook/src/msgMapiMain.h
new file mode 100644
index 000000000..be74c5db1
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiMain.h
@@ -0,0 +1,86 @@
+/* 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/. */
+
+#ifndef MSG_MAPI_MAIN_H_
+#define NSG_MAPI_MAIN_H_
+
+#define MAX_NAME_LEN 256
+#define MAX_PW_LEN 256
+#define MAX_SESSIONS 50
+#define MAPI_SENDCOMPLETE_EVENT "SendCompletionEvent"
+
+#define MAPI_PROPERTIES_CHROME "chrome://messenger-mapi/locale/mapi.properties"
+#define PREF_MAPI_WARN_PRIOR_TO_BLIND_SEND "mapi.blind-send.warn"
+#define PREF_MAPI_BLIND_SEND_ENABLED "mapi.blind-send.enabled"
+
+#include "nspr.h"
+#include "nsDataHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsStringGlue.h"
+
+class nsMAPISession;
+
+class nsMAPIConfiguration
+{
+private :
+
+ static uint32_t session_generator;
+ static uint32_t sessionCount;
+ static nsMAPIConfiguration *m_pSelfRef;
+ PRLock *m_Lock;
+ uint32_t m_nMaxSessions;
+
+ nsDataHashtable<nsStringHashKey, uint32_t> m_ProfileMap;
+ nsClassHashtable<nsUint32HashKey, nsMAPISession> m_SessionMap;
+ nsMAPIConfiguration();
+ ~nsMAPIConfiguration();
+
+public :
+ static nsMAPIConfiguration *GetMAPIConfiguration();
+ void OpenConfiguration();
+ int16_t RegisterSession(uint32_t aHwnd, const char16_t *aUserName, \
+ const char16_t *aPassword, bool aForceDownLoad, \
+ bool aNewSession, uint32_t *aSession, const char *aIdKey);
+ bool IsSessionValid(uint32_t aSessionID);
+ bool UnRegisterSession(uint32_t aSessionID);
+ char16_t *GetPassword(uint32_t aSessionID);
+ void GetIdKey(uint32_t aSessionID, nsCString& aKey);
+ void *GetMapiListContext(uint32_t aSessionID);
+ void SetMapiListContext(uint32_t aSessionID, void *mapiListContext);
+
+ // a util func
+ static HRESULT GetMAPIErrorFromNSError (nsresult res) ;
+};
+
+class nsMAPISession
+{
+ friend class nsMAPIConfiguration;
+
+ private :
+ bool m_bIsForcedDownLoad;
+ bool m_bApp_or_Service;
+ uint32_t m_hAppHandle;
+ uint32_t m_nShared;
+ nsCString m_pIdKey;
+ nsString m_pProfileName;
+ nsString m_pPassword;
+ int32_t m_messageIndex;
+ void *m_listContext; // used by findNext
+
+ public :
+ nsMAPISession(uint32_t aHwnd, const char16_t *aUserName, \
+ const char16_t *aPassword, \
+ bool aForceDownLoad, const char *aKey);
+ uint32_t IncrementSession();
+ uint32_t DecrementSession();
+ uint32_t GetSessionCount();
+ char16_t *nsMAPISession::GetPassword();
+ void GetIdKey(nsCString& aKey);
+ ~nsMAPISession();
+ // For enumerating Messages...
+ void SetMapiListContext( void *listContext) { m_listContext = listContext; }
+ void *GetMapiListContext( ) { return m_listContext; }
+};
+
+#endif // MSG_MAPI_MAIN_H_
diff --git a/mailnews/mapi/mapihook/src/msgMapiSupport.cpp b/mailnews/mapi/mapihook/src/msgMapiSupport.cpp
new file mode 100644
index 000000000..5c57f087d
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiSupport.cpp
@@ -0,0 +1,151 @@
+/* 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/. */
+#include "nsCOMPtr.h"
+#include "objbase.h"
+#include "nsISupports.h"
+
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "nsIAppStartupNotifier.h"
+#include "nsIServiceManager.h"
+#include "nsIComponentManager.h"
+#include "nsICategoryManager.h"
+#include "Registry.h"
+#include "msgMapiSupport.h"
+
+#include "msgMapiImp.h"
+
+/** Implementation of the nsIMapiSupport interface.
+ * Use standard implementation of nsISupports stuff.
+ */
+
+NS_IMPL_ISUPPORTS(nsMapiSupport, nsIMapiSupport, nsIObserver)
+
+NS_IMETHODIMP
+nsMapiSupport::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
+{
+ nsresult rv = NS_OK ;
+
+ if (!strcmp(aTopic, "profile-after-change"))
+ return InitializeMAPISupport();
+
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID))
+ return ShutdownMAPISupport();
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
+
+ rv = observerService->AddObserver(this,"profile-after-change", false);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ if (NS_FAILED(rv)) return rv;
+
+ return rv;
+}
+
+
+nsMapiSupport::nsMapiSupport()
+: m_dwRegister(0),
+ m_nsMapiFactory(nullptr)
+{
+}
+
+nsMapiSupport::~nsMapiSupport()
+{
+}
+
+NS_IMETHODIMP
+nsMapiSupport::InitializeMAPISupport()
+{
+ ::OleInitialize(nullptr) ;
+
+ if (m_nsMapiFactory == nullptr) // No Registering if already done. Sanity Check!!
+ {
+ m_nsMapiFactory = new CMapiFactory();
+
+ if (m_nsMapiFactory != nullptr)
+ {
+ HRESULT hr = ::CoRegisterClassObject(CLSID_CMapiImp, \
+ m_nsMapiFactory, \
+ CLSCTX_LOCAL_SERVER, \
+ REGCLS_MULTIPLEUSE, \
+ &m_dwRegister);
+
+ if (FAILED(hr))
+ {
+ m_nsMapiFactory->Release() ;
+ m_nsMapiFactory = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMapiSupport::ShutdownMAPISupport()
+{
+ if (m_dwRegister != 0)
+ ::CoRevokeClassObject(m_dwRegister);
+
+ if (m_nsMapiFactory != nullptr)
+ {
+ m_nsMapiFactory->Release();
+ m_nsMapiFactory = nullptr;
+ }
+
+ ::OleUninitialize();
+
+ return NS_OK ;
+}
+
+NS_IMETHODIMP
+nsMapiSupport::RegisterServer()
+{
+ // TODO: Figure out what kind of error propogation to pass back
+ ::RegisterServer(CLSID_CMapiImp, "Mozilla MAPI", "MozillaMapi", "MozillaMapi.1");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMapiSupport::UnRegisterServer()
+{
+ // TODO: Figure out what kind of error propogation to pass back
+ ::UnregisterServer(CLSID_CMapiImp, "MozillaMapi", "MozillaMapi.1");
+ return NS_OK;
+}
+
+NS_DEFINE_NAMED_CID(NS_IMAPISUPPORT_CID);
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMapiSupport)
+
+static const mozilla::Module::CategoryEntry kMAPICategories[] = {
+ { APPSTARTUP_CATEGORY, "Mapi Support", "service," NS_IMAPISUPPORT_CONTRACTID, },
+ { NULL }
+};
+
+const mozilla::Module::CIDEntry kMAPICIDs[] = {
+ { &kNS_IMAPISUPPORT_CID, false, NULL, nsMapiSupportConstructor },
+ { NULL }
+};
+
+const mozilla::Module::ContractIDEntry kMAPIContracts[] = {
+ { NS_IMAPISUPPORT_CONTRACTID, &kNS_IMAPISUPPORT_CID },
+ { NULL }
+};
+
+static const mozilla::Module kMAPIModule = {
+ mozilla::Module::kVersion,
+ kMAPICIDs,
+ kMAPIContracts,
+ kMAPICategories
+};
+
+NSMODULE_DEFN(msgMapiModule) = &kMAPIModule;
+
+
diff --git a/mailnews/mapi/mapihook/src/msgMapiSupport.h b/mailnews/mapi/mapihook/src/msgMapiSupport.h
new file mode 100644
index 000000000..ff7ffca9f
--- /dev/null
+++ b/mailnews/mapi/mapihook/src/msgMapiSupport.h
@@ -0,0 +1,34 @@
+/* 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/. */
+
+#ifndef MSG_MAPI_SUPPORT_H_
+#define MSG_MAPI_SUPPORT_H_
+
+#include "nsIObserver.h"
+#include "nsIMapiSupport.h"
+#include "msgMapiFactory.h"
+
+#define NS_IMAPISUPPORT_CID \
+ {0x8967fed2, 0xc8bb, 0x11d5, \
+ { 0xa3, 0xe9, 0x00, 0xb0, 0xd0, 0xf3, 0xba, 0xa7 }}
+
+class nsMapiSupport : public nsIMapiSupport,
+ public nsIObserver
+{
+ public :
+ nsMapiSupport();
+
+ // Declare all interface methods we must implement.
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIMAPISUPPORT
+
+ private :
+ ~nsMapiSupport();
+
+ DWORD m_dwRegister;
+ CMapiFactory *m_nsMapiFactory;
+};
+
+#endif // MSG_MAPI_SUPPORT_H_