Bug 1048658 - Implement MAPISendMailW(). r=jorgk
authorMike Kaganski <mikekaganski@gmail.com>
Thu, 17 Jan 2019 15:38:00 +0100
changeset 34237 aaf39a9bd368
parent 34236 1939e10a1251
child 34238 41d0ee56c665
push id389
push userclokep@gmail.com
push dateMon, 18 Mar 2019 19:01:53 +0000
reviewersjorgk
bugs1048658
Bug 1048658 - Implement MAPISendMailW(). r=jorgk
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/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
@@ -217,16 +217,61 @@ 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;
+    nsIMapi *pNsMapi = nullptr;
+
+    if (!InitMozillaReference(&pNsMapi))
+      return MAPI_E_FAILURE;
+
+    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 == nullptr))
+      return MAPI_E_UNKNOWN_RECIPIENT;
+
+    if (!lhSession || pNsMapi->IsValidSession(lhSession) != S_OK)
+    {
+      FLAGS LoginFlag = 0;
+      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;
+    }
+
+    hr = pNsMapi->SendMailW(lhSession, lpMessage, 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
@@ -41,16 +41,52 @@ typedef struct
     unsigned long     flFlags;                          /* unread,return receipt */
     lpnsMapiRecipDesc lpOriginator;                     /* Originator descriptor  */
     unsigned long     nRecipCount;                      /* Number of recipients   */
     [size_is (nRecipCount)] lpnsMapiRecipDesc lpRecips; /* Recipient descriptors  */
     unsigned long     nFileCount;                       /* # of file attachments  */
     [size_is (nFileCount)] lpnsMapiFileDesc lpFiles;    /* Attachment descriptors */
 } nsMapiMessage, * lpnsMapiMessage;
 
+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(6EDCD38E-8861-11d5-A3DD-00B0D0F3BAA7),
     helpstring("nsIMapi Interface"),
     pointer_default(unique)
 ]
 
 interface nsIMapi : IUnknown
@@ -78,14 +114,17 @@ interface nsIMapi : IUnknown
                      [in] ULONG flFlags, [in] ULONG ulReserved, [out] lpnsMapiMessage *lppMessage);
 
     HRESULT DeleteMail([in] unsigned long lhSession, [in] ULONG ulUIParam, [in, unique] LPTSTR lpszMessageID,
                        [in] ULONG flFlags, [in] ULONG ulReserved);
 
     HRESULT SaveMail([in] unsigned long lhSession, [in] ULONG ulUIParam, [in, unique] lpnsMapiMessage lppMessage,
                      [in] ULONG flFlags, [in] ULONG ulReserved, [in, unique] LPTSTR lpszMessageID);
 
+    HRESULT SendMailW([in] unsigned long aSession, [in, unique] lpnsMapiMessageW aMessage,
+                      [in] unsigned long aFlags, [in] unsigned long aReserved);
+
     HRESULT Logoff(unsigned long aSession);
     HRESULT CleanUp();
 };
 
 
 
--- a/mailnews/mapi/mapihook/src/msgMapiHook.cpp
+++ b/mailnews/mapi/mapihook/src/msgMapiHook.cpp
@@ -449,16 +449,126 @@ nsresult nsMapiHook::HandleAttachments (
             if (NS_FAILED(rv))
               MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("nsMapiHook::HandleAttachments: AddAttachment rv =  %lx\n", rv));
         }
     }
     return rv ;
 }
 
 
