diff options
Diffstat (limited to 'comm/mailnews/mapi/mapihook/src/msgMapiImp.cpp')
-rw-r--r-- | comm/mailnews/mapi/mapihook/src/msgMapiImp.cpp | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/comm/mailnews/mapi/mapihook/src/msgMapiImp.cpp b/comm/mailnews/mapi/mapihook/src/msgMapiImp.cpp new file mode 100644 index 0000000000..62abed1f7b --- /dev/null +++ b/comm/mailnews/mapi/mapihook/src/msgMapiImp.cpp @@ -0,0 +1,801 @@ +/* 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 <winstring.h> +#include "msgMapiImp.h" +#include "msgMapiFactory.h" +#include "msgMapiMain.h" + +#include "nsIMsgCompFields.h" +#include "msgMapiHook.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsIMsgDatabase.h" +#include "nsMsgFolderFlags.h" +#include "nsIMsgHdr.h" +#include "MailNewsTypes.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; + +mozilla::LazyLogModule MAPI("MAPI"); + +CMapiImp::CMapiImp() : m_cRef(1) { m_Lock = PR_NewLock(); } + +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, LPSTR aLogin, + LPSTR 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 %lu", aFlags)); + if (aFlags & MAPI_NEW_SESSION) bNewSession = true; + + // Check For Profile Name + if (aLogin != nullptr && aLogin[0] != '\0') { + if (!nsMapiHook::VerifyUserName(nsDependentCString(aLogin), id_key)) { + *aSessionId = MAPI_E_LOGIN_FAILURE; + MOZ_LOG(MAPI, mozilla::LogLevel::Debug, + ("CMapiImp::Login failed for username %s", aLogin)); + NS_ASSERTION(false, "failed verifying user name"); + return hr; + } + } else { + // get default account + nsresult rv; + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, MAPI_E_LOGIN_FAILURE); + + nsCOMPtr<nsIMsgAccount> account; + rv = accountManager->GetDefaultAccount(getter_AddRefs(account)); + NS_ENSURE_SUCCESS(rv, MAPI_E_LOGIN_FAILURE); + if (!account) return MAPI_E_LOGIN_FAILURE; + + nsCOMPtr<nsIMsgIdentity> identity; + 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, aLogin ? nsDependentCString(aLogin) : EmptyCString(), + aPassWord ? nsDependentCString(aPassWord) : EmptyCString(), + (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")); + break; + } + } + + return S_OK; +} + +STDMETHODIMP CMapiImp::SendMail(unsigned long aSession, + lpnsMapiMessage aMessage, unsigned long aFlags, + unsigned long aReserved) { + MOZ_LOG(MAPI, mozilla::LogLevel::Debug, + ("CMapiImp::SendMail flags=%lx subject: %s sender: %s", aFlags, + (aMessage && aMessage->lpszSubject) ? aMessage->lpszSubject + : "(no subject)", + (aMessage && aMessage->lpOriginator && + aMessage->lpOriginator->lpszAddress) + ? aMessage->lpOriginator->lpszAddress + : "(no sender)")); + + /** create nsIMsgCompFields obj and populate it **/ + nsresult rv = NS_OK; + nsCOMPtr<nsIMsgCompFields> pCompFields = + do_CreateInstance("@mozilla.org/messengercompose/composefields;1", &rv); + if (NS_FAILED(rv) || (!pCompFields)) return MAPI_E_INSUFFICIENT_MEMORY; + + if (aMessage) + 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::SendMailW(unsigned long aSession, + lpnsMapiMessageW aMessage, + unsigned long aFlags, + unsigned long aReserved) { + MOZ_LOG( + MAPI, mozilla::LogLevel::Debug, + ("CMapiImp::SendMailW flags=%lx subject: %s sender: %s", aFlags, + (aMessage && aMessage->lpszSubject) + ? NS_ConvertUTF16toUTF8(aMessage->lpszSubject).get() + : "(no subject)", + (aMessage && aMessage->lpOriginator && + aMessage->lpOriginator->lpszAddress) + ? NS_ConvertUTF16toUTF8(aMessage->lpOriginator->lpszAddress).get() + : "(no sender)")); + + // Create nsIMsgCompFields obj and populate it. + nsresult rv = NS_OK; + nsCOMPtr<nsIMsgCompFields> pCompFields = + do_CreateInstance("@mozilla.org/messengercompose/composefields;1", &rv); + if (NS_FAILED(rv) || !pCompFields) return MAPI_E_INSUFFICIENT_MEMORY; + + if (aMessage) 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, LPSTR aDelimChar, + LPSTR aFilePaths, LPSTR aFileNames, + ULONG aFlags) { + nsresult rv = NS_OK; + + MOZ_LOG(MAPI, mozilla::LogLevel::Debug, + ("CMapiImp::SendDocument using flags %lu", aFlags)); + /** create nsIMsgCompFields obj and populate it **/ + nsCOMPtr<nsIMsgCompFields> pCompFields = + do_CreateInstance("@mozilla.org/messengercompose/composefields;1", &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 = %x, paths = %s names = %s", rv, + aFilePaths, aFileNames)); + + return nsMAPIConfiguration::GetMAPIErrorFromNSError(rv); +} + +nsresult CMapiImp::GetDefaultInbox(nsIMsgFolder** inboxFolder) { + // get default account + nsresult rv; + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgAccount> account; + rv = accountManager->GetDefaultAccount(getter_AddRefs(account)); + NS_ENSURE_SUCCESS(rv, rv); + if (!account) return NS_ERROR_FAILURE; + + // 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<nsIMsgEnumerator> m_msgEnumerator; +}; + +LONG CMapiImp::InitContext(unsigned long session, + MsgMapiListContext** listContext) { + nsMAPIConfiguration* pMapiConfig = + nsMAPIConfiguration::GetMAPIConfiguration(); + if (!pMapiConfig) return MAPI_E_FAILURE; // get the singleton 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, + LPSTR lpszMessageType, LPSTR 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 singleton 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", (char*)lpszMessageID)); + return (SUCCESS_SUCCESS); +} + +STDMETHODIMP CMapiImp::ReadMail(unsigned long aSession, unsigned long ulUIParam, + LPSTR 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", (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, LPSTR 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, + LPSTR 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<nsIMsgDBHdr> msgHdr; + if (NS_SUCCEEDED(m_msgEnumerator->GetNext(getter_AddRefs(msgHdr))) && + msgHdr) { + 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; + + m_db->GetMsgHdrForKey(key, getter_AddRefs(msgHdr)); + if (msgHdr) { + msgHdr->GetSubject(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=%lu subject %s date %s " + "sender %s", + 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<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 + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr<nsILineInputStream> fileLineStream = do_QueryInterface(fileStream); + 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(fileStream); + seekableStream->Seek(PR_SEEK_SET, messageOffset); + bool hasMore = true; + nsAutoCString curLine; + + 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", + bytesCopied, msgSize + 1, (char*)body)); + body[bytesCopied] = '\0'; // rhp - fix last line garbage... + return body; +} + +//***************************************************************************** +// 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()) { + nsTArray<nsMsgKey> doomed({key}); + return NS_SUCCEEDED((m_db->DeleteMessages(doomed, 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; +} |