/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corp.
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *                 Krishna Mohan Khandrika (kkhandrika@netscape.com)
 *                 Srilatha Moturi (srilatha@netscape.com)
 *                 Rajiv Dayal (rdayal@netscape.com)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#define MAPI_STARTUP_ARG       "/MAPIStartUp"

#ifdef MOZ_LOGGING
// this has to be before the pre-compiled header
#define FORCE_PR_LOG /* Allow logging in the release build */
#endif
#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 "nsIDOMWindowInternal.h"
#include "nsINativeAppSupport.h"
#include "nsIMsgAccountManager.h"
#include "nsIDOMWindowInternal.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 "nsIProxyObjectManager.h"
#include "nsIMsgComposeService.h"
#include "nsProxiedService.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 "nsNetUtil.h"

#include "nsEmbedCID.h"

extern PRLogModuleInfo *MAPI;

class nsMAPISendListener : public nsIMsgSendListener
{
public:

    virtual ~nsMAPISendListener() { }

    // nsISupports interface
    NS_DECL_ISUPPORTS

    /* void OnStartSending (in string aMsgID, in PRUint32 aMsgSize); */
    NS_IMETHOD OnStartSending(const char *aMsgID, PRUint32 aMsgSize) { return NS_OK; }

    /* void OnProgress (in string aMsgID, in PRUint32 aProgress, in PRUint32 aProgressMax); */
    NS_IMETHOD OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax) { return NS_OK;}

    /* void OnStatus (in string aMsgID, in wstring aMsg); */
    NS_IMETHOD OnStatus(const char *aMsgID, const PRUnichar *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 PRUnichar *aMsg,
                           nsIFile *returnFile) {
        PR_CEnterMonitor(this);
        PR_CNotifyAll(this);
        m_done = PR_TRUE;
        PR_CExitMonitor(this);
        return NS_OK ;
    }

	/* void OnSendNotPerformed */
	NS_IMETHOD OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
	{
		return OnStopSending(aMsgID, aStatus, nsnull, nsnull) ;
	}

    /* void OnGetDraftFolderURI (); */
    NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;}

    static nsresult CreateMAPISendListener( nsIMsgSendListener **ppListener);

    PRBool IsDone() { return m_done ; }

protected :
    nsMAPISendListener() {
        m_done = PR_FALSE;
    }

    PRBool          m_done;
};


NS_IMPL_THREADSAFE_ISUPPORTS1(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;
}

PRBool nsMapiHook::isMapiService = PR_FALSE;

void nsMapiHook::CleanUp()
{
    // This routine will be fully implemented in future
    // to cleanup mapi related stuff inside mozilla code.
}

PRBool nsMapiHook::DisplayLoginDialog(PRBool aLogin, PRUnichar **aUsername,
                      PRUnichar **aPassword)
{
  nsresult rv;
  PRBool btnResult = PR_FALSE;

  nsCOMPtr<nsIPromptService> dlgService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
  if (NS_SUCCEEDED(rv) && dlgService)
  {
    nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
    if (NS_FAILED(rv) || !bundleService) return PR_FALSE;

    nsCOMPtr<nsIStringBundle> bundle;
    rv = bundleService->CreateBundle(MAPI_PROPERTIES_CHROME, getter_AddRefs(bundle));
    if (NS_FAILED(rv) || !bundle) return PR_FALSE;

    nsCOMPtr<nsIStringBundle> brandBundle;
    rv = bundleService->CreateBundle(
                    "chrome://branding/locale/brand.properties",
                    getter_AddRefs(brandBundle));
    if (NS_FAILED(rv)) return PR_FALSE;

    nsString brandName;
    rv = brandBundle->GetStringFromName(
                       NS_LITERAL_STRING("brandFullName").get(),
                       getter_Copies(brandName));
    if (NS_FAILED(rv)) return PR_FALSE;

    nsString loginTitle;
    const PRUnichar *brandStrings[] = { brandName.get() };
    NS_NAMED_LITERAL_STRING(loginTitlePropertyTag, "loginTitle");
    const PRUnichar *dTitlePropertyTag = loginTitlePropertyTag.get();
    rv = bundle->FormatStringFromName(dTitlePropertyTag, brandStrings, 1,
                                      getter_Copies(loginTitle));
    if (NS_FAILED(rv)) return PR_FALSE;

    if (aLogin)
    {
      nsString loginText;
      rv = bundle->GetStringFromName(NS_LITERAL_STRING("loginTextwithName").get(),
                                     getter_Copies(loginText));
      if (NS_FAILED(rv) || loginText.IsEmpty()) return PR_FALSE;

      PRBool dummyValue = PR_FALSE;
      rv = dlgService->PromptUsernameAndPassword(nsnull, loginTitle.get(),
                                                 loginText.get(), aUsername, aPassword,
                                                 nsnull, &dummyValue, &btnResult);
    }
    else
    {
      //nsString loginString;
      nsString loginText;
      const PRUnichar *userNameStrings[] = { *aUsername };

      NS_NAMED_LITERAL_STRING(loginTextPropertyTag, "loginText");
      const PRUnichar *dpropertyTag = loginTextPropertyTag.get();
      rv = bundle->FormatStringFromName(dpropertyTag, userNameStrings, 1,
                                        getter_Copies(loginText));
      if (NS_FAILED(rv)) return PR_FALSE;

      PRBool dummyValue = PR_FALSE;
      rv = dlgService->PromptPassword(nsnull, loginTitle.get(), loginText.get(),
                                      aPassword, nsnull, &dummyValue, &btnResult);
    }
  }

  return btnResult;
}

