Bug 1048658: Implement MAPISendMailW draft MAPISendMailW
authorMike Kaganski <mikekaganski@gmail.com>
Fri, 09 Mar 2018 18:12:47 +0300
branchMAPISendMailW
changeset 24155 1e814f5741d3c3fc342df6727ceb48c845bbe169
parent 24154 0d0988b6c3a1f2aca51d03c7d884ed7f17577e6f
push id208
push userbmo:mikekaganski@gmail.com
push dateFri, 09 Mar 2018 15:15:41 +0000
bugs1048658
Bug 1048658: Implement MAPISendMailW Since Windows 8, a new Simple MAPI fucntion is available: MAPISendMailW (https://msdn.microsoft.com/en-us/library/hh707275). It allows for sending UTF-16-encoded string to default mail provider. Currently, is an application tries to use MAPISendMailW (available in mapi32.dll), the library checks if default mail provider exports a function with that name, and if not, it internally converts data to 8-bit CP_ACP before sending to old MAPISendMail. In this case, any data that can not be encoded in active 8-bit codepage, will arrive replaced with some default replacement character, usually question mark ("?"). This has a number of problems: 1. Text may be garbled. 2. Attachments may be inaccessible. 3. Attachments may fail to save. The problem #2 is when the attachment temporary file name includes such characters. Replacing correct character in names naturally makes files not accessible. The problem #3 is when attachment temporary file is accessible, but its user-visible name is different and contains such characters. In that case, the 8-bit string encoded in CP_ACP would contain "?", which are illegal in file names on Windows. TB tries to save the file locally with the user-visible name, which would fail, and then fail to send or save the message. The patch adds MAPISendMailW exported function to MapiDll, and its implementation to CMapiImp. To do that, it introduces a new COM interface: nsIMapi2, which inherits from nsIMapi, adding the new function. To use the IIDs in the MapiDll, it links to MIDL- generated msgMapi_i.c, and thus removes the definition from MapiDll.cpp. The old interface is changed a little (nsMapiFileDesc now explicitly uses 8-bit strings, which is consistent with MS definition of MapiFileDesc structure - see MSDN documentation at https://msdn.microsoft.com/en-us/library/dd296737), but this change is ABI-compatible and doesn't change behaviour, and only replaces some casts in code with opposite casts (btw, it reduces the number of required casts :)). MozReview-Commit-ID: CS2aibAB6F9
mailnews/mapi/mapiDll/Makefile.in
mailnews/mapi/mapiDll/Mapi32.DEF
mailnews/mapi/mapiDll/MapiDll.cpp
mailnews/mapi/mapihook/build/msgMapi.idl
mailnews/mapi/mapihook/src/msgMapiHook.cpp
mailnews/mapi/mapihook/src/msgMapiHook.h
mailnews/mapi/mapihook/src/msgMapiImp.cpp
mailnews/mapi/mapihook/src/msgMapiImp.h
--- a/mailnews/mapi/mapiDll/Makefile.in
+++ b/mailnews/mapi/mapiDll/Makefile.in
@@ -1,6 +1,8 @@
 #
 # 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/.
 
 EMBED_MANIFEST_AT = 2
+
+CSRCS += ../mapihook/build/msgMapi_i.c
--- a/mailnews/mapi/mapiDll/Mapi32.DEF
+++ b/mailnews/mapi/mapiDll/Mapi32.DEF
@@ -12,10 +12,11 @@ EXPORTS
         MAPIFindNext
         MAPIReadMail
         MAPISaveMail
         MAPIDeleteMail
         MAPIAddress
         MAPIDetails
         MAPIResolveName
         MAPIFreeBuffer
+        MAPISendMailW
         GetMapiDllVersion
 
--- a/mailnews/mapi/mapiDll/MapiDll.cpp
+++ b/mailnews/mapi/mapiDll/MapiDll.cpp
@@ -16,18 +16,16 @@
 
 #define           MAX_NAME_LEN    256
 #define           MAX_PW_LEN      256
 #define           MAX_MSGINFO_LEN 512
 #define           MAX_POINTERS    32
 
 const CLSID CLSID_CMapiImp = {0x29f458be, 0x8866, 0x11d5,
                               {0xa3, 0xdd, 0x0, 0xb0, 0xd0, 0xf3, 0xba, 0xa7}};
-const IID IID_nsIMapi = {0x6EDCD38E,0x8861,0x11d5,
-                        {0xA3,0xDD,0x00,0xB0,0xD0,0xF3,0xBA,0xA7}};
 
 DWORD tId = 0;
 
 #define   MAPI_MESSAGE_TYPE     0
 #define   MAPI_RECIPIENT_TYPE   1
 
 typedef struct {
   LPVOID    lpMem;
@@ -250,16 +248,108 @@ ULONG FAR PASCAL MAPISendMail (LHANDLE l
         hr = SUCCESS_SUCCESS;
 
     if (bTempSession)
         MAPILogoff (lhSession, ulUIParam, 0,0) ;
 
     return hr ;
 }
 
+ULONG FAR PASCAL MAPISendMailW(LHANDLE lhSession, ULONG ulUIParam, nsMapiMessageW *lpMessage,
+	FLAGS flFlags, ULONG ulReserved)
+{
+	HRESULT hr = 0;
+	BOOL bTempSession = FALSE;
+	nsIMapi2* pNsMapi2 = NULL;
+	{
+		nsIMapi *pNsMapi = NULL;
+
+		if (!InitMozillaReference(&pNsMapi))
+			return MAPI_E_FAILURE;
+
+		hr = pNsMapi->QueryInterface(IID_nsIMapi2, (void**)&pNsMapi2);
+		pNsMapi->Release();
+		if (FAILED(hr) || !pNsMapi2)
+			return MAPI_E_NOT_SUPPORTED;
+	}
+
+	struct ComReleaseGuard {
+		IUnknown* mpInterface;
+		ComReleaseGuard(IUnknown* pInterface) : mpInterface(pInterface) {}
+		~ComReleaseGuard() { if (mpInterface) mpInterface->Release(); }
+	} aComReleaseGuard(pNsMapi2);
+
+	if (lpMessage->nRecipCount > MAX_RECIPS)
+		return MAPI_E_TOO_MANY_RECIPIENTS;
+
+	if (lpMessage->nFileCount > MAX_FILES)
+		return MAPI_E_TOO_MANY_FILES;
+
+	if ((!(flFlags & MAPI_DIALOG)) && (lpMessage->lpRecips == NULL))
+		return MAPI_E_UNKNOWN_RECIPIENT;
+
+	if (!lhSession || pNsMapi2->IsValidSession(lhSession) != S_OK)
+	{
+		FLAGS LoginFlag;
+		if ((flFlags & MAPI_LOGON_UI) && (flFlags & MAPI_NEW_SESSION))
+			LoginFlag = MAPI_LOGON_UI | MAPI_NEW_SESSION;
+		else if (flFlags & MAPI_LOGON_UI)
+			LoginFlag = MAPI_LOGON_UI;
+
+		hr = MAPILogon(ulUIParam, (LPTSTR)NULL, (LPTSTR)NULL, LoginFlag, 0, &lhSession);
+		if (hr != SUCCESS_SUCCESS)
+			return MAPI_E_LOGIN_FAILURE;
+		bTempSession = TRUE;
+	}
+
+	// we need to deal with null data passed in by MAPI clients, specially when MAPI_DIALOG is set.
+	// The MS COM type lib code generated by MIDL for the MS COM interfaces checks for these parameters
+	// to be non null, although null is a valid value for them here.
+	nsMapiRecipDescW * lpRecips;
+	nsMapiFileDescW * lpFiles;
+
+	nsMapiMessageW Message;
+	memset(&Message, 0, sizeof(Message));
+	nsMapiRecipDescW Recipient;
+	memset(&Recipient, 0, sizeof(Recipient));
+	nsMapiFileDescW Files;
+	memset(&Files, 0, sizeof(Files));
+
+	if (!lpMessage)
+	{
+		lpMessage = &Message;
+	}
+	if (!lpMessage->lpRecips)
+	{
+		lpRecips = &Recipient;
+	}
+	else
+		lpRecips = lpMessage->lpRecips;
+	if (!lpMessage->lpFiles)
+	{
+		lpFiles = &Files;
+	}
+	else
+		lpFiles = lpMessage->lpFiles;
+
+	hr = pNsMapi2->SendMailW(lhSession, lpMessage,
+		(short)lpMessage->nRecipCount, lpRecips,
+		(short)lpMessage->nFileCount, lpFiles,
+		flFlags, ulReserved);
+
+	// we are seeing a problem when using Word, although we return success from the MAPI support
+	// MS COM interface in mozilla, we are getting this error here. This is a temporary hack !!
+	if (hr == 0x800703e6)
+		hr = SUCCESS_SUCCESS;
+
+	if (bTempSession)
+		MAPILogoff(lhSession, ulUIParam, 0, 0);
+
+	return hr;
+}
 
 ULONG FAR PASCAL MAPISendDocuments(ULONG ulUIParam, LPTSTR lpszDelimChar, LPTSTR lpszFilePaths,
                                 LPTSTR lpszFileNames, ULONG ulReserved)
 {
     LHANDLE lhSession ;
     nsIMapi *pNsMapi = NULL;
 
     if (!InitMozillaReference(&pNsMapi))
--- a/mailnews/mapi/mapihook/build/msgMapi.idl
+++ b/mailnews/mapi/mapihook/build/msgMapi.idl
@@ -9,18 +9,18 @@ import "unknwn.idl";
 
 typedef wchar_t LOGIN_PW_TYPE[256];
 
 typedef struct
 {
     unsigned long     ulReserved;
     unsigned long     flFlags;               /* Flags */
     unsigned long     nPosition_NotUsed;     /* character in text to be replaced by attachment */
-    LPTSTR            lpszPathName;          /* Full path name including file name */
-    LPTSTR            lpszFileName;          /* Real (original) file name */
+    LPSTR             lpszPathName;          /* Full path name including file name */
+    LPSTR             lpszFileName;          /* Real (original) file name */
     unsigned char *   lpFileType_NotUsed ;
 } nsMapiFileDesc, * lpnsMapiFileDesc;
 
 
 typedef struct
 {
     unsigned long      ulReserved;
     unsigned long      ulRecipClass;  /* MAPI_TO, MAPI_CC, MAPI_BCC, MAPI_ORIG    */
@@ -84,10 +84,61 @@ interface nsIMapi : IUnknown
 
     HRESULT SaveMail( [in] unsigned long lhSession, [in] ULONG ulUIParam, [in, unique] lpnsMapiMessage lppMessage,
                       [in] ULONG flFlags, [in] ULONG ulReserved, [in, unique] LPTSTR lpszMessageID);
 
     HRESULT Logoff (unsigned long aSession);
     HRESULT CleanUp();
 };
 
+typedef struct
+{
+	unsigned long     ulReserved;
+	unsigned long     flFlags;               /* Flags */
+	unsigned long     nPosition_NotUsed;     /* character in text to be replaced by attachment */
+	LPWSTR            lpszPathName;          /* Full path name including file name */
+	LPWSTR            lpszFileName;          /* Real (original) file name */
+	unsigned char *   lpFileType_NotUsed;
+} nsMapiFileDescW, *lpnsMapiFileDescW;
 
 
+typedef struct
+{
+	unsigned long      ulReserved;
+	unsigned long      ulRecipClass;  /* MAPI_TO, MAPI_CC, MAPI_BCC, MAPI_ORIG    */
+	LPWSTR             lpszName;      /* Recipient name to display */
+	LPWSTR             lpszAddress;   /* Recipient email address */
+	unsigned long      ulEIDSize_NotUsed;
+	unsigned char  *   lpEntryID_NotUsed;
+} nsMapiRecipDescW, *lpnsMapiRecipDescW;
+
+typedef struct
+{
+	unsigned long    ulReserved;
+	LPWSTR           lpszSubject;  /* Message Subject */
+	LPWSTR           lpszNoteText; /* Message Text */
+	LPWSTR           lpszMessageType;
+	LPWSTR           lpszDateReceived;	 /* in YYYY/MM/DD HH:MM format */
+	LPWSTR           lpszConversationID_NotUsed; /* conversation thread ID */
+	unsigned long    flFlags; /* unread,return receipt */
+	lpnsMapiRecipDescW  lpOriginator;  /* Originator descriptor  */
+	unsigned long      nRecipCount;   /* Number of recipients   */
+	[size_is (nRecipCount)]	lpnsMapiRecipDescW lpRecips;/* Recipient descriptors  */
+	unsigned long      nFileCount;    /* # of file attachments  */
+	[size_is (nFileCount)]	lpnsMapiFileDescW   lpFiles;       /* Attachment descriptors */
+} nsMapiMessageW, *lpnsMapiMessageW;
+
+[
+	object,
+	uuid(77310070-291E-4415-886B-28FC684077C7),
+	helpstring("nsIMapi2 Inteface"),
+	pointer_default(unique)
+]
+
+interface nsIMapi2 : nsIMapi
+{
+	HRESULT SendMailW([in] unsigned long aSession, [in, unique] lpnsMapiMessageW aMessage,
+		[in] short aRecipCount, [in, size_is(aRecipCount)] lpnsMapiRecipDescW aRecips,
+		[in] short aFileCount, [in, size_is(aFileCount)] lpnsMapiFileDescW aFiles,
+		[in] unsigned long aFlags, [in] unsigned long aReserved);
+};
+
+
--- a/mailnews/mapi/mapihook/src/msgMapiHook.cpp
+++ b/mailnews/mapi/mapihook/src/msgMapiHook.cpp
@@ -350,17 +350,17 @@ nsresult nsMapiHook::BlindSendMail (unsi
 
 // 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));
+    aCompFields->SetFrom (NS_ConvertASCIItoUTF16(aMessage->lpOriginator->lpszAddress));
 
   nsAutoString To ;
   nsAutoString Cc ;
   nsAutoString Bcc ;
 
   NS_NAMED_LITERAL_STRING(Comma, ",");
 
   if (aMessage->lpRecips)
@@ -443,23 +443,23 @@ nsresult nsMapiHook::HandleAttachments (
     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));
+                pFile->InitWithPath (nsDependentString((const wchar_t*)aFiles[i].lpszPathName));
             else
-                pFile->InitWithNativePath (nsDependentCString((const char*)aFiles[i].lpszPathName));
+                pFile->InitWithNativePath (nsDependentCString(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"));
+            MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("nsMapiHook::HandleAttachments: filename: %s path: %s exists = %s \n", aFiles[i].lpszFileName, 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"));
@@ -475,20 +475,20 @@ nsresult nsMapiHook::HandleAttachments (
             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);
+                    wholeFileName.Assign((const wchar_t*)aFiles[i].lpszFileName);
                 else
                     nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(),
-                                              nsDependentCString((char *) aFiles[i].lpszFileName),
+                                              nsDependentCString(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);
             }
@@ -546,17 +546,17 @@ nsresult nsMapiHook::HandleAttachments (
 nsresult nsMapiHook::PopulateCompFieldsWithConversion(lpnsMapiMessage aMessage,
                                     nsIMsgCompFields * aCompFields)
 {
   nsresult rv = NS_OK;
 
   if (aMessage->lpOriginator)
   {
     nsAutoString From;
-    From.Append(NS_ConvertASCIItoUTF16((char *) aMessage->lpOriginator->lpszAddress));
+    From.Append(NS_ConvertASCIItoUTF16(aMessage->lpOriginator->lpszAddress));
     aCompFields->SetFrom (From);
   }
 
   nsAutoString To;
   nsAutoString Cc;
   nsAutoString Bcc;
   NS_NAMED_LITERAL_STRING(Comma, ",");
   if (aMessage->lpRecips)
@@ -604,34 +604,34 @@ nsresult nsMapiHook::PopulateCompFieldsW
   nsAutoCString platformCharSet;
   // set subject
   if (aMessage->lpszSubject)
   {
     nsAutoString Subject ;
     if (platformCharSet.IsEmpty())
       platformCharSet = nsMsgI18NFileSystemCharset();
     rv = nsMsgI18NConvertToUnicode(platformCharSet,
-                                   nsDependentCString((char *) aMessage->lpszSubject),
+                                   nsDependentCString(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 = nsMsgI18NFileSystemCharset();
     rv = nsMsgI18NConvertToUnicode(platformCharSet,
-                                   nsDependentCString((char *) aMessage->lpszNoteText),
+                                   nsDependentCString(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)
@@ -645,16 +645,96 @@ nsresult nsMapiHook::PopulateCompFieldsW
   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)
+{
+  nsresult rv = NS_OK ;
+
+  if (aMessage->lpOriginator)
+    aCompFields->SetFrom (nsDependentString(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 wchar_t *addressWithoutType = (aMessage->lpRecips[i].lpszAddress)
+          ? aMessage->lpRecips[i].lpszAddress : aMessage->lpRecips[i].lpszName;
+        if (nsDependentString(addressWithoutType, 5).EqualsASCII("SMTP:") == 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
+  rv = HandleAttachments (aCompFields, aMessage->nFileCount, (lpnsMapiFileDesc)aMessage->lpFiles, true) ;
+  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("<html>") == kNotFound)
+        aCompFields->SetForcePlainText(true);
+
+      rv = aCompFields->SetBody(Body) ;
+  }
+  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 ;
--- a/mailnews/mapi/mapihook/src/msgMapiHook.h
+++ b/mailnews/mapi/mapihook/src/msgMapiHook.h
@@ -16,16 +16,17 @@ class nsMapiHook
         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 PopulateCompFieldsW(lpnsMapiMessageW 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;
 };
--- a/mailnews/mapi/mapihook/src/msgMapiImp.cpp
+++ b/mailnews/mapi/mapihook/src/msgMapiImp.cpp
@@ -1,15 +1,15 @@
 /* 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 <winstring.h>
 #include "msgMapiImp.h"
 #include "msgMapiFactory.h"
 #include "msgMapiMain.h"
 
 #include "nsIMsgCompFields.h"
 #include "msgMapiHook.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
@@ -55,16 +55,20 @@ STDMETHODIMP CMapiImp::QueryInterface(co
     if (aIid == IID_IUnknown)
     {
         *aPpv = static_cast<nsIMapi*>(this);
     }
     else if (aIid == IID_nsIMapi)
     {
         *aPpv = static_cast<nsIMapi*>(this);
     }
+    else if (aIid == IID_nsIMapi2)
+    {
+        *aPpv = static_cast<nsIMapi2*>(this);
+    }
     else
     {
         *aPpv = nullptr;
         return E_NOINTERFACE;
     }
 
     reinterpret_cast<IUnknown*>(*aPpv)->AddRef();
     return S_OK;
@@ -201,17 +205,17 @@ STDMETHODIMP CMapiImp::SendMail( unsigne
 
     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 : ""));
+      aFlags, 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
@@ -228,16 +232,52 @@ STDMETHODIMP CMapiImp::SendMail( unsigne
         {
             rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
         }
     }
 
     return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
 }
 
+STDMETHODIMP CMapiImp::SendMailW( unsigned long aSession, lpnsMapiMessageW aMessage,
+     short aRecipCount, lpnsMapiRecipDescW aRecips , short aFileCount, lpnsMapiFileDescW 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, aMessage->lpszSubject, (aMessage->lpOriginator) ? aMessage->lpOriginator->lpszAddress : L""));
+
+    /** 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 ;
+
+    rv = nsMapiHook::PopulateCompFieldsW(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 **/
--- a/mailnews/mapi/mapihook/src/msgMapiImp.h
+++ b/mailnews/mapi/mapihook/src/msgMapiImp.h
@@ -1,31 +1,29 @@
 /* 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
+class CMapiImp : public nsIMapi2
 {
 
 public :
 
   // IUnknown
 
   STDMETHODIMP            QueryInterface(const IID& aIid, void** aPpv);
   STDMETHODIMP_(ULONG)    AddRef();
@@ -58,16 +56,23 @@ public :
 
   STDMETHODIMP Initialize();
   STDMETHODIMP IsValid();
   STDMETHODIMP IsValidSession(unsigned long aSession);
 
   STDMETHODIMP Logoff (unsigned long aSession);
   STDMETHODIMP CleanUp();
 
+  // Interface nsIMapi2
+
+  STDMETHODIMP SendMailW( unsigned long aSession, lpnsMapiMessageW aMessage,
+       short aRecipCount, lpnsMapiRecipDescW aRecips ,
+       short aFileCount, lpnsMapiFileDescW aFiles ,
+       unsigned long aFlags, unsigned long aReserved) ;
+
   CMapiImp();
   ~CMapiImp();
 
   LONG InitContext(unsigned long session, MsgMapiListContext **listContext);
   nsresult GetDefaultInbox(nsIMsgFolder **inboxFolder);
 
 private :
   PRLock *m_Lock;