diff options
Diffstat (limited to 'mailnews/import/outlook/src/nsOutlookMail.cpp')
-rw-r--r-- | mailnews/import/outlook/src/nsOutlookMail.cpp | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/mailnews/import/outlook/src/nsOutlookMail.cpp b/mailnews/import/outlook/src/nsOutlookMail.cpp new file mode 100644 index 000000000..b9a851ce8 --- /dev/null +++ b/mailnews/import/outlook/src/nsOutlookMail.cpp @@ -0,0 +1,863 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + Outlook mail import +*/ + +#include "nsCOMPtr.h" +#include "nscore.h" +#include "nsMsgUtils.h" +#include "nsIServiceManager.h" +#include "nsIImportService.h" +#include "nsIImportFieldMap.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsIImportABDescriptor.h" +#include "nsIMutableArray.h" +#include "nsOutlookStringBundle.h" +#include "nsABBaseCID.h" +#include "nsIAbCard.h" +#include "mdb.h" +#include "OutlookDebugLog.h" +#include "nsOutlookMail.h" +#include "nsUnicharUtils.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIMsgPluggableStore.h" +#include "nsIMsgHdr.h" +#include "nsIMsgFolder.h" +#include "nsMsgI18N.h" +#include "nsNetUtil.h" + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + +/* ------------ Address book stuff ----------------- */ +typedef struct { + int32_t mozField; + int32_t multiLine; + ULONG mapiTag; +} MAPIFields; + +/* + Fields in MAPI, not in Mozilla + PR_OFFICE_LOCATION + FIX - PR_BIRTHDAY - stored as PT_SYSTIME - FIX to extract for moz address book birthday + PR_DISPLAY_NAME_PREFIX - Mr., Mrs. Dr., etc. + PR_SPOUSE_NAME + PR_GENDER - integer, not text + FIX - PR_CONTACT_EMAIL_ADDRESSES - multiuline strings for email addresses, needs + parsing to get secondary email address for mozilla +*/ + +#define kIsMultiLine -2 +#define kNoMultiLine -1 + +static MAPIFields gMapiFields[] = { + { 35, kIsMultiLine, PR_BODY}, + { 6, kNoMultiLine, PR_BUSINESS_TELEPHONE_NUMBER}, + { 7, kNoMultiLine, PR_HOME_TELEPHONE_NUMBER}, + { 25, kNoMultiLine, PR_COMPANY_NAME}, + { 23, kNoMultiLine, PR_TITLE}, + { 10, kNoMultiLine, PR_CELLULAR_TELEPHONE_NUMBER}, + { 9, kNoMultiLine, PR_PAGER_TELEPHONE_NUMBER}, + { 8, kNoMultiLine, PR_BUSINESS_FAX_NUMBER}, + { 8, kNoMultiLine, PR_HOME_FAX_NUMBER}, + { 22, kNoMultiLine, PR_COUNTRY}, + { 19, kNoMultiLine, PR_LOCALITY}, + { 20, kNoMultiLine, PR_STATE_OR_PROVINCE}, + { 17, 18, PR_STREET_ADDRESS}, + { 21, kNoMultiLine, PR_POSTAL_CODE}, + { 27, kNoMultiLine, PR_PERSONAL_HOME_PAGE}, + { 26, kNoMultiLine, PR_BUSINESS_HOME_PAGE}, + { 13, kNoMultiLine, PR_HOME_ADDRESS_CITY}, + { 16, kNoMultiLine, PR_HOME_ADDRESS_COUNTRY}, + { 15, kNoMultiLine, PR_HOME_ADDRESS_POSTAL_CODE}, + { 14, kNoMultiLine, PR_HOME_ADDRESS_STATE_OR_PROVINCE}, + { 11, 12, PR_HOME_ADDRESS_STREET}, + { 24, kNoMultiLine, PR_DEPARTMENT_NAME} +}; +/* ---------------------------------------------------- */ + + +#define kCopyBufferSize (16 * 1024) + +// The email address in Outlook Contacts doesn't have a named +// property, we need to use this mapi name ID to access the email +// The MAPINAMEID for email address has ulKind=MNID_ID +// Outlook stores each email address in two IDs, 32899/32900 for Email1 +// 32915/32916 for Email2, 32931/32932 for Email3 +// Current we use OUTLOOK_EMAIL1_MAPI_ID1 for primary email +// OUTLOOK_EMAIL2_MAPI_ID1 for secondary email +#define OUTLOOK_EMAIL1_MAPI_ID1 32899 +#define OUTLOOK_EMAIL1_MAPI_ID2 32900 +#define OUTLOOK_EMAIL2_MAPI_ID1 32915 +#define OUTLOOK_EMAIL2_MAPI_ID2 32916 +#define OUTLOOK_EMAIL3_MAPI_ID1 32931 +#define OUTLOOK_EMAIL3_MAPI_ID2 32932 + +nsOutlookMail::nsOutlookMail() +{ + m_gotAddresses = false; + m_gotFolders = false; + m_haveMapi = CMapiApi::LoadMapi(); + m_lpMdb = NULL; +} + +nsOutlookMail::~nsOutlookMail() +{ +// EmptyAttachments(); +} + +nsresult nsOutlookMail::GetMailFolders(nsIArray **pArray) +{ + if (!m_haveMapi) { + IMPORT_LOG0("GetMailFolders called before Mapi is initialized\n"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("FAILED to allocate the nsIMutableArray for the mail folder list\n"); + return rv; + } + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return rv; + + m_gotFolders = true; + + m_folderList.ClearAll(); + + m_mapi.Initialize(); + m_mapi.LogOn(); + + if (m_storeList.GetSize() == 0) + m_mapi.IterateStores(m_storeList); + + int i = 0; + CMapiFolder *pFolder; + if (m_storeList.GetSize() > 1) { + while ((pFolder = m_storeList.GetItem(i))) { + CMapiFolder *pItem = new CMapiFolder(pFolder); + pItem->SetDepth(1); + m_folderList.AddItem(pItem); + if (!m_mapi.GetStoreFolders(pItem->GetCBEntryID(), pItem->GetEntryID(), m_folderList, 2)) { + IMPORT_LOG1("GetStoreFolders for index %d failed.\n", i); + } + i++; + } + } + else { + if ((pFolder = m_storeList.GetItem(i))) { + if (!m_mapi.GetStoreFolders(pFolder->GetCBEntryID(), pFolder->GetEntryID(), m_folderList, 1)) { + IMPORT_LOG1("GetStoreFolders for index %d failed.\n", i); + } + } + } + + // Create the mailbox descriptors for the list of folders + nsIImportMailboxDescriptor * pID; + nsISupports * pInterface; + nsString name; + nsString uniName; + + for (i = 0; i < m_folderList.GetSize(); i++) { + pFolder = m_folderList.GetItem(i); + rv = impSvc->CreateNewMailboxDescriptor(&pID); + if (NS_SUCCEEDED(rv)) { + pID->SetDepth(pFolder->GetDepth()); + pID->SetIdentifier(i); + + pFolder->GetDisplayName(name); + pID->SetDisplayName(name.get()); + + pID->SetSize(1000); + rv = pID->QueryInterface(kISupportsIID, (void **) &pInterface); + array->AppendElement(pInterface, false); + pInterface->Release(); + pID->Release(); + } + } + array.forget(pArray); + return NS_OK; +} + +bool nsOutlookMail::IsAddressBookNameUnique(nsString& name, nsString& list) +{ + nsString usedName; + usedName.AppendLiteral("["); + usedName.Append(name); + usedName.AppendLiteral("],"); + + return list.Find(usedName) == -1; +} + +void nsOutlookMail::MakeAddressBookNameUnique(nsString& name, nsString& list) +{ + nsString newName; + int idx = 1; + + newName = name; + while (!IsAddressBookNameUnique(newName, list)) { + newName = name; + newName.Append(char16_t(' ')); + newName.AppendInt((int32_t) idx); + idx++; + } + + name = newName; + list.AppendLiteral("["); + list.Append(name); + list.AppendLiteral("],"); +} + +nsresult nsOutlookMail::GetAddressBooks(nsIArray **pArray) +{ + if (!m_haveMapi) { + IMPORT_LOG0("GetAddressBooks called before Mapi is initialized\n"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("FAILED to allocate the nsIMutableArray for the address book list\n"); + return rv; + } + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return rv; + + m_gotAddresses = true; + + m_addressList.ClearAll(); + m_mapi.Initialize(); + m_mapi.LogOn(); + if (m_storeList.GetSize() == 0) + m_mapi.IterateStores(m_storeList); + + int i = 0; + CMapiFolder *pFolder; + if (m_storeList.GetSize() > 1) { + while ((pFolder = m_storeList.GetItem(i))) { + CMapiFolder *pItem = new CMapiFolder(pFolder); + pItem->SetDepth(1); + m_addressList.AddItem(pItem); + if (!m_mapi.GetStoreAddressFolders(pItem->GetCBEntryID(), pItem->GetEntryID(), m_addressList)) { + IMPORT_LOG1("GetStoreAddressFolders for index %d failed.\n", i); + } + i++; + } + } + else { + if ((pFolder = m_storeList.GetItem(i))) { + if (!m_mapi.GetStoreAddressFolders(pFolder->GetCBEntryID(), pFolder->GetEntryID(), m_addressList)) { + IMPORT_LOG1("GetStoreFolders for index %d failed.\n", i); + } + } + } + + // Create the mailbox descriptors for the list of folders + nsIImportABDescriptor * pID; + nsISupports * pInterface; + nsString name; + nsString list; + + for (i = 0; i < m_addressList.GetSize(); i++) { + pFolder = m_addressList.GetItem(i); + if (!pFolder->IsStore()) { + rv = impSvc->CreateNewABDescriptor(&pID); + if (NS_SUCCEEDED(rv)) { + pID->SetIdentifier(i); + pFolder->GetDisplayName(name); + MakeAddressBookNameUnique(name, list); + pID->SetPreferredName(name); + pID->SetSize(100); + rv = pID->QueryInterface(kISupportsIID, (void **) &pInterface); + array->AppendElement(pInterface, false); + pInterface->Release(); + pID->Release(); + } + } + } + array.forget(pArray); + return NS_OK; +} + +void nsOutlookMail::OpenMessageStore(CMapiFolder *pNextFolder) +{ + // Open the store specified + if (pNextFolder->IsStore()) { + if (!m_mapi.OpenStore(pNextFolder->GetCBEntryID(), pNextFolder->GetEntryID(), &m_lpMdb)) { + m_lpMdb = NULL; + IMPORT_LOG0("CMapiApi::OpenStore failed\n"); + } + + return; + } + + // Check to see if we should open the one and only store + if (!m_lpMdb) { + if (m_storeList.GetSize() == 1) { + CMapiFolder * pFolder = m_storeList.GetItem(0); + if (pFolder) { + if (!m_mapi.OpenStore(pFolder->GetCBEntryID(), pFolder->GetEntryID(), &m_lpMdb)) { + m_lpMdb = NULL; + IMPORT_LOG0("CMapiApi::OpenStore failed\n"); + } + } + else { + IMPORT_LOG0("Error retrieving the one & only message store\n"); + } + } + else { + IMPORT_LOG0("*** Error importing a folder without a valid message store\n"); + } + } +} + +// Roles and responsibilities: +// nsOutlookMail +// - Connect to Outlook +// - Enumerate the mailboxes +// - Iterate the mailboxes +// - For each mail, create one nsOutlookCompose object +// - For each mail, create one CMapiMessage object +// +// nsOutlookCompose +// - Establish a TB session +// - Connect to all required services +// - Perform the composition of the RC822 document from the data gathered by CMapiMessage +// - Save the composed message to the TB mailbox +// - Ensure the proper cleanup +// +// CMapiMessage +// - Encapsulate the MAPI message interface +// - Gather the information required to (re)compose the message + +nsresult nsOutlookMail::ImportMailbox(uint32_t *pDoneSoFar, bool *pAbort, + int32_t index, const char16_t *pName, + nsIMsgFolder *dstFolder, + int32_t *pMsgCount) +{ + if ((index < 0) || (index >= m_folderList.GetSize())) { + IMPORT_LOG0("*** Bad mailbox identifier, unable to import\n"); + *pAbort = true; + return NS_ERROR_FAILURE; + } + + int32_t dummyMsgCount = 0; + if (pMsgCount) + *pMsgCount = 0; + else + pMsgCount = &dummyMsgCount; + + CMapiFolder *pFolder = m_folderList.GetItem(index); + OpenMessageStore(pFolder); + if (!m_lpMdb) { + IMPORT_LOG1("*** Unable to obtain mapi message store for mailbox: %S\n", pName); + return NS_ERROR_FAILURE; + } + + if (pFolder->IsStore()) + return NS_OK; + + // now what? + CMapiFolderContents contents(m_lpMdb, pFolder->GetCBEntryID(), pFolder->GetEntryID()); + + BOOL done = FALSE; + ULONG cbEid; + LPENTRYID lpEid; + ULONG oType; + LPMESSAGE lpMsg = nullptr; + ULONG totalCount; + double doneCalc; + + nsCOMPtr<nsIOutputStream> outputStream; + nsCOMPtr<nsIMsgPluggableStore> msgStore; + nsresult rv = dstFolder->GetMsgStore(getter_AddRefs(msgStore)); + NS_ENSURE_SUCCESS(rv, rv); + + while (!done) { + if (!contents.GetNext(&cbEid, &lpEid, &oType, &done)) { + IMPORT_LOG1("*** Error iterating mailbox: %S\n", pName); + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIMsgDBHdr> msgHdr; + bool reusable; + + rv = msgStore->GetNewMsgOutputStream(dstFolder, getter_AddRefs(msgHdr), &reusable, + getter_AddRefs(outputStream)); + if (NS_FAILED(rv)) { + IMPORT_LOG1("*** Error getting nsIOutputStream of mailbox: %S\n", pName); + return rv; + } + totalCount = contents.GetCount(); + doneCalc = *pMsgCount; + doneCalc /= totalCount; + doneCalc *= 1000; + if (pDoneSoFar) { + *pDoneSoFar = (uint32_t) doneCalc; + if (*pDoneSoFar > 1000) + *pDoneSoFar = 1000; + } + + if (!done && (oType == MAPI_MESSAGE)) { + if (!m_mapi.OpenMdbEntry(m_lpMdb, cbEid, lpEid, (LPUNKNOWN *) &lpMsg)) { + IMPORT_LOG1("*** Error opening messages in mailbox: %S\n", pName); + return NS_ERROR_FAILURE; + } + + // See if it's a drafts folder. Outlook doesn't allow drafts + // folder to be configured so it's ok to hard code it here. + nsAutoString folderName(pName); + nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow; + mode = nsIMsgSend::nsMsgSaveAsDraft; + if (folderName.LowerCaseEqualsLiteral("drafts")) + mode = nsIMsgSend::nsMsgSaveAsDraft; + + rv = ImportMessage(lpMsg, outputStream, mode); + if (NS_SUCCEEDED(rv)){ // No errors & really imported + (*pMsgCount)++; + msgStore->FinishNewMessage(outputStream, msgHdr); + } + else { + IMPORT_LOG1( "*** Error reading message from mailbox: %S\n", pName); + msgStore->DiscardNewMessage(outputStream, msgHdr); + } + if (!reusable) + outputStream->Close(); + } + } + + if (outputStream) + outputStream->Close(); + return NS_OK; +} + +nsresult nsOutlookMail::ImportMessage(LPMESSAGE lpMsg, nsIOutputStream *pDest, nsMsgDeliverMode mode) +{ + CMapiMessage msg(lpMsg); + // If we wanted to skip messages that were downloaded in header only mode, we + // would return NS_ERROR_FAILURE if !msg.FullMessageDownloaded. However, we + // don't do this because it may cause seemingly wrong import results. + // A user will get less mails in his imported folder than were in the original folder, + // and this may make user feel like TB import is bad. + // In reality, the skipped messages are those that have not been downloaded yet, because + // they were downloaded in the "headers-only" mode. This is different from the case when + // the message is downloaded completely, but consists only of headers - in this case + // the message will be imported anyway. + + if (!msg.ValidState()) + return NS_ERROR_FAILURE; + + // I have to create a composer for each message, since it turns out that if we create + // one composer for several messages, the Send Proxy object that is shared between those messages + // isn't reset properly (at least in the current implementation), which leads to crash. + // If there's a proper way to reinitialize the Send Proxy object, + // then we could slightly optimize the send process. + nsOutlookCompose compose; + nsresult rv = compose.ProcessMessage(mode, msg, pDest); + + // Just for YUCKS, let's try an extra endline + WriteData(pDest, "\x0D\x0A", 2); + + return rv; +} + +BOOL nsOutlookMail::WriteData(nsIOutputStream *pDest, const char *pData, int32_t len) +{ + uint32_t written; + nsresult rv = pDest->Write(pData, len, &written); + return NS_SUCCEEDED(rv) && written == len; +} + +nsresult nsOutlookMail::ImportAddresses(uint32_t *pCount, uint32_t *pTotal, const char16_t *pName, uint32_t id, nsIAddrDatabase *pDb, nsString& errors) +{ + if (id >= (uint32_t)(m_addressList.GetSize())) { + IMPORT_LOG0("*** Bad address identifier, unable to import\n"); + return NS_ERROR_FAILURE; + } + + uint32_t dummyCount = 0; + if (pCount) + *pCount = 0; + else + pCount = &dummyCount; + + CMapiFolder *pFolder; + if (id > 0) { + int32_t idx = (int32_t) id; + idx--; + while (idx >= 0) { + pFolder = m_addressList.GetItem(idx); + if (pFolder->IsStore()) { + OpenMessageStore(pFolder); + break; + } + idx--; + } + } + + pFolder = m_addressList.GetItem(id); + OpenMessageStore(pFolder); + if (!m_lpMdb) { + IMPORT_LOG1("*** Unable to obtain mapi message store for address book: %S\n", pName); + return NS_ERROR_FAILURE; + } + + if (pFolder->IsStore()) + return NS_OK; + + nsresult rv; + + nsCOMPtr<nsIImportFieldMap> pFieldMap; + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewFieldMap(getter_AddRefs(pFieldMap)); + } + + CMapiFolderContents contents(m_lpMdb, pFolder->GetCBEntryID(), pFolder->GetEntryID()); + + BOOL done = FALSE; + ULONG cbEid; + LPENTRYID lpEid; + ULONG oType; + LPMESSAGE lpMsg; + nsCString type; + LPSPropValue pVal; + nsString subject; + + while (!done) { + (*pCount)++; + + if (!contents.GetNext(&cbEid, &lpEid, &oType, &done)) { + IMPORT_LOG1("*** Error iterating address book: %S\n", pName); + return NS_ERROR_FAILURE; + } + + if (pTotal && (*pTotal == 0)) + *pTotal = contents.GetCount(); + + if (!done && (oType == MAPI_MESSAGE)) { + if (!m_mapi.OpenMdbEntry(m_lpMdb, cbEid, lpEid, (LPUNKNOWN *) &lpMsg)) { + IMPORT_LOG1("*** Error opening messages in mailbox: %S\n", pName); + return NS_ERROR_FAILURE; + } + + // Get the PR_MESSAGE_CLASS attribute, + // ensure that it is IPM.Contact + pVal = m_mapi.GetMapiProperty(lpMsg, PR_MESSAGE_CLASS); + if (pVal) { + type.Truncate(); + m_mapi.GetStringFromProp(pVal, type); + if (type.EqualsLiteral("IPM.Contact")) { + // This is a contact, add it to the address book! + subject.Truncate(); + pVal = m_mapi.GetMapiProperty(lpMsg, PR_SUBJECT); + if (pVal) + m_mapi.GetStringFromProp(pVal, subject); + + nsIMdbRow* newRow = nullptr; + pDb->GetNewRow(&newRow); + // FIXME: Check with Candice about releasing the newRow if it + // isn't added to the database. Candice's code in nsAddressBook + // never releases it but that doesn't seem right to me! + if (newRow) { + if (BuildCard(subject.get(), pDb, newRow, lpMsg, pFieldMap)) { + pDb->AddCardRowToDB(newRow); + } + } + } + else if (type.EqualsLiteral("IPM.DistList")) + { + // This is a list/group, add it to the address book! + subject.Truncate(); + pVal = m_mapi.GetMapiProperty(lpMsg, PR_SUBJECT); + if (pVal) + m_mapi.GetStringFromProp(pVal, subject); + CreateList(subject.get(), pDb, lpMsg, pFieldMap); + } + } + + lpMsg->Release(); + } + } + + rv = pDb->Commit(nsAddrDBCommitType::kLargeCommit); + return rv; +} +nsresult nsOutlookMail::CreateList(const char16_t * pName, + nsIAddrDatabase *pDb, + LPMAPIPROP pUserList, + nsIImportFieldMap *pFieldMap) +{ + // If no name provided then we're done. + if (!pName || !(*pName)) + return NS_OK; + + nsresult rv = NS_ERROR_FAILURE; + // Make sure we have db to work with. + if (!pDb) + return rv; + + nsCOMPtr <nsIMdbRow> newListRow; + rv = pDb->GetNewListRow(getter_AddRefs(newListRow)); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoCString column; + LossyCopyUTF16toASCII(nsDependentString(pName), column); + rv = pDb->AddListName(newListRow, column.get()); + NS_ENSURE_SUCCESS(rv, rv); + + HRESULT hr; + LPSPropValue value = NULL; + ULONG valueCount = 0; + + LPSPropTagArray properties = NULL; + m_mapi.MAPIAllocateBuffer(CbNewSPropTagArray(1), + (void **)&properties); + properties->cValues = 1; + properties->aulPropTag [0] = m_mapi.GetEmailPropertyTag(pUserList, 0x8054); + hr = pUserList->GetProps(properties, 0, &valueCount, &value); + m_mapi.MAPIFreeBuffer(properties); + if (HR_FAILED(hr)) + return NS_ERROR_FAILURE; + if (!value) + return NS_ERROR_NOT_AVAILABLE; + // XXX from here out, value must be freed with MAPIFreeBuffer + + SBinaryArray *sa=(SBinaryArray *)&value->Value.bin; + if (!sa || !sa->lpbin) { + m_mapi.MAPIFreeBuffer(value); + return NS_ERROR_NULL_POINTER; + } + + LPENTRYID lpEid; + ULONG cbEid; + ULONG idx; + LPMESSAGE lpMsg; + nsCString type; + LPSPropValue pVal; + nsString subject; + ULONG total; + + total = sa->cValues; + for (idx = 0; idx < total; idx++) + { + lpEid= (LPENTRYID) sa->lpbin[idx].lpb; + cbEid = sa->lpbin[idx].cb; + + if (!m_mapi.OpenEntry(cbEid, lpEid, (LPUNKNOWN *) &lpMsg)) + { + + IMPORT_LOG1("*** Error opening messages in mailbox: %S\n", pName); + m_mapi.MAPIFreeBuffer(value); + return NS_ERROR_FAILURE; + } + // This is a contact, add it to the address book! + subject.Truncate(); + pVal = m_mapi.GetMapiProperty(lpMsg, PR_SUBJECT); + if (pVal) + m_mapi.GetStringFromProp(pVal, subject); + + nsCOMPtr <nsIMdbRow> newRow; + nsCOMPtr <nsIMdbRow> oldRow; + pDb->GetNewRow(getter_AddRefs(newRow)); + if (newRow) { + if (BuildCard(subject.get(), pDb, newRow, lpMsg, pFieldMap)) + { + nsCOMPtr <nsIAbCard> userCard; + nsCOMPtr <nsIAbCard> newCard; + userCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + pDb->InitCardFromRow(userCard,newRow); + + //add card to db + pDb->FindRowByCard(userCard,getter_AddRefs(oldRow)); + if (oldRow) + newRow = oldRow; + else + pDb->AddCardRowToDB(newRow); + + //add card list + pDb->AddListCardColumnsToRow(userCard, + newListRow,idx+1, getter_AddRefs(newCard), + true, nullptr, nullptr); + } + } + } + m_mapi.MAPIFreeBuffer(value); + + rv = pDb->AddCardRowToDB(newListRow); + NS_ENSURE_SUCCESS(rv, rv); + + rv = pDb->SetListAddressTotal(newListRow, (uint32_t)total); + rv = pDb->AddListDirNode(newListRow); + return rv; +} + +void nsOutlookMail::SanitizeValue(nsString& val) +{ + MsgReplaceSubstring(val, NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING(", ")); + MsgReplaceChar(val, "\r\n", ','); +} + +void nsOutlookMail::SplitString(nsString& val1, nsString& val2) +{ + // Find the last line if there is more than one! + int32_t idx = val1.RFind("\x0D\x0A"); + int32_t cnt = 2; + if (idx == -1) { + cnt = 1; + idx = val1.RFindChar(13); + } + if (idx == -1) + idx= val1.RFindChar(10); + if (idx != -1) { + val2 = Substring(val1, idx + cnt); + val1.SetLength(idx); + SanitizeValue(val1); + } +} + +bool nsOutlookMail::BuildCard(const char16_t *pName, nsIAddrDatabase *pDb, nsIMdbRow *newRow, LPMAPIPROP pUser, nsIImportFieldMap *pFieldMap) +{ + + nsString lastName; + nsString firstName; + nsString eMail; + nsString nickName; + nsString middleName; + nsString secondEMail; + ULONG emailTag; + + LPSPropValue pProp = m_mapi.GetMapiProperty(pUser, PR_EMAIL_ADDRESS); + if (!pProp) { + emailTag = m_mapi.GetEmailPropertyTag(pUser, OUTLOOK_EMAIL1_MAPI_ID1); + if (emailTag) { + pProp = m_mapi.GetMapiProperty(pUser, emailTag); + } + } + if (pProp) { + m_mapi.GetStringFromProp(pProp, eMail); + SanitizeValue(eMail); + } + + // for secondary email + emailTag = m_mapi.GetEmailPropertyTag(pUser, OUTLOOK_EMAIL2_MAPI_ID1); + if (emailTag) { + pProp = m_mapi.GetMapiProperty(pUser, emailTag); + if (pProp) { + m_mapi.GetStringFromProp(pProp, secondEMail); + SanitizeValue(secondEMail); + } + } + + pProp = m_mapi.GetMapiProperty(pUser, PR_GIVEN_NAME); + if (pProp) { + m_mapi.GetStringFromProp(pProp, firstName); + SanitizeValue(firstName); + } + pProp = m_mapi.GetMapiProperty(pUser, PR_SURNAME); + if (pProp) { + m_mapi.GetStringFromProp(pProp, lastName); + SanitizeValue(lastName); + } + pProp = m_mapi.GetMapiProperty(pUser, PR_MIDDLE_NAME); + if (pProp) { + m_mapi.GetStringFromProp(pProp, middleName); + SanitizeValue(middleName); + } + pProp = m_mapi.GetMapiProperty(pUser, PR_NICKNAME); + if (pProp) { + m_mapi.GetStringFromProp(pProp, nickName); + SanitizeValue(nickName); + } + if (firstName.IsEmpty() && lastName.IsEmpty()) { + firstName = pName; + } + + nsString displayName; + pProp = m_mapi.GetMapiProperty(pUser, PR_DISPLAY_NAME); + if (pProp) { + m_mapi.GetStringFromProp(pProp, displayName); + SanitizeValue(displayName); + } + if (displayName.IsEmpty()) { + if (firstName.IsEmpty()) + displayName = pName; + else { + displayName = firstName; + if (!middleName.IsEmpty()) { + displayName.Append(char16_t(' ')); + displayName.Append(middleName); + } + if (!lastName.IsEmpty()) { + displayName.Append(char16_t(' ')); + displayName.Append(lastName); + } + } + } + + // We now have the required fields + // write them out followed by any optional fields! + if (!displayName.IsEmpty()) { + pDb->AddDisplayName(newRow, NS_ConvertUTF16toUTF8(displayName).get()); + } + if (!firstName.IsEmpty()) { + pDb->AddFirstName(newRow, NS_ConvertUTF16toUTF8(firstName).get()); + } + if (!lastName.IsEmpty()) { + pDb->AddLastName(newRow, NS_ConvertUTF16toUTF8(lastName).get()); + } + if (!nickName.IsEmpty()) { + pDb->AddNickName(newRow, NS_ConvertUTF16toUTF8(nickName).get()); + } + if (!eMail.IsEmpty()) { + pDb->AddPrimaryEmail(newRow, NS_ConvertUTF16toUTF8(eMail).get()); + } + if (!secondEMail.IsEmpty()) { + pDb->Add2ndEmail(newRow, NS_ConvertUTF16toUTF8(secondEMail).get()); + } + + // Do all of the extra fields! + + nsString value; + nsString line2; + + if (pFieldMap) { + int max = sizeof(gMapiFields) / sizeof(MAPIFields); + for (int i = 0; i < max; i++) { + pProp = m_mapi.GetMapiProperty(pUser, gMapiFields[i].mapiTag); + if (pProp) { + m_mapi.GetStringFromProp(pProp, value); + if (!value.IsEmpty()) { + if (gMapiFields[i].multiLine == kNoMultiLine) { + SanitizeValue(value); + pFieldMap->SetFieldValue(pDb, newRow, gMapiFields[i].mozField, value.get()); + } + else if (gMapiFields[i].multiLine == kIsMultiLine) { + pFieldMap->SetFieldValue(pDb, newRow, gMapiFields[i].mozField, value.get()); + } + else { + line2.Truncate(); + SplitString(value, line2); + if (!value.IsEmpty()) + pFieldMap->SetFieldValue(pDb, newRow, gMapiFields[i].mozField, value.get()); + if (!line2.IsEmpty()) + pFieldMap->SetFieldValue(pDb, newRow, gMapiFields[i].multiLine, line2.get()); + } + } + } + } + } + + return true; +} |