PRBool nsMapiHook::VerifyUserName(const nsString& aUsername, nsCString& aIdKey)
{
  nsresult rv;

  if (aUsername.IsEmpty())
    return PR_FALSE;

  nsCOMPtr<nsIMsgAccountManager> accountManager(do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv));
  if (NS_FAILED(rv)) return PR_FALSE;
  nsCOMPtr<nsISupportsArray> identities;
  rv = accountManager->GetAllIdentities(getter_AddRefs(identities));
  if (NS_FAILED(rv)) return PR_FALSE;
  PRUint32 numIndentities;
  identities->Count(&numIndentities);

  for (PRUint32 i = 0; i < numIndentities; i++)
  {
    // convert supports->Identity
    nsCOMPtr<nsISupports> thisSupports;
    rv = identities->GetElementAt(i, getter_AddRefs(thisSupports));
    if (NS_FAILED(rv)) continue;
    nsCOMPtr<nsIMsgIdentity> thisIdentity(do_QueryInterface(thisSupports, &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
      PRInt32 index = email.FindChar('@');
      if (index != -1)
        email.SetLength(index);

      if (aUsername.Equals(NS_ConvertASCIItoUTF16(email)))
        return NS_SUCCEEDED(thisIdentity->GetKey(aIdKey));
    }
  }

  return PR_FALSE;
}

PRBool
nsMapiHook::IsBlindSendAllowed()
{
  PRBool enabled = PR_FALSE;
  PRBool warn = PR_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 PR_FALSE;

  if (!warn)
      return PR_TRUE; // Everything is okay.

  nsresult rv;
  nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
  if (NS_FAILED(rv) || !bundleService) return PR_FALSE;

  nsCOMPtr<nsIStringBundle> bundle;
  rv = bundleService->CreateBundle(MAPI_PROPERTIES_CHROME, getter_AddRefs(bundle));
  if (NS_FAILED(rv) || !bundle) return PR_FALSE;

  nsString warningMsg;
  rv = bundle->GetStringFromName(NS_LITERAL_STRING("mapiBlindSendWarning").get(),
                                      getter_Copies(warningMsg));
  if (NS_FAILED(rv)) return PR_FALSE;

  nsString dontShowAgainMessage;
  rv = bundle->GetStringFromName(NS_LITERAL_STRING("mapiBlindSendDontShowAgain").get(),
                                      getter_Copies(dontShowAgainMessage));
  if (NS_FAILED(rv)) return PR_FALSE;

  nsCOMPtr<nsIPromptService> dlgService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
  if (NS_FAILED(rv) || !dlgService) return PR_FALSE;

  PRBool continueToWarn = PR_TRUE;
  PRBool okayToContinue = PR_FALSE;
  dlgService->ConfirmCheck(nsnull, nsnull, warningMsg.get(), dontShowAgainMessage.get(), &continueToWarn, &okayToContinue);

  if (!continueToWarn && okayToContinue && prefBranch)
    prefBranch->SetBoolPref(PREF_MAPI_WARN_PRIOR_TO_BLIND_SEND, PR_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<nsIDOMWindowInternal>  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
  PRUnichar * password = pMapiConfig->GetPassword(aSession) ;
  // password
  nsCAutoString 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
  PRBool 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, nsnull);
  if (NS_FAILED(rv)) return rv ;

  return pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, pMsgId, nsnull, nsnull, nsnull) ;
  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;
        }
      }
    }
  }

  PR_LOG(MAPI, PR_LOG_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, PR_TRUE) ;
  if (NS_FAILED(rv)) return rv ;

  // set body
  if (aMessage->lpszNoteText)
  {
      nsString Body;
      CopyASCIItoUTF16(aMessage->lpszNoteText, Body);
      if (Body.Last() != '\n')
        Body.AppendLiteral(CRLF);

      if (Body.Find("<html>") == kNotFound)
        aCompFields->SetForcePlainText(PR_TRUE);

      rv = aCompFields->SetBody(Body) ;
  }
  return rv ;
}

