1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <mapidefs.h>
6 #include <mapi.h>
7 #include <winstring.h>
8 #include "msgMapiImp.h"
9 #include "msgMapiFactory.h"
10 #include "msgMapiMain.h"
11
12 #include "nsIMsgCompFields.h"
13 #include "msgMapiHook.h"
14 #include "nsString.h"
15 #include "nsCOMPtr.h"
16 #include "nsISupports.h"
17 #include "nsMsgCompCID.h"
18 #include "nsIMsgDatabase.h"
19 #include "nsMsgFolderFlags.h"
20 #include "nsIMsgHdr.h"
21 #include "MailNewsTypes.h"
22 #include "nsMsgBaseCID.h"
23 #include "nsIMsgAccountManager.h"
24 #include "nsIMsgFolder.h"
25 #include "nsIMsgImapMailFolder.h"
26 #include <time.h>
27 #include "nsIInputStream.h"
28 #include "nsILineInputStream.h"
29 #include "nsISeekableStream.h"
30 #include "nsIFile.h"
31 #include "nsIFileStreams.h"
32 #include "nsNetCID.h"
33 #include "nsMsgMessageFlags.h"
34 #include "mozilla/mailnews/MimeHeaderParser.h"
35 #include "mozilla/Logging.h"
36
37 using namespace mozilla::mailnews;
38
39 mozilla::LazyLogModule MAPI("MAPI");
40
CMapiImp()41 CMapiImp::CMapiImp() : m_cRef(1) { m_Lock = PR_NewLock(); }
42
~CMapiImp()43 CMapiImp::~CMapiImp() {
44 if (m_Lock) PR_DestroyLock(m_Lock);
45 }
46
QueryInterface(const IID & aIid,void ** aPpv)47 STDMETHODIMP CMapiImp::QueryInterface(const IID& aIid, void** aPpv) {
48 if (aIid == IID_IUnknown) {
49 *aPpv = static_cast<nsIMapi*>(this);
50 } else if (aIid == IID_nsIMapi) {
51 *aPpv = static_cast<nsIMapi*>(this);
52 } else {
53 *aPpv = nullptr;
54 return E_NOINTERFACE;
55 }
56
57 reinterpret_cast<IUnknown*>(*aPpv)->AddRef();
58 return S_OK;
59 }
60
STDMETHODIMP_(ULONG)61 STDMETHODIMP_(ULONG) CMapiImp::AddRef() { return ++m_cRef; }
62
STDMETHODIMP_(ULONG)63 STDMETHODIMP_(ULONG) CMapiImp::Release() {
64 int32_t temp = --m_cRef;
65 if (m_cRef == 0) {
66 delete this;
67 return 0;
68 }
69
70 return temp;
71 }
72
IsValid()73 STDMETHODIMP CMapiImp::IsValid() { return S_OK; }
74
IsValidSession(unsigned long aSession)75 STDMETHODIMP CMapiImp::IsValidSession(unsigned long aSession) {
76 nsMAPIConfiguration* pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
77 if (pConfig && pConfig->IsSessionValid(aSession)) return S_OK;
78
79 return E_FAIL;
80 }
81
Initialize()82 STDMETHODIMP CMapiImp::Initialize() {
83 HRESULT hr = E_FAIL;
84
85 if (!m_Lock) return E_FAIL;
86
87 PR_Lock(m_Lock);
88
89 // Initialize MAPI Configuration
90
91 nsMAPIConfiguration* pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
92 if (pConfig != nullptr) hr = S_OK;
93
94 PR_Unlock(m_Lock);
95
96 return hr;
97 }
98
Login(unsigned long aUIArg,LPSTR aLogin,LPSTR aPassWord,unsigned long aFlags,unsigned long * aSessionId)99 STDMETHODIMP CMapiImp::Login(unsigned long aUIArg, LPSTR aLogin,
100 LPSTR aPassWord, unsigned long aFlags,
101 unsigned long* aSessionId) {
102 HRESULT hr = E_FAIL;
103 bool bNewSession = false;
104 nsCString id_key;
105
106 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
107 ("CMapiImp::Login using flags %d", aFlags));
108 if (aFlags & MAPI_NEW_SESSION) bNewSession = true;
109
110 // Check For Profile Name
111 if (aLogin != nullptr && aLogin[0] != '\0') {
112 if (!nsMapiHook::VerifyUserName(nsDependentCString(aLogin), id_key)) {
113 *aSessionId = MAPI_E_LOGIN_FAILURE;
114 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
115 ("CMapiImp::Login failed for username %s", aLogin));
116 NS_ASSERTION(false, "failed verifying user name");
117 return hr;
118 }
119 } else {
120 // get default account
121 nsresult rv;
122 nsCOMPtr<nsIMsgAccountManager> accountManager =
123 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
124 NS_ENSURE_SUCCESS(rv, MAPI_E_LOGIN_FAILURE);
125
126 nsCOMPtr<nsIMsgAccount> account;
127 rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
128 NS_ENSURE_SUCCESS(rv, MAPI_E_LOGIN_FAILURE);
129 if (!account) return MAPI_E_LOGIN_FAILURE;
130
131 nsCOMPtr<nsIMsgIdentity> identity;
132 rv = account->GetDefaultIdentity(getter_AddRefs(identity));
133 NS_ENSURE_SUCCESS(rv, MAPI_E_LOGIN_FAILURE);
134 if (!identity) return MAPI_E_LOGIN_FAILURE;
135 identity->GetKey(id_key);
136 }
137
138 // finally register(create) the session.
139 uint32_t nSession_Id;
140 int16_t nResult = 0;
141
142 nsMAPIConfiguration* pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
143 if (pConfig != nullptr)
144 nResult = pConfig->RegisterSession(
145 aUIArg, aLogin ? nsDependentCString(aLogin) : EmptyCString(),
146 aPassWord ? nsDependentCString(aPassWord) : EmptyCString(),
147 (aFlags & MAPI_FORCE_DOWNLOAD), bNewSession, &nSession_Id,
148 id_key.get());
149 switch (nResult) {
150 case -1: {
151 *aSessionId = MAPI_E_TOO_MANY_SESSIONS;
152 return hr;
153 }
154 case 0: {
155 *aSessionId = MAPI_E_INSUFFICIENT_MEMORY;
156 return hr;
157 }
158 default: {
159 *aSessionId = nSession_Id;
160 MOZ_LOG(MAPI, mozilla::LogLevel::Debug, ("CMapiImp::Login succeeded"));
161 break;
162 }
163 }
164
165 return S_OK;
166 }
167
SendMail(unsigned long aSession,lpnsMapiMessage aMessage,unsigned long aFlags,unsigned long aReserved)168 STDMETHODIMP CMapiImp::SendMail(unsigned long aSession,
169 lpnsMapiMessage aMessage, unsigned long aFlags,
170 unsigned long aReserved) {
171 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
172 ("CMapiImp::SendMail flags=%lx subject: %s sender: %s", aFlags,
173 (aMessage && aMessage->lpszSubject) ? aMessage->lpszSubject
174 : "(no subject)",
175 (aMessage && aMessage->lpOriginator &&
176 aMessage->lpOriginator->lpszAddress)
177 ? aMessage->lpOriginator->lpszAddress
178 : "(no sender)"));
179
180 /** create nsIMsgCompFields obj and populate it **/
181 nsresult rv = NS_OK;
182 nsCOMPtr<nsIMsgCompFields> pCompFields =
183 do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
184 if (NS_FAILED(rv) || (!pCompFields)) return MAPI_E_INSUFFICIENT_MEMORY;
185
186 if (aMessage)
187 rv = nsMapiHook::PopulateCompFieldsWithConversion(aMessage, pCompFields);
188
189 if (NS_SUCCEEDED(rv)) {
190 // see flag to see if UI needs to be brought up
191 if (!(aFlags & MAPI_DIALOG)) {
192 rv = nsMapiHook::BlindSendMail(aSession, pCompFields);
193 } else {
194 rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
195 }
196 }
197
198 return nsMAPIConfiguration::GetMAPIErrorFromNSError(rv);
199 }
200
SendMailW(unsigned long aSession,lpnsMapiMessageW aMessage,unsigned long aFlags,unsigned long aReserved)201 STDMETHODIMP CMapiImp::SendMailW(unsigned long aSession,
202 lpnsMapiMessageW aMessage,
203 unsigned long aFlags,
204 unsigned long aReserved) {
205 MOZ_LOG(
206 MAPI, mozilla::LogLevel::Debug,
207 ("CMapiImp::SendMailW flags=%lx subject: %s sender: %s", aFlags,
208 (aMessage && aMessage->lpszSubject)
209 ? NS_ConvertUTF16toUTF8(aMessage->lpszSubject).get()
210 : "(no subject)",
211 (aMessage && aMessage->lpOriginator &&
212 aMessage->lpOriginator->lpszAddress)
213 ? NS_ConvertUTF16toUTF8(aMessage->lpOriginator->lpszAddress).get()
214 : "(no sender)"));
215
216 // Create nsIMsgCompFields obj and populate it.
217 nsresult rv = NS_OK;
218 nsCOMPtr<nsIMsgCompFields> pCompFields =
219 do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
220 if (NS_FAILED(rv) || !pCompFields) return MAPI_E_INSUFFICIENT_MEMORY;
221
222 if (aMessage) rv = nsMapiHook::PopulateCompFieldsW(aMessage, pCompFields);
223
224 if (NS_SUCCEEDED(rv)) {
225 // Check flag to see if UI needs to be brought up.
226 if (!(aFlags & MAPI_DIALOG)) {
227 rv = nsMapiHook::BlindSendMail(aSession, pCompFields);
228 } else {
229 rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
230 }
231 }
232
233 return nsMAPIConfiguration::GetMAPIErrorFromNSError(rv);
234 }
235
SendDocuments(unsigned long aSession,LPSTR aDelimChar,LPSTR aFilePaths,LPSTR aFileNames,ULONG aFlags)236 STDMETHODIMP CMapiImp::SendDocuments(unsigned long aSession, LPSTR aDelimChar,
237 LPSTR aFilePaths, LPSTR aFileNames,
238 ULONG aFlags) {
239 nsresult rv = NS_OK;
240
241 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
242 ("CMapiImp::SendDocument using flags %d", aFlags));
243 /** create nsIMsgCompFields obj and populate it **/
244 nsCOMPtr<nsIMsgCompFields> pCompFields =
245 do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
246 if (NS_FAILED(rv) || (!pCompFields)) return MAPI_E_INSUFFICIENT_MEMORY;
247
248 if (aFilePaths) {
249 rv = nsMapiHook::PopulateCompFieldsForSendDocs(pCompFields, aFlags,
250 aDelimChar, aFilePaths);
251 }
252
253 if (NS_SUCCEEDED(rv))
254 rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
255 else
256 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
257 ("CMapiImp::SendDocument error rv = %lx, paths = %s names = %s", rv,
258 aFilePaths, aFileNames));
259
260 return nsMAPIConfiguration::GetMAPIErrorFromNSError(rv);
261 }
262
GetDefaultInbox(nsIMsgFolder ** inboxFolder)263 nsresult CMapiImp::GetDefaultInbox(nsIMsgFolder** inboxFolder) {
264 // get default account
265 nsresult rv;
266 nsCOMPtr<nsIMsgAccountManager> accountManager =
267 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
268 NS_ENSURE_SUCCESS(rv, rv);
269
270 nsCOMPtr<nsIMsgAccount> account;
271 rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
272 NS_ENSURE_SUCCESS(rv, rv);
273 if (!account) return NS_ERROR_FAILURE;
274
275 // get incoming server
276 nsCOMPtr<nsIMsgIncomingServer> server;
277 rv = account->GetIncomingServer(getter_AddRefs(server));
278 NS_ENSURE_SUCCESS(rv, rv);
279
280 nsCString type;
281 rv = server->GetType(type);
282 NS_ENSURE_SUCCESS(rv, rv);
283
284 // we only care about imap and pop3
285 if (type.EqualsLiteral("imap") || type.EqualsLiteral("pop3")) {
286 // imap and pop3 account should have an Inbox
287 nsCOMPtr<nsIMsgFolder> rootMsgFolder;
288 rv = server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
289 NS_ENSURE_SUCCESS(rv, rv);
290
291 if (!rootMsgFolder) return NS_ERROR_FAILURE;
292
293 rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox, inboxFolder);
294 if (!*inboxFolder) return NS_ERROR_FAILURE;
295 }
296 return NS_OK;
297 }
298
299 //*****************************************************************************
300 // Encapsulate the XP DB stuff required to enumerate messages
301
302 class MsgMapiListContext {
303 public:
MsgMapiListContext()304 MsgMapiListContext() {}
305 ~MsgMapiListContext();
306
307 nsresult OpenDatabase(nsIMsgFolder* folder);
308
309 nsMsgKey GetNext();
310 nsresult MarkRead(nsMsgKey key, bool read);
311
312 lpnsMapiMessage GetMessage(nsMsgKey, unsigned long flFlags);
313 bool IsIMAPHost(void);
314 bool DeleteMessage(nsMsgKey key);
315
316 protected:
317 char* ConvertDateToMapiFormat(time_t);
318 char* ConvertBodyToMapiFormat(nsIMsgDBHdr* hdr);
319 void ConvertRecipientsToMapiFormat(
320 const nsCOMArray<msgIAddressObject>& ourRecips,
321 lpnsMapiRecipDesc mapiRecips, int mapiRecipClass);
322
323 nsCOMPtr<nsIMsgFolder> m_folder;
324 nsCOMPtr<nsIMsgDatabase> m_db;
325 nsCOMPtr<nsIMsgEnumerator> m_msgEnumerator;
326 };
327
InitContext(unsigned long session,MsgMapiListContext ** listContext)328 LONG CMapiImp::InitContext(unsigned long session,
329 MsgMapiListContext** listContext) {
330 nsMAPIConfiguration* pMapiConfig =
331 nsMAPIConfiguration::GetMAPIConfiguration();
332 if (!pMapiConfig) return MAPI_E_FAILURE; // get the singleton obj
333 *listContext = (MsgMapiListContext*)pMapiConfig->GetMapiListContext(session);
334 // This is the first message
335 if (!*listContext) {
336 nsCOMPtr<nsIMsgFolder> inboxFolder;
337 nsresult rv = GetDefaultInbox(getter_AddRefs(inboxFolder));
338 if (NS_FAILED(rv)) {
339 NS_ASSERTION(false, "in init context, no inbox");
340 return (MAPI_E_NO_MESSAGES);
341 }
342
343 *listContext = new MsgMapiListContext;
344 if (!*listContext) return MAPI_E_INSUFFICIENT_MEMORY;
345
346 rv = (*listContext)->OpenDatabase(inboxFolder);
347 if (NS_FAILED(rv)) {
348 pMapiConfig->SetMapiListContext(session, NULL);
349 delete *listContext;
350 NS_ASSERTION(false, "in init context, unable to open db");
351 return MAPI_E_NO_MESSAGES;
352 } else
353 pMapiConfig->SetMapiListContext(session, *listContext);
354 }
355 return SUCCESS_SUCCESS;
356 }
357
FindNext(unsigned long aSession,unsigned long ulUIParam,LPSTR lpszMessageType,LPSTR lpszSeedMessageID,unsigned long flFlags,unsigned long ulReserved,unsigned char lpszMessageID[64])358 STDMETHODIMP CMapiImp::FindNext(unsigned long aSession, unsigned long ulUIParam,
359 LPSTR lpszMessageType, LPSTR lpszSeedMessageID,
360 unsigned long flFlags, unsigned long ulReserved,
361 unsigned char lpszMessageID[64])
362
363 {
364 //
365 // If this is true, then this is the first call to this FindNext function
366 // and we should start the enumeration operation.
367 //
368
369 *lpszMessageID = '\0';
370 nsMAPIConfiguration* pMapiConfig =
371 nsMAPIConfiguration::GetMAPIConfiguration();
372 if (!pMapiConfig) {
373 NS_ASSERTION(false, "failed to get config in findnext");
374 return MAPI_E_FAILURE; // get the singleton obj
375 }
376 MsgMapiListContext* listContext;
377 LONG ret = InitContext(aSession, &listContext);
378 if (ret != SUCCESS_SUCCESS) {
379 NS_ASSERTION(false, "init context failed");
380 return ret;
381 }
382 NS_ASSERTION(listContext, "initContext returned null context");
383 if (listContext) {
384 // NS_ASSERTION(false, "find next init context succeeded");
385 nsMsgKey nextKey = listContext->GetNext();
386 if (nextKey == nsMsgKey_None) {
387 pMapiConfig->SetMapiListContext(aSession, NULL);
388 delete listContext;
389 return (MAPI_E_NO_MESSAGES);
390 }
391
392 // TRACE("MAPI: ProcessMAPIFindNext() Found message id = %d\n", nextKey);
393
394 sprintf((char*)lpszMessageID, "%d", nextKey);
395 }
396
397 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
398 ("CMapiImp::FindNext returning key %s", (char*)lpszMessageID));
399 return (SUCCESS_SUCCESS);
400 }
401
ReadMail(unsigned long aSession,unsigned long ulUIParam,LPSTR lpszMessageID,unsigned long flFlags,unsigned long ulReserved,lpnsMapiMessage * lppMessage)402 STDMETHODIMP CMapiImp::ReadMail(unsigned long aSession, unsigned long ulUIParam,
403 LPSTR lpszMessageID, unsigned long flFlags,
404 unsigned long ulReserved,
405 lpnsMapiMessage* lppMessage) {
406 nsresult irv;
407 nsAutoCString keyString((char*)lpszMessageID);
408 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
409 ("CMapiImp::ReadMail asking for key %s", (char*)lpszMessageID));
410 nsMsgKey msgKey = keyString.ToInteger(&irv);
411 if (NS_FAILED(irv)) {
412 NS_ASSERTION(false, "invalid lpszMessageID");
413 return MAPI_E_INVALID_MESSAGE;
414 }
415 MsgMapiListContext* listContext;
416 LONG ret = InitContext(aSession, &listContext);
417 if (ret != SUCCESS_SUCCESS) {
418 NS_ASSERTION(false, "init context failed in ReadMail");
419 return ret;
420 }
421 *lppMessage = listContext->GetMessage(msgKey, flFlags);
422 NS_ASSERTION(*lppMessage, "get message failed");
423
424 return (*lppMessage) ? SUCCESS_SUCCESS : E_FAIL;
425 }
426
DeleteMail(unsigned long aSession,unsigned long ulUIParam,LPSTR lpszMessageID,unsigned long flFlags,unsigned long ulReserved)427 STDMETHODIMP CMapiImp::DeleteMail(unsigned long aSession,
428 unsigned long ulUIParam, LPSTR lpszMessageID,
429 unsigned long flFlags,
430 unsigned long ulReserved) {
431 nsresult irv;
432 nsAutoCString keyString((char*)lpszMessageID);
433 nsMsgKey msgKey = keyString.ToInteger(&irv);
434 // XXX Why do we return success on failure?
435 if (NS_FAILED(irv)) return SUCCESS_SUCCESS;
436 MsgMapiListContext* listContext;
437 LONG ret = InitContext(aSession, &listContext);
438 if (ret != SUCCESS_SUCCESS) return ret;
439 return (listContext->DeleteMessage(msgKey)) ? SUCCESS_SUCCESS
440 : MAPI_E_INVALID_MESSAGE;
441 }
442
SaveMail(unsigned long aSession,unsigned long ulUIParam,lpnsMapiMessage lppMessage,unsigned long flFlags,unsigned long ulReserved,LPSTR lpszMessageID)443 STDMETHODIMP CMapiImp::SaveMail(unsigned long aSession, unsigned long ulUIParam,
444 lpnsMapiMessage lppMessage,
445 unsigned long flFlags, unsigned long ulReserved,
446 LPSTR lpszMessageID) {
447 MsgMapiListContext* listContext;
448 LONG ret = InitContext(aSession, &listContext);
449 if (ret != SUCCESS_SUCCESS) return ret;
450 return S_OK;
451 }
452
Logoff(unsigned long aSession)453 STDMETHODIMP CMapiImp::Logoff(unsigned long aSession) {
454 nsMAPIConfiguration* pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
455
456 if (pConfig->UnRegisterSession((uint32_t)aSession)) return S_OK;
457
458 return E_FAIL;
459 }
460
CleanUp()461 STDMETHODIMP CMapiImp::CleanUp() {
462 nsMapiHook::CleanUp();
463 return S_OK;
464 }
465
466 #define MAX_NAME_LEN 256
467
~MsgMapiListContext()468 MsgMapiListContext::~MsgMapiListContext() {
469 if (m_db) m_db->Close(false);
470 }
471
OpenDatabase(nsIMsgFolder * folder)472 nsresult MsgMapiListContext::OpenDatabase(nsIMsgFolder* folder) {
473 nsresult dbErr = NS_ERROR_FAILURE;
474 if (folder) {
475 m_folder = folder;
476 dbErr = folder->GetMsgDatabase(getter_AddRefs(m_db));
477 if (m_db) dbErr = m_db->EnumerateMessages(getter_AddRefs(m_msgEnumerator));
478 }
479 return dbErr;
480 }
481
IsIMAPHost(void)482 bool MsgMapiListContext::IsIMAPHost(void) {
483 if (!m_folder) return FALSE;
484 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
485
486 return imapFolder != nullptr;
487 }
488
GetNext()489 nsMsgKey MsgMapiListContext::GetNext() {
490 nsMsgKey key = nsMsgKey_None;
491 bool keepTrying = TRUE;
492
493 // NS_ASSERTION (m_msgEnumerator && m_db, "need enumerator and db");
494 if (m_msgEnumerator && m_db) {
495 do {
496 keepTrying = FALSE;
497 nsCOMPtr<nsIMsgDBHdr> msgHdr;
498 if (NS_SUCCEEDED(m_msgEnumerator->GetNext(getter_AddRefs(msgHdr))) &&
499 msgHdr) {
500 msgHdr->GetMessageKey(&key);
501
502 // Check here for IMAP message...if not, just return...
503 if (!IsIMAPHost()) return key;
504
505 // If this is an IMAP message, we have to make sure we have a valid
506 // body to work with.
507 uint32_t flags = 0;
508
509 (void)msgHdr->GetFlags(&flags);
510 if (flags & nsMsgMessageFlags::Offline) return key;
511
512 // Ok, if we get here, we have an IMAP message without a body!
513 // We need to keep trying by calling the GetNext member recursively...
514 keepTrying = TRUE;
515 }
516 } while (keepTrying);
517 }
518
519 return key;
520 }
521
MarkRead(nsMsgKey key,bool read)522 nsresult MsgMapiListContext::MarkRead(nsMsgKey key, bool read) {
523 nsresult err = NS_ERROR_FAILURE;
524 NS_ASSERTION(m_db, "no db");
525 if (m_db) err = m_db->MarkRead(key, read, nullptr);
526 return err;
527 }
528
GetMessage(nsMsgKey key,unsigned long flFlags)529 lpnsMapiMessage MsgMapiListContext::GetMessage(nsMsgKey key,
530 unsigned long flFlags) {
531 lpnsMapiMessage message =
532 (lpnsMapiMessage)CoTaskMemAlloc(sizeof(nsMapiMessage));
533 memset(message, 0, sizeof(nsMapiMessage));
534 if (message) {
535 nsCString subject;
536 nsCString author;
537 nsCOMPtr<nsIMsgDBHdr> msgHdr;
538
539 m_db->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
540 if (msgHdr) {
541 msgHdr->GetSubject(getter_Copies(subject));
542 message->lpszSubject = (char*)CoTaskMemAlloc(subject.Length() + 1);
543 strcpy((char*)message->lpszSubject, subject.get());
544 uint32_t date;
545 (void)msgHdr->GetDateInSeconds(&date);
546 message->lpszDateReceived = ConvertDateToMapiFormat(date);
547
548 // Pull out the flags info
549 // anything to do with MAPI_SENT? Since we're only reading the Inbox, I
550 // guess not
551 uint32_t ourFlags;
552 (void)msgHdr->GetFlags(&ourFlags);
553 if (!(ourFlags & nsMsgMessageFlags::Read))
554 message->flFlags |= MAPI_UNREAD;
555 if (ourFlags & (nsMsgMessageFlags::MDNReportNeeded |
556 nsMsgMessageFlags::MDNReportSent))
557 message->flFlags |= MAPI_RECEIPT_REQUESTED;
558
559 // Pull out the author/originator info
560 message->lpOriginator =
561 (lpnsMapiRecipDesc)CoTaskMemAlloc(sizeof(nsMapiRecipDesc));
562 memset(message->lpOriginator, 0, sizeof(nsMapiRecipDesc));
563 if (message->lpOriginator) {
564 msgHdr->GetAuthor(getter_Copies(author));
565 ConvertRecipientsToMapiFormat(EncodedHeader(author),
566 message->lpOriginator, MAPI_ORIG);
567 }
568 // Pull out the To/CC info
569 nsCString recipients, ccList;
570 msgHdr->GetRecipients(getter_Copies(recipients));
571 msgHdr->GetCcList(getter_Copies(ccList));
572
573 nsCOMArray<msgIAddressObject> parsedToRecips = EncodedHeader(recipients);
574 nsCOMArray<msgIAddressObject> parsedCCRecips = EncodedHeader(ccList);
575 uint32_t numToRecips = parsedToRecips.Length();
576 uint32_t numCCRecips = parsedCCRecips.Length();
577
578 message->lpRecips = (lpnsMapiRecipDesc)CoTaskMemAlloc(
579 (numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
580 memset(message->lpRecips, 0,
581 (numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
582 if (message->lpRecips) {
583 ConvertRecipientsToMapiFormat(parsedToRecips, message->lpRecips,
584 MAPI_TO);
585 ConvertRecipientsToMapiFormat(parsedCCRecips,
586 &message->lpRecips[numToRecips], MAPI_CC);
587 }
588
589 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
590 ("MsgMapiListContext::GetMessage flags=%x subject %s date %s "
591 "sender %s",
592 flFlags, (char*)message->lpszSubject,
593 (char*)message->lpszDateReceived, author.get()));
594
595 // Convert any body text that we have locally
596 if (!(flFlags & MAPI_ENVELOPE_ONLY))
597 message->lpszNoteText = (char*)ConvertBodyToMapiFormat(msgHdr);
598 }
599 if (!(flFlags & (MAPI_PEEK | MAPI_ENVELOPE_ONLY)))
600 m_db->MarkRead(key, true, nullptr);
601 }
602 return message;
603 }
604
ConvertDateToMapiFormat(time_t ourTime)605 char* MsgMapiListContext::ConvertDateToMapiFormat(time_t ourTime) {
606 char* date = (char*)CoTaskMemAlloc(32);
607 if (date) {
608 // MAPI time format is YYYY/MM/DD HH:MM
609 // Note that we're not using XP_StrfTime because that localizes the time
610 // format, and the way I read the MAPI spec is that their format is
611 // canonical, not localized.
612 struct tm* local = localtime(&ourTime);
613 if (local)
614 strftime(date, 32, "%Y/%m/%d %I:%M",
615 local); // use %H if hours should be 24 hour format
616 }
617 return date;
618 }
619
ConvertRecipientsToMapiFormat(const nsCOMArray<msgIAddressObject> & recipients,lpnsMapiRecipDesc mapiRecips,int mapiRecipClass)620 void MsgMapiListContext::ConvertRecipientsToMapiFormat(
621 const nsCOMArray<msgIAddressObject>& recipients,
622 lpnsMapiRecipDesc mapiRecips, int mapiRecipClass) {
623 nsTArray<nsCString> names, addresses;
624 ExtractAllAddresses(recipients, UTF16ArrayAdapter<>(names),
625 UTF16ArrayAdapter<>(addresses));
626
627 size_t numAddresses = names.Length();
628 for (size_t i = 0; i < numAddresses; i++) {
629 if (!names[i].IsEmpty()) {
630 mapiRecips[i].lpszName = (char*)CoTaskMemAlloc(names[i].Length() + 1);
631 if (mapiRecips[i].lpszName)
632 strcpy((char*)mapiRecips[i].lpszName, names[i].get());
633 }
634 if (!addresses[i].IsEmpty()) {
635 mapiRecips[i].lpszName = (char*)CoTaskMemAlloc(addresses[i].Length() + 1);
636 if (mapiRecips[i].lpszName)
637 strcpy((char*)mapiRecips[i].lpszName, addresses[i].get());
638 }
639 mapiRecips[i].ulRecipClass = mapiRecipClass;
640 }
641 }
642
ConvertBodyToMapiFormat(nsIMsgDBHdr * hdr)643 char* MsgMapiListContext::ConvertBodyToMapiFormat(nsIMsgDBHdr* hdr) {
644 const int kBufLen =
645 64000; // I guess we only return the first 64K of a message.
646 #define EMPTY_MESSAGE_LINE(buf) \
647 (buf[0] == '\r' || buf[0] == '\n' || buf[0] == '\0')
648
649 nsCOMPtr<nsIMsgFolder> folder;
650 hdr->GetFolder(getter_AddRefs(folder));
651 if (!folder) return nullptr;
652
653 nsCOMPtr<nsIFile> localFile;
654 folder->GetFilePath(getter_AddRefs(localFile));
655
656 nsresult rv;
657 nsCOMPtr<nsIFileInputStream> fileStream =
658 do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
659 NS_ENSURE_SUCCESS(rv, nullptr);
660
661 rv = fileStream->Init(localFile, PR_RDONLY, 0664,
662 false); // just have to read the messages
663 NS_ENSURE_SUCCESS(rv, nullptr);
664
665 nsCOMPtr<nsILineInputStream> fileLineStream = do_QueryInterface(fileStream);
666 if (!fileLineStream) return nullptr;
667
668 // ### really want to skip past headers...
669 uint64_t messageOffset;
670 uint32_t lineCount;
671 hdr->GetMessageOffset(&messageOffset);
672 hdr->GetLineCount(&lineCount);
673 nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(fileStream);
674 seekableStream->Seek(PR_SEEK_SET, messageOffset);
675 bool hasMore = true;
676 nsAutoCString curLine;
677
678 while (hasMore) // advance past message headers
679 {
680 nsresult rv = fileLineStream->ReadLine(curLine, &hasMore);
681 if (NS_FAILED(rv) || EMPTY_MESSAGE_LINE(curLine)) break;
682 }
683 uint32_t msgSize;
684 hdr->GetMessageSize(&msgSize);
685 if (msgSize > kBufLen) msgSize = kBufLen - 1;
686 // this is too big, since it includes the msg hdr size...oh well
687 char* body = (char*)CoTaskMemAlloc(msgSize + 1);
688
689 if (!body) return nullptr;
690 int32_t bytesCopied = 0;
691 for (hasMore = TRUE; lineCount > 0 && hasMore && NS_SUCCEEDED(rv);
692 lineCount--) {
693 rv = fileLineStream->ReadLine(curLine, &hasMore);
694 if (NS_FAILED(rv)) break;
695 curLine.Append(CRLF);
696 // make sure we have room left
697 if (bytesCopied + curLine.Length() < msgSize) {
698 strcpy(body + bytesCopied, curLine.get());
699 bytesCopied += curLine.Length();
700 }
701 }
702 MOZ_LOG(MAPI, mozilla::LogLevel::Debug,
703 ("ConvertBodyToMapiFormat size=%x allocated size %x body = %100.100s",
704 bytesCopied, msgSize + 1, (char*)body));
705 body[bytesCopied] = '\0'; // rhp - fix last line garbage...
706 return body;
707 }
708
709 //*****************************************************************************
710 // MSGMAPI API implementation
711
msg_FreeMAPIFile(lpMapiFileDesc f)712 static void msg_FreeMAPIFile(lpMapiFileDesc f) {
713 if (f) {
714 CoTaskMemFree(f->lpszPathName);
715 CoTaskMemFree(f->lpszFileName);
716 }
717 }
718
msg_FreeMAPIRecipient(lpMapiRecipDesc rd)719 static void msg_FreeMAPIRecipient(lpMapiRecipDesc rd) {
720 if (rd) {
721 if (rd->lpszName) CoTaskMemFree(rd->lpszName);
722 if (rd->lpszAddress) CoTaskMemFree(rd->lpszAddress);
723 // CoTaskMemFree(rd->lpEntryID);
724 }
725 }
726
MSG_FreeMapiMessage(lpMapiMessage msg)727 extern "C" void MSG_FreeMapiMessage(lpMapiMessage msg) {
728 ULONG i;
729
730 if (msg) {
731 CoTaskMemFree(msg->lpszSubject);
732 CoTaskMemFree(msg->lpszNoteText);
733 CoTaskMemFree(msg->lpszMessageType);
734 CoTaskMemFree(msg->lpszDateReceived);
735 CoTaskMemFree(msg->lpszConversationID);
736
737 if (msg->lpOriginator) msg_FreeMAPIRecipient(msg->lpOriginator);
738
739 for (i = 0; i < msg->nRecipCount; i++)
740 if (&(msg->lpRecips[i]) != nullptr)
741 msg_FreeMAPIRecipient(&(msg->lpRecips[i]));
742
743 CoTaskMemFree(msg->lpRecips);
744
745 for (i = 0; i < msg->nFileCount; i++)
746 if (&(msg->lpFiles[i]) != nullptr) msg_FreeMAPIFile(&(msg->lpFiles[i]));
747
748 CoTaskMemFree(msg->lpFiles);
749
750 CoTaskMemFree(msg);
751 }
752 }
753
MsgMarkMapiMessageRead(nsIMsgFolder * folder,nsMsgKey key,bool read)754 extern "C" bool MsgMarkMapiMessageRead(nsIMsgFolder* folder, nsMsgKey key,
755 bool read) {
756 bool success = FALSE;
757 MsgMapiListContext* context = new MsgMapiListContext();
758 if (context) {
759 if (NS_SUCCEEDED(context->OpenDatabase(folder))) {
760 if (NS_SUCCEEDED(context->MarkRead(key, read))) success = TRUE;
761 }
762 delete context;
763 }
764 return success;
765 }
766
DeleteMessage(nsMsgKey key)767 bool MsgMapiListContext::DeleteMessage(nsMsgKey key) {
768 if (!m_db) return FALSE;
769
770 if (!IsIMAPHost()) {
771 nsTArray<nsMsgKey> doomed({key});
772 return NS_SUCCEEDED((m_db->DeleteMessages(doomed, nullptr)));
773 }
774 #if 0
775 else if ( m_folder->GetIMAPFolderInfoMail() )
776 {
777 AutoTArray<nsMsgKey, 1> messageKeys;
778 messageKeys.AppendElement(key);
779
780 (m_folder->GetIMAPFolderInfoMail())->DeleteSpecifiedMessages(pane, messageKeys, nsMsgKey_None);
781 m_db->DeleteMessage(key, nullptr, FALSE);
782 return TRUE;
783 }
784 #endif
785 else {
786 return FALSE;
787 }
788 }
789
790 /* Return TRUE on success, FALSE on failure */
MSG_DeleteMapiMessage(nsIMsgFolder * folder,nsMsgKey key)791 extern "C" bool MSG_DeleteMapiMessage(nsIMsgFolder* folder, nsMsgKey key) {
792 bool success = FALSE;
793 MsgMapiListContext* context = new MsgMapiListContext();
794 if (context) {
795 if (NS_SUCCEEDED(context->OpenDatabase(folder))) {
796 success = context->DeleteMessage(key);
797 }
798
799 delete context;
800 }
801
802 return success;
803 }
804