+nsresult nsMapiHook::HandleAttachmentsW(nsIMsgCompFields* aCompFields, int32_t aFileCount,
+                                        lpnsMapiFileDescW aFiles)
+{
+  nsresult rv = NS_OK ;
+  // Do nothing if there are no files to process.
+  if (!aFiles || aFileCount <= 0)
+      return NS_OK;
+
+  nsAutoCString Attachments ;
+  nsAutoCString TempFiles ;
+
+  nsCOMPtr <nsIFile> pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv) ;
+  if (NS_FAILED(rv) || (!pFile)) return rv ;
+  nsCOMPtr <nsIFile> pTempDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv) ;
+  if (NS_FAILED(rv) || (!pTempDir)) return rv ;
+
+  for (int i=0 ; i < aFileCount ; i++)
+  {
+    if (aFiles[i].lpszPathName)
+    {
+      // Check if attachment exists.
+      pFile->InitWithPath (nsDependentString(aFiles[i].lpszPathName));
+
+      bool bExist ;
+      rv = pFile->Exists(&bExist) ;
+      MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
+        ("nsMapiHook::HandleAttachmentsW: filename: %s path: %s exists = %s \n",
+         NS_ConvertUTF16toUTF8(aFiles[i].lpszFileName).get(),
+         NS_ConvertUTF16toUTF8(aFiles[i].lpszPathName).get(),
+         bExist ? "true" : "false"));
+      if (NS_FAILED(rv) || (!bExist)) return NS_ERROR_FILE_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 ;
+      // leafName already contains a unicode leafName from lpszPathName. If we were given
+      // a value for lpszFileName, use it. Otherwise stick with leafName.
+      if (aFiles[i].lpszFileName)
+      {
+        nsAutoString wholeFileName(aFiles[i].lpszFileName);
+        // Need to find the last '\' and find the leafname from that.
+        int32_t lastSlash = wholeFileName.RFindChar(char16_t('\\'));
+        if (lastSlash != kNotFound)
+          leafName.Assign(Substring(wholeFileName, lastSlash + 1));
+        else
+          leafName.Assign(wholeFileName);
+      }
+      else
+        pFile->GetLeafName(leafName);
+
+      nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(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::HandleAttachmentsW: 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)
   {
@@ -550,16 +660,99 @@ 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 = HandleAttachmentsW(aCompFields, aMessage->nFileCount, aMessage->lpFiles);
+  if (NS_FAILED(rv)) return rv;
+
+  // Set body.
+  if (aMessage->lpszNoteText)
+  {
+    nsString Body(aMessage->lpszNoteText);
+    if (Body.IsEmpty() || Body.Last() != '\n')
+      Body.AppendLiteral(CRLF);
+
+    // This is needed when Simple MAPI is used without a compose window.
+    // See bug 1366196.
+    if (Body.Find("<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
@@ -15,18 +15,21 @@ class nsMapiHook
                         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 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) ;
+        static nsresult HandleAttachmentsW(nsIMsgCompFields *aCompFields, int32_t aFileCount,
+                                           lpnsMapiFileDescW aFiles);
         static void CleanUp();
 
         static bool isMapiService;
 };
 
 #endif  // MSG_MAPI_HOOK_H_
--- 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"
@@ -233,16 +233,54 @@ STDMETHODIMP CMapiImp::SendMail( unsigne
         {
             rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
         }
     }
 
     return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
 }
 
+STDMETHODIMP CMapiImp::SendMailW(unsigned long aSession, lpnsMapiMessageW aMessage,
+                                 unsigned long aFlags, unsigned long aReserved)
+{
+    nsresult rv = NS_OK;
+
+    MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendMailW using flags %d\n", aFlags));
+
+    // Handle possible nullptr argument.
+    nsMapiMessageW Message{};
+    if (!aMessage)
+      aMessage = &Message;
+
+    MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::SendMailW flags=%x subject: %s sender: %s\n",
+            aFlags,
+            NS_ConvertUTF16toUTF8(aMessage->lpszSubject).get(),
+            NS_ConvertUTF16toUTF8((aMessage->lpOriginator) ? aMessage->lpOriginator->lpszAddress : L"").get()));
+
+    // 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))
+    {
+      // Check 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,17 +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/. */
 
 #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;
 
@@ -53,16 +51,19 @@ public :
                             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 SendMailW(unsigned long aSession, lpnsMapiMessageW aMessage,
+                         unsigned long aFlags, unsigned long aReserved);
+
   STDMETHODIMP Logoff (unsigned long aSession);
   STDMETHODIMP CleanUp();
 
   CMapiImp();
   virtual ~CMapiImp();
 
   LONG InitContext(unsigned long session, MsgMapiListContext **listContext);
   nsresult GetDefaultInbox(nsIMsgFolder **inboxFolder);