nsresult nsMapiHook::HandleAttachments (nsIMsgCompFields * aCompFields, PRInt32 aFileCount,
                                        lpnsMapiFileDesc aFiles, BOOL aIsUnicode)
{
    nsresult rv = NS_OK ;

    nsCAutoString Attachments ;
    nsCAutoString TempFiles ;

    nsCOMPtr <nsILocalFile> pFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID, &rv) ;
    if (NS_FAILED(rv) || (!pFile) ) return rv ;
    nsCOMPtr <nsILocalFile> pTempDir = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID, &rv) ;
    if (NS_FAILED(rv) || (!pTempDir) ) return rv ;

    for (int i=0 ; i < aFileCount ; i++)
    {
        PRBool bTempFile = PR_FALSE ;
        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));

            PRBool bExist ;
            rv = pFile->Exists(&bExist) ;
            PR_LOG(MAPI, PR_LOG_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> pTempFileDir;
            NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pTempFileDir));
            nsCOMPtr <nsILocalFile> pTempDir = do_QueryInterface(pTempFileDir);

            // 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.
                PRInt32 lastSlash = wholeFileName.RFindChar(PRUnichar('\\'));
                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(PR_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(PR_TRUE); // this one is a temp file so set the flag for MsgCompose

            // now set the attachment object
            nsCAutoString pURL ;
            NS_GetURLSpecFromFile(pFile, pURL);
            attachment->SetUrl(pURL);

            // set the file size
            PRInt64 fileSize;
            pFile->GetFileSize(&fileSize);
            attachment->SetSize(fileSize);

            // add the attachment
            rv = aCompFields->AddAttachment (attachment);
            if (NS_FAILED(rv))
              PR_LOG(MAPI, PR_LOG_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) ;

  PR_LOG(MAPI, PR_LOG_DEBUG, ("to: %s cc: %s bcc: %s \n", NS_ConvertUTF16toUTF8(To).get(), NS_ConvertUTF16toUTF8(Cc).get(), NS_ConvertUTF16toUTF8(Bcc).get()));

  nsCAutoString 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, PR_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.Last() != '\n')
      Body.AppendLiteral(CRLF);

    if (Body.Find("<html>") == kNotFound)
      aCompFields->SetForcePlainText(PR_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,
                            PRUnichar * aDelimChar, PRUnichar * aFilePaths)
{
  nsAutoString strDelimChars ;
  nsString strFilePaths;
  nsresult rv = NS_OK ;
  PRBool bExist ;

  if (aFlags & MAPI_UNICODE)
  {
    if (aDelimChar)
      strDelimChars.Assign (aDelimChar);
    if (aFilePaths)
      strFilePaths.Assign (aFilePaths);
  }
  else
  {
    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(";");

  PRInt32 offset = 0 ;
  PRInt32 FilePathsLen = strFilePaths.Length() ;
  if (FilePathsLen)
  {
    nsAutoString Subject ;

    // multiple files to be sent, delim specified
    nsCOMPtr <nsILocalFile> pFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID, &rv) ;
    if (NS_FAILED(rv) || (!pFile) ) return rv ;

    PRUnichar * newFilePaths = (PRUnichar *) strFilePaths.get() ;
    while (offset != kNotFound)
    {
      //Temp Directory
      nsCOMPtr <nsIFile> pTempFileDir;
      NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pTempFileDir));
      nsCOMPtr <nsILocalFile> pTempDir = do_QueryInterface(pTempFileDir);

      // 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 + 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(PR_TRUE) ;

      // now set the attachment object
      nsCAutoString pURL ;
      NS_GetURLSpecFromFile(pTempDir, pURL);
      attachment->SetUrl(pURL);

      // 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 ;

    PRBool forcePlainText;
    aCompFields->GetForcePlainText(&forcePlainText);
    pMsgComposeParams->SetFormat(forcePlainText ? nsIMsgCompFormat::Default : nsIMsgCompFormat::HTML);
    // populate the compose params
    pMsgComposeParams->SetType(nsIMsgCompType::New);
    pMsgComposeParams->SetFormat(nsIMsgCompFormat::Default);
    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(nsnull, pMsgComposeParams) ;
    if (NS_FAILED(rv)) return rv ;

    return rv ;
}
