summaryrefslogtreecommitdiffstats
path: root/mailnews/import
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/import')
-rw-r--r--mailnews/import/applemail/src/moz.build15
-rw-r--r--mailnews/import/applemail/src/nsAppleMailImport.cpp623
-rw-r--r--mailnews/import/applemail/src/nsAppleMailImport.h78
-rw-r--r--mailnews/import/applemail/src/nsEmlxHelperUtils.h55
-rw-r--r--mailnews/import/applemail/src/nsEmlxHelperUtils.mm240
-rw-r--r--mailnews/import/becky/src/moz.build16
-rw-r--r--mailnews/import/becky/src/nsBeckyAddressBooks.cpp383
-rw-r--r--mailnews/import/becky/src/nsBeckyAddressBooks.h35
-rw-r--r--mailnews/import/becky/src/nsBeckyFilters.cpp800
-rw-r--r--mailnews/import/becky/src/nsBeckyFilters.h77
-rw-r--r--mailnews/import/becky/src/nsBeckyImport.cpp168
-rw-r--r--mailnews/import/becky/src/nsBeckyImport.h36
-rw-r--r--mailnews/import/becky/src/nsBeckyMail.cpp641
-rw-r--r--mailnews/import/becky/src/nsBeckyMail.h45
-rw-r--r--mailnews/import/becky/src/nsBeckySettings.cpp471
-rw-r--r--mailnews/import/becky/src/nsBeckySettings.h52
-rw-r--r--mailnews/import/becky/src/nsBeckyStringBundle.cpp74
-rw-r--r--mailnews/import/becky/src/nsBeckyStringBundle.h33
-rw-r--r--mailnews/import/becky/src/nsBeckyUtils.cpp334
-rw-r--r--mailnews/import/becky/src/nsBeckyUtils.h37
-rw-r--r--mailnews/import/build/moz.build61
-rw-r--r--mailnews/import/build/nsImportModule.cpp194
-rw-r--r--mailnews/import/content/fieldMapImport.js186
-rw-r--r--mailnews/import/content/fieldMapImport.xul68
-rw-r--r--mailnews/import/content/import-test.html36
-rw-r--r--mailnews/import/content/importDialog.js1066
-rw-r--r--mailnews/import/content/importDialog.xul143
-rw-r--r--mailnews/import/outlook/src/MapiApi.cpp1937
-rw-r--r--mailnews/import/outlook/src/MapiApi.h265
-rw-r--r--mailnews/import/outlook/src/MapiDbgLog.h40
-rw-r--r--mailnews/import/outlook/src/MapiMessage.cpp1473
-rw-r--r--mailnews/import/outlook/src/MapiMessage.h271
-rw-r--r--mailnews/import/outlook/src/MapiMimeTypes.cpp96
-rw-r--r--mailnews/import/outlook/src/MapiMimeTypes.h31
-rw-r--r--mailnews/import/outlook/src/MapiTagStrs.cpp1070
-rw-r--r--mailnews/import/outlook/src/OutlookDebugLog.h24
-rw-r--r--mailnews/import/outlook/src/moz.build24
-rw-r--r--mailnews/import/outlook/src/nsOutlookCompose.cpp814
-rw-r--r--mailnews/import/outlook/src/nsOutlookCompose.h66
-rw-r--r--mailnews/import/outlook/src/nsOutlookImport.cpp589
-rw-r--r--mailnews/import/outlook/src/nsOutlookImport.h44
-rw-r--r--mailnews/import/outlook/src/nsOutlookMail.cpp863
-rw-r--r--mailnews/import/outlook/src/nsOutlookMail.h54
-rw-r--r--mailnews/import/outlook/src/nsOutlookSettings.cpp567
-rw-r--r--mailnews/import/outlook/src/nsOutlookSettings.h29
-rw-r--r--mailnews/import/outlook/src/nsOutlookStringBundle.cpp71
-rw-r--r--mailnews/import/outlook/src/nsOutlookStringBundle.h38
-rw-r--r--mailnews/import/outlook/src/rtfDecoder.cpp520
-rw-r--r--mailnews/import/outlook/src/rtfDecoder.h22
-rw-r--r--mailnews/import/outlook/src/rtfMailDecoder.cpp79
-rw-r--r--mailnews/import/outlook/src/rtfMailDecoder.h41
-rw-r--r--mailnews/import/public/moz.build21
-rw-r--r--mailnews/import/public/nsIImportABDescriptor.idl70
-rw-r--r--mailnews/import/public/nsIImportAddressBooks.idl153
-rw-r--r--mailnews/import/public/nsIImportFieldMap.idl72
-rw-r--r--mailnews/import/public/nsIImportFilters.idl32
-rw-r--r--mailnews/import/public/nsIImportGeneric.idl89
-rw-r--r--mailnews/import/public/nsIImportMail.idl98
-rw-r--r--mailnews/import/public/nsIImportMailboxDescriptor.idl46
-rw-r--r--mailnews/import/public/nsIImportMimeEncode.idl42
-rw-r--r--mailnews/import/public/nsIImportModule.idl32
-rw-r--r--mailnews/import/public/nsIImportService.idl59
-rw-r--r--mailnews/import/public/nsIImportSettings.idl39
-rw-r--r--mailnews/import/src/ImportCharSet.cpp58
-rw-r--r--mailnews/import/src/ImportCharSet.h175
-rw-r--r--mailnews/import/src/ImportDebug.h22
-rw-r--r--mailnews/import/src/ImportOutFile.cpp299
-rw-r--r--mailnews/import/src/ImportOutFile.h94
-rw-r--r--mailnews/import/src/ImportTranslate.cpp105
-rw-r--r--mailnews/import/src/ImportTranslate.h23
-rw-r--r--mailnews/import/src/moz.build25
-rw-r--r--mailnews/import/src/nsImportABDescriptor.cpp32
-rw-r--r--mailnews/import/src/nsImportABDescriptor.h103
-rw-r--r--mailnews/import/src/nsImportAddressBooks.cpp894
-rw-r--r--mailnews/import/src/nsImportEmbeddedImageData.cpp64
-rw-r--r--mailnews/import/src/nsImportEmbeddedImageData.h32
-rw-r--r--mailnews/import/src/nsImportEncodeScan.cpp374
-rw-r--r--mailnews/import/src/nsImportEncodeScan.h39
-rw-r--r--mailnews/import/src/nsImportFieldMap.cpp384
-rw-r--r--mailnews/import/src/nsImportFieldMap.h46
-rw-r--r--mailnews/import/src/nsImportMail.cpp1208
-rw-r--r--mailnews/import/src/nsImportMailboxDescriptor.cpp39
-rw-r--r--mailnews/import/src/nsImportMailboxDescriptor.h63
-rw-r--r--mailnews/import/src/nsImportMimeEncode.cpp411
-rw-r--r--mailnews/import/src/nsImportMimeEncode.h73
-rw-r--r--mailnews/import/src/nsImportScanFile.cpp172
-rw-r--r--mailnews/import/src/nsImportScanFile.h54
-rw-r--r--mailnews/import/src/nsImportService.cpp583
-rw-r--r--mailnews/import/src/nsImportService.h96
-rw-r--r--mailnews/import/src/nsImportStringBundle.cpp80
-rw-r--r--mailnews/import/src/nsImportStringBundle.h48
-rw-r--r--mailnews/import/src/nsImportTranslator.cpp296
-rw-r--r--mailnews/import/src/nsImportTranslator.h66
-rw-r--r--mailnews/import/text/src/TextDebugLog.h21
-rw-r--r--mailnews/import/text/src/moz.build16
-rw-r--r--mailnews/import/text/src/nsTextAddress.cpp470
-rw-r--r--mailnews/import/text/src/nsTextAddress.h57
-rw-r--r--mailnews/import/text/src/nsTextImport.cpp714
-rw-r--r--mailnews/import/text/src/nsTextImport.h39
-rw-r--r--mailnews/import/vcard/src/moz.build20
-rw-r--r--mailnews/import/vcard/src/nsVCardAddress.cpp139
-rw-r--r--mailnews/import/vcard/src/nsVCardAddress.h40
-rw-r--r--mailnews/import/vcard/src/nsVCardImport.cpp398
-rw-r--r--mailnews/import/vcard/src/nsVCardImport.h38
-rw-r--r--mailnews/import/winlivemail/WMDebugLog.h20
-rw-r--r--mailnews/import/winlivemail/moz.build14
-rw-r--r--mailnews/import/winlivemail/nsWMImport.cpp248
-rw-r--r--mailnews/import/winlivemail/nsWMImport.h38
-rw-r--r--mailnews/import/winlivemail/nsWMSettings.cpp758
-rw-r--r--mailnews/import/winlivemail/nsWMSettings.h22
-rw-r--r--mailnews/import/winlivemail/nsWMStringBundle.cpp71
-rw-r--r--mailnews/import/winlivemail/nsWMStringBundle.h38
-rw-r--r--mailnews/import/winlivemail/nsWMUtils.cpp164
-rw-r--r--mailnews/import/winlivemail/nsWMUtils.h27
114 files changed, 25058 insertions, 0 deletions
diff --git a/mailnews/import/applemail/src/moz.build b/mailnews/import/applemail/src/moz.build
new file mode 100644
index 000000000..1955cf30a
--- /dev/null
+++ b/mailnews/import/applemail/src/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsAppleMailImport.cpp',
+]
+
+SOURCES += [
+ 'nsEmlxHelperUtils.mm',
+]
+
+FINAL_LIBRARY = 'import'
+
diff --git a/mailnews/import/applemail/src/nsAppleMailImport.cpp b/mailnews/import/applemail/src/nsAppleMailImport.cpp
new file mode 100644
index 000000000..3097b7df5
--- /dev/null
+++ b/mailnews/import/applemail/src/nsAppleMailImport.cpp
@@ -0,0 +1,623 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportService.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsIFile.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsIMutableArray.h"
+#include "nsNetUtil.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+
+#include "nsEmlxHelperUtils.h"
+#include "nsAppleMailImport.h"
+#include "nsIOutputStream.h"
+
+PRLogModuleInfo *APPLEMAILLOGMODULE = nullptr;
+
+// some hard-coded strings
+#define DEFAULT_MAIL_FOLDER "~/Library/Mail/"
+#define POP_MBOX_SUFFIX ".mbox"
+#define IMAP_MBOX_SUFFIX ".imapmbox"
+
+// stringbundle URI
+#define APPLEMAIL_MSGS_URL "chrome://messenger/locale/appleMailImportMsgs.properties"
+
+// magic constants
+#define kAccountMailboxID 1234
+
+nsAppleMailImportModule::nsAppleMailImportModule()
+{
+ // Init logging module.
+ if (!APPLEMAILLOGMODULE)
+ APPLEMAILLOGMODULE = PR_NewLogModule("APPLEMAILIMPORTLOG");
+
+ IMPORT_LOG0("nsAppleMailImportModule Created");
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (bundleService)
+ bundleService->CreateBundle(APPLEMAIL_MSGS_URL, getter_AddRefs(mBundle));
+}
+
+
+nsAppleMailImportModule::~nsAppleMailImportModule()
+{
+ IMPORT_LOG0("nsAppleMailImportModule Deleted");
+}
+
+
+NS_IMPL_ISUPPORTS(nsAppleMailImportModule, nsIImportModule)
+
+
+NS_IMETHODIMP nsAppleMailImportModule::GetName(char16_t **aName)
+{
+ return mBundle ?
+ mBundle->GetStringFromName(u"ApplemailImportName", aName) : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAppleMailImportModule::GetDescription(char16_t **aName)
+{
+ return mBundle ?
+ mBundle->GetStringFromName(u"ApplemailImportDescription", aName) : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAppleMailImportModule::GetSupports(char **aSupports)
+{
+ NS_ENSURE_ARG_POINTER(aSupports);
+ *aSupports = strdup(NS_IMPORT_MAIL_STR);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAppleMailImportModule::GetSupportsUpgrade(bool *aUpgrade)
+{
+ NS_ENSURE_ARG_POINTER(aUpgrade);
+ *aUpgrade = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAppleMailImportModule::GetImportInterface(const char *aImportType, nsISupports **aInterface)
+{
+ NS_ENSURE_ARG_POINTER(aImportType);
+ NS_ENSURE_ARG_POINTER(aInterface);
+ *aInterface = nullptr;
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+
+ if (!strcmp(aImportType, "mail")) {
+ nsCOMPtr<nsIImportMail> mail(do_CreateInstance(NS_APPLEMAILIMPL_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportGeneric> generic;
+ rv = impSvc->CreateNewGenericMail(getter_AddRefs(generic));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString name;
+ rv = mBundle->GetStringFromName(u"ApplemailImportName", getter_Copies(name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> nameString(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nameString->SetData(name);
+
+ generic->SetData("name", nameString);
+ generic->SetData("mailInterface", mail);
+
+ generic.forget(aInterface);
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+#pragma mark -
+
+nsAppleMailImportMail::nsAppleMailImportMail() : mProgress(0), mCurDepth(0)
+{
+ IMPORT_LOG0("nsAppleMailImportMail created");
+}
+
+nsresult nsAppleMailImportMail::Initialize()
+{
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ return bundleService->CreateBundle(APPLEMAIL_MSGS_URL, getter_AddRefs(mBundle));
+}
+
+nsAppleMailImportMail::~nsAppleMailImportMail()
+{
+ IMPORT_LOG0("nsAppleMailImportMail destroyed");
+}
+
+NS_IMPL_ISUPPORTS(nsAppleMailImportMail, nsIImportMail)
+
+NS_IMETHODIMP nsAppleMailImportMail::GetDefaultLocation(nsIFile **aLocation, bool *aFound, bool *aUserVerify)
+{
+ NS_ENSURE_ARG_POINTER(aFound);
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(aUserVerify);
+
+ *aLocation = nullptr;
+ *aFound = false;
+ *aUserVerify = true;
+
+ // try to find current user's top-level Mail folder
+ nsCOMPtr<nsIFile> mailFolder(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
+ if (mailFolder) {
+ nsresult rv = mailFolder->InitWithNativePath(NS_LITERAL_CSTRING(DEFAULT_MAIL_FOLDER));
+ if (NS_SUCCEEDED(rv)) {
+ *aFound = true;
+ *aUserVerify = false;
+ mailFolder.forget(aLocation);
+ }
+ }
+
+ return NS_OK;
+}
+
+// this is the method that initiates all searching for mailboxes.
+// it will assume that it has a directory like ~/Library/Mail/
+NS_IMETHODIMP nsAppleMailImportMail::FindMailboxes(nsIFile *aMailboxFile, nsIArray **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aMailboxFile);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ IMPORT_LOG0("FindMailboxes for Apple mail invoked");
+
+ bool exists = false;
+ nsresult rv = aMailboxFile->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIImportService> importService(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> resultsArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (!resultsArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mCurDepth = 1;
+
+ // 1. look for accounts with mailboxes
+ FindAccountMailDirs(aMailboxFile, resultsArray, importService);
+ mCurDepth--;
+
+ if (NS_SUCCEEDED(rv)) {
+ // 2. look for "global" mailboxes, that don't belong to any specific account. they are inside the
+ // root's Mailboxes/ folder
+ nsCOMPtr<nsIFile> mailboxesDir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ mailboxesDir->InitWithFile(aMailboxFile);
+ rv = mailboxesDir->Append(NS_LITERAL_STRING("Mailboxes"));
+ if (NS_SUCCEEDED(rv)) {
+ IMPORT_LOG0("Looking for global Apple mailboxes");
+
+ mCurDepth++;
+ rv = FindMboxDirs(mailboxesDir, resultsArray, importService);
+ mCurDepth--;
+ }
+ }
+ }
+
+ if (NS_SUCCEEDED(rv) && resultsArray)
+ resultsArray.forget(aResult);
+
+ return rv;
+}
+
+// operates on the Mail/ directory root, trying to find accounts (which are folders named something like "POP-hwaara@gmail.com")
+// and add their .mbox dirs
+void nsAppleMailImportMail::FindAccountMailDirs(nsIFile *aRoot, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService)
+{
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ nsresult rv = aRoot->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ if (NS_FAILED(rv))
+ return;
+
+ bool hasMore = false;
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) && hasMore) {
+
+ // get the next file entry
+ nsCOMPtr<nsIFile> currentEntry;
+ {
+ nsCOMPtr<nsISupports> rawSupports;
+ directoryEnumerator->GetNext(getter_AddRefs(rawSupports));
+ if (!rawSupports)
+ continue;
+ currentEntry = do_QueryInterface(rawSupports);
+ if (!currentEntry)
+ continue;
+ }
+
+ // make sure it's a directory
+ bool isDirectory = false;
+ currentEntry->IsDirectory(&isDirectory);
+
+ if (isDirectory) {
+ // now let's see if it's an account folder. if so, we want to traverse it for .mbox children
+ nsAutoString folderName;
+ currentEntry->GetLeafName(folderName);
+ bool isAccountFolder = false;
+
+ if (StringBeginsWith(folderName, NS_LITERAL_STRING("POP-"))) {
+ // cut off "POP-" prefix so we get a nice folder name
+ folderName.Cut(0, 4);
+ isAccountFolder = true;
+ }
+ else if (StringBeginsWith(folderName, NS_LITERAL_STRING("IMAP-"))) {
+ // cut off "IMAP-" prefix so we get a nice folder name
+ folderName.Cut(0, 5);
+ isAccountFolder = true;
+ }
+
+ if (isAccountFolder) {
+ IMPORT_LOG1("Found account: %s\n", NS_ConvertUTF16toUTF8(folderName).get());
+
+ // create a mailbox for this account, so we get a parent for "Inbox", "Sent Messages", etc.
+ nsCOMPtr<nsIImportMailboxDescriptor> desc;
+ nsresult rv = aImportService->CreateNewMailboxDescriptor(getter_AddRefs(desc));
+ desc->SetSize(1);
+ desc->SetDepth(mCurDepth);
+ desc->SetDisplayName(folderName.get());
+ desc->SetIdentifier(kAccountMailboxID);
+
+ nsCOMPtr<nsIFile> mailboxDescFile;
+ rv = desc->GetFile(getter_AddRefs(mailboxDescFile));
+ if (!mailboxDescFile)
+ continue;
+
+ mailboxDescFile->InitWithFile(currentEntry);
+
+ // add this mailbox descriptor to the list
+ aMailboxDescs->AppendElement(desc, false);
+
+ // now add all the children mailboxes
+ mCurDepth++;
+ FindMboxDirs(currentEntry, aMailboxDescs, aImportService);
+ mCurDepth--;
+ }
+ }
+ }
+}
+
+// adds the specified file as a mailboxdescriptor to the array
+nsresult nsAppleMailImportMail::AddMboxDir(nsIFile *aFolder, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService)
+{
+ nsAutoString folderName;
+ aFolder->GetLeafName(folderName);
+
+ // cut off the suffix, if any, or prefix if this is an account folder.
+ if (StringEndsWith(folderName, NS_LITERAL_STRING(POP_MBOX_SUFFIX)))
+ folderName.SetLength(folderName.Length()-5);
+ else if (StringEndsWith(folderName, NS_LITERAL_STRING(IMAP_MBOX_SUFFIX)))
+ folderName.SetLength(folderName.Length()-9);
+ else if (StringBeginsWith(folderName, NS_LITERAL_STRING("POP-")))
+ folderName.Cut(4, folderName.Length());
+ else if (StringBeginsWith(folderName, NS_LITERAL_STRING("IMAP-")))
+ folderName.Cut(5, folderName.Length());
+
+ nsCOMPtr<nsIImportMailboxDescriptor> desc;
+ nsresult rv = aImportService->CreateNewMailboxDescriptor(getter_AddRefs(desc));
+ if (NS_SUCCEEDED(rv)) {
+ // find out number of messages in this .mbox
+ uint32_t numMessages = 0;
+ {
+ // move to the .mbox's Messages folder
+ nsCOMPtr<nsIFile> messagesFolder;
+ aFolder->Clone(getter_AddRefs(messagesFolder));
+ nsresult rv = messagesFolder->Append(NS_LITERAL_STRING("Messages"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // count the number of messages in this folder. it sucks that we have to iterate through the folder
+ // but XPCOM doesn't give us any way to just get the file count, unfortunately. :-(
+ nsCOMPtr<nsISimpleEnumerator> dirEnumerator;
+ messagesFolder->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
+ if (dirEnumerator) {
+ bool hasMore = false;
+ while (NS_SUCCEEDED(dirEnumerator->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> rawSupports;
+ dirEnumerator->GetNext(getter_AddRefs(rawSupports));
+ if (!rawSupports)
+ continue;
+
+ nsCOMPtr<nsIFile> file(do_QueryInterface(rawSupports));
+ if (file) {
+ bool isFile = false;
+ file->IsFile(&isFile);
+ if (isFile)
+ numMessages++;
+ }
+ }
+ }
+ }
+
+ desc->SetSize(numMessages);
+ desc->SetDisplayName(folderName.get());
+ desc->SetDepth(mCurDepth);
+
+ IMPORT_LOG3("Will import %s with approx %d messages, depth is %d", NS_ConvertUTF16toUTF8(folderName).get(), numMessages, mCurDepth);
+
+ // XXX: this is silly. there's no setter for the mailbox descriptor's file, so we need to get it, and then modify it.
+ nsCOMPtr<nsIFile> mailboxDescFile;
+ rv = desc->GetFile(getter_AddRefs(mailboxDescFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mailboxDescFile)
+ mailboxDescFile->InitWithFile(aFolder);
+
+ // add this mailbox descriptor to the list
+ aMailboxDescs->AppendElement(desc, false);
+ }
+
+ return NS_OK;
+}
+
+// Starts looking for .mbox dirs in the specified dir. The .mbox dirs contain messages and can be considered leafs in a tree of
+// nested mailboxes (subfolders).
+//
+// If a mailbox has sub-mailboxes, they are contained in a sibling folder with the same name without the ".mbox" part.
+// example:
+// MyParentMailbox.mbox/
+// MyParentMailbox/
+// MyChildMailbox.mbox/
+// MyOtherChildMailbox.mbox/
+//
+nsresult nsAppleMailImportMail::FindMboxDirs(nsIFile *aFolder, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aMailboxDescs);
+ NS_ENSURE_ARG_POINTER(aImportService);
+
+ // make sure this is a directory.
+ bool isDir = false;
+ if (NS_FAILED(aFolder->IsDirectory(&isDir)) || !isDir)
+ return NS_ERROR_FAILURE;
+
+ // iterate through the folder contents
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ nsresult rv = aFolder->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ if (NS_FAILED(rv) || !directoryEnumerator)
+ return rv;
+
+ bool hasMore = false;
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) && hasMore) {
+
+ // get the next file entry
+ nsCOMPtr<nsIFile> currentEntry;
+ {
+ nsCOMPtr<nsISupports> rawSupports;
+ directoryEnumerator->GetNext(getter_AddRefs(rawSupports));
+ if (!rawSupports)
+ continue;
+ currentEntry = do_QueryInterface(rawSupports);
+ if (!currentEntry)
+ continue;
+ }
+
+ // we only care about directories...
+ if (NS_FAILED(currentEntry->IsDirectory(&isDir)) || !isDir)
+ continue;
+
+ // now find out if this is a .mbox dir
+ nsAutoString currentFolderName;
+ if (NS_SUCCEEDED(currentEntry->GetLeafName(currentFolderName)) &&
+ (StringEndsWith(currentFolderName, NS_LITERAL_STRING(POP_MBOX_SUFFIX)) ||
+ StringEndsWith(currentFolderName, NS_LITERAL_STRING(IMAP_MBOX_SUFFIX)))) {
+ IMPORT_LOG1("Adding .mbox dir: %s", NS_ConvertUTF16toUTF8(currentFolderName).get());
+
+ // add this .mbox
+ rv = AddMboxDir(currentEntry, aMailboxDescs, aImportService);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("Couldn't add .mbox for import: %s ... continuing anyway", NS_ConvertUTF16toUTF8(currentFolderName).get());
+ continue;
+ }
+
+ // see if this .mbox dir has any sub-mailboxes
+ nsAutoString siblingMailboxDirPath;
+ currentEntry->GetPath(siblingMailboxDirPath);
+
+ // cut off suffix
+ if (StringEndsWith(siblingMailboxDirPath, NS_LITERAL_STRING(IMAP_MBOX_SUFFIX)))
+ siblingMailboxDirPath.SetLength(siblingMailboxDirPath.Length()-9);
+ else if (StringEndsWith(siblingMailboxDirPath, NS_LITERAL_STRING(POP_MBOX_SUFFIX)))
+ siblingMailboxDirPath.SetLength(siblingMailboxDirPath.Length()-5);
+
+ IMPORT_LOG1("trying to locate a '%s'", NS_ConvertUTF16toUTF8(siblingMailboxDirPath).get());
+ nsCOMPtr<nsIFile> siblingMailboxDir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ rv = siblingMailboxDir->InitWithPath(siblingMailboxDirPath);
+ bool reallyExists = false;
+ siblingMailboxDir->Exists(&reallyExists);
+
+ if (NS_SUCCEEDED(rv) && reallyExists) {
+ IMPORT_LOG1("Found what looks like an .mbox container: %s", NS_ConvertUTF16toUTF8(currentFolderName).get());
+
+ // traverse this folder for other .mboxes
+ mCurDepth++;
+ FindMboxDirs(siblingMailboxDir, aMailboxDescs, aImportService);
+ mCurDepth--;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppleMailImportMail::ImportMailbox(nsIImportMailboxDescriptor *aMailbox,
+ nsIMsgFolder *aDstFolder,
+ char16_t **aErrorLog,
+ char16_t **aSuccessLog, bool *aFatalError)
+{
+ nsAutoString errorLog, successLog;
+
+ // reset progress
+ mProgress = 0;
+
+ nsAutoString mailboxName;
+ aMailbox->GetDisplayName(getter_Copies(mailboxName));
+
+ nsCOMPtr<nsIFile> mboxFolder;
+ nsresult rv = aMailbox->GetFile(getter_AddRefs(mboxFolder));
+ if (NS_FAILED(rv) || !mboxFolder) {
+ ReportStatus(u"ApplemailImportMailboxConverterror", mailboxName, errorLog);
+ SetLogs(successLog, errorLog, aSuccessLog, aErrorLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ // if we're an account mailbox, nothing do. if we're a real mbox
+ // then we've got some messages to import!
+ uint32_t mailboxIdentifier;
+ aMailbox->GetIdentifier(&mailboxIdentifier);
+
+ if (mailboxIdentifier != kAccountMailboxID) {
+ // move to the .mbox's Messages folder
+ nsCOMPtr<nsIFile> messagesFolder;
+ mboxFolder->Clone(getter_AddRefs(messagesFolder));
+ rv = messagesFolder->Append(NS_LITERAL_STRING("Messages"));
+ if (NS_FAILED(rv)) {
+ // even if there are no messages, it might still be a valid mailbox, or even
+ // a parent for other mailboxes.
+ //
+ // just indicate that we're done, using the same number that we used to estimate
+ // number of messages earlier.
+ uint32_t finalSize;
+ aMailbox->GetSize(&finalSize);
+ mProgress = finalSize;
+
+ // report that we successfully imported this mailbox
+ ReportStatus(u"ApplemailImportMailboxSuccess", mailboxName, successLog);
+ SetLogs(successLog, errorLog, aSuccessLog, aErrorLog);
+ return NS_OK;
+ }
+
+ // let's import the messages!
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ rv = messagesFolder->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ if (NS_FAILED(rv)) {
+ ReportStatus(u"ApplemailImportMailboxConvertError", mailboxName, errorLog);
+ SetLogs(successLog, errorLog, aSuccessLog, aErrorLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ // prepare an outstream to the destination file
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = aDstFolder->GetMsgStore(getter_AddRefs(msgStore));
+ if (!msgStore || NS_FAILED(rv)) {
+ ReportStatus(u"ApplemailImportMailboxConverterror", mailboxName, errorLog);
+ SetLogs(successLog, errorLog, aSuccessLog, aErrorLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ bool hasMore = false;
+ nsCOMPtr<nsIOutputStream> outStream;
+
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) && hasMore) {
+ // get the next file entry
+ nsCOMPtr<nsIFile> currentEntry;
+ {
+ nsCOMPtr<nsISupports> rawSupports;
+ directoryEnumerator->GetNext(getter_AddRefs(rawSupports));
+ if (!rawSupports)
+ continue;
+ currentEntry = do_QueryInterface(rawSupports);
+ if (!currentEntry)
+ continue;
+ }
+
+ // make sure it's an .emlx file
+ bool isFile = false;
+ currentEntry->IsFile(&isFile);
+ if (!isFile)
+ continue;
+
+ nsAutoString leafName;
+ currentEntry->GetLeafName(leafName);
+ if (!StringEndsWith(leafName, NS_LITERAL_STRING(".emlx")))
+ continue;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ bool reusable;
+ rv = msgStore->GetNewMsgOutputStream(aDstFolder, getter_AddRefs(msgHdr),
+ &reusable,
+ getter_AddRefs(outStream));
+ if (NS_FAILED(rv))
+ break;
+
+ // add the data to the mbox stream
+ if (NS_SUCCEEDED(nsEmlxHelperUtils::AddEmlxMessageToStream(currentEntry, outStream))) {
+ mProgress++;
+ msgStore->FinishNewMessage(outStream, msgHdr);
+ }
+ else {
+ msgStore->DiscardNewMessage(outStream, msgHdr);
+ break;
+ }
+ if (!reusable)
+ outStream->Close();
+ }
+ if (outStream)
+ outStream->Close();
+ }
+ // just indicate that we're done, using the same number that we used to estimate
+ // number of messages earlier.
+ uint32_t finalSize;
+ aMailbox->GetSize(&finalSize);
+ mProgress = finalSize;
+
+ // report that we successfully imported this mailbox
+ ReportStatus(u"ApplemailImportMailboxSuccess", mailboxName, successLog);
+ SetLogs(successLog, errorLog, aSuccessLog, aErrorLog);
+
+ return NS_OK;
+}
+
+void nsAppleMailImportMail::ReportStatus(const char16_t* aErrorName, nsString &aName,
+ nsAString &aStream)
+{
+ // get (and format, if needed) the error string from the bundle
+ nsAutoString outString;
+ const char16_t *fmt = { aName.get() };
+ nsresult rv = mBundle->FormatStringFromName(aErrorName, &fmt, 1, getter_Copies(outString));
+ // write it out the stream
+ if (NS_SUCCEEDED(rv)) {
+ aStream.Append(outString);
+ aStream.Append(char16_t('\n'));
+ }
+}
+
+void nsAppleMailImportMail::SetLogs(const nsAString &aSuccess, const nsAString &aError, char16_t **aOutSuccess, char16_t **aOutError)
+{
+ if (aOutError && !*aOutError)
+ *aOutError = ToNewUnicode(aError);
+ if (aOutSuccess && !*aOutSuccess)
+ *aOutSuccess = ToNewUnicode(aSuccess);
+}
+
+NS_IMETHODIMP nsAppleMailImportMail::GetImportProgress(uint32_t *aDoneSoFar)
+{
+ NS_ENSURE_ARG_POINTER(aDoneSoFar);
+ *aDoneSoFar = mProgress;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAppleMailImportMail::TranslateFolderName(const nsAString &aFolderName, nsAString &aResult)
+{
+ aResult = aFolderName;
+ return NS_OK;
+}
diff --git a/mailnews/import/applemail/src/nsAppleMailImport.h b/mailnews/import/applemail/src/nsAppleMailImport.h
new file mode 100644
index 000000000..b906aecf5
--- /dev/null
+++ b/mailnews/import/applemail/src/nsAppleMailImport.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppleMailImport_h___
+#define nsAppleMailImport_h___
+
+#include "mozilla/Logging.h"
+#include "nsIImportModule.h"
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+#include "nsIImportMail.h"
+
+// logging facilities
+extern PRLogModuleInfo *APPLEMAILLOGMODULE;
+
+#define IMPORT_LOG0(x) MOZ_LOG(APPLEMAILLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(APPLEMAILLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(APPLEMAILLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(APPLEMAILLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+#define NS_APPLEMAILIMPL_CID \
+{ 0x9117a1ea, 0xe012, 0x43b5, { 0xa0, 0x20, 0xcb, 0x8a, 0x66, 0xcc, 0x09, 0xe1 } }
+
+#define NS_APPLEMAILIMPORT_CID \
+{ 0x6d3f101c, 0x70ec, 0x4e04, { 0xb6, 0x8d, 0x99, 0x08, 0xd1, 0xae, 0xdd, 0xf3 } }
+
+#define NS_APPLEMAILIMPL_CONTRACTID "@mozilla.org/import/import-appleMailImpl;1"
+
+#define kAppleMailSupportsString "mail"
+
+class nsIImportService;
+class nsIMutableArray;
+
+class nsAppleMailImportModule : public nsIImportModule
+{
+ public:
+
+ nsAppleMailImportModule();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIMPORTMODULE
+
+ private:
+ virtual ~nsAppleMailImportModule();
+
+ nsCOMPtr<nsIStringBundle> mBundle;
+};
+
+class nsAppleMailImportMail : public nsIImportMail
+{
+ public:
+
+ nsAppleMailImportMail();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIMPORTMAIL
+
+ nsresult Initialize();
+
+ private:
+ virtual ~nsAppleMailImportMail();
+
+ void FindAccountMailDirs(nsIFile *aRoot, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService);
+ nsresult FindMboxDirs(nsIFile *aFolder, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService);
+ nsresult AddMboxDir(nsIFile *aFolder, nsIMutableArray *aMailboxDescs, nsIImportService *aImportService);
+
+ // aInfoString is the format to a "foo %s" string. It may be NULL if the error string needs no such format.
+ void ReportStatus(const char16_t* aErrorName, nsString &aName, nsAString &aStream);
+ static void SetLogs(const nsAString& success, const nsAString& error, char16_t **aOutErrorLog, char16_t **aSuccessLog);
+
+ nsCOMPtr<nsIStringBundle> mBundle;
+ uint32_t mProgress;
+ uint16_t mCurDepth;
+};
+
+#endif /* nsAppleMailImport_h___ */
diff --git a/mailnews/import/applemail/src/nsEmlxHelperUtils.h b/mailnews/import/applemail/src/nsEmlxHelperUtils.h
new file mode 100644
index 000000000..728b725b6
--- /dev/null
+++ b/mailnews/import/applemail/src/nsEmlxHelperUtils.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEmlxHelperUtils_h___
+#define nsEmlxHelperUtils_h___
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+
+class nsIOutputStream;
+class nsIFile;
+
+class nsEmlxHelperUtils {
+ /* All emlx messages have a "flags" number in the metadata.
+ These are the masks to decode that, found via http://jwz.livejournal.com/505711.html */
+ enum EmlxMetadataMask {
+ kRead = 1 << 0, // read
+ // 1 << 1, // deleted
+ kAnswered = 1 << 2, // answered
+ // 1 << 3, // encrypted
+ kFlagged = 1 << 4, // flagged
+ // 1 << 5, // recent
+ // 1 << 6, // draft
+ // 1 << 7, // initial (no longer used)
+ kForwarded = 1 << 8, // forwarded
+ // 1 << 9, // redirected
+ // 3F << 10, // attachment count (6 bits)
+ // 7F << 16, // priority level (7 bits)
+ // 1 << 23, // signed
+ // 1 << 24, // is junk
+ // 1 << 25, // is not junk
+ // 1 << 26, // font size delta 7 (3 bits)
+ // 1 << 29, // junk mail level recorded
+ // 1 << 30, // highlight text in toc
+ // 1 << 31 // (unused)
+ };
+
+ // This method will scan the raw EMLX message buffer for "dangerous" so-called "From-lines" that we need to escape.
+ // If it needs to modify any lines, it will return a non-NULL aOutBuffer. If aOutBuffer is NULL, no modification needed
+ // to be made.
+ static nsresult ConvertToMboxRD(const char *aMessageBufferStart, const char *aMessageBufferEnd, nsCString &aOutBuffer);
+
+ // returns an int representing the X-Mozilla-Status flags set (e.g. "read", "flagged") converted from EMLX flags.
+ static nsresult ConvertToMozillaStatusFlags(const char *aXMLBufferStart, const char *aXMLBufferEnd, uint32_t *aMozillaStatusFlags);
+
+ public:
+
+ // add an .emlx message to the mbox output
+ static nsresult AddEmlxMessageToStream(nsIFile *aEmlxFile, nsIOutputStream *aOutoutStream);
+
+};
+
+#endif // nsEmlxHelperUtils_h___
diff --git a/mailnews/import/applemail/src/nsEmlxHelperUtils.mm b/mailnews/import/applemail/src/nsEmlxHelperUtils.mm
new file mode 100644
index 000000000..d2feb166e
--- /dev/null
+++ b/mailnews/import/applemail/src/nsEmlxHelperUtils.mm
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsEmlxHelperUtils.h"
+#include "nsIFileStreams.h"
+#include "nsIBufferedStreams.h"
+#include "nsIOutputStream.h"
+#include "nsNetUtil.h"
+#include "nsCOMPtr.h"
+#include "nsObjCExceptions.h"
+#include "nsMsgMessageFlags.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "msgCore.h"
+#include "nsTArray.h"
+#include "nsAppleMailImport.h"
+#include "prprf.h"
+#include "nsIFile.h"
+
+#import <Cocoa/Cocoa.h>
+
+
+nsresult nsEmlxHelperUtils::ConvertToMozillaStatusFlags(const char *aXMLBufferStart,
+ const char *aXMLBufferEnd,
+ uint32_t *aMozillaStatusFlags)
+{
+ // create a NSData wrapper around the buffer, so we can use the Cocoa call below
+ NSData *metadata =
+ [[[NSData alloc] initWithBytesNoCopy:(void *)aXMLBufferStart length:(aXMLBufferEnd-aXMLBufferStart) freeWhenDone:NO] autorelease];
+
+ // get the XML data as a dictionary
+ NSPropertyListFormat format;
+ id plist = [NSPropertyListSerialization propertyListWithData:metadata
+ options:NSPropertyListImmutable
+ format:&format
+ error:NULL];
+
+ if (!plist)
+ return NS_ERROR_FAILURE;
+
+ // find the <flags>...</flags> value and convert to int
+ const uint32_t emlxMessageFlags = [[(NSDictionary *)plist objectForKey:@"flags"] intValue];
+
+ if (emlxMessageFlags == 0)
+ return NS_ERROR_FAILURE;
+
+ if (emlxMessageFlags & nsEmlxHelperUtils::kRead)
+ *aMozillaStatusFlags |= nsMsgMessageFlags::Read;
+ if (emlxMessageFlags & nsEmlxHelperUtils::kForwarded)
+ *aMozillaStatusFlags |= nsMsgMessageFlags::Forwarded;
+ if (emlxMessageFlags & nsEmlxHelperUtils::kAnswered)
+ *aMozillaStatusFlags |= nsMsgMessageFlags::Replied;
+ if (emlxMessageFlags & nsEmlxHelperUtils::kFlagged)
+ *aMozillaStatusFlags |= nsMsgMessageFlags::Marked;
+
+ return NS_OK;
+}
+
+nsresult nsEmlxHelperUtils::ConvertToMboxRD(const char *aMessageBufferStart, const char *aMessageBufferEnd, nsCString &aOutBuffer)
+{
+ nsTArray<const char *> foundFromLines;
+
+ const char *cur = aMessageBufferStart;
+ while (cur < aMessageBufferEnd) {
+
+ const char *foundFromStr = strnstr(cur, "From ", aMessageBufferEnd-cur);
+
+ if (foundFromStr) {
+ // skip all prepending '>' chars
+ const char *fromLineStart = foundFromStr;
+ while (fromLineStart-- >= aMessageBufferStart) {
+ if (*fromLineStart == '\n' || fromLineStart == aMessageBufferStart) {
+ if (fromLineStart > aMessageBufferStart)
+ fromLineStart++;
+ foundFromLines.AppendElement(fromLineStart);
+ break;
+ }
+ else if (*fromLineStart != '>')
+ break;
+ }
+
+ // advance past the last found From string.
+ cur = foundFromStr + 5;
+
+ // look for more From lines.
+ continue;
+ }
+
+ break;
+ }
+
+ // go through foundFromLines
+ if (foundFromLines.Length()) {
+
+ const char *chunkStart = aMessageBufferStart;
+ for (unsigned i=0; i<foundFromLines.Length(); ++i) {
+ aOutBuffer.Append(chunkStart, (foundFromLines[i]-chunkStart));
+ aOutBuffer.Append(NS_LITERAL_CSTRING(">"));
+
+ chunkStart = foundFromLines[i];
+ }
+ aOutBuffer.Append(chunkStart, (aMessageBufferEnd - chunkStart));
+ }
+
+ return NS_OK;
+}
+
+nsresult nsEmlxHelperUtils::AddEmlxMessageToStream(nsIFile *aMessage, nsIOutputStream *aOut)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // needed to be sure autoreleased objects are released too, which they might not
+ // in a C++ environment where the main event loop has no autorelease pool (e.g on a XPCOM thread)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsAutoCString path;
+ aMessage->GetNativePath(path);
+
+ NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithUTF8String:path.get()]];
+ if (!data) {
+ [pool release];
+ return NS_ERROR_FAILURE;
+ }
+
+ char *startOfMessageData = NULL;
+ uint32_t actualBytesWritten = 0;
+
+ // The anatomy of an EMLX file:
+ //
+ // -------------------------------
+ // < A number describing how many bytes ahead there is message data >
+ // < Message data >
+ // < XML metadata for this message >
+ // -------------------------------
+
+ // read the first line of the emlx file, which is a number of how many bytes ahead the actual
+ // message data is.
+ uint64_t numberOfBytesToRead = strtol((char *)[data bytes], &startOfMessageData, 10);
+ if (numberOfBytesToRead <= 0 || !startOfMessageData) {
+ [pool release];
+ return NS_ERROR_FAILURE;
+ }
+
+ // skip whitespace
+ while (*startOfMessageData == ' ' ||
+ *startOfMessageData == '\n' ||
+ *startOfMessageData == '\r' ||
+ *startOfMessageData == '\t')
+ ++startOfMessageData;
+
+ NS_NAMED_LITERAL_CSTRING(kBogusFromLine, "From \n");
+ NS_NAMED_LITERAL_CSTRING(kEndOfMessage, "\n\n");
+
+ // write the bogus "From " line which is a magic separator in the mbox format
+ rv = aOut->Write(kBogusFromLine.get(), kBogusFromLine.Length(), &actualBytesWritten);
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ // now read the XML metadata, so we can extract info like which flags (read? replied? flagged? etc) this message has.
+ const char *startOfXMLMetadata = startOfMessageData + numberOfBytesToRead;
+ const char *endOfXMLMetadata = (char *)[data bytes] + [data length];
+
+ uint32_t x_mozilla_flags = 0;
+ ConvertToMozillaStatusFlags(startOfXMLMetadata, endOfXMLMetadata, &x_mozilla_flags);
+
+ // write the X-Mozilla-Status header according to which flags we've gathered above.
+ uint32_t dummyRv;
+ nsAutoCString buf(PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, x_mozilla_flags));
+ NS_ASSERTION(!buf.IsEmpty(), "printf error with X-Mozilla-Status header");
+ if (buf.IsEmpty()) {
+ [pool release];
+ return rv;
+ }
+
+ rv = aOut->Write(buf.get(), buf.Length(), &dummyRv);
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ // write out X-Mozilla-Keywords header as well to reserve some space for it
+ // in the mbox file.
+ rv = aOut->Write(X_MOZILLA_KEYWORDS, X_MOZILLA_KEYWORDS_LEN, &dummyRv);
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ // write out empty X-Mozilla_status2 header
+ buf.Adopt(PR_smprintf(X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, 0));
+ NS_ASSERTION(!buf.IsEmpty(), "printf error with X-Mozilla-Status2 header");
+ if (buf.IsEmpty()) {
+ [pool release];
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = aOut->Write(buf.get(), buf.Length(), &dummyRv);
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ // do any conversion needed for the mbox data to be valid mboxrd.
+ nsCString convertedData;
+ rv = ConvertToMboxRD(startOfMessageData, (startOfMessageData + numberOfBytesToRead), convertedData);
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ // write the actual message data.
+ if (convertedData.IsEmpty())
+ rv = aOut->Write(startOfMessageData, (uint32_t)numberOfBytesToRead, &actualBytesWritten);
+ else {
+ IMPORT_LOG1("Escaped From-lines in %s!", path.get());
+ rv = aOut->Write(convertedData.get(), convertedData.Length(), &actualBytesWritten);
+ }
+
+ if (NS_FAILED(rv)) {
+ [pool release];
+ return rv;
+ }
+
+ NS_ASSERTION(actualBytesWritten == (convertedData.IsEmpty() ? numberOfBytesToRead : convertedData.Length()),
+ "Didn't write as many bytes as expected for .emlx file?");
+
+ // add newlines to denote the end of this message in the mbox
+ rv = aOut->Write(kEndOfMessage.get(), kEndOfMessage.Length(), &actualBytesWritten);
+
+ [pool release];
+
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
diff --git a/mailnews/import/becky/src/moz.build b/mailnews/import/becky/src/moz.build
new file mode 100644
index 000000000..ce7651aa8
--- /dev/null
+++ b/mailnews/import/becky/src/moz.build
@@ -0,0 +1,16 @@
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ 'nsBeckyAddressBooks.cpp',
+ 'nsBeckyFilters.cpp',
+ 'nsBeckyImport.cpp',
+ 'nsBeckyMail.cpp',
+ 'nsBeckySettings.cpp',
+ 'nsBeckyStringBundle.cpp',
+ 'nsBeckyUtils.cpp',
+]
+
+FINAL_LIBRARY = 'import'
diff --git a/mailnews/import/becky/src/nsBeckyAddressBooks.cpp b/mailnews/import/becky/src/nsBeckyAddressBooks.cpp
new file mode 100644
index 000000000..86654ac0e
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyAddressBooks.cpp
@@ -0,0 +1,383 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIMutableArray.h"
+#include "nsStringGlue.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbManager.h"
+#include "nsIImportService.h"
+#include "nsIImportABDescriptor.h"
+#include "nsMsgUtils.h"
+#include "nsIStringBundle.h"
+#include "nsVCardAddress.h"
+
+#include "nsBeckyAddressBooks.h"
+#include "nsBeckyStringBundle.h"
+#include "nsBeckyUtils.h"
+
+NS_IMPL_ISUPPORTS(nsBeckyAddressBooks, nsIImportAddressBooks)
+
+nsresult
+nsBeckyAddressBooks::Create(nsIImportAddressBooks **aImport)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+
+ *aImport = new nsBeckyAddressBooks();
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsBeckyAddressBooks::nsBeckyAddressBooks()
+: mReadBytes(0)
+{
+}
+
+nsBeckyAddressBooks::~nsBeckyAddressBooks()
+{
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetSupportsMultiple(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetAutoFind(char16_t **aDescription,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(aDescription);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *aDescription =
+ nsBeckyStringBundle::GetStringByName(u"BeckyImportDescription");
+ *_retval = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetNeedsFieldMap(nsIFile *aLocation, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = false;
+ return NS_OK;
+}
+
+nsresult
+nsBeckyAddressBooks::FindAddressBookDirectory(nsIFile **aAddressBookDirectory)
+{
+ nsCOMPtr<nsIFile> userDirectory;
+ nsresult rv = nsBeckyUtils::FindUserDirectory(getter_AddRefs(userDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = userDirectory->Append(NS_LITERAL_STRING("AddrBook"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists = false;
+ rv = userDirectory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ bool isDirectory = false;
+ rv = userDirectory->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isDirectory)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ userDirectory.forget(aAddressBookDirectory);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetDefaultLocation(nsIFile **aLocation,
+ bool *aFound,
+ bool *aUserVerify)
+{
+ NS_ENSURE_ARG_POINTER(aFound);
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(aUserVerify);
+
+ *aLocation = nullptr;
+ *aFound = false;
+ *aUserVerify = true;
+
+ if (NS_SUCCEEDED(nsBeckyAddressBooks::FindAddressBookDirectory(aLocation))) {
+ *aFound = true;
+ *aUserVerify = false;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyAddressBooks::CreateAddressBookDescriptor(nsIImportABDescriptor **aDescriptor)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportService> importService = do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return importService->CreateNewABDescriptor(aDescriptor);
+}
+
+bool
+nsBeckyAddressBooks::IsAddressBookFile(nsIFile *aFile)
+{
+ if (!aFile)
+ return false;
+
+ nsresult rv;
+ bool isFile = false;
+ rv = aFile->IsFile(&isFile);
+ if (NS_FAILED(rv) && !isFile)
+ return false;
+
+ nsAutoString name;
+ rv = aFile->GetLeafName(name);
+ return StringEndsWith(name, NS_LITERAL_STRING(".bab"));
+}
+
+bool
+nsBeckyAddressBooks::HasAddressBookFile(nsIFile *aDirectory)
+{
+ if (!aDirectory)
+ return false;
+
+ nsresult rv;
+ bool isDirectory = false;
+ rv = aDirectory->IsDirectory(&isDirectory);
+ if (NS_FAILED(rv) || !isDirectory)
+ return false;
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ rv = entries->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ if (IsAddressBookFile(file))
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+nsBeckyAddressBooks::CountAddressBookSize(nsIFile *aDirectory)
+{
+ if (!aDirectory)
+ return 0;
+
+ nsresult rv;
+ bool isDirectory = false;
+ rv = aDirectory->IsDirectory(&isDirectory);
+ if (NS_FAILED(rv) || !isDirectory)
+ return 0;
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ uint32_t total = 0;
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ rv = entries->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ int64_t size;
+ file->GetFileSize(&size);
+ if (total + size > std::numeric_limits<uint32_t>::max())
+ return std::numeric_limits<uint32_t>::max();
+
+ total += static_cast<uint32_t>(size);
+ }
+
+ return total;
+}
+
+nsresult
+nsBeckyAddressBooks::AppendAddressBookDescriptor(nsIFile *aEntry,
+ nsIMutableArray *aCollected)
+{
+ NS_ENSURE_ARG_POINTER(aCollected);
+
+ if (!HasAddressBookFile(aEntry))
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIImportABDescriptor> descriptor;
+ rv = CreateAddressBookDescriptor(getter_AddRefs(descriptor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t size = CountAddressBookSize(aEntry);
+ descriptor->SetSize(size);
+ descriptor->SetAbFile(aEntry);
+
+ nsAutoString name;
+ aEntry->GetLeafName(name);
+ descriptor->SetPreferredName(name);
+
+ return aCollected->AppendElement(descriptor, false);
+}
+
+nsresult
+nsBeckyAddressBooks::CollectAddressBooks(nsIFile *aTarget,
+ nsIMutableArray *aCollected)
+{
+ nsresult rv = AppendAddressBookDescriptor(aTarget, aCollected);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = aTarget->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ rv = entries->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDirectory = false;
+ rv = file->IsDirectory(&isDirectory);
+ if (NS_SUCCEEDED(rv) && isDirectory)
+ rv = CollectAddressBooks(file, aCollected);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::FindAddressBooks(nsIFile *aLocation,
+ nsIArray **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDirectory = false;
+ rv = aLocation->IsDirectory(&isDirectory);
+ if (NS_FAILED(rv) || !isDirectory)
+ return NS_ERROR_FAILURE;
+
+ rv = CollectAddressBooks(aLocation, array);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ array.forget(_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::InitFieldMap(nsIImportFieldMap *aFieldMap)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::ImportAddressBook(nsIImportABDescriptor *aSource,
+ nsIAddrDatabase *aDestination,
+ nsIImportFieldMap *aFieldMap,
+ nsISupports *aSupportService,
+ char16_t **aErrorLog,
+ char16_t **aSuccessLog,
+ bool *aFatalError)
+{
+ NS_ENSURE_ARG_POINTER(aSource);
+ NS_ENSURE_ARG_POINTER(aDestination);
+ NS_ENSURE_ARG_POINTER(aErrorLog);
+ NS_ENSURE_ARG_POINTER(aSuccessLog);
+ NS_ENSURE_ARG_POINTER(aFatalError);
+
+ mReadBytes = 0;
+
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = aSource->GetAbFile(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = file->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ nsAutoString error;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ rv = entries->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!IsAddressBookFile(file))
+ continue;
+
+ bool aborted = false;
+ nsAutoString name;
+ aSource->GetPreferredName(name);
+ nsVCardAddress vcard;
+ rv = vcard.ImportAddresses(&aborted, name.get(), file, aDestination, error, &mReadBytes);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ }
+
+ if (!error.IsEmpty())
+ *aErrorLog = ToNewUnicode(error);
+ else
+ *aSuccessLog = nsBeckyStringBundle::GetStringByName(u"BeckyImportAddressSuccess");
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetImportProgress(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mReadBytes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::SetSampleLocation(nsIFile *aLocation)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::GetSampleData(int32_t aRecordNumber,
+ bool *aRecordExists,
+ char16_t **_retval)
+{
+ return NS_ERROR_FAILURE;
+}
+
diff --git a/mailnews/import/becky/src/nsBeckyAddressBooks.h b/mailnews/import/becky/src/nsBeckyAddressBooks.h
new file mode 100644
index 000000000..83eb4c895
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyAddressBooks.h
@@ -0,0 +1,35 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBeckyAddressBooks_h___
+#define nsBeckyAddressBooks_h___
+
+#include "nsIImportAddressBooks.h"
+
+class nsBeckyAddressBooks final : public nsIImportAddressBooks
+{
+public:
+ nsBeckyAddressBooks();
+ static nsresult Create(nsIImportAddressBooks **aImport);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTADDRESSBOOKS
+
+private:
+ virtual ~nsBeckyAddressBooks();
+
+ uint32_t mReadBytes;
+
+ nsresult CollectAddressBooks(nsIFile *aTarget, nsIMutableArray *aCollected);
+ nsresult FindAddressBookDirectory(nsIFile **aAddressBookDirectory);
+ nsresult AppendAddressBookDescriptor(nsIFile *aEntry,
+ nsIMutableArray *aCollected);
+ uint32_t CountAddressBookSize(nsIFile *aDirectory);
+ bool HasAddressBookFile(nsIFile *aDirectory);
+ bool IsAddressBookFile(nsIFile *aFile);
+ nsresult CreateAddressBookDescriptor(nsIImportABDescriptor **aDescriptor);
+};
+
+#endif /* nsBeckyAddressBooks_h___ */
diff --git a/mailnews/import/becky/src/nsBeckyFilters.cpp b/mailnews/import/becky/src/nsBeckyFilters.cpp
new file mode 100644
index 000000000..97b99fec2
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyFilters.cpp
@@ -0,0 +1,800 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nsArrayUtils.h"
+#include "nsILineInputStream.h"
+#include "nsIStringBundle.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIMsgFilter.h"
+#include "nsIMsgFilterList.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsIMsgSearchTerm.h"
+#include "nsIMsgFolder.h"
+#include "nsCOMPtr.h"
+#include "nsMsgSearchCore.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgUtils.h"
+#include "msgCore.h"
+
+#include "nsBeckyFilters.h"
+#include "nsBeckyStringBundle.h"
+#include "nsBeckyUtils.h"
+
+NS_IMPL_ISUPPORTS(nsBeckyFilters, nsIImportFilters)
+
+nsresult
+nsBeckyFilters::Create(nsIImportFilters **aImport)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+
+ *aImport = new nsBeckyFilters();
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsBeckyFilters::nsBeckyFilters()
+: mLocation(nullptr),
+ mServer(nullptr),
+ mConvertedFile(nullptr)
+{
+}
+
+nsBeckyFilters::~nsBeckyFilters()
+{
+}
+
+nsresult
+nsBeckyFilters::GetDefaultFilterLocation(nsIFile **aFile)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> filterDir;
+ rv = nsBeckyUtils::GetDefaultMailboxDirectory(getter_AddRefs(filterDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ filterDir.forget(aFile);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::GetFilterFile(bool aIncoming, nsIFile *aLocation, nsIFile **aFile)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(aFile);
+
+ // We assume the caller has already checked that aLocation is a directory,
+ // otherwise it would not make sense to call us.
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> filter;
+ aLocation->Clone(getter_AddRefs(filter));
+ if (aIncoming)
+ rv = filter->Append(NS_LITERAL_STRING("IFilter.def"));
+ else
+ rv = filter->Append(NS_LITERAL_STRING("OFilter.def"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists = false;
+ rv = filter->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ filter.forget(aFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyFilters::AutoLocate(char16_t **aDescription,
+ nsIFile **aLocation,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (aDescription) {
+ *aDescription =
+ nsBeckyStringBundle::GetStringByName(u"BeckyImportDescription");
+ }
+ *aLocation = nullptr;
+ *_retval = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> location;
+ rv = GetDefaultFilterLocation(getter_AddRefs(location));
+ if (NS_FAILED(rv))
+ location = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ else
+ *_retval = true;
+
+ location.forget(aLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyFilters::SetLocation(nsIFile *aLocation)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ bool exists = false;
+ nsresult rv = aLocation->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ mLocation = aLocation;
+ return NS_OK;
+}
+
+static nsMsgSearchAttribValue
+ConvertSearchKeyToAttrib(const nsACString &aKey)
+{
+ if (aKey.EqualsLiteral("From") ||
+ aKey.EqualsLiteral("Sender") ||
+ aKey.EqualsLiteral("From, Sender, X-Sender")) {
+ return nsMsgSearchAttrib::Sender;
+ } else if (aKey.EqualsLiteral("Subject")) {
+ return nsMsgSearchAttrib::Subject;
+ } else if (aKey.EqualsLiteral("[body]")) {
+ return nsMsgSearchAttrib::Body;
+ } else if (aKey.EqualsLiteral("Date")) {
+ return nsMsgSearchAttrib::Date;
+ } else if (aKey.EqualsLiteral("To")) {
+ return nsMsgSearchAttrib::To;
+ } else if (aKey.EqualsLiteral("Cc")) {
+ return nsMsgSearchAttrib::CC;
+ } else if (aKey.EqualsLiteral("To, Cc, Bcc:")) {
+ return nsMsgSearchAttrib::ToOrCC;
+ }
+ return -1;
+}
+
+static nsMsgSearchOpValue
+ConvertSearchFlagsToOperator(const nsACString &aFlags)
+{
+ nsCString flags(aFlags);
+ int32_t lastTabPosition = flags.RFindChar('\t');
+ if ((lastTabPosition == -1) ||
+ ((int32_t)aFlags.Length() == lastTabPosition - 1)) {
+ return -1;
+ }
+
+ switch (aFlags.CharAt(0)) {
+ case 'X':
+ return nsMsgSearchOp::DoesntContain;
+ case 'O':
+ if (aFlags.FindChar('T', lastTabPosition + 1) >= 0)
+ return nsMsgSearchOp::BeginsWith;
+ return nsMsgSearchOp::Contains;
+ default:
+ return -1;
+ }
+}
+
+nsresult
+nsBeckyFilters::ParseRuleLine(const nsCString &aLine,
+ nsMsgSearchAttribValue *aSearchAttribute,
+ nsMsgSearchOpValue *aSearchOperator,
+ nsString &aSearchKeyword)
+{
+ int32_t firstColonPosition = aLine.FindChar(':');
+ if (firstColonPosition == -1 ||
+ (int32_t)aLine.Length() == firstColonPosition - 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t secondColonPosition = aLine.FindChar(':', firstColonPosition + 1);
+ if (secondColonPosition == -1 ||
+ (int32_t)aLine.Length() == secondColonPosition - 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t length = secondColonPosition - firstColonPosition - 1;
+ nsMsgSearchAttribValue searchAttribute;
+ searchAttribute = ConvertSearchKeyToAttrib(Substring(aLine, firstColonPosition + 1, length));
+ if (searchAttribute < 0)
+ return NS_ERROR_FAILURE;
+
+ int32_t tabPosition = aLine.FindChar('\t');
+ if (tabPosition == -1 ||
+ (int32_t)aLine.Length() == tabPosition - 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsMsgSearchOpValue searchOperator;
+ searchOperator = ConvertSearchFlagsToOperator(Substring(aLine, tabPosition + 1));
+ if (searchOperator < 0)
+ return NS_ERROR_FAILURE;
+
+ *aSearchOperator = searchOperator;
+ *aSearchAttribute = searchAttribute;
+ length = tabPosition - secondColonPosition - 1;
+ CopyUTF8toUTF16(Substring(aLine, secondColonPosition + 1, length), aSearchKeyword);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::SetSearchTerm(const nsCString &aLine, nsIMsgFilter *aFilter)
+{
+ NS_ENSURE_ARG_POINTER(aFilter);
+
+ nsresult rv;
+ nsMsgSearchAttribValue searchAttribute = -1;
+ nsMsgSearchOpValue searchOperator = -1;
+ nsAutoString searchKeyword;
+ rv = ParseRuleLine(aLine, &searchAttribute, &searchOperator, searchKeyword);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgSearchTerm> term;
+ rv = aFilter->CreateTerm(getter_AddRefs(term));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = term->SetAttrib(searchAttribute);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = term->SetOp(searchOperator);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgSearchValue> value;
+ rv = term->GetValue(getter_AddRefs(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = value->SetAttrib(searchAttribute);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = value->SetStr(searchKeyword);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = term->SetValue(value);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = term->SetBooleanAnd(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!searchKeyword.IsEmpty())
+ rv = aFilter->SetFilterName(searchKeyword);
+ else
+ rv = aFilter->SetFilterName(NS_LITERAL_STRING("No name"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aFilter->AppendTerm(term);
+}
+
+nsresult
+nsBeckyFilters::CreateRuleAction(nsIMsgFilter *aFilter,
+ nsMsgRuleActionType actionType,
+ nsIMsgRuleAction **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgRuleAction> action;
+ rv = aFilter->CreateAction(getter_AddRefs(action));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = action->SetType(actionType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ action.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::GetActionTarget(const nsCString &aLine,
+ nsCString &aTarget)
+{
+ int32_t firstColonPosition = aLine.FindChar(':');
+ if (firstColonPosition < -1 ||
+ aLine.Length() == static_cast<uint32_t>(firstColonPosition)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aTarget.Assign(Substring(aLine, firstColonPosition + 1));
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::GetResendTarget(const nsCString &aLine,
+ nsCString &aTemplate,
+ nsCString &aTargetAddress)
+{
+ nsresult rv;
+ nsAutoCString target;
+ rv = GetActionTarget(aLine, target);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t asteriskPosition = target.FindChar('*');
+ if (asteriskPosition < 0) {
+ aTemplate.Assign(target);
+ return NS_OK;
+ }
+
+ if (target.Length() == static_cast<uint32_t>(asteriskPosition))
+ return NS_ERROR_FAILURE;
+
+ aTemplate.Assign(StringHead(target, asteriskPosition - 1));
+ aTargetAddress.Assign(Substring(target, asteriskPosition + 1));
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::CreateResendAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ const nsMsgRuleActionType &aActionType,
+ nsIMsgRuleAction **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgRuleAction> action;
+ rv = CreateRuleAction(aFilter, aActionType, getter_AddRefs(action));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString templateString;
+ nsAutoCString targetAddress;
+ rv = GetResendTarget(aLine, templateString, targetAddress);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aActionType == nsMsgFilterAction::Forward)
+ rv = action->SetStrValue(targetAddress);
+ else
+ rv = action->SetStrValue(templateString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ action.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::GetFolderNameFromTarget(const nsCString &aTarget, nsAString &aName)
+{
+ int32_t backslashPosition = aTarget.RFindChar('\\');
+ if (backslashPosition > 0) {
+ NS_ConvertUTF8toUTF16 utf16String(Substring(aTarget, backslashPosition + 1));
+ nsBeckyUtils::TranslateFolderName(utf16String, aName);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::GetDistributeTarget(const nsCString &aLine,
+ nsCString &aTargetFolder)
+{
+ nsresult rv;
+ nsAutoCString target;
+ rv = GetActionTarget(aLine, target);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ target.Trim("\\", false, true);
+ nsAutoString folderName;
+ rv = GetFolderNameFromTarget(target, folderName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIMsgFolder> folder;
+ rv = GetMessageFolder(folderName, getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!folder) {
+ rv = mServer->GetRootMsgFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return folder->GetURI(aTargetFolder);
+}
+
+nsresult
+nsBeckyFilters::CreateDistributeAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ const nsMsgRuleActionType &aActionType,
+ nsIMsgRuleAction **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgRuleAction> action;
+ rv = CreateRuleAction(aFilter, aActionType, getter_AddRefs(action));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString targetFolder;
+ rv = GetDistributeTarget(aLine, targetFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = action->SetTargetFolderUri(targetFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ action.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::CreateLeaveOrDeleteAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ nsIMsgRuleAction **_retval)
+{
+ nsresult rv;
+ nsMsgRuleActionType actionType;
+ if (aLine.CharAt(3) == '0') {
+ actionType = nsMsgFilterAction::LeaveOnPop3Server;
+ } else if (aLine.CharAt(3) == '1') {
+ if (aLine.CharAt(5) == '1')
+ actionType = nsMsgFilterAction::Delete;
+ else
+ actionType = nsMsgFilterAction::DeleteFromPop3Server;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIMsgRuleAction> action;
+ rv = CreateRuleAction(aFilter, actionType, getter_AddRefs(action));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ action.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::SetRuleAction(const nsCString &aLine, nsIMsgFilter *aFilter)
+{
+ if (!aFilter || aLine.Length() < 4)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgRuleAction> action;
+ switch (aLine.CharAt(1)) {
+ case 'R': // Reply
+ rv = CreateResendAction(aLine,
+ aFilter,
+ nsMsgFilterAction::Reply,
+ getter_AddRefs(action));
+ break;
+ case 'F': // Forward
+ rv = CreateResendAction(aLine,
+ aFilter,
+ nsMsgFilterAction::Forward,
+ getter_AddRefs(action));
+ break;
+ case 'L': // Leave or delete
+ rv = CreateLeaveOrDeleteAction(aLine, aFilter, getter_AddRefs(action));
+ break;
+ case 'Y': // Copy
+ rv = CreateDistributeAction(aLine,
+ aFilter,
+ nsMsgFilterAction::CopyToFolder,
+ getter_AddRefs(action));
+ break;
+ case 'M': // Move
+ rv = CreateDistributeAction(aLine,
+ aFilter,
+ nsMsgFilterAction::MoveToFolder,
+ getter_AddRefs(action));
+ break;
+ case 'G': // Set flag
+ if (aLine.CharAt(3) == 'R') // Read
+ rv = CreateRuleAction(aFilter, nsMsgFilterAction::MarkRead, getter_AddRefs(action));
+ break;
+ default:
+ return NS_OK;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (action) {
+ rv = aFilter->AppendAction(action);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::CreateFilter(bool aIncoming, nsIMsgFilter **_retval)
+{
+ NS_ENSURE_STATE(mServer);
+
+ nsCOMPtr <nsIMsgFilterList> filterList;
+ nsresult rv = mServer->GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFilter> filter;
+ rv = filterList->CreateFilter(EmptyString(), getter_AddRefs(filter));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aIncoming)
+ filter->SetFilterType(nsMsgFilterType::InboxRule | nsMsgFilterType::Manual);
+ else
+ filter->SetFilterType(nsMsgFilterType::PostOutgoing | nsMsgFilterType::Manual);
+
+ filter->SetEnabled(true);
+ filter.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::AppendFilter(nsIMsgFilter *aFilter)
+{
+ NS_ENSURE_STATE(mServer);
+
+ nsCOMPtr <nsIMsgFilterList> filterList;
+ nsresult rv = mServer->GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t count;
+ rv = filterList->GetFilterCount(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return filterList->InsertFilterAt(count, aFilter);
+}
+
+nsresult
+nsBeckyFilters::ParseFilterFile(nsIFile *aFile, bool aIncoming)
+{
+ nsresult rv;
+ nsCOMPtr<nsILineInputStream> lineStream;
+ rv = nsBeckyUtils::CreateLineInputStream(aFile, getter_AddRefs(lineStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ nsAutoCString line;
+
+ nsCOMPtr<nsIMsgFilter> filter;
+ while (NS_SUCCEEDED(rv) && more) {
+ rv = lineStream->ReadLine(line, &more);
+
+ switch (line.CharAt(0)) {
+ case ':':
+ if (line.EqualsLiteral(":Begin \"\"")) {
+ CreateFilter(aIncoming, getter_AddRefs(filter));
+ } else if (line.EqualsLiteral(":End \"\"")) {
+ if (filter)
+ AppendFilter(filter);
+ filter = nullptr;
+ }
+ break;
+ case '!':
+ SetRuleAction(line, filter);
+ break;
+ case '@':
+ SetSearchTerm(line, filter);
+ break;
+ case '$': // $X: disabled
+ if (StringBeginsWith(line, NS_LITERAL_CSTRING("$X")) && filter) {
+ filter->SetEnabled(false);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyFilters::Import(char16_t **aError,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(aError);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // If mLocation is null, set it to the default filter directory.
+ // If mLocation is a file, we import it as incoming folder.
+ // If mLocation is a directory, we try to import incoming and outgoing folders
+ // from it (in default files).
+
+ *_retval = false;
+ nsresult rv;
+ nsCOMPtr<nsIFile> filterFile;
+
+ bool haveFile = false;
+
+ if (!mLocation) {
+ bool retval = false;
+ rv = AutoLocate(nullptr, getter_AddRefs(mLocation), &retval);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!retval)
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // What type of location do we have?
+ bool isDirectory = false;
+ rv = mLocation->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (isDirectory) {
+ haveFile = false;
+ } else {
+ bool isFile = false;
+ rv = mLocation->IsFile(&isFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (isFile) {
+ haveFile = true;
+ mLocation->Clone(getter_AddRefs(filterFile));
+ } else {
+ // mLocation is neither file nor directory.
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ bool haveIncoming = true;
+ if (haveFile) {
+ // If the passed filename equals OFilter.def, import as outgoing filters.
+ // Everything else is considered incoming.
+ nsAutoString fileName;
+ rv = mLocation->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (fileName.EqualsLiteral("OFilter.def"))
+ haveIncoming = false;
+ }
+
+ // Try importing from the passed in file or the default incoming filters file.
+ if ((haveFile && haveIncoming) || (!haveFile &&
+ NS_SUCCEEDED(GetFilterFile(true, mLocation, getter_AddRefs(filterFile)))))
+ {
+ rv = CollectServers();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = nsBeckyUtils::ConvertToUTF8File(filterFile, getter_AddRefs(mConvertedFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ParseFilterFile(mConvertedFile, true);
+ if (NS_SUCCEEDED(rv))
+ *_retval = true;
+
+ (void)RemoveConvertedFile();
+ }
+
+ // If we didn't have a file passed (but a directory), try finding also outgoing filters.
+ if ((haveFile && !haveIncoming) || (!haveFile &&
+ NS_SUCCEEDED(GetFilterFile(false, mLocation, getter_AddRefs(filterFile)))))
+ {
+ rv = CollectServers();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = nsBeckyUtils::ConvertToUTF8File(filterFile, getter_AddRefs(mConvertedFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ParseFilterFile(mConvertedFile, false);
+ if (NS_SUCCEEDED(rv))
+ *_retval = true;
+
+ (void)RemoveConvertedFile();
+ }
+
+ return rv;
+}
+
+nsresult
+nsBeckyFilters::FindMessageFolder(const nsAString &aName,
+ nsIMsgFolder *aParentFolder,
+ nsIMsgFolder **_retval)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgFolder> found;
+ rv = aParentFolder->GetChildNamed(aName, getter_AddRefs(found));
+ if (found) {
+ found.forget(_retval);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> children;
+ rv = aParentFolder->GetSubFolders(getter_AddRefs(children));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ while (NS_SUCCEEDED(children->HasMoreElements(&more)) && more) {
+ rv = children->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> child = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = FindMessageFolder(aName, child, getter_AddRefs(found));
+ if (found) {
+ found.forget(_retval);
+ return NS_OK;
+ }
+ }
+
+ return NS_MSG_ERROR_INVALID_FOLDER_NAME;
+}
+
+nsresult
+nsBeckyFilters::FindMessageFolderInServer(const nsAString &aName,
+ nsIMsgIncomingServer *aServer,
+ nsIMsgFolder **_retval)
+{
+ nsresult rv;
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ rv = aServer->GetRootMsgFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return FindMessageFolder(aName, rootFolder, _retval);
+}
+
+nsresult
+nsBeckyFilters::GetMessageFolder(const nsAString &aName,
+ nsIMsgFolder **_retval)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager;
+ accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIArray> accounts;
+ rv = accountManager->GetAccounts(getter_AddRefs(accounts));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t accountCount;
+ rv = accounts->GetLength(&accountCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> found;
+ for (uint32_t i = 0; i < accountCount; i++) {
+ nsCOMPtr<nsIMsgAccount> account(do_QueryElementAt(accounts, i));
+ if (!account)
+ continue;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ account->GetIncomingServer(getter_AddRefs(server));
+ if (!server)
+ continue;
+ FindMessageFolderInServer(aName, server, getter_AddRefs(found));
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = accountManager->GetLocalFoldersServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ FindMessageFolderInServer(aName, server, getter_AddRefs(found));
+ }
+
+ if (!found)
+ return NS_MSG_ERROR_INVALID_FOLDER_NAME;
+
+ found.forget(_retval);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::CollectServers()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager;
+ accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccount> defaultAccount;
+ rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (defaultAccount)
+ return defaultAccount->GetIncomingServer(getter_AddRefs(mServer));
+
+ // We can also import filters into the Local Folders account.
+ rv = accountManager->GetLocalFoldersServer(getter_AddRefs(mServer));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!mServer)
+ return NS_ERROR_UNEXPECTED;
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyFilters::RemoveConvertedFile()
+{
+ nsresult rv = NS_OK;
+ if (mConvertedFile) {
+ bool exists = false;
+ mConvertedFile->Exists(&exists);
+ if (exists) {
+ rv = mConvertedFile->Remove(false);
+ if (NS_SUCCEEDED(rv))
+ mConvertedFile = nullptr;
+ }
+ }
+ return rv;
+}
+
diff --git a/mailnews/import/becky/src/nsBeckyFilters.h b/mailnews/import/becky/src/nsBeckyFilters.h
new file mode 100644
index 000000000..20dd6d5ee
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyFilters.h
@@ -0,0 +1,77 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBeckyFilters_h___
+#define nsBeckyFilters_h___
+
+#include "nsIImportFilters.h"
+#include "nsIFile.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsMsgFilterCore.h"
+
+class nsIMsgFilter;
+class nsIMsgRuleAction;
+class nsCString;
+
+class nsBeckyFilters final : public nsIImportFilters
+{
+public:
+ nsBeckyFilters();
+ static nsresult Create(nsIImportFilters **aImport);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTFILTERS
+
+private:
+ virtual ~nsBeckyFilters();
+
+ nsCOMPtr<nsIFile> mLocation;
+ nsCOMPtr<nsIMsgIncomingServer> mServer;
+ nsCOMPtr<nsIFile> mConvertedFile;
+
+ nsresult GetDefaultFilterLocation(nsIFile **aFile);
+ nsresult GetFilterFile(bool aIncoming, nsIFile *aLocation, nsIFile **aFile);
+ nsresult ParseFilterFile(nsIFile *aFile, bool aIncoming);
+ nsresult ParseRuleLine(const nsCString &aLine,
+ nsMsgSearchAttribValue *aSearchAttribute,
+ nsMsgSearchOpValue *aSearchOperator,
+ nsString &aSearchKeyword);
+ nsresult CollectServers();
+ nsresult FindMessageFolder(const nsAString& aName,
+ nsIMsgFolder *aParantFolder,
+ nsIMsgFolder **_retval);
+ nsresult FindMessageFolderInServer(const nsAString& aName,
+ nsIMsgIncomingServer *aServer,
+ nsIMsgFolder **_retval);
+ nsresult GetMessageFolder(const nsAString& aName, nsIMsgFolder **_retval);
+ nsresult GetActionTarget(const nsCString &aLine, nsCString &aTarget);
+ nsresult GetFolderNameFromTarget(const nsCString &aTarget, nsAString &aName);
+ nsresult GetDistributeTarget(const nsCString &aLine,
+ nsCString &aTargetFolder);
+ nsresult GetResendTarget(const nsCString &aLine,
+ nsCString &aTemplate,
+ nsCString &aTargetAddress);
+ nsresult CreateRuleAction(nsIMsgFilter *aFilter,
+ nsMsgRuleActionType actionType,
+ nsIMsgRuleAction **_retval);
+ nsresult CreateDistributeAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ const nsMsgRuleActionType &aActionType,
+ nsIMsgRuleAction **_retval);
+ nsresult CreateLeaveOrDeleteAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ nsIMsgRuleAction **_retval);
+ nsresult CreateResendAction(const nsCString &aLine,
+ nsIMsgFilter *aFilter,
+ const nsMsgRuleActionType &aActionType,
+ nsIMsgRuleAction **_retval);
+ nsresult CreateFilter(bool aIncoming, nsIMsgFilter **_retval);
+ nsresult AppendFilter(nsIMsgFilter *aFilter);
+ nsresult SetRuleAction(const nsCString &aLine, nsIMsgFilter *aFilter);
+ nsresult SetSearchTerm(const nsCString &aLine, nsIMsgFilter *aFilter);
+ nsresult RemoveConvertedFile();
+};
+
+#endif /* nsBeckyFilters_h___ */
diff --git a/mailnews/import/becky/src/nsBeckyImport.cpp b/mailnews/import/becky/src/nsBeckyImport.cpp
new file mode 100644
index 000000000..d4820528a
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyImport.cpp
@@ -0,0 +1,168 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nscore.h"
+#include "nsIServiceManager.h"
+#include "nsIImportService.h"
+#include "nsIComponentManager.h"
+#include "nsIMemory.h"
+#include "nsIImportMail.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportSettings.h"
+#include "nsIImportFilters.h"
+#include "nsIImportFieldMap.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIOutputStream.h"
+#include "nsIAddrDatabase.h"
+#include "nsTextFormatter.h"
+#include "nsIStringBundle.h"
+#include "nsUnicharUtils.h"
+#include "nsIMsgTagService.h"
+#include "nsMsgBaseCID.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+
+#include "nsBeckyImport.h"
+#include "nsBeckyMail.h"
+#include "nsBeckyAddressBooks.h"
+#include "nsBeckySettings.h"
+#include "nsBeckyFilters.h"
+#include "nsBeckyStringBundle.h"
+
+nsBeckyImport::nsBeckyImport()
+{
+}
+
+nsBeckyImport::~nsBeckyImport()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsBeckyImport, nsIImportModule)
+
+NS_IMETHODIMP
+nsBeckyImport::GetName(char16_t **aName)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+ *aName =
+ nsBeckyStringBundle::GetStringByName(u"BeckyImportName");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyImport::GetDescription(char16_t **aDescription)
+{
+ NS_ENSURE_ARG_POINTER(aDescription);
+ *aDescription =
+ nsBeckyStringBundle::GetStringByName(u"BeckyImportDescription");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyImport::GetSupports(char **aSupports)
+{
+ NS_ENSURE_ARG_POINTER(aSupports);
+ *aSupports = strdup(kBeckySupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyImport::GetSupportsUpgrade(bool *aUpgrade)
+{
+ NS_ENSURE_ARG_POINTER(aUpgrade);
+ *aUpgrade = true;
+ return NS_OK;
+}
+
+nsresult
+nsBeckyImport::GetMailImportInterface(nsISupports **aInterface)
+{
+ nsCOMPtr<nsIImportMail> importer;
+ nsresult rv = nsBeckyMail::Create(getter_AddRefs(importer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIImportService> importService(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIImportGeneric> generic;
+ rv = importService->CreateNewGenericMail(getter_AddRefs(generic));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ generic->SetData("mailInterface", importer);
+
+ nsString name;
+ name.Adopt(nsBeckyStringBundle::GetStringByName(u"BeckyImportName"));
+
+ nsCOMPtr<nsISupportsString> nameString(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nameString->SetData(name);
+ generic->SetData("name", nameString);
+
+ return CallQueryInterface(generic, aInterface);
+}
+
+nsresult
+nsBeckyImport::GetAddressBookImportInterface(nsISupports **aInterface)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportAddressBooks> importer;
+ rv = nsBeckyAddressBooks::Create(getter_AddRefs(importer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIImportService> importService(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIImportGeneric> generic;
+ rv = importService->CreateNewGenericAddressBooks(getter_AddRefs(generic));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ generic->SetData("addressInterface", importer);
+ return CallQueryInterface(generic, aInterface);
+}
+
+nsresult
+nsBeckyImport::GetSettingsImportInterface(nsISupports **aInterface)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportSettings> importer;
+ rv = nsBeckySettings::Create(getter_AddRefs(importer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(importer, aInterface);
+}
+
+nsresult
+nsBeckyImport::GetFiltersImportInterface(nsISupports **aInterface)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportFilters> importer;
+ rv = nsBeckyFilters::Create(getter_AddRefs(importer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(importer, aInterface);
+}
+
+NS_IMETHODIMP
+nsBeckyImport::GetImportInterface(const char *aImportType, nsISupports **aInterface)
+{
+ NS_ENSURE_ARG_POINTER(aImportType);
+ NS_ENSURE_ARG_POINTER(aInterface);
+
+ *aInterface = nullptr;
+ if (!strcmp(aImportType, "mail"))
+ return GetMailImportInterface(aInterface);
+ if (!strcmp(aImportType, "addressbook"))
+ return GetAddressBookImportInterface(aInterface);
+ if (!strcmp(aImportType, "settings"))
+ return GetSettingsImportInterface(aInterface);
+ if (!strcmp(aImportType, "filters"))
+ return GetFiltersImportInterface(aInterface);
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
diff --git a/mailnews/import/becky/src/nsBeckyImport.h b/mailnews/import/becky/src/nsBeckyImport.h
new file mode 100644
index 000000000..60d81c18c
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyImport.h
@@ -0,0 +1,36 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBeckyImport_h___
+#define nsBeckyImport_h___
+
+#include "nsIImportModule.h"
+
+#define NS_BECKYIMPORT_CID \
+{ \
+ 0x7952a6cf, 0x2442,0x4c04, \
+ {0x9f, 0x02, 0x15, 0x0b, 0x15, 0xa0, 0xa8, 0x41}}
+
+#define kBeckySupportsString NS_IMPORT_MAIL_STR "," NS_IMPORT_ADDRESS_STR "," NS_IMPORT_SETTINGS_STR "," NS_IMPORT_FILTERS_STR
+
+class nsBeckyImport final : public nsIImportModule
+{
+public:
+ nsBeckyImport();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTMODULE
+
+private:
+ virtual ~nsBeckyImport();
+
+ nsresult GetMailImportInterface(nsISupports **aInterface);
+ nsresult GetAddressBookImportInterface(nsISupports **aInterface);
+ nsresult GetSettingsImportInterface(nsISupports **aInterface);
+ nsresult GetFiltersImportInterface(nsISupports **aInterface);
+
+};
+
+#endif /* nsBeckyImport_h___ */
diff --git a/mailnews/import/becky/src/nsBeckyMail.cpp b/mailnews/import/becky/src/nsBeckyMail.cpp
new file mode 100644
index 000000000..9c837d190
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyMail.cpp
@@ -0,0 +1,641 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsILineInputStream.h"
+#include "nsNetUtil.h"
+#include "nsIArray.h"
+#include "nsIImportService.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsIMutableArray.h"
+#include "nsMsgUtils.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgMessageFlags.h"
+#include "nsTArray.h"
+#include "nspr.h"
+#include "nsIStringBundle.h"
+#include "nsThreadUtils.h"
+
+#include "nsBeckyMail.h"
+#include "nsBeckyUtils.h"
+#include "nsBeckyStringBundle.h"
+
+#define FROM_LINE "From - Mon Jan 1 00:00:00 1965" MSG_LINEBREAK
+#define X_BECKY_STATUS_HEADER "X-Becky-Status"
+#define X_BECKY_INCLUDE_HEADER "X-Becky-Include"
+
+enum {
+ BECKY_STATUS_READ = 1 << 0,
+ BECKY_STATUS_FORWARDED = 1 << 1,
+ BECKY_STATUS_REPLIED = 1 << 2
+};
+
+NS_IMPL_ISUPPORTS(nsBeckyMail, nsIImportMail)
+
+nsresult
+nsBeckyMail::Create(nsIImportMail **aImport)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+
+ *aImport = new nsBeckyMail();
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsBeckyMail::nsBeckyMail()
+: mReadBytes(0)
+{
+}
+
+nsBeckyMail::~nsBeckyMail()
+{
+}
+
+NS_IMETHODIMP
+nsBeckyMail::GetDefaultLocation(nsIFile **aLocation,
+ bool *aFound,
+ bool *aUserVerify)
+{
+ NS_ENSURE_ARG_POINTER(aFound);
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(aUserVerify);
+
+ *aLocation = nullptr;
+ *aUserVerify = true;
+ *aFound = false;
+ if (NS_SUCCEEDED(nsBeckyUtils::GetDefaultMailboxDirectory(aLocation)))
+ *aFound = true;
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyMail::CreateMailboxDescriptor(nsIImportMailboxDescriptor **aDescriptor)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportService> importService;
+ importService = do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return importService->CreateNewMailboxDescriptor(aDescriptor);
+}
+
+nsresult
+nsBeckyMail::GetMailboxName(nsIFile *aMailbox, nsAString &aName)
+{
+ nsCOMPtr<nsIFile> iniFile;
+ nsBeckyUtils::GetMailboxINIFile(aMailbox, getter_AddRefs(iniFile));
+ if (iniFile) {
+ nsCOMPtr<nsIFile> convertedFile;
+ nsBeckyUtils::ConvertToUTF8File(iniFile, getter_AddRefs(convertedFile));
+ if (convertedFile) {
+ nsAutoCString utf8Name;
+ nsBeckyUtils::GetMailboxNameFromINIFile(convertedFile, utf8Name);
+ convertedFile->Remove(false);
+ CopyUTF8toUTF16(utf8Name, aName);
+ }
+ }
+
+ if (aName.IsEmpty()) {
+ nsAutoString name;
+ aMailbox->GetLeafName(name);
+ name.Trim("!", true, false);
+ aName.Assign(name);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyMail::AppendMailboxDescriptor(nsIFile *aEntry,
+ const nsString &aName,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImportMailboxDescriptor> descriptor;
+ rv = CreateMailboxDescriptor(getter_AddRefs(descriptor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t size;
+ rv = aEntry->GetFileSize(&size);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = descriptor->SetSize(size);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = descriptor->SetDisplayName(aName.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> mailboxFile;
+ rv = descriptor->GetFile(getter_AddRefs(mailboxFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ descriptor->SetDepth(aDepth);
+
+ mailboxFile->InitWithFile(aEntry);
+ aCollected->AppendElement(descriptor, false);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyMail::CollectMailboxesInFolderListFile(nsIFile *aListFile,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected)
+{
+ nsresult rv;
+ nsCOMPtr<nsILineInputStream> lineStream;
+ rv = nsBeckyUtils::CreateLineInputStream(aListFile,
+ getter_AddRefs(lineStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> parent;
+ rv = aListFile->GetParent(getter_AddRefs(parent));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ nsAutoCString folderName;
+ bool isEmpty = true;
+ while (more && NS_SUCCEEDED(rv)) {
+ rv = lineStream->ReadLine(folderName, &more);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (folderName.IsEmpty())
+ continue;
+
+ nsCOMPtr<nsIFile> folder;
+ rv = parent->Clone(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = folder->AppendNative(folderName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ isEmpty = false;
+ rv = CollectMailboxesInDirectory(folder, aDepth + 1, aCollected);
+ }
+
+ return isEmpty ? NS_ERROR_FILE_NOT_FOUND : NS_OK;
+}
+
+nsresult
+nsBeckyMail::CollectMailboxesInDirectory(nsIFile *aDirectory,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected)
+{
+ nsAutoString mailboxName;
+ nsresult rv = GetMailboxName(aDirectory, mailboxName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aDepth != 0)
+ AppendMailboxDescriptor(aDirectory, mailboxName, aDepth, aCollected);
+
+ nsCOMPtr<nsIFile> folderListFile;
+ rv = nsBeckyUtils::GetFolderListFile(aDirectory, getter_AddRefs(folderListFile));
+ bool folderListExists = false;
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = CollectMailboxesInFolderListFile(folderListFile, aDepth, aCollected);
+ folderListExists = true;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> entry;
+ rv = entries->GetNext(getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString name;
+ rv = file->GetLeafName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (StringEndsWith(name, NS_LITERAL_STRING(".bmf"))) {
+ AppendMailboxDescriptor(file, mailboxName, aDepth, aCollected);
+ }
+
+ // The Folder.lst file is not created if there is only one sub folder,
+ // so we need to find the sub folder by our hands.
+ // The folder name does not begin with # or ! maybe. Yes, maybe...
+ if (!folderListExists) {
+ if (StringBeginsWith(name, NS_LITERAL_STRING("#")) ||
+ StringBeginsWith(name, NS_LITERAL_STRING("!")))
+ continue;
+
+ bool isDirectory = false;
+ rv = file->IsDirectory(&isDirectory);
+ if (isDirectory) {
+ CollectMailboxesInDirectory(file, aDepth + 1, aCollected);
+ continue;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyMail::FindMailboxes(nsIFile *aLocation, nsIArray **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CollectMailboxesInDirectory(aLocation, 0, array);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ array.forget(_retval);
+
+ return NS_OK;
+}
+
+static nsresult
+GetBeckyStatusValue(const nsCString &aHeader, nsACString &aValue)
+{
+ int32_t valueStartPosition;
+
+ valueStartPosition = aHeader.FindChar(':');
+ if (valueStartPosition < 0)
+ return NS_ERROR_UNEXPECTED;
+
+ valueStartPosition++;
+
+ int32_t commaPosition = aHeader.FindChar(',', valueStartPosition);
+ if (commaPosition < 0)
+ return NS_ERROR_UNEXPECTED;
+
+ nsAutoCString value(Substring(aHeader,
+ valueStartPosition,
+ commaPosition - valueStartPosition));
+ value.Trim(" \t");
+
+ aValue.Assign(value);
+
+ return NS_OK;
+}
+
+static nsresult
+GetBeckyIncludeValue(const nsCString &aHeader, nsACString &aValue)
+{
+ int32_t valueStartPosition;
+
+ valueStartPosition = aHeader.FindChar(':');
+ if (valueStartPosition < 0)
+ return NS_ERROR_FAILURE;
+
+ valueStartPosition++;
+ nsAutoCString value(Substring(aHeader, valueStartPosition));
+ value.Trim(" \t");
+
+ aValue.Assign(value);
+
+ return NS_OK;
+}
+
+static bool
+ConvertBeckyStatusToMozillaStatus(const nsCString &aHeader,
+ nsMsgMessageFlagType *aMozillaStatusFlag)
+{
+ nsresult rv;
+ nsAutoCString statusString;
+ rv = GetBeckyStatusValue(aHeader, statusString);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsresult errorCode;
+ uint32_t beckyStatusFlag = static_cast<uint32_t>(statusString.ToInteger(&errorCode, 16));
+ if (NS_FAILED(errorCode))
+ return false;
+
+ if (beckyStatusFlag & BECKY_STATUS_READ)
+ *aMozillaStatusFlag |= nsMsgMessageFlags::Read;
+ if (beckyStatusFlag & BECKY_STATUS_FORWARDED)
+ *aMozillaStatusFlag |= nsMsgMessageFlags::Forwarded;
+ if (beckyStatusFlag & BECKY_STATUS_REPLIED)
+ *aMozillaStatusFlag |= nsMsgMessageFlags::Replied;
+
+ return true;
+}
+
+static inline bool
+CheckHeaderKey(const nsCString &aHeader, const char *aKeyString)
+{
+ nsAutoCString key(StringHead(aHeader, aHeader.FindChar(':')));
+ key.Trim(" \t");
+ return key.Equals(aKeyString);
+}
+
+static inline bool
+IsBeckyStatusHeader(const nsCString &aHeader)
+{
+ return CheckHeaderKey(aHeader, X_BECKY_STATUS_HEADER);
+}
+
+static inline bool
+IsBeckyIncludeLine(const nsCString &aLine)
+{
+ return CheckHeaderKey(aLine, X_BECKY_INCLUDE_HEADER);
+}
+
+static inline bool
+IsEndOfHeaders(const nsCString &aLine)
+{
+ return aLine.IsEmpty();
+}
+
+static inline bool
+IsEndOfMessage(const nsCString &aLine)
+{
+ return aLine.Equals(".");
+}
+
+class ImportMessageRunnable: public mozilla::Runnable
+{
+public:
+ ImportMessageRunnable(nsIFile *aMessageFile,
+ nsIMsgFolder *aFolder);
+ NS_DECL_NSIRUNNABLE
+private:
+ nsresult WriteHeaders(nsCString &aHeaders, nsIOutputStream *aOutputStream);
+ nsresult HandleHeaderLine(const nsCString &aHeaderLine, nsACString &aHeaders);
+ nsresult GetAttachmentFile(nsIFile *aMailboxFile,
+ const nsCString &aHeader,
+ nsIFile **_retval);
+ nsresult WriteAttachmentFile(nsIFile *aMailboxFile,
+ const nsCString &aHeader,
+ nsIOutputStream *aOutputStream);
+
+ nsCOMPtr<nsIFile> mMessageFile;
+ nsCOMPtr<nsIMsgFolder> mFolder;
+};
+
+ImportMessageRunnable::ImportMessageRunnable(nsIFile *aMessageFile,
+ nsIMsgFolder *aFolder) :
+ mMessageFile(aMessageFile), mFolder(aFolder)
+{
+}
+
+nsresult
+ImportMessageRunnable::WriteHeaders(nsCString &aHeaders,
+ nsIOutputStream *aOutputStream)
+{
+ nsresult rv;
+ uint32_t writtenBytes = 0;
+
+ rv = aOutputStream->Write(FROM_LINE, strlen(FROM_LINE), &writtenBytes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aOutputStream->Write(aHeaders.get(), aHeaders.Length(), &writtenBytes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aOutputStream->Write(MSG_LINEBREAK, strlen(MSG_LINEBREAK), &writtenBytes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aHeaders.Truncate();
+
+ return NS_OK;
+}
+
+nsresult
+ImportMessageRunnable::HandleHeaderLine(const nsCString &aHeaderLine,
+ nsACString &aHeaders)
+{
+ aHeaders.Append(aHeaderLine);
+ aHeaders.AppendLiteral(MSG_LINEBREAK);
+
+ nsMsgMessageFlagType flag = 0;
+ if (IsBeckyStatusHeader(aHeaderLine) &&
+ ConvertBeckyStatusToMozillaStatus(aHeaderLine, &flag)) {
+ char *statusLine;
+ statusLine = PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, flag);
+ aHeaders.Append(statusLine);
+ PR_smprintf_free(statusLine);
+ aHeaders.AppendLiteral(X_MOZILLA_KEYWORDS);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+ImportMessageRunnable::GetAttachmentFile(nsIFile *aMailboxFile,
+ const nsCString &aHeader,
+ nsIFile **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> attachmentFile;
+
+ rv = aMailboxFile->Clone(getter_AddRefs(attachmentFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = attachmentFile->Append(NS_LITERAL_STRING("#Attach"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString nativeAttachmentPath;
+ rv = GetBeckyIncludeValue(aHeader, nativeAttachmentPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = attachmentFile->AppendRelativeNativePath(nativeAttachmentPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists = false;
+ attachmentFile->Exists(&exists);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ attachmentFile.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+ImportMessageRunnable::WriteAttachmentFile(nsIFile *aMailboxFile,
+ const nsCString &aHeader,
+ nsIOutputStream *aOutputStream)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> parentDirectory;
+ rv = aMailboxFile->GetParent(getter_AddRefs(parentDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> attachmentFile;
+ rv = GetAttachmentFile(parentDirectory,
+ aHeader,
+ getter_AddRefs(attachmentFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
+ attachmentFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char buffer[FILE_IO_BUFFER_SIZE];
+ uint32_t readBytes = 0;
+ uint32_t writtenBytes = 0;
+ rv = aOutputStream->Write(MSG_LINEBREAK, strlen(MSG_LINEBREAK), &writtenBytes);
+ while (NS_SUCCEEDED(inputStream->Read(buffer, sizeof(buffer), &readBytes)) &&
+ readBytes > 0) {
+ rv = aOutputStream->Write(buffer, readBytes, &writtenBytes);
+ if (NS_FAILED(rv))
+ break;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP ImportMessageRunnable::Run()
+{
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = mFolder->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILineInputStream> lineStream;
+ rv = nsBeckyUtils::CreateLineInputStream(mMessageFile,
+ getter_AddRefs(lineStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool reusable;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ nsCOMPtr<nsIOutputStream> outputStream;
+ rv = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr), &reusable,
+ getter_AddRefs(outputStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool inHeader = true;
+ bool more = true;
+ nsAutoCString headers;
+ while (NS_SUCCEEDED(rv) && more) {
+ nsAutoCString line;
+ rv = lineStream->ReadLine(line, &more);
+ if (NS_FAILED(rv))
+ break;
+
+ if (inHeader) {
+ if (IsEndOfHeaders(line)) {
+ inHeader = false;
+ rv = WriteHeaders(headers, outputStream);
+ } else {
+ rv = HandleHeaderLine(line, headers);
+ }
+ } else if (IsEndOfMessage(line)) {
+ inHeader = true;
+ rv = msgStore->FinishNewMessage(outputStream, msgHdr);
+ if (!reusable)
+ outputStream->Close();
+ rv = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr), &reusable,
+ getter_AddRefs(outputStream));
+ } else if (IsBeckyIncludeLine(line)) {
+ rv = WriteAttachmentFile(mMessageFile, line, outputStream);
+ } else {
+ uint32_t writtenBytes = 0;
+ if (StringBeginsWith(line, NS_LITERAL_CSTRING("..")))
+ line.Cut(0, 1);
+ else if (CheckHeaderKey(line, "From"))
+ line.Insert('>', 0);
+
+ line.AppendLiteral(MSG_LINEBREAK);
+ rv = outputStream->Write(line.get(), line.Length(), &writtenBytes);
+ }
+ }
+
+ if (outputStream) {
+ if (NS_FAILED(rv))
+ msgStore->DiscardNewMessage(outputStream, msgHdr);
+ outputStream->Close();
+ }
+
+ return rv;
+}
+
+static
+nsresult ProxyImportMessage(nsIFile *aMessageFile,
+ nsIMsgFolder *aFolder)
+{
+ RefPtr<ImportMessageRunnable> importMessage =
+ new ImportMessageRunnable(aMessageFile, aFolder);
+ return NS_DispatchToMainThread(importMessage, NS_DISPATCH_SYNC);
+}
+
+nsresult
+nsBeckyMail::ImportMailFile(nsIFile *aMailFile,
+ nsIMsgFolder *aDestination)
+{
+ int64_t size;
+ aMailFile->GetFileSize(&size);
+ if (size == 0)
+ return NS_OK;
+
+ return ProxyImportMessage(aMailFile, aDestination);
+}
+
+NS_IMETHODIMP
+nsBeckyMail::ImportMailbox(nsIImportMailboxDescriptor *aSource,
+ nsIMsgFolder *aDestination,
+ char16_t **aErrorLog,
+ char16_t **aSuccessLog,
+ bool *aFatalError)
+{
+ NS_ENSURE_ARG_POINTER(aSource);
+ NS_ENSURE_ARG_POINTER(aDestination);
+ NS_ENSURE_ARG_POINTER(aErrorLog);
+ NS_ENSURE_ARG_POINTER(aSuccessLog);
+ NS_ENSURE_ARG_POINTER(aFatalError);
+
+ mReadBytes = 0;
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> mailboxFolder;
+ rv = aSource->GetFile(getter_AddRefs(mailboxFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ImportMailFile(mailboxFolder, aDestination);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t finalSize;
+ aSource->GetSize(&finalSize);
+ mReadBytes = finalSize;
+
+ nsAutoString name;
+ aSource->GetDisplayName(getter_Copies(name));
+
+ nsAutoString successMessage;
+ const char16_t *format = { name.get() };
+ rv =
+ nsBeckyStringBundle::FormatStringFromName(u"BeckyImportMailboxSuccess",
+ &format,
+ 1,
+ getter_Copies(successMessage));
+ successMessage.AppendLiteral("\n");
+ *aSuccessLog = ToNewUnicode(successMessage);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBeckyMail::GetImportProgress(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mReadBytes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyMail::TranslateFolderName(const nsAString & aFolderName,
+ nsAString & _retval)
+{
+ return nsBeckyUtils::TranslateFolderName(aFolderName, _retval);
+}
+
diff --git a/mailnews/import/becky/src/nsBeckyMail.h b/mailnews/import/becky/src/nsBeckyMail.h
new file mode 100644
index 000000000..ae287a05f
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyMail.h
@@ -0,0 +1,45 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBeckyMail_h___
+#define nsBeckyMail_h___
+
+#include "nsIImportMail.h"
+
+class nsIFile;
+class nsIMutableArray;
+class nsIMsgFolder;
+
+class nsBeckyMail final : public nsIImportMail
+{
+public:
+ nsBeckyMail();
+ static nsresult Create(nsIImportMail **aImport);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTMAIL
+
+private:
+ virtual ~nsBeckyMail();
+
+ uint32_t mReadBytes;
+
+ nsresult CollectMailboxesInDirectory(nsIFile *aDirectory,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected);
+ nsresult CollectMailboxesInFolderListFile(nsIFile *aListFile,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected);
+ nsresult AppendMailboxDescriptor(nsIFile *aEntry,
+ const nsString &aName,
+ uint32_t aDepth,
+ nsIMutableArray *aCollected);
+ nsresult ImportMailFile(nsIFile *aMailFile,
+ nsIMsgFolder *aDestination);
+ nsresult CreateMailboxDescriptor(nsIImportMailboxDescriptor **aDescriptor);
+ nsresult GetMailboxName(nsIFile *aMailbox, nsAString &aName);
+};
+
+#endif /* nsBeckyMail_h___ */
diff --git a/mailnews/import/becky/src/nsBeckySettings.cpp b/mailnews/import/becky/src/nsBeckySettings.cpp
new file mode 100644
index 000000000..8e1cab960
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckySettings.cpp
@@ -0,0 +1,471 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIINIParser.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsIStringEnumerator.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsILineInputStream.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "msgCore.h"
+#include "nsIStringBundle.h"
+
+#include "nsBeckySettings.h"
+#include "nsBeckyStringBundle.h"
+#include "nsBeckyUtils.h"
+
+NS_IMPL_ISUPPORTS(nsBeckySettings, nsIImportSettings)
+
+nsresult
+nsBeckySettings::Create(nsIImportSettings **aImport)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+
+ *aImport = new nsBeckySettings();
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsBeckySettings::nsBeckySettings()
+{
+}
+
+nsBeckySettings::~nsBeckySettings()
+{
+}
+
+NS_IMETHODIMP
+nsBeckySettings::AutoLocate(char16_t **aDescription,
+ nsIFile **aLocation,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(aDescription);
+ NS_ENSURE_ARG_POINTER(aLocation);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *aDescription =
+ nsBeckyStringBundle::GetStringByName(u"BeckyImportName");
+ *aLocation = nullptr;
+ *_retval = false;
+
+ nsCOMPtr<nsIFile> location;
+ nsresult rv = nsBeckyUtils::GetDefaultMailboxINIFile(getter_AddRefs(location));
+ if (NS_FAILED(rv))
+ location = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ else
+ *_retval = true;
+
+ location.forget(aLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckySettings::SetLocation(nsIFile *aLocation)
+{
+ mLocation = aLocation;
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::CreateParser()
+{
+ if (!mLocation) {
+ nsresult rv = nsBeckyUtils::GetDefaultMailboxINIFile(getter_AddRefs(mLocation));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // nsIINIParser accepts only UTF-8 encoding, so we need to convert the file
+ // first.
+ nsresult rv;
+ rv = nsBeckyUtils::ConvertToUTF8File(mLocation, getter_AddRefs(mConvertedFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsBeckyUtils::CreateINIParserForFile(mConvertedFile,
+ getter_AddRefs(mParser));
+}
+
+nsresult
+nsBeckySettings::CreateSmtpServer(const nsCString &aUserName,
+ const nsCString &aServerName,
+ nsISmtpServer **aServer,
+ bool *existing)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsISmtpService> smtpService = do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISmtpServer> server;
+ rv = smtpService->FindServer(aUserName.get(),
+ aServerName.get(),
+ getter_AddRefs(server));
+
+ if (NS_FAILED(rv) || !server) {
+ rv = smtpService->CreateServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ server->SetHostname(aServerName);
+ server->SetUsername(aUserName);
+ *existing = false;
+ } else {
+ *existing = true;
+ }
+
+ server.forget(aServer);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::CreateIncomingServer(const nsCString &aUserName,
+ const nsCString &aServerName,
+ const nsCString &aProtocol,
+ nsIMsgIncomingServer **aServer)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIncomingServer> incomingServer;
+ rv = accountManager->FindServer(aUserName,
+ aServerName,
+ aProtocol,
+ getter_AddRefs(incomingServer));
+
+ if (NS_FAILED(rv) || !incomingServer) {
+ rv = accountManager->CreateIncomingServer(aUserName,
+ aServerName,
+ aProtocol,
+ getter_AddRefs(incomingServer));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ incomingServer.forget(aServer);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::SetupSmtpServer(nsISmtpServer **aServer)
+{
+ nsresult rv;
+ nsAutoCString userName, serverName;
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SMTPServer"),
+ serverName);
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("UserID"),
+ userName);
+
+ nsCOMPtr<nsISmtpServer> server;
+ bool existing = false;
+ rv = CreateSmtpServer(userName, serverName, getter_AddRefs(server), &existing);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we already have an existing server, do not touch it's settings.
+ if (existing) {
+ server.forget(aServer);
+ return NS_OK;
+ }
+
+ nsAutoCString value;
+ rv = mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SMTPPort"),
+ value);
+ int32_t port = 25;
+ if (NS_SUCCEEDED(rv)) {
+ nsresult errorCode;
+ port = value.ToInteger(&errorCode, 10);
+ }
+ server->SetPort(port);
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SSLSMTP"),
+ value);
+ if (value.Equals("1"))
+ server->SetSocketType(nsMsgSocketType::SSL);
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SMTPAUTH"),
+ value);
+ if (value.Equals("1")) {
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SMTPAUTHMODE"),
+ value);
+ nsMsgAuthMethodValue authMethod = nsMsgAuthMethod::none;
+ if (value.Equals("1")) {
+ authMethod = nsMsgAuthMethod::passwordEncrypted;
+ } else if (value.Equals("2") ||
+ value.Equals("4") ||
+ value.Equals("6")) {
+ authMethod = nsMsgAuthMethod::passwordCleartext;
+ } else {
+ authMethod = nsMsgAuthMethod::anything;
+ }
+ server->SetAuthMethod(authMethod);
+ }
+
+ server.forget(aServer);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::SetPop3ServerProperties(nsIMsgIncomingServer *aServer)
+{
+ nsCOMPtr<nsIPop3IncomingServer> pop3Server = do_QueryInterface(aServer);
+
+ nsAutoCString value;
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("POP3Auth"),
+ value); // 0: plain, 1: APOP, 2: CRAM-MD5, 3: NTLM
+ nsMsgAuthMethodValue authMethod;
+ if (value.IsEmpty() || value.Equals("0")) {
+ authMethod = nsMsgAuthMethod::passwordCleartext;
+ } else if (value.Equals("1")) {
+ authMethod = nsMsgAuthMethod::old;
+ } else if (value.Equals("2")) {
+ authMethod = nsMsgAuthMethod::passwordEncrypted;
+ } else if (value.Equals("3")) {
+ authMethod = nsMsgAuthMethod::NTLM;
+ } else {
+ authMethod = nsMsgAuthMethod::none;
+ }
+ aServer->SetAuthMethod(authMethod);
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("LeaveServer"),
+ value);
+ if (value.Equals("1")) {
+ pop3Server->SetLeaveMessagesOnServer(true);
+ nsresult rv = mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("KeepDays"),
+ value);
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ nsresult errorCode;
+ int32_t leftDays = value.ToInteger(&errorCode, 10);
+ if (NS_SUCCEEDED(errorCode)) {
+ pop3Server->SetNumDaysToLeaveOnServer(leftDays);
+ pop3Server->SetDeleteByAgeFromServer(true);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::SetupIncomingServer(nsIMsgIncomingServer **aServer)
+{
+ nsAutoCString value;
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("Protocol"),
+ value);
+ nsCString protocol;
+ if (value.Equals("1")) {
+ protocol = NS_LITERAL_CSTRING("imap");
+ } else {
+ protocol = NS_LITERAL_CSTRING("pop3");
+ }
+
+ nsAutoCString userName, serverName;
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("MailServer"),
+ serverName);
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("UserID"),
+ userName);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = CreateIncomingServer(userName, serverName, protocol, getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isSecure = false;
+ int32_t port = 0;
+ nsresult errorCode;
+ if (protocol.EqualsLiteral("pop3")) {
+ SetPop3ServerProperties(server);
+ rv = mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("POP3Port"),
+ value);
+ if (NS_SUCCEEDED(rv))
+ port = value.ToInteger(&errorCode, 10);
+ else
+ port = 110;
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SSLPOP"),
+ value);
+ if (value.Equals("1"))
+ isSecure = true;
+ } else if (protocol.EqualsLiteral("imap")) {
+ rv = mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("IMAP4Port"),
+ value);
+ if (NS_SUCCEEDED(rv))
+ port = value.ToInteger(&errorCode, 10);
+ else
+ port = 143;
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("SSLIMAP"),
+ value);
+ if (value.Equals("1"))
+ isSecure = true;
+ }
+
+ server->SetPort(port);
+ if (isSecure)
+ server->SetSocketType(nsMsgSocketType::SSL);
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("CheckInt"),
+ value);
+ if (value.Equals("1"))
+ server->SetDoBiff(true);
+ rv = mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("CheckEvery"),
+ value);
+ if (NS_SUCCEEDED(rv)) {
+ int32_t minutes = value.ToInteger(&errorCode, 10);
+ if (NS_SUCCEEDED(errorCode))
+ server->SetBiffMinutes(minutes);
+ }
+
+ server.forget(aServer);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::CreateIdentity(nsIMsgIdentity **aIdentity)
+{
+ nsAutoCString email, fullName, identityName, bccAddress;
+
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("Name"),
+ identityName);
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("YourName"),
+ fullName);
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("MailAddress"),
+ email);
+ mParser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("PermBcc"),
+ bccAddress);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = accountManager->CreateIdentity(getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ identity->SetLabel(NS_ConvertUTF8toUTF16(identityName));
+ identity->SetFullName(NS_ConvertUTF8toUTF16(fullName));
+ identity->SetEmail(email);
+ if (!bccAddress.IsEmpty()) {
+ identity->SetDoBcc(true);
+ identity->SetDoBccList(bccAddress);
+ }
+
+ identity.forget(aIdentity);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::CreateAccount(nsIMsgIdentity *aIdentity,
+ nsIMsgIncomingServer *aIncomingServer,
+ nsIMsgAccount **aAccount)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = accountManager->CreateAccount(getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = account->AddIdentity(aIdentity);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = account->SetIncomingServer(aIncomingServer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ account.forget(aAccount);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckySettings::RemoveConvertedFile()
+{
+ if (mConvertedFile) {
+ bool exists;
+ mConvertedFile->Exists(&exists);
+ if (exists)
+ mConvertedFile->Remove(false);
+ mConvertedFile = nullptr;
+ }
+ return NS_OK;
+}
+
+#define NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(expr, rv) \
+ if (NS_FAILED(expr)) { \
+ RemoveConvertedFile(); \
+ return rv; \
+ }
+
+NS_IMETHODIMP
+nsBeckySettings::Import(nsIMsgAccount **aLocalMailAccount,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(aLocalMailAccount);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv = CreateParser();
+ NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(rv, rv);
+
+ nsCOMPtr<nsIMsgIncomingServer> incomingServer;
+ rv = SetupIncomingServer(getter_AddRefs(incomingServer));
+ NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(rv, rv);
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = SetupSmtpServer(getter_AddRefs(smtpServer));
+ NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(rv, rv);
+
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = CreateIdentity(getter_AddRefs(identity));
+ NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(rv, rv);
+
+ nsAutoCString smtpKey;
+ smtpServer->GetKey(getter_Copies(smtpKey));
+ identity->SetSmtpServerKey(smtpKey);
+
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = CreateAccount(identity, incomingServer, getter_AddRefs(account));
+ NS_RETURN_IF_FAILED_WITH_REMOVE_CONVERTED_FILE(rv, rv);
+
+ RemoveConvertedFile();
+ if (aLocalMailAccount)
+ account.forget(aLocalMailAccount);
+ *_retval = true;
+ return NS_OK;
+}
+
diff --git a/mailnews/import/becky/src/nsBeckySettings.h b/mailnews/import/becky/src/nsBeckySettings.h
new file mode 100644
index 000000000..19e7d45ed
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckySettings.h
@@ -0,0 +1,52 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBeckySettings_h___
+#define nsBeckySettings_h___
+
+#include "nsIImportSettings.h"
+#include "nsIFile.h"
+#include "nsIINIParser.h"
+
+class nsIMsgIncomingServer;
+class nsIMsgIdentity;
+class nsISmtpServer;
+
+class nsBeckySettings final : public nsIImportSettings
+{
+public:
+ nsBeckySettings();
+ static nsresult Create(nsIImportSettings **aImport);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTSETTINGS
+
+private:
+ virtual ~nsBeckySettings();
+
+ nsCOMPtr<nsIFile> mLocation;
+ nsCOMPtr<nsIFile> mConvertedFile;
+ nsCOMPtr<nsIINIParser> mParser;
+
+ nsresult CreateParser();
+ nsresult CreateIdentity(nsIMsgIdentity **aIdentity);
+ nsresult CreateAccount(nsIMsgIdentity *aIdentity,
+ nsIMsgIncomingServer *aIncomingServer,
+ nsIMsgAccount **aAccount);
+ nsresult CreateSmtpServer(const nsCString &aUserName,
+ const nsCString &aServerName,
+ nsISmtpServer **aServer,
+ bool *existing);
+ nsresult CreateIncomingServer(const nsCString &aUserName,
+ const nsCString &aServerName,
+ const nsCString &aProtocol,
+ nsIMsgIncomingServer **aServer);
+ nsresult SetupIncomingServer(nsIMsgIncomingServer **aServer);
+ nsresult SetupSmtpServer(nsISmtpServer **aServer);
+ nsresult SetPop3ServerProperties(nsIMsgIncomingServer *aServer);
+ nsresult RemoveConvertedFile();
+};
+
+#endif /* nsBeckySettings_h___ */
diff --git a/mailnews/import/becky/src/nsBeckyStringBundle.cpp b/mailnews/import/becky/src/nsBeckyStringBundle.cpp
new file mode 100644
index 000000000..41209dff5
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyStringBundle.cpp
@@ -0,0 +1,74 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "prprf.h"
+#include "prmem.h"
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+#include "nsIServiceManager.h"
+#include "nsIURI.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "nsBeckyStringBundle.h"
+
+#define BECKY_MESSAGES_URL "chrome://messenger/locale/beckyImportMsgs.properties"
+
+nsIStringBundle *nsBeckyStringBundle::mBundle = nullptr;
+
+nsIStringBundle *
+nsBeckyStringBundle::GetStringBundle(void)
+{
+ if (mBundle)
+ return mBundle;
+
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && bundleService)
+ rv = bundleService->CreateBundle(BECKY_MESSAGES_URL, &mBundle);
+
+ return mBundle;
+}
+
+void
+nsBeckyStringBundle::EnsureStringBundle(void)
+{
+ if (!mBundle)
+ (void) GetStringBundle();
+}
+
+char16_t *
+nsBeckyStringBundle::GetStringByName(const char16_t *aName)
+{
+ EnsureStringBundle();
+
+ char16_t *string = nullptr;
+ if (mBundle)
+ mBundle->GetStringFromName(aName, &string);
+
+ return string;
+}
+
+nsresult
+nsBeckyStringBundle::FormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t length,
+ char16_t **_retval)
+{
+ EnsureStringBundle();
+
+ return mBundle->FormatStringFromName(name,
+ params,
+ length,
+ _retval);
+}
+
+void
+nsBeckyStringBundle::Cleanup(void)
+{
+ if (mBundle)
+ mBundle->Release();
+ mBundle = nullptr;
+}
diff --git a/mailnews/import/becky/src/nsBeckyStringBundle.h b/mailnews/import/becky/src/nsBeckyStringBundle.h
new file mode 100644
index 000000000..190208c9d
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyStringBundle.h
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsBeckyStringBundle_H__
+#define _nsBeckyStringBundle_H__
+
+#include "nsString.h"
+
+class nsIStringBundle;
+
+class nsBeckyStringBundle final {
+public:
+ static char16_t *GetStringByName(const char16_t *name);
+ static nsresult FormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t length,
+ char16_t **_retval);
+ static nsIStringBundle * GetStringBundle(void); // don't release
+ static void EnsureStringBundle(void);
+ static void Cleanup(void);
+private:
+ static nsIStringBundle *mBundle;
+};
+
+#define BECKYIMPORT_NAME 2000
+#define BECKYIMPORT_DESCRIPTION 2001
+#define BECKYIMPORT_MAILBOX_SUCCESS 2002
+#define BECKYIMPORT_MAILBOX_BADPARAM 2003
+#define BECKYIMPORT_MAILBOX_CONVERTERROR 2004
+#define BECKYIMPORT_ADDRESS_SUCCESS 2005
+
+
+#endif /* _nsBeckyStringBundle_H__ */
diff --git a/mailnews/import/becky/src/nsBeckyUtils.cpp b/mailnews/import/becky/src/nsBeckyUtils.cpp
new file mode 100644
index 000000000..2e9af84a5
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyUtils.cpp
@@ -0,0 +1,334 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsString.h"
+#include "nsIUTF8ConverterService.h"
+#include "nsUConvCID.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsILineInputStream.h"
+#include "nsIConverterInputStream.h"
+#include "nsIConverterOutputStream.h"
+#include "nsMsgI18N.h"
+#include "nsNetUtil.h"
+#include "nsIINIParser.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsMsgUtils.h"
+#include "msgCore.h"
+#include "nsIImportMail.h"
+#include "nsThreadUtils.h"
+
+#include "nsBeckyUtils.h"
+
+nsresult
+nsBeckyUtils::FindUserDirectoryOnWindows7(nsIFile **aLocation)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> directory;
+ rv = GetSpecialDirectoryWithFileName(NS_WIN_DOCUMENTS_DIR,
+ "Becky",
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists = false;
+ rv = directory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ bool isDirectory = false;
+ rv = directory->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isDirectory)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ directory.forget(aLocation);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::FindUserDirectoryOnWindowsXP(nsIFile **aLocation)
+{
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> directory = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = directory->InitWithPath(NS_LITERAL_STRING("C:\\Becky!"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists = false;
+ rv = directory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ bool isDirectory = false;
+ rv = directory->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isDirectory)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ nsCOMPtr<nsISupports> entry;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ rv = entries->GetNext(getter_AddRefs(entry));
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(entry, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDirectory = false;
+ rv = file->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isDirectory) {
+ file.forget(aLocation);
+ return NS_OK;
+ }
+ }
+
+ directory.forget(aLocation);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::FindUserDirectory(nsIFile **aLocation)
+{
+ nsresult rv = FindUserDirectoryOnWindows7(aLocation);
+ if (rv == NS_ERROR_FILE_NOT_FOUND) {
+ rv = FindUserDirectoryOnWindowsXP(aLocation);
+ }
+ return rv;
+}
+
+nsresult
+nsBeckyUtils::ConvertNativeStringToUTF8(const nsACString& aOriginal,
+ nsACString& _retval)
+{
+ nsresult rv;
+ nsAutoString unicodeString;
+ rv = NS_CopyNativeToUnicode(aOriginal, unicodeString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF16toUTF8(unicodeString, _retval);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::CreateLineInputStream(nsIFile *aFile,
+ nsILineInputStream **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(inputStream, _retval);
+}
+
+nsresult
+nsBeckyUtils::GetFolderListFile(nsIFile *aLocation, nsIFile **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> folderListFile;
+ rv = aLocation->Clone(getter_AddRefs(folderListFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = folderListFile->Append(NS_LITERAL_STRING("Folder.lst"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = folderListFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ folderListFile.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::GetDefaultFolderName(nsIFile *aFolderListFile, nsACString& name)
+{
+ nsresult rv;
+ nsCOMPtr<nsILineInputStream> lineStream;
+ rv = CreateLineInputStream(aFolderListFile, getter_AddRefs(lineStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ rv = lineStream->ReadLine(name, &more);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::GetDefaultMailboxDirectory(nsIFile **_retval)
+{
+ nsCOMPtr<nsIFile> userDirectory;
+ nsresult rv = FindUserDirectory(getter_AddRefs(userDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> folderListFile;
+ rv = GetFolderListFile(userDirectory, getter_AddRefs(folderListFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString defaultFolderName;
+ rv = GetDefaultFolderName(folderListFile, defaultFolderName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = userDirectory->AppendNative(defaultFolderName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = userDirectory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ bool isDirectory = false;
+ rv = userDirectory->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isDirectory)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ userDirectory.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::GetDefaultMailboxINIFile(nsIFile **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> mailboxDirectory;
+ rv = GetDefaultMailboxDirectory(getter_AddRefs(mailboxDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return GetMailboxINIFile(mailboxDirectory, _retval);
+}
+
+nsresult
+nsBeckyUtils::GetMailboxINIFile(nsIFile *aDirectory, nsIFile **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> target;
+ rv = aDirectory->Clone(getter_AddRefs(target));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = target->Append(NS_LITERAL_STRING("Mailbox.ini"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool exists;
+ rv = target->Exists(&exists);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ target.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::CreateINIParserForFile(nsIFile *aFile,
+ nsIINIParser **aParser)
+{
+ nsresult rv;
+ nsCOMPtr<nsIINIParserFactory> factory =
+ do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return factory->CreateINIParser(aFile, aParser);
+}
+
+nsresult
+nsBeckyUtils::GetMailboxNameFromINIFile(nsIFile *aFile, nsCString &aName)
+{
+ nsresult rv;
+ nsCOMPtr<nsIINIParser> parser;
+ rv = CreateINIParserForFile(aFile, getter_AddRefs(parser));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return parser->GetString(NS_LITERAL_CSTRING("Account"),
+ NS_LITERAL_CSTRING("Name"),
+ aName);
+}
+
+nsresult
+nsBeckyUtils::ConvertToUTF8File(nsIFile *aSourceFile,
+ nsIFile **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> convertedFile;
+ rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ "thunderbird-becky-import",
+ getter_AddRefs(convertedFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = convertedFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> source;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(source), aSourceFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> destination;
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(destination),
+ convertedFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const uint32_t kBlock = 8192;
+
+ nsCOMPtr<nsIConverterInputStream> convertedInput =
+ do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
+ convertedInput->Init(source, nsMsgI18NFileSystemCharset(), kBlock, 0x0000);
+
+ nsCOMPtr<nsIConverterOutputStream> convertedOutput =
+ do_CreateInstance("@mozilla.org/intl/converter-output-stream;1");
+ convertedOutput->Init(destination, "UTF-8", kBlock, 0x0000);
+
+ char16_t *line = (char16_t *)moz_xmalloc(kBlock);
+ uint32_t readBytes = kBlock;
+ bool writtenBytes;
+ while (readBytes == kBlock) {
+ rv = convertedInput->Read(line, kBlock, &readBytes);
+ rv = convertedOutput->Write(readBytes, line, &writtenBytes);
+ }
+ convertedOutput->Close();
+ convertedInput->Close();
+
+ convertedFile.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+nsBeckyUtils::TranslateFolderName(const nsAString & aFolderName,
+ nsAString & _retval)
+{
+ if (aFolderName.LowerCaseEqualsLiteral("!trash"))
+ _retval = NS_LITERAL_STRING(kDestTrashFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!inbox"))
+ _retval = NS_LITERAL_STRING(kDestInboxFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!outbox"))
+ _retval = NS_LITERAL_STRING(kDestSentFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!unsent"))
+ _retval = NS_LITERAL_STRING(kDestUnsentMessagesFolderName);
+ else
+ _retval = aFolderName;
+
+ return NS_OK;
+}
diff --git a/mailnews/import/becky/src/nsBeckyUtils.h b/mailnews/import/becky/src/nsBeckyUtils.h
new file mode 100644
index 000000000..8b6e3a542
--- /dev/null
+++ b/mailnews/import/becky/src/nsBeckyUtils.h
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsBeckyUtils_H__
+#define _nsBeckyUtils_H__
+
+class nsIFile;
+class nsILineInputStream;
+class nsIINIParser;
+
+class nsBeckyUtils final {
+public:
+ static nsresult FindUserDirectoryOnWindows7(nsIFile **aLocation);
+ static nsresult FindUserDirectoryOnWindowsXP(nsIFile **aLocation);
+ static nsresult FindUserDirectory(nsIFile **aFile);
+ static nsresult ConvertNativeStringToUTF8(const nsACString& aOriginal,
+ nsACString& _retval);
+ static nsresult CreateLineInputStream(nsIFile *aFile,
+ nsILineInputStream **_retval);
+ static nsresult GetDefaultMailboxDirectory(nsIFile **_retval);
+ static nsresult GetFolderListFile(nsIFile *aLocation,
+ nsIFile **_retval);
+ static nsresult GetDefaultFolderName(nsIFile *aFolderListFile,
+ nsACString& name);
+ static nsresult GetDefaultMailboxINIFile(nsIFile **_retval);
+ static nsresult GetMailboxINIFile(nsIFile *aDirectory, nsIFile **_retval);
+ static nsresult CreateINIParserForFile(nsIFile *aFile,
+ nsIINIParser **aParser);
+ static nsresult GetMailboxNameFromINIFile(nsIFile *aFile, nsCString &aName);
+ static nsresult ConvertToUTF8File(nsIFile *aSourceFile,
+ nsIFile **_retval);
+ static nsresult TranslateFolderName(const nsAString & aFolderName,
+ nsAString & _retval);
+};
+
+
+#endif /* _nsBeckyUtils_H__ */
diff --git a/mailnews/import/build/moz.build b/mailnews/import/build/moz.build
new file mode 100644
index 000000000..56a6eb8c1
--- /dev/null
+++ b/mailnews/import/build/moz.build
@@ -0,0 +1,61 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsImportModule.cpp',
+]
+
+USE_LIBS += [
+ 'nspr',
+]
+
+if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']:
+ XPCOMBinaryComponent('import')
+ USE_LIBS += [
+ 'msgbsutl_s',
+ 'rdfutil_external_s',
+ 'unicharutil_external_s',
+ 'xpcomglue_s',
+ 'xul',
+ ]
+else:
+ Library('import')
+ FINAL_LIBRARY = 'xul'
+
+# js needs to come after xul for now, because it is an archive and its content
+# is discarded when it comes first.
+USE_LIBS += [
+ 'js',
+]
+
+LOCAL_INCLUDES += [
+ '../src',
+ '../text/src',
+ '../vcard/src',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '../applemail/src',
+ ]
+ OS_LIBS += CONFIG['TK_LIBS']
+ OS_LIBS += ['-framework Cocoa']
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ ]
+ if not CONFIG['GNU_CC']:
+ LOCAL_INCLUDES += [
+ '../becky/src',
+ '../outlook/src',
+ '../winlivemail',
+ ]
+ if CONFIG['MOZ_MAPI_SUPPORT']:
+ LOCAL_INCLUDES += [
+ '../outlook/src',
+ ]
+else:
+ OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+
diff --git a/mailnews/import/build/nsImportModule.cpp b/mailnews/import/build/nsImportModule.cpp
new file mode 100644
index 000000000..f8f3814e6
--- /dev/null
+++ b/mailnews/import/build/nsImportModule.cpp
@@ -0,0 +1,194 @@
+/* -*- 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/. */
+
+////////////////////////////////////////////////////////////////////////////////
+// Core Module Include Files
+////////////////////////////////////////////////////////////////////////////////
+#include "nsCOMPtr.h"
+#include "mozilla/ModuleUtils.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// core import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#include "nsImportService.h"
+#include "nsImportMimeEncode.h"
+#include "nsImportStringBundle.h"
+
+NS_DEFINE_NAMED_CID(NS_IMPORTSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_IMPORTMIMEENCODE_CID);
+////////////////////////////////////////////////////////////////////////////////
+// text import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#include "nsTextImport.h"
+
+NS_DEFINE_NAMED_CID(NS_TEXTIMPORT_CID);
+
+////////////////////////////////////////////////////////////////////////////////
+// vCard import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#include "nsVCardImport.h"
+
+NS_DEFINE_NAMED_CID(NS_VCARDIMPORT_CID);
+
+////////////////////////////////////////////////////////////////////////////////
+// Apple Mail import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#if defined(XP_MACOSX)
+#include "nsAppleMailImport.h"
+
+NS_DEFINE_NAMED_CID(NS_APPLEMAILIMPORT_CID);
+NS_DEFINE_NAMED_CID(NS_APPLEMAILIMPL_CID);
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// outlook import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#ifdef XP_WIN
+#ifdef MOZ_MAPI_SUPPORT
+#include "nsOutlookImport.h"
+#include "nsOutlookStringBundle.h"
+#endif
+#include "nsWMImport.h"
+#include "nsWMStringBundle.h"
+
+NS_DEFINE_NAMED_CID(NS_WMIMPORT_CID);
+#ifdef MOZ_MAPI_SUPPORT
+NS_DEFINE_NAMED_CID(NS_OUTLOOKIMPORT_CID);
+#endif
+#endif // XP_WIN
+
+////////////////////////////////////////////////////////////////////////////////
+// becky import Include Files
+////////////////////////////////////////////////////////////////////////////////
+#ifdef XP_WIN
+#include "nsBeckyImport.h"
+#include "nsBeckyStringBundle.h"
+
+NS_DEFINE_NAMED_CID(NS_BECKYIMPORT_CID);
+#endif // XP_WIN
+
+////////////////////////////////////////////////////////////////////////////////
+// core import factories
+////////////////////////////////////////////////////////////////////////////////
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsImportService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsIImportMimeEncodeImpl)
+
+////////////////////////////////////////////////////////////////////////////////
+// text import factories
+////////////////////////////////////////////////////////////////////////////////
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextImport)
+
+////////////////////////////////////////////////////////////////////////////////
+// vcard import factories
+////////////////////////////////////////////////////////////////////////////////
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVCardImport)
+
+////////////////////////////////////////////////////////////////////////////////
+// apple mail import factories
+////////////////////////////////////////////////////////////////////////////////
+#if defined(XP_MACOSX)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppleMailImportModule)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppleMailImportMail, Initialize)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// outlook import factories
+////////////////////////////////////////////////////////////////////////////////
+#ifdef XP_WIN
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWMImport)
+#ifdef MOZ_MAPI_SUPPORT
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsOutlookImport)
+#endif
+#endif // XP_WIN
+////////////////////////////////////////////////////////////////////////////////
+// becky import factory
+////////////////////////////////////////////////////////////////////////////////
+#ifdef XP_WIN
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBeckyImport)
+#endif // XP_WIN
+
+static const mozilla::Module::CategoryEntry kMailNewsImportCategories[] = {
+ // XXX These CIDs should match the explicit CIDs defined in the header files,
+ // or be changed so that they are contract IDs (with appropriate code updates)
+ { "mailnewsimport", "{A5991D01-ADA7-11d3-A9C2-00A0CC26DA63}", NS_IMPORT_ADDRESS_STR },
+ { "mailnewsimport", "{0eb034a3-964a-4e2f-92eb-cc55d9ae9dd2}", NS_IMPORT_ADDRESS_STR },
+#ifdef XP_WIN
+ { "mailnewsimport", "{42bc82bc-8e9f-4597-8b6e-e529daaf3af1}", kWMSupportsString },
+ { "mailnewsimport", "{7952a6cf-2442-4c04-9f02-150b15a0a841}", kBeckySupportsString },
+#ifdef MOZ_MAPI_SUPPORT
+ { "mailnewsimport", "{1DB469A0-8B00-11d3-A206-00A0CC26DA63}", kOutlookSupportsString },
+#endif
+#endif
+#if defined(XP_MACOSX)
+ { "mailnewsimport", "{6d3f101c-70ec-4e04-b68d-9908d1aeddf3}", kAppleMailSupportsString },
+#endif
+ { NULL }
+};
+
+const mozilla::Module::CIDEntry kMailNewsImportCIDs[] = {
+ { &kNS_IMPORTSERVICE_CID, false, NULL, nsImportServiceConstructor },
+ { &kNS_IMPORTMIMEENCODE_CID, false, NULL, nsIImportMimeEncodeImplConstructor },
+ { &kNS_TEXTIMPORT_CID, false, NULL, nsTextImportConstructor },
+ { &kNS_VCARDIMPORT_CID, false, NULL, nsVCardImportConstructor },
+#if defined(XP_MACOSX)
+ { &kNS_APPLEMAILIMPORT_CID, false, NULL, nsAppleMailImportModuleConstructor },
+ { &kNS_APPLEMAILIMPL_CID, false, NULL, nsAppleMailImportMailConstructor },
+#endif
+
+#ifdef XP_WIN
+ { &kNS_WMIMPORT_CID, false, NULL, nsWMImportConstructor },
+ { &kNS_BECKYIMPORT_CID, false, NULL, nsBeckyImportConstructor },
+#ifdef MOZ_MAPI_SUPPORT
+ { &kNS_OUTLOOKIMPORT_CID, false, NULL, nsOutlookImportConstructor },
+#endif
+#endif
+ { NULL }
+};
+
+const mozilla::Module::ContractIDEntry kMailNewsImportContracts[] = {
+ { NS_IMPORTSERVICE_CONTRACTID, &kNS_IMPORTSERVICE_CID },
+ { "@mozilla.org/import/import-mimeencode;1", &kNS_IMPORTMIMEENCODE_CID },
+ { "@mozilla.org/import/import-text;1", &kNS_TEXTIMPORT_CID },
+ { "@mozilla.org/import/import-vcard;1", &kNS_VCARDIMPORT_CID },
+#if defined(XP_MACOSX)
+ { "@mozilla.org/import/import-applemail;1", &kNS_APPLEMAILIMPORT_CID },
+ { NS_APPLEMAILIMPL_CONTRACTID, &kNS_APPLEMAILIMPL_CID },
+#endif
+
+#ifdef XP_WIN
+ { "@mozilla.org/import/import-wm;1", &kNS_WMIMPORT_CID },
+ { "@mozilla.org/import/import-becky;1", &kNS_BECKYIMPORT_CID },
+#ifdef MOZ_MAPI_SUPPORT
+ { "@mozilla.org/import/import-outlook;1", &kNS_OUTLOOKIMPORT_CID },
+#endif
+#endif
+ { NULL }
+};
+
+
+static void importModuleDtor()
+{
+#ifdef XP_WIN
+ nsWMStringBundle::Cleanup();
+ nsBeckyStringBundle::Cleanup();
+#ifdef MOZ_MAPI_SUPPORT
+ nsOutlookStringBundle::Cleanup();
+#endif
+#endif
+}
+
+static const mozilla::Module kMailNewsImportModule = {
+ mozilla::Module::kVersion,
+ kMailNewsImportCIDs,
+ kMailNewsImportContracts,
+ kMailNewsImportCategories,
+ NULL,
+ NULL,
+ importModuleDtor
+};
+
+NSMODULE_DEFN(nsImportServiceModule) = &kMailNewsImportModule;
+
+
diff --git a/mailnews/import/content/fieldMapImport.js b/mailnews/import/content/fieldMapImport.js
new file mode 100644
index 000000000..cd0f2d5da
--- /dev/null
+++ b/mailnews/import/content/fieldMapImport.js
@@ -0,0 +1,186 @@
+/* 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/. */
+
+var importService;
+var fieldMap = null;
+var recordNum = 0;
+var addInterface = null;
+var dialogResult = null;
+var gPreviousButton;
+var gNextButton;
+var gMoveUpButton;
+var gMoveDownButton;
+var gListbox;
+var gSkipFirstRecordButton;
+
+function OnLoadFieldMapImport()
+{
+ top.importService = Components.classes["@mozilla.org/import/import-service;1"]
+ .getService(Components.interfaces.nsIImportService);
+
+ // We need a field map object...
+ // assume we have one passed in? or just make one?
+ if (window.arguments && window.arguments[0]) {
+ top.fieldMap = window.arguments[0].fieldMap;
+ top.addInterface = window.arguments[0].addInterface;
+ top.dialogResult = window.arguments[0].result;
+ }
+ if (top.fieldMap == null) {
+ top.fieldMap = top.importService.CreateNewFieldMap();
+ top.fieldMap.DefaultFieldMap( top.fieldMap.numMozFields);
+ }
+
+ gMoveUpButton = document.getElementById("upButton");
+ gMoveDownButton = document.getElementById("downButton");
+ gPreviousButton = document.getElementById("previous");
+ gNextButton = document.getElementById("next");
+ gListbox = document.getElementById("fieldList");
+ gSkipFirstRecordButton = document.getElementById("skipFirstRecord");
+
+ // Set the state of the skip first record button
+ gSkipFirstRecordButton.checked = top.fieldMap.skipFirstRecord;
+
+ ListFields();
+ Browse(1);
+ gListbox.selectedItem = gListbox.getItemAtIndex(0);
+ disableMoveButtons();
+}
+
+function IndexInMap( index)
+{
+ var count = top.fieldMap.mapSize;
+ for (var i = 0; i < count; i++) {
+ if (top.fieldMap.GetFieldMap( i) == index)
+ return( true);
+ }
+
+ return( false);
+}
+
+function ListFields() {
+ if (top.fieldMap == null)
+ return;
+
+ var count = top.fieldMap.mapSize;
+ var index;
+ var i;
+ for (i = 0; i < count; i++) {
+ index = top.fieldMap.GetFieldMap( i);
+ AddFieldToList(top.fieldMap.GetFieldDescription( index), index, top.fieldMap.GetFieldActive( i));
+ }
+
+ count = top.fieldMap.numMozFields;
+ for (i = 0; i < count; i++) {
+ if (!IndexInMap( i))
+ AddFieldToList(top.fieldMap.GetFieldDescription( i), i, false);
+ }
+}
+
+function CreateField( name, index, on)
+{
+ var item = document.createElement('listitem');
+ item.setAttribute('field-index', index);
+ item.setAttribute('type', "checkbox");
+ var cell = document.createElement('listcell');
+ var cCell = document.createElement( 'listcell');
+ cCell.setAttribute('type', "checkbox");
+ cCell.setAttribute( 'label', name);
+ if (on == true)
+ cCell.setAttribute( 'checked', "true");
+ item.appendChild( cCell);
+ cell.setAttribute( "class", "importsampledata");
+ cell.setAttribute( 'label', "");
+ item.appendChild( cell);
+ return( item);
+}
+
+function AddFieldToList(name, index, on)
+{
+ var item = CreateField(name, index, on);
+ gListbox.appendChild(item);
+}
+
+function itemClicked(event)
+{
+ if (event.button == 0) {
+ var on = gListbox.selectedItem.firstChild.getAttribute('checked');
+ gListbox.selectedItem.firstChild.setAttribute('checked', (on != "true"));
+ }
+}
+
+// The "Move Up/Move Down" buttons should move the items in the left column
+// up/down but the values in the right column should not change.
+function moveItem(up)
+{
+ var selectedItem = gListbox.selectedItem;
+ var swapPartner = (up ? gListbox.getPreviousItem(selectedItem, 1)
+ : gListbox.getNextItem(selectedItem, 1));
+
+ var tmpLabel = swapPartner.lastChild.getAttribute('label');
+ swapPartner.lastChild.setAttribute('label', selectedItem.lastChild.getAttribute('label'));
+ selectedItem.lastChild.setAttribute('label', tmpLabel);
+
+ var newItemPosition = (up ? selectedItem.nextSibling : selectedItem);
+ gListbox.insertBefore(swapPartner, newItemPosition);
+ gListbox.ensureElementIsVisible(selectedItem);
+ disableMoveButtons();
+}
+
+function disableMoveButtons()
+{
+ var selectedIndex = gListbox.selectedIndex;
+ gMoveUpButton.disabled = (selectedIndex == 0);
+ gMoveDownButton.disabled = (selectedIndex == (gListbox.getRowCount() - 1));
+}
+
+function ShowSampleData(data)
+{
+ var fields = data.split("\n");
+ for (var i = 0; i < gListbox.getRowCount(); i++)
+ gListbox.getItemAtIndex(i).lastChild.setAttribute('label', (i < fields.length) ? fields[i] : '');
+}
+
+function FetchSampleData(num)
+{
+ if (!top.addInterface)
+ return false;
+
+ var data = top.addInterface.GetData( "sampleData-" + num);
+ if (!(data instanceof Components.interfaces.nsISupportsString))
+ return false;
+ ShowSampleData( data.data);
+ return true;
+}
+
+function Browse(step)
+{
+ recordNum += step;
+ if (FetchSampleData(recordNum - 1))
+ document.getElementById('recordNumber').setAttribute('value', ("" + recordNum));
+
+ gPreviousButton.disabled = (recordNum == 1);
+ gNextButton.disabled = (addInterface.GetData("sampleData-" + recordNum) == null);
+}
+
+function FieldImportOKButton()
+{
+ var max = gListbox.getRowCount();
+ var fIndex;
+ var on;
+ // Ensure field map is the right size
+ top.fieldMap.SetFieldMapSize(max);
+
+ for (var i = 0; i < max; i++) {
+ fIndex = gListbox.getItemAtIndex(i).getAttribute( 'field-index');
+ on = gListbox.getItemAtIndex(i).firstChild.getAttribute('checked');
+ top.fieldMap.SetFieldMap( i, fIndex);
+ top.fieldMap.SetFieldActive( i, (on == "true"));
+ }
+
+ top.fieldMap.skipFirstRecord = gSkipFirstRecordButton.checked;
+
+ top.dialogResult.ok = true;
+
+ return true;
+}
diff --git a/mailnews/import/content/fieldMapImport.xul b/mailnews/import/content/fieldMapImport.xul
new file mode 100644
index 000000000..abaca10ba
--- /dev/null
+++ b/mailnews/import/content/fieldMapImport.xul
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/fieldMapImport.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ title="&fieldMapImport.title;"
+ style="&fieldMapImport.size;"
+ ondialogaccept="FieldImportOKButton();"
+ onload="OnLoadFieldMapImport();">
+
+ <script type="application/javascript" src="chrome://messenger/content/fieldMapImport.js"/>
+
+ <hbox align="center">
+ <label value="&fieldMapImport.recordNumber;"/>
+ <label id="recordNumber"/>
+ <spacer flex="1"/>
+ <button id="previous" oncommand="Browse(-1);"
+ label="&fieldMapImport.previous.label;"
+ accesskey="&fieldMapImport.previous.accesskey;"/>
+ <button id="next" oncommand="Browse(1);"
+ label="&fieldMapImport.next.label;"
+ accesskey="&fieldMapImport.next.accesskey;"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="skipFirstRecord"
+ label="&fieldMapImport.skipFirstRecord.label;"
+ accesskey="&fieldMapImport.skipFirstRecord.accessKey;"/>
+ </hbox>
+
+ <separator class="thin"/>
+ <label control="fieldList">&fieldMapImport.text;</label>
+ <separator class="thin"/>
+
+ <!-- field list -->
+ <hbox flex="1">
+ <listbox id="fieldList" flex="1" onselect="disableMoveButtons();"
+ onclick="itemClicked(event);">
+ <listcols>
+ <listcol flex="7"/>
+ <listcol flex="13"/>
+ </listcols>
+
+ <listhead>
+ <listheader id="fieldNameHeader" label="&fieldMapImport.fieldListTitle;"/>
+ <listheader id="sampleDataHeader" label="&fieldMapImport.dataTitle;"/>
+ </listhead>
+ </listbox>
+
+ <vbox>
+ <spacer flex="1"/>
+ <button id="upButton" class="up" label="&fieldMapImport.up.label;"
+ accesskey="&fieldMapImport.up.accesskey;"
+ oncommand="moveItem(true);"/>
+ <button id="downButton" class="down" label="&fieldMapImport.down.label;"
+ accesskey="&fieldMapImport.down.accesskey;"
+ oncommand="moveItem(false);"/>
+ <spacer flex="1"/>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/mailnews/import/content/import-test.html b/mailnews/import/content/import-test.html
new file mode 100644
index 000000000..ef9c5ed7f
--- /dev/null
+++ b/mailnews/import/content/import-test.html
@@ -0,0 +1,36 @@
+<!-- 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/. -->
+
+<html>
+<body>
+
+<script type="application/javascript">
+
+function toImport(importType)
+{
+ /*
+ window.openDialog("chrome:/messenger/content/fieldMapImport.xul",
+ "fieldMapImportDialog",
+ "chrome,modal");
+ */
+ window.openDialog("chrome:/messenger/content/importDialog.xul",
+ "",
+ "chrome,modal",
+ {importType:importType});
+
+}
+
+
+</script>
+
+<p>
+<form name="form">
+<input type="button" value="Import Address Books" onclick="toImport( 'addressbook');"><br>
+<input type="button" value="Import Mail" onclick="toImport( 'mail');"><br>
+<input type="button" value="Import Settings" onclick="toImport( 'settings');"><br>
+<input type="button" value="Import Filters" onclick="toImport( 'filters');"><br>
+<form>
+
+</body>
+</html>
diff --git a/mailnews/import/content/importDialog.js b/mailnews/import/content/importDialog.js
new file mode 100644
index 000000000..2ad7c6fb3
--- /dev/null
+++ b/mailnews/import/content/importDialog.js
@@ -0,0 +1,1066 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+"use strict";
+
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gImportType = null;
+var gImportMsgsBundle;
+var gFeedsBundle;
+var gImportService = null;
+var gSuccessStr = null;
+var gErrorStr = null;
+var gInputStr = null;
+var gProgressInfo = null;
+var gSelectedModuleName = null;
+var gAddInterface = null;
+var gNewFeedAcctCreated = false;
+
+var Ci = Components.interfaces;
+var nsISupportsString = Ci.nsISupportsString;
+
+function OnLoadImportDialog()
+{
+ gImportMsgsBundle = document.getElementById("bundle_importMsgs");
+ gFeedsBundle = document.getElementById("bundle_feeds");
+ gImportService = Components.classes["@mozilla.org/import/import-service;1"]
+ .getService(Ci.nsIImportService);
+
+ gProgressInfo = { };
+ gProgressInfo.progressWindow = null;
+ gProgressInfo.importInterface = null;
+ gProgressInfo.mainWindow = window;
+ gProgressInfo.intervalState = 0;
+ gProgressInfo.importSuccess = false;
+ gProgressInfo.importType = null;
+ gProgressInfo.localFolderExists = false;
+
+ gSuccessStr = Components.classes["@mozilla.org/supports-string;1"]
+ .createInstance(nsISupportsString);
+ gErrorStr = Components.classes["@mozilla.org/supports-string;1"]
+ .createInstance(nsISupportsString);
+ gInputStr = Components.classes["@mozilla.org/supports-string;1"]
+ .createInstance(nsISupportsString);
+
+ // look in arguments[0] for parameters
+ if (("arguments" in window) && window.arguments.length >= 1 &&
+ ("importType" in window.arguments[0]) && window.arguments[0].importType)
+ {
+ // keep parameters in global for later
+ gImportType = window.arguments[0].importType;
+ gProgressInfo.importType = gImportType;
+ }
+ else
+ {
+ gImportType = "all";
+ gProgressInfo.importType = "all";
+ }
+
+ SetUpImportType();
+
+ // on startup, set the focus to the control element
+ // for accessibility reasons.
+ // if we used the wizardOverlay, we would get this for free.
+ // see bug #101874
+ document.getElementById("importFields").focus();
+}
+
+
+function SetUpImportType()
+{
+ // set dialog title
+ document.getElementById("importFields").value = gImportType;
+
+ // Mac migration not working right now, so disable it.
+ if (Services.appinfo.OS == "Darwin")
+ {
+ document.getElementById("allRadio").setAttribute("disabled", "true");
+ if (gImportType == "all")
+ document.getElementById("importFields").value = "addressbook";
+ }
+
+ let descriptionDeck = document.getElementById("selectDescriptionDeck");
+ descriptionDeck.selectedIndex = 0;
+ if (gImportType == "feeds")
+ {
+ descriptionDeck.selectedIndex = 1;
+ ListFeedAccounts();
+ }
+ else
+ ListModules();
+}
+
+
+function SetDivText(id, text)
+{
+ var div = document.getElementById(id);
+
+ if (div) {
+ if (!div.hasChildNodes()) {
+ var textNode = document.createTextNode(text);
+ div.appendChild(textNode);
+ }
+ else if (div.childNodes.length == 1) {
+ div.childNodes[0].nodeValue = text;
+ }
+ }
+}
+
+function CheckIfLocalFolderExists()
+{
+ try {
+ if (MailServices.accounts.localFoldersServer)
+ gProgressInfo.localFolderExists = true;
+ }
+ catch (ex) {
+ gProgressInfo.localFolderExists = false;
+ }
+}
+
+function ImportDialogOKButton()
+{
+ var listbox = document.getElementById("moduleList");
+ var deck = document.getElementById("stateDeck");
+ var header = document.getElementById("header");
+ var progressMeterEl = document.getElementById("progressMeter");
+ progressMeterEl.mode = "determined";
+ var progressStatusEl = document.getElementById("progressStatus");
+ var progressTitleEl = document.getElementById("progressTitle");
+
+ // better not mess around with navigation at this point
+ var nextButton = document.getElementById("forward");
+ nextButton.setAttribute("disabled", "true");
+ var backButton = document.getElementById("back");
+ backButton.setAttribute("disabled", "true");
+
+ if (listbox && (listbox.selectedCount == 1))
+ {
+ let module = "";
+ let name = "";
+ gImportType = document.getElementById("importFields").value;
+ let index = listbox.selectedItem.getAttribute("list-index");
+ if (index == -1)
+ return false;
+ if (gImportType == "feeds")
+ module = "Feeds";
+ else
+ {
+ module = gImportService.GetModule(gImportType, index);
+ name = gImportService.GetModuleName(gImportType, index);
+ }
+ gSelectedModuleName = name;
+ if (module)
+ {
+ // Fix for Bug 57839 & 85219
+ // We use localFoldersServer(in nsIMsgAccountManager) to check if Local Folder exists.
+ // We need to check localFoldersServer before importing "mail", "settings", or "filters".
+ // Reason: We will create an account with an incoming server of type "none" after
+ // importing "mail", so the localFoldersServer is valid even though the Local Folder
+ // is not created.
+ if (gImportType == "mail" || gImportType == "settings" || gImportType == "filters")
+ CheckIfLocalFolderExists();
+
+ let meterText = "";
+ let error = {};
+ switch(gImportType)
+ {
+ case "mail":
+ if (ImportMail(module, gSuccessStr, gErrorStr))
+ {
+ // We think it was a success, either, we need to
+ // wait for the import to finish
+ // or we are done!
+ if (gProgressInfo.importInterface == null) {
+ ShowImportResults(true, 'Mail');
+ return true;
+ }
+ else {
+ meterText = gImportMsgsBundle.getFormattedString('MailProgressMeterText',
+ [ name ]);
+ header.setAttribute("description", meterText);
+
+ progressStatusEl.setAttribute("label", "");
+ progressTitleEl.setAttribute("label", meterText);
+
+ deck.selectedIndex = 2;
+ gProgressInfo.progressWindow = window;
+ gProgressInfo.intervalState = setInterval(ContinueImportCallback, 100);
+ return true;
+ }
+ }
+ else
+ {
+ ShowImportResults(false, 'Mail');
+ // Re-enable the next button, as we are here, because the user cancelled the picking.
+ // Enable next, so they can try again.
+ nextButton.removeAttribute("disabled");
+ // Also enable back button so that users can pick other import options.
+ backButton.removeAttribute("disabled");
+ return false;
+ }
+ break;
+
+ case "feeds":
+ if (ImportFeeds())
+ {
+ // Successful completion of pre processing and launch of async import.
+ meterText = document.getElementById("description").textContent;
+ header.setAttribute("description", meterText);
+
+ progressStatusEl.setAttribute("label", "");
+ progressTitleEl.setAttribute("label", meterText);
+ progressMeterEl.mode = "undetermined";
+
+ deck.selectedIndex = 2;
+ return true;
+ }
+ else
+ {
+ // Re-enable the next button, as we are here, because the user cancelled the picking.
+ // Enable next, so they can try again.
+ nextButton.removeAttribute("disabled");
+ // Also enable back button so that users can pick other import options.
+ backButton.removeAttribute("disabled");
+ return false;
+ }
+ break;
+
+ case "addressbook":
+ if (ImportAddress(module, gSuccessStr, gErrorStr)) {
+ // We think it was a success, either, we need to
+ // wait for the import to finish
+ // or we are done!
+ if (gProgressInfo.importInterface == null) {
+ ShowImportResults(true, 'Address');
+ return true;
+ }
+ else {
+ meterText = gImportMsgsBundle.getFormattedString('AddrProgressMeterText',
+ [ name ]);
+ header.setAttribute("description", meterText);
+
+ progressStatusEl.setAttribute("label", "");
+ progressTitleEl.setAttribute("label", meterText);
+
+ deck.selectedIndex = 2;
+ gProgressInfo.progressWindow = window;
+ gProgressInfo.intervalState = setInterval(ContinueImportCallback, 100);
+
+ return true;
+ }
+ }
+ else
+ {
+ ShowImportResults(false, 'Address');
+ // Re-enable the next button, as we are here, because the user cancelled the picking.
+ // Enable next, so they can try again.
+ nextButton.removeAttribute("disabled");
+ // Also enable back button so that users can pick other import options.
+ backButton.removeAttribute("disabled");
+ return false;
+ }
+ break;
+
+ case "settings":
+ error.value = null;
+ let newAccount = {};
+ if (!ImportSettings(module, newAccount, error))
+ {
+ if (error.value)
+ ShowImportResultsRaw(gImportMsgsBundle.getString("ImportSettingsFailed"),
+ null, false);
+ // Re-enable the next button, as we are here, because the user cancelled the picking.
+ // Enable next, so they can try again.
+ nextButton.removeAttribute("disabled");
+ // Also enable back button so that users can pick other import options.
+ backButton.removeAttribute("disabled");
+ return false;
+ }
+ else
+ ShowImportResultsRaw(
+ gImportMsgsBundle.getFormattedString("ImportSettingsSuccess", [ name ]),
+ null, true);
+ break;
+
+ case "filters":
+ error.value = null;
+ if (!ImportFilters(module, error))
+ {
+ if (error.value)
+ ShowImportResultsRaw(
+ gImportMsgsBundle.getFormattedString("ImportFiltersFailed", [ name ]),
+ error.value, false);
+ // Re-enable the next button, as we are here, because the user cancelled the picking.
+ // Enable next, so they can try again.
+ nextButton.removeAttribute("disabled");
+ // Also enable back button so that users can pick other import options.
+ backButton.removeAttribute("disabled");
+ return false;
+ }
+ else
+ {
+ if (error.value)
+ ShowImportResultsRaw(
+ gImportMsgsBundle.getFormattedString('ImportFiltersPartial', [ name ]),
+ error.value, true);
+ else
+ ShowImportResultsRaw(
+ gImportMsgsBundle.getFormattedString('ImportFiltersSuccess', [ name ]),
+ null, true);
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+function SetStatusText(val)
+{
+ var progressStatus = document.getElementById("progressStatus");
+ progressStatus.setAttribute("label", val);
+}
+
+function SetProgress(val)
+{
+ var progressMeter = document.getElementById("progressMeter");
+ progressMeter.value = val;
+}
+
+function ContinueImportCallback()
+{
+ gProgressInfo.mainWindow.ContinueImport(gProgressInfo);
+}
+
+function ImportSelectionChanged()
+{
+ let listbox = document.getElementById('moduleList');
+ let acctNameBox = document.getElementById('acctName-box');
+ if (listbox && (listbox.selectedCount == 1))
+ {
+ let index = listbox.selectedItem.getAttribute("list-index");
+ if (index == -1)
+ return;
+ acctNameBox.setAttribute('style', 'visibility: hidden;');
+ if (gImportType == "feeds")
+ {
+ if (index == 0)
+ {
+ SetDivText('description', gFeedsBundle.getString('ImportFeedsNewAccount'));
+ let defaultName = gFeedsBundle.getString("feeds-accountname");
+ document.getElementById("acctName").value = defaultName;
+ acctNameBox.removeAttribute('style');
+ }
+ else
+ SetDivText('description', gFeedsBundle.getString('ImportFeedsExistingAccount'));
+ }
+ else
+ SetDivText("description", gImportService.GetModuleDescription(gImportType, index));
+ }
+}
+
+function CompareImportModuleName(a, b)
+{
+ if (a.name > b.name)
+ return 1;
+ if (a.name < b.name)
+ return -1;
+ return 0;
+}
+
+function ListModules() {
+ if (gImportService == null)
+ return;
+
+ var body = document.getElementById("moduleList");
+ while (body.hasChildNodes()) {
+ body.lastChild.remove();
+ }
+
+ var count = gImportService.GetModuleCount(gImportType);
+ var i;
+
+ var moduleArray = new Array(count);
+ for (i = 0; i < count; i++) {
+ moduleArray[i] = { name: gImportService.GetModuleName(gImportType, i), index: i };
+ }
+
+ // sort the array of modules by name, so that they'll show up in the right order
+ moduleArray.sort(CompareImportModuleName);
+
+ for (i = 0; i < count; i++) {
+ AddModuleToList(moduleArray[i].name, moduleArray[i].index);
+ }
+}
+
+function AddModuleToList(moduleName, index)
+{
+ var body = document.getElementById("moduleList");
+
+ var item = document.createElement('listitem');
+ item.setAttribute('label', moduleName);
+
+ // Temporarily skip Outlook Import which are busted (Bug 1175055).
+ if (moduleName == "Outlook") {
+ item.setAttribute('list-index', -1);
+ item.setAttribute('disabled', true);
+ item.setAttribute('tooltiptext', "Currently disabled due to bug 1175055");
+ } else {
+ item.setAttribute('list-index', index);
+ }
+ body.appendChild(item);
+}
+
+function ListFeedAccounts() {
+ let body = document.getElementById("moduleList");
+ while (body.hasChildNodes())
+ body.lastChild.remove();
+
+ // Add item to allow for new account creation.
+ let item = document.createElement("listitem");
+ item.setAttribute("label", gFeedsBundle.getString('ImportFeedsCreateNewListItem'));
+ item.setAttribute("list-index", 0);
+ body.appendChild(item);
+
+ let index = 0;
+ let feedRootFolders = FeedUtils.getAllRssServerRootFolders();
+
+ feedRootFolders.forEach(function(rootFolder) {
+ item = document.createElement("listitem");
+ item.setAttribute("label", rootFolder.prettyName);
+ item.setAttribute("list-index", ++index);
+ item.server = rootFolder.server;
+ body.appendChild(item);
+ }, this);
+
+ if (index)
+ // If there is an existing feed account, select the first one.
+ body.selectedIndex = 1;
+}
+
+function ContinueImport(info) {
+ var isMail = info.importType == 'mail';
+ var clear = true;
+ var deck;
+ var pcnt;
+
+ if (info.importInterface) {
+ if (!info.importInterface.ContinueImport()) {
+ info.importSuccess = false;
+ clearInterval(info.intervalState);
+ if (info.progressWindow != null) {
+ deck = document.getElementById("stateDeck");
+ deck.selectedIndex = 3;
+ info.progressWindow = null;
+ }
+
+ ShowImportResults(false, isMail ? 'Mail' : 'Address');
+ }
+ else if ((pcnt = info.importInterface.GetProgress()) < 100) {
+ clear = false;
+ if (info.progressWindow != null) {
+ if (pcnt < 5)
+ pcnt = 5;
+ SetProgress(pcnt);
+ if (isMail) {
+ let mailName = info.importInterface.GetData("currentMailbox");
+ if (mailName) {
+ mailName = mailName.QueryInterface(Ci.nsISupportsString);
+ if (mailName)
+ SetStatusText(mailName.data);
+ }
+ }
+ }
+ }
+ else {
+ dump("*** WARNING! sometimes this shows results too early. \n");
+ dump(" something screwy here. this used to work fine.\n");
+ clearInterval(info.intervalState);
+ info.importSuccess = true;
+ if (info.progressWindow) {
+ deck = document.getElementById("stateDeck");
+ deck.selectedIndex = 3;
+ info.progressWindow = null;
+ }
+
+ ShowImportResults(true, isMail ? 'Mail' : 'Address');
+ }
+ }
+ if (clear) {
+ info.intervalState = null;
+ info.importInterface = null;
+ }
+}
+
+
+function ShowResults(doesWantProgress, result)
+{
+ if (result)
+ {
+ if (doesWantProgress)
+ {
+ let deck = document.getElementById("stateDeck");
+ let header = document.getElementById("header");
+ let progressStatusEl = document.getElementById("progressStatus");
+ let progressTitleEl = document.getElementById("progressTitle");
+
+ let meterText = gImportMsgsBundle.getFormattedString("AddrProgressMeterText", [ name ]);
+ header.setAttribute("description", meterText);
+
+ progressStatusEl.setAttribute("label", "");
+ progressTitleEl.setAttribute("label", meterText);
+
+ deck.selectedIndex = 2;
+ gProgressInfo.progressWindow = window;
+ gProgressInfo.intervalState = setInterval(ContinueImportCallback, 100);
+ }
+ else
+ {
+ ShowImportResults(true, 'Address');
+ }
+ }
+ else
+ {
+ ShowImportResults(false, 'Address');
+ }
+
+ return true;
+}
+
+function ShowImportResults(good, module)
+{
+ // String keys for ImportSettingsSuccess, ImportSettingsFailed,
+ // ImportMailSuccess, ImportMailFailed, ImportAddressSuccess,
+ // ImportAddressFailed, ImportFiltersSuccess, and ImportFiltersFailed.
+ var modSuccess = 'Import' + module + 'Success';
+ var modFailed = 'Import' + module + 'Failed';
+
+ // The callers seem to set 'good' to true even if there's something
+ // in the error log. So we should only make it a success case if
+ // error log/str is empty.
+ var results, title;
+ var moduleName = gSelectedModuleName ? gSelectedModuleName : "";
+ if (good && !gErrorStr.data) {
+ title = gImportMsgsBundle.getFormattedString(modSuccess, [ moduleName ]);
+ results = gSuccessStr.data;
+ }
+ else if (gErrorStr.data) {
+ title = gImportMsgsBundle.getFormattedString(modFailed, [ moduleName ]);
+ results = gErrorStr.data;
+ }
+
+ if (results && title)
+ ShowImportResultsRaw(title, results, good);
+}
+
+function ShowImportResultsRaw(title, results, good)
+{
+ SetDivText("status", title);
+ var header = document.getElementById("header");
+ header.setAttribute("description", title);
+ dump("*** results = " + results + "\n");
+ attachStrings("results", results);
+ var deck = document.getElementById("stateDeck");
+ deck.selectedIndex = 3;
+ var nextButton = document.getElementById("forward");
+ nextButton.label = nextButton.getAttribute("finishedval");
+ nextButton.removeAttribute("disabled");
+ var cancelButton = document.getElementById("cancel");
+ cancelButton.setAttribute("disabled", "true");
+ var backButton = document.getElementById("back");
+ backButton.setAttribute("disabled", "true");
+
+ // If the Local Folder doesn't exist, create it after successfully
+ // importing "mail" and "settings"
+ var checkLocalFolder = (gProgressInfo.importType == "mail" ||
+ gProgressInfo.importType == "settings");
+ if (good && checkLocalFolder && !gProgressInfo.localFolderExists) {
+ MailServices.accounts.createLocalMailAccount();
+ }
+}
+
+function attachStrings(aNode, aString)
+{
+ var attachNode = document.getElementById(aNode);
+ if (!aString) {
+ attachNode.parentNode.setAttribute("hidden", "true");
+ return;
+ }
+ var strings = aString.split("\n");
+ for (let string of strings) {
+ if (string) {
+ let currNode = document.createTextNode(string);
+ attachNode.appendChild(currNode);
+ let br = document.createElementNS("http://www.w3.org/1999/xhtml", 'br');
+ attachNode.appendChild(br);
+ }
+ }
+}
+
+/*
+ Import Settings from a specific module, returns false if it failed
+ and true if successful. A "local mail" account is returned in newAccount.
+ This is only useful in upgrading - import the settings first, then
+ import mail into the account returned from ImportSettings, then
+ import address books.
+ An error string is returned as error.value
+*/
+function ImportSettings(module, newAccount, error) {
+ var setIntf = module.GetImportInterface("settings");
+ if (!(setIntf instanceof Ci.nsIImportSettings)) {
+ error.value = gImportMsgsBundle.getString('ImportSettingsBadModule');
+ return false;
+ }
+
+ // determine if we can auto find the settings or if we need to ask the user
+ var location = {};
+ var description = {};
+ var result = setIntf.AutoLocate(description, location);
+ if (!result) {
+ // In this case, we couldn't find the settings
+ if (location.value != null) {
+ // Settings were not found, however, they are specified
+ // in a file, so ask the user for the settings file.
+ let filePicker = Components.classes["@mozilla.org/filepicker;1"].createInstance();
+ if (filePicker instanceof Ci.nsIFilePicker) {
+ let file = null;
+ try {
+ filePicker.init(window,
+ gImportMsgsBundle.getString("ImportSelectSettings"),
+ filePicker.modeOpen);
+ filePicker.appendFilters(filePicker.filterAll);
+ filePicker.show();
+ file = filePicker.file;
+ }
+ catch(ex) {
+ file = null;
+ error.value = null;
+ return false;
+ }
+ if (file != null) {
+ setIntf.SetLocation(file);
+ }
+ else {
+ error.value = null;
+ return false;
+ }
+ }
+ else {
+ error.value = gImportMsgsBundle.getString('ImportSettingsNotFound');
+ return false;
+ }
+ }
+ else {
+ error.value = gImportMsgsBundle.getString('ImportSettingsNotFound');
+ return false;
+ }
+ }
+
+ // interesting, we need to return the account that new
+ // mail should be imported into?
+ // that's really only useful for "Upgrade"
+ result = setIntf.Import(newAccount);
+ if (!result) {
+ error.value = gImportMsgsBundle.getString('ImportSettingsFailed');
+ }
+ return result;
+}
+
+function ImportMail(module, success, error) {
+ if (gProgressInfo.importInterface || gProgressInfo.intervalState) {
+ error.data = gImportMsgsBundle.getString('ImportAlreadyInProgress');
+ return false;
+ }
+
+ gProgressInfo.importSuccess = false;
+
+ var mailInterface = module.GetImportInterface("mail");
+ if (!(mailInterface instanceof Ci.nsIImportGeneric)) {
+ error.data = gImportMsgsBundle.getString('ImportMailBadModule');
+ return false;
+ }
+
+ var loc = mailInterface.GetData("mailLocation");
+
+ if (loc == null) {
+ // No location found, check to see if we can ask the user.
+ if (mailInterface.GetStatus("canUserSetLocation") != 0) {
+ let filePicker = Components.classes["@mozilla.org/filepicker;1"].createInstance();
+ if (filePicker instanceof Ci.nsIFilePicker) {
+ try {
+ filePicker.init(window,
+ gImportMsgsBundle.getString("ImportSelectMailDir"),
+ filePicker.modeGetFolder);
+ filePicker.appendFilters(filePicker.filterAll);
+ filePicker.show();
+ if (filePicker.file && (filePicker.file.path.length > 0))
+ mailInterface.SetData("mailLocation", filePicker.file);
+ else
+ return false;
+ } catch(ex) {
+ // don't show an error when we return!
+ return false;
+ }
+ }
+ else {
+ error.data = gImportMsgsBundle.getString('ImportMailNotFound');
+ return false;
+ }
+ }
+ else {
+ error.data = gImportMsgsBundle.getString('ImportMailNotFound');
+ return false;
+ }
+ }
+
+ if (mailInterface.WantsProgress()) {
+ if (mailInterface.BeginImport(success, error)) {
+ gProgressInfo.importInterface = mailInterface;
+ // intervalState = setInterval(ContinueImport, 100);
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return mailInterface.BeginImport(success, error);
+}
+
+
+// The address import! A little more complicated than the mail import
+// due to field maps...
+function ImportAddress(module, success, error) {
+ if (gProgressInfo.importInterface || gProgressInfo.intervalState) {
+ error.data = gImportMsgsBundle.getString('ImportAlreadyInProgress');
+ return false;
+ }
+
+ gProgressInfo.importSuccess = false;
+
+ gAddInterface = module.GetImportInterface("addressbook");
+ if (!(gAddInterface instanceof Ci.nsIImportGeneric)) {
+ error.data = gImportMsgsBundle.getString('ImportAddressBadModule');
+ return false;
+ }
+
+ var loc = gAddInterface.GetStatus("autoFind");
+ if (loc == 0) {
+ loc = gAddInterface.GetData("addressLocation");
+ if ((loc instanceof Ci.nsIFile) && !loc.exists)
+ loc = null;
+ }
+
+ if (loc == null) {
+ // Couldn't find the address book, see if we can
+ // as the user for the location or not?
+ if (gAddInterface.GetStatus("canUserSetLocation") == 0) {
+ // an autofind address book that could not be found!
+ error.data = gImportMsgsBundle.getString('ImportAddressNotFound');
+ return false;
+ }
+
+ let filePicker = Components.classes["@mozilla.org/filepicker;1"].createInstance();
+ if (!(filePicker instanceof Ci.nsIFilePicker)) {
+ error.data = gImportMsgsBundle.getString('ImportAddressNotFound');
+ return false;
+ }
+
+ // The address book location was not found.
+ // Determine if we need to ask for a directory
+ // or a single file.
+ let file = null;
+ let fileIsDirectory = false;
+ if (gAddInterface.GetStatus("supportsMultiple") != 0) {
+ // ask for dir
+ try {
+ filePicker.init(window,
+ gImportMsgsBundle.getString("ImportSelectAddrDir"),
+ filePicker.modeGetFolder);
+ filePicker.appendFilters(filePicker.filterAll);
+ filePicker.show();
+ if (filePicker.file && (filePicker.file.path.length > 0)) {
+ file = filePicker.file;
+ fileIsDirectory = true;
+ }
+ else {
+ file = null;
+ }
+ } catch(ex) {
+ file = null;
+ }
+ }
+ else {
+ // ask for file
+ try {
+ filePicker.init(window,
+ gImportMsgsBundle.getString("ImportSelectAddrFile"),
+ filePicker.modeOpen);
+ let addressbookBundle = document.getElementById("bundle_addressbook");
+ if (gSelectedModuleName ==
+ document.getElementById("bundle_vcardImportMsgs")
+ .getString("vCardImportName")) {
+ filePicker.appendFilter(addressbookBundle.getString('VCFFiles'), "*.vcf");
+ } else {
+ filePicker.appendFilter(addressbookBundle.getString('LDIFFiles'), "*.ldi; *.ldif");
+ filePicker.appendFilter(addressbookBundle.getString('CSVFiles'), "*.csv");
+ filePicker.appendFilter(addressbookBundle.getString('TABFiles'), "*.tab; *.txt");
+ filePicker.appendFilters(filePicker.filterAll);
+ }
+
+ if (filePicker.show() == filePicker.returnCancel)
+ return false;
+
+ if (filePicker.file && (filePicker.file.path.length > 0))
+ file = filePicker.file;
+ else
+ file = null;
+ } catch(ex) {
+ dump("ImportAddress(): failure when picking a file to import: " + ex + "\n");
+ file = null;
+ }
+ }
+
+ if (file == null) {
+ return false;
+ }
+
+ if (!fileIsDirectory && (file.fileSize == 0)) {
+ let errorText = gImportMsgsBundle.getFormattedString("ImportEmptyAddressBook",
+ [filePicker.file.leafName]);
+
+ Services.prompt.alert(window, document.title, errorText);
+ return false;
+ }
+ gAddInterface.SetData("addressLocation", file);
+ }
+
+ var map = gAddInterface.GetData("fieldMap");
+ if (map instanceof Ci.nsIImportFieldMap) {
+ let result = {};
+ result.ok = false;
+ window.openDialog(
+ "chrome://messenger/content/fieldMapImport.xul",
+ "",
+ "chrome,modal,titlebar",
+ { fieldMap: map,
+ addInterface: gAddInterface,
+ result: result });
+
+ if (!result.ok)
+ return false;
+ }
+
+ if (gAddInterface.WantsProgress()) {
+ if (gAddInterface.BeginImport(success, error)) {
+ gProgressInfo.importInterface = gAddInterface;
+ // intervalState = setInterval(ContinueImport, 100);
+ return true;
+ }
+ return false;
+ }
+
+ return gAddInterface.BeginImport(success, error);
+}
+
+/*
+ Import filters from a specific module.
+ Returns false if it failed and true if it succeeded.
+ An error string is returned as error.value.
+*/
+function ImportFilters(module, error)
+{
+ if (gProgressInfo.importInterface || gProgressInfo.intervalState) {
+ error.data = gImportMsgsBundle.getString('ImportAlreadyInProgress');
+ return false;
+ }
+
+ gProgressInfo.importSuccess = false;
+
+ var filtersInterface = module.GetImportInterface("filters");
+ if (!(filtersInterface instanceof Ci.nsIImportFilters)) {
+ error.data = gImportMsgsBundle.getString('ImportFiltersBadModule');
+ return false;
+ }
+
+ return filtersInterface.Import(error);
+}
+
+/*
+ Import feeds.
+*/
+function ImportFeeds()
+{
+ // Get file to open from filepicker.
+ let openFile = FeedSubscriptions.opmlPickOpenFile();
+ if (!openFile)
+ return false;
+
+ let acctName;
+ let acctNewExist = gFeedsBundle.getString("ImportFeedsExisting");
+ let fileName = openFile.path;
+ let server = document.getElementById("moduleList").selectedItem.server;
+ gNewFeedAcctCreated = false;
+
+ if (!server)
+ {
+ // Create a new Feeds account.
+ acctName = document.getElementById("acctName").value;
+ server = FeedUtils.createRssAccount(acctName).incomingServer;
+ acctNewExist = gFeedsBundle.getString("ImportFeedsNew");
+ gNewFeedAcctCreated = true;
+ }
+
+ acctName = server.rootFolder.prettyName;
+
+ let callback = function(aStatusReport, aLastFolder , aFeedWin)
+ {
+ let message = gFeedsBundle.getFormattedString("ImportFeedsDone",
+ [fileName, acctNewExist, acctName]);
+ ShowImportResultsRaw(message + " " + aStatusReport, null, true);
+ document.getElementById("back").removeAttribute("disabled");
+
+ let subscriptionsWindow = Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
+ if (subscriptionsWindow)
+ {
+ let feedWin = subscriptionsWindow.FeedSubscriptions;
+ if (aLastFolder)
+ feedWin.FolderListener.folderAdded(aLastFolder);
+ feedWin.mActionMode = null;
+ feedWin.updateButtons(feedWin.mView.currentItem);
+ feedWin.clearStatusInfo();
+ feedWin.updateStatusItem("statusText", aStatusReport);
+ }
+ }
+
+ if (!FeedSubscriptions.importOPMLFile(openFile, server, callback))
+ return false;
+
+ let subscriptionsWindow = Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
+ if (subscriptionsWindow)
+ {
+ let feedWin = subscriptionsWindow.FeedSubscriptions;
+ feedWin.mActionMode = feedWin.kImportingOPML;
+ feedWin.updateButtons(null);
+ let statusReport = gFeedsBundle.getString("subscribe-loading");
+ feedWin.updateStatusItem("statusText", statusReport);
+ feedWin.updateStatusItem("progressMeter", "?");
+ }
+
+ return true;
+}
+
+function SwitchType(newType)
+{
+ if (gImportType == newType)
+ return;
+
+ gImportType = newType;
+ gProgressInfo.importType = newType;
+
+ SetUpImportType();
+
+ SetDivText('description', "");
+}
+
+
+function next()
+{
+ var deck = document.getElementById("stateDeck");
+ switch (deck.selectedIndex) {
+ case "0":
+ let backButton = document.getElementById("back");
+ backButton.removeAttribute("disabled");
+ let radioGroup = document.getElementById("importFields");
+
+ if (radioGroup.value == "all")
+ {
+ let args = { closeMigration: true };
+ let SEAMONKEY_ID = "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}";
+ if (Services.appinfo.ID == SEAMONKEY_ID) {
+ window.openDialog("chrome://communicator/content/migration/migration.xul",
+ "", "chrome,dialog,modal,centerscreen");
+ } else {
+ // Running as Thunderbird or its clone.
+ window.openDialog("chrome://messenger/content/migration/migration.xul",
+ "", "chrome,dialog,modal,centerscreen", null, null, null, args);
+ }
+ if (args.closeMigration)
+ close();
+ }
+ else
+ {
+ SwitchType(radioGroup.value);
+ deck.selectedIndex = 1;
+ document.getElementById("modulesFound").selectedIndex =
+ (document.getElementById("moduleList").itemCount > 0) ? 0 : 1;
+ SelectFirstItem();
+ enableAdvance();
+ }
+ break;
+ case "1":
+ ImportDialogOKButton();
+ break;
+ case "3":
+ close();
+ break;
+ }
+}
+
+function SelectFirstItem()
+{
+ var listbox = document.getElementById("moduleList");
+ if ((listbox.selectedIndex == -1) && (listbox.itemCount > 0))
+ listbox.selectedIndex = 0;
+ ImportSelectionChanged();
+}
+
+function enableAdvance()
+{
+ var listbox = document.getElementById("moduleList");
+ var nextButton = document.getElementById("forward");
+ if (listbox.selectedCount > 0)
+ nextButton.removeAttribute("disabled");
+ else
+ nextButton.setAttribute("disabled", "true");
+}
+
+function back()
+{
+ var deck = document.getElementById("stateDeck");
+ var backButton = document.getElementById("back");
+ var nextButton = document.getElementById("forward");
+ switch (deck.selectedIndex) {
+ case "1":
+ backButton.setAttribute("disabled", "true");
+ nextButton.label = nextButton.getAttribute("nextval");
+ nextButton.removeAttribute("disabled");
+ deck.selectedIndex = 0;
+ break;
+ case "3":
+ // Clear out the results box.
+ let results = document.getElementById("results");
+ while (results.hasChildNodes())
+ results.lastChild.remove();
+
+ // Reset the next button.
+ nextButton.label = nextButton.getAttribute("nextval");
+ nextButton.removeAttribute("disabled");
+
+ // Enable the cancel button again.
+ document.getElementById("cancel").removeAttribute("disabled");
+
+ // If a new Feed account has been created, rebuild the list.
+ if (gNewFeedAcctCreated)
+ ListFeedAccounts();
+
+ // Now go back to the second page.
+ deck.selectedIndex = 1;
+ break;
+ }
+}
diff --git a/mailnews/import/content/importDialog.xul b/mailnews/import/content/importDialog.xul
new file mode 100644
index 000000000..383585f83
--- /dev/null
+++ b/mailnews/import/content/importDialog.xul
@@ -0,0 +1,143 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/dialogs.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % importDTD SYSTEM "chrome://messenger/locale/importDialog.dtd" >
+%importDTD;
+]>
+
+<window xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="OnLoadImportDialog()"
+#ifdef XP_MACOSX
+ style="width: &window.macWidth; !important;"
+#else
+ style="width: &window.width; !important;"
+#endif
+ title="&importDialog.windowTitle;">
+
+ <stringbundle id="bundle_importMsgs" src="chrome://messenger/locale/importMsgs.properties"/>
+ <stringbundle id="bundle_addressbook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ <stringbundle id="bundle_vcardImportMsgs" src="chrome://messenger/locale/vCardImportMsgs.properties"/>
+ <stringbundle id="bundle_feeds" src="chrome://messenger-newsblog/locale/newsblog.properties"/>
+ <script type="application/javascript" src="chrome://messenger/content/importDialog.js"/>
+ <script type="application/javascript" src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
+
+ <keyset id="dialogKeys"/>
+
+ <hbox class="box-header" id="header"
+ title="&importTitle.label;"
+ description="&importShortDesc.label;"/>
+
+ <deck id="stateDeck" selectedIndex="0" style="min-height: 30em">
+ <vbox class="wizard-box">
+ <description>&importDescription1.label;</description>
+ <description>&importDescription2.label;</description>
+ <separator/>
+ <radiogroup id="importFields">
+ <radio id="allRadio"
+ value="all"
+ label="&importAll.label;"
+ accesskey="&importAll.accesskey;"/>
+ <separator/>
+ <label control="importFields">&select.label;</label>
+ <separator class="thin"/>
+ <vbox class="indent">
+ <radio id="addressbookRadio"
+ value="addressbook"
+ label="&importAddressbook.label;"
+ accesskey="&importAddressbook.accesskey;"/>
+ <radio id="mailRadio"
+ value="mail"
+ label="&importMail.label;"
+ accesskey="&importMail.accesskey;"/>
+ <radio id="feedsRadio"
+ value="feeds"
+ label="&importFeeds.label;"
+ accesskey="&importFeeds.accesskey;"/>
+ <radio id="settingsRadio"
+ value="settings"
+ label="&importSettings.label;"
+ accesskey="&importSettings.accesskey;"/>
+ <radio id="filtersRadio"
+ value="filters"
+ label="&importFilters.label;"
+ accesskey="&importFilters.accesskey;"/>
+ </vbox>
+ </radiogroup>
+ </vbox>
+ <vbox class="wizard-box">
+ <deck id="modulesFound"
+ selectedIndex="0">
+ <vbox>
+ <deck id="selectDescriptionDeck"
+ selectedIndex="0">
+ <label control="moduleList"
+ value="&selectDescription.label;"
+ accesskey="&selectDescription.accesskey;"/>
+ <label control="moduleList"
+ value="&selectDescriptionB.label;"
+ accesskey="&selectDescription.accesskey;"/>
+ </deck>
+ <listbox id="moduleList" flex="3"
+ onselect="ImportSelectionChanged(); enableAdvance();"/>
+ </vbox>
+ <label>&noModulesFound.label;</label>
+ </deck>
+ <grid flex="1">
+ <columns><column flex="1"/></columns>
+ <rows>
+ <row>
+ <description control="moduleList" id="description" class="box-padded"/>
+ </row>
+ <row>
+ <hbox id="acctName-box" flex="1" style="visibility: hidden;">
+ <label control="acctName" class="box-padded"
+ accesskey="&acctName.accesskey;"
+ value="&acctName.label;"/>
+ <textbox id="acctName" clickSelectsAll="true"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ <vbox class="wizard-box">
+ <spacer flex="1"/>
+ <groupbox>
+ <caption id="progressTitle" label="&title.label;"/>
+ <label class="indent" id="progressStatus" value="&processing.label;"/>
+ <vbox class="box-padded">
+ <progressmeter id="progressMeter" mode="determined" value="5"/>
+ </vbox>
+ </groupbox>
+ </vbox>
+ <vbox class="wizard-box">
+ <description id="status"/>
+ <hbox style="overflow: auto" class="inset" flex="1">
+ <description id="results" flex="1"/>
+ </hbox>
+ </vbox>
+ </deck>
+
+ <separator/>
+
+ <separator class="groove"/>
+
+ <hbox class="box-padded">
+ <spacer flex="1"/>
+ <button id="back" label="&back.label;" disabled="true"
+ oncommand="back();"/>
+ <button id="forward" label="&forward.label;" nextval="&forward.label;" finishedval="&finish.label;"
+ oncommand="next();"/>
+ <separator orient="vertical"/>
+ <button id="cancel" label="&cancel.label;"
+ oncommand="close();"/>
+ </hbox>
+
+</window>
diff --git a/mailnews/import/outlook/src/MapiApi.cpp b/mailnews/import/outlook/src/MapiApi.cpp
new file mode 100644
index 000000000..d23b44dd1
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiApi.cpp
@@ -0,0 +1,1937 @@
+/* -*- 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/. */
+
+#include "MapiDbgLog.h"
+#include "MapiApi.h"
+
+#include <sstream>
+#include "rtfMailDecoder.h"
+
+#include "prprf.h"
+#include "nsMemory.h"
+#include "nsMsgUtils.h"
+#include "nsUnicharUtils.h"
+
+int CMapiApi::m_clients = 0;
+BOOL CMapiApi::m_initialized = false;
+nsTArray<CMsgStore*> *CMapiApi::m_pStores = NULL;
+LPMAPISESSION CMapiApi::m_lpSession = NULL;
+LPMDB CMapiApi::m_lpMdb = NULL;
+HRESULT CMapiApi::m_lastError;
+char16_t * CMapiApi::m_pUniBuff = NULL;
+int CMapiApi::m_uniBuffLen = 0;
+/*
+Type: 1, name: Calendar, class: IPF.Appointment
+Type: 1, name: Contacts, class: IPF.Contact
+Type: 1, name: Journal, class: IPF.Journal
+Type: 1, name: Notes, class: IPF.StickyNote
+Type: 1, name: Tasks, class: IPF.Task
+Type: 1, name: Drafts, class: IPF.Note
+*/
+
+HINSTANCE CMapiApi::m_hMapi32 = NULL;
+
+LPMAPIUNINITIALIZE gpMapiUninitialize = NULL;
+LPMAPIINITIALIZE gpMapiInitialize = NULL;
+LPMAPIALLOCATEBUFFER gpMapiAllocateBuffer = NULL;
+LPMAPIFREEBUFFER gpMapiFreeBuffer = NULL;
+LPMAPILOGONEX gpMapiLogonEx = NULL;
+LPOPENSTREAMONFILE gpMapiOpenStreamOnFile = NULL;
+
+typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAM) (
+ LPSTREAM lpCompressedRTFStream, ULONG ulFlags, LPSTREAM FAR *lpUncompressedRTFStream);
+typedef WRAPCOMPRESSEDRTFSTREAM *LPWRAPCOMPRESSEDRTFSTREAM;
+LPWRAPCOMPRESSEDRTFSTREAM gpWrapCompressedRTFStream = NULL;
+
+// WrapCompressedRTFStreamEx related stuff - see http://support.microsoft.com/kb/839560
+typedef struct {
+ ULONG size;
+ ULONG ulFlags;
+ ULONG ulInCodePage;
+ ULONG ulOutCodePage;
+} RTF_WCSINFO;
+typedef struct {
+ ULONG size;
+ ULONG ulStreamFlags;
+} RTF_WCSRETINFO;
+
+typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAMEX) (
+ LPSTREAM lpCompressedRTFStream, CONST RTF_WCSINFO * pWCSInfo,
+ LPSTREAM * lppUncompressedRTFStream, RTF_WCSRETINFO * pRetInfo);
+typedef WRAPCOMPRESSEDRTFSTREAMEX *LPWRAPCOMPRESSEDRTFSTREAMEX;
+LPWRAPCOMPRESSEDRTFSTREAMEX gpWrapCompressedRTFStreamEx = NULL;
+
+BOOL CMapiApi::LoadMapiEntryPoints(void)
+{
+ if (!(gpMapiUninitialize = (LPMAPIUNINITIALIZE) GetProcAddress(
+ m_hMapi32, "MAPIUninitialize")))
+ return FALSE;
+ if (!(gpMapiInitialize = (LPMAPIINITIALIZE) GetProcAddress(
+ m_hMapi32, "MAPIInitialize")))
+ return FALSE;
+ if (!(gpMapiAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress(
+ m_hMapi32, "MAPIAllocateBuffer")))
+ return FALSE;
+ if (!(gpMapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress(
+ m_hMapi32, "MAPIFreeBuffer")))
+ return FALSE;
+ if (!(gpMapiLogonEx = (LPMAPILOGONEX) GetProcAddress(m_hMapi32,
+ "MAPILogonEx")))
+ return FALSE;
+ if (!(gpMapiOpenStreamOnFile = (LPOPENSTREAMONFILE) GetProcAddress(
+ m_hMapi32, "OpenStreamOnFile")))
+ return FALSE;
+
+ // Available from the Outlook 2002 post-SP3 hotfix (http://support.microsoft.com/kb/883924/)
+ // Exported by msmapi32.dll; so it's unavailable to us using mapi32.dll
+ gpWrapCompressedRTFStreamEx = (LPWRAPCOMPRESSEDRTFSTREAMEX) GetProcAddress(
+ m_hMapi32, "WrapCompressedRTFStreamEx");
+ // Available always
+ gpWrapCompressedRTFStream = (LPWRAPCOMPRESSEDRTFSTREAM) GetProcAddress(
+ m_hMapi32, "WrapCompressedRTFStream");
+
+ return TRUE;
+}
+
+// Gets the PR_RTF_COMPRESSED tag property
+// Codepage is used only if the WrapCompressedRTFStreamEx is available
+BOOL CMapiApi::GetRTFPropertyDecodedAsUTF16(LPMAPIPROP pProp, nsString& val,
+ unsigned long& nativeBodyType,
+ unsigned long codepage)
+{
+ if (!m_hMapi32 || !(gpWrapCompressedRTFStreamEx || gpWrapCompressedRTFStream))
+ return FALSE; // Fallback to the default processing
+
+ LPSTREAM icstream = 0; // for the compressed stream
+ LPSTREAM iunstream = 0; // for the uncompressed stream
+ HRESULT hr = pProp->OpenProperty(PR_RTF_COMPRESSED,
+ &IID_IStream, STGM_READ | STGM_DIRECT,
+ 0, (LPUNKNOWN *)&icstream);
+ if (HR_FAILED(hr))
+ return FALSE;
+
+ if (gpWrapCompressedRTFStreamEx) { // Impossible - we use mapi32.dll!
+ RTF_WCSINFO wcsinfo = {0};
+ RTF_WCSRETINFO retinfo = {0};
+
+ retinfo.size = sizeof(RTF_WCSRETINFO);
+
+ wcsinfo.size = sizeof(RTF_WCSINFO);
+ wcsinfo.ulFlags = MAPI_NATIVE_BODY;
+ wcsinfo.ulInCodePage = codepage;
+ wcsinfo.ulOutCodePage = CP_UTF8;
+
+ if(HR_SUCCEEDED(hr = gpWrapCompressedRTFStreamEx(icstream, &wcsinfo,
+ &iunstream, &retinfo)))
+ nativeBodyType = retinfo.ulStreamFlags;
+ }
+ else { // mapi32.dll
+ gpWrapCompressedRTFStream(icstream,0,&iunstream);
+ }
+ icstream->Release();
+
+ if(iunstream) { // Succeeded
+ std::string streamData;
+ // Stream.Stat doesn't work for this stream!
+ bool done = false;
+ while (!done) {
+ // I think 10K is a good guess to minimize the number of reads while keeping memory usage low
+ const int bufsize = 10240;
+ char buf[bufsize];
+ ULONG read;
+ hr = iunstream->Read(buf, bufsize, &read);
+ done = (read < bufsize) || (hr != S_OK);
+ if (read)
+ streamData.append(buf, read);
+ }
+ iunstream->Release();
+ // if rtf -> convert to plain text.
+ if (!gpWrapCompressedRTFStreamEx ||
+ (nativeBodyType==MAPI_NATIVE_BODY_TYPE_RTF)) {
+ std::stringstream s(streamData);
+ CRTFMailDecoder decoder;
+ DecodeRTF(s, decoder);
+ if (decoder.mode() == CRTFMailDecoder::mHTML)
+ nativeBodyType = MAPI_NATIVE_BODY_TYPE_HTML;
+ else if (decoder.mode() == CRTFMailDecoder::mText)
+ nativeBodyType = MAPI_NATIVE_BODY_TYPE_PLAINTEXT;
+ else
+ nativeBodyType = MAPI_NATIVE_BODY_TYPE_RTF;
+ val.Assign(decoder.text(), decoder.textSize());
+ }
+ else { // WrapCompressedRTFStreamEx available and original type is not rtf
+ CopyUTF8toUTF16(nsDependentCString(streamData.c_str()), val);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CMapiApi::MAPIUninitialize(void)
+{
+ if (m_hMapi32 && gpMapiUninitialize)
+ (*gpMapiUninitialize)();
+}
+
+HRESULT CMapiApi::MAPIInitialize(LPVOID lpInit)
+{
+ return (m_hMapi32 && gpMapiInitialize) ? (*gpMapiInitialize)(lpInit) :
+ MAPI_E_NOT_INITIALIZED;
+}
+
+SCODE CMapiApi::MAPIAllocateBuffer(ULONG cbSize, LPVOID FAR * lppBuffer)
+{
+ return (m_hMapi32 && gpMapiAllocateBuffer) ?
+ (*gpMapiAllocateBuffer)(cbSize, lppBuffer) : MAPI_E_NOT_INITIALIZED;
+}
+
+ULONG CMapiApi::MAPIFreeBuffer(LPVOID lpBuff)
+{
+ return (m_hMapi32 && gpMapiFreeBuffer) ? (*gpMapiFreeBuffer)(lpBuff) :
+ MAPI_E_NOT_INITIALIZED;
+}
+
+HRESULT CMapiApi::MAPILogonEx(ULONG ulUIParam, LPTSTR lpszProfileName,
+ LPTSTR lpszPassword, FLAGS flFlags,
+ LPMAPISESSION FAR * lppSession)
+{
+ return (m_hMapi32 && gpMapiLogonEx) ?
+ (*gpMapiLogonEx)(ulUIParam, lpszProfileName, lpszPassword, flFlags, lppSession) :
+ MAPI_E_NOT_INITIALIZED;
+}
+
+HRESULT CMapiApi::OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer,
+ LPFREEBUFFER lpFreeBuffer, ULONG ulFlags,
+ LPTSTR lpszFileName, LPTSTR lpszPrefix,
+ LPSTREAM FAR * lppStream)
+{
+ return (m_hMapi32 && gpMapiOpenStreamOnFile) ?
+ (*gpMapiOpenStreamOnFile)(lpAllocateBuffer, lpFreeBuffer, ulFlags,
+ lpszFileName, lpszPrefix, lppStream) :
+ MAPI_E_NOT_INITIALIZED;
+}
+
+void CMapiApi::FreeProws(LPSRowSet prows)
+{
+ ULONG irow;
+ if (!prows)
+ return;
+ for (irow = 0; irow < prows->cRows; ++irow)
+ MAPIFreeBuffer(prows->aRow[irow].lpProps);
+ MAPIFreeBuffer(prows);
+}
+
+BOOL CMapiApi::LoadMapi(void)
+{
+ if (m_hMapi32)
+ return TRUE;
+
+ HINSTANCE hInst = ::LoadLibrary("MAPI32.DLL");
+ if (!hInst)
+ return FALSE;
+ FARPROC pProc = GetProcAddress(hInst, "MAPIGetNetscapeVersion");
+ if (pProc) {
+ ::FreeLibrary(hInst);
+ hInst = ::LoadLibrary("MAPI32BAK.DLL");
+ if (!hInst)
+ return FALSE;
+ }
+
+ m_hMapi32 = hInst;
+ return LoadMapiEntryPoints();
+}
+
+void CMapiApi::UnloadMapi(void)
+{
+ if (m_hMapi32)
+ ::FreeLibrary(m_hMapi32);
+ m_hMapi32 = NULL;
+}
+
+CMapiApi::CMapiApi()
+{
+ m_clients++;
+ LoadMapi();
+ if (!m_pStores)
+ m_pStores = new nsTArray<CMsgStore*>();
+}
+
+CMapiApi::~CMapiApi()
+{
+ m_clients--;
+ if (!m_clients) {
+ HRESULT hr;
+
+ ClearMessageStores();
+ delete m_pStores;
+ m_pStores = NULL;
+
+ m_lpMdb = NULL;
+
+ if (m_lpSession) {
+ hr = m_lpSession->Logoff(NULL, 0, 0);
+ if (FAILED(hr)) {
+ MAPI_TRACE2("Logoff failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ }
+ m_lpSession->Release();
+ m_lpSession = NULL;
+ }
+
+ if (m_initialized) {
+ MAPIUninitialize();
+ m_initialized = FALSE;
+ }
+
+ UnloadMapi();
+
+ if (m_pUniBuff)
+ delete [] m_pUniBuff;
+ m_pUniBuff = NULL;
+ m_uniBuffLen = 0;
+ }
+}
+
+void CMapiApi::CStrToUnicode(const char *pStr, nsString& result)
+{
+ result.Truncate();
+ int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), 0);
+ if (wLen >= m_uniBuffLen) {
+ if (m_pUniBuff)
+ delete [] m_pUniBuff;
+ m_pUniBuff = new char16_t[wLen + 64];
+ m_uniBuffLen = wLen + 64;
+ }
+ if (wLen) {
+ MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), m_uniBuffLen);
+ result = m_pUniBuff;
+ }
+}
+
+BOOL CMapiApi::Initialize(void)
+{
+ if (m_initialized)
+ return TRUE;
+
+ HRESULT hr;
+
+ hr = MAPIInitialize(NULL);
+
+ if (FAILED(hr)) {
+ MAPI_TRACE2("MAPI Initialize failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ m_initialized = TRUE;
+ MAPI_TRACE0("MAPI Initialized\n");
+
+ return TRUE;
+}
+
+BOOL CMapiApi::LogOn(void)
+{
+ if (!m_initialized) {
+ MAPI_TRACE0("Tried to LogOn before initializing MAPI\n");
+ return FALSE;
+ }
+
+ if (m_lpSession)
+ return TRUE;
+
+ HRESULT hr;
+
+ hr = MAPILogonEx( 0, // might need to be passed in HWND
+ NULL, // profile name, 64 char max (LPTSTR)
+ NULL, // profile password, 64 char max (LPTSTR)
+ // MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI | MAPI_EXPLICIT_PROFILE,
+ // MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI,
+ // MAPI_NO_MAIL | MAPI_LOGON_UI,
+ MAPI_NO_MAIL | MAPI_USE_DEFAULT | MAPI_EXTENDED | MAPI_NEW_SESSION,
+ &m_lpSession);
+
+ if (FAILED(hr)) {
+ m_lpSession = NULL;
+ MAPI_TRACE2("LogOn failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ MAPI_TRACE0("MAPI Logged on\n");
+ return TRUE;
+}
+
+class CGetStoreFoldersIter : public CMapiHierarchyIter {
+public:
+ CGetStoreFoldersIter(CMapiApi *pApi, CMapiFolderList& folders, int depth, BOOL isMail = TRUE);
+
+ virtual BOOL HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry);
+
+protected:
+ BOOL ExcludeFolderClass(const char16_t *pName);
+
+ BOOL m_isMail;
+ CMapiApi * m_pApi;
+ CMapiFolderList * m_pList;
+ int m_depth;
+};
+
+CGetStoreFoldersIter::CGetStoreFoldersIter(CMapiApi *pApi, CMapiFolderList& folders, int depth, BOOL isMail)
+{
+ m_pApi = pApi;
+ m_pList = &folders;
+ m_depth = depth;
+ m_isMail = isMail;
+}
+
+BOOL CGetStoreFoldersIter::ExcludeFolderClass(const char16_t *pName)
+{
+ BOOL bResult;
+ nsDependentString pNameStr(pName);
+ if (m_isMail) {
+ bResult = FALSE;
+ if (pNameStr.EqualsLiteral("IPF.Appointment"))
+ bResult = TRUE;
+ else if (pNameStr.EqualsLiteral("IPF.Contact"))
+ bResult = TRUE;
+ else if (pNameStr.EqualsLiteral("IPF.Journal"))
+ bResult = TRUE;
+ else if (pNameStr.EqualsLiteral("IPF.StickyNote"))
+ bResult = TRUE;
+ else if (pNameStr.EqualsLiteral("IPF.Task"))
+ bResult = TRUE;
+ // Skip IMAP folders
+ else if (pNameStr.EqualsLiteral("IPF.Imap"))
+ bResult = TRUE;
+ // else if (!stricmp(pName, "IPF.Note"))
+ // bResult = TRUE;
+ }
+ else {
+ bResult = TRUE;
+ if (pNameStr.EqualsLiteral("IPF.Contact"))
+ bResult = FALSE;
+ }
+
+ return bResult;
+}
+
+BOOL CGetStoreFoldersIter::HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry)
+{
+ if (oType == MAPI_FOLDER) {
+ LPMAPIFOLDER pFolder;
+ if (m_pApi->OpenEntry(cb, pEntry, (LPUNKNOWN *) &pFolder)) {
+ LPSPropValue pVal;
+ nsString name;
+
+ pVal = m_pApi->GetMapiProperty(pFolder, PR_CONTAINER_CLASS);
+ if (pVal)
+ m_pApi->GetStringFromProp(pVal, name);
+ else
+ name.Truncate();
+
+ if ((name.IsEmpty() && m_isMail) || (!ExcludeFolderClass(name.get()))) {
+ pVal = m_pApi->GetMapiProperty(pFolder, PR_DISPLAY_NAME);
+ m_pApi->GetStringFromProp(pVal, name);
+ CMapiFolder *pNewFolder = new CMapiFolder(name.get(), cb, pEntry, m_depth);
+ m_pList->AddItem(pNewFolder);
+
+ pVal = m_pApi->GetMapiProperty(pFolder, PR_FOLDER_TYPE);
+ MAPI_TRACE2("Type: %d, name: %s\n",
+ m_pApi->GetLongFromProp(pVal), name.get());
+ // m_pApi->ListProperties(pFolder);
+
+ CGetStoreFoldersIter nextIter(m_pApi, *m_pList, m_depth + 1, m_isMail);
+ m_pApi->IterateHierarchy(&nextIter, pFolder);
+ }
+ pFolder->Release();
+ }
+ else {
+ MAPI_TRACE0("GetStoreFolders - HandleHierarchyItem: Error opening folder entry.\n");
+ return FALSE;
+ }
+ }
+ else
+ MAPI_TRACE1("GetStoreFolders - HandleHierarchyItem: Unhandled ObjectType: %ld\n", oType);
+ return TRUE;
+}
+
+BOOL CMapiApi::GetStoreFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders, int startDepth)
+{
+ // Fill in the array with the folders in the given store
+ if (!m_initialized || !m_lpSession) {
+ MAPI_TRACE0("MAPI not initialized for GetStoreFolders\n");
+ return FALSE;
+ }
+
+ m_lpMdb = NULL;
+
+ CMsgStore * pStore = FindMessageStore(cbEid, lpEid);
+ BOOL bResult = FALSE;
+ LPSPropValue pVal;
+
+ if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) {
+ // Successful open, do the iteration of the store
+ pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID);
+ if (pVal) {
+ ULONG cbEntry;
+ LPENTRYID pEntry;
+ LPMAPIFOLDER lpSubTree = NULL;
+
+ if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
+ // Open up the folder!
+ bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN *)&lpSubTree);
+ MAPIFreeBuffer(pEntry);
+ if (bResult && lpSubTree) {
+ // Iterate the subtree with the results going into the folder list
+ CGetStoreFoldersIter iterHandler(this, folders, startDepth);
+ bResult = IterateHierarchy(&iterHandler, lpSubTree);
+ lpSubTree->Release();
+ }
+ else {
+ MAPI_TRACE0("GetStoreFolders: Error opening sub tree.\n");
+ }
+ }
+ else {
+ MAPI_TRACE0("GetStoreFolders: Error getting entryID from sub tree property val.\n");
+ }
+ }
+ else {
+ MAPI_TRACE0("GetStoreFolders: Error getting sub tree property.\n");
+ }
+ }
+ else {
+ MAPI_TRACE0("GetStoreFolders: Error opening message store.\n");
+ }
+
+ return bResult;
+}
+
+BOOL CMapiApi::GetStoreAddressFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders)
+{
+ // Fill in the array with the folders in the given store
+ if (!m_initialized || !m_lpSession) {
+ MAPI_TRACE0("MAPI not initialized for GetStoreAddressFolders\n");
+ return FALSE;
+ }
+
+ m_lpMdb = NULL;
+
+ CMsgStore * pStore = FindMessageStore(cbEid, lpEid);
+ BOOL bResult = FALSE;
+ LPSPropValue pVal;
+
+ if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) {
+ // Successful open, do the iteration of the store
+ pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID);
+ if (pVal) {
+ ULONG cbEntry;
+ LPENTRYID pEntry;
+ LPMAPIFOLDER lpSubTree = NULL;
+
+ if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
+ // Open up the folder!
+ bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN *)&lpSubTree);
+ MAPIFreeBuffer(pEntry);
+ if (bResult && lpSubTree) {
+ // Iterate the subtree with the results going into the folder list
+ CGetStoreFoldersIter iterHandler(this, folders, 1, FALSE);
+ bResult = IterateHierarchy(&iterHandler, lpSubTree);
+ lpSubTree->Release();
+ }
+ else {
+ MAPI_TRACE0("GetStoreAddressFolders: Error opening sub tree.\n");
+ }
+ }
+ else {
+ MAPI_TRACE0("GetStoreAddressFolders: Error getting entryID from sub tree property val.\n");
+ }
+ }
+ else {
+ MAPI_TRACE0("GetStoreAddressFolders: Error getting sub tree property.\n");
+ }
+ }
+ else
+ MAPI_TRACE0("GetStoreAddressFolders: Error opening message store.\n");
+
+ return bResult;
+}
+
+
+BOOL CMapiApi::OpenStore(ULONG cbEid, LPENTRYID lpEid, LPMDB *ppMdb)
+{
+ if (!m_lpSession) {
+ MAPI_TRACE0("OpenStore called before a session was opened\n");
+ return FALSE;
+ }
+
+ CMsgStore * pStore = FindMessageStore(cbEid, lpEid);
+ if (pStore && pStore->Open(m_lpSession, ppMdb))
+ return TRUE;
+ return FALSE;
+}
+
+
+BOOL CMapiApi::OpenEntry(ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen)
+{
+ if (!m_lpMdb) {
+ MAPI_TRACE0("OpenEntry called before the message store is open\n");
+ return FALSE;
+ }
+
+ return OpenMdbEntry(m_lpMdb, cbEntry, pEntryId, ppOpen);
+}
+
+BOOL CMapiApi::OpenMdbEntry(LPMDB lpMdb, ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen)
+{
+ ULONG ulObjType;
+ HRESULT hr;
+ hr = m_lpSession->OpenEntry(cbEntry,
+ pEntryId,
+ NULL,
+ 0,
+ &ulObjType,
+ (LPUNKNOWN *) ppOpen);
+ if (FAILED(hr)) {
+ MAPI_TRACE2("OpenMdbEntry failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+enum {
+ ieidPR_ENTRYID = 0,
+ ieidPR_OBJECT_TYPE,
+ ieidMax
+};
+
+static const SizedSPropTagArray(ieidMax, ptaEid)=
+{
+ ieidMax,
+ {
+ PR_ENTRYID,
+ PR_OBJECT_TYPE,
+ }
+};
+
+BOOL CMapiApi::IterateContents(CMapiContentIter *pIter, LPMAPIFOLDER pFolder, ULONG flags)
+{
+ // flags can be 0 or MAPI_ASSOCIATED
+ // MAPI_ASSOCIATED is usually used for forms and views
+
+ HRESULT hr;
+ LPMAPITABLE lpTable;
+ hr = pFolder->GetContentsTable(flags, &lpTable);
+ if (FAILED(hr)) {
+ MAPI_TRACE2("GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ ULONG rowCount;
+ hr = lpTable->GetRowCount(0, &rowCount);
+ if (!rowCount) {
+ MAPI_TRACE0(" Empty Table\n");
+ }
+
+ hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
+ if (FAILED(hr)) {
+ lpTable->Release();
+ MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (FAILED(hr)) {
+ lpTable->Release();
+ MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ int cNumRows = 0;
+ LPSRowSet lpRow;
+ BOOL keepGoing = TRUE;
+ BOOL bResult = TRUE;
+ do {
+ lpRow = NULL;
+ hr = lpTable->QueryRows(1, 0, &lpRow);
+ if(HR_FAILED(hr)) {
+ MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ bResult = FALSE;
+ break;
+ }
+
+ if(lpRow) {
+ cNumRows = lpRow->cRows;
+ if (cNumRows) {
+ LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
+ ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
+ ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
+ keepGoing = HandleContentsItem(oType, cbEID, lpEID);
+ MAPI_TRACE1(" ObjectType: %ld\n", oType);
+ }
+ FreeProws(lpRow);
+ }
+
+ } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
+
+ lpTable->Release();
+ return bResult;
+}
+
+BOOL CMapiApi::HandleContentsItem(ULONG oType, ULONG cb, LPENTRYID pEntry)
+{
+ if (oType == MAPI_MESSAGE) {
+ LPMESSAGE pMsg;
+ if (OpenEntry(cb, pEntry, (LPUNKNOWN *) &pMsg)) {
+ LPSPropValue pVal;
+ pVal = GetMapiProperty(pMsg, PR_SUBJECT);
+ ReportStringProp("PR_SUBJECT:", pVal);
+ pVal = GetMapiProperty(pMsg, PR_DISPLAY_BCC);
+ ReportStringProp("PR_DISPLAY_BCC:", pVal);
+ pVal = GetMapiProperty(pMsg, PR_DISPLAY_CC);
+ ReportStringProp("PR_DISPLAY_CC:", pVal);
+ pVal = GetMapiProperty(pMsg, PR_DISPLAY_TO);
+ ReportStringProp("PR_DISPLAY_TO:", pVal);
+ pVal = GetMapiProperty(pMsg, PR_MESSAGE_CLASS);
+ ReportStringProp("PR_MESSAGE_CLASS:", pVal);
+ ListProperties(pMsg);
+ pMsg->Release();
+ }
+ else {
+ MAPI_TRACE0(" Folder type - error opening\n");
+ }
+ }
+ else
+ MAPI_TRACE1(" ObjectType: %ld\n", oType);
+
+ return TRUE;
+}
+
+void CMapiApi::ListProperties(LPMAPIPROP lpProp, BOOL getValues)
+{
+ LPSPropTagArray pArray;
+ HRESULT hr = lpProp->GetPropList(0, &pArray);
+ if (FAILED(hr)) {
+ MAPI_TRACE0(" Unable to retrieve property list\n");
+ return;
+ }
+ ULONG count = 0;
+ LPMAPINAMEID FAR * lppPropNames;
+ SPropTagArray tagArray;
+ LPSPropTagArray lpTagArray = &tagArray;
+ tagArray.cValues = (ULONG)1;
+ nsCString desc;
+ for (ULONG i = 0; i < pArray->cValues; i++) {
+ GetPropTagName(pArray->aulPropTag[i], desc);
+ if (getValues) {
+ tagArray.aulPropTag[0] = pArray->aulPropTag[i];
+ hr = lpProp->GetNamesFromIDs(&lpTagArray, nullptr, 0, &count, &lppPropNames);
+ if (hr == S_OK)
+ MAPIFreeBuffer(lppPropNames);
+
+ LPSPropValue pVal = GetMapiProperty(lpProp, pArray->aulPropTag[i]);
+ if (pVal) {
+ desc += ", ";
+ ListPropertyValue(pVal, desc);
+ MAPIFreeBuffer(pVal);
+ }
+ }
+ MAPI_TRACE2(" Tag #%d: %s\n", (int) i, desc.get());
+ }
+
+ MAPIFreeBuffer(pArray);
+}
+
+ULONG CMapiApi::GetEmailPropertyTag(LPMAPIPROP lpProp, LONG nameID)
+{
+static GUID emailGUID = {
+ 0x00062004, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46
+};
+
+ MAPINAMEID mapiNameID;
+ mapiNameID.lpguid = &emailGUID;
+ mapiNameID.ulKind = MNID_ID;
+ mapiNameID.Kind.lID = nameID;
+
+ LPMAPINAMEID lpMapiNames = &mapiNameID;
+ LPSPropTagArray lpMailTagArray = nullptr;
+
+ HRESULT result = lpProp->GetIDsFromNames(1L, &lpMapiNames, 0, &lpMailTagArray);
+ if (result == S_OK)
+ {
+ ULONG lTag = lpMailTagArray->aulPropTag[0];
+ MAPIFreeBuffer(lpMailTagArray);
+ return lTag;
+ }
+ else
+ return 0L;
+}
+
+BOOL CMapiApi::HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry)
+{
+ if (oType == MAPI_FOLDER) {
+ LPMAPIFOLDER pFolder;
+ if (OpenEntry(cb, pEntry, (LPUNKNOWN *) &pFolder)) {
+ LPSPropValue pVal;
+ pVal = GetMapiProperty(pFolder, PR_DISPLAY_NAME);
+ ReportStringProp("Folder name:", pVal);
+ IterateContents(NULL, pFolder);
+ IterateHierarchy(NULL, pFolder);
+ pFolder->Release();
+ }
+ else {
+ MAPI_TRACE0(" Folder type - error opening\n");
+ }
+ }
+ else
+ MAPI_TRACE1(" ObjectType: %ld\n", oType);
+
+ return TRUE;
+}
+
+BOOL CMapiApi::IterateHierarchy(CMapiHierarchyIter *pIter, LPMAPIFOLDER pFolder, ULONG flags)
+{
+ // flags can be CONVENIENT_DEPTH or 0
+ // CONVENIENT_DEPTH will return all depths I believe instead
+ // of just children
+ HRESULT hr;
+ LPMAPITABLE lpTable;
+ hr = pFolder->GetHierarchyTable(flags, &lpTable);
+ if (HR_FAILED(hr)) {
+ m_lastError = hr;
+ MAPI_TRACE2("IterateHierarchy: GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ ULONG rowCount;
+ hr = lpTable->GetRowCount(0, &rowCount);
+ if (!rowCount) {
+ lpTable->Release();
+ return TRUE;
+ }
+
+ hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
+ if (HR_FAILED(hr)) {
+ m_lastError = hr;
+ lpTable->Release();
+ MAPI_TRACE2("IterateHierarchy: SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (HR_FAILED(hr)) {
+ m_lastError = hr;
+ lpTable->Release();
+ MAPI_TRACE2("IterateHierarchy: SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ int cNumRows = 0;
+ LPSRowSet lpRow;
+ BOOL keepGoing = TRUE;
+ BOOL bResult = TRUE;
+ do {
+ lpRow = NULL;
+ hr = lpTable->QueryRows(1, 0, &lpRow);
+
+ if(HR_FAILED(hr)) {
+ MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ m_lastError = hr;
+ bResult = FALSE;
+ break;
+ }
+
+ if(lpRow) {
+ cNumRows = lpRow->cRows;
+
+ if (cNumRows) {
+ LPENTRYID lpEntry = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
+ ULONG cb = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
+ ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
+
+ if (pIter)
+ keepGoing = pIter->HandleHierarchyItem(oType, cb, lpEntry);
+ else
+ keepGoing = HandleHierarchyItem(oType, cb, lpEntry);
+
+ }
+ FreeProws(lpRow);
+ }
+ } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
+
+ lpTable->Release();
+
+ if (bResult && !keepGoing)
+ bResult = FALSE;
+
+ return bResult;
+}
+
+
+enum {
+ itblPR_DISPLAY_NAME,
+ itblPR_ENTRYID,
+ itblMax
+};
+
+static const SizedSPropTagArray(itblMax, ptaTbl)=
+{
+ itblMax,
+ {
+ PR_DISPLAY_NAME,
+ PR_ENTRYID,
+ }
+};
+
+BOOL CMapiApi::IterateStores(CMapiFolderList& stores)
+{
+ stores.ClearAll();
+
+ if (!m_lpSession) {
+ MAPI_TRACE0("IterateStores called before session is open\n");
+ m_lastError = E_UNEXPECTED;
+ return FALSE;
+ }
+
+
+ HRESULT hr;
+
+ /* -- Some Microsoft sample code just to see if things are working --- *//*
+
+ ULONG cbEIDStore;
+ LPENTRYID lpEIDStore;
+
+ hr = HrMAPIFindDefaultMsgStore(m_lpSession, &cbEIDStore, &lpEIDStore);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE0("Default message store not found\n");
+ // MessageBoxW(NULL, L"Message Store Not Found", NULL, MB_OK);
+ }
+ else {
+ LPMDB lpStore;
+ MAPI_TRACE0("Default Message store FOUND\n");
+ hr = m_lpSession->OpenMsgStore(NULL, cbEIDStore,
+ lpEIDStore, NULL,
+ MDB_NO_MAIL | MDB_NO_DIALOG, &lpStore);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE1("Unable to open default message store: 0x%lx\n", hr);
+ }
+ else {
+ MAPI_TRACE0("Default message store OPENED\n");
+ lpStore->Release();
+ }
+ }
+ */
+
+
+
+ LPMAPITABLE lpTable;
+
+ hr = m_lpSession->GetMsgStoresTable(0, &lpTable);
+ if (FAILED(hr)) {
+ MAPI_TRACE0("GetMsgStoresTable failed\n");
+ m_lastError = hr;
+ return FALSE;
+ }
+
+
+ ULONG rowCount;
+ hr = lpTable->GetRowCount(0, &rowCount);
+ MAPI_TRACE1("MsgStores Table rowCount: %ld\n", rowCount);
+
+ hr = lpTable->SetColumns((LPSPropTagArray)&ptaTbl, 0);
+ if (FAILED(hr)) {
+ lpTable->Release();
+ MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ m_lastError = hr;
+ return FALSE;
+ }
+
+ hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (FAILED(hr)) {
+ lpTable->Release();
+ MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ m_lastError = hr;
+ return FALSE;
+ }
+
+ int cNumRows = 0;
+ LPSRowSet lpRow;
+ BOOL keepGoing = TRUE;
+ BOOL bResult = TRUE;
+ do {
+ lpRow = NULL;
+ hr = lpTable->QueryRows(1, 0, &lpRow);
+
+ if(HR_FAILED(hr)) {
+ MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ bResult = FALSE;
+ m_lastError = hr;
+ break;
+ }
+
+ if(lpRow) {
+ cNumRows = lpRow->cRows;
+
+ if (cNumRows) {
+ LPCTSTR lpStr = (LPCTSTR) lpRow->aRow[0].lpProps[itblPR_DISPLAY_NAME].Value.LPSZ;
+ LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.lpb;
+ ULONG cbEID = lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.cb;
+
+ // In the future, GetStoreInfo needs to somehow return
+ // whether or not the store is from an IMAP server.
+ // Currently, GetStoreInfo opens the store and attempts
+ // to get the hierarchy tree. If the tree is empty or
+ // does not exist, then szContents will be zero. We'll
+ // assume that any store that doesn't have anything in
+ // it's hierarchy tree is not a store we want to import -
+ // there would be nothing to import from anyway!
+ // Currently, this does exclude IMAP server accounts
+ // which is the desired behaviour.
+
+ int strLen = strlen(lpStr);
+ char16_t * pwszStr = (char16_t *) moz_xmalloc((strLen + 1) * sizeof(WCHAR));
+ if (!pwszStr) {
+ // out of memory
+ FreeProws(lpRow);
+ lpTable->Release();
+ return FALSE;
+ }
+ ::MultiByteToWideChar(CP_ACP, 0, lpStr, strlen(lpStr) + 1, wwc(pwszStr), (strLen + 1) * sizeof(WCHAR));
+ CMapiFolder *pFolder = new CMapiFolder(pwszStr, cbEID, lpEID, 0, MAPI_STORE);
+ free(pwszStr);
+
+ long szContents = 1;
+ GetStoreInfo(pFolder, &szContents);
+
+ MAPI_TRACE1(" DisplayName: %s\n", lpStr);
+ if (szContents)
+ stores.AddItem(pFolder);
+ else {
+ delete pFolder;
+ MAPI_TRACE0(" ^^^^^ Not added to store list\n");
+ }
+
+ keepGoing = TRUE;
+ }
+ FreeProws(lpRow);
+ }
+ } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
+
+ lpTable->Release();
+
+ return bResult;
+}
+
+void CMapiApi::GetStoreInfo(CMapiFolder *pFolder, long *pSzContents)
+{
+ HRESULT hr;
+ LPMDB lpMdb;
+
+ if (pSzContents)
+ *pSzContents = 0;
+
+ if (!OpenStore(pFolder->GetCBEntryID(), pFolder->GetEntryID(), &lpMdb))
+ return;
+
+ LPSPropValue pVal;
+ /*
+ pVal = GetMapiProperty(lpMdb, PR_DISPLAY_NAME);
+ ReportStringProp(" Message store name:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_MDB_PROVIDER);
+ ReportUIDProp(" Message store provider:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_COMMENT);
+ ReportStringProp(" Message comment:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_ACCESS_LEVEL);
+ ReportLongProp(" Message store Access Level:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_STORE_SUPPORT_MASK);
+ ReportLongProp(" Message store support mask:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_STORE_STATE);
+ ReportLongProp(" Message store state:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_OBJECT_TYPE);
+ ReportLongProp(" Message store object type:", pVal);
+ pVal = GetMapiProperty(lpMdb, PR_VALID_FOLDER_MASK);
+ ReportLongProp(" Message store valid folder mask:", pVal);
+
+ pVal = GetMapiProperty(lpMdb, 0x8001001e);
+ ReportStringProp(" Message prop 0x8001001e:", pVal);
+
+ // This key appears to be the OMI Account Manager account that corresponds
+ // to this message store. This is important for IMAP accounts
+ // since we may not want to import messages from an IMAP store!
+ // Seems silly if you ask me!
+ // In order to test this, we'll need the registry key to look under to determine
+ // if it contains the "IMAP Server" value, if it does then we are an
+ // IMAP store, if not, then we are a non-IMAP store - which may always mean
+ // a regular store that should be imported.
+
+ pVal = GetMapiProperty(lpMdb, 0x80000003);
+ ReportLongProp(" Message prop 0x80000003:", pVal);
+
+ // ListProperties(lpMdb);
+ */
+
+ pVal = GetMapiProperty(lpMdb, PR_IPM_SUBTREE_ENTRYID);
+ if (pVal) {
+ ULONG cbEntry;
+ LPENTRYID pEntry;
+ LPMAPIFOLDER lpSubTree = NULL;
+
+ if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
+ // Open up the folder!
+ ULONG ulObjType;
+ hr = lpMdb->OpenEntry(cbEntry, pEntry, NULL, 0, &ulObjType, (LPUNKNOWN *) &lpSubTree);
+ MAPIFreeBuffer(pEntry);
+ if (SUCCEEDED(hr) && lpSubTree) {
+ // Find out if there are any contents in the
+ // tree.
+ LPMAPITABLE lpTable;
+ hr = lpSubTree->GetHierarchyTable(0, &lpTable);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE2("GetStoreInfo: GetHierarchyTable failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ }
+ else {
+ ULONG rowCount;
+ hr = lpTable->GetRowCount(0, &rowCount);
+ lpTable->Release();
+ if (SUCCEEDED(hr) && pSzContents)
+ *pSzContents = (long) rowCount;
+ }
+
+ lpSubTree->Release();
+ }
+ }
+ }
+}
+
+
+void CMapiApi::ClearMessageStores(void)
+{
+ if (m_pStores) {
+ CMsgStore * pStore;
+ for (size_t i = 0; i < m_pStores->Length(); i++) {
+ pStore = m_pStores->ElementAt(i);
+ delete pStore;
+ }
+ m_pStores->Clear();
+ }
+}
+
+void CMapiApi::AddMessageStore(CMsgStore *pStore)
+{
+ if (m_pStores)
+ m_pStores->AppendElement(pStore);
+}
+
+CMsgStore * CMapiApi::FindMessageStore(ULONG cbEid, LPENTRYID lpEid)
+{
+ if (!m_lpSession) {
+ MAPI_TRACE0("FindMessageStore called before session is open\n");
+ m_lastError = E_UNEXPECTED;
+ return NULL;
+ }
+
+ ULONG result;
+ HRESULT hr;
+ CMsgStore * pStore;
+ for (size_t i = 0; i < m_pStores->Length(); i++) {
+ pStore = m_pStores->ElementAt(i);
+ hr = m_lpSession->CompareEntryIDs(cbEid, lpEid, pStore->GetCBEntryID(), pStore->GetLPEntryID(),
+ 0, &result);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE2("CompareEntryIDs failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ m_lastError = hr;
+ return NULL;
+ }
+ if (result) {
+ return pStore;
+ }
+ }
+
+ pStore = new CMsgStore(cbEid, lpEid);
+ AddMessageStore(pStore);
+ return pStore;
+}
+
+// --------------------------------------------------------------------
+// Utility stuff
+// --------------------------------------------------------------------
+
+LPSPropValue CMapiApi::GetMapiProperty(LPMAPIPROP pProp, ULONG tag)
+{
+ if (!pProp)
+ return NULL;
+
+ int sz = CbNewSPropTagArray(1);
+ SPropTagArray *pTag = (SPropTagArray *) new char[sz];
+ pTag->cValues = 1;
+ pTag->aulPropTag[0] = tag;
+ LPSPropValue lpProp = NULL;
+ ULONG cValues = 0;
+ HRESULT hr = pProp->GetProps(pTag, 0, &cValues, &lpProp);
+ delete [] pTag;
+ if (HR_FAILED(hr) || (cValues != 1)) {
+ if (lpProp)
+ MAPIFreeBuffer(lpProp);
+ return NULL;
+ }
+ else {
+ if (PROP_TYPE(lpProp->ulPropTag) == PT_ERROR) {
+ if (lpProp->Value.l == MAPI_E_NOT_FOUND) {
+ MAPIFreeBuffer(lpProp);
+ lpProp = NULL;
+ }
+ }
+ }
+
+ return lpProp;
+}
+
+BOOL CMapiApi::IsLargeProperty(LPSPropValue pVal)
+{
+ return ((PROP_TYPE(pVal->ulPropTag) == PT_ERROR) && (pVal->Value.l == E_OUTOFMEMORY));
+}
+
+// The output buffer (result) must be freed with operator delete[]
+BOOL CMapiApi::GetLargeProperty(LPMAPIPROP pProp, ULONG tag, void** result)
+{
+ LPSTREAM lpStream;
+ HRESULT hr = pProp->OpenProperty(tag, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpStream);
+ if (HR_FAILED(hr))
+ return FALSE;
+ STATSTG st;
+ BOOL bResult = TRUE;
+ hr = lpStream->Stat(&st, STATFLAG_NONAME);
+ if (HR_FAILED(hr))
+ bResult = FALSE;
+ else {
+ if (!st.cbSize.QuadPart)
+ st.cbSize.QuadPart = 1;
+ char *pVal = new char[ (int) st.cbSize.QuadPart + 2];
+ if (pVal) {
+ ULONG sz;
+ hr = lpStream->Read(pVal, (ULONG) st.cbSize.QuadPart, &sz);
+ if (HR_FAILED(hr)) {
+ bResult = FALSE;
+ delete[] pVal;
+ }
+ else {
+ // Just in case it's a UTF16 string
+ pVal[(int) st.cbSize.QuadPart] = pVal[(int) st.cbSize.QuadPart+1] = 0;
+ *result = pVal;
+ }
+ }
+ else
+ bResult = FALSE;
+ }
+
+ lpStream->Release();
+
+ return bResult;
+}
+
+BOOL CMapiApi::GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsCString& val)
+{
+ void* result;
+ if (!GetLargeProperty(pProp, tag, &result))
+ return FALSE;
+ if (PROP_TYPE(tag) == PT_UNICODE) // unicode string
+ LossyCopyUTF16toASCII(nsDependentString(static_cast<wchar_t*>(result)), val);
+ else // either PT_STRING8 or some other binary - use as is
+ val.Assign(static_cast<char*>(result));
+ delete[] result;
+ return TRUE;
+}
+
+BOOL CMapiApi::GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsString& val)
+{
+ void* result;
+ if (!GetLargeProperty(pProp, tag, &result))
+ return FALSE;
+ if (PROP_TYPE(tag) == PT_UNICODE) // We already get the unicode string
+ val.Assign(static_cast<wchar_t*>(result));
+ else // either PT_STRING8 or some other binary
+ CStrToUnicode(static_cast<char*>(result), val);
+ delete[] result;
+ return TRUE;
+}
+// If the value is a string, get it...
+BOOL CMapiApi::GetEntryIdFromProp(LPSPropValue pVal, ULONG& cbEntryId,
+ LPENTRYID& lpEntryId, BOOL delVal)
+{
+ if (!pVal)
+ return FALSE;
+
+ BOOL bResult = TRUE;
+ switch(PROP_TYPE(pVal->ulPropTag)) {
+ case PT_BINARY:
+ cbEntryId = pVal->Value.bin.cb;
+ MAPIAllocateBuffer(cbEntryId, (LPVOID *) &lpEntryId);
+ memcpy(lpEntryId, pVal->Value.bin.lpb, cbEntryId);
+ break;
+
+ default:
+ MAPI_TRACE0("EntryId not in BINARY prop value\n");
+ bResult = FALSE;
+ break;
+ }
+
+ if (pVal && delVal)
+ MAPIFreeBuffer(pVal);
+
+ return bResult;
+}
+
+BOOL CMapiApi::GetStringFromProp(LPSPropValue pVal, nsCString& val, BOOL delVal)
+{
+ BOOL bResult = TRUE;
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_STRING8))
+ val = pVal->Value.lpszA;
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE))
+ LossyCopyUTF16toASCII(nsDependentString(pVal->Value.lpszW), val);
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL))
+ val.Truncate();
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ val.Truncate();
+ bResult = FALSE;
+ }
+ else {
+ if (pVal) {
+ MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n", (int) PROP_TYPE(pVal->ulPropTag));
+ }
+ else {
+ MAPI_TRACE0("GetStringFromProp: invalid value, expecting string, got null pointer\n");
+ }
+ val.Truncate();
+ bResult = FALSE;
+ }
+ if (pVal && delVal)
+ MAPIFreeBuffer(pVal);
+
+ return bResult;
+}
+
+BOOL CMapiApi::GetStringFromProp(LPSPropValue pVal, nsString& val, BOOL delVal)
+{
+ BOOL bResult = TRUE;
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_STRING8)) {
+ CStrToUnicode((const char *)pVal->Value.lpszA, val);
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE)) {
+ val = (char16_t *) pVal->Value.lpszW;
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
+ val.Truncate();
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ val.Truncate();
+ bResult = FALSE;
+ }
+ else {
+ if (pVal) {
+ MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n", (int) PROP_TYPE(pVal->ulPropTag));
+ }
+ else {
+ MAPI_TRACE0("GetStringFromProp: invalid value, expecting string, got null pointer\n");
+ }
+ val.Truncate();
+ bResult = FALSE;
+ }
+ if (pVal && delVal)
+ MAPIFreeBuffer(pVal);
+
+ return bResult;
+}
+
+
+LONG CMapiApi::GetLongFromProp(LPSPropValue pVal, BOOL delVal)
+{
+ LONG val = 0;
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) {
+ val = pVal->Value.l;
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
+ val = 0;
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ val = 0;
+ MAPI_TRACE0("GetLongFromProp: Error retrieving property\n");
+ }
+ else {
+ MAPI_TRACE0("GetLongFromProp: invalid value, expecting long\n");
+ }
+ if (pVal && delVal)
+ MAPIFreeBuffer(pVal);
+
+ return val;
+}
+
+
+void CMapiApi::ReportUIDProp(const char *pTag, LPSPropValue pVal)
+{
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_BINARY)) {
+ if (pVal->Value.bin.cb != 16) {
+ MAPI_TRACE1("%s - INVALID, expecting 16 bytes of binary data for UID\n", pTag);
+ }
+ else {
+ nsIID uid;
+ memcpy(&uid, pVal->Value.bin.lpb, 16);
+ char * pStr = uid.ToString();
+ if (pStr) {
+ MAPI_TRACE2("%s %s\n", pTag, (const char *)pStr);
+ NS_Free(pStr);
+ }
+ }
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
+ MAPI_TRACE1("%s {NULL}\n", pTag);
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
+ }
+ else {
+ MAPI_TRACE1("%s invalid value, expecting binary\n", pTag);
+ }
+ if (pVal)
+ MAPIFreeBuffer(pVal);
+}
+
+void CMapiApi::ReportLongProp(const char *pTag, LPSPropValue pVal)
+{
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) {
+ nsCString num;
+ nsCString num2;
+
+ num.AppendInt((int32_t) pVal->Value.l);
+ num2.AppendInt((int32_t) pVal->Value.l, 16);
+ MAPI_TRACE3("%s %s, 0x%s\n", pTag, num, num2);
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
+ MAPI_TRACE1("%s {NULL}\n", pTag);
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
+ }
+ else {
+ MAPI_TRACE1("%s invalid value, expecting long\n", pTag);
+ }
+ if (pVal)
+ MAPIFreeBuffer(pVal);
+}
+
+void CMapiApi::ReportStringProp(const char *pTag, LPSPropValue pVal)
+{
+ if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_TSTRING)) {
+ nsCString val((LPCTSTR) (pVal->Value.LPSZ));
+ MAPI_TRACE2("%s %s\n", pTag, val.get());
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
+ MAPI_TRACE1("%s {NULL}\n", pTag);
+ }
+ else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
+ MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
+ }
+ else {
+ MAPI_TRACE1("%s invalid value, expecting string\n", pTag);
+ }
+ if (pVal)
+ MAPIFreeBuffer(pVal);
+}
+
+void CMapiApi::GetPropTagName(ULONG tag, nsCString& s)
+{
+ char numStr[256];
+ PR_snprintf(numStr, 256, "0x%lx, %ld", tag, tag);
+ s = numStr;
+ switch(tag) {
+#include "mapitagstrs.cpp"
+ }
+ s += ", data: ";
+ switch(PROP_TYPE(tag)) {
+ case PT_UNSPECIFIED: s += "PT_UNSPECIFIED"; break;
+ case PT_NULL: s += "PT_NULL"; break;
+ case PT_I2: s += "PT_I2"; break;
+ case PT_LONG: s += "PT_LONG"; break;
+ case PT_R4: s += "PT_R4"; break;
+ case PT_DOUBLE: s += "PT_DOUBLE"; break;
+ case PT_CURRENCY: s += "PT_CURRENCY"; break;
+ case PT_APPTIME: s += "PT_APPTIME"; break;
+ case PT_ERROR: s += "PT_ERROR"; break;
+ case PT_BOOLEAN: s += "PT_BOOLEAN"; break;
+ case PT_OBJECT: s += "PT_OBJECT"; break;
+ case PT_I8: s += "PT_I8"; break;
+ case PT_STRING8: s += "PT_STRING8"; break;
+ case PT_UNICODE: s += "PT_UNICODE"; break;
+ case PT_SYSTIME: s += "PT_SYSTIME"; break;
+ case PT_CLSID: s += "PT_CLSID"; break;
+ case PT_BINARY: s += "PT_BINARY"; break;
+ case PT_MV_I2: s += "PT_MV_I2"; break;
+ case PT_MV_LONG: s += "PT_MV_LONG"; break;
+ case PT_MV_R4: s += "PT_MV_R4"; break;
+ case PT_MV_DOUBLE: s += "PT_MV_DOUBLE"; break;
+ case PT_MV_CURRENCY: s += "PT_MV_CURRENCY"; break;
+ case PT_MV_APPTIME: s += "PT_MV_APPTIME"; break;
+ case PT_MV_SYSTIME: s += "PT_MV_SYSTIME"; break;
+ case PT_MV_STRING8: s += "PT_MV_STRING8"; break;
+ case PT_MV_BINARY: s += "PT_MV_BINARY"; break;
+ case PT_MV_UNICODE: s += "PT_MV_UNICODE"; break;
+ case PT_MV_CLSID: s += "PT_MV_CLSID"; break;
+ case PT_MV_I8: s += "PT_MV_I8"; break;
+ default:
+ s += "Unknown";
+ }
+}
+
+void CMapiApi::ListPropertyValue(LPSPropValue pVal, nsCString& s)
+{
+ nsCString strVal;
+ char nBuff[64];
+
+ s += "value: ";
+ switch (PROP_TYPE(pVal->ulPropTag)) {
+ case PT_STRING8:
+ GetStringFromProp(pVal, strVal, FALSE);
+ if (strVal.Length() > 60) {
+ strVal.SetLength(60);
+ strVal += "...";
+ }
+ MsgReplaceSubstring(strVal, "\r", "\\r");
+ MsgReplaceSubstring(strVal, "\n", "\\n");
+ s += strVal;
+ break;
+ case PT_LONG:
+ s.AppendInt((int32_t) pVal->Value.l);
+ s += ", 0x";
+ s.AppendInt((int32_t) pVal->Value.l, 16);
+ s += nBuff;
+ break;
+ case PT_BOOLEAN:
+ if (pVal->Value.b)
+ s += "True";
+ else
+ s += "False";
+ break;
+ case PT_NULL:
+ s += "--NULL--";
+ break;
+ case PT_SYSTIME: {
+ /*
+ COleDateTime tm(pVal->Value.ft);
+ s += tm.Format();
+ */
+ s += "-- Figure out how to format time in mozilla, PT_SYSTIME --";
+ }
+ break;
+ default:
+ s += "?";
+ }
+}
+
+
+
+// -------------------------------------------------------------------
+// Folder list stuff
+// -------------------------------------------------------------------
+CMapiFolderList::CMapiFolderList()
+{
+}
+
+CMapiFolderList::~CMapiFolderList()
+{
+ ClearAll();
+}
+
+void CMapiFolderList::AddItem(CMapiFolder *pFolder)
+{
+ EnsureUniqueName(pFolder);
+ GenerateFilePath(pFolder);
+ m_array.AppendElement(pFolder);
+}
+
+void CMapiFolderList::ChangeName(nsString& name)
+{
+ if (name.IsEmpty()) {
+ name.AssignLiteral("1");
+ return;
+ }
+ char16_t lastC = name.Last();
+ if ((lastC >= '0') && (lastC <= '9')) {
+ lastC++;
+ if (lastC > '9') {
+ lastC = '1';
+ name.SetCharAt(lastC, name.Length() - 1);
+ name.AppendLiteral("0");
+ }
+ else {
+ name.SetCharAt(lastC, name.Length() - 1);
+ }
+ }
+ else {
+ name.AppendLiteral(" 2");
+ }
+}
+
+void CMapiFolderList::EnsureUniqueName(CMapiFolder *pFolder)
+{
+ // For everybody in the array before me with the SAME
+ // depth, my name must be unique
+ CMapiFolder * pCurrent;
+ int i;
+ BOOL done;
+ nsString name;
+ nsString cName;
+
+ pFolder->GetDisplayName(name);
+ do {
+ done = TRUE;
+ i = m_array.Length() - 1;
+ while (i >= 0) {
+ pCurrent = GetAt(i);
+ if (pCurrent->GetDepth() == pFolder->GetDepth()) {
+ pCurrent->GetDisplayName(cName);
+ if (cName.Equals(name, nsCaseInsensitiveStringComparator())) {
+ ChangeName(name);
+ pFolder->SetDisplayName(name.get());
+ done = FALSE;
+ break;
+ }
+ }
+ else if (pCurrent->GetDepth() < pFolder->GetDepth())
+ break;
+ i--;
+ }
+ } while (!done);
+}
+
+void CMapiFolderList::GenerateFilePath(CMapiFolder *pFolder)
+{
+ // A file path, includes all of my parent's path, plus mine
+ nsString name;
+ nsString path;
+ if (!pFolder->GetDepth()) {
+ pFolder->GetDisplayName(name);
+ pFolder->SetFilePath(name.get());
+ return;
+ }
+
+ CMapiFolder * pCurrent;
+ int i = m_array.Length() - 1;
+ while (i >= 0) {
+ pCurrent = GetAt(i);
+ if (pCurrent->GetDepth() == (pFolder->GetDepth() - 1)) {
+ pCurrent->GetFilePath(path);
+ path.AppendLiteral(".sbd\\");
+ pFolder->GetDisplayName(name);
+ path += name;
+ pFolder->SetFilePath(path.get());
+ return;
+ }
+ i--;
+ }
+ pFolder->GetDisplayName(name);
+ pFolder->SetFilePath(name.get());
+}
+
+void CMapiFolderList::ClearAll(void)
+{
+ CMapiFolder *pFolder;
+ for (size_t i = 0; i < m_array.Length(); i++) {
+ pFolder = GetAt(i);
+ delete pFolder;
+ }
+ m_array.Clear();
+}
+
+void CMapiFolderList::DumpList(void)
+{
+ CMapiFolder *pFolder;
+ nsString str;
+ int depth;
+ char prefix[256];
+
+ MAPI_TRACE0("Folder List ---------------------------------\n");
+ for (size_t i = 0; i < m_array.Length(); i++) {
+ pFolder = GetAt(i);
+ depth = pFolder->GetDepth();
+ pFolder->GetDisplayName(str);
+ depth *= 2;
+ if (depth > 255)
+ depth = 255;
+ memset(prefix, ' ', depth);
+ prefix[depth] = 0;
+#ifdef MAPI_DEBUG
+ char *ansiStr = ToNewCString(str);
+ MAPI_TRACE2("%s%s: ", prefix, ansiStr);
+ NS_Free(ansiStr);
+#endif
+ pFolder->GetFilePath(str);
+#ifdef MAPI_DEBUG
+ ansiStr = ToNewCString(str);
+ MAPI_TRACE2("depth=%d, filePath=%s\n", pFolder->GetDepth(), ansiStr);
+ NS_Free(ansiStr);
+#endif
+ }
+ MAPI_TRACE0("---------------------------------------------\n");
+}
+
+
+CMapiFolder::CMapiFolder()
+{
+ m_objectType = MAPI_FOLDER;
+ m_cbEid = 0;
+ m_lpEid = NULL;
+ m_depth = 0;
+ m_doImport = TRUE;
+}
+
+CMapiFolder::CMapiFolder(const char16_t *pDisplayName, ULONG cbEid, LPENTRYID lpEid, int depth, LONG oType)
+{
+ m_cbEid = 0;
+ m_lpEid = NULL;
+ SetDisplayName(pDisplayName);
+ SetEntryID(cbEid, lpEid);
+ SetDepth(depth);
+ SetObjectType(oType);
+ SetDoImport(TRUE);
+}
+
+CMapiFolder::CMapiFolder(const CMapiFolder *pCopyFrom)
+{
+ m_lpEid = NULL;
+ m_cbEid = 0;
+ SetDoImport(pCopyFrom->GetDoImport());
+ SetDisplayName(pCopyFrom->m_displayName.get());
+ SetObjectType(pCopyFrom->GetObjectType());
+ SetEntryID(pCopyFrom->GetCBEntryID(), pCopyFrom->GetEntryID());
+ SetDepth(pCopyFrom->GetDepth());
+ SetFilePath(pCopyFrom->m_mailFilePath.get());
+}
+
+CMapiFolder::~CMapiFolder()
+{
+ if (m_lpEid)
+ delete m_lpEid;
+}
+
+void CMapiFolder::SetEntryID(ULONG cbEid, LPENTRYID lpEid)
+{
+ if (m_lpEid)
+ delete m_lpEid;
+ m_lpEid = NULL;
+ m_cbEid = cbEid;
+ if (cbEid) {
+ m_lpEid = new BYTE[cbEid];
+ memcpy(m_lpEid, lpEid, cbEid);
+ }
+}
+
+// ---------------------------------------------------------------------
+// Message store stuff
+// ---------------------------------------------------------------------
+
+
+CMsgStore::CMsgStore(ULONG cbEid, LPENTRYID lpEid)
+{
+ m_lpEid = NULL;
+ m_lpMdb = NULL;
+ SetEntryID(cbEid, lpEid);
+}
+
+CMsgStore::~CMsgStore()
+{
+ if (m_lpEid)
+ delete m_lpEid;
+
+ if (m_lpMdb) {
+ ULONG flags = LOGOFF_NO_WAIT;
+ HRESULT hr = m_lpMdb->StoreLogoff(&flags);
+ m_lpMdb->Release();
+ m_lpMdb = NULL;
+ }
+}
+
+void CMsgStore::SetEntryID(ULONG cbEid, LPENTRYID lpEid)
+{
+ HRESULT hr;
+
+ if (m_lpEid)
+ delete m_lpEid;
+
+ m_lpEid = NULL;
+ if (cbEid) {
+ m_lpEid = new BYTE[cbEid];
+ memcpy(m_lpEid, lpEid, cbEid);
+ }
+ m_cbEid = cbEid;
+
+ if (m_lpMdb) {
+ ULONG flags = LOGOFF_NO_WAIT;
+ hr = m_lpMdb->StoreLogoff(&flags);
+ m_lpMdb->Release();
+ m_lpMdb = NULL;
+ }
+}
+
+BOOL CMsgStore::Open(LPMAPISESSION pSession, LPMDB *ppMdb)
+{
+ if (m_lpMdb) {
+ if (ppMdb)
+ *ppMdb = m_lpMdb;
+ return TRUE;
+ }
+
+ BOOL bResult = TRUE;
+ HRESULT hr = pSession->OpenMsgStore(NULL, m_cbEid, (LPENTRYID)m_lpEid, NULL, MDB_NO_MAIL, &m_lpMdb); // MDB pointer
+ if (HR_FAILED(hr)) {
+ m_lpMdb = NULL;
+ MAPI_TRACE2("OpenMsgStore failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ bResult = FALSE;
+ }
+
+ if (ppMdb)
+ *ppMdb = m_lpMdb;
+ return bResult;
+}
+
+
+
+// ------------------------------------------------------------
+// Contents Iterator
+// -----------------------------------------------------------
+
+
+CMapiFolderContents::CMapiFolderContents(LPMDB lpMdb, ULONG cbEid, LPENTRYID lpEid)
+{
+ m_lpMdb = lpMdb;
+ m_fCbEid = cbEid;
+ m_fLpEid = new BYTE[cbEid];
+ memcpy(m_fLpEid, lpEid, cbEid);
+ m_count = 0;
+ m_iterCount = 0;
+ m_failure = FALSE;
+ m_lastError = 0;
+ m_lpFolder = NULL;
+ m_lpTable = NULL;
+ m_lastLpEid = NULL;
+ m_lastCbEid = 0;
+}
+
+CMapiFolderContents::~CMapiFolderContents()
+{
+ if (m_lastLpEid)
+ delete m_lastLpEid;
+ delete m_fLpEid;
+ if (m_lpTable)
+ m_lpTable->Release();
+ if (m_lpFolder)
+ m_lpFolder->Release();
+}
+
+
+BOOL CMapiFolderContents::SetUpIter(void)
+{
+ // First, open up the MAPIFOLDER object
+ ULONG ulObjType;
+ HRESULT hr;
+ hr = m_lpMdb->OpenEntry(m_fCbEid, (LPENTRYID) m_fLpEid, NULL, 0, &ulObjType, (LPUNKNOWN *) &m_lpFolder);
+
+ if (FAILED(hr) || !m_lpFolder) {
+ m_lpFolder = NULL;
+ m_lastError = hr;
+ MAPI_TRACE2("CMapiFolderContents OpenEntry failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ if (ulObjType != MAPI_FOLDER) {
+ m_lastError = E_UNEXPECTED;
+ MAPI_TRACE0("CMapiFolderContents - bad object type, not a folder.\n");
+ return FALSE;
+ }
+
+
+ hr = m_lpFolder->GetContentsTable(0, &m_lpTable);
+ if (FAILED(hr) || !m_lpTable) {
+ m_lastError = hr;
+ m_lpTable = NULL;
+ MAPI_TRACE2("CMapiFolderContents - GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ hr = m_lpTable->GetRowCount(0, &m_count);
+ if (FAILED(hr)) {
+ m_lastError = hr;
+ MAPI_TRACE0("CMapiFolderContents - GetRowCount failed\n");
+ return FALSE;
+ }
+
+ hr = m_lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
+ if (FAILED(hr)) {
+ m_lastError = hr;
+ MAPI_TRACE2("CMapiFolderContents - SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ hr = m_lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (FAILED(hr)) {
+ m_lastError = hr;
+ MAPI_TRACE2("CMapiFolderContents - SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL CMapiFolderContents::GetNext(ULONG *pcbEid, LPENTRYID *ppEid, ULONG *poType, BOOL *pDone)
+{
+ *pDone = FALSE;
+ if (m_failure)
+ return FALSE;
+ if (!m_lpFolder) {
+ if (!SetUpIter()) {
+ m_failure = TRUE;
+ return FALSE;
+ }
+ if (!m_count) {
+ *pDone = TRUE;
+ return TRUE;
+ }
+ }
+
+ int cNumRows = 0;
+ LPSRowSet lpRow = NULL;
+ HRESULT hr = m_lpTable->QueryRows(1, 0, &lpRow);
+
+ if(HR_FAILED(hr)) {
+ m_lastError = hr;
+ m_failure = TRUE;
+ MAPI_TRACE2("CMapiFolderContents - QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ return FALSE;
+ }
+
+ if(lpRow) {
+ cNumRows = lpRow->cRows;
+ if (cNumRows) {
+ LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
+ ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
+ ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
+
+ if (m_lastCbEid != cbEID) {
+ if (m_lastLpEid)
+ delete m_lastLpEid;
+ m_lastLpEid = new BYTE[cbEID];
+ m_lastCbEid = cbEID;
+ }
+ memcpy(m_lastLpEid, lpEID, cbEID);
+
+ *ppEid = (LPENTRYID) m_lastLpEid;
+ *pcbEid = cbEID;
+ *poType = oType;
+ }
+ else
+ *pDone = TRUE;
+ CMapiApi::FreeProws(lpRow);
+ }
+ else
+ *pDone = TRUE;
+
+ return TRUE;
+}
+
diff --git a/mailnews/import/outlook/src/MapiApi.h b/mailnews/import/outlook/src/MapiApi.h
new file mode 100644
index 000000000..8b59b80d8
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiApi.h
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef MapiApi_h___
+#define MapiApi_h___
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+#include <stdio.h>
+
+#include <windows.h>
+#include <mapi.h>
+#include <mapix.h>
+#include <mapidefs.h>
+#include <mapicode.h>
+#include <mapitags.h>
+#include <mapiutil.h>
+// wabutil.h expects mapiutil to define _MAPIUTIL_H but it actually
+// defines _MAPIUTIL_H_
+#define _MAPIUTIL_H
+
+#ifndef PR_INTERNET_CPID
+#define PR_INTERNET_CPID (PROP_TAG(PT_LONG,0x3FDE))
+#endif
+#ifndef MAPI_NATIVE_BODY
+#define MAPI_NATIVE_BODY (0x00010000)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_RTF
+#define MAPI_NATIVE_BODY_TYPE_RTF (0x00000001)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_HTML
+#define MAPI_NATIVE_BODY_TYPE_HTML (0x00000002)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_PLAINTEXT
+#define MAPI_NATIVE_BODY_TYPE_PLAINTEXT (0x00000004)
+#endif
+#ifndef PR_BODY_HTML_A
+#define PR_BODY_HTML_A (PROP_TAG(PT_STRING8,0x1013))
+#endif
+#ifndef PR_BODY_HTML_W
+#define PR_BODY_HTML_W (PROP_TAG(PT_UNICODE,0x1013))
+#endif
+#ifndef PR_BODY_HTML
+#define PR_BODY_HTML (PROP_TAG(PT_TSTRING,0x1013))
+#endif
+
+class CMapiFolderList;
+class CMsgStore;
+class CMapiFolder;
+
+class CMapiContentIter {
+public:
+ virtual BOOL HandleContentItem(ULONG oType, ULONG cb, LPENTRYID pEntry) = 0;
+};
+
+class CMapiHierarchyIter {
+public:
+ virtual BOOL HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry) = 0;
+};
+
+class CMapiApi {
+public:
+ CMapiApi();
+ ~CMapiApi();
+
+ static BOOL LoadMapi(void);
+ static BOOL LoadMapiEntryPoints(void);
+ static void UnloadMapi(void);
+
+ static HINSTANCE m_hMapi32;
+
+ static void MAPIUninitialize(void);
+ static HRESULT MAPIInitialize(LPVOID lpInit);
+ static SCODE MAPIAllocateBuffer(ULONG cbSize, LPVOID FAR * lppBuffer);
+ static ULONG MAPIFreeBuffer(LPVOID lpBuff);
+ static HRESULT MAPILogonEx(ULONG ulUIParam, LPTSTR lpszProfileName, LPTSTR lpszPassword, FLAGS flFlags, LPMAPISESSION FAR * lppSession);
+ static HRESULT OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer, LPFREEBUFFER lpFreeBuffer, ULONG ulFlags, LPTSTR lpszFileName, LPTSTR lpszPrefix, LPSTREAM FAR * lppStream);
+ static void FreeProws(LPSRowSet prows);
+
+
+ BOOL Initialize(void);
+ BOOL LogOn(void);
+
+ void AddMessageStore(CMsgStore *pStore);
+ void SetCurrentMsgStore(LPMDB lpMdb) { m_lpMdb = lpMdb;}
+
+ // Open any given entry from the current Message Store
+ BOOL OpenEntry(ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen);
+ static BOOL OpenMdbEntry(LPMDB lpMdb, ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen);
+
+ // Fill in the folders list with the hierarchy from the given
+ // message store.
+ BOOL GetStoreFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders, int startDepth);
+ BOOL GetStoreAddressFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders);
+ BOOL OpenStore(ULONG cbEid, LPENTRYID lpEid, LPMDB *ppMdb);
+
+ // Iteration
+ BOOL IterateStores(CMapiFolderList& list);
+ BOOL IterateContents(CMapiContentIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
+ BOOL IterateHierarchy(CMapiHierarchyIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
+
+ // Properties
+ static LPSPropValue GetMapiProperty(LPMAPIPROP pProp, ULONG tag);
+ // If delVal is true, functions will call CMapiApi::MAPIFreeBuffer on pVal.
+ static BOOL GetEntryIdFromProp(LPSPropValue pVal, ULONG& cbEntryId,
+ LPENTRYID& lpEntryId, BOOL delVal = TRUE);
+ static BOOL GetStringFromProp(LPSPropValue pVal, nsCString& val, BOOL delVal = TRUE);
+ static BOOL GetStringFromProp(LPSPropValue pVal, nsString& val, BOOL delVal = TRUE);
+ static LONG GetLongFromProp(LPSPropValue pVal, BOOL delVal = TRUE);
+ static BOOL GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsCString& val);
+ static BOOL GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsString& val);
+ static BOOL IsLargeProperty(LPSPropValue pVal);
+ static ULONG GetEmailPropertyTag(LPMAPIPROP lpProp, LONG nameID);
+
+ static BOOL GetRTFPropertyDecodedAsUTF16(LPMAPIPROP pProp, nsString& val,
+ unsigned long& nativeBodyType,
+ unsigned long codepage = 0);
+
+ // Debugging & reporting stuff
+ static void ListProperties(LPMAPIPROP lpProp, BOOL getValues = TRUE);
+ static void ListPropertyValue(LPSPropValue pVal, nsCString& s);
+
+protected:
+ BOOL HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry);
+ BOOL HandleContentsItem(ULONG oType, ULONG cb, LPENTRYID pEntry);
+ void GetStoreInfo(CMapiFolder *pFolder, long *pSzContents);
+
+ // array of available message stores, cached so that
+ // message stores are only opened once, preventing multiple
+ // logon's by the user if the store requires a logon.
+ CMsgStore * FindMessageStore(ULONG cbEid, LPENTRYID lpEid);
+ void ClearMessageStores(void);
+
+ static void CStrToUnicode(const char *pStr, nsString& result);
+
+ // Debugging & reporting stuff
+ static void GetPropTagName(ULONG tag, nsCString& s);
+ static void ReportStringProp(const char *pTag, LPSPropValue pVal);
+ static void ReportUIDProp(const char *pTag, LPSPropValue pVal);
+ static void ReportLongProp(const char *pTag, LPSPropValue pVal);
+
+
+private:
+ static int m_clients;
+ static BOOL m_initialized;
+ static nsTArray<CMsgStore*> * m_pStores;
+ static LPMAPISESSION m_lpSession;
+ static LPMDB m_lpMdb;
+ static HRESULT m_lastError;
+ static char16_t * m_pUniBuff;
+ static int m_uniBuffLen;
+
+ static BOOL GetLargeProperty(LPMAPIPROP pProp, ULONG tag, void** result);
+};
+
+class CMapiFolder {
+public:
+ CMapiFolder();
+ CMapiFolder(const CMapiFolder *pCopyFrom);
+ CMapiFolder(const char16_t *pDisplayName, ULONG cbEid, LPENTRYID lpEid, int depth, LONG oType = MAPI_FOLDER);
+ ~CMapiFolder();
+
+ void SetDoImport(BOOL doIt) { m_doImport = doIt;}
+ void SetObjectType(long oType) { m_objectType = oType;}
+ void SetDisplayName(const char16_t *pDisplayName) { m_displayName = pDisplayName;}
+ void SetEntryID(ULONG cbEid, LPENTRYID lpEid);
+ void SetDepth(int depth) { m_depth = depth;}
+ void SetFilePath(const char16_t *pFilePath) { m_mailFilePath = pFilePath;}
+
+ BOOL GetDoImport(void) const { return m_doImport;}
+ LONG GetObjectType(void) const { return m_objectType;}
+ void GetDisplayName(nsString& name) const { name = m_displayName;}
+ void GetFilePath(nsString& path) const { path = m_mailFilePath;}
+ BOOL IsStore(void) const { return m_objectType == MAPI_STORE;}
+ BOOL IsFolder(void) const { return m_objectType == MAPI_FOLDER;}
+ int GetDepth(void) const { return m_depth;}
+
+ LPENTRYID GetEntryID(ULONG *pCb = NULL) const { if (pCb) *pCb = m_cbEid; return (LPENTRYID) m_lpEid;}
+ ULONG GetCBEntryID(void) const { return m_cbEid;}
+
+private:
+ LONG m_objectType;
+ ULONG m_cbEid;
+ BYTE * m_lpEid;
+ nsString m_displayName;
+ int m_depth;
+ nsString m_mailFilePath;
+ BOOL m_doImport;
+
+};
+
+class CMapiFolderList {
+public:
+ CMapiFolderList();
+ ~CMapiFolderList();
+
+ void AddItem(CMapiFolder *pFolder);
+ CMapiFolder * GetItem(int index) { if ((index >= 0) && (index < (int)m_array.Length())) return GetAt(index); else return NULL;}
+ void ClearAll(void);
+
+ // Debugging and reporting
+ void DumpList(void);
+
+ CMapiFolder * GetAt(int index) { return m_array.ElementAt(index);}
+ int GetSize(void) { return m_array.Length();}
+
+protected:
+ void EnsureUniqueName(CMapiFolder *pFolder);
+ void GenerateFilePath(CMapiFolder *pFolder);
+ void ChangeName(nsString& name);
+
+private:
+ nsTArray<CMapiFolder*> m_array;
+};
+
+
+class CMsgStore {
+public:
+ CMsgStore(ULONG cbEid = 0, LPENTRYID lpEid = NULL);
+ ~CMsgStore();
+
+ void SetEntryID(ULONG cbEid, LPENTRYID lpEid);
+ BOOL Open(LPMAPISESSION pSession, LPMDB *ppMdb);
+
+ ULONG GetCBEntryID(void) { return m_cbEid;}
+ LPENTRYID GetLPEntryID(void) { return (LPENTRYID) m_lpEid;}
+
+private:
+ ULONG m_cbEid;
+ BYTE * m_lpEid;
+ LPMDB m_lpMdb;
+};
+
+
+class CMapiFolderContents {
+public:
+ CMapiFolderContents(LPMDB lpMdb, ULONG cbEID, LPENTRYID lpEid);
+ ~CMapiFolderContents();
+
+ BOOL GetNext(ULONG *pcbEid, LPENTRYID *ppEid, ULONG *poType, BOOL *pDone);
+
+ ULONG GetCount(void) { return m_count;}
+
+protected:
+ BOOL SetUpIter(void);
+
+private:
+ HRESULT m_lastError;
+ BOOL m_failure;
+ LPMDB m_lpMdb;
+ LPMAPIFOLDER m_lpFolder;
+ LPMAPITABLE m_lpTable;
+ ULONG m_fCbEid;
+ BYTE * m_fLpEid;
+ ULONG m_count;
+ ULONG m_iterCount;
+ BYTE * m_lastLpEid;
+ ULONG m_lastCbEid;
+};
+
+
+#endif /* MapiApi_h__ */
diff --git a/mailnews/import/outlook/src/MapiDbgLog.h b/mailnews/import/outlook/src/MapiDbgLog.h
new file mode 100644
index 000000000..668418c42
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiDbgLog.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MapiDbgLog_h___
+#define MapiDbgLog_h___
+
+/*
+#ifdef NS_DEBUG
+#define MAPI_DEBUG 1
+#endif
+*/
+
+#ifdef MAPI_DEBUG
+#include <stdio.h>
+
+#define MAPI_DUMP_STRING(x) printf("%s", (const char *)x)
+#define MAPI_TRACE0(x) printf(x)
+#define MAPI_TRACE1(x, y) printf(x, y)
+#define MAPI_TRACE2(x, y, z) printf(x, y, z)
+#define MAPI_TRACE3(x, y, z, a) printf(x, y, z, a)
+#define MAPI_TRACE4(x, y, z, a, b) printf(x, y, z, a, b)
+
+
+#else
+
+#define MAPI_DUMP_STRING(x)
+#define MAPI_TRACE0(x)
+#define MAPI_TRACE1(x, y)
+#define MAPI_TRACE2(x, y, z)
+#define MAPI_TRACE3(x, y, z, a)
+#define MAPI_TRACE4(x, y, z, a, b)
+
+#endif
+
+
+
+#endif /* MapiDbgLog_h___ */
+
diff --git a/mailnews/import/outlook/src/MapiMessage.cpp b/mailnews/import/outlook/src/MapiMessage.cpp
new file mode 100644
index 000000000..6b02db314
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiMessage.cpp
@@ -0,0 +1,1473 @@
+/* -*- 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/. */
+
+#ifndef INITGUID
+#define INITGUID
+#endif
+
+#ifndef USES_IID_IMessage
+#define USES_IID_IMessage
+#endif
+
+#include "nscore.h"
+#include <time.h>
+#include "nsStringGlue.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsMsgUtils.h"
+#include "nsMimeTypes.h"
+#include "nsIOutputStream.h"
+
+#include "nsMsgCompCID.h"
+#include "nsIMutableArray.h"
+#include "MapiDbgLog.h"
+#include "MapiApi.h"
+
+#include "MapiMimeTypes.h"
+
+#include "nsMsgI18N.h"
+#include "nsICharsetConverterManager.h"
+#include "nsCRT.h"
+#include "nsNetUtil.h"
+#include "MapiMessage.h"
+
+#include "nsOutlookMail.h"
+
+// needed for the call the OpenStreamOnFile
+extern LPMAPIALLOCATEBUFFER gpMapiAllocateBuffer;
+extern LPMAPIFREEBUFFER gpMapiFreeBuffer;
+
+// Sample From line: From - 1 Jan 1965 00:00:00
+
+typedef const char * PC_S8;
+
+static const char * kWhitespace = "\b\t\r\n ";
+static const char * sFromLine = "From - ";
+static const char * sFromDate = "Mon Jan 1 00:00:00 1965";
+static const char * sDaysOfWeek[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const char *sMonths[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+CMapiMessage::CMapiMessage(LPMESSAGE lpMsg)
+ : m_lpMsg(lpMsg), m_dldStateHeadersOnly(false), m_msgFlags(0)
+{
+ nsresult rv;
+ m_pIOService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return;
+
+ FetchHeaders();
+ if (ValidState()) {
+ BuildFromLine();
+ FetchFlags();
+ GetDownloadState();
+ if (FullMessageDownloaded()) {
+ FetchBody();
+ ProcessAttachments();
+ }
+ }
+}
+
+CMapiMessage::~CMapiMessage()
+{
+ ClearAttachments();
+ if (m_lpMsg)
+ m_lpMsg->Release();
+}
+
+void CMapiMessage::FormatDateTime(SYSTEMTIME& tm, nsCString& s, bool includeTZ)
+{
+ long offset = _timezone;
+ s += sDaysOfWeek[tm.wDayOfWeek];
+ s += ", ";
+ s.AppendInt((int32_t) tm.wDay);
+ s += " ";
+ s += sMonths[tm.wMonth - 1];
+ s += " ";
+ s.AppendInt((int32_t) tm.wYear);
+ s += " ";
+ int val = tm.wHour;
+ if (val < 10)
+ s += "0";
+ s.AppendInt((int32_t) val);
+ s += ":";
+ val = tm.wMinute;
+ if (val < 10)
+ s += "0";
+ s.AppendInt((int32_t) val);
+ s += ":";
+ val = tm.wSecond;
+ if (val < 10)
+ s += "0";
+ s.AppendInt((int32_t) val);
+ if (includeTZ) {
+ s += " ";
+ if (offset < 0) {
+ offset *= -1;
+ s += "+";
+ }
+ else
+ s += "-";
+ offset /= 60;
+ val = (int) (offset / 60);
+ if (val < 10)
+ s += "0";
+ s.AppendInt((int32_t) val);
+ val = (int) (offset % 60);
+ if (val < 10)
+ s += "0";
+ s.AppendInt((int32_t) val);
+ }
+}
+
+bool CMapiMessage::EnsureHeader(CMapiMessageHeaders::SpecialHeader special,
+ ULONG mapiTag)
+{
+ if (m_headers.Value(special))
+ return true;
+
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, mapiTag);
+ bool success = false;
+ if (pVal) {
+ if (PROP_TYPE(pVal->ulPropTag) == PT_STRING8) {
+ if (pVal->Value.lpszA && strlen(pVal->Value.lpszA)) {
+ m_headers.SetValue(special, pVal->Value.lpszA);
+ success = true;
+ }
+ }
+ else if (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) {
+ if (pVal->Value.lpszW && wcslen(pVal->Value.lpszW)) {
+ m_headers.SetValue(special, NS_ConvertUTF16toUTF8(pVal->Value.lpszW).get());
+ success = true;
+ }
+ }
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+
+ return success;
+}
+
+bool CMapiMessage::EnsureDate()
+{
+ if (m_headers.Value(CMapiMessageHeaders::hdrDate))
+ return true;
+
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_MESSAGE_DELIVERY_TIME);
+ if (!pVal)
+ pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_CREATION_TIME);
+ if (pVal) {
+ SYSTEMTIME st;
+ // the following call returns UTC
+ ::FileTimeToSystemTime(&(pVal->Value.ft), &st);
+ CMapiApi::MAPIFreeBuffer(pVal);
+ // FormatDateTime would append the local time zone, so don't use it.
+ // Instead, we just append +0000 for GMT/UTC here.
+ nsCString str;
+ FormatDateTime(st, str, false);
+ str += " +0000";
+ m_headers.SetValue(CMapiMessageHeaders::hdrDate, str.get());
+ return true;
+ }
+
+ return false;
+}
+
+void CMapiMessage::BuildFromLine(void)
+{
+ m_fromLine = sFromLine;
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_CREATION_TIME);
+ if (pVal) {
+ SYSTEMTIME st;
+ ::FileTimeToSystemTime(&(pVal->Value.ft), &st);
+ CMapiApi::MAPIFreeBuffer(pVal);
+ FormatDateTime(st, m_fromLine, FALSE);
+ }
+ else
+ m_fromLine += sFromDate;
+
+ m_fromLine += "\x0D\x0A";
+}
+
+#ifndef dispidHeaderItem
+#define dispidHeaderItem 0x8578
+#endif
+DEFINE_OLEGUID(PSETID_Common, MAKELONG(0x2000+(8),0x0006),0,0);
+
+void CMapiMessage::GetDownloadState()
+{
+ // See http://support.microsoft.com/kb/912239
+ HRESULT hRes = S_OK;
+ ULONG ulVal = 0;
+ LPSPropValue lpPropVal = NULL;
+ LPSPropTagArray lpNamedPropTag = NULL;
+ MAPINAMEID NamedID = {0};
+ LPMAPINAMEID lpNamedID = NULL;
+
+ NamedID.lpguid = (LPGUID) &PSETID_Common;
+ NamedID.ulKind = MNID_ID;
+ NamedID.Kind.lID = dispidHeaderItem;
+ lpNamedID = &NamedID;
+
+ hRes = m_lpMsg->GetIDsFromNames(1, &lpNamedID, NULL, &lpNamedPropTag);
+
+ if (lpNamedPropTag && 1 == lpNamedPropTag->cValues)
+ {
+ lpNamedPropTag->aulPropTag[0] = CHANGE_PROP_TYPE(lpNamedPropTag->aulPropTag[0], PT_LONG);
+
+ //Get the value of the property.
+ hRes = m_lpMsg->GetProps(lpNamedPropTag, 0, &ulVal, &lpPropVal);
+ if (lpPropVal && 1 == ulVal && PT_LONG == PROP_TYPE(lpPropVal->ulPropTag) &&
+ lpPropVal->Value.ul)
+ m_dldStateHeadersOnly = true;
+ }
+
+ CMapiApi::MAPIFreeBuffer(lpPropVal);
+ CMapiApi::MAPIFreeBuffer(lpNamedPropTag);
+}
+
+// Headers - fetch will get PR_TRANSPORT_MESSAGE_HEADERS
+// or if they do not exist will build a header from
+// PR_DISPLAY_TO, _CC, _BCC
+// PR_SUBJECT
+// PR_MESSAGE_RECIPIENTS
+// and PR_CREATION_TIME if needed?
+bool CMapiMessage::FetchHeaders(void)
+{
+ ULONG tag = PR_TRANSPORT_MESSAGE_HEADERS_A;
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, tag);
+ if (!pVal)
+ pVal = CMapiApi::GetMapiProperty(m_lpMsg, tag = PR_TRANSPORT_MESSAGE_HEADERS_W);
+ if (pVal) {
+ if (CMapiApi::IsLargeProperty(pVal)) {
+ nsCString headers;
+ CMapiApi::GetLargeStringProperty(m_lpMsg, tag, headers);
+ m_headers.Assign(headers.get());
+ }
+ else if ((PROP_TYPE(pVal->ulPropTag) == PT_STRING8) &&
+ (pVal->Value.lpszA) && (*(pVal->Value.lpszA)))
+ m_headers.Assign(pVal->Value.lpszA);
+ else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+ (pVal->Value.lpszW) && (*(pVal->Value.lpszW))) {
+ nsCString headers;
+ LossyCopyUTF16toASCII(nsDependentString(pVal->Value.lpszW), headers);
+ m_headers.Assign(headers.get());
+ }
+
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+
+ EnsureDate();
+ if (!EnsureHeader(CMapiMessageHeaders::hdrFrom, PR_SENDER_NAME_W))
+ EnsureHeader(CMapiMessageHeaders::hdrFrom, PR_SENDER_EMAIL_ADDRESS_W);
+ EnsureHeader(CMapiMessageHeaders::hdrSubject, PR_SUBJECT_W);
+ EnsureHeader(CMapiMessageHeaders::hdrTo, PR_DISPLAY_TO_W);
+ EnsureHeader(CMapiMessageHeaders::hdrCc, PR_DISPLAY_CC_W);
+ EnsureHeader(CMapiMessageHeaders::hdrBcc, PR_DISPLAY_BCC_W);
+
+ ProcessContentType();
+
+ return !m_headers.IsEmpty();
+}
+
+// Mime-Version: 1.0
+// Content-Type: text/plain; charset="US-ASCII"
+// Content-Type: multipart/mixed; boundary="=====================_874475278==_"
+
+void CMapiMessage::ProcessContentType()
+{
+ m_mimeContentType.Truncate();
+ m_mimeBoundary.Truncate();
+ m_mimeCharset.Truncate();
+
+ const char* contentType = m_headers.Value(CMapiMessageHeaders::hdrContentType);
+ if (!contentType)
+ return;
+
+ const char *begin = contentType, *end;
+ nsCString tStr;
+
+ // Note: this isn't a complete parser, the content type
+ // we extract could have rfc822 comments in it
+ while (*begin && IsSpace(*begin))
+ begin++;
+ if (!(*begin))
+ return;
+ end = begin;
+ while (*end && (*end != ';'))
+ end++;
+ m_mimeContentType.Assign(begin, end-begin);
+ if (!(*end))
+ return;
+ // look for "boundary="
+ begin = end + 1;
+ bool haveB;
+ bool haveC;
+ while (*begin) {
+ haveB = false;
+ haveC = false;
+ while (*begin && IsSpace(*begin))
+ begin++;
+ if (!(*begin))
+ return;
+ end = begin;
+ while (*end && (*end != '='))
+ end++;
+ if (end - begin) {
+ tStr.Assign(begin, end-begin);
+ if (tStr.LowerCaseEqualsLiteral("boundary"))
+ haveB = true;
+ else if (tStr.LowerCaseEqualsLiteral("charset"))
+ haveC = true;
+ }
+ if (!(*end))
+ return;
+ begin = end+1;
+ while (*begin && IsSpace(*begin))
+ begin++;
+ if (*begin == '"') {
+ begin++;
+ bool slash = false;
+ tStr.Truncate();
+ while (*begin) {
+ if (slash) {
+ slash = false;
+ tStr.Append(*begin);
+ }
+ else if (*begin == '"')
+ break;
+ else if (*begin != '\\')
+ tStr.Append(*begin);
+ else
+ slash = true;
+ begin++;
+ }
+ if (haveB) {
+ m_mimeBoundary = tStr;
+ haveB = false;
+ }
+ if (haveC) {
+ m_mimeCharset = tStr;
+ haveC = false;
+ }
+ if (!(*begin))
+ return;
+ begin++;
+ }
+ tStr.Truncate();
+ while (*begin && (*begin != ';')) {
+ tStr.Append(*(begin++));
+ }
+ if (haveB) {
+ tStr.Trim(kWhitespace);
+ m_mimeBoundary = tStr;
+ }
+ if (haveC) {
+ tStr.Trim(kWhitespace);
+ m_mimeCharset = tStr;
+ }
+ if (*begin)
+ begin++;
+ }
+}
+
+const char* CpToCharset(unsigned int cp)
+{
+ struct CODEPAGE_TO_CHARSET {
+ unsigned long cp;
+ const char* charset;
+ };
+
+ // This table is based on http://msdn.microsoft.com/en-us/library/dd317756(v=VS.85).aspx#1;
+ // Please extend as appropriate. The codepage values are sorted ascending.
+ static const CODEPAGE_TO_CHARSET cptocharset[] =
+ {
+ {37, "IBM037"}, // IBM EBCDIC US-Canada
+ {437, "IBM437"}, //OEM United States
+ {500, "IBM500"}, //IBM EBCDIC International
+ {708, "ASMO-708"}, //Arabic (ASMO 708)
+ //709 Arabic (ASMO-449+, BCON V4)
+ //710 Arabic - Transparent Arabic
+ {720, "DOS-720"}, //Arabic (Transparent ASMO); Arabic (DOS)
+ {737, "ibm737"}, // OEM Greek (formerly 437G); Greek (DOS)
+ {775, "ibm775"}, // OEM Baltic; Baltic (DOS)
+ {850, "ibm850"}, // OEM Multilingual Latin 1; Western European (DOS)
+ {852, "ibm852"}, // OEM Latin 2; Central European (DOS)
+ {855, "IBM855"}, // OEM Cyrillic (primarily Russian)
+ {857, "ibm857"}, // OEM Turkish; Turkish (DOS)
+ {858, "IBM00858"}, // OEM Multilingual Latin 1 + Euro symbol
+ {860, "IBM860"}, // OEM Portuguese; Portuguese (DOS)
+ {861, "ibm861"}, // OEM Icelandic; Icelandic (DOS)
+ {862, "DOS-862"}, // OEM Hebrew; Hebrew (DOS)
+ {863, "IBM863"}, // OEM French Canadian; French Canadian (DOS)
+ {864, "IBM864"}, // OEM Arabic; Arabic (864)
+ {865, "IBM865"}, // OEM Nordic; Nordic (DOS)
+ {866, "cp866"}, // OEM Russian; Cyrillic (DOS)
+ {869, "ibm869"}, // OEM Modern Greek; Greek, Modern (DOS)
+ {870, "IBM870"}, // IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
+ {874, "windows-874"}, // ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
+ {875, "cp875"}, // IBM EBCDIC Greek Modern
+ {932, "shift_jis"}, // ANSI/OEM Japanese; Japanese (Shift-JIS)
+ {936, "gb2312"}, // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
+ {949, "ks_c_5601-1987"}, // ANSI/OEM Korean (Unified Hangul Code)
+ {950, "big5"}, // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
+ {1026, "IBM1026"}, // IBM EBCDIC Turkish (Latin 5)
+ {1047, "IBM01047"}, // IBM EBCDIC Latin 1/Open System
+ {1140, "IBM01140"}, // IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
+ {1141, "IBM01141"}, // IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
+ {1142, "IBM01142"}, // IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
+ {1143, "IBM01143"}, // IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
+ {1144, "IBM01144"}, // IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
+ {1145, "IBM01145"}, // IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
+ {1146, "IBM01146"}, // IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
+ {1147, "IBM01147"}, // IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
+ {1148, "IBM01148"}, // IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
+ {1149, "IBM01149"}, // IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
+ {1200, "utf-16"}, // Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
+ {1201, "unicodeFFFE"}, // Unicode UTF-16, big endian byte order; available only to managed applications
+ {1250, "windows-1250"}, // ANSI Central European; Central European (Windows)
+ {1251, "windows-1251"}, // ANSI Cyrillic; Cyrillic (Windows)
+ {1252, "windows-1252"}, // ANSI Latin 1; Western European (Windows)
+ {1253, "windows-1253"}, // ANSI Greek; Greek (Windows)
+ {1254, "windows-1254"}, // ANSI Turkish; Turkish (Windows)
+ {1255, "windows-1255"}, // ANSI Hebrew; Hebrew (Windows)
+ {1256, "windows-1256"}, // ANSI Arabic; Arabic (Windows)
+ {1257, "windows-1257"}, // ANSI Baltic; Baltic (Windows)
+ {1258, "windows-1258"}, // ANSI/OEM Vietnamese; Vietnamese (Windows)
+ {1361, "Johab"}, // Korean (Johab)
+ {10000, "macintosh"}, // MAC Roman; Western European (Mac)
+ {10001, "x-mac-japanese"}, // Japanese (Mac)
+ {10002, "x-mac-chinesetrad"}, // MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
+ {10003, "x-mac-korean"}, // Korean (Mac)
+ {10004, "x-mac-arabic"}, // Arabic (Mac)
+ {10005, "x-mac-hebrew"}, // Hebrew (Mac)
+ {10006, "x-mac-greek"}, // Greek (Mac)
+ {10007, "x-mac-cyrillic"}, // Cyrillic (Mac)
+ {10008, "x-mac-chinesesimp"}, // MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
+ {10010, "x-mac-romanian"}, // Romanian (Mac)
+ {10017, "x-mac-ukrainian"}, // Ukrainian (Mac)
+ {10021, "x-mac-thai"}, // Thai (Mac)
+ {10029, "x-mac-ce"}, // MAC Latin 2; Central European (Mac)
+ {10079, "x-mac-icelandic"}, // Icelandic (Mac)
+ {10081, "x-mac-turkish"}, // Turkish (Mac)
+ {10082, "x-mac-croatian"}, // Croatian (Mac)
+ // Unicode UTF-32, little endian byte order; available only to managed applications
+ // impossible in 8-bit mail
+ {12000, "utf-32"},
+ // Unicode UTF-32, big endian byte order; available only to managed applications
+ // impossible in 8-bit mail
+ {12001, "utf-32BE"},
+ {20000, "x-Chinese_CNS"}, // CNS Taiwan; Chinese Traditional (CNS)
+ {20001, "x-cp20001"}, // TCA Taiwan
+ {20002, "x_Chinese-Eten"}, // Eten Taiwan; Chinese Traditional (Eten)
+ {20003, "x-cp20003"}, // IBM5550 Taiwan
+ {20004, "x-cp20004"}, // TeleText Taiwan
+ {20005, "x-cp20005"}, // Wang Taiwan
+ {20105, "x-IA5"}, // IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
+ {20106, "x-IA5-German"}, // IA5 German (7-bit)
+ {20107, "x-IA5-Swedish"}, // IA5 Swedish (7-bit)
+ {20108, "x-IA5-Norwegian"}, // IA5 Norwegian (7-bit)
+ {20127, "us-ascii"}, // US-ASCII (7-bit)
+ {20261, "x-cp20261"}, // T.61
+ {20269, "x-cp20269"}, // ISO 6937 Non-Spacing Accent
+ {20273, "IBM273"}, // IBM EBCDIC Germany
+ {20277, "IBM277"}, // IBM EBCDIC Denmark-Norway
+ {20278, "IBM278"}, // IBM EBCDIC Finland-Sweden
+ {20280, "IBM280"}, // IBM EBCDIC Italy
+ {20284, "IBM284"}, // IBM EBCDIC Latin America-Spain
+ {20285, "IBM285"}, // IBM EBCDIC United Kingdom
+ {20290, "IBM290"}, // IBM EBCDIC Japanese Katakana Extended
+ {20297, "IBM297"}, // IBM EBCDIC France
+ {20420, "IBM420"}, // IBM EBCDIC Arabic
+ {20423, "IBM423"}, // IBM EBCDIC Greek
+ {20424, "IBM424"}, // IBM EBCDIC Hebrew
+ {20833, "x-EBCDIC-KoreanExtended"}, // IBM EBCDIC Korean Extended
+ {20838, "IBM-Thai"}, // IBM EBCDIC Thai
+ {20866, "koi8-r"}, // Russian (KOI8-R); Cyrillic (KOI8-R)
+ {20871, "IBM871"}, // IBM EBCDIC Icelandic
+ {20880, "IBM880"}, // IBM EBCDIC Cyrillic Russian
+ {20905, "IBM905"}, // IBM EBCDIC Turkish
+ {20924, "IBM00924"}, // IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
+ {20932, "EUC-JP"}, // Japanese (JIS 0208-1990 and 0121-1990)
+ {20936, "x-cp20936"}, // Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
+ {20949, "x-cp20949"}, // Korean Wansung
+ {21025, "cp1025"}, // IBM EBCDIC Cyrillic Serbian-Bulgarian
+ //21027 (deprecated)
+ {21866, "koi8-u"}, // Ukrainian (KOI8-U); Cyrillic (KOI8-U)
+ {28591, "iso-8859-1"}, // ISO 8859-1 Latin 1; Western European (ISO)
+ {28592, "iso-8859-2"}, // ISO 8859-2 Central European; Central European (ISO)
+ {28593, "iso-8859-3"}, // ISO 8859-3 Latin 3
+ {28594, "iso-8859-4"}, // ISO 8859-4 Baltic
+ {28595, "iso-8859-5"}, // ISO 8859-5 Cyrillic
+ {28596, "iso-8859-6"}, // ISO 8859-6 Arabic
+ {28597, "iso-8859-7"}, // ISO 8859-7 Greek
+ {28598, "iso-8859-8"}, // ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
+ {28599, "iso-8859-9"}, // ISO 8859-9 Turkish
+ {28603, "iso-8859-13"}, // ISO 8859-13 Estonian
+ {28605, "iso-8859-15"}, // ISO 8859-15 Latin 9
+ {29001, "x-Europa"}, // Europa 3
+ {38598, "iso-8859-8-i"}, // ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
+ {50220, "iso-2022-jp"}, // ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
+ {50221, "csISO2022JP"}, // ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
+ {50222, "iso-2022-jp"}, // ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
+ {50225, "iso-2022-kr"}, // ISO 2022 Korean
+ {50227, "x-cp50227"}, // ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
+ //50229 ISO 2022 Traditional Chinese
+ //50930 EBCDIC Japanese (Katakana) Extended
+ //50931 EBCDIC US-Canada and Japanese
+ //50933 EBCDIC Korean Extended and Korean
+ //50935 EBCDIC Simplified Chinese Extended and Simplified Chinese
+ //50936 EBCDIC Simplified Chinese
+ //50937 EBCDIC US-Canada and Traditional Chinese
+ //50939 EBCDIC Japanese (Latin) Extended and Japanese
+ {51932, "euc-jp"}, // EUC Japanese
+ {51936, "EUC-CN"}, // EUC Simplified Chinese; Chinese Simplified (EUC)
+ {51949, "euc-kr"}, // EUC Korean
+ //51950 EUC Traditional Chinese
+ {52936, "hz-gb-2312"}, // HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
+ {54936, "GB18030"}, // Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
+ {57002, "x-iscii-de"}, // ISCII Devanagari
+ {57003, "x-iscii-be"}, // ISCII Bengali
+ {57004, "x-iscii-ta"}, // ISCII Tamil
+ {57005, "x-iscii-te"}, // ISCII Telugu
+ {57006, "x-iscii-as"}, // ISCII Assamese
+ {57007, "x-iscii-or"}, // ISCII Oriya (Odia)
+ {57008, "x-iscii-ka"}, // ISCII Kannada
+ {57009, "x-iscii-ma"}, // ISCII Malayalam
+ {57010, "x-iscii-gu"}, // ISCII Gujarati
+ {57011, "x-iscii-pa"}, // ISCII Punjabi
+ {65000, "utf-7"}, // Unicode (UTF-7)
+ {65001, "utf-8"}, // Unicode (UTF-8)
+ };
+
+ // Binary search
+ int begin = 0, end = sizeof(cptocharset)/sizeof(cptocharset[0])-1;
+ while (begin <= end) {
+ int mid = (begin+end)/2;
+ unsigned int mid_cp = cptocharset[mid].cp;
+ if (cp == mid_cp)
+ return cptocharset[mid].charset;
+ if (cp < mid_cp)
+ end = mid - 1;
+ else // cp > cptocharset[mid].cp
+ begin = mid + 1;
+ }
+ return 0; // not found
+}
+
+// We don't use nsMsgI18Ncheck_data_in_charset_range because it returns true
+// even if there's no such charset:
+// 1. result initialized by true and returned if, eg, GetUnicodeEncoderRaw fail
+// 2. it uses GetUnicodeEncoderRaw(), not GetUnicodeEncoder() (to normalize the
+// charset string) (see nsMsgI18N.cpp)
+// This function returns true only if the unicode (utf-16) text can be
+// losslessly represented in specified charset
+bool CMapiMessage::CheckBodyInCharsetRange(const char* charset)
+{
+ if (m_body.IsEmpty())
+ return true;
+ if (!_stricmp(charset, "utf-8"))
+ return true;
+ if (!_stricmp(charset, "utf-7"))
+ return true;
+
+ nsresult rv;
+ static nsCOMPtr<nsICharsetConverterManager> ccm =
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr<nsIUnicodeEncoder> encoder;
+
+ // get an unicode converter
+ rv = ccm->GetUnicodeEncoder(charset, getter_AddRefs(encoder));
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal, nullptr, 0);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ const char16_t *txt = m_body.get();
+ int32_t txtLen = m_body.Length();
+ const char16_t *currentSrcPtr = txt;
+ int srcLength;
+ int dstLength;
+ char localbuf[512];
+ int consumedLen = 0;
+
+ // convert
+ while (consumedLen < txtLen) {
+ srcLength = txtLen - consumedLen;
+ dstLength = sizeof(localbuf)/sizeof(localbuf[0]);
+ rv = encoder->Convert(currentSrcPtr, &srcLength, localbuf, &dstLength);
+ if (rv == NS_ERROR_UENC_NOMAPPING)
+ return false;
+ if (NS_FAILED(rv) || dstLength == 0)
+ break;
+
+ currentSrcPtr += srcLength;
+ consumedLen = currentSrcPtr - txt; // src length used so far
+ }
+ return true;
+}
+
+bool CaseInsensitiveComp (wchar_t elem1, wchar_t elem2)
+{
+ return _wcsnicmp(&elem1, &elem2, 1) == 0;
+}
+
+void ExtractMetaCharset(const wchar_t* body, int bodySz, /*out*/nsCString& charset)
+{
+ charset.Truncate();
+ const wchar_t* body_end = body+bodySz;
+ const wchar_t str_eohd[] = L"/head";
+ const wchar_t *str_eohd_end = str_eohd+sizeof(str_eohd)/sizeof(str_eohd[0])-1;
+ const wchar_t* eohd_pos = std::search(body, body_end, str_eohd, str_eohd_end,
+ CaseInsensitiveComp);
+ if (eohd_pos == body_end) // No header!
+ return;
+ const wchar_t str_chset[] = L"charset=";
+ const wchar_t *str_chset_end =
+ str_chset + sizeof(str_chset)/sizeof(str_chset[0])-1;
+ const wchar_t* chset_pos = std::search(body, eohd_pos, str_chset,
+ str_chset_end, CaseInsensitiveComp);
+ if (chset_pos == eohd_pos) // No charset!
+ return;
+ chset_pos += 8;
+
+ // remove everything from the string after the next ; or " or space,
+ // whichever comes first.
+ // The inital sting looks something like
+ // <META content="text/html; charset=utf-8" http-equiv=Content-Type>
+ // <META content="text/html; charset=utf-8;" http-equiv=Content-Type>
+ // <META content="text/html; charset=utf-8 ;" http-equiv=Content-Type>
+ // <META content="text/html; charset=utf-8 " http-equiv=Content-Type>
+ const wchar_t term[] = L";\" ", *term_end= term+sizeof(term)/sizeof(term[0])-1;
+ const wchar_t* chset_end = std::find_first_of(chset_pos, eohd_pos, term,
+ term_end);
+ if (chset_end != eohd_pos)
+ LossyCopyUTF16toASCII(Substring(wwc(const_cast<wchar_t *>(chset_pos)),
+ wwc(const_cast<wchar_t *>(chset_end))),
+ charset);
+}
+
+bool CMapiMessage::FetchBody(void)
+{
+ m_bodyIsHtml = false;
+ m_body.Truncate();
+
+ // Get the Outlook codepage info; if unsuccessful then it defaults to 0 (CP_ACP) -> system default
+ // Maybe we can use this info later?
+ unsigned int codepage=0;
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_INTERNET_CPID);
+ if (pVal) {
+ if (PROP_TYPE(pVal->ulPropTag) == PT_LONG)
+ codepage = pVal->Value.l;
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+
+ unsigned long nativeBodyType = 0;
+ if (CMapiApi::GetRTFPropertyDecodedAsUTF16(m_lpMsg, m_body, nativeBodyType,
+ codepage)) {
+ m_bodyIsHtml = nativeBodyType == MAPI_NATIVE_BODY_TYPE_HTML;
+ }
+ else { // Cannot get RTF version
+ // Is it html?
+ pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_BODY_HTML_W);
+ if (pVal) {
+ if (CMapiApi::IsLargeProperty(pVal))
+ CMapiApi::GetLargeStringProperty(m_lpMsg, PR_BODY_HTML_W, m_body);
+ else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+ (pVal->Value.lpszW) && (*(pVal->Value.lpszW)))
+ m_body.Assign(pVal->Value.lpszW);
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+
+ // Kind-hearted Outlook will give us html even for a plain text message.
+ // But it will include a comment saying it did the conversion.
+ // We'll use this as a hack to really use the plain text part.
+ //
+ // Sadly there are cases where this string is returned despite the fact
+ // that the message is indeed HTML.
+ //
+ // To detect the "true" plain text messages, we look for our string
+ // immediately following the <BODY> tag.
+ if (!m_body.IsEmpty() &&
+ m_body.Find("<BODY>\r\n<!-- Converted from text/plain format -->") ==
+ kNotFound) {
+ m_bodyIsHtml = true;
+ }
+ else {
+ pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_BODY_W);
+ if (pVal) {
+ if (CMapiApi::IsLargeProperty(pVal))
+ CMapiApi::GetLargeStringProperty(m_lpMsg, PR_BODY_W, m_body);
+ else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+ (pVal->Value.lpszW) && (*(pVal->Value.lpszW)))
+ m_body.Assign(pVal->Value.lpszW);
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+ }
+ }
+
+ // OK, now let's restore the original encoding!
+ // 1. We may have a header defining the charset (we already called the FetchHeaders(), and there ProcessHeaders();
+ // in this case, the m_mimeCharset is set. See nsOutlookMail::ImportMailbox())
+ // 2. We may have the codepage walue provided by Outlook ("codepage" at the very beginning of this function)
+ // 3. We may have an HTML charset header.
+
+ bool bFoundCharset = false;
+
+ if (!m_mimeCharset.IsEmpty()) // The top-level header data
+ bFoundCharset = CheckBodyInCharsetRange(m_mimeCharset.get());
+ // No valid charset in the message header - try the HTML header.
+ // arguably may be useless
+ if (!bFoundCharset && m_bodyIsHtml) {
+ ExtractMetaCharset(m_body.get(), m_body.Length(), m_mimeCharset);
+ if (!m_mimeCharset.IsEmpty())
+ bFoundCharset = CheckBodyInCharsetRange(m_mimeCharset.get());
+ }
+ // Get from Outlook (seems like it keeps the MIME part header encoding info)
+ if (!bFoundCharset && codepage) {
+ const char* charset = CpToCharset(codepage);
+ if (charset) {
+ bFoundCharset = CheckBodyInCharsetRange(charset);
+ if (bFoundCharset)
+ m_mimeCharset.Assign(charset);
+ }
+ }
+ if (!bFoundCharset) { // Use system default
+ const char* charset = nsMsgI18NFileSystemCharset();
+ if (charset) {
+ bFoundCharset = CheckBodyInCharsetRange(charset);
+ if (bFoundCharset)
+ m_mimeCharset.Assign(charset);
+ }
+ }
+ if (!bFoundCharset) // Everything else failed, let's use the lossless utf-8...
+ m_mimeCharset.Assign("utf-8");
+
+ MAPI_DUMP_STRING(m_body.get());
+ MAPI_TRACE0("\r\n");
+
+ return true;
+}
+
+void CMapiMessage::GetBody(nsCString& dest) const
+{
+ nsMsgI18NConvertFromUnicode(m_mimeCharset.get(), m_body, dest);
+}
+
+void CMapiMessage::FetchFlags(void)
+{
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_MESSAGE_FLAGS);
+ if (pVal)
+ m_msgFlags = CMapiApi::GetLongFromProp(pVal);
+ pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_LAST_VERB_EXECUTED);
+ if (pVal)
+ m_msgLastVerb = CMapiApi::GetLongFromProp(pVal);
+}
+
+enum {
+ ieidPR_ATTACH_NUM = 0,
+ ieidAttachMax
+};
+
+static const SizedSPropTagArray(ieidAttachMax, ptaEid)=
+{
+ ieidAttachMax,
+ {
+ PR_ATTACH_NUM
+ }
+};
+
+bool CMapiMessage::IterateAttachTable(LPMAPITABLE lpTable)
+{
+ ULONG rowCount;
+ HRESULT hr = lpTable->GetRowCount(0, &rowCount);
+ if (!rowCount) {
+ return true;
+ }
+
+ hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
+ if (FAILED(hr)) {
+ MAPI_TRACE2("SetColumns for attachment table failed: 0x%lx, %d\r\n", (long)hr, (int)hr);
+ return false;
+ }
+
+ hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (FAILED(hr)) {
+ MAPI_TRACE2("SeekRow for attachment table failed: 0x%lx, %d\r\n", (long)hr, (int)hr);
+ return false;
+ }
+
+ int cNumRows = 0;
+ LPSRowSet lpRow;
+ bool bResult = true;
+ do {
+
+ lpRow = NULL;
+ hr = lpTable->QueryRows(1, 0, &lpRow);
+
+ if(HR_FAILED(hr)) {
+ MAPI_TRACE2("QueryRows for attachment table failed: 0x%lx, %d\n", (long)hr, (int)hr);
+ bResult = false;
+ break;
+ }
+
+ if (lpRow) {
+ cNumRows = lpRow->cRows;
+
+ if (cNumRows) {
+ DWORD aNum = lpRow->aRow[0].lpProps[ieidPR_ATTACH_NUM].Value.ul;
+ AddAttachment(aNum);
+ MAPI_TRACE1("\t\t****Attachment found - #%d\r\n", (int)aNum);
+ }
+ CMapiApi::FreeProws(lpRow);
+ }
+
+ } while (SUCCEEDED(hr) && cNumRows && lpRow);
+
+ return bResult;
+}
+
+bool CMapiMessage::GetTmpFile(/*out*/ nsIFile **aResult)
+{
+ nsCOMPtr<nsIFile> tmpFile;
+ nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ "mapiattach.tmp",
+ getter_AddRefs(tmpFile));
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ if (NS_FAILED(rv))
+ return false;
+
+ tmpFile.forget(aResult);
+ return true;
+}
+
+bool CMapiMessage::CopyMsgAttachToFile(LPATTACH lpAttach, /*out*/ nsIFile **tmp_file)
+{
+ bool bResult = true;
+ LPMESSAGE lpMsg;
+ HRESULT hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0,
+ reinterpret_cast<LPUNKNOWN *>(&lpMsg));
+ if (HR_FAILED(hr))
+ return false;
+
+ if (!GetTmpFile(tmp_file))
+ return false;
+
+ nsCOMPtr<nsIOutputStream> destOutputStream;
+ nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(destOutputStream), *tmp_file, -1, 0600);
+ if (NS_SUCCEEDED(rv))
+ rv = nsOutlookMail::ImportMessage(lpMsg, destOutputStream, nsIMsgSend::nsMsgSaveAsDraft);
+
+ if (NS_FAILED(rv)) {
+ (*tmp_file)->Remove(false);
+ (*tmp_file)->Release();
+ *tmp_file = 0;
+ }
+
+ return NS_SUCCEEDED(rv);
+}
+
+bool CMapiMessage::CopyBinAttachToFile(LPATTACH lpAttach,
+ nsIFile **tmp_file)
+{
+ nsCOMPtr<nsIFile> _tmp_file;
+ nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ "mapiattach.tmp",
+ getter_AddRefs(_tmp_file));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ rv = _tmp_file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCString tmpPath;
+ _tmp_file->GetNativePath(tmpPath);
+ LPSTREAM lpStreamFile;
+ HRESULT hr = CMapiApi::OpenStreamOnFile(gpMapiAllocateBuffer, gpMapiFreeBuffer, STGM_READWRITE | STGM_CREATE,
+ const_cast<char*>(tmpPath.get()), NULL, &lpStreamFile);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE1("~~ERROR~~ OpenStreamOnFile failed - temp path: %s\r\n",
+ tmpPath.get());
+ return false;
+ }
+
+ bool bResult = true;
+ LPSTREAM lpAttachStream;
+ hr = lpAttach->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpAttachStream);
+
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE0("~~ERROR~~ OpenProperty failed for PR_ATTACH_DATA_BIN.\r\n");
+ lpAttachStream = NULL;
+ bResult = false;
+ }
+ else {
+ STATSTG st;
+ hr = lpAttachStream->Stat(&st, STATFLAG_NONAME);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE0("~~ERROR~~ Stat failed for attachment stream\r\n");
+ bResult = false;
+ }
+ else {
+ hr = lpAttachStream->CopyTo(lpStreamFile, st.cbSize, NULL, NULL);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE0("~~ERROR~~ Attach Stream CopyTo temp file failed.\r\n");
+ bResult = false;
+ }
+ }
+ }
+
+ if (lpAttachStream)
+ lpAttachStream->Release();
+ lpStreamFile->Release();
+ if (!bResult)
+ _tmp_file->Remove(false);
+ else
+ _tmp_file.forget(tmp_file);
+
+ return bResult;
+}
+
+bool CMapiMessage::GetURL(nsIFile *aFile, nsIURI **url)
+{
+ if (!m_pIOService)
+ return false;
+
+ nsresult rv = m_pIOService->NewFileURI(aFile, url);
+ return NS_SUCCEEDED(rv);
+}
+
+bool CMapiMessage::AddAttachment(DWORD aNum)
+{
+ LPATTACH lpAttach = NULL;
+ HRESULT hr = m_lpMsg->OpenAttach(aNum, NULL, 0, &lpAttach);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE2("\t\t****Attachment error, unable to open attachment: %d, 0x%lx\r\n", idx, hr);
+ return false;
+ }
+
+ bool bResult = false;
+ attach_data *data = new attach_data;
+ ULONG aMethod;
+ if (data) {
+ bResult = true;
+
+ // 1. Get the file that contains the attachment data
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_METHOD);
+ if (pVal) {
+ aMethod = CMapiApi::GetLongFromProp(pVal);
+ switch (aMethod) {
+ case ATTACH_BY_VALUE:
+ MAPI_TRACE1("\t\t** Attachment #%d by value.\r\n", aNum);
+ bResult = CopyBinAttachToFile(lpAttach, getter_AddRefs(data->tmp_file));
+ data->delete_file = true;
+ break;
+ case ATTACH_BY_REFERENCE:
+ case ATTACH_BY_REF_RESOLVE:
+ case ATTACH_BY_REF_ONLY:
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_PATHNAME_W);
+ if (pVal) {
+ nsCString path;
+ CMapiApi::GetStringFromProp(pVal, path);
+ nsresult rv;
+ data->tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !data->tmp_file) {
+ MAPI_TRACE0("*** Error creating file spec for attachment\n");
+ bResult = false;
+ }
+ else data->tmp_file->InitWithNativePath(path);
+ }
+ MAPI_TRACE2("\t\t** Attachment #%d by ref: %s\r\n",
+ aNum, m_attachPath.get());
+ break;
+ case ATTACH_EMBEDDED_MSG:
+ MAPI_TRACE1("\t\t** Attachment #%d by Embedded Message??\r\n", aNum);
+ // Convert the embedded IMessage from PR_ATTACH_DATA_OBJ to rfc822 attachment
+ // (see http://msdn.microsoft.com/en-us/library/cc842329.aspx)
+ // This is a recursive call.
+ bResult = CopyMsgAttachToFile(lpAttach, getter_AddRefs(data->tmp_file));
+ data->delete_file = true;
+ break;
+ case ATTACH_OLE:
+ MAPI_TRACE1("\t\t** Attachment #%d by OLE - yuck!!!\r\n", aNum);
+ break;
+ default:
+ MAPI_TRACE2("\t\t** Attachment #%d unknown attachment method - 0x%lx\r\n", aNum, aMethod);
+ bResult = false;
+ }
+ }
+ else
+ bResult = false;
+
+ if (bResult)
+ bResult = data->tmp_file;
+
+ if (bResult) {
+ bool isFile = false;
+ bool exists = false;
+ data->tmp_file->Exists(&exists);
+ data->tmp_file->IsFile(&isFile);
+
+ if (!exists || !isFile) {
+ bResult = false;
+ MAPI_TRACE0("Attachment file does not exist\n");
+ }
+ }
+
+ if (bResult)
+ bResult = GetURL(data->tmp_file, getter_AddRefs(data->orig_url));
+
+ if (bResult) {
+ // Now we have the file; proceed to the other properties
+
+ data->encoding = NS_strdup(ENCODING_BINARY);
+
+ nsString fname, fext;
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_LONG_FILENAME_W);
+ if (!pVal)
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FILENAME_W);
+ CMapiApi::GetStringFromProp(pVal, fname);
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_EXTENSION_W);
+ CMapiApi::GetStringFromProp(pVal, fext);
+ MAPI_TRACE2("\t\t\t--- File name: %s, extension: %s\r\n",
+ fname.get(), fext.get());
+
+ if (fext.IsEmpty()) {
+ int idx = fname.RFindChar(L'.');
+ if (idx != -1)
+ fext = Substring(fname, idx);
+ }
+ else if (fname.RFindChar(L'.') == -1) {
+ fname += L".";
+ fname += fext;
+ }
+ if (fname.IsEmpty()) {
+ // If no description use "Attachment i" format.
+ fname = L"Attachment ";
+ fname.AppendInt(static_cast<uint32_t>(aNum));
+ }
+ data->real_name = ToNewUTF8String(fname);
+
+ nsCString tmp;
+ // We have converted it to the rfc822 document
+ if (aMethod == ATTACH_EMBEDDED_MSG) {
+ data->type = NS_strdup(MESSAGE_RFC822);
+ } else {
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_MIME_TAG_A);
+ CMapiApi::GetStringFromProp(pVal, tmp);
+ MAPI_TRACE1("\t\t\t--- Mime type: %s\r\n", tmp.get());
+ if (tmp.IsEmpty()) {
+ uint8_t *pType = NULL;
+ if (!fext.IsEmpty()) {
+ pType = CMimeTypes::GetMimeType(fext);
+ }
+ if (pType)
+ data->type = NS_strdup((PC_S8)pType);
+ else
+ data->type = NS_strdup(APPLICATION_OCTET_STREAM);
+ }
+ else
+ data->type = ToNewCString(tmp);
+ }
+
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_CONTENT_ID_A);
+ CMapiApi::GetStringFromProp(pVal, tmp);
+ if (!tmp.IsEmpty())
+ data->cid = ToNewCString(tmp);
+ }
+ if (bResult) {
+ // Now we need to decide if this attachment is embedded or not.
+ // At first, I tried to simply check for the presence of the Content-Id.
+ // But it turned out that this method is unreliable, since there exist cases
+ // when an attachment has a Content-Id while isn't embedded (even in a message
+ // with a plain-text body!). So next I tried to look for <img> tags that contain
+ // the found Content-Id. But this is unreliable, too, because there exist cases
+ // where other places of HTML reference the embedded messages (e.g. it may be
+ // a background of a table cell, or some CSS; further, it is possible that the
+ // reference to an embedded object is not in the main body, but in another
+ // embedded object - like body references a CSS attachment that in turn references
+ // a picture as a background of its element). From the other hand, it's unreliable
+ // to relax the search criteria to any occurence of the Content-Id string in the body -
+ // partly because the string may be simply in a text or other non-referencing part,
+ // partly because of the abovementioned possibility that the reference is outside
+ // the body at all.
+ // There exist the PR_ATTACH_FLAGS property of the attachment. The MS documentation
+ // tells about two possible flags in it: ATT_INVISIBLE_IN_HTML and ATT_INVISIBLE_IN_RTF.
+ // There is at least one more undocumented flag: ATT_MHTML_REF. Some sources in Internet
+ // suggest simply check for the latter flag to distinguish between the embedded
+ // and ordinary attachments. But my observations indicate that even if the flags
+ // don't include ATT_MHTML_REF, the attachment is still may be embedded.
+ // However, my observations always show that the message is embedded if the flags
+ // is not 0.
+ // So now I will simply test for the non-zero flags to decide whether the attachment
+ // is embedded or not. Possible advantage is reliability (I hope).
+ // Another advantage is that it's much faster than search the body for Content-Id.
+
+ DWORD flags = 0;
+
+ pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FLAGS);
+ if (pVal)
+ flags = CMapiApi::GetLongFromProp(pVal);
+ if (m_bodyIsHtml && data->cid && (flags != 0)) // this is the embedded attachment
+ m_embattachments.push_back(data);
+ else // this is ordinary attachment
+ m_stdattachments.push_back(data);
+ }
+ else {
+ delete data;
+ }
+ }
+
+ lpAttach->Release();
+ return bResult;
+}
+
+void CMapiMessage::ClearAttachment(attach_data* data)
+{
+ if (data->delete_file && data->tmp_file)
+ data->tmp_file->Remove(false);
+
+ if (data->type)
+ NS_Free(data->type);
+ if (data->encoding)
+ NS_Free(data->encoding);
+ if (data->real_name)
+ NS_Free(data->real_name);
+ if (data->cid)
+ NS_Free(data->cid);
+
+ delete data;
+}
+
+void CMapiMessage::ClearAttachments()
+{
+ std::for_each(m_stdattachments.begin(), m_stdattachments.end(), ClearAttachment);
+ m_stdattachments.clear();
+ std::for_each(m_embattachments.begin(), m_embattachments.end(), ClearAttachment);
+ m_embattachments.clear();
+}
+
+// This method must be called AFTER the retrieval of the body,
+// since the decision if an attachment is embedded or not is made
+// based on the body type and contents
+void CMapiMessage::ProcessAttachments()
+{
+ LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_HASATTACH);
+ bool hasAttach = true;
+
+ if (pVal) {
+ if (PROP_TYPE(pVal->ulPropTag) == PT_BOOLEAN)
+ hasAttach = (pVal->Value.b != 0);
+ CMapiApi::MAPIFreeBuffer(pVal);
+ }
+
+ if (!hasAttach)
+ return;
+
+ // Get the attachment table?
+ LPMAPITABLE pTable = NULL;
+ HRESULT hr = m_lpMsg->GetAttachmentTable(0, &pTable);
+ if (FAILED(hr) || !pTable)
+ return;
+ IterateAttachTable(pTable);
+ pTable->Release();
+}
+
+nsresult CMapiMessage::GetAttachments(nsIArray **aArray)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> attachments (do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_IF_ADDREF(*aArray = attachments);
+
+ for (std::vector<attach_data*>::const_iterator it = m_stdattachments.begin();
+ it != m_stdattachments.end(); it++) {
+ nsCOMPtr<nsIMsgAttachedFile> a(do_CreateInstance(NS_MSGATTACHEDFILE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ a->SetOrigUrl((*it)->orig_url);
+ a->SetTmpFile((*it)->tmp_file);
+ a->SetEncoding(nsDependentCString((*it)->encoding));
+ a->SetRealName(nsDependentCString((*it)->real_name));
+ a->SetType(nsDependentCString((*it)->type));
+ attachments->AppendElement(a, false);
+ }
+ return rv;
+}
+
+bool CMapiMessage::GetEmbeddedAttachmentInfo(unsigned int i, nsIURI **uri,
+ const char **cid,
+ const char **name) const
+{
+ if ((i < 0) || (i >= m_embattachments.size()))
+ return false;
+ attach_data* data = m_embattachments[i];
+ if (!data)
+ return false;
+ *uri = data->orig_url;
+ *cid = data->cid;
+ *name = data->real_name;
+ return true;
+}
+
+//////////////////////////////////////////////////////
+
+// begin and end MUST point to the same string
+char* dup(const char* begin, const char* end)
+{
+ if (begin >= end)
+ return 0;
+ char* str = new char[end-begin+1];
+ memcpy(str, begin, (end-begin)*sizeof(begin[0]));
+ str[end - begin] = 0;
+ return str;
+}
+
+// See RFC822
+// 9 = '\t', 32 = ' '.
+inline bool IsPrintableASCII(char c) { return (c > ' ') && (c < 127); }
+inline bool IsWSP(char c) { return (c == ' ') || (c == '\t'); }
+
+CMapiMessageHeaders::CHeaderField::CHeaderField(const char* begin, int len)
+ : m_fname(0), m_fbody(0), m_fbody_utf8(false)
+{
+ const char *end = begin+len, *fname_end = begin;
+ while ((fname_end < end) && IsPrintableASCII(*fname_end) && (*fname_end != ':'))
+ ++fname_end;
+ if ((fname_end == end) || (*fname_end != ':'))
+ return; // Not a valid header!
+ m_fname = dup(begin, fname_end+1); // including colon
+ m_fbody = dup(fname_end+1, end);
+}
+
+CMapiMessageHeaders::CHeaderField::CHeaderField(const char* name, const char* body, bool utf8)
+ : m_fname(dup(name, name+strlen(name))), m_fbody(dup(body, body+strlen(body))), m_fbody_utf8(utf8)
+{
+}
+
+CMapiMessageHeaders::CHeaderField::~CHeaderField()
+{
+ delete[] m_fname;
+ delete[] m_fbody;
+}
+
+void CMapiMessageHeaders::CHeaderField::set_fbody(const char* txt)
+{
+ if (m_fbody == txt)
+ return; // to avoid assigning to self
+ char* oldbody = m_fbody;
+ m_fbody = dup(txt, txt+strlen(txt));
+ delete[] oldbody;
+ m_fbody_utf8 = true;
+}
+
+void CMapiMessageHeaders::CHeaderField::GetUnfoldedString(nsString& dest,
+ const char* fallbackCharset) const
+{
+ dest.Truncate();
+ if (!m_fbody)
+ return;
+
+ nsCString unfolded;
+ const char* pos = m_fbody;
+ while (*pos) {
+ if ((*pos == nsCRT::CR) && (*(pos+1) == nsCRT::LF) && IsWSP(*(pos+2)))
+ pos += 2; // Skip CRLF if it is followed by SPACE or TAB
+ else
+ unfolded.Append(*(pos++));
+ }
+ if (m_fbody_utf8)
+ CopyUTF8toUTF16(unfolded, dest);
+ else
+ nsMsgI18NConvertToUnicode(fallbackCharset, unfolded, dest);
+}
+
+////////////////////////////////////////
+
+const char* CMapiMessageHeaders::Specials[hdrMax] = {
+ "Date:",
+ "From:",
+ "Sender:",
+ "Reply-To:",
+ "To:",
+ "Cc:",
+ "Bcc:",
+ "Message-ID:",
+ "Subject:",
+ "Mime-Version:",
+ "Content-Type:",
+ "Content-Transfer-Encoding:"
+};
+
+CMapiMessageHeaders::~CMapiMessageHeaders()
+{
+ ClearHeaderFields();
+}
+
+void Delete(void* p) { delete p; }
+
+void CMapiMessageHeaders::ClearHeaderFields()
+{
+ std::for_each(m_headerFields.begin(), m_headerFields.end(), Delete);
+ m_headerFields.clear();
+}
+
+void CMapiMessageHeaders::Assign(const char* headers)
+{
+ for (int i=0; i<hdrMax; i++)
+ m_SpecialHeaders[i] = 0;
+ ClearHeaderFields();
+ if (!headers)
+ return;
+
+ const char *start=headers, *end=headers;
+ while (*end) {
+ if ((*end == nsCRT::CR) && (*(end+1) == nsCRT::LF)) {
+ if (!IsWSP(*(end+2))) { // Not SPACE nor TAB (avoid FSP) -> next header or EOF
+ Add(new CHeaderField(start, end-start));
+ start = ++end + 1;
+ }
+ }
+ ++end;
+ }
+
+ if (start < end) { // Last header left
+ Add(new CHeaderField(start, end-start));
+ }
+}
+
+void CMapiMessageHeaders::Add(CHeaderField* f)
+{
+ if (!f)
+ return;
+ if (!f->Valid()) {
+ delete f;
+ return;
+ }
+
+ SpecialHeader idx = CheckSpecialHeader(f->fname());
+ if (idx != hdrNone) {
+ // Now check if the special header was already inserted;
+ // if so, remove previous and add this new
+ CHeaderField* PrevSpecial = m_SpecialHeaders[idx];
+ if (PrevSpecial) {
+ std::vector<CHeaderField*>::iterator iter = std::find(m_headerFields.begin(), m_headerFields.end(), PrevSpecial);
+ if (iter != m_headerFields.end())
+ m_headerFields.erase(iter);
+ delete PrevSpecial;
+ }
+ m_SpecialHeaders[idx] = f;
+ }
+ m_headerFields.push_back(f);
+}
+
+CMapiMessageHeaders::SpecialHeader CMapiMessageHeaders::CheckSpecialHeader(const char* fname)
+{
+ for (int i = hdrFirst; i < hdrMax; i++)
+ if (stricmp(fname, Specials[i]) == 0)
+ return static_cast<SpecialHeader>(i);
+
+ return hdrNone;
+}
+
+const CMapiMessageHeaders::CHeaderField* CMapiMessageHeaders::CFind(const char* name) const
+{
+ SpecialHeader special = CheckSpecialHeader(name);
+ if ((special > hdrNone) && (special < hdrMax))
+ return m_SpecialHeaders[special]; // No need to search further, because it MUST be here
+
+ std::vector<CHeaderField*>::const_iterator iter = std::find_if(m_headerFields.begin(), m_headerFields.end(), fname_equals(name));
+ if (iter == m_headerFields.end())
+ return 0;
+ return *iter;
+}
+
+const char* CMapiMessageHeaders::SpecialName(SpecialHeader special)
+{
+ if ((special <= hdrNone) || (special >= hdrMax))
+ return 0;
+ return Specials[special];
+}
+
+const char* CMapiMessageHeaders::Value(SpecialHeader special) const
+{
+ if ((special <= hdrNone) || (special >= hdrMax))
+ return 0;
+ return (m_SpecialHeaders[special]) ? m_SpecialHeaders[special]->fbody() : 0;
+}
+
+const char* CMapiMessageHeaders::Value(const char* name) const
+{
+ const CHeaderField* result = CFind(name);
+ return result ? result->fbody() : 0;
+}
+
+void CMapiMessageHeaders::UnfoldValue(const char* name, nsString& dest, const char* fallbackCharset) const
+{
+ const CHeaderField* result = CFind(name);
+ if (result)
+ result->GetUnfoldedString(dest, fallbackCharset);
+ else
+ dest.Truncate();
+}
+
+void CMapiMessageHeaders::UnfoldValue(SpecialHeader special, nsString& dest, const char* fallbackCharset) const
+{
+ if ((special <= hdrNone) || (special >= hdrMax) || (!m_SpecialHeaders[special]))
+ dest.Truncate();
+ else
+ m_SpecialHeaders[special]->GetUnfoldedString(dest, fallbackCharset);
+}
+
+int CMapiMessageHeaders::SetValue(const char* name, const char* value, bool replace)
+{
+ if (!replace) {
+ CHeaderField* result = Find(name);
+ if (result) {
+ result->set_fbody(value);
+ return 0;
+ }
+ }
+ Add(new CHeaderField(name, value, true));
+ return 0; // No sensible result is returned; maybe do something senseful later
+}
+
+int CMapiMessageHeaders::SetValue(SpecialHeader special, const char* value)
+{
+ CHeaderField* result = m_SpecialHeaders[special];
+ if (result)
+ result->set_fbody(value);
+ else
+ Add(new CHeaderField(Specials[special], value, true));
+ return 0;
+}
+
+void CMapiMessageHeaders::write_to_stream::operator () (const CHeaderField* f)
+{
+ if (!f || NS_FAILED(m_rv))
+ return;
+
+ uint32_t written;
+ m_rv = m_pDst->Write(f->fname(), strlen(f->fname()), &written);
+ NS_ENSURE_SUCCESS_VOID(m_rv);
+ if (f->fbody()) {
+ m_rv = m_pDst->Write(f->fbody(), strlen(f->fbody()), &written);
+ NS_ENSURE_SUCCESS_VOID(m_rv);
+ }
+ m_rv = m_pDst->Write("\x0D\x0A", 2, &written);
+}
+
+nsresult CMapiMessageHeaders::ToStream(nsIOutputStream *pDst) const
+{
+ nsresult rv = std::for_each(m_headerFields.begin(), m_headerFields.end(),
+ write_to_stream(pDst));
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t written;
+ rv = pDst->Write("\x0D\x0A", 2, &written); // Separator line
+ }
+ return rv;
+}
diff --git a/mailnews/import/outlook/src/MapiMessage.h b/mailnews/import/outlook/src/MapiMessage.h
new file mode 100644
index 000000000..dc6a6cda1
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiMessage.h
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef MapiMessage_h___
+#define MapiMessage_h___
+
+#include "nsTArray.h"
+#include "nsStringGlue.h"
+#include "nsIFile.h"
+#include "MapiApi.h"
+#include "nsIMsgSend.h"
+
+#include <vector>
+
+#ifndef PR_LAST_VERB_EXECUTED
+#define PR_LAST_VERB_EXECUTED PROP_TAG(PT_LONG, 0x1081)
+#endif
+
+#define EXCHIVERB_REPLYTOSENDER (102)
+#define EXCHIVERB_REPLYTOALL (103)
+#define EXCHIVERB_FORWARD (104)
+
+#ifndef PR_ATTACH_CONTENT_ID
+#define PR_ATTACH_CONTENT_ID PROP_TAG(PT_TSTRING, 0x3712)
+#endif
+#ifndef PR_ATTACH_CONTENT_ID_W
+#define PR_ATTACH_CONTENT_ID_W PROP_TAG(PT_UNICODE, 0x3712)
+#endif
+#ifndef PR_ATTACH_CONTENT_ID_A
+#define PR_ATTACH_CONTENT_ID_A PROP_TAG(PT_STRING8, 0x3712)
+#endif
+
+#ifndef PR_ATTACH_FLAGS
+#define PR_ATTACH_FLAGS PROP_TAG(PT_LONG, 0x3714)
+#endif
+
+#ifndef ATT_INVISIBLE_IN_HTML
+#define ATT_INVISIBLE_IN_HTML (0x1)
+#endif
+#ifndef ATT_INVISIBLE_IN_RTF
+#define ATT_INVISIBLE_IN_RTF (0x2)
+#endif
+#ifndef ATT_MHTML_REF
+#define ATT_MHTML_REF (0x4)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CMapiMessageHeaders {
+public:
+ // Special headers that MUST appear at most once (see RFC822)
+ enum SpecialHeader { hdrNone=-1, hdrFirst = 0, // utility values
+ hdrDate=hdrFirst,
+ hdrFrom,
+ hdrSender,
+ hdrReplyTo,
+ hdrTo,
+ hdrCc,
+ hdrBcc,
+ hdrMessageID,
+ hdrSubject,
+ hdrMimeVersion,
+ hdrContentType,
+ hdrContentTransferEncoding,
+ hdrMax // utility value
+ };
+
+ CMapiMessageHeaders(const char* headers = 0) { Assign(headers); }
+ ~CMapiMessageHeaders();
+ void Assign(const char* headers);
+
+ inline bool IsEmpty() const { return m_headerFields.empty(); }
+ // if no such header exists then 0 is returned, else the first value returned
+ const char* Value(const char* name) const;
+ // if no such header exists then 0 is returned
+ const char* Value(SpecialHeader special) const;
+
+ void UnfoldValue(const char* name, nsString& dest, const char* fallbackCharset) const;
+ void UnfoldValue(SpecialHeader special, nsString& dest, const char* fallbackCharset) const;
+
+ // value must be utf-8 or 7-bit; supposed that this function will be called
+ // when the charset of the value is known
+ // TODO: if replace is set, then all headers with this name will be removed
+ // and one with this value will be added, otherwise a new header is added
+ // (Unnecessary for now)
+ int SetValue(const char* name, const char* value, bool replace = true);
+ int SetValue(SpecialHeader special, const char* value);
+
+ static const char* SpecialName(SpecialHeader special);
+
+ nsresult ToStream(nsIOutputStream *pDst) const;
+private:
+ class CHeaderField {
+ public:
+ CHeaderField(const char* begin, int len);
+ CHeaderField(const char* name, const char* body, bool utf8 = false);
+ ~CHeaderField();
+ inline bool Valid() const { return m_fname; }
+ inline const char* fname() const { return m_fname; }
+ inline const char* fbody() const { return m_fbody; }
+
+ // txt must be utf-8 or 7-bit; supposed that this function will be called
+ // when the charset of the txt is known
+ void set_fbody(const char* txt);
+
+ void GetUnfoldedString(nsString& dest, const char* fallbackCharset) const;
+ private:
+ char* m_fname;
+ char* m_fbody;
+ bool m_fbody_utf8;
+ }; //class HeaderField
+
+ class write_to_stream {
+ public:
+ write_to_stream(nsIOutputStream *pDst) : m_pDst(pDst), m_rv(NS_OK) {}
+ void operator () (const CHeaderField* f);
+ inline operator nsresult() const { return m_rv; }
+ private:
+ nsIOutputStream *m_pDst;
+ nsresult m_rv;
+ };
+
+ // Search helper
+ class fname_equals {
+ public:
+ fname_equals(const char* search) : m_search(search) {}
+ inline bool operator () (const CHeaderField* f) const { return stricmp(f->fname(), m_search) == 0; }
+ private:
+ const char* m_search;
+ }; // class fname_equals
+
+ // The common array of special headers' names
+ static const char* Specials[hdrMax];
+
+ std::vector<CHeaderField*> m_headerFields;
+ CHeaderField* m_SpecialHeaders[hdrMax]; // Pointers into the m_headerFields
+
+ void ClearHeaderFields();
+ void Add(CHeaderField* f);
+ static SpecialHeader CheckSpecialHeader(const char* fname);
+ const CHeaderField* CFind(const char* name) const;
+ inline CHeaderField* Find(const char* name) { return const_cast<CHeaderField*>(CFind(name)); }
+
+}; // class CMapiMessageHeaders
+
+//////////////////////////////////////////////////////
+
+class CMapiMessage {
+public:
+ CMapiMessage(LPMESSAGE lpMsg);
+ ~CMapiMessage();
+
+ // Attachments
+ // Ordinary (not embedded) attachments.
+ nsresult GetAttachments(nsIArray **aArray);
+ // Embedded attachments
+ size_t EmbeddedAttachmentsCount() const { return m_embattachments.size(); }
+ bool GetEmbeddedAttachmentInfo(unsigned int i, nsIURI **uri, const char **cid,
+ const char **name) const;
+ // We don't check MSGFLAG_HASATTACH, since it returns true even if there are
+ // only embedded attachmentsin the message. TB only counts the ordinary
+ // attachments when shows the message status, so here we check only for the
+ // ordinary attachments.
+ inline bool HasAttach() const { return !m_stdattachments.empty(); }
+
+ // Retrieve info for message
+ inline bool BodyIsHtml(void) const { return m_bodyIsHtml;}
+ const char *GetFromLine(int& len) const {
+ if (m_fromLine.IsEmpty())
+ return NULL;
+ else {
+ len = m_fromLine.Length();
+ return m_fromLine.get();}
+ }
+ inline CMapiMessageHeaders *GetHeaders() { return &m_headers; }
+ inline const wchar_t *GetBody(void) const { return m_body.get(); }
+ inline size_t GetBodyLen(void) const { return m_body.Length(); }
+ void GetBody(nsCString& dest) const;
+ inline const char *GetBodyCharset(void) const { return m_mimeCharset.get();}
+ inline bool IsRead() const { return m_msgFlags & MSGFLAG_READ; }
+ inline bool IsReplied() const {
+ return (m_msgLastVerb == EXCHIVERB_REPLYTOSENDER) ||
+ (m_msgLastVerb == EXCHIVERB_REPLYTOALL); }
+ inline bool IsForvarded() const {
+ return m_msgLastVerb == EXCHIVERB_FORWARD; }
+
+ bool HasContentHeader(void) const {
+ return !m_mimeContentType.IsEmpty();}
+ bool HasMimeVersion(void) const {
+ return m_headers.Value(CMapiMessageHeaders::hdrMimeVersion); }
+ const char *GetMimeContent(void) const { return m_mimeContentType.get();}
+ int32_t GetMimeContentLen(void) const { return m_mimeContentType.Length();}
+ const char *GetMimeBoundary(void) const { return m_mimeBoundary.get();}
+
+ // The only required part of a message is its header
+ inline bool ValidState() const { return !m_headers.IsEmpty(); }
+ inline bool FullMessageDownloaded() const { return !m_dldStateHeadersOnly; }
+
+private:
+ struct attach_data {
+ nsCOMPtr<nsIURI> orig_url;
+ nsCOMPtr<nsIFile> tmp_file;
+ char *type;
+ char *encoding;
+ char *real_name;
+ char *cid;
+ bool delete_file;
+ attach_data() : type(0), encoding(0), real_name(0), cid(0), delete_file(false) {}
+ };
+
+ static const nsCString m_whitespace;
+
+ LPMESSAGE m_lpMsg;
+
+ bool m_dldStateHeadersOnly; // if the message has not been downloaded yet
+ CMapiMessageHeaders m_headers;
+ nsCString m_fromLine; // utf-8
+ nsCString m_mimeContentType; // utf-8
+ nsCString m_mimeBoundary; // utf-8
+ nsCString m_mimeCharset; // utf-8
+
+ std::vector<attach_data*> m_stdattachments;
+ std::vector<attach_data*> m_embattachments; // Embedded
+
+ nsString m_body; // to be converted from UTF-16 using m_mimeCharset
+ bool m_bodyIsHtml;
+
+ uint32_t m_msgFlags;
+ uint32_t m_msgLastVerb;
+
+ nsCOMPtr<nsIIOService> m_pIOService;
+
+ void GetDownloadState();
+
+ // Headers - fetch will get PR_TRANSPORT_MESSAGE_HEADERS
+ // or if they do not exist will build a header from
+ // PR_DISPLAY_TO, _CC, _BCC
+ // PR_SUBJECT
+ // PR_MESSAGE_RECIPIENTS
+ // and PR_CREATION_TIME if needed?
+ bool FetchHeaders(void);
+ bool FetchBody(void);
+ void FetchFlags(void);
+
+ static bool GetTmpFile(/*out*/ nsIFile **aResult);
+ static bool CopyMsgAttachToFile(LPATTACH lpAttach, /*out*/ nsIFile **tmp_file);
+ static bool CopyBinAttachToFile(LPATTACH lpAttach, nsIFile **tmp_file);
+
+ static void ClearAttachment(attach_data* data);
+ void ClearAttachments();
+ bool AddAttachment(DWORD aNum);
+ bool IterateAttachTable(LPMAPITABLE tbl);
+ bool GetURL(nsIFile *aFile, nsIURI **url);
+ void ProcessAttachments();
+
+ bool EnsureHeader(CMapiMessageHeaders::SpecialHeader special, ULONG mapiTag);
+ bool EnsureDate();
+
+ void ProcessContentType();
+ bool CheckBodyInCharsetRange(const char* charset);
+ void FormatDateTime(SYSTEMTIME& tm, nsCString& s, bool includeTZ = true);
+ void BuildFromLine(void);
+
+ inline static bool IsSpace(char c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\b' || c == '\t';}
+ inline static bool IsSpace(wchar_t c) {
+ return ((c & 0xFF) == c) && IsSpace(static_cast<char>(c)); } // Avoid false detections
+};
+
+#endif /* MapiMessage_h__ */
diff --git a/mailnews/import/outlook/src/MapiMimeTypes.cpp b/mailnews/import/outlook/src/MapiMimeTypes.cpp
new file mode 100644
index 000000000..38b2046db
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiMimeTypes.cpp
@@ -0,0 +1,96 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "MapiMimeTypes.h"
+
+uint8_t CMimeTypes::m_mimeBuffer[kMaxMimeTypeSize];
+
+
+BOOL CMimeTypes::GetKey(HKEY root, LPCTSTR pName, PHKEY pKey)
+{
+ LONG result = RegOpenKeyEx(root, pName, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, pKey);
+ return result == ERROR_SUCCESS;
+}
+
+BOOL CMimeTypes::GetValueBytes(HKEY rootKey, LPCTSTR pValName, LPBYTE *ppBytes)
+{
+ LONG err;
+ DWORD bufSz;
+
+ *ppBytes = NULL;
+ // Get the installed directory
+ err = RegQueryValueEx(rootKey, pValName, NULL, NULL, NULL, &bufSz);
+ if (err == ERROR_SUCCESS) {
+ *ppBytes = new BYTE[bufSz];
+ err = RegQueryValueEx(rootKey, pValName, NULL, NULL, *ppBytes, &bufSz);
+ if (err == ERROR_SUCCESS) {
+ return TRUE;
+ }
+ delete *ppBytes;
+ *ppBytes = NULL;
+ }
+ return FALSE;
+}
+
+void CMimeTypes::ReleaseValueBytes(LPBYTE pBytes)
+{
+ if (pBytes)
+ delete pBytes;
+}
+
+BOOL CMimeTypes::GetMimeTypeFromReg(const nsCString& ext, LPBYTE *ppBytes)
+{
+ HKEY extensionKey;
+ BOOL result = FALSE;
+ *ppBytes = NULL;
+ if (GetKey(HKEY_CLASSES_ROOT, ext.get(), &extensionKey)) {
+ result = GetValueBytes(extensionKey, "Content Type", ppBytes);
+ RegCloseKey(extensionKey);
+ }
+
+ return result;
+}
+
+uint8_t * CMimeTypes::GetMimeType(const nsString& theExt)
+{
+ nsCString ext;
+ LossyCopyUTF16toASCII(theExt, ext);
+ return GetMimeType(ext);
+}
+
+uint8_t * CMimeTypes::GetMimeType(const nsCString& theExt)
+{
+ nsCString ext = theExt;
+ if (ext.Length()) {
+ if (ext.First() != '.') {
+ ext = ".";
+ ext += theExt;
+ }
+ }
+
+
+ BOOL result = FALSE;
+ int len;
+
+ if (!ext.Length())
+ return NULL;
+ LPBYTE pByte;
+ if (GetMimeTypeFromReg(ext, &pByte)) {
+ len = strlen((const char *) pByte);
+ if (len && (len < kMaxMimeTypeSize)) {
+ memcpy(m_mimeBuffer, pByte, len);
+ m_mimeBuffer[len] = 0;
+ result = TRUE;
+ }
+ ReleaseValueBytes(pByte);
+ }
+
+ if (result)
+ return m_mimeBuffer;
+
+ return NULL;
+}
diff --git a/mailnews/import/outlook/src/MapiMimeTypes.h b/mailnews/import/outlook/src/MapiMimeTypes.h
new file mode 100644
index 000000000..61a90dd19
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiMimeTypes.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MapiMimeTypes_h___
+#define MapiMimeTypes_h___
+
+#include <windows.h>
+
+#define kMaxMimeTypeSize 256
+
+class CMimeTypes {
+public:
+
+static uint8_t * GetMimeType(const nsCString& theExt);
+static uint8_t * GetMimeType(const nsString& theExt);
+
+protected:
+ // Registry stuff
+static BOOL GetKey(HKEY root, LPCTSTR pName, PHKEY pKey);
+static BOOL GetValueBytes(HKEY rootKey, LPCTSTR pValName, LPBYTE *ppBytes);
+static void ReleaseValueBytes(LPBYTE pBytes);
+static BOOL GetMimeTypeFromReg(const nsCString& ext, LPBYTE *ppBytes);
+
+
+static uint8_t m_mimeBuffer[kMaxMimeTypeSize];
+};
+
+#endif /* MapiMimeTypes_h__ */
+
diff --git a/mailnews/import/outlook/src/MapiTagStrs.cpp b/mailnews/import/outlook/src/MapiTagStrs.cpp
new file mode 100644
index 000000000..217b2186c
--- /dev/null
+++ b/mailnews/import/outlook/src/MapiTagStrs.cpp
@@ -0,0 +1,1070 @@
+/* -*- 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/. */
+
+/*
+ * Message envelope properties
+ */
+
+case PR_ACKNOWLEDGEMENT_MODE:
+ s = "PR_ACKNOWLEDGEMENT_MODE"; break;
+case PR_ALTERNATE_RECIPIENT_ALLOWED:
+ s = "PR_ALTERNATE_RECIPIENT_ALLOWED"; break;
+case PR_AUTHORIZING_USERS:
+ s = "PR_AUTHORIZING_USERS"; break;
+case PR_AUTO_FORWARD_COMMENT:
+ s = "PR_AUTO_FORWARD_COMMENT"; break;
+case PR_AUTO_FORWARDED:
+ s = "PR_AUTO_FORWARDED"; break;
+case PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID:
+ s = "PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID"; break;
+case PR_CONTENT_CORRELATOR:
+ s = "PR_CONTENT_CORRELATOR"; break;
+case PR_CONTENT_IDENTIFIER:
+ s = "PR_CONTENT_IDENTIFIER"; break;
+case PR_CONTENT_LENGTH:
+ s = "PR_CONTENT_LENGTH"; break;
+case PR_CONTENT_RETURN_REQUESTED:
+ s = "PR_CONTENT_RETURN_REQUESTED"; break;
+
+
+
+case PR_CONVERSATION_KEY:
+ s = "PR_CONVERSATION_KEY"; break;
+
+case PR_CONVERSION_EITS:
+ s = "PR_CONVERSION_EITS"; break;
+case PR_CONVERSION_WITH_LOSS_PROHIBITED:
+ s = "PR_CONVERSION_WITH_LOSS_PROHIBITED"; break;
+case PR_CONVERTED_EITS:
+ s = "PR_CONVERTED_EITS"; break;
+case PR_DEFERRED_DELIVERY_TIME:
+ s = "PR_DEFERRED_DELIVERY_TIME"; break;
+case PR_DELIVER_TIME:
+ s = "PR_DELIVER_TIME"; break;
+case PR_DISCARD_REASON:
+ s = "PR_DISCARD_REASON"; break;
+case PR_DISCLOSURE_OF_RECIPIENTS:
+ s = "PR_DISCLOSURE_OF_RECIPIENTS"; break;
+case PR_DL_EXPANSION_HISTORY:
+ s = "PR_DL_EXPANSION_HISTORY"; break;
+case PR_DL_EXPANSION_PROHIBITED:
+ s = "PR_DL_EXPANSION_PROHIBITED"; break;
+case PR_EXPIRY_TIME:
+ s = "PR_EXPIRY_TIME"; break;
+case PR_IMPLICIT_CONVERSION_PROHIBITED:
+ s = "PR_IMPLICIT_CONVERSION_PROHIBITED"; break;
+case PR_IMPORTANCE:
+ s = "PR_IMPORTANCE"; break;
+case PR_IPM_ID:
+ s = "PR_IPM_ID"; break;
+case PR_LATEST_DELIVERY_TIME:
+ s = "PR_LATEST_DELIVERY_TIME"; break;
+case PR_MESSAGE_CLASS:
+ s = "PR_MESSAGE_CLASS"; break;
+case PR_MESSAGE_DELIVERY_ID:
+ s = "PR_MESSAGE_DELIVERY_ID"; break;
+
+
+
+
+
+case PR_MESSAGE_SECURITY_LABEL:
+ s = "PR_MESSAGE_SECURITY_LABEL"; break;
+case PR_OBSOLETED_IPMS:
+ s = "PR_OBSOLETED_IPMS"; break;
+case PR_ORIGINALLY_INTENDED_RECIPIENT_NAME:
+ s = "PR_ORIGINALLY_INTENDED_RECIPIENT_NAME"; break;
+case PR_ORIGINAL_EITS:
+ s = "PR_ORIGINAL_EITS"; break;
+case PR_ORIGINATOR_CERTIFICATE:
+ s = "PR_ORIGINATOR_CERTIFICATE"; break;
+case PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED:
+ s = "PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED"; break;
+case PR_ORIGINATOR_RETURN_ADDRESS:
+ s = "PR_ORIGINATOR_RETURN_ADDRESS"; break;
+
+
+
+case PR_PARENT_KEY:
+ s = "PR_PARENT_KEY"; break;
+case PR_PRIORITY:
+ s = "PR_PRIORITY"; break;
+
+
+
+case PR_ORIGIN_CHECK:
+ s = "PR_ORIGIN_CHECK"; break;
+case PR_PROOF_OF_SUBMISSION_REQUESTED:
+ s = "PR_PROOF_OF_SUBMISSION_REQUESTED"; break;
+case PR_READ_RECEIPT_REQUESTED:
+ s = "PR_READ_RECEIPT_REQUESTED"; break;
+case PR_RECEIPT_TIME:
+ s = "PR_RECEIPT_TIME"; break;
+case PR_RECIPIENT_REASSIGNMENT_PROHIBITED:
+ s = "PR_RECIPIENT_REASSIGNMENT_PROHIBITED"; break;
+case PR_REDIRECTION_HISTORY:
+ s = "PR_REDIRECTION_HISTORY"; break;
+case PR_RELATED_IPMS:
+ s = "PR_RELATED_IPMS"; break;
+case PR_ORIGINAL_SENSITIVITY:
+ s = "PR_ORIGINAL_SENSITIVITY"; break;
+case PR_LANGUAGES:
+ s = "PR_LANGUAGES"; break;
+case PR_REPLY_TIME:
+ s = "PR_REPLY_TIME"; break;
+case PR_REPORT_TAG:
+ s = "PR_REPORT_TAG"; break;
+case PR_REPORT_TIME:
+ s = "PR_REPORT_TIME"; break;
+case PR_RETURNED_IPM:
+ s = "PR_RETURNED_IPM"; break;
+case PR_SECURITY:
+ s = "PR_SECURITY"; break;
+case PR_INCOMPLETE_COPY:
+ s = "PR_INCOMPLETE_COPY"; break;
+case PR_SENSITIVITY:
+ s = "PR_SENSITIVITY"; break;
+case PR_SUBJECT:
+ s = "PR_SUBJECT"; break;
+case PR_SUBJECT_IPM:
+ s = "PR_SUBJECT_IPM"; break;
+case PR_CLIENT_SUBMIT_TIME:
+ s = "PR_CLIENT_SUBMIT_TIME"; break;
+case PR_REPORT_NAME:
+ s = "PR_REPORT_NAME"; break;
+case PR_SENT_REPRESENTING_SEARCH_KEY:
+ s = "PR_SENT_REPRESENTING_SEARCH_KEY"; break;
+case PR_X400_CONTENT_TYPE:
+ s = "PR_X400_CONTENT_TYPE"; break;
+case PR_SUBJECT_PREFIX:
+ s = "PR_SUBJECT_PREFIX"; break;
+case PR_NON_RECEIPT_REASON:
+ s = "PR_NON_RECEIPT_REASON"; break;
+case PR_RECEIVED_BY_ENTRYID:
+ s = "PR_RECEIVED_BY_ENTRYID"; break;
+case PR_RECEIVED_BY_NAME:
+ s = "PR_RECEIVED_BY_NAME"; break;
+case PR_SENT_REPRESENTING_ENTRYID:
+ s = "PR_SENT_REPRESENTING_ENTRYID"; break;
+case PR_SENT_REPRESENTING_NAME:
+ s = "PR_SENT_REPRESENTING_NAME"; break;
+case PR_RCVD_REPRESENTING_ENTRYID:
+ s = "PR_RCVD_REPRESENTING_ENTRYID"; break;
+case PR_RCVD_REPRESENTING_NAME:
+ s = "PR_RCVD_REPRESENTING_NAME"; break;
+case PR_REPORT_ENTRYID:
+ s = "PR_REPORT_ENTRYID"; break;
+case PR_READ_RECEIPT_ENTRYID:
+ s = "PR_READ_RECEIPT_ENTRYID"; break;
+case PR_MESSAGE_SUBMISSION_ID:
+ s = "PR_MESSAGE_SUBMISSION_ID"; break;
+case PR_PROVIDER_SUBMIT_TIME:
+ s = "PR_PROVIDER_SUBMIT_TIME"; break;
+case PR_ORIGINAL_SUBJECT:
+ s = "PR_ORIGINAL_SUBJECT"; break;
+case PR_DISC_VAL:
+ s = "PR_DISC_VAL"; break;
+case PR_ORIG_MESSAGE_CLASS:
+ s = "PR_ORIG_MESSAGE_CLASS"; break;
+case PR_ORIGINAL_AUTHOR_ENTRYID:
+ s = "PR_ORIGINAL_AUTHOR_ENTRYID"; break;
+case PR_ORIGINAL_AUTHOR_NAME:
+ s = "PR_ORIGINAL_AUTHOR_NAME"; break;
+case PR_ORIGINAL_SUBMIT_TIME:
+ s = "PR_ORIGINAL_SUBMIT_TIME"; break;
+case PR_REPLY_RECIPIENT_ENTRIES:
+ s = "PR_REPLY_RECIPIENT_ENTRIES"; break;
+case PR_REPLY_RECIPIENT_NAMES:
+ s = "PR_REPLY_RECIPIENT_NAMES"; break;
+
+case PR_RECEIVED_BY_SEARCH_KEY:
+ s = "PR_RECEIVED_BY_SEARCH_KEY"; break;
+case PR_RCVD_REPRESENTING_SEARCH_KEY:
+ s = "PR_RCVD_REPRESENTING_SEARCH_KEY"; break;
+case PR_READ_RECEIPT_SEARCH_KEY:
+ s = "PR_READ_RECEIPT_SEARCH_KEY"; break;
+case PR_REPORT_SEARCH_KEY:
+ s = "PR_REPORT_SEARCH_KEY"; break;
+case PR_ORIGINAL_DELIVERY_TIME:
+ s = "PR_ORIGINAL_DELIVERY_TIME"; break;
+case PR_ORIGINAL_AUTHOR_SEARCH_KEY:
+ s = "PR_ORIGINAL_AUTHOR_SEARCH_KEY"; break;
+
+case PR_MESSAGE_TO_ME:
+ s = "PR_MESSAGE_TO_ME"; break;
+case PR_MESSAGE_CC_ME:
+ s = "PR_MESSAGE_CC_ME"; break;
+case PR_MESSAGE_RECIP_ME:
+ s = "PR_MESSAGE_RECIP_ME"; break;
+
+case PR_ORIGINAL_SENDER_NAME:
+ s = "PR_ORIGINAL_SENDER_NAME"; break;
+case PR_ORIGINAL_SENDER_ENTRYID:
+ s = "PR_ORIGINAL_SENDER_ENTRYID"; break;
+case PR_ORIGINAL_SENDER_SEARCH_KEY:
+ s = "PR_ORIGINAL_SENDER_SEARCH_KEY"; break;
+case PR_ORIGINAL_SENT_REPRESENTING_NAME:
+ s = "PR_ORIGINAL_SENT_REPRESENTING_NAME"; break;
+case PR_ORIGINAL_SENT_REPRESENTING_ENTRYID:
+ s = "PR_ORIGINAL_SENT_REPRESENTING_ENTRYID"; break;
+case PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY:
+ s = "PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY"; break;
+
+case PR_START_DATE:
+ s = "PR_START_DATE"; break;
+case PR_END_DATE:
+ s = "PR_END_DATE"; break;
+case PR_OWNER_APPT_ID:
+ s = "PR_OWNER_APPT_ID"; break;
+case PR_RESPONSE_REQUESTED:
+ s = "PR_RESPONSE_REQUESTED"; break;
+
+case PR_SENT_REPRESENTING_ADDRTYPE:
+ s = "PR_SENT_REPRESENTING_ADDRTYPE"; break;
+case PR_SENT_REPRESENTING_EMAIL_ADDRESS:
+ s = "PR_SENT_REPRESENTING_EMAIL_ADDRESS"; break;
+
+case PR_ORIGINAL_SENDER_ADDRTYPE:
+ s = "PR_ORIGINAL_SENDER_ADDRTYPE"; break;
+case PR_ORIGINAL_SENDER_EMAIL_ADDRESS:
+ s = "PR_ORIGINAL_SENDER_EMAIL_ADDRESS"; break;
+
+case PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE:
+ s = "PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE"; break;
+case PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS:
+ s = "PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS"; break;
+
+case PR_CONVERSATION_TOPIC:
+ s = "PR_CONVERSATION_TOPIC"; break;
+case PR_CONVERSATION_INDEX:
+ s = "PR_CONVERSATION_INDEX"; break;
+
+case PR_ORIGINAL_DISPLAY_BCC:
+ s = "PR_ORIGINAL_DISPLAY_BCC"; break;
+case PR_ORIGINAL_DISPLAY_CC:
+ s = "PR_ORIGINAL_DISPLAY_CC"; break;
+case PR_ORIGINAL_DISPLAY_TO:
+ s = "PR_ORIGINAL_DISPLAY_TO"; break;
+
+case PR_RECEIVED_BY_ADDRTYPE:
+ s = "PR_RECEIVED_BY_ADDRTYPE"; break;
+case PR_RECEIVED_BY_EMAIL_ADDRESS:
+ s = "PR_RECEIVED_BY_EMAIL_ADDRESS"; break;
+
+case PR_RCVD_REPRESENTING_ADDRTYPE:
+ s = "PR_RCVD_REPRESENTING_ADDRTYPE"; break;
+case PR_RCVD_REPRESENTING_EMAIL_ADDRESS:
+ s = "PR_RCVD_REPRESENTING_EMAIL_ADDRESS"; break;
+
+case PR_ORIGINAL_AUTHOR_ADDRTYPE:
+ s = "PR_ORIGINAL_AUTHOR_ADDRTYPE"; break;
+case PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS:
+ s = "PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS"; break;
+
+case PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE:
+ s = "PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE"; break;
+case PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS:
+ s = "PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS"; break;
+
+case PR_TRANSPORT_MESSAGE_HEADERS:
+ s = "PR_TRANSPORT_MESSAGE_HEADERS"; break;
+
+case PR_DELEGATION:
+ s = "PR_DELEGATION"; break;
+
+case PR_TNEF_CORRELATION_KEY:
+ s = "PR_TNEF_CORRELATION_KEY"; break;
+
+
+
+/*
+ * Message content properties
+ */
+
+case PR_BODY:
+ s = "PR_BODY"; break;
+case PR_REPORT_TEXT:
+ s = "PR_REPORT_TEXT"; break;
+case PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY:
+ s = "PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY"; break;
+case PR_REPORTING_DL_NAME:
+ s = "PR_REPORTING_DL_NAME"; break;
+case PR_REPORTING_MTA_CERTIFICATE:
+ s = "PR_REPORTING_MTA_CERTIFICATE"; break;
+
+/* Removed PR_REPORT_ORIGIN_AUTHENTICATION_CHECK with DCR 3865, use PR_ORIGIN_CHECK */
+
+case PR_RTF_SYNC_BODY_CRC:
+ s = "PR_RTF_SYNC_BODY_CRC"; break;
+case PR_RTF_SYNC_BODY_COUNT:
+ s = "PR_RTF_SYNC_BODY_COUNT"; break;
+case PR_RTF_SYNC_BODY_TAG:
+ s = "PR_RTF_SYNC_BODY_TAG"; break;
+case PR_RTF_COMPRESSED:
+ s = "PR_RTF_COMPRESSED"; break;
+case PR_RTF_SYNC_PREFIX_COUNT:
+ s = "PR_RTF_SYNC_PREFIX_COUNT"; break;
+case PR_RTF_SYNC_TRAILING_COUNT:
+ s = "PR_RTF_SYNC_TRAILING_COUNT"; break;
+case PR_ORIGINALLY_INTENDED_RECIP_ENTRYID:
+ s = "PR_ORIGINALLY_INTENDED_RECIP_ENTRYID"; break;
+
+/*
+ * Reserved 0x1100-0x1200
+ */
+
+
+/*
+ * Message recipient properties
+ */
+
+case PR_CONTENT_INTEGRITY_CHECK:
+ s = "PR_CONTENT_INTEGRITY_CHECK"; break;
+case PR_EXPLICIT_CONVERSION:
+ s = "PR_EXPLICIT_CONVERSION"; break;
+case PR_IPM_RETURN_REQUESTED:
+ s = "PR_IPM_RETURN_REQUESTED"; break;
+case PR_MESSAGE_TOKEN:
+ s = "PR_MESSAGE_TOKEN"; break;
+case PR_NDR_REASON_CODE:
+ s = "PR_NDR_REASON_CODE"; break;
+case PR_NDR_DIAG_CODE:
+ s = "PR_NDR_DIAG_CODE"; break;
+case PR_NON_RECEIPT_NOTIFICATION_REQUESTED:
+ s = "PR_NON_RECEIPT_NOTIFICATION_REQUESTED"; break;
+case PR_DELIVERY_POINT:
+ s = "PR_DELIVERY_POINT"; break;
+
+case PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED:
+ s = "PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED"; break;
+case PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT:
+ s = "PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT"; break;
+case PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY:
+ s = "PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY"; break;
+case PR_PHYSICAL_DELIVERY_MODE:
+ s = "PR_PHYSICAL_DELIVERY_MODE"; break;
+case PR_PHYSICAL_DELIVERY_REPORT_REQUEST:
+ s = "PR_PHYSICAL_DELIVERY_REPORT_REQUEST"; break;
+case PR_PHYSICAL_FORWARDING_ADDRESS:
+ s = "PR_PHYSICAL_FORWARDING_ADDRESS"; break;
+case PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED:
+ s = "PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED"; break;
+case PR_PHYSICAL_FORWARDING_PROHIBITED:
+ s = "PR_PHYSICAL_FORWARDING_PROHIBITED"; break;
+case PR_PHYSICAL_RENDITION_ATTRIBUTES:
+ s = "PR_PHYSICAL_RENDITION_ATTRIBUTES"; break;
+case PR_PROOF_OF_DELIVERY:
+ s = "PR_PROOF_OF_DELIVERY"; break;
+case PR_PROOF_OF_DELIVERY_REQUESTED:
+ s = "PR_PROOF_OF_DELIVERY_REQUESTED"; break;
+case PR_RECIPIENT_CERTIFICATE:
+ s = "PR_RECIPIENT_CERTIFICATE"; break;
+case PR_RECIPIENT_NUMBER_FOR_ADVICE:
+ s = "PR_RECIPIENT_NUMBER_FOR_ADVICE"; break;
+case PR_RECIPIENT_TYPE:
+ s = "PR_RECIPIENT_TYPE"; break;
+case PR_REGISTERED_MAIL_TYPE:
+ s = "PR_REGISTERED_MAIL_TYPE"; break;
+case PR_REPLY_REQUESTED:
+ s = "PR_REPLY_REQUESTED"; break;
+case PR_REQUESTED_DELIVERY_METHOD:
+ s = "PR_REQUESTED_DELIVERY_METHOD"; break;
+case PR_SENDER_ENTRYID:
+ s = "PR_SENDER_ENTRYID"; break;
+case PR_SENDER_NAME:
+ s = "PR_SENDER_NAME"; break;
+case PR_SUPPLEMENTARY_INFO:
+ s = "PR_SUPPLEMENTARY_INFO"; break;
+case PR_TYPE_OF_MTS_USER:
+ s = "PR_TYPE_OF_MTS_USER"; break;
+case PR_SENDER_SEARCH_KEY:
+ s = "PR_SENDER_SEARCH_KEY"; break;
+case PR_SENDER_ADDRTYPE:
+ s = "PR_SENDER_ADDRTYPE"; break;
+case PR_SENDER_EMAIL_ADDRESS:
+ s = "PR_SENDER_EMAIL_ADDRESS"; break;
+
+/*
+ * Message non-transmittable properties
+ */
+
+/*
+ * The two tags, PR_MESSAGE_RECIPIENTS and PR_MESSAGE_ATTACHMENTS,
+ * are to be used in the exclude list passed to
+ * IMessage::CopyTo when the caller wants either the recipients or attachments
+ * of the message to not get copied. It is also used in the ProblemArray
+ * return from IMessage::CopyTo when an error is encountered copying them
+ */
+
+case PR_CURRENT_VERSION:
+ s = "PR_CURRENT_VERSION"; break;
+case PR_DELETE_AFTER_SUBMIT:
+ s = "PR_DELETE_AFTER_SUBMIT"; break;
+case PR_DISPLAY_BCC:
+ s = "PR_DISPLAY_BCC"; break;
+case PR_DISPLAY_CC:
+ s = "PR_DISPLAY_CC"; break;
+case PR_DISPLAY_TO:
+ s = "PR_DISPLAY_TO"; break;
+case PR_PARENT_DISPLAY:
+ s = "PR_PARENT_DISPLAY"; break;
+case PR_MESSAGE_DELIVERY_TIME:
+ s = "PR_MESSAGE_DELIVERY_TIME"; break;
+case PR_MESSAGE_FLAGS:
+ s = "PR_MESSAGE_FLAGS"; break;
+case PR_MESSAGE_SIZE:
+ s = "PR_MESSAGE_SIZE"; break;
+case PR_PARENT_ENTRYID:
+ s = "PR_PARENT_ENTRYID"; break;
+case PR_SENTMAIL_ENTRYID:
+ s = "PR_SENTMAIL_ENTRYID"; break;
+case PR_CORRELATE:
+ s = "PR_CORRELATE"; break;
+case PR_CORRELATE_MTSID:
+ s = "PR_CORRELATE_MTSID"; break;
+case PR_DISCRETE_VALUES:
+ s = "PR_DISCRETE_VALUES"; break;
+case PR_RESPONSIBILITY:
+ s = "PR_RESPONSIBILITY"; break;
+case PR_SPOOLER_STATUS:
+ s = "PR_SPOOLER_STATUS"; break;
+case PR_TRANSPORT_STATUS:
+ s = "PR_TRANSPORT_STATUS"; break;
+case PR_MESSAGE_RECIPIENTS:
+ s = "PR_MESSAGE_RECIPIENTS"; break;
+case PR_MESSAGE_ATTACHMENTS:
+ s = "PR_MESSAGE_ATTACHMENTS"; break;
+case PR_SUBMIT_FLAGS:
+ s = "PR_SUBMIT_FLAGS"; break;
+case PR_RECIPIENT_STATUS:
+ s = "PR_RECIPIENT_STATUS"; break;
+case PR_TRANSPORT_KEY:
+ s = "PR_TRANSPORT_KEY"; break;
+case PR_MSG_STATUS:
+ s = "PR_MSG_STATUS"; break;
+case PR_MESSAGE_DOWNLOAD_TIME:
+ s = "PR_MESSAGE_DOWNLOAD_TIME"; break;
+case PR_CREATION_VERSION:
+ s = "PR_CREATION_VERSION"; break;
+case PR_MODIFY_VERSION:
+ s = "PR_MODIFY_VERSION"; break;
+case PR_HASATTACH:
+ s = "PR_HASATTACH"; break;
+case PR_BODY_CRC:
+ s = "PR_BODY_CRC"; break;
+case PR_NORMALIZED_SUBJECT:
+ s = "PR_NORMALIZED_SUBJECT"; break;
+case PR_RTF_IN_SYNC:
+ s = "PR_RTF_IN_SYNC"; break;
+case PR_ATTACH_SIZE:
+ s = "PR_ATTACH_SIZE"; break;
+case PR_ATTACH_NUM:
+ s = "PR_ATTACH_NUM"; break;
+case PR_PREPROCESS:
+ s = "PR_PREPROCESS"; break;
+
+/* PR_ORIGINAL_DISPLAY_TO, _CC, and _BCC moved to transmittible range 03/09/95 */
+
+case PR_ORIGINATING_MTA_CERTIFICATE:
+ s = "PR_ORIGINATING_MTA_CERTIFICATE"; break;
+case PR_PROOF_OF_SUBMISSION:
+ s = "PR_PROOF_OF_SUBMISSION"; break;
+
+
+/*
+ * The range of non-message and non-recipient property IDs (0x3000 - 0x3FFF) is
+ * further broken down into ranges to make assigning new property IDs easier.
+ *
+ * From To Kind of property
+ * --------------------------------
+ * 3000 32FF MAPI_defined common property
+ * 3200 33FF MAPI_defined form property
+ * 3400 35FF MAPI_defined message store property
+ * 3600 36FF MAPI_defined Folder or AB Container property
+ * 3700 38FF MAPI_defined attachment property
+ * 3900 39FF MAPI_defined address book property
+ * 3A00 3BFF MAPI_defined mailuser property
+ * 3C00 3CFF MAPI_defined DistList property
+ * 3D00 3DFF MAPI_defined Profile Section property
+ * 3E00 3EFF MAPI_defined Status property
+ * 3F00 3FFF MAPI_defined display table property
+ */
+
+/*
+ * Properties common to numerous MAPI objects.
+ *
+ * Those properties that can appear on messages are in the
+ * non-transmittable range for messages. They start at the high
+ * end of that range and work down.
+ *
+ * Properties that never appear on messages are defined in the common
+ * property range (see above).
+ */
+
+/*
+ * properties that are common to multiple objects (including message objects)
+ * -- these ids are in the non-transmittable range
+ */
+
+case PR_ENTRYID:
+ s = "PR_ENTRYID"; break;
+case PR_OBJECT_TYPE:
+ s = "PR_OBJECT_TYPE"; break;
+case PR_ICON:
+ s = "PR_ICON"; break;
+case PR_MINI_ICON:
+ s = "PR_MINI_ICON"; break;
+case PR_STORE_ENTRYID:
+ s = "PR_STORE_ENTRYID"; break;
+case PR_STORE_RECORD_KEY:
+ s = "PR_STORE_RECORD_KEY"; break;
+case PR_RECORD_KEY:
+ s = "PR_RECORD_KEY"; break;
+case PR_MAPPING_SIGNATURE:
+ s = "PR_MAPPING_SIGNATURE"; break;
+case PR_ACCESS_LEVEL:
+ s = "PR_ACCESS_LEVEL"; break;
+case PR_INSTANCE_KEY:
+ s = "PR_INSTANCE_KEY"; break;
+case PR_ROW_TYPE:
+ s = "PR_ROW_TYPE"; break;
+case PR_ACCESS:
+ s = "PR_ACCESS"; break;
+
+/*
+ * properties that are common to multiple objects (usually not including message objects)
+ * -- these ids are in the transmittable range
+ */
+
+case PR_ROWID:
+ s = "PR_ROWID"; break;
+case PR_DISPLAY_NAME:
+ s = "PR_DISPLAY_NAME"; break;
+case PR_ADDRTYPE:
+ s = "PR_ADDRTYPE"; break;
+case PR_EMAIL_ADDRESS:
+ s = "PR_EMAIL_ADDRESS"; break;
+case PR_COMMENT:
+ s = "PR_COMMENT"; break;
+case PR_DEPTH:
+ s = "PR_DEPTH"; break;
+case PR_PROVIDER_DISPLAY:
+ s = "PR_PROVIDER_DISPLAY"; break;
+case PR_CREATION_TIME:
+ s = "PR_CREATION_TIME"; break;
+case PR_LAST_MODIFICATION_TIME:
+ s = "PR_LAST_MODIFICATION_TIME"; break;
+case PR_RESOURCE_FLAGS:
+ s = "PR_RESOURCE_FLAGS"; break;
+case PR_PROVIDER_DLL_NAME:
+ s = "PR_PROVIDER_DLL_NAME"; break;
+case PR_SEARCH_KEY:
+ s = "PR_SEARCH_KEY"; break;
+case PR_PROVIDER_UID:
+ s = "PR_PROVIDER_UID"; break;
+case PR_PROVIDER_ORDINAL:
+ s = "PR_PROVIDER_ORDINAL"; break;
+
+/*
+ * MAPI Form properties
+ */
+case PR_FORM_VERSION:
+ s = "PR_FORM_VERSION"; break;
+case PR_FORM_CLSID:
+ s = "PR_FORM_CLSID"; break;
+case PR_FORM_CONTACT_NAME:
+ s = "PR_FORM_CONTACT_NAME"; break;
+case PR_FORM_CATEGORY:
+ s = "PR_FORM_CATEGORY"; break;
+case PR_FORM_CATEGORY_SUB:
+ s = "PR_FORM_CATEGORY_SUB"; break;
+case PR_FORM_HOST_MAP:
+ s = "PR_FORM_HOST_MAP"; break;
+case PR_FORM_HIDDEN:
+ s = "PR_FORM_HIDDEN"; break;
+case PR_FORM_DESIGNER_NAME:
+ s = "PR_FORM_DESIGNER_NAME"; break;
+case PR_FORM_DESIGNER_GUID:
+ s = "PR_FORM_DESIGNER_GUID"; break;
+case PR_FORM_MESSAGE_BEHAVIOR:
+ s = "PR_FORM_MESSAGE_BEHAVIOR"; break;
+
+/*
+ * Message store properties
+ */
+
+case PR_DEFAULT_STORE:
+ s = "PR_DEFAULT_STORE"; break;
+case PR_STORE_SUPPORT_MASK:
+ s = "PR_STORE_SUPPORT_MASK"; break;
+case PR_STORE_STATE:
+ s = "PR_STORE_STATE"; break;
+
+case PR_IPM_SUBTREE_SEARCH_KEY:
+ s = "PR_IPM_SUBTREE_SEARCH_KEY"; break;
+case PR_IPM_OUTBOX_SEARCH_KEY:
+ s = "PR_IPM_OUTBOX_SEARCH_KEY"; break;
+case PR_IPM_WASTEBASKET_SEARCH_KEY:
+ s = "PR_IPM_WASTEBASKET_SEARCH_KEY"; break;
+case PR_IPM_SENTMAIL_SEARCH_KEY:
+ s = "PR_IPM_SENTMAIL_SEARCH_KEY"; break;
+case PR_MDB_PROVIDER:
+ s = "PR_MDB_PROVIDER"; break;
+case PR_RECEIVE_FOLDER_SETTINGS:
+ s = "PR_RECEIVE_FOLDER_SETTINGS"; break;
+
+case PR_VALID_FOLDER_MASK:
+ s = "PR_VALID_FOLDER_MASK"; break;
+case PR_IPM_SUBTREE_ENTRYID:
+ s = "PR_IPM_SUBTREE_ENTRYID"; break;
+
+case PR_IPM_OUTBOX_ENTRYID:
+ s = "PR_IPM_OUTBOX_ENTRYID"; break;
+case PR_IPM_WASTEBASKET_ENTRYID:
+ s = "PR_IPM_WASTEBASKET_ENTRYID"; break;
+case PR_IPM_SENTMAIL_ENTRYID:
+ s = "PR_IPM_SENTMAIL_ENTRYID"; break;
+case PR_VIEWS_ENTRYID:
+ s = "PR_VIEWS_ENTRYID"; break;
+case PR_COMMON_VIEWS_ENTRYID:
+ s = "PR_COMMON_VIEWS_ENTRYID"; break;
+case PR_FINDER_ENTRYID:
+ s = "PR_FINDER_ENTRYID"; break;
+
+/* Proptags 0x35E8-0x35FF reserved for folders "guaranteed" by PR_VALID_FOLDER_MASK */
+
+
+/*
+ * Folder and AB Container properties
+ */
+
+case PR_CONTAINER_FLAGS:
+ s = "PR_CONTAINER_FLAGS"; break;
+case PR_FOLDER_TYPE:
+ s = "PR_FOLDER_TYPE"; break;
+case PR_CONTENT_COUNT:
+ s = "PR_CONTENT_COUNT"; break;
+case PR_CONTENT_UNREAD:
+ s = "PR_CONTENT_UNREAD"; break;
+case PR_CREATE_TEMPLATES:
+ s = "PR_CREATE_TEMPLATES"; break;
+case PR_DETAILS_TABLE:
+ s = "PR_DETAILS_TABLE"; break;
+case PR_SEARCH:
+ s = "PR_SEARCH"; break;
+case PR_SELECTABLE:
+ s = "PR_SELECTABLE"; break;
+case PR_SUBFOLDERS:
+ s = "PR_SUBFOLDERS"; break;
+case PR_STATUS:
+ s = "PR_STATUS"; break;
+case PR_ANR:
+ s = "PR_ANR"; break;
+case PR_CONTENTS_SORT_ORDER:
+ s = "PR_CONTENTS_SORT_ORDER"; break;
+case PR_CONTAINER_HIERARCHY:
+ s = "PR_CONTAINER_HIERARCHY"; break;
+case PR_CONTAINER_CONTENTS:
+ s = "PR_CONTAINER_CONTENTS"; break;
+case PR_FOLDER_ASSOCIATED_CONTENTS:
+ s = "PR_FOLDER_ASSOCIATED_CONTENTS"; break;
+case PR_DEF_CREATE_DL:
+ s = "PR_DEF_CREATE_DL"; break;
+case PR_DEF_CREATE_MAILUSER:
+ s = "PR_DEF_CREATE_MAILUSER"; break;
+case PR_CONTAINER_CLASS:
+ s = "PR_CONTAINER_CLASS"; break;
+case PR_CONTAINER_MODIFY_VERSION:
+ s = "PR_CONTAINER_MODIFY_VERSION"; break;
+case PR_AB_PROVIDER_ID:
+ s = "PR_AB_PROVIDER_ID"; break;
+case PR_DEFAULT_VIEW_ENTRYID:
+ s = "PR_DEFAULT_VIEW_ENTRYID"; break;
+case PR_ASSOC_CONTENT_COUNT:
+ s = "PR_ASSOC_CONTENT_COUNT"; break;
+
+/* Reserved 0x36C0-0x36FF */
+
+/*
+ * Attachment properties
+ */
+
+case PR_ATTACHMENT_X400_PARAMETERS:
+ s = "PR_ATTACHMENT_X400_PARAMETERS"; break;
+case PR_ATTACH_DATA_OBJ:
+ s = "PR_ATTACH_DATA_OBJ"; break;
+case PR_ATTACH_DATA_BIN:
+ s = "PR_ATTACH_DATA_BIN"; break;
+case PR_ATTACH_ENCODING:
+ s = "PR_ATTACH_ENCODING"; break;
+case PR_ATTACH_EXTENSION:
+ s = "PR_ATTACH_EXTENSION"; break;
+case PR_ATTACH_FILENAME:
+ s = "PR_ATTACH_FILENAME"; break;
+case PR_ATTACH_METHOD:
+ s = "PR_ATTACH_METHOD"; break;
+case PR_ATTACH_LONG_FILENAME:
+ s = "PR_ATTACH_LONG_FILENAME"; break;
+case PR_ATTACH_PATHNAME:
+ s = "PR_ATTACH_PATHNAME"; break;
+case PR_ATTACH_RENDERING:
+ s = "PR_ATTACH_RENDERING"; break;
+case PR_ATTACH_TAG:
+ s = "PR_ATTACH_TAG"; break;
+case PR_RENDERING_POSITION:
+ s = "PR_RENDERING_POSITION"; break;
+case PR_ATTACH_TRANSPORT_NAME:
+ s = "PR_ATTACH_TRANSPORT_NAME"; break;
+case PR_ATTACH_LONG_PATHNAME:
+ s = "PR_ATTACH_LONG_PATHNAME"; break;
+case PR_ATTACH_MIME_TAG:
+ s = "PR_ATTACH_MIME_TAG"; break;
+case PR_ATTACH_ADDITIONAL_INFO:
+ s = "PR_ATTACH_ADDITIONAL_INFO"; break;
+
+/*
+ * AB Object properties
+ */
+
+case PR_DISPLAY_TYPE:
+ s = "PR_DISPLAY_TYPE"; break;
+case PR_TEMPLATEID:
+ s = "PR_TEMPLATEID"; break;
+case PR_PRIMARY_CAPABILITY:
+ s = "PR_PRIMARY_CAPABILITY"; break;
+
+
+/*
+ * Mail user properties
+ */
+case PR_7BIT_DISPLAY_NAME:
+ s = "PR_7BIT_DISPLAY_NAME"; break;
+case PR_ACCOUNT:
+ s = "PR_ACCOUNT"; break;
+case PR_ALTERNATE_RECIPIENT:
+ s = "PR_ALTERNATE_RECIPIENT"; break;
+case PR_CALLBACK_TELEPHONE_NUMBER:
+ s = "PR_CALLBACK_TELEPHONE_NUMBER"; break;
+case PR_CONVERSION_PROHIBITED:
+ s = "PR_CONVERSION_PROHIBITED"; break;
+case PR_DISCLOSE_RECIPIENTS:
+ s = "PR_DISCLOSE_RECIPIENTS"; break;
+case PR_GENERATION:
+ s = "PR_GENERATION"; break;
+case PR_GIVEN_NAME:
+ s = "PR_GIVEN_NAME"; break;
+case PR_GOVERNMENT_ID_NUMBER:
+ s = "PR_GOVERNMENT_ID_NUMBER"; break;
+case PR_BUSINESS_TELEPHONE_NUMBER:
+ s = "PR_BUSINESS_TELEPHONE_NUMBER or PR_OFFICE_TELEPHONE_NUMBER"; break;
+case PR_HOME_TELEPHONE_NUMBER:
+ s = "PR_HOME_TELEPHONE_NUMBER"; break;
+case PR_INITIALS:
+ s = "PR_INITIALS"; break;
+case PR_KEYWORD:
+ s = "PR_KEYWORD"; break;
+case PR_LANGUAGE:
+ s = "PR_LANGUAGE"; break;
+case PR_LOCATION:
+ s = "PR_LOCATION"; break;
+case PR_MAIL_PERMISSION:
+ s = "PR_MAIL_PERMISSION"; break;
+case PR_MHS_COMMON_NAME:
+ s = "PR_MHS_COMMON_NAME"; break;
+case PR_ORGANIZATIONAL_ID_NUMBER:
+ s = "PR_ORGANIZATIONAL_ID_NUMBER"; break;
+case PR_SURNAME:
+ s = "PR_SURNAME"; break;
+case PR_ORIGINAL_ENTRYID:
+ s = "PR_ORIGINAL_ENTRYID"; break;
+case PR_ORIGINAL_DISPLAY_NAME:
+ s = "PR_ORIGINAL_DISPLAY_NAME"; break;
+case PR_ORIGINAL_SEARCH_KEY:
+ s = "PR_ORIGINAL_SEARCH_KEY"; break;
+case PR_POSTAL_ADDRESS:
+ s = "PR_POSTAL_ADDRESS"; break;
+case PR_COMPANY_NAME:
+ s = "PR_COMPANY_NAME"; break;
+case PR_TITLE:
+ s = "PR_TITLE"; break;
+case PR_DEPARTMENT_NAME:
+ s = "PR_DEPARTMENT_NAME"; break;
+case PR_OFFICE_LOCATION:
+ s = "PR_OFFICE_LOCATION"; break;
+case PR_PRIMARY_TELEPHONE_NUMBER:
+ s = "PR_PRIMARY_TELEPHONE_NUMBER"; break;
+case PR_BUSINESS2_TELEPHONE_NUMBER:
+ s = "PR_BUSINESS2_TELEPHONE_NUMBER or PR_OFFICE2_TELEPHONE_NUMBER"; break;
+case PR_MOBILE_TELEPHONE_NUMBER:
+ s = "PR_MOBILE_TELEPHONE_NUMBER or PR_CELLULAR_TELEPHONE_NUMBER"; break;
+case PR_RADIO_TELEPHONE_NUMBER:
+ s = "PR_RADIO_TELEPHONE_NUMBER"; break;
+case PR_CAR_TELEPHONE_NUMBER:
+ s = "PR_CAR_TELEPHONE_NUMBER"; break;
+case PR_OTHER_TELEPHONE_NUMBER:
+ s = "PR_OTHER_TELEPHONE_NUMBER"; break;
+case PR_TRANSMITABLE_DISPLAY_NAME:
+ s = "PR_TRANSMITABLE_DISPLAY_NAME"; break;
+case PR_PAGER_TELEPHONE_NUMBER:
+ s = "PR_PAGER_TELEPHONE_NUMBER or PR_BEEPER_TELEPHONE_NUMBER"; break;
+case PR_USER_CERTIFICATE:
+ s = "PR_USER_CERTIFICATE"; break;
+case PR_PRIMARY_FAX_NUMBER:
+ s = "PR_PRIMARY_FAX_NUMBER"; break;
+case PR_BUSINESS_FAX_NUMBER:
+ s = "PR_BUSINESS_FAX_NUMBER"; break;
+case PR_HOME_FAX_NUMBER:
+ s = "PR_HOME_FAX_NUMBER"; break;
+case PR_COUNTRY:
+ s = "PR_COUNTRY or PR_BUSINESS_ADDRESS_COUNTRY"; break;
+
+case PR_LOCALITY:
+ s = "PR_LOCALITY or PR_BUSINESS_ADDRESS_CITY"; break;
+
+case PR_STATE_OR_PROVINCE:
+ s = "PR_STATE_OR_PROVINCE or PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE"; break;
+
+case PR_STREET_ADDRESS:
+ s = "PR_STREET_ADDRESS or PR_BUSINESS_ADDRESS_STREET"; break;
+
+case PR_POSTAL_CODE:
+ s = "PR_POSTAL_CODE or PR_BUSINESS_ADDRESS_POSTAL_CODE"; break;
+
+
+case PR_POST_OFFICE_BOX:
+ s = "PR_POST_OFFICE_BOX or PR_BUSINESS_ADDRESS_POST_OFFICE_BOX"; break;
+
+
+case PR_TELEX_NUMBER:
+ s = "PR_TELEX_NUMBER"; break;
+case PR_ISDN_NUMBER:
+ s = "PR_ISDN_NUMBER"; break;
+case PR_ASSISTANT_TELEPHONE_NUMBER:
+ s = "PR_ASSISTANT_TELEPHONE_NUMBER"; break;
+case PR_HOME2_TELEPHONE_NUMBER:
+ s = "PR_HOME2_TELEPHONE_NUMBER"; break;
+case PR_ASSISTANT:
+ s = "PR_ASSISTANT"; break;
+case PR_SEND_RICH_INFO:
+ s = "PR_SEND_RICH_INFO"; break;
+
+case PR_WEDDING_ANNIVERSARY:
+ s = "PR_WEDDING_ANNIVERSARY"; break;
+case PR_BIRTHDAY:
+ s = "PR_BIRTHDAY"; break;
+
+
+case PR_HOBBIES:
+ s = "PR_HOBBIES"; break;
+
+case PR_MIDDLE_NAME:
+ s = "PR_MIDDLE_NAME"; break;
+
+case PR_DISPLAY_NAME_PREFIX:
+ s = "PR_DISPLAY_NAME_PREFIX"; break;
+
+case PR_PROFESSION:
+ s = "PR_PROFESSION"; break;
+
+case PR_PREFERRED_BY_NAME:
+ s = "PR_PREFERRED_BY_NAME"; break;
+
+case PR_SPOUSE_NAME:
+ s = "PR_SPOUSE_NAME"; break;
+
+case PR_COMPUTER_NETWORK_NAME:
+ s = "PR_COMPUTER_NETWORK_NAME"; break;
+
+case PR_CUSTOMER_ID:
+ s = "PR_CUSTOMER_ID"; break;
+
+case PR_TTYTDD_PHONE_NUMBER:
+ s = "PR_TTYTDD_PHONE_NUMBER"; break;
+
+case PR_FTP_SITE:
+ s = "PR_FTP_SITE"; break;
+
+case PR_GENDER:
+ s = "PR_GENDER"; break;
+
+case PR_MANAGER_NAME:
+ s = "PR_MANAGER_NAME"; break;
+
+case PR_NICKNAME:
+ s = "PR_NICKNAME"; break;
+
+case PR_PERSONAL_HOME_PAGE:
+ s = "PR_PERSONAL_HOME_PAGE"; break;
+
+
+case PR_BUSINESS_HOME_PAGE:
+ s = "PR_BUSINESS_HOME_PAGE"; break;
+
+case PR_CONTACT_VERSION:
+ s = "PR_CONTACT_VERSION"; break;
+case PR_CONTACT_ENTRYIDS:
+ s = "PR_CONTACT_ENTRYIDS"; break;
+
+case PR_CONTACT_ADDRTYPES:
+ s = "PR_CONTACT_ADDRTYPES"; break;
+
+case PR_CONTACT_DEFAULT_ADDRESS_INDEX:
+ s = "PR_CONTACT_DEFAULT_ADDRESS_INDEX"; break;
+
+case PR_CONTACT_EMAIL_ADDRESSES:
+ s = "PR_CONTACT_EMAIL_ADDRESSES"; break;
+
+
+case PR_COMPANY_MAIN_PHONE_NUMBER:
+ s = "PR_COMPANY_MAIN_PHONE_NUMBER"; break;
+
+case PR_CHILDRENS_NAMES:
+ s = "PR_CHILDRENS_NAMES"; break;
+
+
+
+case PR_HOME_ADDRESS_CITY:
+ s = "PR_HOME_ADDRESS_CITY"; break;
+
+case PR_HOME_ADDRESS_COUNTRY:
+ s = "PR_HOME_ADDRESS_COUNTRY"; break;
+
+case PR_HOME_ADDRESS_POSTAL_CODE:
+ s = "PR_HOME_ADDRESS_POSTAL_CODE"; break;
+
+case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
+ s = "PR_HOME_ADDRESS_STATE_OR_PROVINCE"; break;
+
+case PR_HOME_ADDRESS_STREET:
+ s = "PR_HOME_ADDRESS_STREET"; break;
+
+case PR_HOME_ADDRESS_POST_OFFICE_BOX:
+ s = "PR_HOME_ADDRESS_POST_OFFICE_BOX"; break;
+
+case PR_OTHER_ADDRESS_CITY:
+ s = "PR_OTHER_ADDRESS_CITY"; break;
+
+case PR_OTHER_ADDRESS_COUNTRY:
+ s = "PR_OTHER_ADDRESS_COUNTRY"; break;
+
+case PR_OTHER_ADDRESS_POSTAL_CODE:
+ s = "PR_OTHER_ADDRESS_POSTAL_CODE"; break;
+
+case PR_OTHER_ADDRESS_STATE_OR_PROVINCE:
+ s = "PR_OTHER_ADDRESS_STATE_OR_PROVINCE"; break;
+
+case PR_OTHER_ADDRESS_STREET:
+ s = "PR_OTHER_ADDRESS_STREET"; break;
+
+case PR_OTHER_ADDRESS_POST_OFFICE_BOX:
+ s = "PR_OTHER_ADDRESS_POST_OFFICE_BOX"; break;
+
+
+/*
+ * Profile section properties
+ */
+
+case PR_STORE_PROVIDERS:
+ s = "PR_STORE_PROVIDERS"; break;
+case PR_AB_PROVIDERS:
+ s = "PR_AB_PROVIDERS"; break;
+case PR_TRANSPORT_PROVIDERS:
+ s = "PR_TRANSPORT_PROVIDERS"; break;
+
+case PR_DEFAULT_PROFILE:
+ s = "PR_DEFAULT_PROFILE"; break;
+case PR_AB_SEARCH_PATH:
+ s = "PR_AB_SEARCH_PATH"; break;
+case PR_AB_DEFAULT_DIR:
+ s = "PR_AB_DEFAULT_DIR"; break;
+case PR_AB_DEFAULT_PAB:
+ s = "PR_AB_DEFAULT_PAB"; break;
+
+case PR_FILTERING_HOOKS:
+ s = "PR_FILTERING_HOOKS"; break;
+case PR_SERVICE_NAME:
+ s = "PR_SERVICE_NAME"; break;
+case PR_SERVICE_DLL_NAME:
+ s = "PR_SERVICE_DLL_NAME"; break;
+case PR_SERVICE_ENTRY_NAME:
+ s = "PR_SERVICE_ENTRY_NAME"; break;
+case PR_SERVICE_UID:
+ s = "PR_SERVICE_UID"; break;
+case PR_SERVICE_EXTRA_UIDS:
+ s = "PR_SERVICE_EXTRA_UIDS"; break;
+case PR_SERVICES:
+ s = "PR_SERVICES"; break;
+case PR_SERVICE_SUPPORT_FILES:
+ s = "PR_SERVICE_SUPPORT_FILES"; break;
+case PR_SERVICE_DELETE_FILES:
+ s = "PR_SERVICE_DELETE_FILES"; break;
+case PR_AB_SEARCH_PATH_UPDATE:
+ s = "PR_AB_SEARCH_PATH_UPDATE"; break;
+case PR_PROFILE_NAME:
+ s = "PR_PROFILE_NAME"; break;
+
+/*
+ * Status object properties
+ */
+
+case PR_IDENTITY_DISPLAY:
+ s = "PR_IDENTITY_DISPLAY"; break;
+case PR_IDENTITY_ENTRYID:
+ s = "PR_IDENTITY_ENTRYID"; break;
+case PR_RESOURCE_METHODS:
+ s = "PR_RESOURCE_METHODS"; break;
+case PR_RESOURCE_TYPE:
+ s = "PR_RESOURCE_TYPE"; break;
+case PR_STATUS_CODE:
+ s = "PR_STATUS_CODE"; break;
+case PR_IDENTITY_SEARCH_KEY:
+ s = "PR_IDENTITY_SEARCH_KEY"; break;
+case PR_OWN_STORE_ENTRYID:
+ s = "PR_OWN_STORE_ENTRYID"; break;
+case PR_RESOURCE_PATH:
+ s = "PR_RESOURCE_PATH"; break;
+case PR_STATUS_STRING:
+ s = "PR_STATUS_STRING"; break;
+case PR_X400_DEFERRED_DELIVERY_CANCEL:
+ s = "PR_X400_DEFERRED_DELIVERY_CANCEL"; break;
+case PR_HEADER_FOLDER_ENTRYID:
+ s = "PR_HEADER_FOLDER_ENTRYID"; break;
+case PR_REMOTE_PROGRESS:
+ s = "PR_REMOTE_PROGRESS"; break;
+case PR_REMOTE_PROGRESS_TEXT:
+ s = "PR_REMOTE_PROGRESS_TEXT"; break;
+case PR_REMOTE_VALIDATE_OK:
+ s = "PR_REMOTE_VALIDATE_OK"; break;
+
+/*
+ * Display table properties
+ */
+
+case PR_CONTROL_FLAGS:
+ s = "PR_CONTROL_FLAGS"; break;
+case PR_CONTROL_STRUCTURE:
+ s = "PR_CONTROL_STRUCTURE"; break;
+case PR_CONTROL_TYPE:
+ s = "PR_CONTROL_TYPE"; break;
+case PR_DELTAX:
+ s = "PR_DELTAX"; break;
+case PR_DELTAY:
+ s = "PR_DELTAY"; break;
+case PR_XPOS:
+ s = "PR_XPOS"; break;
+case PR_YPOS:
+ s = "PR_YPOS"; break;
+case PR_CONTROL_ID:
+ s = "PR_CONTROL_ID"; break;
+case PR_INITIAL_DETAILS_PANE:
+ s = "PR_INITIAL_DETAILS_PANE"; break;
+/*
+ * Secure property id range
+ */
+
+case PROP_ID_SECURE_MIN:
+ s = "PROP_ID_SECURE_MIN"; break;
+case PROP_ID_SECURE_MAX:
+ s = "PROP_ID_SECURE_MAX"; break;
diff --git a/mailnews/import/outlook/src/OutlookDebugLog.h b/mailnews/import/outlook/src/OutlookDebugLog.h
new file mode 100644
index 000000000..5b189bf9b
--- /dev/null
+++ b/mailnews/import/outlook/src/OutlookDebugLog.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef OutlookDebugLog_h___
+#define OutlookDebugLog_h___
+
+#ifdef NS_DEBUG
+#define IMPORT_DEBUG 1
+#endif
+
+// Use PR_LOG for logging.
+#include "mozilla/Logging.h"
+extern PRLogModuleInfo *OUTLOOKLOGMODULE; // Logging module
+
+#define IMPORT_LOG0(x) MOZ_LOG(OUTLOOKLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(OUTLOOKLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(OUTLOOKLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(OUTLOOKLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+
+
+#endif /* OutlookDebugLog_h___ */
diff --git a/mailnews/import/outlook/src/moz.build b/mailnews/import/outlook/src/moz.build
new file mode 100644
index 000000000..4ffbad572
--- /dev/null
+++ b/mailnews/import/outlook/src/moz.build
@@ -0,0 +1,24 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'MapiApi.cpp',
+ 'MapiMessage.cpp',
+ 'MapiMimeTypes.cpp',
+ 'nsOutlookCompose.cpp',
+ 'nsOutlookImport.cpp',
+ 'nsOutlookMail.cpp',
+ 'nsOutlookSettings.cpp',
+ 'nsOutlookStringBundle.cpp',
+ 'rtfDecoder.cpp',
+ 'rtfMailDecoder.cpp',
+]
+
+FINAL_LIBRARY = 'import'
+
+LOCAL_INCLUDES += [
+ '../../src'
+]
+
diff --git a/mailnews/import/outlook/src/nsOutlookCompose.cpp b/mailnews/import/outlook/src/nsOutlookCompose.cpp
new file mode 100644
index 000000000..96c0824aa
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookCompose.cpp
@@ -0,0 +1,814 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "prthread.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIIOService.h"
+#include "nsIURI.h"
+#include "nsMsgI18N.h"
+#include "nsINetUtil.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsMsgAttachmentData.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsIArray.h"
+#include "nsIMsgCompose.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgSend.h"
+#include "nsIMutableArray.h"
+#include "nsImportEmbeddedImageData.h"
+#include "nsNetCID.h"
+#include "nsCRT.h"
+#include "nsOutlookCompose.h"
+
+#include "OutlookDebugLog.h"
+
+#include "nsMimeTypes.h"
+#include "nsMsgUtils.h"
+
+#include "nsAutoPtr.h"
+
+#include "nsMsgMessageFlags.h"
+#include "nsMsgLocalFolderHdrs.h"
+
+
+static NS_DEFINE_CID(kMsgSendCID, NS_MSGSEND_CID);
+static NS_DEFINE_CID(kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID);
+
+// We need to do some calculations to set these numbers to something reasonable!
+// Unless of course, CreateAndSendMessage will NEVER EVER leave us in the lurch
+#define kHungCount 100000
+#define kHungAbortCount 1000
+
+#ifdef IMPORT_DEBUG
+static const char *p_test_headers =
+"Received: from netppl.invalid (IDENT:monitor@get.freebsd.because.microsoftsucks.invalid [209.3.31.115])\n\
+ by mail4.sirius.invalid (8.9.1/8.9.1) with SMTP id PAA27232;\n\
+ Mon, 17 May 1999 15:27:43 -0700 (PDT)\n\
+Message-ID: <ikGD3jRTsKklU.Ggm2HmE2A1Jsqd0p@netppl.invalid>\n\
+From: \"adsales@qualityservice.invalid\" <adsales@qualityservice.invalid>\n\
+Subject: Re: Your College Diploma (36822)\n\
+Date: Mon, 17 May 1999 15:09:29 -0400 (EDT)\n\
+MIME-Version: 1.0\n\
+Content-Type: TEXT/PLAIN; charset=\"US-ASCII\"\n\
+Content-Transfer-Encoding: 7bit\n\
+X-UIDL: 19990517.152941\n\
+Status: RO";
+
+static const char *p_test_body =
+"Hello world?\n\
+";
+#else
+#define p_test_headers nullptr
+#define p_test_body nullptr
+#endif
+
+#define kWhitespace "\b\t\r\n "
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// A replacement for SimpleBufferTonyRCopiedTwice round-robin buffer and ReadFileState classes
+class CCompositionFile {
+public:
+ // fifoBuffer is used for memory allocation optimization
+ // convertCRs controls if we want to convert standalone CRs to CRLFs
+ CCompositionFile(nsIFile* aFile, void* fifoBuffer, uint32_t fifoBufferSize, bool convertCRs=false);
+
+ operator bool() const { return m_fileSize && m_pInputStream; }
+
+ // Reads up to and including the term sequence, or entire file if term isn't found
+ // termSize may be used to include NULLs in the terminator sequences.
+ // termSize value of -1 means "zero-terminated string" -> size is calculated with strlen
+ nsresult ToString(nsCString& dest, const char* term=0, int termSize=-1);
+ nsresult ToStream(nsIOutputStream *dest, const char* term=0, int termSize=-1);
+ char LastChar() { return m_lastChar; }
+private:
+ nsCOMPtr<nsIFile> m_pFile;
+ nsCOMPtr<nsIInputStream> m_pInputStream;
+ int64_t m_fileSize;
+ int64_t m_fileReadPos;
+ char* m_fifoBuffer;
+ uint32_t m_fifoBufferSize;
+ char* m_fifoBufferReadPos; // next character to read
+ char* m_fifoBufferWrittenPos; // if we have read less than buffer size then this will show it
+ bool m_convertCRs;
+ char m_lastChar;
+
+ nsresult EnsureHasDataInBuffer();
+ template <class _OutFn> nsresult ToDest(_OutFn dest, const char* term, int termSize);
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// First off, a listener
+class OutlookSendListener : public nsIMsgSendListener
+{
+public:
+ OutlookSendListener() {
+ m_done = false;
+ m_location = nullptr;
+ }
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* void OnStartSending (in string aMsgID, in uint32_t aMsgSize); */
+ NS_IMETHOD OnStartSending(const char *aMsgID, uint32_t aMsgSize) {return NS_OK;}
+
+ /* void OnProgress (in string aMsgID, in uint32_t aProgress, in uint32_t aProgressMax); */
+ NS_IMETHOD OnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax) {return NS_OK;}
+
+ /* void OnStatus (in string aMsgID, in wstring aMsg); */
+ NS_IMETHOD OnStatus(const char *aMsgID, const char16_t *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 char16_t *aMsg,
+ nsIFile *returnFile) {
+ m_done = true;
+ NS_IF_ADDREF(m_location = returnFile);
+ return NS_OK;
+ }
+
+ /* void OnSendNotPerformed */
+ NS_IMETHOD OnSendNotPerformed(const char *aMsgID, nsresult aStatus) {return NS_OK;}
+
+ /* void OnGetDraftFolderURI (); */
+ NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;}
+
+ static nsresult CreateSendListener(nsIMsgSendListener **ppListener);
+ void Reset() { m_done = false; NS_IF_RELEASE(m_location);}
+
+public:
+ virtual ~OutlookSendListener() { NS_IF_RELEASE(m_location); }
+
+ bool m_done;
+ nsIFile * m_location;
+};
+
+NS_IMPL_ISUPPORTS(OutlookSendListener, nsIMsgSendListener)
+
+nsresult OutlookSendListener::CreateSendListener(nsIMsgSendListener **ppListener)
+{
+ NS_PRECONDITION(ppListener != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(ppListener);
+
+ *ppListener = new OutlookSendListener();
+ if (! *ppListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*ppListener);
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+#define hackBeginA "begin"
+#define hackBeginW u"begin"
+#define hackEndA "\015\012end"
+#define hackEndW u"\015\012end"
+#define hackCRLFA "crlf"
+#define hackCRLFW u"crlf"
+#define hackAmpersandA "amp"
+#define hackAmpersandW u"amp"
+
+nsOutlookCompose::nsOutlookCompose()
+{
+ m_pListener = nullptr;
+ m_pMsgFields = nullptr;
+
+ m_optimizationBuffer = new char[FILE_IO_BUFFER_SIZE];
+}
+
+nsOutlookCompose::~nsOutlookCompose()
+{
+ NS_IF_RELEASE(m_pListener);
+ NS_IF_RELEASE(m_pMsgFields);
+ if (m_pIdentity) {
+ nsresult rv = m_pIdentity->ClearAllValues();
+ NS_ASSERTION(NS_SUCCEEDED(rv),"failed to clear values");
+ if (NS_FAILED(rv))
+ return;
+ }
+ delete[] m_optimizationBuffer;
+}
+
+nsIMsgIdentity * nsOutlookCompose::m_pIdentity = nullptr;
+
+nsresult nsOutlookCompose::CreateIdentity(void)
+{
+ if (m_pIdentity)
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = accMgr->CreateIdentity(&m_pIdentity);
+ nsString name;
+ name.AssignLiteral("Import Identity");
+ if (m_pIdentity) {
+ m_pIdentity->SetFullName(name);
+ m_pIdentity->SetEmail(NS_LITERAL_CSTRING("import@service.invalid"));
+ }
+ return rv;
+}
+
+void nsOutlookCompose::ReleaseIdentity()
+{
+ NS_IF_RELEASE(m_pIdentity);
+}
+
+nsresult nsOutlookCompose::CreateComponents(void)
+{
+ nsresult rv = NS_OK;
+
+ NS_IF_RELEASE(m_pMsgFields);
+ if (!m_pListener && NS_SUCCEEDED(rv))
+ rv = OutlookSendListener::CreateSendListener(&m_pListener);
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = CallCreateInstance(kMsgCompFieldsCID, &m_pMsgFields);
+ if (NS_SUCCEEDED(rv) && m_pMsgFields) {
+ // IMPORT_LOG0("nsOutlookCompose - CreateComponents succeeded\n");
+ m_pMsgFields->SetForcePlainText(false);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsOutlookCompose::ComposeTheMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIFile **pMsg)
+{
+ nsresult rv = CreateComponents();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = CreateIdentity();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // IMPORT_LOG0("Outlook Compose created necessary components\n");
+
+ CMapiMessageHeaders* headers = msg.GetHeaders();
+
+ nsString unival;
+ headers->UnfoldValue(CMapiMessageHeaders::hdrFrom, unival, msg.GetBodyCharset());
+ m_pMsgFields->SetFrom(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrTo, unival, msg.GetBodyCharset());
+ m_pMsgFields->SetTo(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrSubject, unival, msg.GetBodyCharset());
+ m_pMsgFields->SetSubject(unival);
+ m_pMsgFields->SetCharacterSet(msg.GetBodyCharset());
+ headers->UnfoldValue(CMapiMessageHeaders::hdrCc, unival, msg.GetBodyCharset());
+ m_pMsgFields->SetCc(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrReplyTo, unival, msg.GetBodyCharset());
+ m_pMsgFields->SetReplyTo(unival);
+ m_pMsgFields->SetMessageId(headers->Value(CMapiMessageHeaders::hdrMessageID));
+
+ // We only use those headers that may need to be processed by Thunderbird
+ // to create a good rfc822 document, or need to be encoded (like To and Cc).
+ // These will replace the originals on import. All the other headers
+ // will be copied to the destination unaltered in CopyComposedMessage().
+
+ nsCOMPtr<nsIArray> pAttach;
+ msg.GetAttachments(getter_AddRefs(pAttach));
+
+ nsString bodyW;
+ // Bug 593907
+ if (GenerateHackSequence(msg.GetBody(), msg.GetBodyLen()))
+ HackBody(msg.GetBody(), msg.GetBodyLen(), bodyW);
+ else
+ bodyW = msg.GetBody();
+ // End Bug 593907
+
+ nsCOMPtr<nsIMutableArray> embeddedObjects;
+
+ if (msg.BodyIsHtml()) {
+ for (unsigned int i = 0; i <msg.EmbeddedAttachmentsCount(); i++) {
+ nsIURI *uri;
+ const char* cid;
+ const char* name;
+ if (msg.GetEmbeddedAttachmentInfo(i, &uri, &cid, &name)) {
+ if (!embeddedObjects) {
+ embeddedObjects = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsCOMPtr<nsIMsgEmbeddedImageData> imageData =
+ new nsImportEmbeddedImageData(uri, nsDependentCString(cid),
+ nsDependentCString(name));
+ embeddedObjects->AppendElement(imageData, false);
+ }
+ }
+ }
+
+ nsCString bodyA;
+ nsMsgI18NConvertFromUnicode(msg.GetBodyCharset(), bodyW, bodyA);
+
+ nsCOMPtr<nsIImportService> impService(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = impService->CreateRFC822Message(
+ m_pIdentity, // dummy identity
+ m_pMsgFields, // message fields
+ msg.BodyIsHtml() ? "text/html" : "text/plain",
+ bodyA, // body pointer
+ mode == nsIMsgSend::nsMsgSaveAsDraft,
+ pAttach, // local attachments
+ embeddedObjects,
+ m_pListener); // listener
+
+ OutlookSendListener *pListen = (OutlookSendListener *)m_pListener;
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** Error, CreateAndSendMessage FAILED: 0x%lx\n", rv);
+ }
+ else {
+ // wait for the listener to get done!
+ int32_t abortCnt = 0;
+ int32_t cnt = 0;
+ int32_t sleepCnt = 1;
+ while (!pListen->m_done && (abortCnt < kHungAbortCount)) {
+ PR_Sleep(sleepCnt);
+ cnt++;
+ if (cnt > kHungCount) {
+ abortCnt++;
+ sleepCnt *= 2;
+ cnt = 0;
+ }
+ }
+
+ if (abortCnt >= kHungAbortCount) {
+ IMPORT_LOG0("**** Create and send message hung\n");
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+
+ if (pListen->m_location) {
+ pListen->m_location->Clone(pMsg);
+ rv = NS_OK;
+ }
+ else {
+ rv = NS_ERROR_FAILURE;
+ IMPORT_LOG0("*** Error, Outlook compose unsuccessful\n");
+ }
+
+ pListen->Reset();
+ return rv;
+}
+
+nsresult nsOutlookCompose::CopyComposedMessage(nsIFile *pSrc,
+ nsIOutputStream *pDst,
+ CMapiMessage& origMsg)
+{
+ // I'm unsure if we really need the convertCRs feature here.
+ // The headers in the file are generated by TB, the body was generated by rtf reader that always used CRLF,
+ // and the attachments were processed by TB either... However, I let it stay as it was in the original code.
+ CCompositionFile f(pSrc, m_optimizationBuffer, FILE_IO_BUFFER_SIZE, true);
+ if (!f) {
+ IMPORT_LOG0("*** Error, unexpected zero file size for composed message\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ // The "From ..." separates the messages. Without it, TB cannot see the messages in the mailbox file.
+ // Thus, the lines that look like "From ..." in the message must be escaped (see EscapeFromSpaceLine())
+ int fromLineLen;
+ const char* fromLine = origMsg.GetFromLine(fromLineLen);
+ uint32_t written;
+ nsresult rv = pDst->Write(fromLine, fromLineLen, &written);
+
+ // Bug 219269
+ // Write out the x-mozilla-status headers.
+ char statusLine[50];
+ uint32_t msgFlags = 0;
+ if (origMsg.IsRead())
+ msgFlags |= nsMsgMessageFlags::Read;
+ if (!origMsg.FullMessageDownloaded())
+ msgFlags |= nsMsgMessageFlags::Partial;
+ if (origMsg.IsForvarded())
+ msgFlags |= nsMsgMessageFlags::Forwarded;
+ if (origMsg.IsReplied())
+ msgFlags |= nsMsgMessageFlags::Replied;
+ if (origMsg.HasAttach())
+ msgFlags |= nsMsgMessageFlags::Attachment;
+ _snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF);
+ rv = pDst->Write(statusLine, strlen(statusLine), &written);
+ _snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000);
+ rv = pDst->Write(statusLine, strlen(statusLine), &written);
+ // End Bug 219269
+
+ // well, isn't this a hoot!
+ // Read the headers from the new message, get the ones we like
+ // and write out only the headers we want from the new message,
+ // along with all of the other headers from the "old" message!
+
+ nsCString newHeadersStr;
+ rv = f.ToString(newHeadersStr, MSG_LINEBREAK MSG_LINEBREAK); // Read all the headers
+ NS_ENSURE_SUCCESS(rv, rv);
+ UpdateHeaders(*origMsg.GetHeaders(), newHeadersStr.get());
+ rv = origMsg.GetHeaders()->ToStream(pDst);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bug 593907
+ if (!m_hackedPostfix.IsEmpty()) {
+ nsCString hackedPartEnd;
+ LossyCopyUTF16toASCII(m_hackedPostfix, hackedPartEnd);
+ hackedPartEnd.Insert(hackEndA, 0);
+ nsCString body;
+ rv = f.ToString(body, hackedPartEnd.get(), hackedPartEnd.Length());
+ UnhackBody(body);
+ EscapeFromSpaceLine(pDst, const_cast<char*>(body.get()), body.get()+body.Length());
+ }
+ // End Bug 593907
+
+ // I use the terminating sequence here to avoid a possible situation when a "From " line
+ // gets split over two sequential reads and thus will not be escaped.
+ // This is done by reading up to CRLF (one line every time), though it may be slower
+
+ // Here I revert the changes that were made when the multipart/related message
+ // was composed in nsMsgSend::ProcessMultipartRelated() - the Content-Ids of
+ // attachments were replaced with new ones.
+ nsCString line;
+ while (NS_SUCCEEDED(f.ToString(line, MSG_LINEBREAK))) {
+ EscapeFromSpaceLine(pDst, const_cast<char*>(line.get()), line.get()+line.Length());
+ }
+
+ if (f.LastChar() != nsCRT::LF) {
+ rv = pDst->Write(MSG_LINEBREAK, 2, &written);
+ if (written != 2)
+ rv = NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+nsresult nsOutlookCompose::ProcessMessage(nsMsgDeliverMode mode,
+ CMapiMessage &msg,
+ nsIOutputStream *pDst)
+{
+ nsCOMPtr<nsIFile> compositionFile;
+ nsresult rv = ComposeTheMessage(mode, msg, getter_AddRefs(compositionFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = CopyComposedMessage(compositionFile, pDst, msg);
+ compositionFile->Remove(false);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error copying composed message to destination mailbox\n");
+ }
+ return rv;
+}
+
+void nsOutlookCompose::UpdateHeader(CMapiMessageHeaders& oldHeaders,
+ const CMapiMessageHeaders& newHeaders,
+ CMapiMessageHeaders::SpecialHeader header,
+ bool addIfAbsent)
+{
+ const char* oldVal = oldHeaders.Value(header);
+ if (!addIfAbsent && !oldVal)
+ return;
+ const char* newVal = newHeaders.Value(header);
+ if (!newVal)
+ return;
+ // Bug 145150 - Turn "Content-Type: application/ms-tnef" into "Content-Type: text/plain"
+ // so the body text can be displayed normally (instead of in an attachment).
+ if (header == CMapiMessageHeaders::hdrContentType)
+ if (stricmp(newVal, "application/ms-tnef") == 0)
+ newVal = "text/plain";
+ // End Bug 145150
+ if (oldVal) {
+ if (strcmp(oldVal, newVal) == 0)
+ return;
+ // Backup the old header value
+ nsCString backupHdrName("X-MozillaBackup-");
+ backupHdrName += CMapiMessageHeaders::SpecialName(header);
+ oldHeaders.SetValue(backupHdrName.get(), oldVal, false);
+ }
+ // Now replace it with new value
+ oldHeaders.SetValue(header, newVal);
+}
+
+void nsOutlookCompose::UpdateHeaders(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders)
+{
+ // Well, ain't this a peach?
+ // This is rather disgusting but there really isn't much to be done about it....
+
+ // 1. For each "old" header, replace it with the new one if we want,
+ // then right it out.
+ // 2. Then if we haven't written the "important" new headers, write them out
+ // 3. Terminate the headers with an extra eol.
+
+ // Important headers:
+ // "Content-type",
+ // "MIME-Version",
+ // "Content-transfer-encoding"
+ // consider "X-Accept-Language"?
+
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrContentType);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrMimeVersion);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrContentTransferEncoding);
+
+ // Other replaced headers (only if they exist):
+ // "From",
+ // "To",
+ // "Subject",
+ // "Reply-to",
+ // "Cc"
+
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrFrom, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrTo, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrSubject, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrReplyTo, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrCc, false);
+}
+
+// Bug 593907
+// This is just a workaround of the deficiency of the nsMsgComposeAndSend::EnsureLineBreaks().
+// The import from Outlook will stay OK (I hope), but other messages may still suffer.
+// However, I cannot deny the possibility that the (possible) recode of the body
+// may interfere with this hack. A possible scenario is if a multi-byte character will either
+// contain 0x0D 0x0A sequence, or end with 0x0D, after which MAC-style standalone LF will go.
+// I hope that this possibility is insignificant (eg, utf-8 doesn't contain such sequences).
+// This hack will slow down the import, but as the import is one-time procedure, I hope that
+// the user will agree to wait a little longer to get better results.
+
+// The process of composing the message differs depending on whether the editor is present or not.
+// If the editor is absent, the "attachment1_body" parameter of CreateAndSendMessage() is taken as is,
+// while in the presence o the editor, the body that is taken from it is further processed in the
+// nsMsgComposeAndSend::GetBodyFromEditor(). Specifically, the TXTToHTML::ScanHTML() first calls
+// UnescapeStr() to properly handle a limited number of HTML character entities (namely &amp; &lt; &gt; &quot;)
+// and then calls ScanTXT() where escapes all ampersands and quotes again. As the UnescapeStr() works so
+// selectively (i.e. handling only a subset of valid entities), the so often seen "&nbsp;" becomes "&amp;nbsp;"
+// in the resulting body, which leads to text "&nbsp;" interspersed all over the imported mail. The same
+// applies to html &#XXXX; (where XXXX is unicode codepoint).
+// See also Bug 503690, where the same issue in Eudora import is reported.
+// By the way, the root of the Bug 359303 lies in the same place - the nsMsgComposeAndSend::GetBodyFromEditor()
+// changes the 0xA0 codes to 0x20 when it converts the body to plain text.
+// We scan the body here to find all the & and convert them to the safe character sequense to revert later.
+
+void nsOutlookCompose::HackBody(const wchar_t* orig, size_t origLen, nsString& hack)
+{
+#ifdef MOZILLA_INTERNAL_API
+ hack.SetCapacity(static_cast<size_t>(origLen*1.4));
+#endif
+ hack.Assign(hackBeginW);
+ hack.Append(m_hackedPostfix);
+
+ while (*orig) {
+ if (*orig == L'&') {
+ hack.Append(hackAmpersandW);
+ hack.Append(m_hackedPostfix);
+ } else if ((*orig == L'\x0D') && (*(orig+1) == L'\x0A')) {
+ hack.Append(hackCRLFW);
+ hack.Append(m_hackedPostfix);
+ ++orig;
+ } else
+ hack.Append(*orig);
+ ++orig;
+ }
+
+ hack.Append(hackEndW);
+ hack.Append(m_hackedPostfix);
+}
+
+void nsOutlookCompose::UnhackBody(nsCString& txt)
+{
+ nsCString hackedPostfixA;
+ LossyCopyUTF16toASCII(m_hackedPostfix, hackedPostfixA);
+
+ nsCString hackedString(hackBeginA);
+ hackedString.Append(hackedPostfixA);
+ int32_t begin = txt.Find(hackedString);
+ if (begin == kNotFound)
+ return;
+ txt.Cut(begin, hackedString.Length());
+
+ hackedString.Assign(hackEndA);
+ hackedString.Append(hackedPostfixA);
+ int32_t end = MsgFind(txt, hackedString, false, begin);
+ if (end == kNotFound)
+ return; // ?
+ txt.Cut(end, hackedString.Length());
+
+ nsCString range;
+ range.Assign(Substring(txt, begin, end - begin));
+ // 1. Remove all CRLFs from the selected range
+ MsgReplaceSubstring(range, MSG_LINEBREAK, "");
+ // 2. Restore the original CRLFs
+ hackedString.Assign(hackCRLFA);
+ hackedString.Append(hackedPostfixA);
+ MsgReplaceSubstring(range, hackedString.get(), MSG_LINEBREAK);
+
+ // 3. Restore the original ampersands
+ hackedString.Assign(hackAmpersandA);
+ hackedString.Append(hackedPostfixA);
+ MsgReplaceSubstring(range, hackedString.get(), "&");
+
+ txt.Replace(begin, end - begin, range);
+}
+
+bool nsOutlookCompose::GenerateHackSequence(const wchar_t* body, size_t origLen)
+{
+ nsDependentString nsBody(body, origLen);
+ const wchar_t* hack_base = L"hacked";
+ int i = 0;
+ do {
+ if (++i == 0) { // Cycle complete :) - could not generate an unique string
+ m_hackedPostfix.Truncate();
+ return false;
+ }
+ m_hackedPostfix.Assign(hack_base);
+ m_hackedPostfix.AppendInt(i);
+ } while (nsBody.Find(m_hackedPostfix) != kNotFound);
+
+ return true;
+}
+// End Bug 593907
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CCompositionFile::CCompositionFile(nsIFile* aFile, void* fifoBuffer,
+ uint32_t fifoBufferSize, bool convertCRs)
+ : m_pFile(aFile), m_fileSize(0), m_fileReadPos(0),
+ m_fifoBuffer(static_cast<char*>(fifoBuffer)),
+ m_fifoBufferSize(fifoBufferSize),
+ m_fifoBufferReadPos(static_cast<char*>(fifoBuffer)),
+ m_fifoBufferWrittenPos(static_cast<char*>(fifoBuffer)),
+ m_convertCRs(convertCRs),
+ m_lastChar(0)
+{
+ m_pFile->GetFileSize(&m_fileSize);
+ if (!m_fileSize) {
+ IMPORT_LOG0("*** Error, unexpected zero file size for composed message\n");
+ return;
+ }
+
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(m_pInputStream), m_pFile);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error, unable to open composed message file\n");
+ return;
+ }
+}
+
+nsresult CCompositionFile::EnsureHasDataInBuffer()
+{
+ if (m_fifoBufferReadPos < m_fifoBufferWrittenPos)
+ return NS_OK;
+ // Populate the buffer with new data!
+ uint32_t count = m_fifoBufferSize;
+ if ((m_fileReadPos + count) > m_fileSize)
+ count = m_fileSize - m_fileReadPos;
+ if (!count)
+ return NS_ERROR_FAILURE; // Isn't there a "No more data" error?
+
+ uint32_t bytesRead = 0;
+ nsresult rv = m_pInputStream->Read(m_fifoBuffer, count, &bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!bytesRead || (bytesRead > count))
+ return NS_ERROR_FAILURE;
+ m_fifoBufferWrittenPos = m_fifoBuffer+bytesRead;
+ m_fifoBufferReadPos = m_fifoBuffer;
+ m_fileReadPos += bytesRead;
+
+ return NS_OK;
+}
+
+class CTermGuard {
+public:
+ CTermGuard(const char* term, int termSize)
+ : m_term(term),
+ m_termSize(term ? ((termSize!=-1) ? termSize : strlen(term)) : 0),
+ m_matchPos(0)
+ {}
+
+ // if the guard triggered
+ inline bool IsTriggered() const {
+ return m_termSize && (m_matchPos == m_termSize); }
+ // indicates if the guard has something to check
+ inline bool IsChecking() const { return m_termSize; }
+
+ bool Check(char c) // returns true only if the whole sequence passed
+ {
+ if (!m_termSize) // no guard
+ return false;
+ if (m_matchPos >= m_termSize) // check past success!
+ m_matchPos = 0;
+ if (m_term[m_matchPos] != c) // Reset sequence
+ m_matchPos = 0;
+ if (m_term[m_matchPos] == c) { // Sequence continues
+ return ++m_matchPos == m_termSize; // If equal then sequence complete!
+ }
+ // Sequence broken
+ return false;
+ }
+private:
+ const char* m_term;
+ int m_termSize;
+ int m_matchPos;
+};
+
+template <class _OutFn>
+nsresult CCompositionFile::ToDest(_OutFn dest, const char* term, int termSize)
+{
+ CTermGuard guard(term, termSize);
+
+#ifdef MOZILLA_INTERNAL_API
+ // We already know the required string size, so reduce future reallocations
+ if (!guard.IsChecking() && !m_convertCRs)
+ dest.SetCapacity(m_fileSize - m_fileReadPos);
+#endif
+
+ bool wasCR = false;
+ char c = 0;
+ nsresult rv;
+ while (NS_SUCCEEDED(rv = EnsureHasDataInBuffer())) {
+ if (!guard.IsChecking() && !m_convertCRs) { // Use efficient algorithm
+ dest.Append(m_fifoBufferReadPos, m_fifoBufferWrittenPos-m_fifoBufferReadPos);
+ }
+ else { // Check character by character to convert CRs and find terminating sequence
+ while (m_fifoBufferReadPos < m_fifoBufferWrittenPos) {
+ c = *m_fifoBufferReadPos;
+ if (m_convertCRs && wasCR) {
+ wasCR = false;
+ if (c != nsCRT::LF) {
+ const char kTmpLF = nsCRT::LF;
+ dest.Append(&kTmpLF, 1);
+ if (guard.Check(nsCRT::LF)) {
+ c = nsCRT::LF; // save last char
+ break;
+ }
+ }
+ }
+ dest.Append(&c, 1);
+ m_fifoBufferReadPos++;
+
+ if (guard.Check(c))
+ break;
+
+ if (m_convertCRs && (c == nsCRT::CR))
+ wasCR = true;
+ }
+ if (guard.IsTriggered())
+ break;
+ }
+ }
+
+ // check for trailing CR (only if caller didn't specify the terminating sequence that ends with CR -
+ // in this case he knows what he does!)
+ if (m_convertCRs && !guard.IsTriggered() && (c == nsCRT::CR)) {
+ c = nsCRT::LF;
+ dest.Append(&c, 1);
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_lastChar = c;
+ return NS_OK;
+}
+
+class dest_nsCString {
+public:
+ dest_nsCString(nsCString& str) : m_str(str) { m_str.Truncate(); }
+#ifdef MOZILLA_INTERNAL_API
+ void SetCapacity(int32_t sz) { m_str.SetCapacity(sz); }
+#endif
+ nsresult Append(const char* buf, uint32_t count) {
+ m_str.Append(buf, count); return NS_OK; }
+private:
+ nsCString& m_str;
+};
+
+class dest_Stream {
+public:
+ dest_Stream(nsIOutputStream *dest) : m_stream(dest) {}
+#ifdef MOZILLA_INTERNAL_API
+ void SetCapacity(int32_t) { /*do nothing*/ }
+#endif
+ // const_cast here is due to the poor design of the EscapeFromSpaceLine()
+ // that requires a non-constant pointer while doesn't modify its data
+ nsresult Append(const char* buf, uint32_t count) {
+ return EscapeFromSpaceLine(m_stream, const_cast<char*>(buf), buf+count); }
+private:
+ nsIOutputStream *m_stream;
+};
+
+nsresult CCompositionFile::ToString(nsCString& dest, const char* term,
+ int termSize)
+{
+ return ToDest(dest_nsCString(dest), term, termSize);
+}
+
+nsresult CCompositionFile::ToStream(nsIOutputStream *dest, const char* term,
+ int termSize)
+{
+ return ToDest(dest_Stream(dest), term, termSize);
+}
diff --git a/mailnews/import/outlook/src/nsOutlookCompose.h b/mailnews/import/outlook/src/nsOutlookCompose.h
new file mode 100644
index 000000000..68f07f754
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookCompose.h
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#ifndef nsOutlookCompose_h__
+#define nsOutlookCompose_h__
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsIFile.h"
+#include "nsIImportService.h"
+
+class nsIMsgSend;
+class nsIMsgCompFields;
+class nsIMsgIdentity;
+class nsIMsgSendListener;
+class nsIIOService;
+
+#include "nsIMsgSend.h"
+#include "nsNetUtil.h"
+
+#include "MapiMessage.h"
+
+#include <list>
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class nsOutlookCompose {
+public:
+ nsOutlookCompose();
+ ~nsOutlookCompose();
+
+ nsresult ProcessMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIOutputStream *pDst);
+ static nsresult CreateIdentity(void);
+ static void ReleaseIdentity(void);
+private:
+ nsresult CreateComponents(void);
+
+ void UpdateHeader(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders, CMapiMessageHeaders::SpecialHeader header, bool addIfAbsent = true);
+ void UpdateHeaders(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders);
+
+ nsresult ComposeTheMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIFile **pMsg);
+ nsresult CopyComposedMessage(nsIFile *pSrc, nsIOutputStream *pDst, CMapiMessage& origMsg);
+
+ // Bug 593907
+ void HackBody(const wchar_t* orig, size_t origLen, nsString& hack);
+ void UnhackBody(nsCString& body);
+ bool GenerateHackSequence(const wchar_t* body, size_t origLen);
+ // End Bug 593907
+
+private:
+ nsIMsgSendListener * m_pListener;
+ nsIMsgCompFields * m_pMsgFields;
+ static nsIMsgIdentity * m_pIdentity;
+ char* m_optimizationBuffer;
+ nsCOMPtr<nsIImportService> m_pImportService;
+
+ // Bug 593907
+ nsString m_hackedPostfix;
+ // End Bug 593907
+};
+
+
+#endif /* nsOutlookCompose_h__ */
diff --git a/mailnews/import/outlook/src/nsOutlookImport.cpp b/mailnews/import/outlook/src/nsOutlookImport.cpp
new file mode 100644
index 000000000..eaaf24fc3
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookImport.cpp
@@ -0,0 +1,589 @@
+/* -*- Mode: C++; tab-width: 2; 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 Express (Win32) import mail and addressbook interfaces
+*/
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIImportService.h"
+#include "nsIComponentManager.h"
+#include "nsOutlookImport.h"
+#include "nsIMemory.h"
+#include "nsIImportService.h"
+#include "nsIImportMail.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIOutputStream.h"
+#include "nsIAddrDatabase.h"
+#include "nsOutlookSettings.h"
+#include "nsTextFormatter.h"
+#include "nsOutlookStringBundle.h"
+#include "nsIStringBundle.h"
+#include "OutlookDebugLog.h"
+#include "nsUnicharUtils.h"
+
+#include "nsOutlookMail.h"
+
+#include "MapiApi.h"
+
+static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+PRLogModuleInfo *OUTLOOKLOGMODULE = nullptr;
+
+class ImportOutlookMailImpl : public nsIImportMail
+{
+public:
+ ImportOutlookMailImpl();
+
+ static nsresult Create(nsIImportMail** aImport);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIImportmail interface
+
+ /* void GetDefaultLocation (out nsIFile location, out boolean found, out boolean userVerify); */
+ NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify);
+
+ /* nsIArray FindMailboxes (in nsIFile location); */
+ NS_IMETHOD FindMailboxes(nsIFile *location, nsIArray **_retval);
+
+ NS_IMETHOD ImportMailbox(nsIImportMailboxDescriptor *source,
+ nsIMsgFolder *dstFolder,
+ char16_t **pErrorLog, char16_t **pSuccessLog,
+ bool *fatalError);
+
+ /* unsigned long GetImportProgress (); */
+ NS_IMETHOD GetImportProgress(uint32_t *_retval);
+
+ NS_IMETHOD TranslateFolderName(const nsAString & aFolderName, nsAString & _retval);
+
+public:
+ static void ReportSuccess(nsString& name, int32_t count, nsString *pStream);
+ static void ReportError(int32_t errorNum, nsString& name, nsString *pStream);
+ static void AddLinebreak(nsString *pStream);
+ static void SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess);
+
+private:
+ virtual ~ImportOutlookMailImpl();
+ nsOutlookMail m_mail;
+ uint32_t m_bytesDone;
+};
+
+
+class ImportOutlookAddressImpl : public nsIImportAddressBooks
+{
+public:
+ ImportOutlookAddressImpl();
+
+ static nsresult Create(nsIImportAddressBooks** aImport);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIImportAddressBooks interface
+
+ NS_IMETHOD GetSupportsMultiple(bool *_retval) { *_retval = true; return NS_OK;}
+
+ NS_IMETHOD GetAutoFind(char16_t **description, bool *_retval);
+
+ NS_IMETHOD GetNeedsFieldMap(nsIFile *location, bool *_retval) { *_retval = false; return NS_OK;}
+
+ NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify)
+ { return NS_ERROR_FAILURE;}
+
+ NS_IMETHOD FindAddressBooks(nsIFile *location, nsIArray **_retval);
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap *fieldMap)
+ { return NS_ERROR_FAILURE; }
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor *source,
+ nsIAddrDatabase *destination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t **errorLog,
+ char16_t **successLog,
+ bool *fatalError);
+
+ NS_IMETHOD GetImportProgress(uint32_t *_retval);
+
+ NS_IMETHOD GetSampleData(int32_t index, bool *pFound, char16_t **pStr)
+ { return NS_ERROR_FAILURE;}
+
+ NS_IMETHOD SetSampleLocation(nsIFile *) { return NS_OK; }
+
+private:
+ virtual ~ImportOutlookAddressImpl();
+ void ReportSuccess(nsString& name, nsString *pStream);
+
+private:
+ uint32_t m_msgCount;
+ uint32_t m_msgTotal;
+ nsOutlookMail m_address;
+};
+////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+nsOutlookImport::nsOutlookImport()
+{
+ // Init logging module.
+ if (!OUTLOOKLOGMODULE)
+ OUTLOOKLOGMODULE = PR_NewLogModule("IMPORT");
+
+ IMPORT_LOG0("nsOutlookImport Module Created\n");
+
+ nsOutlookStringBundle::GetStringBundle();
+}
+
+
+nsOutlookImport::~nsOutlookImport()
+{
+ IMPORT_LOG0("nsOutlookImport Module Deleted\n");
+}
+
+NS_IMPL_ISUPPORTS(nsOutlookImport, nsIImportModule)
+
+NS_IMETHODIMP nsOutlookImport::GetName(char16_t **name)
+{
+ NS_PRECONDITION(name != nullptr, "null ptr");
+ if (! name)
+ return NS_ERROR_NULL_POINTER;
+
+ *name = nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_NAME);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookImport::GetDescription(char16_t **name)
+{
+ NS_PRECONDITION(name != nullptr, "null ptr");
+ if (!name)
+ return NS_ERROR_NULL_POINTER;
+
+ *name = nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_DESCRIPTION);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookImport::GetSupports(char **supports)
+{
+ NS_PRECONDITION(supports != nullptr, "null ptr");
+ if (! supports)
+ return NS_ERROR_NULL_POINTER;
+
+ *supports = strdup(kOutlookSupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookImport::GetSupportsUpgrade(bool *pUpgrade)
+{
+ NS_PRECONDITION(pUpgrade != nullptr, "null ptr");
+ if (! pUpgrade)
+ return NS_ERROR_NULL_POINTER;
+
+ *pUpgrade = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookImport::GetImportInterface(const char *pImportType, nsISupports **ppInterface)
+{
+ NS_PRECONDITION(pImportType != nullptr, "null ptr");
+ if (! pImportType)
+ return NS_ERROR_NULL_POINTER;
+ NS_PRECONDITION(ppInterface != nullptr, "null ptr");
+ if (! ppInterface)
+ return NS_ERROR_NULL_POINTER;
+
+ *ppInterface = nullptr;
+ nsresult rv;
+ if (!strcmp(pImportType, "mail")) {
+ // create the nsIImportMail interface and return it!
+ nsIImportMail * pMail = nullptr;
+ nsIImportGeneric *pGeneric = nullptr;
+ rv = ImportOutlookMailImpl::Create(&pMail);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericMail(&pGeneric);
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("mailInterface", pMail);
+ nsString name;
+ nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_NAME, name);
+ nsCOMPtr<nsISupportsString> nameString (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nameString->SetData(name);
+ pGeneric->SetData("name", nameString);
+ rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface);
+ }
+ }
+ }
+ }
+ NS_IF_RELEASE(pMail);
+ NS_IF_RELEASE(pGeneric);
+ return rv;
+ }
+
+ if (!strcmp(pImportType, "addressbook")) {
+ // create the nsIImportAddressBook interface and return it!
+ nsIImportAddressBooks * pAddress = nullptr;
+ nsIImportGeneric * pGeneric = nullptr;
+ rv = ImportOutlookAddressImpl::Create(&pAddress);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(&pGeneric);
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface);
+ }
+ }
+ }
+ NS_IF_RELEASE(pAddress);
+ NS_IF_RELEASE(pGeneric);
+ return rv;
+ }
+
+ if (!strcmp(pImportType, "settings")) {
+ nsIImportSettings *pSettings = nullptr;
+ rv = nsOutlookSettings::Create(&pSettings);
+ if (NS_SUCCEEDED(rv))
+ pSettings->QueryInterface(kISupportsIID, (void **)ppInterface);
+ NS_IF_RELEASE(pSettings);
+ return rv;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+nsresult ImportOutlookMailImpl::Create(nsIImportMail** aImport)
+{
+ NS_PRECONDITION(aImport != nullptr, "null ptr");
+ if (! aImport)
+ return NS_ERROR_NULL_POINTER;
+
+ *aImport = new ImportOutlookMailImpl();
+ if (! *aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+ImportOutlookMailImpl::ImportOutlookMailImpl()
+{
+ nsOutlookCompose::CreateIdentity();
+}
+
+ImportOutlookMailImpl::~ImportOutlookMailImpl()
+{
+ nsOutlookCompose::ReleaseIdentity();
+}
+
+NS_IMPL_ISUPPORTS(ImportOutlookMailImpl, nsIImportMail)
+
+NS_IMETHODIMP ImportOutlookMailImpl::GetDefaultLocation(nsIFile **ppLoc, bool *found, bool *userVerify)
+{
+ NS_PRECONDITION(ppLoc != nullptr, "null ptr");
+ NS_PRECONDITION(found != nullptr, "null ptr");
+ NS_PRECONDITION(userVerify != nullptr, "null ptr");
+ if (!ppLoc || !found || !userVerify)
+ return NS_ERROR_NULL_POINTER;
+
+ *found = false;
+ *ppLoc = nullptr;
+ *userVerify = false;
+ // We need to verify here that we can get the mail, if true then
+ // return a dummy location, otherwise return no location
+ CMapiApi mapi;
+ if (!mapi.Initialize())
+ return NS_OK;
+ if (!mapi.LogOn())
+ return NS_OK;
+
+ CMapiFolderList store;
+ if (!mapi.IterateStores(store))
+ return NS_OK;
+
+ if (store.GetSize() == 0)
+ return NS_OK;
+
+
+ nsresult rv;
+ nsCOMPtr <nsIFile> resultFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *found = true;
+ NS_IF_ADDREF(*ppLoc = resultFile);
+ *userVerify = false;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP ImportOutlookMailImpl::FindMailboxes(nsIFile *pLoc, nsIArray **ppArray)
+{
+ NS_PRECONDITION(pLoc != nullptr, "null ptr");
+ NS_PRECONDITION(ppArray != nullptr, "null ptr");
+ if (!pLoc || !ppArray)
+ return NS_ERROR_NULL_POINTER;
+ return m_mail.GetMailFolders(ppArray);
+}
+
+void ImportOutlookMailImpl::AddLinebreak(nsString *pStream)
+{
+ if (pStream)
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportOutlookMailImpl::ReportSuccess(nsString& name, int32_t count, nsString *pStream)
+{
+ if (!pStream)
+ return;
+ // load the success string
+ char16_t *pFmt = nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_MAILBOX_SUCCESS);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get(), count);
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ nsOutlookStringBundle::FreeString(pFmt);
+ AddLinebreak(pStream);
+}
+
+void ImportOutlookMailImpl::ReportError(int32_t errorNum, nsString& name, nsString *pStream)
+{
+ if (!pStream)
+ return;
+ // load the error string
+ char16_t *pFmt = nsOutlookStringBundle::GetStringByID(errorNum);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ nsOutlookStringBundle::FreeString(pFmt);
+ AddLinebreak(pStream);
+}
+
+
+void ImportOutlookMailImpl::SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess)
+{
+ if (pError)
+ *pError = ToNewUnicode(error);
+ if (pSuccess)
+ *pSuccess = ToNewUnicode(success);
+}
+
+NS_IMETHODIMP
+ImportOutlookMailImpl::ImportMailbox(nsIImportMailboxDescriptor *pSource,
+ nsIMsgFolder *dstFolder,
+ char16_t **pErrorLog,
+ char16_t **pSuccessLog,
+ bool *fatalError)
+{
+ NS_ENSURE_ARG_POINTER(pSource);
+ NS_ENSURE_ARG_POINTER(dstFolder);
+ NS_ENSURE_ARG_POINTER(fatalError);
+
+ nsString success;
+ nsString error;
+ bool abort = false;
+ nsString name;
+ char16_t *pName;
+ if (NS_SUCCEEDED( pSource->GetDisplayName( &pName))) {
+ name = pName;
+ NS_Free( pName);
+ }
+
+ uint32_t mailSize = 0;
+ pSource->GetSize(&mailSize);
+ if (mailSize == 0) {
+ ReportSuccess(name, 0, &success);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_OK;
+ }
+
+ uint32_t index = 0;
+ pSource->GetIdentifier(&index);
+ int32_t msgCount = 0;
+ nsresult rv = NS_OK;
+
+ m_bytesDone = 0;
+
+ rv = m_mail.ImportMailbox(&m_bytesDone, &abort, (int32_t)index, name.get(),
+ dstFolder, &msgCount);
+
+ if (NS_SUCCEEDED(rv))
+ ReportSuccess(name, msgCount, &success);
+ else
+ ReportError(OUTLOOKIMPORT_MAILBOX_CONVERTERROR, name, &error);
+
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+
+ return rv;
+}
+
+
+NS_IMETHODIMP ImportOutlookMailImpl::GetImportProgress(uint32_t *pDoneSoFar)
+{
+ NS_PRECONDITION(pDoneSoFar != nullptr, "null ptr");
+ if (! pDoneSoFar)
+ return NS_ERROR_NULL_POINTER;
+
+ *pDoneSoFar = m_bytesDone;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportOutlookMailImpl::TranslateFolderName(const nsAString & aFolderName, nsAString & _retval)
+{
+ if (aFolderName.LowerCaseEqualsLiteral("deleted items"))
+ _retval = NS_LITERAL_STRING(kDestTrashFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("sent items"))
+ _retval = NS_LITERAL_STRING(kDestSentFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("outbox"))
+ _retval = NS_LITERAL_STRING(kDestUnsentMessagesFolderName);
+ else
+ _retval = aFolderName;
+ return NS_OK;
+}
+
+nsresult ImportOutlookAddressImpl::Create(nsIImportAddressBooks** aImport)
+{
+ NS_PRECONDITION(aImport != nullptr, "null ptr");
+ if (! aImport)
+ return NS_ERROR_NULL_POINTER;
+
+ *aImport = new ImportOutlookAddressImpl();
+ if (! *aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+ImportOutlookAddressImpl::ImportOutlookAddressImpl()
+{
+ m_msgCount = 0;
+ m_msgTotal = 0;
+}
+
+ImportOutlookAddressImpl::~ImportOutlookAddressImpl()
+{
+}
+
+NS_IMPL_ISUPPORTS(ImportOutlookAddressImpl, nsIImportAddressBooks)
+
+NS_IMETHODIMP ImportOutlookAddressImpl::GetAutoFind(char16_t **description, bool *_retval)
+{
+ NS_PRECONDITION(description != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! description || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = true;
+ nsString str;
+ nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_ADDRNAME, str);
+ *description = ToNewUnicode(str);
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportOutlookAddressImpl::FindAddressBooks(nsIFile *location, nsIArray **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_address.GetAddressBooks(_retval);
+}
+
+NS_IMETHODIMP ImportOutlookAddressImpl::ImportAddressBook(nsIImportABDescriptor *source,
+ nsIAddrDatabase *destination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t **pErrorLog,
+ char16_t **pSuccessLog,
+ bool *fatalError)
+{
+ m_msgCount = 0;
+ m_msgTotal = 0;
+ NS_PRECONDITION(source != nullptr, "null ptr");
+ NS_PRECONDITION(destination != nullptr, "null ptr");
+ NS_PRECONDITION(fatalError != nullptr, "null ptr");
+
+ nsString success;
+ nsString error;
+ if (!source || !destination || !fatalError) {
+ IMPORT_LOG0("*** Bad param passed to outlook address import\n");
+ nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_ADDRESS_BADPARAM, error);
+ if (fatalError)
+ *fatalError = true;
+ ImportOutlookMailImpl::SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsString name;
+ source->GetPreferredName(name);
+
+ uint32_t id;
+ if (NS_FAILED(source->GetIdentifier(&id))) {
+ ImportOutlookMailImpl::ReportError(OUTLOOKIMPORT_ADDRESS_BADSOURCEFILE, name, &error);
+ ImportOutlookMailImpl::SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_OK;
+ rv = m_address.ImportAddresses(&m_msgCount, &m_msgTotal, name.get(), id, destination, error);
+ if (NS_SUCCEEDED(rv) && error.IsEmpty())
+ ReportSuccess(name, &success);
+ else
+ ImportOutlookMailImpl::ReportError(OUTLOOKIMPORT_ADDRESS_CONVERTERROR, name, &error);
+
+ ImportOutlookMailImpl::SetLogs(success, error, pErrorLog, pSuccessLog);
+ IMPORT_LOG0("*** Returning from outlook address import\n");
+ return destination->Commit(nsAddrDBCommitType::kLargeCommit);
+}
+
+
+NS_IMETHODIMP ImportOutlookAddressImpl::GetImportProgress(uint32_t *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ uint32_t result = m_msgCount;
+ if (m_msgTotal) {
+ result *= 100;
+ result /= m_msgTotal;
+ }
+ else
+ result = 0;
+
+ if (result > 100)
+ result = 100;
+
+ *_retval = result;
+
+ return NS_OK;
+}
+
+void ImportOutlookAddressImpl::ReportSuccess(nsString& name, nsString *pStream)
+{
+ if (!pStream)
+ return;
+ // load the success string
+ char16_t *pFmt = nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_ADDRESS_SUCCESS);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ nsOutlookStringBundle::FreeString(pFmt);
+ ImportOutlookMailImpl::AddLinebreak(pStream);
+}
diff --git a/mailnews/import/outlook/src/nsOutlookImport.h b/mailnews/import/outlook/src/nsOutlookImport.h
new file mode 100644
index 000000000..e017d6efc
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookImport.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsOutlookImport_h___
+#define nsOutlookImport_h___
+
+#include "nsIImportModule.h"
+#include "nsCOMPtr.h"
+
+
+#define NS_OUTLOOKIMPORT_CID \
+{ /* 1DB469A0-8B00-11d3-A206-00A0CC26DA63 */ \
+ 0x1db469a0, 0x8b00, 0x11d3, \
+ {0xa2, 0x6, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63 }}
+
+
+
+
+#define kOutlookSupportsString NS_IMPORT_MAIL_STR "," NS_IMPORT_ADDRESS_STR "," NS_IMPORT_SETTINGS_STR
+
+class nsOutlookImport : public nsIImportModule
+{
+public:
+
+ nsOutlookImport();
+
+ NS_DECL_ISUPPORTS
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+protected:
+ virtual ~nsOutlookImport();
+};
+
+
+
+
+#endif /* nsOutlookImport_h___ */
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;
+}
diff --git a/mailnews/import/outlook/src/nsOutlookMail.h b/mailnews/import/outlook/src/nsOutlookMail.h
new file mode 100644
index 000000000..3aa317ece
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookMail.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsOutlookMail_h___
+#define nsOutlookMail_h___
+
+#include "nsIArray.h"
+#include "nsStringGlue.h"
+#include "nsOutlookCompose.h"
+#include "nsIFile.h"
+#include "MapiApi.h"
+#include "MapiMessage.h"
+#include "nsIAddrDatabase.h"
+
+class nsIAddrDatabase;
+class nsIImportFieldMap;
+
+class nsOutlookMail {
+public:
+ nsOutlookMail();
+ ~nsOutlookMail();
+
+ nsresult GetMailFolders(nsIArray **pArray);
+ nsresult GetAddressBooks(nsIArray **pArray);
+ nsresult ImportMailbox(uint32_t *pDoneSoFar, bool *pAbort, int32_t index,
+ const char16_t *pName, nsIMsgFolder *pDest,
+ int32_t *pMsgCount);
+ static nsresult ImportMessage(LPMESSAGE lpMsg, nsIOutputStream *destOutputStream, nsMsgDeliverMode mode);
+ nsresult ImportAddresses(uint32_t *pCount, uint32_t *pTotal, const char16_t *pName, uint32_t id, nsIAddrDatabase *pDb, nsString& errors);
+private:
+ void OpenMessageStore(CMapiFolder *pNextFolder);
+ static BOOL WriteData(nsIOutputStream *pDest, const char *pData, int32_t len);
+
+ bool IsAddressBookNameUnique(nsString& name, nsString& list);
+ void MakeAddressBookNameUnique(nsString& name, nsString& list);
+ void SanitizeValue(nsString& val);
+ void SplitString(nsString& val1, nsString& val2);
+ bool BuildCard(const char16_t *pName, nsIAddrDatabase *pDb, nsIMdbRow *newRow, LPMAPIPROP pUser, nsIImportFieldMap *pFieldMap);
+ nsresult CreateList(const char16_t * pName, nsIAddrDatabase *pDb, LPMAPIPROP pUserList, nsIImportFieldMap *pFieldMap);
+
+private:
+ bool m_gotFolders;
+ bool m_gotAddresses;
+ bool m_haveMapi;
+ CMapiApi m_mapi;
+ CMapiFolderList m_folderList;
+ CMapiFolderList m_addressList;
+ CMapiFolderList m_storeList;
+ LPMDB m_lpMdb;
+};
+
+#endif /* nsOutlookMail_h___ */
diff --git a/mailnews/import/outlook/src/nsOutlookSettings.cpp b/mailnews/import/outlook/src/nsOutlookSettings.cpp
new file mode 100644
index 000000000..ec03c8ee2
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookSettings.cpp
@@ -0,0 +1,567 @@
+/* -*- 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 (Win32) settings
+
+*/
+
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsMsgUtils.h"
+#include "nsOutlookImport.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIImportService.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsIImportSettings.h"
+#include "nsOutlookSettings.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsOutlookStringBundle.h"
+#include "OutlookDebugLog.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsMsgI18N.h"
+#include <windows.h>
+#include "nsIWindowsRegKey.h"
+#include "nsComponentManagerUtils.h"
+#ifdef MOZILLA_INTERNAL_API
+#include "nsNativeCharsetUtils.h"
+#else
+#include "nsMsgI18N.h"
+#define NS_CopyNativeToUnicode(source, dest) \
+ nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest)
+#define NS_CopyUnicodeToNative(source, dest) \
+ nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), source, dest)
+#endif
+
+class OutlookSettings {
+public:
+ static nsresult FindAccountsKey(nsIWindowsRegKey **aKey);
+ static nsresult QueryAccountSubKey(nsIWindowsRegKey **aKey);
+ static nsresult GetDefaultMailAccountName(nsAString &aName);
+
+ static bool DoImport(nsIMsgAccount **aAccount);
+
+ static bool DoIMAPServer(nsIMsgAccountManager *aMgr,
+ nsIWindowsRegKey *aKey,
+ const nsString &aServerName,
+ nsIMsgAccount **aAccount);
+ static bool DoPOP3Server(nsIMsgAccountManager *aMgr,
+ nsIWindowsRegKey *aKey,
+ const nsString &aServerName,
+ nsIMsgAccount **aAccount);
+
+ static void SetIdentities(nsIMsgAccountManager *pMgr,
+ nsIMsgAccount *pAcc,
+ nsIWindowsRegKey *aKey);
+
+ static nsresult SetSmtpServer(nsIMsgAccountManager *aMgr,
+ nsIMsgAccount *aAcc,
+ nsIMsgIdentity *aId,
+ const nsString &aServer,
+ const nsString &aUser);
+ static nsresult SetSmtpServerKey(nsIMsgIdentity *aId,
+ nsISmtpServer *aServer);
+ static nsresult GetAccountName(nsIWindowsRegKey *aKey,
+ const nsString &aDefaultName,
+ nsAString &aAccountName);
+};
+
+#define OUTLOOK2003_REGISTRY_KEY "Software\\Microsoft\\Office\\Outlook\\OMI Account Manager"
+#define OUTLOOK98_REGISTRY_KEY "Software\\Microsoft\\Office\\8.0\\Outlook\\OMI Account Manager"
+
+////////////////////////////////////////////////////////////////////////
+nsresult nsOutlookSettings::Create(nsIImportSettings** aImport)
+{
+ NS_PRECONDITION(aImport != nullptr, "null ptr");
+ if (! aImport)
+ return NS_ERROR_NULL_POINTER;
+
+ *aImport = new nsOutlookSettings();
+ if (! *aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsOutlookSettings::nsOutlookSettings()
+{
+}
+
+nsOutlookSettings::~nsOutlookSettings()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsOutlookSettings, nsIImportSettings)
+
+NS_IMETHODIMP nsOutlookSettings::AutoLocate(char16_t **description, nsIFile **location, bool *_retval)
+{
+ NS_PRECONDITION(description != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!description || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *description = nsOutlookStringBundle::GetStringByID(OUTLOOKIMPORT_NAME);
+ *_retval = false;
+
+ if (location)
+ *location = nullptr;
+
+ // look for the registry key for the accounts
+ nsCOMPtr<nsIWindowsRegKey> key;
+ *_retval = NS_SUCCEEDED(OutlookSettings::FindAccountsKey(getter_AddRefs(key)));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookSettings::SetLocation(nsIFile *location)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookSettings::Import(nsIMsgAccount **localMailAccount, bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+
+ if (OutlookSettings::DoImport(localMailAccount)) {
+ *_retval = true;
+ IMPORT_LOG0("Settings import appears successful\n");
+ }
+ else {
+ *_retval = false;
+ IMPORT_LOG0("Settings import returned FALSE\n");
+ }
+
+ return NS_OK;
+}
+
+
+nsresult OutlookSettings::FindAccountsKey(nsIWindowsRegKey **aKey)
+{
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> key =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING(OUTLOOK2003_REGISTRY_KEY),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE |
+ nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS);
+
+ if (NS_FAILED(rv)) {
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING(OUTLOOK98_REGISTRY_KEY),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE |
+ nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS);
+ }
+
+ if (NS_SUCCEEDED(rv))
+ key.forget(aKey);
+
+ return rv;
+}
+
+nsresult OutlookSettings::QueryAccountSubKey(nsIWindowsRegKey **aKey)
+{
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> key =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING(OUTLOOK2003_REGISTRY_KEY),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE |
+ nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS);
+ if (NS_SUCCEEDED(rv)) {
+ key.forget(aKey);
+ return rv;
+ }
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING(OUTLOOK98_REGISTRY_KEY),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE |
+ nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS);
+ if (NS_SUCCEEDED(rv)) {
+ key.forget(aKey);
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult OutlookSettings::GetDefaultMailAccountName(nsAString &aName)
+{
+ nsCOMPtr<nsIWindowsRegKey> key;
+ nsresult rv = QueryAccountSubKey(getter_AddRefs(key));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return key->ReadStringValue(NS_LITERAL_STRING("Default Mail Account"), aName);
+}
+
+bool OutlookSettings::DoImport(nsIMsgAccount **aAccount)
+{
+ nsCOMPtr<nsIWindowsRegKey> key;
+ nsresult rv = OutlookSettings::FindAccountsKey(getter_AddRefs(key));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error finding Outlook registry account keys\n");
+ return false;
+ }
+
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create a account manager!\n");
+ return false;
+ }
+
+ nsAutoString defMailName;
+ rv = GetDefaultMailAccountName(defMailName);
+
+ uint32_t childCount;
+ key->GetChildCount(&childCount);
+
+ uint32_t accounts = 0;
+ uint32_t popCount = 0;
+ for (uint32_t i = 0; i < childCount; i++) {
+ nsAutoString keyName;
+ key->GetChildName(i, keyName);
+ nsCOMPtr<nsIWindowsRegKey> subKey;
+ rv = key->OpenChild(keyName,
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+ getter_AddRefs(subKey));
+ if (NS_FAILED(rv))
+ continue;
+
+ // Get the values for this account.
+ nsAutoCString nativeKeyName;
+ NS_CopyUnicodeToNative(keyName, nativeKeyName);
+ IMPORT_LOG1("Opened Outlook account: %s\n", nativeKeyName.get());
+
+ nsCOMPtr<nsIMsgAccount> account;
+ nsAutoString value;
+ rv = subKey->ReadStringValue(NS_LITERAL_STRING("IMAP Server"), value);
+ if (NS_SUCCEEDED(rv) &&
+ DoIMAPServer(accMgr, subKey, value, getter_AddRefs(account)))
+ accounts++;
+
+ rv = subKey->ReadStringValue(NS_LITERAL_STRING("POP3 Server"), value);
+ if (NS_SUCCEEDED(rv) &&
+ DoPOP3Server(accMgr, subKey, value, getter_AddRefs(account))) {
+ popCount++;
+ accounts++;
+ if (aAccount && account) {
+ // If we created a mail account, get rid of it since
+ // we have 2 POP accounts!
+ if (popCount > 1)
+ NS_RELEASE(*aAccount);
+ else
+ NS_ADDREF(*aAccount = account);
+ }
+ }
+
+ // Is this the default account?
+ if (account && keyName.Equals(defMailName))
+ accMgr->SetDefaultAccount(account);
+ }
+
+ // Now save the new acct info to pref file.
+ rv = accMgr->SaveAccountInfo();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file");
+
+ return accounts != 0;
+}
+
+nsresult OutlookSettings::GetAccountName(nsIWindowsRegKey *aKey,
+ const nsString &aDefaultName,
+ nsAString &aAccountName)
+{
+ nsresult rv;
+ rv = aKey->ReadStringValue(NS_LITERAL_STRING("Account Name"), aAccountName);
+ if (NS_FAILED(rv))
+ aAccountName.Assign(aDefaultName);
+
+ return NS_OK;
+}
+
+bool OutlookSettings::DoIMAPServer(nsIMsgAccountManager *aMgr,
+ nsIWindowsRegKey *aKey,
+ const nsString &aServerName,
+ nsIMsgAccount **aAccount)
+{
+ nsAutoString userName;
+ nsresult rv;
+ rv = aKey->ReadStringValue(NS_LITERAL_STRING("IMAP User Name"), userName);
+ if (NS_FAILED(rv))
+ return false;
+
+ bool result = false;
+
+ // I now have a user name/server name pair, find out if it already exists?
+ nsAutoCString nativeUserName;
+ NS_CopyUnicodeToNative(userName, nativeUserName);
+ nsAutoCString nativeServerName;
+ NS_CopyUnicodeToNative(aServerName, nativeServerName);
+ nsCOMPtr<nsIMsgIncomingServer> in;
+ rv = aMgr->FindServer(nativeUserName,
+ nativeServerName,
+ NS_LITERAL_CSTRING("imap"),
+ getter_AddRefs(in));
+ if (NS_FAILED(rv) || (in == nullptr)) {
+ // Create the incoming server and an account for it?
+ rv = aMgr->CreateIncomingServer(nativeUserName,
+ nativeServerName,
+ NS_LITERAL_CSTRING("imap"),
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv) && in) {
+ rv = in->SetType(NS_LITERAL_CSTRING("imap"));
+ // TODO SSL, auth method
+
+ IMPORT_LOG2("Created IMAP server named: %s, userName: %s\n",
+ nativeServerName.get(), nativeUserName.get());
+
+ nsAutoString prettyName;
+ if (NS_SUCCEEDED(GetAccountName(aKey, aServerName, prettyName)))
+ rv = in->SetPrettyName(prettyName);
+ // We have a server, create an account.
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = aMgr->CreateAccount(getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ rv = account->SetIncomingServer(in);
+
+ IMPORT_LOG0("Created an account and set the IMAP server as the incoming server\n");
+
+ // Fiddle with the identities
+ SetIdentities(aMgr, account, aKey);
+ result = true;
+ if (aAccount)
+ account.forget(aAccount);
+ }
+ }
+ }
+ else
+ result = true;
+
+ return result;
+}
+
+bool OutlookSettings::DoPOP3Server(nsIMsgAccountManager *aMgr,
+ nsIWindowsRegKey *aKey,
+ const nsString &aServerName,
+ nsIMsgAccount **aAccount)
+{
+ nsAutoString userName;
+ nsresult rv;
+ rv = aKey->ReadStringValue(NS_LITERAL_STRING("POP3 User Name"), userName);
+ if (NS_FAILED(rv))
+ return false;
+
+ // I now have a user name/server name pair, find out if it already exists?
+ nsAutoCString nativeUserName;
+ NS_CopyUnicodeToNative(userName, nativeUserName);
+ nsAutoCString nativeServerName;
+ NS_CopyUnicodeToNative(aServerName, nativeServerName);
+ nsCOMPtr<nsIMsgIncomingServer> in;
+ rv = aMgr->FindServer(nativeUserName,
+ nativeServerName,
+ NS_LITERAL_CSTRING("pop3"),
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv))
+ return true;
+
+ // Create the incoming server and an account for it?
+ rv = aMgr->CreateIncomingServer(nativeUserName,
+ nativeServerName,
+ NS_LITERAL_CSTRING("pop3"),
+ getter_AddRefs(in));
+ rv = in->SetType(NS_LITERAL_CSTRING("pop3"));
+
+ // TODO SSL, auth method
+
+ nsCOMPtr<nsIPop3IncomingServer> pop3Server = do_QueryInterface(in);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // set local folders as the Inbox to use for this POP3 server
+ nsCOMPtr<nsIMsgIncomingServer> localFoldersServer;
+ aMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
+
+ if (!localFoldersServer) {
+ // XXX: We may need to move this local folder creation code to the generic nsImportSettings code
+ // if the other import modules end up needing to do this too.
+ // if Local Folders does not exist already, create it
+ rv = aMgr->CreateLocalMailAccount();
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create Local Folders!\n");
+ return false;
+ }
+ aMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
+ }
+
+ // now get the account for this server
+ nsCOMPtr<nsIMsgAccount> localFoldersAccount;
+ aMgr->FindAccountForServer(localFoldersServer, getter_AddRefs(localFoldersAccount));
+ if (localFoldersAccount) {
+ nsCString localFoldersAcctKey;
+ localFoldersAccount->GetKey(localFoldersAcctKey);
+ pop3Server->SetDeferredToAccount(localFoldersAcctKey);
+ pop3Server->SetDeferGetNewMail(true);
+ }
+
+ IMPORT_LOG2("Created POP3 server named: %s, userName: %s\n",
+ nativeServerName.get(),
+ nativeUserName.get());
+
+ nsString prettyName;
+ rv = GetAccountName(aKey, aServerName, prettyName);
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = in->SetPrettyName(prettyName);
+ // We have a server, create an account.
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = aMgr->CreateAccount(getter_AddRefs(account));
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = account->SetIncomingServer(in);
+
+ IMPORT_LOG0("Created a new account and set the incoming server to the POP3 server.\n");
+
+ uint32_t leaveOnServer;
+ rv = aKey->ReadIntValue(NS_LITERAL_STRING("Leave Mail On Server"), &leaveOnServer);
+ if (NS_SUCCEEDED(rv))
+ pop3Server->SetLeaveMessagesOnServer(leaveOnServer == 1 ? true : false);
+
+ // Fiddle with the identities
+ SetIdentities(aMgr, account, aKey);
+
+ if (aAccount)
+ account.forget(aAccount);
+
+ return true;
+}
+
+void OutlookSettings::SetIdentities(nsIMsgAccountManager *aMgr,
+ nsIMsgAccount *aAcc,
+ nsIWindowsRegKey *aKey)
+{
+ // Get the relevant information for an identity
+ nsAutoString name;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Display Name"), name);
+
+ nsAutoString server;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Server"), server);
+
+ nsAutoString email;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Email Address"), email);
+
+ nsAutoString reply;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Reply To Email Address"), reply);
+
+ nsAutoString userName;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP User Name"), userName);
+
+ nsAutoString orgName;
+ aKey->ReadStringValue(NS_LITERAL_STRING("SMTP Organization Name"), orgName);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgIdentity> id;
+ if (!email.IsEmpty() && !name.IsEmpty() && !server.IsEmpty()) {
+ // The default identity, nor any other identities matched,
+ // create a new one and add it to the account.
+ rv = aMgr->CreateIdentity(getter_AddRefs(id));
+ if (id) {
+ id->SetFullName(name);
+ id->SetOrganization(orgName);
+
+ nsAutoCString nativeEmail;
+ NS_CopyUnicodeToNative(email, nativeEmail);
+ id->SetEmail(nativeEmail);
+ if (!reply.IsEmpty()) {
+ nsAutoCString nativeReply;
+ NS_CopyUnicodeToNative(reply, nativeReply);
+ id->SetReplyTo(nativeReply);
+ }
+ aAcc->AddIdentity(id);
+
+ nsAutoCString nativeName;
+ NS_CopyUnicodeToNative(name, nativeName);
+ IMPORT_LOG0("Created identity and added to the account\n");
+ IMPORT_LOG1("\tname: %s\n", nativeName.get());
+ IMPORT_LOG1("\temail: %s\n", nativeEmail.get());
+ }
+ }
+
+ if (userName.IsEmpty()) {
+ nsCOMPtr<nsIMsgIncomingServer> incomingServer;
+ rv = aAcc->GetIncomingServer(getter_AddRefs(incomingServer));
+ if (NS_SUCCEEDED(rv) && incomingServer) {
+ nsAutoCString nativeUserName;
+ rv = incomingServer->GetUsername(nativeUserName);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get UserName from incomingServer");
+ NS_CopyNativeToUnicode(nativeUserName, userName);
+ }
+ }
+
+ SetSmtpServer(aMgr, aAcc, id, server, userName);
+}
+
+nsresult OutlookSettings::SetSmtpServerKey(nsIMsgIdentity *aId,
+ nsISmtpServer *aServer)
+{
+ nsAutoCString smtpServerKey;
+ aServer->GetKey(getter_Copies(smtpServerKey));
+ return aId->SetSmtpServerKey(smtpServerKey);
+}
+
+nsresult OutlookSettings::SetSmtpServer(nsIMsgAccountManager *aMgr,
+ nsIMsgAccount *aAcc,
+ nsIMsgIdentity *aId,
+ const nsString &aServer,
+ const nsString &aUser)
+{
+ nsresult rv;
+ nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString nativeUserName;
+ NS_CopyUnicodeToNative(aUser, nativeUserName);
+ nsAutoCString nativeServerName;
+ NS_CopyUnicodeToNative(aServer, nativeServerName);
+ nsCOMPtr<nsISmtpServer> foundServer;
+ rv = smtpService->FindServer(nativeUserName.get(),
+ nativeServerName.get(),
+ getter_AddRefs(foundServer));
+ if (NS_SUCCEEDED(rv) && foundServer) {
+ if (aId)
+ SetSmtpServerKey(aId, foundServer);
+ IMPORT_LOG1("SMTP server already exists: %s\n",
+ nativeServerName.get());
+ return rv;
+ }
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpService->CreateServer(getter_AddRefs(smtpServer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ smtpServer->SetHostname(nativeServerName);
+ if (!aUser.IsEmpty())
+ smtpServer->SetUsername(nativeUserName);
+
+ if (aId)
+ SetSmtpServerKey(aId, smtpServer);
+
+ // TODO SSL, auth method
+ IMPORT_LOG1("Ceated new SMTP server: %s\n",
+ nativeServerName.get());
+ return NS_OK;
+}
+
diff --git a/mailnews/import/outlook/src/nsOutlookSettings.h b/mailnews/import/outlook/src/nsOutlookSettings.h
new file mode 100644
index 000000000..ea074e5b4
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookSettings.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsOutlookSettings_h___
+#define nsOutlookSettings_h___
+
+#include "nsIImportSettings.h"
+
+
+class nsOutlookSettings : public nsIImportSettings {
+public:
+ nsOutlookSettings();
+
+ static nsresult Create(nsIImportSettings** aImport);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIImportSettings interface
+ NS_DECL_NSIIMPORTSETTINGS
+
+private:
+ virtual ~nsOutlookSettings();
+
+};
+
+#endif /* nsOutlookSettings_h___ */
diff --git a/mailnews/import/outlook/src/nsOutlookStringBundle.cpp b/mailnews/import/outlook/src/nsOutlookStringBundle.cpp
new file mode 100644
index 000000000..826e30e40
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookStringBundle.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "prprf.h"
+#include "prmem.h"
+#include "nsCOMPtr.h"
+#include "nsMsgUtils.h"
+#include "nsIStringBundle.h"
+#include "nsOutlookStringBundle.h"
+#include "nsIServiceManager.h"
+#include "nsIURI.h"
+#include "mozilla/Services.h"
+
+#define OUTLOOK_MSGS_URL "chrome://messenger/locale/outlookImportMsgs.properties"
+
+nsIStringBundle * nsOutlookStringBundle::m_pBundle = nullptr;
+
+nsIStringBundle *nsOutlookStringBundle::GetStringBundle(void)
+{
+ if (m_pBundle)
+ return m_pBundle;
+
+ char* propertyURL = OUTLOOK_MSGS_URL;
+ nsIStringBundle* sBundle = nullptr;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ if (sBundleService) {
+ sBundleService->CreateBundle(propertyURL, &sBundle);
+ }
+
+ m_pBundle = sBundle;
+
+ return sBundle;
+}
+
+void nsOutlookStringBundle::GetStringByID(int32_t stringID, nsString& result)
+{
+ char16_t *ptrv = GetStringByID(stringID);
+ result = ptrv;
+ FreeString(ptrv);
+}
+
+char16_t *nsOutlookStringBundle::GetStringByID(int32_t stringID)
+{
+ if (m_pBundle)
+ m_pBundle = GetStringBundle();
+
+ if (m_pBundle) {
+ char16_t *ptrv = nullptr;
+ nsresult rv = m_pBundle->GetStringFromID(stringID, &ptrv);
+
+ if (NS_SUCCEEDED(rv) && ptrv)
+ return ptrv;
+ }
+
+ nsString resultString;
+ resultString.AppendLiteral("[StringID ");
+ resultString.AppendInt(stringID);
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
+
+void nsOutlookStringBundle::Cleanup(void)
+{
+ if (m_pBundle)
+ m_pBundle->Release();
+ m_pBundle = nullptr;
+}
diff --git a/mailnews/import/outlook/src/nsOutlookStringBundle.h b/mailnews/import/outlook/src/nsOutlookStringBundle.h
new file mode 100644
index 000000000..1351088d4
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookStringBundle.h
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsOutlookStringBundle_H__
+#define _nsOutlookStringBundle_H__
+
+#include "nsCRTGlue.h"
+#include "nsStringGlue.h"
+
+class nsIStringBundle;
+
+class nsOutlookStringBundle {
+public:
+ static char16_t * GetStringByID(int32_t stringID);
+ static void GetStringByID(int32_t stringID, nsString& result);
+ static nsIStringBundle * GetStringBundle(void); // don't release
+ static void FreeString(char16_t *pStr) { NS_Free(pStr);}
+ static void Cleanup(void);
+private:
+ static nsIStringBundle * m_pBundle;
+};
+
+
+
+#define OUTLOOKIMPORT_NAME 2000
+#define OUTLOOKIMPORT_DESCRIPTION 2010
+#define OUTLOOKIMPORT_MAILBOX_SUCCESS 2002
+#define OUTLOOKIMPORT_MAILBOX_BADPARAM 2003
+#define OUTLOOKIMPORT_MAILBOX_CONVERTERROR 2004
+#define OUTLOOKIMPORT_ADDRNAME 2005
+#define OUTLOOKIMPORT_ADDRESS_SUCCESS 2006
+#define OUTLOOKIMPORT_ADDRESS_BADPARAM 2007
+#define OUTLOOKIMPORT_ADDRESS_BADSOURCEFILE 2008
+#define OUTLOOKIMPORT_ADDRESS_CONVERTERROR 2009
+
+
+#endif /* _nsOutlookStringBundle_H__ */
diff --git a/mailnews/import/outlook/src/rtfDecoder.cpp b/mailnews/import/outlook/src/rtfDecoder.cpp
new file mode 100644
index 000000000..837beec0b
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfDecoder.cpp
@@ -0,0 +1,520 @@
+/* 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 <stack>
+#include <map>
+#include <sstream>
+#include "Windows.h"
+#include "rtfDecoder.h"
+
+#define SIZEOF(x) (sizeof(x)/sizeof((x)[0]))
+#define IS_DIGIT(i) ((i) >= '0' && (i) <= '9')
+#define IS_ALPHA(VAL) (((VAL) >= 'a' && (VAL) <= 'z') || ((VAL) >= 'A' && (VAL) <= 'Z'))
+
+inline int HexToInt(char ch)
+{
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ return ch-'0';
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return ch-'A'+10;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return ch-'a'+10;
+ default:
+ return 0;
+ }
+}
+
+inline int CharsetToCP(int charset)
+{
+ // We don't know the Code page for the commented out charsets.
+ switch (charset) {
+ case 0: return 1252; // ANSI
+ case 1: return 0; // Default
+//case 2: return 42; // Symbol
+ case 2: return 1252; // Symbol
+ case 77: return 10000; // Mac Roman
+ case 78: return 10001; // Mac Shift Jis
+ case 79: return 10003; // Mac Hangul
+ case 80: return 10008; // Mac GB2312
+ case 81: return 10002; // Mac Big5
+//case 82: Mac Johab (old)
+ case 83: return 10005; // Mac Hebrew
+ case 84: return 10004; // Mac Arabic
+ case 85: return 10006; // Mac Greek
+ case 86: return 10081; // Mac Turkish
+ case 87: return 10021; // Mac Thai
+ case 88: return 10029; // Mac East Europe
+ case 89: return 10007; // Mac Russian
+ case 128: return 932; // Shift JIS
+ case 129: return 949; // Hangul
+ case 130: return 1361; // Johab
+ case 134: return 936; // GB2312
+ case 136: return 950; // Big5
+ case 161: return 1253; // Greek
+ case 162: return 1254; // Turkish
+ case 163: return 1258; // Vietnamese
+ case 177: return 1255; // Hebrew
+ case 178: return 1256; // Arabic
+//case 179: Arabic Traditional (old)
+//case 180: Arabic user (old)
+//case 181: Hebrew user (old)
+ case 186: return 1257; // Baltic
+ case 204: return 1251; // Russian
+ case 222: return 874; // Thai
+ case 238: return 1250; // Eastern European
+ case 254: return 437; // PC 437
+ case 255: return 850; // OEM
+ default: return CP_ACP;
+ }
+}
+
+struct FontInfo {
+ enum Options {has_fcharset = 0x0001,
+ has_cpg = 0x0002};
+ unsigned int options;
+ int fcharset;
+ unsigned int cpg;
+ FontInfo() : options(0), fcharset(0), cpg(0xFFFFFFFF) {}
+ unsigned int Codepage()
+ {
+ if (options & has_cpg)
+ return cpg;
+ else if (options & has_fcharset)
+ return CharsetToCP(fcharset);
+ else return 0xFFFFFFFF;
+ }
+};
+typedef std::map<int, FontInfo> Fonttbl;
+
+struct LocalState {
+ bool fonttbl; // When fonts are being defined
+ int f; // Index of the font being defined/used; defines the codepage if no \cpg
+ unsigned int uc; // ucN keyword value; its default is 1
+ unsigned int codepage;// defined by \cpg
+};
+typedef std::stack<LocalState> StateStack;
+
+struct GlobalState {
+ enum Pcdata_state { pcdsno, pcdsin, pcdsfinished };
+ std::istream& stream;
+ Fonttbl fonttbl;
+ StateStack stack;
+ unsigned int codepage; // defined by \ansi, \mac, \pc, \pca, and \ansicpgN
+ int deff;
+ std::stringstream pcdata_a;
+ unsigned int pcdata_a_codepage;
+ Pcdata_state pcdata_a_state;
+
+ GlobalState(std::istream& s)
+ : stream(s), codepage(CP_ACP), deff(-1), pcdata_a_state(pcdsno)
+ {
+ LocalState st;
+ st.fonttbl = false;
+ st.f = -1;
+ st.uc = 1;
+ st.codepage = 0xFFFFFFFF;
+ stack.push(st);
+ }
+ unsigned int GetCurrentCP()
+ {
+ if (stack.top().codepage != 0xFFFFFFFF) // \cpg in use
+ return stack.top().codepage;
+ // \cpg not used; use font settings
+ int f = (stack.top().f != -1) ? stack.top().f : deff;
+ if (f != -1) {
+ Fonttbl::iterator iter = fonttbl.find(f);
+ if (iter != fonttbl.end()) {
+ unsigned int cp = iter->second.Codepage();
+ if (cp != 0xFFFFFFFF)
+ return cp;
+ }
+ }
+ return codepage; // No overrides; use the top-level legacy setting
+ }
+};
+
+struct Keyword {
+ char name[33];
+ bool hasVal;
+ int val;
+};
+
+class Lexem {
+public:
+ enum Type {ltGroupBegin, ltGroupEnd, ltKeyword, ltPCDATA_A, ltPCDATA_W,
+ ltBDATA, ltEOF, ltError};
+ Lexem(Type t=ltError) : m_type(t) {}
+ Lexem(Lexem& from) // Move pointers when copying
+ {
+ switch (m_type = from.m_type) {
+ case ltKeyword:
+ m_keyword = from.m_keyword;
+ break;
+ case ltPCDATA_A:
+ m_pcdata_a = from.m_pcdata_a;
+ break;
+ case ltPCDATA_W:
+ m_pcdata_w = from.m_pcdata_w;
+ break;
+ case ltBDATA:
+ m_bdata = from.m_bdata;
+ from.m_type = ltError;
+ break;
+ }
+ }
+ ~Lexem() { Clear(); }
+ Lexem& operator = (Lexem& from)
+ {
+ if (&from != this) {
+ Clear();
+ switch (m_type = from.m_type) {
+ case ltKeyword:
+ m_keyword = from.m_keyword;
+ break;
+ case ltPCDATA_A:
+ m_pcdata_a = from.m_pcdata_a;
+ break;
+ case ltPCDATA_W:
+ m_pcdata_w = from.m_pcdata_w;
+ break;
+ case ltBDATA:
+ m_bdata = from.m_bdata;
+ from.m_type = ltError;
+ break;
+ }
+ }
+ return *this;
+ }
+ Type type() const { return m_type; }
+ void SetPCDATA_A(char chdata)
+ {
+ Clear();
+ m_pcdata_a = chdata;
+ m_type = ltPCDATA_A;
+ }
+ void SetPCDATA_W(wchar_t chdata)
+ {
+ Clear();
+ m_pcdata_w = chdata;
+ m_type = ltPCDATA_W;
+ }
+ void SetBDATA(const char* data, int sz)
+ {
+ char* tmp = new char[sz]; // to allow getting the data from itself
+ if (tmp) {
+ memcpy(tmp, data, sz);
+ Clear();
+ m_bdata.data = tmp;
+ m_bdata.sz = sz;
+ m_type = ltBDATA;
+ }
+ else m_type = ltError;
+ }
+ void SetKeyword(const Keyword& src)
+ {
+ Clear();
+ m_type = ltKeyword;
+ m_keyword = src;
+ }
+ void SetKeyword(const char* name, bool hasVal=false, int val=0)
+ {
+ char tmp[SIZEOF(m_keyword.name)];
+ strncpy(tmp, name, SIZEOF(m_keyword.name)-1); // to allow copy drom itself
+ tmp[SIZEOF(m_keyword.name)-1]=0;
+ Clear();
+ m_type = ltKeyword;
+ memcpy(m_keyword.name, tmp, SIZEOF(m_keyword.name));
+ m_keyword.hasVal=hasVal;
+ m_keyword.val=val;
+ }
+ const char* KeywordName() const {
+ return (m_type == ltKeyword) ? m_keyword.name : 0; }
+ const int* KeywordVal() const {
+ return ((m_type == ltKeyword) && m_keyword.hasVal) ? &m_keyword.val : 0; }
+ char pcdata_a() const { return (m_type == ltPCDATA_A) ? m_pcdata_a : 0; }
+ wchar_t pcdata_w() const { return (m_type == ltPCDATA_W) ? m_pcdata_w : 0; }
+ const char* bdata() const { return (m_type == ltBDATA) ? m_bdata.data : 0; }
+ int bdata_sz() const { return (m_type == ltBDATA) ? m_bdata.sz : 0; }
+ static Lexem eof;
+ static Lexem groupBegin;
+ static Lexem groupEnd;
+ static Lexem error;
+private:
+ struct BDATA {
+ size_t sz;
+ char* data;
+ };
+
+ Type m_type;
+ union {
+ Keyword m_keyword;
+ char m_pcdata_a;
+ wchar_t m_pcdata_w;
+ BDATA m_bdata;
+ };
+ // This function leaves the object in the broken state. Must be followed
+ // by a correct initialization.
+ void Clear()
+ {
+ switch (m_type) {
+ case ltBDATA:
+ delete[] m_bdata.data;
+ break;
+ }
+// m_type = ltError;
+ }
+};
+
+Lexem Lexem::eof(ltEOF);
+Lexem Lexem::groupBegin(ltGroupBegin);
+Lexem Lexem::groupEnd(ltGroupEnd);
+Lexem Lexem::error(ltError);
+
+// This function moves pos. When calling the function, pos must be next to the
+// backslash; pos must be in the same sequence and before end!
+Keyword GetKeyword(std::istream& stream)
+{
+ Keyword keyword = {"", false, 0};
+ char ch;
+ if (stream.get(ch).eof())
+ return keyword;
+ // Control word; maybe delimiter and value
+ if (IS_ALPHA(ch)) {
+ int i = 0;
+ do {
+ // We take up to 32 characters into account, skipping over extra
+ // characters (allowing for some non-conformant implementation).
+ if (i < 32)
+ keyword.name[i++] = ch;
+ } while (!stream.get(ch).eof() && IS_ALPHA(ch));
+ keyword.name[i] = 0; // NULL-terminating
+ if (!stream.eof() && (IS_DIGIT(ch) || (ch == '-'))) { // Value begin
+ keyword.hasVal = true;
+ bool negative = (ch == '-');
+ if (negative) stream.get(ch);
+ i = 0;
+ while (!stream.eof() && IS_DIGIT(ch)) {
+ // We take into account only 10 digits, skip other. Older specs stated
+ // that we must be ready for an arbitrary number of digits.
+ if (i++ < 10)
+ keyword.val = keyword.val*10 + (ch - '0');
+ stream.get(ch);
+ }
+ if (negative) keyword.val = -keyword.val;
+ }
+ // End of control word; the space is just a delimiter - skip it
+ if (!stream.eof() && !(ch == ' '))
+ stream.unget();
+ }
+ else { // Control symbol
+ keyword.name[0] = ch, keyword.name[1] = 0;
+ }
+ return keyword;
+}
+
+Lexem GetLexem(std::istream& stream)
+{
+ Lexem result;
+ // We always stay at the beginning of the next lexem or a crlf
+ // If it's a brace then it's group begin/end
+ // If it's a backslash -> Preprocess
+ // - if it's a \u or \' -> make UTF16 character
+ // - else it's a keyword -> Process (e.g., remember the codepage)
+ // - (if the keyword is \bin then the following is #BDATA)
+ // If it's some other character -> Preprocess
+ // - if it's 0x09 -> it's the keyword \tab
+ // - else it's a PCDATA
+ char ch;
+ while (!stream.get(ch).eof() && ((ch == '\n') || (ch == '\r'))); // Skip crlf
+ if (stream.eof())
+ result = Lexem::eof;
+ else {
+ switch (ch) {
+ case '{': // Group begin
+ case '}': // Group end
+ result = (ch == '{') ? Lexem::groupBegin : Lexem::groupEnd;
+ break;
+ case '\\': // Keyword
+ result.SetKeyword(GetKeyword(stream));
+ break;
+ case '\t': // tab
+ result.SetKeyword("tab");
+ break;
+ default: // PSDATA?
+ result.SetPCDATA_A(ch);
+ break;
+ }
+ }
+ return result;
+}
+
+void PreprocessLexem(/*inout*/Lexem& lexem, std::istream& stream, int uc)
+{
+ if (lexem.type() == Lexem::ltKeyword) {
+ if (lexem.KeywordName()[0] == 0) // Empty keyword - maybe eof?
+ lexem = Lexem::error;
+ else if (eq(lexem.KeywordName(), "u")) {
+ // Unicode character - get the UTF16 and skip the uc characters
+ if (const int* val = lexem.KeywordVal()) {
+ lexem.SetPCDATA_W(*val);
+ stream.ignore(uc);
+ }
+ else lexem = Lexem::error;
+ }
+ else if (eq(lexem.KeywordName(), "'")) {
+ // 8-bit character (\'hh) -> use current codepage
+ char ch, ch1;
+ if (!stream.get(ch).eof()) ch1 = HexToInt(ch);
+ if (!stream.get(ch).eof()) (ch1 <<= 4) += HexToInt(ch);
+ lexem.SetPCDATA_A(ch1);
+ }
+ else if (eq(lexem.KeywordName(), "\\") || eq(lexem.KeywordName(), "{") ||
+ eq(lexem.KeywordName(), "}")) // escaped characters
+ lexem.SetPCDATA_A(lexem.KeywordName()[0]);
+ else if (eq(lexem.KeywordName(), "bin")) {
+ if (const int* i = lexem.KeywordVal()) {
+ char* data = new char[*i];
+ if (data) {
+ stream.read(data, *i);
+ if (stream.fail())
+ lexem = Lexem::error;
+ else
+ lexem.SetBDATA(data, *i);
+ delete[] data;
+ }
+ else lexem = Lexem::error;
+ }
+ else lexem = Lexem::error;
+ }
+ else if (eq(lexem.KeywordName(), "\n") || eq(lexem.KeywordName(), "\r")) {
+ // escaped cr or lf
+ lexem.SetKeyword("par");
+ }
+ }
+}
+
+void UpdateState(const Lexem& lexem, /*inout*/GlobalState& globalState)
+{
+ switch (globalState.pcdata_a_state) {
+ case GlobalState::pcdsfinished: // Last time we finished the pcdata
+ globalState.pcdata_a_state = GlobalState::pcdsno;
+ break;
+ case GlobalState::pcdsin:
+ // to be reset later if still in the pcdata
+ globalState.pcdata_a_state = GlobalState::pcdsfinished;
+ break;
+ }
+
+ switch (lexem.type()) {
+ case Lexem::ltGroupBegin:
+ globalState.stack.push(globalState.stack.top());
+ break;
+ case Lexem::ltGroupEnd:
+ globalState.stack.pop();
+ break;
+ case Lexem::ltKeyword:
+ {
+ const int* val = lexem.KeywordVal();
+ if (eq(lexem.KeywordName(), "ansi")) globalState.codepage = CP_ACP;
+ else if (eq(lexem.KeywordName(), "mac")) globalState.codepage = CP_MACCP;
+ else if (eq(lexem.KeywordName(), "pc")) globalState.codepage = 437;
+ else if (eq(lexem.KeywordName(), "pca")) globalState.codepage = 850;
+ else if (eq(lexem.KeywordName(), "ansicpg") && val)
+ globalState.codepage = static_cast<unsigned int>(*val);
+ else if (eq(lexem.KeywordName(), "deff") && val)
+ globalState.deff = *val;
+ else if (eq(lexem.KeywordName(), "fonttbl")) globalState.stack.top().fonttbl = true;
+ else if (eq(lexem.KeywordName(), "f") && val) {
+ globalState.stack.top().f = *val;
+ }
+ else if (eq(lexem.KeywordName(), "fcharset") &&
+ globalState.stack.top().fonttbl &&
+ (globalState.stack.top().f != -1) && val) {
+ FontInfo& f = globalState.fonttbl[globalState.stack.top().f];
+ f.options |= FontInfo::has_fcharset;
+ f.fcharset = *val;
+ }
+ else if (eq(lexem.KeywordName(), "cpg") && val) {
+ if (globalState.stack.top().fonttbl && (globalState.stack.top().f != -1)) { // Defining a font
+ FontInfo& f = globalState.fonttbl[globalState.stack.top().f];
+ f.options |= FontInfo::has_cpg;
+ f.cpg = *val;
+ }
+ else { // Overriding the codepage for the block - may be in filenames
+ globalState.stack.top().codepage = *val;
+ }
+ }
+ else if (eq(lexem.KeywordName(), "plain"))
+ globalState.stack.top().f = -1;
+ else if (eq(lexem.KeywordName(), "uc") && val)
+ globalState.stack.top().uc = *val;
+ }
+ break;
+ case Lexem::ltPCDATA_A:
+ if (globalState.pcdata_a_state == GlobalState::pcdsno) // Beginning of the pcdata
+ globalState.pcdata_a_codepage = globalState.GetCurrentCP(); // to use later to convert to utf16
+ globalState.pcdata_a_state = GlobalState::pcdsin;
+ globalState.pcdata_a << lexem.pcdata_a();
+ break;
+ }
+}
+
+void DecodeRTF(std::istream& rtf, CRTFDecoder& decoder)
+{
+ // Check if this is the rtf
+ Lexem lexem = GetLexem(rtf);
+ if (lexem.type() != Lexem::ltGroupBegin)
+ return;
+ decoder.BeginGroup();
+ lexem = GetLexem(rtf);
+ if ((lexem.type() != Lexem::ltKeyword) || !eq(lexem.KeywordName(), "rtf") ||
+ !lexem.KeywordVal() || (*lexem.KeywordVal() != 1))
+ return;
+ decoder.Keyword(lexem.KeywordName(), lexem.KeywordVal());
+
+ GlobalState state(rtf);
+ // Level is the count of elements in the stack
+
+ while (!state.stream.eof() && (state.stack.size()>0)) { // Don't go past the global group
+ lexem = GetLexem(state.stream);
+ PreprocessLexem(lexem, state.stream, state.stack.top().uc);
+ UpdateState(lexem, state);
+
+ if (state.pcdata_a_state == GlobalState::pcdsfinished) {
+ std::string s = state.pcdata_a.str();
+ int sz = ::MultiByteToWideChar(state.pcdata_a_codepage, 0, s.c_str(), s.size(), 0, 0);
+ if (sz) {
+ wchar_t* data = new wchar_t[sz];
+ ::MultiByteToWideChar(state.pcdata_a_codepage, 0, s.c_str(), s.size(), data, sz);
+ decoder.PCDATA(data, sz);
+ delete[] data;
+ }
+ state.pcdata_a.str(""); // reset
+ }
+
+ switch (lexem.type()) {
+ case Lexem::ltGroupBegin:
+ decoder.BeginGroup();
+ break;
+ case Lexem::ltGroupEnd:
+ decoder.EndGroup();
+ break;
+ case Lexem::ltKeyword:
+ decoder.Keyword(lexem.KeywordName(), lexem.KeywordVal());
+ break;
+ case Lexem::ltPCDATA_W:
+ {
+ wchar_t ch = lexem.pcdata_w();
+ decoder.PCDATA(&ch, 1);
+ }
+ break;
+ case Lexem::ltBDATA:
+ decoder.BDATA(lexem.bdata(), lexem.bdata_sz());
+ break;
+ case Lexem::ltError:
+ break; // Just silently skip the erroneous data - basic error recovery
+ }
+ } // while
+} // DecodeRTF
diff --git a/mailnews/import/outlook/src/rtfDecoder.h b/mailnews/import/outlook/src/rtfDecoder.h
new file mode 100644
index 000000000..85a17721d
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfDecoder.h
@@ -0,0 +1,22 @@
+/* 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 <istream>
+
+template <size_t len>
+inline bool eq(const char* str1, const char (&str2)[len])
+{
+ return ::strncmp(str1, str2, len) == 0;
+};
+
+class CRTFDecoder {
+public:
+ virtual void BeginGroup() = 0;
+ virtual void EndGroup() = 0;
+ virtual void Keyword(const char* name, const int* Val) = 0;
+ virtual void PCDATA(const wchar_t* data, size_t cch) = 0;
+ virtual void BDATA(const char* data, size_t sz) = 0;
+};
+
+void DecodeRTF(std::istream& rtf, CRTFDecoder& decoder);
diff --git a/mailnews/import/outlook/src/rtfMailDecoder.cpp b/mailnews/import/outlook/src/rtfMailDecoder.cpp
new file mode 100644
index 000000000..9a6b34725
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfMailDecoder.cpp
@@ -0,0 +1,79 @@
+/* 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 "rtfMailDecoder.h"
+
+void CRTFMailDecoder::BeginGroup()
+{
+ ClearState(sAsterisk);
+ SetState(sBeginGroup);
+ if (m_skipLevel)
+ ++m_skipLevel;
+}
+
+void CRTFMailDecoder::EndGroup()
+{
+ ClearState(sAsterisk|sBeginGroup);
+ if (m_skipLevel)
+ --m_skipLevel;
+}
+
+void CRTFMailDecoder::AddText(const wchar_t* txt, size_t cch)
+{
+ if (!IsHtmlRtf()) {
+ if (cch == static_cast<size_t>(-1))
+ m_text += txt;
+ else
+ m_text.append(txt, cch);
+ }
+}
+
+void CRTFMailDecoder::Keyword(const char* name, const int* Val)
+{
+ bool asterisk = IsAsterisk(); ClearState(sAsterisk); // for inside use only
+ bool beginGroup = IsBeginGroup(); ClearState(sBeginGroup); // for inside use only
+ if (!m_skipLevel) {
+ if (eq(name, "*") && beginGroup) SetState(sAsterisk);
+ else if (asterisk) {
+ if (eq(name, "htmltag") && (m_mode == mHTML)) { // \*\htmltag -> don't ignore; include the following text
+ }
+ else ++m_skipLevel;
+ }
+ else if (eq(name, "htmlrtf")) {
+ if (Val && (*Val==0))
+ ClearState(sHtmlRtf);
+ else
+ SetState(sHtmlRtf);
+ }
+ else if (eq(name, "par") || eq(name, "line")) {
+ AddText(L"\r\n");
+ }
+ else if (eq(name, "tab")) {
+ AddText(L"\t");
+ }
+ else if (eq(name, "rquote")) {
+ AddText(L"\x2019"); // Unicode right single quotation mark
+ }
+ else if (eq(name, "fromtext") && (m_mode==mNone)) { // avoid double "fromX"
+ m_mode = mText;
+ }
+ else if (eq(name, "fromhtml") && (m_mode==mNone)) { // avoid double "fromX"
+ m_mode = mHTML;
+ }
+ else if (eq(name, "fonttbl") || eq(name, "colortbl") || eq(name, "stylesheet") || eq(name, "pntext"))
+ ++m_skipLevel;
+ }
+}
+
+void CRTFMailDecoder::PCDATA(const wchar_t* data, size_t cch)
+{
+ ClearState(sAsterisk|sBeginGroup);
+ if (!m_skipLevel)
+ AddText(data, cch);
+}
+
+void CRTFMailDecoder::BDATA(const char* data, size_t sz)
+{
+ ClearState(sAsterisk|sBeginGroup);
+}
diff --git a/mailnews/import/outlook/src/rtfMailDecoder.h b/mailnews/import/outlook/src/rtfMailDecoder.h
new file mode 100644
index 000000000..2b621577f
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfMailDecoder.h
@@ -0,0 +1,41 @@
+/* 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 "mozilla/Attributes.h"
+#include <string>
+#include "rtfDecoder.h"
+
+class CRTFMailDecoder: public CRTFDecoder {
+public:
+ enum Mode {mNone, mText, mHTML};
+ CRTFMailDecoder() : m_mode(mNone), m_state(sNormal), m_skipLevel(0) {}
+ void BeginGroup() override;
+ void EndGroup() override;
+ void Keyword(const char* name, const int* Val) override;
+ void PCDATA(const wchar_t* data, size_t cch) override;
+ void BDATA(const char* data, size_t sz) override;
+ const wchar_t* text() { return m_text.c_str(); }
+ std::wstring::size_type textSize() { return m_text.size(); }
+ Mode mode() { return m_mode; }
+private:
+ enum State {sNormal = 0x0000,
+ sBeginGroup = 0x0001,
+ sAsterisk = 0x0002,
+ sHtmlRtf = 0x0004};
+
+ std::wstring m_text;
+ Mode m_mode;
+ unsigned int m_state; // bitmask of State
+// bool m_beginGroup; // true just after the {
+//bool m_asterisk; // true just after the {\*
+ int m_skipLevel; // if >0 then we ignore everything
+// bool m_htmlrtf;
+ inline void SetState(unsigned int s) { m_state |= s; }
+ inline void ClearState(unsigned int s) { m_state &= ~s; }
+ inline bool CheckState(State s) { return (m_state & s) != 0; }
+ inline bool IsAsterisk() { return CheckState(sAsterisk); }
+ inline bool IsBeginGroup() { return CheckState(sBeginGroup); }
+ inline bool IsHtmlRtf() { return CheckState(sHtmlRtf); }
+ void AddText(const wchar_t* txt, size_t cch=static_cast<size_t>(-1));
+};
diff --git a/mailnews/import/public/moz.build b/mailnews/import/public/moz.build
new file mode 100644
index 000000000..69efefb66
--- /dev/null
+++ b/mailnews/import/public/moz.build
@@ -0,0 +1,21 @@
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsIImportABDescriptor.idl',
+ 'nsIImportAddressBooks.idl',
+ 'nsIImportFieldMap.idl',
+ 'nsIImportFilters.idl',
+ 'nsIImportGeneric.idl',
+ 'nsIImportMail.idl',
+ 'nsIImportMailboxDescriptor.idl',
+ 'nsIImportMimeEncode.idl',
+ 'nsIImportModule.idl',
+ 'nsIImportService.idl',
+ 'nsIImportSettings.idl',
+]
+
+XPIDL_MODULE = 'import'
+
diff --git a/mailnews/import/public/nsIImportABDescriptor.idl b/mailnews/import/public/nsIImportABDescriptor.idl
new file mode 100644
index 000000000..fe0ca1b31
--- /dev/null
+++ b/mailnews/import/public/nsIImportABDescriptor.idl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+
+ Interface for importing mail - ui provided by the import module. If
+ you wish to provide your own UI then implement the nsIImportGeneric
+ interface.
+
+ Can I get an attribute set method to take a const value???
+
+ */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+
+/**
+ * Implementation Note:
+ *
+ * The default implementation can be obtained from
+ * nsIImportService::CreateNewABDescriptor();
+ *
+ * You should only be interested in using this class if you implement
+ * the nsIImportAddressBooks interface in which case, just using the service to
+ * create new ones should work fine for you. If not, implement your
+ * own.
+ */
+[scriptable, uuid(2d8983b2-cea6-4ae2-9145-eb772481fa18)]
+interface nsIImportABDescriptor : nsISupports
+{
+ /**
+ * use the following 2 attributes however you'd like to
+ * refer to a specific address book
+ */
+ attribute unsigned long identifier;
+ attribute unsigned long ref;
+
+ /**
+ * Doesn't have to be accurate, this is merely used to report progress.
+ * If you're importing a file, using file size and reporting progress
+ * as the number of bytes processed so far makes sense. For other formats
+ * returning the number of records may make more sense.
+ */
+ attribute unsigned long size;
+
+ /**
+ * The preferred name for this address book. Depending upon how the
+ * user selected import, the caller of the nsIImportAddressBooks interface
+ * may use this name to create the destination address book or it may
+ * ignore it. However, this must be provided in all cases as it is
+ * also displayed in the UI to the user.
+ */
+ attribute AString preferredName;
+
+ /**
+ * For address books that want a file descriptor to locate the address book.
+ * For formats that do not, use identifier & ref to refer to the address book
+ * OR implement your own nsIImportABDescriptor that contains additional data
+ * necessary to identify specific address books,
+ */
+ attribute nsIFile abFile;
+
+ /**
+ * Set by the UI to indicate whether or not this address book should be imported.
+ */
+ attribute boolean import;
+};
diff --git a/mailnews/import/public/nsIImportAddressBooks.idl b/mailnews/import/public/nsIImportAddressBooks.idl
new file mode 100644
index 000000000..a6f9a7800
--- /dev/null
+++ b/mailnews/import/public/nsIImportAddressBooks.idl
@@ -0,0 +1,153 @@
+/* -*- 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/. */
+
+/*
+ Interface for importing address books using the standard UI. Address
+ book import occurs in several forms (yuck!).
+ The destination can be 1..n new address books corresponding to the source
+ format. For instance a text file would import into a new address book
+ with the same name as the text file.
+ The destination can be 1 pre-defined address book, all entries will be
+ added to the supplied address book - this allows the address book UI so provide
+ an import command specific for an individual address book.
+
+ The source can import 1 or multiple address books.
+ The address books can be auto-discoverable or user specified.
+ The address books can require field mapping or not.
+
+ All of this is rather complicated but it should work out OK.
+ 1) The first UI
+ panel will allow selection of the address book and will indicate to the user
+ if the address book will be imported into an existing address book or new address
+ books. (This could be 2 separate xul UI's?).
+ 2) The second panel will show field mapping if it is required - if it is required then
+ there will be one panel per address book for formats that support multiple
+ address books. If it is not required then there will be no second panel.
+ 3) Show the progress dialog for the import - this could be per address book if
+ mapping is required? what to do, what to doooooo.....
+ 4) All done, maybe a what was done panel??
+*/
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIArray;
+interface nsIImportABDescriptor;
+interface nsIAddrDatabase;
+interface nsIImportFieldMap;
+
+[scriptable, uuid(6bba48be-331c-41e3-bc9f-c2ea3754d977)]
+interface nsIImportAddressBooks : nsISupports
+{
+
+ /*
+ Does this interface supports 1 or 1..n address books. You only
+ get to choose 1 location so for formats where 1..n address books
+ are imported from a directory, then return true. For a 1 to 1 relationship
+ between location and address books return false.
+ */
+ boolean GetSupportsMultiple();
+
+ /*
+ If the address book is not found via a file location.then return true
+ along with a description string of how or where the address book is
+ located. If it is a file location then return false.
+ If true, return a string like: "Outlook Express standard address book,
+ also known as the Windows address book" or just "Outlook Express address book".
+ If false, GetDefaultLocation will be called.
+ */
+
+ boolean GetAutoFind( out wstring description);
+
+ /*
+ Returns true if the address book needs the user to specify a field map
+ for address books imported from this format.
+ */
+ boolean GetNeedsFieldMap( in nsIFile location);
+
+ /*
+ If found and userVerify BOTH return false, then it is assumed that this
+ means an error - address book cannot be found on this machine.
+ If userVerify is true, the user will have an opportunity to specify
+ a different location to import address book from.
+ */
+ void GetDefaultLocation( out nsIFile location,
+ out boolean found,
+ out boolean userVerify);
+ /*
+ Returns an nsIArray which contains an nsIImportABDescriptor for each
+ address book. The array is not sorted before display to the user.
+ location is null if GetAutoFind returned true.
+ */
+ nsIArray FindAddressBooks(in nsIFile location);
+
+ /*
+ Fill in defaults (if any) for a field map for importing address
+ books from this location.
+ */
+ void InitFieldMap(in nsIImportFieldMap fieldMap);
+
+ /**
+ * Import a specific address book into the destination file supplied.
+ * If an error occurs that is non-fatal, the destination will be deleted and
+ * other adress book will be imported. If a fatal error occurs, the
+ * destination will be deleted and the import operation will abort.
+ *
+ * @param aSource The source data for the import.
+ * @param aDestination The proxy database for the destination of the
+ * import.
+ * @param aFieldMap The field map containing the mapping of fields to be
+ * used in cvs and tab type imports.
+ * @param aSupportService An optional proxy support service (nullptr is
+ * acceptable if it is not required), may be required
+ * for certain import types (e.g. nsIAbLDIFService for
+ * LDIF import).
+ * @param aErrorLog The error log from the import.
+ * @param aSuccessLog The success log from the import.
+ * @param aFatalError True if there was a fatal error doing the import.
+ */
+ void ImportAddressBook(in nsIImportABDescriptor aSource,
+ in nsIAddrDatabase aDestination,
+ in nsIImportFieldMap aFieldMap,
+ in nsISupports aSupportService,
+ out wstring aErrorLog,
+ out wstring aSuccessLog,
+ out boolean aFatalError);
+
+ /*
+ Return the amount of the address book that has been imported so far. This number
+ is used to present progress information and must never be larger than the
+ size specified in nsIImportABDescriptor.GetSize(); May be called from
+ a different thread than ImportAddressBook()
+ */
+ unsigned long GetImportProgress();
+
+ /*
+ Set the location for reading sample data, this should be the same
+ as what is passed later to FindAddressBooks
+ */
+ void SetSampleLocation( in nsIFile location);
+
+ /*
+ * Return a string of sample data for a record, each field
+ * is separated by a newline (which means no newlines in the fields!)
+ * This is only supported by address books which use field maps and
+ * is used by the field map UI to allow the user to properly
+ * align fields to be imported.
+ *
+ * @param recordNumber index of the recrds, starting from 0.
+ * @param recordExists true if the record exists.
+ *
+ * @returns a string of sample data for the desired record
+ */
+ wstring GetSampleData(in long recordNumber, out boolean recordExists);
+
+};
+
+
+
+%{ C++
+
+%}
diff --git a/mailnews/import/public/nsIImportFieldMap.idl b/mailnews/import/public/nsIImportFieldMap.idl
new file mode 100644
index 000000000..693e4be87
--- /dev/null
+++ b/mailnews/import/public/nsIImportFieldMap.idl
@@ -0,0 +1,72 @@
+/* -*- 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/. */
+
+/*
+ Field map interface for importing address books
+
+ A field map is an arbitrary sized list of mozilla address book fields.
+ The field map is used by import to map fields from the import format
+ to mozilla fields.
+ For export, the map contains the ordered list of mozilla fields to
+ export!
+*/
+
+#include "nsISupports.idl"
+
+interface nsIAddrDatabase;
+interface nsIMdbRow;
+interface nsIAbCard;
+
+[scriptable, uuid(deee9264-1fe3-47b1-b745-47b22de454e2)]
+interface nsIImportFieldMap : nsISupports
+{
+ /*
+ Flag to indicate whether or not to skip the first record,
+ for instance csv files often have field names as the first
+ record
+ */
+ attribute boolean skipFirstRecord;
+
+ readonly attribute long numMozFields;
+ readonly attribute long mapSize;
+
+ wstring GetFieldDescription( in long index);
+
+ /*
+ Set the size of the field map, all unpopulated entries
+ will default to -1
+ */
+ void SetFieldMapSize( in long size);
+
+ /*
+ Initialize the field map to a given size with default values
+ */
+ void DefaultFieldMap( in long size);
+
+ /*
+ Return the field number that this index maps to, -1 for no field
+ */
+ long GetFieldMap( in long index);
+
+ /*
+ Set the field that this index maps to, -1 for no field
+ */
+ void SetFieldMap( in long index, in long fieldNum);
+
+ /*
+ Return if this field is "active" in the map.
+ */
+ boolean GetFieldActive( in long index);
+
+ /*
+ Set the active state of this field
+ */
+ void SetFieldActive( in long index, in boolean active);
+
+ /*
+ Set the value of the given field in the database row
+ */
+ void SetFieldValue( in nsIAddrDatabase database, in nsIMdbRow row, in long fieldNum, in wstring value);
+};
diff --git a/mailnews/import/public/nsIImportFilters.idl b/mailnews/import/public/nsIImportFilters.idl
new file mode 100644
index 000000000..d8e480617
--- /dev/null
+++ b/mailnews/import/public/nsIImportFilters.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ Interface for importing filters.
+*/
+
+#include "nsISupports.idl"
+
+interface nsIMsgAccount;
+interface nsIFile;
+
+[scriptable, uuid(f2680ccf-d110-4b5b-954d-e072d4a16129)]
+interface nsIImportFilters : nsISupports
+{
+ boolean AutoLocate( out wstring aDescription, out nsIFile aLocation);
+
+ void SetLocation( in nsIFile aLocation);
+
+ /*
+ Import filters and put any problems in the error out parameter.
+ */
+ boolean Import( out wstring aError);
+};
+
+
+
+%{ C++
+
+%}
diff --git a/mailnews/import/public/nsIImportGeneric.idl b/mailnews/import/public/nsIImportGeneric.idl
new file mode 100644
index 000000000..1496bac65
--- /dev/null
+++ b/mailnews/import/public/nsIImportGeneric.idl
@@ -0,0 +1,89 @@
+/* -*- 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/. */
+
+/*
+ Interface for importing anything. You are responsible for opening
+ up UI and doing all of the work to make it happen.
+
+*/
+
+#include "nsISupports.idl"
+
+interface nsISupportsString;
+
+[scriptable, uuid(469d7d5f-144c-4f07-9661-e49e40156348)]
+interface nsIImportGeneric : nsISupports
+{
+ /* Use these to prepare for the import */
+ /*
+ "mailInterface" - nsIImportMail interface
+ "mailBoxes" - nsIArray of nsIImportMailboxDescriptors
+ "mailLocation" - nsIFile, source location for mail
+
+ "addressInterface" - nsIImportAddressBooks interface
+ "addressBooks" - nsIArray of nsIImportABDescriptors
+ "addressLocation" - src location of address books (if needed!)
+ "addressDestination" - uri of destination address book or null if
+ new address books will be created.
+ */
+ nsISupports GetData( in string dataId);
+
+ void SetData( in string dataId, in nsISupports pData);
+
+ /*
+ "isInstalled" - if true then mail can be automatically located.
+ "canUserSetLocation" - if true then the user can specify the location
+ to look for mail. If both are false, then there is no way
+ to import mail from this format!
+ TBD: How to specify whether or not a file or a directory
+ should be specified?
+ "autoFind" - for address books, is the address book located without
+ using the file system - i.e. addressLocation is irrelevant.
+ "supportsMultiple" - 1 or 1..n address books are imported by this format?
+
+ */
+ long GetStatus( in string statusKind);
+
+ /*
+ When you are ready to import call this. If it returns TRUE then
+ you must call BeginImport and then repeatedly call GetProgress until
+ it returns 100 % done or until ContinueImport returns FALSE.
+ If this returns FALSE then BeginImport will begin and finish the import
+ before it returns.
+ */
+ boolean WantsProgress();
+
+ /* Use these for the actual import */
+ /* Begin import is expected to start a new thread UNLESS WantsProgress returned
+ FALSE. It is REQUIRED to call WantsProgress before calling BeginImport.
+ If WantsProgress was false then this will return the success or
+ failure of the import. Failure can be reported even if WantsProgress
+ returned TRUE.
+ */
+ boolean BeginImport(in nsISupportsString successLog,
+ in nsISupportsString errorLog);
+ /*
+ If WantsProgress returned TRUE then this will indicate if the import should
+ continue. If this returns FALSE then no other methods should be called
+ and the error log should be shown to the user.
+ */
+ boolean ContinueImport();
+ /*
+ Returns the percentage done. When this returns 100 then the import is done.
+ (only valid if WantsProgress returned true)
+ */
+ long GetProgress();
+ /*
+ Cancel an import in progress. Again, this is only valid if WantsProgress
+ returned true.
+ */
+ void CancelImport();
+};
+
+
+
+%{ C++
+
+%}
diff --git a/mailnews/import/public/nsIImportMail.idl b/mailnews/import/public/nsIImportMail.idl
new file mode 100644
index 000000000..dcc4dd7ba
--- /dev/null
+++ b/mailnews/import/public/nsIImportMail.idl
@@ -0,0 +1,98 @@
+/* -*- 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/. */
+
+/*
+
+ Interface for importing mail - ui provided by the import module. If
+ you wish to provide your own UI then implement the nsIImportGeneric
+ interface.
+
+*/
+
+/*
+ If you support this interface then the standard mailbox import UI
+ can be used to drive your import of mailboxes, which means you don't have
+ to worry about anything other than implementing this interface
+ (and nsIImportModule) to import mailboxes.
+*/
+
+/*
+ The general process is:
+ 1) Do you know where the mail is located
+ 2) Do you want the user to "verify" this location and have
+ the option of specifying a different mail directory?
+ 3) Given a directory (either specified in 1 or 2) build a list
+ of all of the mailboxes to be imported.
+ 4) Import each mail box to the destination provided!
+ 5) Update the portion of the mailbox imported so far. This should
+ always be less than the mailbox size until you are done. This
+ is used for progress bar updating and MAY BE CALLED FROM ANOTHER
+ THREAD!
+
+*/
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIArray;
+interface nsIImportMailboxDescriptor;
+interface nsIMsgFolder;
+
+[scriptable, uuid(a14a3308-0849-420b-86d3-13a2948b5504)]
+interface nsIImportMail : nsISupports
+{
+
+ /*
+ If found and userVerify BOTH return false, then it is assumed that this
+ means an error - mail cannot be found on this machine.
+ If userVerify is true, the user will have an opportunity to specify
+ a different location to import mail from.
+ */
+ void GetDefaultLocation( out nsIFile location,
+ out boolean found,
+ out boolean userVerify);
+ /*
+ Returns an nsIArray which contains an nsIImportMailboxID for each
+ mailbox. The array is not sorted before display to the user.
+ */
+ nsIArray FindMailboxes(in nsIFile location);
+
+ /*
+ Import a specific mailbox into the destination folder supplied. If an error
+ occurs that is non-fatal, the destination will be deleted and other mailboxes
+ will be imported. If a fatal error occurs, the destination will be deleted
+ and the import operation will abort.
+ */
+ void ImportMailbox(in nsIImportMailboxDescriptor source,
+ in nsIMsgFolder dstFolder,
+ out wstring errorLog,
+ out wstring successLog,
+ out boolean fatalError);
+
+ /*
+ Return the amount of the mailbox that has been imported so far. This number
+ is used to present progress information and must never be larger than the
+ size specified in nsIImportMailboxID.GetSize(); May be called from
+ a different thread than ImportMailbox()
+ */
+ unsigned long GetImportProgress();
+
+ /*
+ * When migrating the local folders from the import source into mozilla,
+ * we want to translate reserved folder names from the import source to
+ * equivalent values for Mozilla.
+ * Localization Impact is unknown here.
+ */
+ AString translateFolderName(in AString aFolderName);
+};
+
+
+
+%{ C++
+#define kDestTrashFolderName "Trash"
+#define kDestUnsentMessagesFolderName "Unsent Messages"
+#define kDestSentFolderName "Sent"
+#define kDestInboxFolderName "Inbox"
+%}
diff --git a/mailnews/import/public/nsIImportMailboxDescriptor.idl b/mailnews/import/public/nsIImportMailboxDescriptor.idl
new file mode 100644
index 000000000..24d24e694
--- /dev/null
+++ b/mailnews/import/public/nsIImportMailboxDescriptor.idl
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+/*
+
+ Interface for importing mail - ui provided by the import module. If
+ you wish to provide your own UI then implement the nsIImportGeneric
+ interface.
+
+ */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+
+[scriptable, uuid(69eba744-9c4f-4f79-a964-2134746b3656)]
+interface nsIImportMailboxDescriptor : nsISupports
+{
+ attribute unsigned long identifier;
+ attribute unsigned long depth;
+ attribute unsigned long size;
+
+ wstring GetDisplayName();
+ void SetDisplayName( [const] in wstring name);
+
+ attribute boolean import;
+ readonly attribute nsIFile file;
+};
+
+
+
+%{ C++
+
+/*
+ The default implementation can be obtained from
+ nsIImportService::CreateNewMailboxDescriptor();
+
+ You should only be interested in using this class if you implement
+ the nsIImportMail interface in which case, just using the service to
+ create new ones should work fine for you. If not, implement your
+ own.
+*/
+
+%}
diff --git a/mailnews/import/public/nsIImportMimeEncode.idl b/mailnews/import/public/nsIImportMimeEncode.idl
new file mode 100644
index 000000000..a03646831
--- /dev/null
+++ b/mailnews/import/public/nsIImportMimeEncode.idl
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+/*
+ Encodes a file from disk into an output stream including properly
+ encoded mime headers.
+*/
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+
+[noscript, uuid(1d63892f-f660-465c-a550-95d4cb089de4)]
+interface nsIImportMimeEncode : nsISupports
+{
+
+ void EncodeFile( in nsIFile inFile, in nsIFile outFile, [const] in string fileName, [const] in string mimeType);
+
+ boolean DoWork( out boolean done);
+
+ long NumBytesProcessed();
+
+ boolean DoEncoding();
+ void Initialize( in nsIFile inFile, in nsIFile outFile, [const] in string fileName, [const] in string mimeType);
+
+};
+
+
+
+%{ C++
+
+#define NS_IMPORTMIMEENCODE_CID \
+{ /* e4a1a340-8de2-11d3-a206-00a0cc26da63 */ \
+ 0xe4a1a340, \
+ 0x8de2, \
+ 0x11d3, \
+ {0xa2, 0x06, 0x00, 0xa0, 0xcc, 0x26, 0xda, 0x63} \
+}
+
+%}
diff --git a/mailnews/import/public/nsIImportModule.idl b/mailnews/import/public/nsIImportModule.idl
new file mode 100644
index 000000000..116cad767
--- /dev/null
+++ b/mailnews/import/public/nsIImportModule.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+
+ An import module.
+
+ */
+
+#include "nsISupports.idl"
+
+
+[scriptable, uuid(624f0280-173f-11d3-a206-00a0cc26da63)]
+interface nsIImportModule : nsISupports
+{
+ readonly attribute wstring name;
+ readonly attribute wstring description;
+ readonly attribute string supports;
+ readonly attribute boolean supportsUpgrade;
+
+ nsISupports GetImportInterface( in string importType);
+};
+
+
+%{ C++
+#define NS_IMPORT_MAIL_STR "mail"
+#define NS_IMPORT_ADDRESS_STR "addressbook"
+#define NS_IMPORT_SETTINGS_STR "settings"
+#define NS_IMPORT_FILTERS_STR "filters"
+%}
diff --git a/mailnews/import/public/nsIImportService.idl b/mailnews/import/public/nsIImportService.idl
new file mode 100644
index 000000000..160fdccdf
--- /dev/null
+++ b/mailnews/import/public/nsIImportService.idl
@@ -0,0 +1,59 @@
+/* -*- 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/. */
+
+/*
+
+ The import service.
+
+ */
+
+#include "nsISupports.idl"
+
+interface nsIImportModule;
+interface nsIImportMailboxDescriptor;
+interface nsIImportABDescriptor;
+interface nsIImportGeneric;
+interface nsIImportFieldMap;
+interface nsIMsgSendListener;
+interface nsIMsgIdentity;
+interface nsIMsgCompFields;
+interface nsIArray;
+
+[scriptable, uuid(d0ed4c50-5997-49c9-8a6a-045f0680ed29)]
+interface nsIImportService : nsISupports
+{
+ void DiscoverModules();
+
+ long GetModuleCount( in string filter);
+ void GetModuleInfo( in string filter, in long index, out wstring name, out wstring description);
+ wstring GetModuleName( in string filter, in long index);
+ wstring GetModuleDescription( in string filter, in long index);
+ nsIImportModule GetModule( in string filter, in long index);
+ nsIImportModule GetModuleWithCID( in nsCIDRef cid);
+
+ nsIImportFieldMap CreateNewFieldMap();
+ nsIImportMailboxDescriptor CreateNewMailboxDescriptor();
+ nsIImportABDescriptor CreateNewABDescriptor();
+ nsIImportGeneric CreateNewGenericMail();
+ nsIImportGeneric CreateNewGenericAddressBooks();
+ void CreateRFC822Message(in nsIMsgIdentity aIdentity,
+ in nsIMsgCompFields aMsgFields,
+ in string aBodytype,
+ in ACString aBody,
+ in boolean aCreateAsDraft,
+ in nsIArray aLoadedAttachments,
+ in nsIArray aEmbeddedObjects,
+ in nsIMsgSendListener aListener);
+
+};
+
+%{ C++
+#define NS_IMPORTSERVICE_CID \
+{ /* 5df96d60-1726-11d3-a206-00a0cc26da63 */ \
+ 0x5df96d60, 0x1726, 0x11d3, \
+ {0xa2, 0x06, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63}}
+
+#define NS_IMPORTSERVICE_CONTRACTID "@mozilla.org/import/import-service;1"
+%}
diff --git a/mailnews/import/public/nsIImportSettings.idl b/mailnews/import/public/nsIImportSettings.idl
new file mode 100644
index 000000000..9ff90d168
--- /dev/null
+++ b/mailnews/import/public/nsIImportSettings.idl
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+
+/*
+ Interface for importing settings. Settings can be auto-located or
+ specified by a specific file. Depends upon the app that the settings
+ are coming from.
+
+*/
+
+#include "nsISupports.idl"
+
+interface nsIMsgAccount;
+interface nsIFile;
+
+[scriptable, uuid(1c0e3012-bc4d-4fb2-be6a-0335c7bab9ac)]
+interface nsIImportSettings : nsISupports
+{
+ boolean AutoLocate( out wstring description, out nsIFile location);
+
+ void SetLocation( in nsIFile location);
+
+ /*
+ Create all of the accounts, identities, and servers. Return an
+ account where any local mail from this app should be imported.
+ The returned account can be null which indicates that no suitable
+ account for local mail was created and a new account specifically for
+ the imported mail should be created.
+ */
+ boolean Import( out nsIMsgAccount localMailAccount);
+};
+
+
+
+%{ C++
+
+%}
diff --git a/mailnews/import/src/ImportCharSet.cpp b/mailnews/import/src/ImportCharSet.cpp
new file mode 100644
index 000000000..a8bc48d19
--- /dev/null
+++ b/mailnews/import/src/ImportCharSet.cpp
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+
+#include "ImportCharSet.h"
+
+char ImportCharSet::m_upperCaseMap[256];
+char ImportCharSet::m_Ascii[256] = {0}; // the initialiser makes it strong
+
+class UInitMaps {
+public:
+ UInitMaps();
+};
+
+UInitMaps gInitMaps;
+
+UInitMaps::UInitMaps()
+{
+ int i;
+
+ for (i = 0; i < 256; i++)
+ ImportCharSet::m_upperCaseMap[i] = i;
+ for (i = 'a'; i <= 'z'; i++)
+ ImportCharSet::m_upperCaseMap[i] = i - 'a' + 'A';
+
+ for (i = 0; i < 256; i++)
+ ImportCharSet::m_Ascii[i] = 0;
+
+ for (i = ImportCharSet::cUpperAChar; i <= ImportCharSet::cUpperZChar; i++)
+ ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cAlphaChar);
+ for (i = ImportCharSet::cLowerAChar; i <= ImportCharSet::cLowerZChar; i++)
+ ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cAlphaChar);
+ for (i = ImportCharSet::cZeroChar; i <= ImportCharSet::cNineChar; i++)
+ ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cDigitChar);
+
+ ImportCharSet::m_Ascii[ImportCharSet::cTabChar] |= ImportCharSet::cWhiteSpaceChar;
+ ImportCharSet::m_Ascii[ImportCharSet::cCRChar] |= ImportCharSet::cWhiteSpaceChar;
+ ImportCharSet::m_Ascii[ImportCharSet::cLinefeedChar] |= ImportCharSet::cWhiteSpaceChar;
+ ImportCharSet::m_Ascii[ImportCharSet::cSpaceChar] |= ImportCharSet::cWhiteSpaceChar;
+
+ ImportCharSet::m_Ascii[uint8_t('(')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t(')')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('<')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('>')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('@')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t(',')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t(';')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t(':')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('\\')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('"')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('.')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t('[')] |= ImportCharSet::c822SpecialChar;
+ ImportCharSet::m_Ascii[uint8_t(']')] |= ImportCharSet::c822SpecialChar;
+
+
+}
diff --git a/mailnews/import/src/ImportCharSet.h b/mailnews/import/src/ImportCharSet.h
new file mode 100644
index 000000000..c1f649ef2
--- /dev/null
+++ b/mailnews/import/src/ImportCharSet.h
@@ -0,0 +1,175 @@
+/* -*- 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/. */
+
+#ifndef ImportCharSet_h___
+#define ImportCharSet_h___
+
+#include "nscore.h"
+
+
+// Some useful ASCII values
+// 'A' = 65, 0x41
+// 'Z' = 90, 0x5a
+// '_' = 95, 0x5f
+// 'a' = 97, 0x61
+// 'z' = 122, 0x7a
+// '0' = 48, 0x30
+// '1' = 49, 0x31
+// '9' = 57, 0x39
+// ' ' = 32, 0x20
+// whitespace, 10, 13, 32, 9 (linefeed, cr, space, tab) - 0x0a, 0x0d, 0x20, 0x09
+// ':' = 58, 0x3a
+
+
+// a typedef enum would be nicer but some compilers still have trouble with treating
+// enum's as plain numbers when needed
+
+class ImportCharSet {
+public:
+ enum {
+ cTabChar = 9,
+ cLinefeedChar = 10,
+ cCRChar = 13,
+ cSpaceChar = 32,
+ cUpperAChar = 65,
+ cUpperZChar = 90,
+ cUnderscoreChar = 95,
+ cLowerAChar = 97,
+ cLowerZChar = 122,
+ cZeroChar = 48,
+ cNineChar = 57,
+
+ cAlphaNumChar = 1,
+ cAlphaChar = 2,
+ cWhiteSpaceChar = 4,
+ cDigitChar = 8,
+ c822SpecialChar = 16
+ };
+
+ static char m_upperCaseMap[256];
+ static char m_Ascii[256];
+
+ inline static bool IsUSAscii(uint8_t ch) { return (((ch & (uint8_t)0x80) == 0));}
+ inline static bool Is822CtlChar(uint8_t ch) { return (ch < 32);}
+ inline static bool Is822SpecialChar(uint8_t ch) { return ((m_Ascii[ch] & c822SpecialChar) == c822SpecialChar);}
+ inline static bool IsWhiteSpace(uint8_t ch) { return ((m_Ascii[ch] & cWhiteSpaceChar) == cWhiteSpaceChar); }
+ inline static bool IsAlphaNum(uint8_t ch) { return ((m_Ascii[ch] & cAlphaNumChar) == cAlphaNumChar); }
+ inline static bool IsDigit(uint8_t ch) { return ((m_Ascii[ch] & cDigitChar) == cDigitChar); }
+
+ inline static uint8_t ToLower(uint8_t ch) { if ((m_Ascii[ch] & cAlphaChar) == cAlphaChar) { return cLowerAChar + (m_upperCaseMap[ch] - cUpperAChar); } else return ch; }
+
+ inline static long AsciiToLong(const uint8_t * pChar, uint32_t len) {
+ long num = 0;
+ while (len) {
+ if ((m_Ascii[*pChar] & cDigitChar) == 0)
+ return num;
+ num *= 10;
+ num += (*pChar - cZeroChar);
+ len--;
+ pChar++;
+ }
+ return num;
+ }
+
+ inline static void ByteToHex(uint8_t byte, uint8_t * pHex) {
+ uint8_t val = byte;
+ val /= 16;
+ if (val < 10)
+ *pHex = '0' + val;
+ else
+ *pHex = 'A' + (val - 10);
+ pHex++;
+ val = byte;
+ val &= 0x0F;
+ if (val < 10)
+ *pHex = '0' + val;
+ else
+ *pHex = 'A' + (val - 10);
+ }
+
+ inline static void LongToHexBytes(uint32_t type, uint8_t * pStr) {
+ ByteToHex((uint8_t) (type >> 24), pStr);
+ pStr += 2;
+ ByteToHex((uint8_t) ((type >> 16) & 0x0FF), pStr);
+ pStr += 2;
+ ByteToHex((uint8_t) ((type >> 8) & 0x0FF), pStr);
+ pStr += 2;
+ ByteToHex((uint8_t) (type & 0x0FF), pStr);
+ }
+
+ inline static void SkipWhiteSpace(const uint8_t * & pChar, uint32_t & pos, uint32_t max) {
+ while ((pos < max) && (IsWhiteSpace(*pChar))) {
+ pos++; pChar++;
+ }
+ }
+
+ inline static void SkipSpaceTab(const uint8_t * & pChar, uint32_t& pos, uint32_t max) {
+ while ((pos < max) && ((*pChar == (uint8_t)cSpaceChar) || (*pChar == (uint8_t)cTabChar))) {
+ pos++; pChar++;
+ }
+ }
+
+ inline static void SkipTilSpaceTab(const uint8_t * & pChar, uint32_t& pos, uint32_t max) {
+ while ((pos < max) && (*pChar != (uint8_t)cSpaceChar) && (*pChar != (uint8_t)cTabChar)) {
+ pos++;
+ pChar++;
+ }
+ }
+
+ inline static bool StrNICmp(const uint8_t * pChar, const uint8_t * pSrc, uint32_t len) {
+ while (len && (m_upperCaseMap[*pChar] == m_upperCaseMap[*pSrc])) {
+ pChar++; pSrc++; len--;
+ }
+ return len == 0;
+ }
+
+ inline static bool StrNCmp(const uint8_t * pChar, const uint8_t *pSrc, uint32_t len) {
+ while (len && (*pChar == *pSrc)) {
+ pChar++; pSrc++; len--;
+ }
+ return len == 0;
+ }
+
+ inline static int FindChar(const uint8_t * pChar, uint8_t ch, uint32_t max) {
+ uint32_t pos = 0;
+ while ((pos < max) && (*pChar != ch)) {
+ pos++; pChar++;
+ }
+ if (pos < max)
+ return (int) pos;
+ else
+ return -1;
+ }
+
+ inline static bool NextChar(const uint8_t * & pChar, uint8_t ch, uint32_t& pos, uint32_t max) {
+ if ((pos < max) && (*pChar == ch)) {
+ pos++;
+ pChar++;
+ return true;
+ }
+ return false;
+ }
+
+ inline static int32_t strcmp(const char * pS1, const char * pS2) {
+ while (*pS1 && *pS2 && (*pS1 == *pS2)) {
+ pS1++;
+ pS2++;
+ }
+ return *pS1 - *pS2;
+ }
+
+ inline static int32_t stricmp(const char * pS1, const char * pS2) {
+ while (*pS1 && *pS2 && (m_upperCaseMap[uint8_t(*pS1)] == m_upperCaseMap[uint8_t(*pS2)])) {
+ pS1++;
+ pS2++;
+ }
+ return m_upperCaseMap[uint8_t(*pS1)] - m_upperCaseMap[uint8_t(*pS2)];
+ }
+
+};
+
+
+#endif /* ImportCharSet_h__ */
+
diff --git a/mailnews/import/src/ImportDebug.h b/mailnews/import/src/ImportDebug.h
new file mode 100644
index 000000000..b905aa4a9
--- /dev/null
+++ b/mailnews/import/src/ImportDebug.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ImportDebug_h___
+#define ImportDebug_h___
+
+#ifdef NS_DEBUG
+#define IMPORT_DEBUG 1
+#endif
+
+// Use MOZ_LOG for logging.
+#include "mozilla/Logging.h"
+extern PRLogModuleInfo *IMPORTLOGMODULE; // Logging module
+
+#define IMPORT_LOG0(x) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+#endif
diff --git a/mailnews/import/src/ImportOutFile.cpp b/mailnews/import/src/ImportOutFile.cpp
new file mode 100644
index 000000000..beeb8903a
--- /dev/null
+++ b/mailnews/import/src/ImportOutFile.cpp
@@ -0,0 +1,299 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "prio.h"
+#include "nsNetUtil.h"
+#include "nsISeekableStream.h"
+#include "nsMsgUtils.h"
+#include "ImportOutFile.h"
+#include "ImportCharSet.h"
+
+#include "ImportDebug.h"
+
+/*
+#ifdef _MAC
+#define kMacNoCreator '????'
+#define kMacTextFile 'TEXT'
+#else
+#define kMacNoCreator 0
+#define kMacTextFile 0
+#endif
+*/
+
+ImportOutFile::ImportOutFile()
+{
+ m_ownsFileAndBuffer = false;
+ m_pos = 0;
+ m_pBuf = nullptr;
+ m_bufSz = 0;
+ m_pTrans = nullptr;
+ m_pTransOut = nullptr;
+ m_pTransBuf = nullptr;
+}
+
+ImportOutFile::ImportOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz)
+{
+ m_pTransBuf = nullptr;
+ m_pTransOut = nullptr;
+ m_pTrans = nullptr;
+ m_ownsFileAndBuffer = false;
+ InitOutFile(pFile, pBuf, sz);
+}
+
+ImportOutFile::~ImportOutFile()
+{
+ if (m_ownsFileAndBuffer)
+ {
+ Flush();
+ delete [] m_pBuf;
+ }
+
+ delete m_pTrans;
+ delete m_pTransOut;
+ delete [] m_pTransBuf;
+}
+
+bool ImportOutFile::Set8bitTranslator(nsImportTranslator *pTrans)
+{
+ if (!Flush())
+ return false;
+
+ m_engaged = false;
+ m_pTrans = pTrans;
+ m_supports8to7 = pTrans->Supports8bitEncoding();
+
+
+ return true;
+}
+
+bool ImportOutFile::End8bitTranslation(bool *pEngaged, nsCString& useCharset, nsCString& encoding)
+{
+ if (!m_pTrans)
+ return false;
+
+
+ bool bResult = Flush();
+ if (m_supports8to7 && m_pTransOut) {
+ if (bResult)
+ bResult = m_pTrans->FinishConvertToFile(m_pTransOut);
+ if (bResult)
+ bResult = Flush();
+ }
+
+ if (m_supports8to7) {
+ m_pTrans->GetCharset(useCharset);
+ m_pTrans->GetEncoding(encoding);
+ }
+ else
+ useCharset.Truncate();
+ *pEngaged = m_engaged;
+ delete m_pTrans;
+ m_pTrans = nullptr;
+ delete m_pTransOut;
+ m_pTransOut = nullptr;
+ delete [] m_pTransBuf;
+ m_pTransBuf = nullptr;
+
+ return bResult;
+}
+
+bool ImportOutFile::InitOutFile(nsIFile *pFile, uint32_t bufSz)
+{
+ if (!bufSz)
+ bufSz = 32 * 1024;
+ if (!m_pBuf)
+ m_pBuf = new uint8_t[ bufSz];
+
+ if (!m_outputStream)
+ {
+ nsresult rv;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_outputStream),
+ pFile,
+ PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+ 0644);
+
+ if (NS_FAILED(rv))
+ {
+ IMPORT_LOG0("Couldn't create outfile\n");
+ delete [] m_pBuf;
+ m_pBuf = nullptr;
+ return false;
+ }
+ }
+ m_pFile = pFile;
+ m_ownsFileAndBuffer = true;
+ m_pos = 0;
+ m_bufSz = bufSz;
+ return true;
+}
+
+void ImportOutFile::InitOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz)
+{
+ m_ownsFileAndBuffer = false;
+ m_pFile = pFile;
+ m_pBuf = pBuf;
+ m_bufSz = sz;
+ m_pos = 0;
+}
+
+
+
+bool ImportOutFile::Flush(void)
+{
+ if (!m_pos)
+ return true;
+
+ uint32_t transLen;
+ bool duddleyDoWrite = false;
+
+ // handle translations if appropriate
+ if (m_pTrans) {
+ if (m_engaged && m_supports8to7) {
+ // Markers can get confused by this crap!!!
+ // TLR: FIXME: Need to update the markers based on
+ // the difference between the translated len and untranslated len
+
+ if (!m_pTrans->ConvertToFile( m_pBuf, m_pos, m_pTransOut, &transLen))
+ return false;
+ if (!m_pTransOut->Flush())
+ return false;
+ // now update our buffer...
+ if (transLen < m_pos) {
+ memcpy(m_pBuf, m_pBuf + transLen, m_pos - transLen);
+ }
+ m_pos -= transLen;
+ }
+ else if (m_engaged) {
+ // does not actually support translation!
+ duddleyDoWrite = true;
+ }
+ else {
+ // should we engage?
+ uint8_t * pChar = m_pBuf;
+ uint32_t len = m_pos;
+ while (len) {
+ if (!ImportCharSet::IsUSAscii(*pChar))
+ break;
+ pChar++;
+ len--;
+ }
+ if (len) {
+ m_engaged = true;
+ if (m_supports8to7) {
+ // allocate our translation output buffer and file...
+ m_pTransBuf = new uint8_t[m_bufSz];
+ m_pTransOut = new ImportOutFile(m_pFile, m_pTransBuf, m_bufSz);
+ return Flush();
+ }
+ else
+ duddleyDoWrite = true;
+ }
+ else {
+ duddleyDoWrite = true;
+ }
+ }
+ }
+ else
+ duddleyDoWrite = true;
+
+ if (duddleyDoWrite) {
+ uint32_t written = 0;
+ nsresult rv = m_outputStream->Write((const char *)m_pBuf, (int32_t)m_pos, &written);
+ if (NS_FAILED(rv) || ((uint32_t)written != m_pos))
+ return false;
+ m_pos = 0;
+ }
+
+ return true;
+}
+
+bool ImportOutFile::WriteU8NullTerm(const uint8_t * pSrc, bool includeNull)
+{
+ while (*pSrc) {
+ if (m_pos >= m_bufSz) {
+ if (!Flush())
+ return false;
+ }
+ *(m_pBuf + m_pos) = *pSrc;
+ m_pos++;
+ pSrc++;
+ }
+ if (includeNull) {
+ if (m_pos >= m_bufSz) {
+ if (!Flush())
+ return false;
+ }
+ *(m_pBuf + m_pos) = 0;
+ m_pos++;
+ }
+
+ return true;
+}
+
+bool ImportOutFile::SetMarker(int markerID)
+{
+ if (!Flush()) {
+ return false;
+ }
+
+ if (markerID < kMaxMarkers) {
+ int64_t pos = 0;
+ if (m_outputStream)
+ {
+ // do we need to flush for the seek to give us the right pos?
+ m_outputStream->Flush();
+ nsresult rv;
+ nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(m_outputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = seekStream->Tell(&pos);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error, Tell failed on output stream\n");
+ return false;
+ }
+ }
+ m_markers[markerID] = (uint32_t)pos + m_pos;
+ }
+
+ return true;
+}
+
+void ImportOutFile::ClearMarker(int markerID)
+{
+ if (markerID < kMaxMarkers)
+ m_markers[markerID] = 0;
+}
+
+bool ImportOutFile::WriteStrAtMarker(int markerID, const char *pStr)
+{
+ if (markerID >= kMaxMarkers)
+ return false;
+
+ if (!Flush())
+ return false;
+ int64_t pos;
+ m_outputStream->Flush();
+ nsresult rv;
+ nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(m_outputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = seekStream->Tell(&pos);
+ if (NS_FAILED(rv))
+ return false;
+ rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, (int32_t) m_markers[markerID]);
+ if (NS_FAILED(rv))
+ return false;
+ uint32_t written;
+ rv = m_outputStream->Write(pStr, strlen(pStr), &written);
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, pos);
+ if (NS_FAILED(rv))
+ return false;
+
+ return true;
+}
+
diff --git a/mailnews/import/src/ImportOutFile.h b/mailnews/import/src/ImportOutFile.h
new file mode 100644
index 000000000..461728c22
--- /dev/null
+++ b/mailnews/import/src/ImportOutFile.h
@@ -0,0 +1,94 @@
+/* -*- 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/. */
+#ifndef ImportOutFile_h___
+#define ImportOutFile_h___
+
+#include "nsImportTranslator.h"
+#include "nsIOutputStream.h"
+#include "nsIFile.h"
+
+#define kMaxMarkers 10
+
+class ImportOutFile;
+
+class ImportOutFile {
+public:
+ ImportOutFile();
+ ImportOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz);
+ ~ImportOutFile();
+
+ bool InitOutFile(nsIFile *pFile, uint32_t bufSz = 4096);
+ void InitOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz);
+ inline bool WriteData(const uint8_t * pSrc, uint32_t len);
+ inline bool WriteByte(uint8_t byte);
+ bool WriteStr(const char *pStr) {return WriteU8NullTerm((const uint8_t *) pStr, false); }
+ bool WriteU8NullTerm(const uint8_t * pSrc, bool includeNull);
+ bool WriteEol(void) { return WriteStr("\x0D\x0A"); }
+ bool Done(void) {return Flush();}
+
+ // Marker support
+ bool SetMarker(int markerID);
+ void ClearMarker(int markerID);
+ bool WriteStrAtMarker(int markerID, const char *pStr);
+
+ // 8-bit to 7-bit translation
+ bool Set8bitTranslator(nsImportTranslator *pTrans);
+ bool End8bitTranslation(bool *pEngaged, nsCString& useCharset, nsCString& encoding);
+
+protected:
+ bool Flush(void);
+
+protected:
+ nsCOMPtr <nsIFile> m_pFile;
+ nsCOMPtr <nsIOutputStream> m_outputStream;
+ uint8_t * m_pBuf;
+ uint32_t m_bufSz;
+ uint32_t m_pos;
+ bool m_ownsFileAndBuffer;
+
+ // markers
+ uint32_t m_markers[kMaxMarkers];
+
+ // 8 bit to 7 bit translations
+ nsImportTranslator * m_pTrans;
+ bool m_engaged;
+ bool m_supports8to7;
+ ImportOutFile * m_pTransOut;
+ uint8_t * m_pTransBuf;
+};
+
+inline bool ImportOutFile::WriteData(const uint8_t * pSrc, uint32_t len) {
+ while ((len + m_pos) > m_bufSz) {
+ if ((m_bufSz - m_pos)) {
+ memcpy(m_pBuf + m_pos, pSrc, m_bufSz - m_pos);
+ len -= (m_bufSz - m_pos);
+ pSrc += (m_bufSz - m_pos);
+ m_pos = m_bufSz;
+ }
+ if (!Flush())
+ return false;
+ }
+
+ if (len) {
+ memcpy(m_pBuf + m_pos, pSrc, len);
+ m_pos += len;
+ }
+
+ return true;
+}
+
+inline bool ImportOutFile::WriteByte(uint8_t byte) {
+ if (m_pos == m_bufSz) {
+ if (!Flush())
+ return false;
+ }
+ *(m_pBuf + m_pos) = byte;
+ m_pos++;
+ return true;
+}
+
+#endif /* ImportOutFile_h__ */
+
+
diff --git a/mailnews/import/src/ImportTranslate.cpp b/mailnews/import/src/ImportTranslate.cpp
new file mode 100644
index 000000000..f7f32f552
--- /dev/null
+++ b/mailnews/import/src/ImportTranslate.cpp
@@ -0,0 +1,105 @@
+/* -*- 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/. */
+
+#include "ImportTranslate.h"
+
+int ImportTranslate::m_useTranslator = -1;
+
+
+bool ImportTranslate::ConvertString(const nsCString& inStr, nsCString& outStr, bool mimeHeader)
+{
+ if (inStr.IsEmpty()) {
+ outStr = inStr;
+ return true;
+ }
+
+ nsImportTranslator *pTrans = GetTranslator();
+ // int maxLen = (int) pTrans->GetMaxBufferSize(inStr.Length());
+ // int hLen = 0;
+ nsCString set;
+ nsCString lang;
+
+ if (mimeHeader) {
+ // add the charset and language
+ pTrans->GetCharset(set);
+ pTrans->GetLanguage(lang);
+ }
+
+ // Unfortunatly, we didn't implement ConvertBuffer for all translators,
+ // just ConvertToFile. This means that this data will not always
+ // be converted to the charset of pTrans. In that case...
+ // We don't always have the data in the same charset as the current
+ // translator...
+ // It is safer to leave the charset and language field blank
+ set.Truncate();
+ lang.Truncate();
+
+ uint8_t * pBuf;
+ /*
+ pBuf = (P_U8) outStr.GetBuffer(maxLen);
+ if (!pBuf) {
+ delete pTrans;
+ return FALSE;
+ }
+ pTrans->ConvertBuffer((PC_U8)(PC_S8)inStr, inStr.GetLength(), pBuf);
+ outStr.ReleaseBuffer();
+ */
+ outStr = inStr;
+ delete pTrans;
+
+
+ // Now I need to run the string through the mime-header special char
+ // encoder.
+
+ pTrans = new CMHTranslator;
+ pBuf = new uint8_t[pTrans->GetMaxBufferSize(outStr.Length())];
+ pTrans->ConvertBuffer((const uint8_t *)(outStr.get()), outStr.Length(), pBuf);
+ delete pTrans;
+ outStr.Truncate();
+ if (mimeHeader) {
+ outStr = set;
+ outStr += "'";
+ outStr += lang;
+ outStr += "'";
+ }
+ outStr += (const char *)pBuf;
+ delete [] pBuf;
+
+ return true;
+}
+
+
+nsImportTranslator *ImportTranslate::GetTranslator(void)
+{
+ if (m_useTranslator == -1) {
+ // get the translator to use...
+ // CString trans;
+ // trans.LoadString(IDS_LANGUAGE_TRANSLATION);
+ m_useTranslator = 0;
+ // if (!trans.CompareNoCase("iso-2022-jp"))
+ // gWizData.m_useTranslator = 1;
+ }
+
+ switch(m_useTranslator) {
+ case 0:
+ return new nsImportTranslator;
+ //case 1:
+ // return new CSJis2JisTranslator;
+ default:
+ return new nsImportTranslator;
+ }
+}
+
+nsImportTranslator *ImportTranslate::GetMatchingTranslator(const char *pCharSet)
+{
+/*
+ CString jp = "iso-2022-jp";
+ if (!jp.CompareNoCase(pCharSet))
+ return new CSJis2JisTranslator;
+*/
+
+ return nullptr;
+}
+
diff --git a/mailnews/import/src/ImportTranslate.h b/mailnews/import/src/ImportTranslate.h
new file mode 100644
index 000000000..3e6c596d4
--- /dev/null
+++ b/mailnews/import/src/ImportTranslate.h
@@ -0,0 +1,23 @@
+/* -*- 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/. */
+
+#ifndef ImportTranslate_h___
+#define ImportTranslate_h___
+
+#include "nsStringGlue.h"
+#include "nsImportTranslator.h"
+
+class ImportTranslate {
+public:
+ static bool ConvertString(const nsCString& inStr, nsCString& outStr, bool mimeHeader);
+ static nsImportTranslator *GetTranslator(void);
+ static nsImportTranslator *GetMatchingTranslator(const char *pCharSet);
+
+protected:
+ static int m_useTranslator;
+};
+
+
+#endif /* ImportTranslate_h__ */
diff --git a/mailnews/import/src/moz.build b/mailnews/import/src/moz.build
new file mode 100644
index 000000000..2df4926d5
--- /dev/null
+++ b/mailnews/import/src/moz.build
@@ -0,0 +1,25 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'ImportCharSet.cpp',
+ 'ImportOutFile.cpp',
+ 'ImportTranslate.cpp',
+ 'nsImportABDescriptor.cpp',
+ 'nsImportAddressBooks.cpp',
+ 'nsImportEmbeddedImageData.cpp',
+ 'nsImportEncodeScan.cpp',
+ 'nsImportFieldMap.cpp',
+ 'nsImportMail.cpp',
+ 'nsImportMailboxDescriptor.cpp',
+ 'nsImportMimeEncode.cpp',
+ 'nsImportScanFile.cpp',
+ 'nsImportService.cpp',
+ 'nsImportStringBundle.cpp',
+ 'nsImportTranslator.cpp',
+]
+
+FINAL_LIBRARY = 'import'
+
diff --git a/mailnews/import/src/nsImportABDescriptor.cpp b/mailnews/import/src/nsImportABDescriptor.cpp
new file mode 100644
index 000000000..05605d09e
--- /dev/null
+++ b/mailnews/import/src/nsImportABDescriptor.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nscore.h"
+#include "nsImportABDescriptor.h"
+
+////////////////////////////////////////////////////////////////////////
+
+NS_METHOD nsImportABDescriptor::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsImportABDescriptor *it = new nsImportABDescriptor();
+ if (it == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(it);
+ nsresult rv = it->QueryInterface(aIID, aResult);
+ NS_RELEASE(it);
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsImportABDescriptor, nsIImportABDescriptor)
+
+nsImportABDescriptor::nsImportABDescriptor()
+ : mId(0), mRef(0), mSize(0), mImport(true)
+{
+}
diff --git a/mailnews/import/src/nsImportABDescriptor.h b/mailnews/import/src/nsImportABDescriptor.h
new file mode 100644
index 000000000..a8fa6fbad
--- /dev/null
+++ b/mailnews/import/src/nsImportABDescriptor.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsImportABDescriptor_h___
+#define nsImportABDescriptor_h___
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////
+
+class nsImportABDescriptor : public nsIImportABDescriptor
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD GetIdentifier(uint32_t *pIdentifier) override {
+ *pIdentifier = mId;
+ return NS_OK;
+ }
+ NS_IMETHOD SetIdentifier(uint32_t ident) override {
+ mId = ident;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetRef(uint32_t *pRef) override {
+ *pRef = mRef;
+ return NS_OK;
+ }
+ NS_IMETHOD SetRef(uint32_t ref) override {
+ mRef = ref;
+ return NS_OK;
+ }
+
+ /* attribute unsigned long size; */
+ NS_IMETHOD GetSize(uint32_t *pSize) override {
+ *pSize = mSize;
+ return NS_OK;
+ }
+ NS_IMETHOD SetSize(uint32_t theSize) override {
+ mSize = theSize;
+ return NS_OK;
+ }
+
+ /* attribute AString displayName; */
+ NS_IMETHOD GetPreferredName(nsAString &aName) override {
+ aName = mDisplayName;
+ return NS_OK;
+ }
+ NS_IMETHOD SetPreferredName(const nsAString &aName) override {
+ mDisplayName = aName;
+ return NS_OK;
+ }
+
+ /* readonly attribute nsIFile fileSpec; */
+ NS_IMETHOD GetAbFile(nsIFile **aFile) override {
+ if (!mFile)
+ return NS_ERROR_NULL_POINTER;
+
+ return mFile->Clone(aFile);
+ }
+
+ NS_IMETHOD SetAbFile(nsIFile *aFile) override {
+ if (!aFile) {
+ mFile = nullptr;
+ return NS_OK;
+ }
+
+ return aFile->Clone(getter_AddRefs(mFile));
+ }
+
+ /* attribute boolean import; */
+ NS_IMETHOD GetImport(bool *pImport) override {
+ *pImport = mImport;
+ return NS_OK;
+ }
+ NS_IMETHOD SetImport(bool doImport) override {
+ mImport = doImport;
+ return NS_OK;
+ }
+
+ nsImportABDescriptor();
+
+ static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ virtual ~nsImportABDescriptor() {}
+ uint32_t mId; // used by creator of the structure
+ uint32_t mRef; // depth in the hierarchy
+ nsString mDisplayName; // name of this mailbox
+ nsCOMPtr<nsIFile> mFile; // source file (if applicable)
+ uint32_t mSize; // size
+ bool mImport; // import it or not?
+};
+
+
+#endif
diff --git a/mailnews/import/src/nsImportAddressBooks.cpp b/mailnews/import/src/nsImportAddressBooks.cpp
new file mode 100644
index 000000000..8791efb42
--- /dev/null
+++ b/mailnews/import/src/nsImportAddressBooks.cpp
@@ -0,0 +1,894 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "prprf.h"
+#include "plstr.h"
+#include "nsCOMPtr.h"
+#include "nsMsgUtils.h"
+#include "nsIImportService.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportGeneric.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsStringGlue.h"
+#include "nsIFile.h"
+#include "nsIAddrDatabase.h"
+#include "nsIAbManager.h"
+#include "nsIAbLDIFService.h"
+#include "nsAbBaseCID.h"
+#include "nsIStringBundle.h"
+#include "nsImportStringBundle.h"
+#include "nsTextFormatter.h"
+#include "nsServiceManagerUtils.h"
+#include "msgCore.h"
+#include "ImportDebug.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIArray.h"
+#include "nsCOMArray.h"
+#include "nsArrayUtils.h"
+
+static void ImportAddressThread(void *stuff);
+
+class AddressThreadData;
+
+class nsImportGenericAddressBooks : public nsIImportGeneric
+{
+public:
+
+ nsImportGenericAddressBooks();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* nsISupports GetData (in string dataId); */
+ NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override;
+
+ NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override;
+
+ NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override;
+
+ NS_IMETHOD WantsProgress(bool *_retval) override;
+
+ NS_IMETHOD BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override;
+
+ NS_IMETHOD ContinueImport(bool *_retval) override;
+
+ NS_IMETHOD GetProgress(int32_t *_retval) override;
+
+ NS_IMETHOD CancelImport(void) override;
+
+private:
+ virtual ~nsImportGenericAddressBooks();
+ void GetDefaultLocation(void);
+ void GetDefaultBooks(void);
+ void GetDefaultFieldMap(void);
+
+public:
+ static void SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError);
+ static void ReportError(const char16_t *pName, nsString *pStream,
+ nsIStringBundle *aBundle);
+
+private:
+ nsIImportAddressBooks * m_pInterface;
+ nsCOMPtr<nsIArray> m_Books;
+ nsCOMArray<nsIAddrDatabase> m_DBs;
+ nsCOMPtr <nsIFile> m_pLocation;
+ nsIImportFieldMap * m_pFieldMap;
+ bool m_autoFind;
+ char16_t * m_description;
+ bool m_gotLocation;
+ bool m_found;
+ bool m_userVerify;
+ nsISupportsString * m_pSuccessLog;
+ nsISupportsString * m_pErrorLog;
+ uint32_t m_totalSize;
+ bool m_doImport;
+ AddressThreadData * m_pThreadData;
+ char * m_pDestinationUri;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+class AddressThreadData {
+public:
+ bool driverAlive;
+ bool threadAlive;
+ bool abort;
+ bool fatalError;
+ uint32_t currentTotal;
+ uint32_t currentSize;
+ nsIArray *books;
+ nsCOMArray<nsIAddrDatabase>* dBs;
+ nsCOMPtr<nsIAbLDIFService> ldifService;
+ nsIImportAddressBooks * addressImport;
+ nsIImportFieldMap * fieldMap;
+ nsISupportsString * successLog;
+ nsISupportsString * errorLog;
+ char * pDestinationUri;
+ nsIStringBundle* stringBundle;
+
+ AddressThreadData();
+ ~AddressThreadData();
+};
+
+
+nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric)
+{
+ NS_PRECONDITION(aImportGeneric != nullptr, "null ptr");
+ if (! aImportGeneric)
+ return NS_ERROR_NULL_POINTER;
+
+ nsImportGenericAddressBooks *pGen = new nsImportGenericAddressBooks();
+
+ if (pGen == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(pGen);
+ nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric);
+ NS_RELEASE(pGen);
+
+ return rv;
+}
+
+nsImportGenericAddressBooks::nsImportGenericAddressBooks()
+{
+ m_pInterface = nullptr;
+ m_pSuccessLog = nullptr;
+ m_pErrorLog = nullptr;
+ m_totalSize = 0;
+ m_doImport = false;
+ m_pThreadData = nullptr;
+ m_pDestinationUri = nullptr;
+ m_pFieldMap = nullptr;
+
+ m_autoFind = false;
+ m_description = nullptr;
+ m_gotLocation = false;
+ m_found = false;
+ m_userVerify = false;
+
+ nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
+}
+
+
+nsImportGenericAddressBooks::~nsImportGenericAddressBooks()
+{
+ if (m_pDestinationUri)
+ NS_Free(m_pDestinationUri);
+
+ if (m_description)
+ NS_Free(m_description);
+
+ NS_IF_RELEASE(m_pFieldMap);
+ NS_IF_RELEASE(m_pInterface);
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+}
+
+
+
+NS_IMPL_ISUPPORTS(nsImportGenericAddressBooks, nsIImportGeneric)
+
+
+NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char *dataId, nsISupports **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ *_retval = nullptr;
+ if (!PL_strcasecmp(dataId, "addressInterface")) {
+ *_retval = m_pInterface;
+ NS_IF_ADDREF(m_pInterface);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressLocation")) {
+ if (!m_pLocation)
+ GetDefaultLocation();
+ NS_IF_ADDREF(*_retval = m_pLocation);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressBooks")) {
+ if (!m_pLocation)
+ GetDefaultLocation();
+ if (!m_Books)
+ GetDefaultBooks();
+ *_retval = m_Books;
+ }
+
+ if (!PL_strcasecmp(dataId, "addressDestination")) {
+ if (m_pDestinationUri) {
+ nsCOMPtr<nsISupportsCString> abString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ abString->SetData(nsDependentCString(m_pDestinationUri));
+ NS_IF_ADDREF(*_retval = abString);
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "fieldMap")) {
+ if (m_pFieldMap) {
+ *_retval = m_pFieldMap;
+ m_pFieldMap->AddRef();
+ }
+ else {
+ if (m_pInterface && m_pLocation) {
+ bool needsIt = false;
+ m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt);
+ if (needsIt) {
+ GetDefaultFieldMap();
+ if (m_pFieldMap) {
+ *_retval = m_pFieldMap;
+ m_pFieldMap->AddRef();
+ }
+ }
+ }
+ }
+ }
+
+ if (!PL_strncasecmp(dataId, "sampleData-", 11)) {
+ // extra the record number
+ const char *pNum = dataId + 11;
+ int32_t rNum = 0;
+ while (*pNum) {
+ rNum *= 10;
+ rNum += (*pNum - '0');
+ pNum++;
+ }
+ IMPORT_LOG1("Requesting sample data #: %ld\n", (long)rNum);
+ if (m_pInterface) {
+ nsCOMPtr<nsISupportsString> data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+ char16_t * pData = nullptr;
+ bool found = false;
+ rv = m_pInterface->GetSampleData(rNum, &found, &pData);
+ if (NS_FAILED(rv))
+ return rv;
+ if (found) {
+ data->SetData(nsDependentString(pData));
+ *_retval = data;
+ NS_ADDREF(*_retval);
+ }
+ NS_Free(pData);
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char *dataId, nsISupports *item)
+{
+ NS_PRECONDITION(dataId != nullptr, "null ptr");
+ if (!dataId)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!PL_strcasecmp(dataId, "addressInterface")) {
+ NS_IF_RELEASE(m_pInterface);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIImportAddressBooks), (void **) &m_pInterface);
+ }
+ if (!PL_strcasecmp(dataId, "addressBooks")) {
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_Books);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressLocation")) {
+ m_pLocation = nullptr;
+
+ if (item) {
+ nsresult rv;
+ m_pLocation = do_QueryInterface(item, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ if (m_pInterface)
+ m_pInterface->SetSampleLocation(m_pLocation);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressDestination")) {
+ if (item) {
+ nsCOMPtr<nsISupportsCString> abString;
+ item->QueryInterface(NS_GET_IID(nsISupportsCString), getter_AddRefs(abString));
+ if (abString) {
+ if (m_pDestinationUri)
+ NS_Free(m_pDestinationUri);
+ m_pDestinationUri = nullptr;
+ nsAutoCString tempUri;
+ abString->GetData(tempUri);
+ m_pDestinationUri = ToNewCString(tempUri);
+ }
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "fieldMap")) {
+ NS_IF_RELEASE(m_pFieldMap);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIImportFieldMap), (void **) &m_pFieldMap);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::GetStatus(const char *statusKind, int32_t *_retval)
+{
+ NS_PRECONDITION(statusKind != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!statusKind || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = 0;
+
+ if (!PL_strcasecmp(statusKind, "isInstalled")) {
+ GetDefaultLocation();
+ *_retval = (int32_t) m_found;
+ }
+
+ if (!PL_strcasecmp(statusKind, "canUserSetLocation")) {
+ GetDefaultLocation();
+ *_retval = (int32_t) m_userVerify;
+ }
+
+ if (!PL_strcasecmp(statusKind, "autoFind")) {
+ GetDefaultLocation();
+ *_retval = (int32_t) m_autoFind;
+ }
+
+ if (!PL_strcasecmp(statusKind, "supportsMultiple")) {
+ bool multi = false;
+ if (m_pInterface)
+ m_pInterface->GetSupportsMultiple(&multi);
+ *_retval = (int32_t) multi;
+ }
+
+ if (!PL_strcasecmp(statusKind, "needsFieldMap")) {
+ bool needs = false;
+ if (m_pInterface && m_pLocation)
+ m_pInterface->GetNeedsFieldMap(m_pLocation, &needs);
+ *_retval = (int32_t) needs;
+ }
+
+ return NS_OK;
+}
+
+void nsImportGenericAddressBooks::GetDefaultLocation(void)
+{
+ if (!m_pInterface)
+ return;
+
+ if ((m_pLocation && m_gotLocation) || m_autoFind)
+ return;
+
+ if (m_description)
+ NS_Free(m_description);
+ m_description = nullptr;
+ m_pInterface->GetAutoFind(&m_description, &m_autoFind);
+ m_gotLocation = true;
+ if (m_autoFind) {
+ m_found = true;
+ m_userVerify = false;
+ return;
+ }
+
+ nsCOMPtr <nsIFile> pLoc;
+ m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify);
+ if (!m_pLocation)
+ m_pLocation = pLoc;
+}
+
+void nsImportGenericAddressBooks::GetDefaultBooks(void)
+{
+ if (!m_pInterface || m_Books)
+ return;
+
+ if (!m_pLocation && !m_autoFind)
+ return;
+
+ nsresult rv = m_pInterface->FindAddressBooks(m_pLocation, getter_AddRefs(m_Books));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error: FindAddressBooks failed\n");
+ }
+}
+
+void nsImportGenericAddressBooks::GetDefaultFieldMap(void)
+{
+ if (!m_pInterface || !m_pLocation)
+ return;
+
+ NS_IF_RELEASE(m_pFieldMap);
+
+ nsresult rv;
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Unable to get nsIImportService.\n");
+ return;
+ }
+
+ rv = impSvc->CreateNewFieldMap(&m_pFieldMap);
+ if (NS_FAILED(rv))
+ return;
+
+ int32_t sz = 0;
+ rv = m_pFieldMap->GetNumMozFields(&sz);
+ if (NS_SUCCEEDED(rv))
+ rv = m_pFieldMap->DefaultFieldMap(sz);
+ if (NS_SUCCEEDED(rv))
+ rv = m_pInterface->InitFieldMap(m_pFieldMap);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error: Unable to initialize field map\n");
+ NS_IF_RELEASE(m_pFieldMap);
+ }
+}
+
+
+NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ GetDefaultLocation();
+ GetDefaultBooks();
+
+ bool result = false;
+
+ if (m_Books) {
+ uint32_t count = 0;
+ uint32_t i;
+ bool import;
+ uint32_t size;
+ uint32_t totalSize = 0;
+
+ m_Books->GetLength(&count);
+
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i);
+ if (book) {
+ import = false;
+ size = 0;
+ nsresult rv = book->GetImport(&import);
+ if (NS_SUCCEEDED(rv) && import) {
+ (void) book->GetSize(&size);
+ result = true;
+ }
+ totalSize += size;
+ }
+ }
+
+ m_totalSize = totalSize;
+ }
+
+ m_doImport = result;
+
+ *_retval = result;
+
+ return NS_OK;
+}
+
+void nsImportGenericAddressBooks::SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError)
+{
+ nsAutoString str;
+ if (pSuccess) {
+ pSuccess->GetData(str);
+ str.Append(success);
+ pSuccess->SetData(success);
+ }
+ if (pError) {
+ pError->GetData(str);
+ str.Append(error);
+ pError->SetData(error);
+ }
+}
+
+already_AddRefed<nsIAddrDatabase> GetAddressBookFromUri(const char *pUri)
+{
+ if (!pUri)
+ return nullptr;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID);
+ if (!abManager)
+ return nullptr;
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ abManager->GetDirectory(nsDependentCString(pUri),
+ getter_AddRefs(directory));
+ if (!directory)
+ return nullptr;
+
+ nsCOMPtr<nsIAbMDBDirectory> mdbDirectory = do_QueryInterface(directory);
+ if (!mdbDirectory)
+ return nullptr;
+
+ nsCOMPtr<nsIAddrDatabase> pDatabase;
+ mdbDirectory->GetDatabase(getter_AddRefs(pDatabase));
+ return pDatabase.forget();
+}
+
+already_AddRefed<nsIAddrDatabase> GetAddressBook(const char16_t *name,
+ bool makeNew)
+{
+ if (!makeNew) {
+ // FIXME: How do I get the list of address books and look for a
+ // specific name. Major bogosity!
+ // For now, assume we didn't find anything with that name
+ }
+
+ IMPORT_LOG0("In GetAddressBook\n");
+
+ nsresult rv;
+ nsCOMPtr<nsIAddrDatabase> pDatabase;
+ nsCOMPtr<nsIFile> dbPath;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ /* Get the profile directory */
+ rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath));
+ if (NS_SUCCEEDED(rv))
+ {
+ // Create a new address book file - we don't care what the file
+ // name is, as long as it's unique
+ rv = dbPath->Append(NS_LITERAL_STRING("impab.mab"));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = dbPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ IMPORT_LOG0("Getting the address database factory\n");
+
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory =
+ do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ IMPORT_LOG0("Opening the new address book\n");
+ rv = addrDBFactory->Open(dbPath, true, true,
+ getter_AddRefs(pDatabase));
+ }
+ }
+ }
+ }
+ if (NS_FAILED(rv))
+ {
+ IMPORT_LOG0("Failed to get the user profile directory from the address book session\n");
+ }
+
+ if (pDatabase && dbPath)
+ {
+ // We made a database, add it to the UI?!?!?!?!?!?!
+ // This is major bogosity again! Why doesn't the address book
+ // just handle this properly for me? Uggggg...
+
+ nsCOMPtr<nsIAbDirectory> parentDir;
+ abManager->GetDirectory(NS_LITERAL_CSTRING(kAllDirectoryRoot),
+ getter_AddRefs(parentDir));
+ if (parentDir)
+ {
+ nsAutoCString URI("moz-abmdbdirectory://");
+ nsAutoCString leafName;
+ rv = dbPath->GetNativeLeafName(leafName);
+ if (NS_FAILED(rv))
+ IMPORT_LOG0("*** Error: Unable to get name of database file\n");
+ else
+ {
+ URI.Append(leafName);
+ rv = parentDir->CreateDirectoryByURI(nsDependentString(name), URI);
+ if (NS_FAILED(rv))
+ IMPORT_LOG0("*** Error: Unable to create address book directory\n");
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ IMPORT_LOG0("Added new address book to the UI\n");
+ else
+ IMPORT_LOG0("*** Error: An error occurred while adding the address book to the UI\n");
+ }
+
+ return pDatabase.forget();
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ nsString success;
+ nsString error;
+
+ if (!m_doImport) {
+ *_retval = true;
+ nsImportStringBundle::GetStringByID(IMPORT_NO_ADDRBOOKS, m_stringBundle,
+ success);
+ SetLogs(success, error, successLog, errorLog);
+ return NS_OK;
+ }
+
+ if (!m_pInterface || !m_Books) {
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ *_retval = false;
+ return NS_OK;
+ }
+
+ bool needsFieldMap = false;
+
+ if (NS_FAILED(m_pInterface->GetNeedsFieldMap(m_pLocation, &needsFieldMap)) ||
+ (needsFieldMap && !m_pFieldMap)) {
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ *_retval = false;
+ return NS_OK;
+ }
+
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+ m_pSuccessLog = successLog;
+ m_pErrorLog = errorLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+ NS_IF_ADDREF(m_pErrorLog);
+
+
+ // create the info need to drive address book import. We're
+ // not going to create a new thread for this since address books
+ // don't tend to be large, and import is rare.
+ m_pThreadData = new AddressThreadData();
+ m_pThreadData->books = m_Books;
+ NS_ADDREF(m_Books);
+ m_pThreadData->addressImport = m_pInterface;
+ NS_ADDREF(m_pInterface);
+ m_pThreadData->fieldMap = m_pFieldMap;
+ NS_IF_ADDREF(m_pFieldMap);
+ m_pThreadData->errorLog = m_pErrorLog;
+ NS_IF_ADDREF(m_pErrorLog);
+ m_pThreadData->successLog = m_pSuccessLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+ if (m_pDestinationUri)
+ m_pThreadData->pDestinationUri = strdup(m_pDestinationUri);
+
+ uint32_t count = 0;
+ m_Books->GetLength(&count);
+ // Create/obtain any address books that we need here, so that we don't need
+ // to do so inside the import thread which would just proxy the create
+ // operations back to the main thread anyway.
+ nsCOMPtr<nsIAddrDatabase> db = GetAddressBookFromUri(m_pDestinationUri);
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i);
+ if (book)
+ {
+ if (!db)
+ {
+ nsString name;
+ book->GetPreferredName(name);
+ db = GetAddressBook(name.get(), true);
+ }
+ m_DBs.AppendObject(db);
+ }
+ }
+ m_pThreadData->dBs = &m_DBs;
+
+ NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle);
+
+ nsresult rv;
+ m_pThreadData->ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv);
+
+ ImportAddressThread(m_pThreadData);
+ delete m_pThreadData;
+ m_pThreadData = nullptr;
+ *_retval = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::ContinueImport(bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = true;
+ if (m_pThreadData) {
+ if (m_pThreadData->fatalError)
+ *_retval = false;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportGenericAddressBooks::GetProgress(int32_t *_retval)
+{
+ // This returns the progress from the the currently
+ // running import mail or import address book thread.
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!m_pThreadData || !(m_pThreadData->threadAlive)) {
+ *_retval = 100;
+ return NS_OK;
+ }
+
+ uint32_t sz = 0;
+ if (m_pThreadData->currentSize && m_pInterface) {
+ if (NS_FAILED(m_pInterface->GetImportProgress(&sz)))
+ sz = 0;
+ }
+
+ if (m_totalSize)
+ *_retval = ((m_pThreadData->currentTotal + sz) * 100) / m_totalSize;
+ else
+ *_retval = 0;
+
+ // never return less than 5 so it looks like we are doing something!
+ if (*_retval < 5)
+ *_retval = 5;
+
+ // as long as the thread is alive don't return completely
+ // done.
+ if (*_retval > 99)
+ *_retval = 99;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportGenericAddressBooks::CancelImport(void)
+{
+ if (m_pThreadData) {
+ m_pThreadData->abort = true;
+ m_pThreadData = nullptr;
+ }
+
+ return NS_OK;
+}
+
+
+AddressThreadData::AddressThreadData()
+{
+ fatalError = false;
+ driverAlive = true;
+ threadAlive = true;
+ abort = false;
+ currentTotal = 0;
+ currentSize = 0;
+ books = nullptr;
+ addressImport = nullptr;
+ successLog = nullptr;
+ errorLog = nullptr;
+ pDestinationUri = nullptr;
+ fieldMap = nullptr;
+ stringBundle = nullptr;
+ ldifService = nullptr;
+}
+
+AddressThreadData::~AddressThreadData()
+{
+ if (pDestinationUri)
+ NS_Free(pDestinationUri);
+
+ NS_IF_RELEASE(books);
+ NS_IF_RELEASE(addressImport);
+ NS_IF_RELEASE(errorLog);
+ NS_IF_RELEASE(successLog);
+ NS_IF_RELEASE(fieldMap);
+ NS_IF_RELEASE(stringBundle);
+}
+
+void nsImportGenericAddressBooks::ReportError(const char16_t *pName,
+ nsString *pStream,
+ nsIStringBundle* aBundle)
+{
+ if (!pStream)
+ return;
+ // load the error string
+ char16_t *pFmt = nsImportStringBundle::GetStringByID(IMPORT_ERROR_GETABOOK, aBundle);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, pName);
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->AppendLiteral(MSG_LINEBREAK);
+}
+
+static void ImportAddressThread(void *stuff)
+{
+ IMPORT_LOG0("In Begin ImportAddressThread\n");
+
+ AddressThreadData *pData = (AddressThreadData *)stuff;
+ uint32_t count = 0;
+ uint32_t i;
+ bool import;
+ uint32_t size;
+
+ nsString success;
+ nsString error;
+
+ (void) pData->books->GetLength(&count);
+
+ for (i = 0; (i < count) && !(pData->abort); i++) {
+ nsCOMPtr<nsIImportABDescriptor> book =
+ do_QueryElementAt(pData->books, i);
+
+ if (book) {
+ import = false;
+ size = 0;
+ nsresult rv = book->GetImport(&import);
+ if (NS_SUCCEEDED(rv) && import)
+ rv = book->GetSize(&size);
+
+ if (NS_SUCCEEDED(rv) && size && import) {
+ nsString name;
+ book->GetPreferredName(name);
+
+ nsCOMPtr<nsIAddrDatabase> db = pData->dBs->ObjectAt(i);
+
+ bool fatalError = false;
+ pData->currentSize = size;
+ if (db) {
+ char16_t *pSuccess = nullptr;
+ char16_t *pError = nullptr;
+
+ /*
+ if (pData->fieldMap) {
+ int32_t sz = 0;
+ int32_t mapIndex;
+ bool active;
+ pData->fieldMap->GetMapSize(&sz);
+ IMPORT_LOG1("**** Field Map Size: %d\n", (int) sz);
+ for (int32_t i = 0; i < sz; i++) {
+ pData->fieldMap->GetFieldMap(i, &mapIndex);
+ pData->fieldMap->GetFieldActive(i, &active);
+ IMPORT_LOG3("Field map #%d: index=%d, active=%d\n", (int) i, (int) mapIndex, (int) active);
+ }
+ }
+ */
+
+ rv = pData->addressImport->ImportAddressBook(book,
+ db,
+ pData->fieldMap,
+ pData->ldifService,
+ &pError,
+ &pSuccess,
+ &fatalError);
+ if (NS_SUCCEEDED(rv) && pSuccess) {
+ success.Append(pSuccess);
+ NS_Free(pSuccess);
+ }
+ if (pError) {
+ error.Append(pError);
+ NS_Free(pError);
+ }
+ }
+ else {
+ nsImportGenericAddressBooks::ReportError(name.get(), &error, pData->stringBundle);
+ }
+
+ pData->currentSize = 0;
+ pData->currentTotal += size;
+
+ if (db)
+ db->Close(true);
+
+ if (fatalError) {
+ pData->fatalError = true;
+ break;
+ }
+ }
+ }
+ }
+
+
+ nsImportGenericAddressBooks::SetLogs(success, error, pData->successLog, pData->errorLog);
+
+ if (pData->abort || pData->fatalError) {
+ // FIXME: do what is necessary to get rid of what has been imported so far.
+ // Nothing if we went into an existing address book! Otherwise, delete
+ // the ones we created?
+ }
+
+}
diff --git a/mailnews/import/src/nsImportEmbeddedImageData.cpp b/mailnews/import/src/nsImportEmbeddedImageData.cpp
new file mode 100644
index 000000000..526f9c21c
--- /dev/null
+++ b/mailnews/import/src/nsImportEmbeddedImageData.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsImportEmbeddedImageData.h"
+
+NS_IMPL_ISUPPORTS(nsImportEmbeddedImageData, nsIMsgEmbeddedImageData)
+
+nsImportEmbeddedImageData::nsImportEmbeddedImageData()
+{
+}
+
+nsImportEmbeddedImageData::nsImportEmbeddedImageData(
+ nsIURI *aUri, const nsACString &aCid) : m_uri(aUri), m_cid(aCid)
+{
+}
+
+nsImportEmbeddedImageData::nsImportEmbeddedImageData(
+ nsIURI *aUri, const nsACString &aCid, const nsACString &aName)
+ : m_uri(aUri), m_cid(aCid), m_name(aName)
+{
+}
+
+nsImportEmbeddedImageData::~nsImportEmbeddedImageData()
+{
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::GetUri(nsIURI **aUri)
+{
+ NS_ENSURE_ARG_POINTER(aUri);
+ NS_IF_ADDREF(*aUri = m_uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::SetUri(nsIURI *aUri)
+{
+ m_uri = aUri;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::GetCid(nsACString &aCid)
+{
+ aCid = m_cid;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::SetCid(const nsACString &aCid)
+{
+ m_cid = aCid;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::GetName(nsACString &aName)
+{
+ aName = m_name;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportEmbeddedImageData::SetName(const nsACString &aName)
+{
+ m_name = aName;
+ return NS_OK;
+}
diff --git a/mailnews/import/src/nsImportEmbeddedImageData.h b/mailnews/import/src/nsImportEmbeddedImageData.h
new file mode 100644
index 000000000..5635dc512
--- /dev/null
+++ b/mailnews/import/src/nsImportEmbeddedImageData.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __IMPORTEMBEDDEDIMAGETDATA_H__
+#define __IMPORTEMBEDDEDIMAGETDATA_H__
+
+#include "nsIMsgSend.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsIURI.h"
+
+class nsImportEmbeddedImageData final : public nsIMsgEmbeddedImageData
+{
+public:
+ nsImportEmbeddedImageData(nsIURI *aUri, const nsACString &aCID);
+ nsImportEmbeddedImageData(nsIURI *aUri, const nsACString &aCID, const nsACString &aName);
+ nsImportEmbeddedImageData();
+ NS_DECL_NSIMSGEMBEDDEDIMAGEDATA
+ NS_DECL_ISUPPORTS
+
+ nsCOMPtr<nsIURI> m_uri;
+ nsCString m_cid;
+ nsCString m_name;
+
+private:
+ ~nsImportEmbeddedImageData();
+};
+
+
+#endif
diff --git a/mailnews/import/src/nsImportEncodeScan.cpp b/mailnews/import/src/nsImportEncodeScan.cpp
new file mode 100644
index 000000000..77e89198d
--- /dev/null
+++ b/mailnews/import/src/nsImportEncodeScan.cpp
@@ -0,0 +1,374 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "nsImportEncodeScan.h"
+#include "nsNetUtil.h"
+
+#define kBeginAppleSingle 0
+#define kBeginDataFork 1
+#define kBeginResourceFork 2
+#define kAddEntries 3
+#define kScanningDataFork 4
+#define kScanningRsrcFork 5
+#define kDoneWithFile 6
+
+uint32_t gAppleSingleHeader[6] = {0x00051600, 0x00020000, 0, 0, 0, 0};
+#define kAppleSingleHeaderSize (6 * sizeof(uint32_t))
+
+#ifdef _MAC_IMPORT_CODE
+#include "MoreFilesExtras.h"
+#include "MoreDesktopMgr.h"
+
+CInfoPBRec gCatInfoPB;
+U32 g2000Secs = 0;
+long gGMTDelta = 0;
+
+long GetGmtDelta(void);
+U32 Get2000Secs(void);
+
+
+long GetGmtDelta(void)
+{
+ MachineLocation myLocation;
+ ReadLocation(&myLocation);
+ long myDelta = BitAnd(myLocation.u.gmtDelta, 0x00FFFFFF);
+ if (BitTst(&myDelta, 23))
+ myDelta = BitOr(myDelta, 0xFF000000);
+ return myDelta;
+}
+
+U32 Get2000Secs(void)
+{
+ DateTimeRec dr;
+ dr.year = 2000;
+ dr.month = 1;
+ dr.day = 1;
+ dr.hour = 0;
+ dr.minute = 0;
+ dr.second = 0;
+ dr.dayOfWeek = 0;
+ U32 result;
+ DateToSeconds(&dr, &result);
+ return result;
+}
+#endif
+
+nsImportEncodeScan::nsImportEncodeScan()
+{
+ m_isAppleSingle = false;
+ m_encodeScanState = 0;
+ m_resourceForkSize = 0;
+ m_dataForkSize = 0;
+}
+
+nsImportEncodeScan::~nsImportEncodeScan()
+{
+}
+
+bool nsImportEncodeScan::InitEncodeScan(bool appleSingleEncode, nsIFile *fileLoc, const char *pName, uint8_t * pBuf, uint32_t sz)
+{
+ CleanUpEncodeScan();
+ m_isAppleSingle = appleSingleEncode;
+ m_encodeScanState = kBeginAppleSingle;
+ m_pInputFile = do_QueryInterface(fileLoc);
+ m_useFileName = pName;
+ m_pBuf = pBuf;
+ m_bufSz = sz;
+ if (!m_isAppleSingle)
+ {
+ if (!m_inputStream)
+ {
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(m_inputStream), m_pInputFile);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+
+ InitScan(m_inputStream, pBuf, sz);
+ }
+ else {
+ #ifdef _MAC_IMPORT_CODE
+ // Fill in the file sizes
+ m_resourceForkSize = fileLoc.GetMacFileSize(UFileLocation::eResourceFork);
+ m_dataForkSize = fileLoc.GetMacFileSize(UFileLocation::eDataFork);
+ #endif
+ }
+
+ return true;
+}
+
+void nsImportEncodeScan::CleanUpEncodeScan(void)
+{
+ m_pInputStream->Close();
+ m_pInputStream = nullptr;
+ m_pInputFile = nullptr;
+}
+
+
+// 26 + 12 per entry
+
+void nsImportEncodeScan::FillInEntries(int numEntries)
+{
+#ifdef _MAC_IMPORT_CODE
+ int len = m_useFileName.GetLength();
+ if (len < 32)
+ len = 32;
+ long entry[3];
+ long fileOffset = 26 + (12 * numEntries);
+ entry[0] = 3;
+ entry[1] = fileOffset;
+ entry[2] = m_useFileName.GetLength();
+ fileOffset += len;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+
+
+ Str255 comment;
+ comment[0] = 0;
+ OSErr err = FSpDTGetComment(m_inputFileLoc, comment);
+ if (comment[0] > 200)
+ comment[0] = 200;
+ entry[0] = 4;
+ entry[1] = fileOffset;
+ entry[2] = comment[0];
+ fileOffset += 200;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+
+
+ entry[0] = 8;
+ entry[1] = fileOffset;
+ entry[2] = 16;
+ fileOffset += 16;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+
+ entry[0] = 9;
+ entry[1] = fileOffset;
+ entry[2] = 32;
+ fileOffset += 32;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+
+
+ entry[0] = 10;
+ entry[1] = fileOffset;
+ entry[2] = 4;
+ fileOffset += 4;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+
+ if (m_resourceForkSize) {
+ entry[0] = 2;
+ entry[1] = fileOffset;
+ entry[2] = m_resourceForkSize;
+ fileOffset += m_resourceForkSize;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+ }
+
+ if (m_dataForkSize) {
+ entry[0] = 1;
+ entry[1] = fileOffset;
+ entry[2] = m_dataForkSize;
+ fileOffset += m_dataForkSize;
+ MemCpy(m_pBuf + m_bytesInBuf, entry, 12);
+ m_bytesInBuf += 12;
+ }
+
+#endif
+}
+
+bool nsImportEncodeScan::AddEntries(void)
+{
+#ifdef _MAC_IMPORT_CODE
+ if (!g2000Secs) {
+ g2000Secs = Get2000Secs();
+ gGMTDelta = GetGmtDelta();
+ }
+ MemCpy(m_pBuf + m_bytesInBuf, (PC_S8) m_useFileName, m_useFileName.GetLength());
+ m_bytesInBuf += m_useFileName.GetLength();
+ if (m_useFileName.GetLength() < 32) {
+ int len = m_useFileName.GetLength();
+ while (len < 32) {
+ *((P_S8)m_pBuf + m_bytesInBuf) = 0;
+ m_bytesInBuf++;
+ len++;
+ }
+ }
+
+ Str255 comment;
+ comment[0] = 0;
+ OSErr err = FSpDTGetComment(m_inputFileLoc, comment);
+ comment[0] = 200;
+ MemCpy(m_pBuf + m_bytesInBuf, &(comment[1]), comment[0]);
+ m_bytesInBuf += comment[0];
+
+ long dates[4];
+ dates[0] = gCatInfoPB.hFileInfo.ioFlCrDat;
+ dates[1] = gCatInfoPB.hFileInfo.ioFlMdDat;
+ dates[2] = gCatInfoPB.hFileInfo.ioFlBkDat;
+ dates[3] = 0x80000000;
+ for (short i = 0; i < 3; i++) {
+ dates[i] -= g2000Secs;
+ dates[i] += gGMTDelta;
+ }
+ MemCpy(m_pBuf + m_bytesInBuf, dates, 16);
+ m_bytesInBuf += 16;
+
+
+ FInfo fInfo = gCatInfoPB.hFileInfo.ioFlFndrInfo;
+ FXInfo fxInfo = gCatInfoPB.hFileInfo.ioFlXFndrInfo;
+ fInfo.fdFlags = 0;
+ fInfo.fdLocation.h = 0;
+ fInfo.fdLocation.v = 0;
+ fInfo.fdFldr = 0;
+ MemSet(&fxInfo, 0, sizeof(fxInfo));
+ MemCpy(m_pBuf + m_bytesInBuf, &fInfo, 16);
+ m_bytesInBuf += 16;
+ MemCpy(m_pBuf + m_bytesInBuf, &fxInfo, 16);
+ m_bytesInBuf += 16;
+
+
+ dates[0] = 0;
+ if ((gCatInfoPB.hFileInfo.ioFlAttrib & 1) != 0)
+ dates[0] |= 1;
+ MemCpy(m_pBuf + m_bytesInBuf, dates, 4);
+ m_bytesInBuf += 4;
+
+
+#endif
+ return true;
+}
+
+bool nsImportEncodeScan::Scan(bool *pDone)
+{
+ nsresult rv;
+
+ *pDone = false;
+ if (m_isAppleSingle) {
+ // Stuff the buffer with things needed to encode the file...
+ // then just allow UScanFile to handle each fork, but be careful
+ // when handling eof.
+ switch(m_encodeScanState) {
+ case kBeginAppleSingle: {
+#ifdef _MAC_IMPORT_CODE
+ OSErr err = GetCatInfoNoName(m_inputFileLoc.GetVRefNum(), m_inputFileLoc.GetParID(), m_inputFileLoc.GetFileNamePtr(), &gCatInfoPB);
+ if (err != noErr)
+ return FALSE;
+#endif
+ m_eof = false;
+ m_pos = 0;
+ memcpy(m_pBuf, gAppleSingleHeader, kAppleSingleHeaderSize);
+ m_bytesInBuf = kAppleSingleHeaderSize;
+ int numEntries = 5;
+ if (m_dataForkSize)
+ numEntries++;
+ if (m_resourceForkSize)
+ numEntries++;
+ memcpy(m_pBuf + m_bytesInBuf, &numEntries, sizeof(numEntries));
+ m_bytesInBuf += sizeof(numEntries);
+ FillInEntries(numEntries);
+ m_encodeScanState = kAddEntries;
+ return ScanBuffer(pDone);
+ }
+ break;
+
+ case kBeginDataFork: {
+ if (!m_dataForkSize) {
+ m_encodeScanState = kDoneWithFile;
+ return true;
+ }
+ // Initialize the scan of the data fork...
+ if (!m_inputStream)
+ {
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(m_inputStream), m_pInputFile);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+ m_encodeScanState = kScanningDataFork;
+ return true;
+ }
+ break;
+
+ case kScanningDataFork: {
+ bool result = FillBufferFromFile();
+ if (!result)
+ return false;
+ if (m_eof) {
+ m_eof = false;
+ result = ScanBuffer(pDone);
+ if (!result)
+ return false;
+ m_inputStream->Close();
+ m_inputStream = nullptr;
+ m_encodeScanState = kDoneWithFile;
+ return true;
+ }
+ else
+ return ScanBuffer(pDone);
+ }
+ break;
+
+ case kScanningRsrcFork: {
+ bool result = FillBufferFromFile();
+ if (!result)
+ return false;
+ if (m_eof) {
+ m_eof = false;
+ result = ScanBuffer(pDone);
+ if (!result)
+ return false;
+ m_inputStream->Close();
+ m_inputStream = nullptr;
+ m_encodeScanState = kBeginDataFork;
+ return true;
+ }
+ else
+ return ScanBuffer(pDone);
+ }
+ break;
+
+ case kBeginResourceFork: {
+ if (!m_resourceForkSize) {
+ m_encodeScanState = kBeginDataFork;
+ return true;
+ }
+ /*
+ // FIXME: Open the resource fork on the Mac!!!
+ m_fH = UFile::OpenRsrcFileRead(m_inputFileLoc);
+ if (m_fH == TR_FILE_ERROR)
+ return FALSE;
+ */
+ m_encodeScanState = kScanningRsrcFork;
+ return true;
+ }
+ break;
+
+ case kAddEntries: {
+ ShiftBuffer();
+ if (!AddEntries())
+ return false;
+ m_encodeScanState = kBeginResourceFork;
+ return ScanBuffer(pDone);
+ }
+ break;
+
+ case kDoneWithFile: {
+ ShiftBuffer();
+ m_eof = true;
+ if (!ScanBuffer(pDone))
+ return false;
+ *pDone = true;
+ return true;
+ }
+ break;
+ }
+
+ }
+ else
+ return nsImportScanFile::Scan(pDone);
+
+ return false;
+}
+
diff --git a/mailnews/import/src/nsImportEncodeScan.h b/mailnews/import/src/nsImportEncodeScan.h
new file mode 100644
index 000000000..3f0e246f1
--- /dev/null
+++ b/mailnews/import/src/nsImportEncodeScan.h
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+
+#ifndef nsImportEncodeScan_h___
+#define nsImportEncodeScan_h___
+
+#include "mozilla/Attributes.h"
+#include "nsIFile.h"
+#include "nsImportScanFile.h"
+#include "nsStringGlue.h"
+
+class nsImportEncodeScan : public nsImportScanFile {
+public:
+ nsImportEncodeScan();
+ ~nsImportEncodeScan();
+
+ bool InitEncodeScan(bool appleSingleEncode, nsIFile *pFile, const char *pName, uint8_t * pBuf, uint32_t sz);
+ void CleanUpEncodeScan(void);
+
+ virtual bool Scan(bool *pDone) override;
+
+protected:
+ void FillInEntries(int numEntries);
+ bool AddEntries(void);
+
+protected:
+ bool m_isAppleSingle;
+ nsCOMPtr<nsIFile> m_pInputFile;
+ nsCOMPtr<nsIInputStream> m_inputStream;
+ int m_encodeScanState;
+ long m_resourceForkSize;
+ long m_dataForkSize;
+ nsCString m_useFileName;
+};
+
+#endif /* nsImportEncodeScan_h__ */
+
diff --git a/mailnews/import/src/nsImportFieldMap.cpp b/mailnews/import/src/nsImportFieldMap.cpp
new file mode 100644
index 000000000..d5e9748dc
--- /dev/null
+++ b/mailnews/import/src/nsImportFieldMap.cpp
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nscore.h"
+#include "nsIStringBundle.h"
+#include "nsImportFieldMap.h"
+#include "nsImportStringBundle.h"
+#include "nsCRTGlue.h"
+#include "ImportDebug.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////
+
+NS_METHOD nsImportFieldMap::Create(nsIStringBundle *aBundle, nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsImportFieldMap *it = new nsImportFieldMap(aBundle);
+ if (it == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(it);
+ nsresult rv = it->QueryInterface(aIID, aResult);
+ NS_RELEASE(it);
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsImportFieldMap, nsIImportFieldMap)
+
+NS_IMETHODIMP nsImportFieldMap::GetSkipFirstRecord(bool *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ *result = m_skipFirstRecord;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::SetSkipFirstRecord(bool aResult)
+{
+ m_skipFirstRecord = aResult;
+ return NS_OK;
+}
+
+nsImportFieldMap::nsImportFieldMap(nsIStringBundle *aBundle)
+{
+ m_numFields = 0;
+ m_pFields = nullptr;
+ m_pActive = nullptr;
+ m_allocated = 0;
+ // need to init the description array
+ m_mozFieldCount = 0;
+ m_skipFirstRecord = false;
+ nsCOMPtr<nsIStringBundle> pBundle = aBundle;
+
+ nsString *pStr;
+ for (int32_t i = IMPORT_FIELD_DESC_START; i <= IMPORT_FIELD_DESC_END; i++, m_mozFieldCount++) {
+ pStr = new nsString();
+ if (pBundle) {
+ nsImportStringBundle::GetStringByID(i, pBundle, *pStr);
+ }
+ else
+ pStr->AppendInt(i);
+ m_descriptions.AppendElement(pStr);
+ }
+}
+
+nsImportFieldMap::~nsImportFieldMap()
+{
+ if (m_pFields)
+ delete [] m_pFields;
+ if (m_pActive)
+ delete [] m_pActive;
+
+ nsString * pStr;
+ for (int32_t i = 0; i < m_mozFieldCount; i++) {
+ pStr = m_descriptions.ElementAt(i);
+ delete pStr;
+ }
+ m_descriptions.Clear();
+}
+
+
+NS_IMETHODIMP nsImportFieldMap::GetNumMozFields(int32_t *aNumFields)
+{
+ NS_PRECONDITION(aNumFields != nullptr, "null ptr");
+ if (!aNumFields)
+ return NS_ERROR_NULL_POINTER;
+
+ *aNumFields = m_mozFieldCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::GetMapSize(int32_t *aNumFields)
+{
+ NS_PRECONDITION(aNumFields != nullptr, "null ptr");
+ if (!aNumFields)
+ return NS_ERROR_NULL_POINTER;
+
+ *aNumFields = m_numFields;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::GetFieldDescription(int32_t index, char16_t **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = nullptr;
+ if ((index < 0) || ((size_t)index >= m_descriptions.Length()))
+ return NS_ERROR_FAILURE;
+
+ *_retval = ToNewUnicode(*(m_descriptions.ElementAt(index)));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::SetFieldMapSize(int32_t size)
+{
+ nsresult rv = Allocate(size);
+ if (NS_FAILED(rv))
+ return rv;
+
+ m_numFields = size;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportFieldMap::DefaultFieldMap(int32_t size)
+{
+ nsresult rv = SetFieldMapSize(size);
+ if (NS_FAILED(rv))
+ return rv;
+ for (int32_t i = 0; i < size; i++) {
+ m_pFields[i] = i;
+ m_pActive[i] = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::GetFieldMap(int32_t index, int32_t *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+
+ if ((index < 0) || (index >= m_numFields))
+ return NS_ERROR_FAILURE;
+
+ *_retval = m_pFields[index];
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::SetFieldMap(int32_t index, int32_t fieldNum)
+{
+ if (index == -1) {
+ nsresult rv = Allocate(m_numFields + 1);
+ if (NS_FAILED(rv))
+ return rv;
+ index = m_numFields;
+ m_numFields++;
+ }
+ else {
+ if ((index < 0) || (index >= m_numFields))
+ return NS_ERROR_FAILURE;
+ }
+
+ if ((fieldNum != -1) && ((fieldNum < 0) || (fieldNum >= m_mozFieldCount)))
+ return NS_ERROR_FAILURE;
+
+ m_pFields[index] = fieldNum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::GetFieldActive(int32_t index, bool *active)
+{
+ NS_PRECONDITION(active != nullptr, "null ptr");
+ if (!active)
+ return NS_ERROR_NULL_POINTER;
+ if ((index < 0) || (index >= m_numFields))
+ return NS_ERROR_FAILURE;
+
+ *active = m_pActive[index];
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportFieldMap::SetFieldActive(int32_t index, bool active)
+{
+ if ((index < 0) || (index >= m_numFields))
+ return NS_ERROR_FAILURE;
+
+ m_pActive[index] = active;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportFieldMap::SetFieldValue(nsIAddrDatabase *database, nsIMdbRow *row, int32_t fieldNum, const char16_t *value)
+{
+ NS_PRECONDITION(database != nullptr, "null ptr");
+ NS_PRECONDITION(row != nullptr, "null ptr");
+ NS_PRECONDITION(value != nullptr, "null ptr");
+ if (!database || !row || !value)
+ return NS_ERROR_NULL_POINTER;
+
+ // Allow the special value for a null field
+ if (fieldNum == -1)
+ return NS_OK;
+
+ if ((fieldNum < 0) || (fieldNum >= m_mozFieldCount))
+ return NS_ERROR_FAILURE;
+
+ // UGGG!!!!! lot's of typing here!
+ nsresult rv;
+
+ nsString str(value);
+ char *pVal = ToNewUTF8String(str);
+
+ switch(fieldNum) {
+ case 0:
+ rv = database->AddFirstName(row, pVal);
+ break;
+ case 1:
+ rv = database->AddLastName(row, pVal);
+ break;
+ case 2:
+ rv = database->AddDisplayName(row, pVal);
+ break;
+ case 3:
+ rv = database->AddNickName(row, pVal);
+ break;
+ case 4:
+ rv = database->AddPrimaryEmail(row, pVal);
+ break;
+ case 5:
+ rv = database->Add2ndEmail(row, pVal);
+ break;
+ case 6:
+ rv = database->AddWorkPhone(row, pVal);
+ break;
+ case 7:
+ rv = database->AddHomePhone(row, pVal);
+ break;
+ case 8:
+ rv = database->AddFaxNumber(row, pVal);
+ break;
+ case 9:
+ rv = database->AddPagerNumber(row, pVal);
+ break;
+ case 10:
+ rv = database->AddCellularNumber(row, pVal);
+ break;
+ case 11:
+ rv = database->AddHomeAddress(row, pVal);
+ break;
+ case 12:
+ rv = database->AddHomeAddress2(row, pVal);
+ break;
+ case 13:
+ rv = database->AddHomeCity(row, pVal);
+ break;
+ case 14:
+ rv = database->AddHomeState(row, pVal);
+ break;
+ case 15:
+ rv = database->AddHomeZipCode(row, pVal);
+ break;
+ case 16:
+ rv = database->AddHomeCountry(row, pVal);
+ break;
+ case 17:
+ rv = database->AddWorkAddress(row, pVal);
+ break;
+ case 18:
+ rv = database->AddWorkAddress2(row, pVal);
+ break;
+ case 19:
+ rv = database->AddWorkCity(row, pVal);
+ break;
+ case 20:
+ rv = database->AddWorkState(row, pVal);
+ break;
+ case 21:
+ rv = database->AddWorkZipCode(row, pVal);
+ break;
+ case 22:
+ rv = database->AddWorkCountry(row, pVal);
+ break;
+ case 23:
+ rv = database->AddJobTitle(row, pVal);
+ break;
+ case 24:
+ rv = database->AddDepartment(row, pVal);
+ break;
+ case 25:
+ rv = database->AddCompany(row, pVal);
+ break;
+ case 26:
+ rv = database->AddWebPage1(row, pVal);
+ break;
+ case 27:
+ rv = database->AddWebPage2(row, pVal);
+ break;
+ case 28:
+ rv = database->AddBirthYear(row, pVal);
+ break;
+ case 29:
+ rv = database->AddBirthMonth(row, pVal);
+ break;
+ case 30:
+ rv = database->AddBirthDay(row, pVal);
+ break;
+ case 31:
+ rv = database->AddCustom1(row, pVal);
+ break;
+ case 32:
+ rv = database->AddCustom2(row, pVal);
+ break;
+ case 33:
+ rv = database->AddCustom3(row, pVal);
+ break;
+ case 34:
+ rv = database->AddCustom4(row, pVal);
+ break;
+ case 35:
+ rv = database->AddNotes(row, pVal);
+ break;
+ case 36:
+ rv = database->AddAimScreenName(row, pVal);
+ break;
+ default:
+ /* Get the field description, and add it as an anonymous attr? */
+ /* OR WHAT???? */
+ {
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+
+ NS_Free(pVal);
+
+ return rv;
+}
+
+
+nsresult nsImportFieldMap::Allocate(int32_t newSize)
+{
+ if (newSize <= m_allocated)
+ return NS_OK;
+
+ int32_t sz = m_allocated;
+ while (sz < newSize)
+ sz += 30;
+
+ int32_t *pData = new int32_t[ sz];
+ if (!pData)
+ return NS_ERROR_OUT_OF_MEMORY;
+ bool *pActive = new bool[sz];
+ if (!pActive) {
+ delete [] pData;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t i;
+ for (i = 0; i < sz; i++) {
+ pData[i] = -1;
+ pActive[i] = true;
+ }
+ if (m_numFields) {
+ for (i = 0; i < m_numFields; i++) {
+ pData[i] = m_pFields[i];
+ pActive[i] = m_pActive[i];
+ }
+ delete [] m_pFields;
+ delete [] m_pActive;
+ }
+ m_allocated = sz;
+ m_pFields = pData;
+ m_pActive = pActive;
+ return NS_OK;
+}
diff --git a/mailnews/import/src/nsImportFieldMap.h b/mailnews/import/src/nsImportFieldMap.h
new file mode 100644
index 000000000..314abd800
--- /dev/null
+++ b/mailnews/import/src/nsImportFieldMap.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsImportFieldMap_h___
+#define nsImportFieldMap_h___
+
+#include "nscore.h"
+#include "nsIImportFieldMap.h"
+#include "nsIAddrDatabase.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+
+////////////////////////////////////////////////////////////////////////
+
+class nsIStringBundle;
+
+class nsImportFieldMap : public nsIImportFieldMap
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIIMPORTFIELDMAP
+
+ explicit nsImportFieldMap(nsIStringBundle *aBundle);
+
+ static NS_METHOD Create(nsIStringBundle *aBundle, nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ virtual ~nsImportFieldMap();
+ nsresult Allocate(int32_t newSize);
+
+private:
+ int32_t m_numFields;
+ int32_t * m_pFields;
+ bool * m_pActive;
+ int32_t m_allocated;
+ nsTArray<nsString*> m_descriptions;
+ int32_t m_mozFieldCount;
+ bool m_skipFirstRecord;
+};
+
+
+#endif
diff --git a/mailnews/import/src/nsImportMail.cpp b/mailnews/import/src/nsImportMail.cpp
new file mode 100644
index 000000000..9e0f6bcd2
--- /dev/null
+++ b/mailnews/import/src/nsImportMail.cpp
@@ -0,0 +1,1208 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "prthread.h"
+#include "prprf.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+
+#include "nsIImportMail.h"
+#include "nsIImportGeneric.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportMailboxDescriptor.h"
+
+#include "nsStringGlue.h"
+#include "nsUnicharUtils.h"
+
+#include "nsMsgUtils.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgFolder.h"
+#include "nsImportStringBundle.h"
+#include "nsIStringBundle.h"
+#include "nsTextFormatter.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIImportService.h"
+#include "ImportDebug.h"
+#include "plstr.h"
+#include "MailNewsTypes.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+
+#define IMPORT_MSGS_URL "chrome://messenger/locale/importMsgs.properties"
+
+////////////////////////////////////////////////////////////////////////
+
+static void ImportMailThread(void *stuff);
+
+class ImportThreadData;
+
+class nsImportGenericMail : public nsIImportGeneric
+{
+public:
+
+ nsImportGenericMail();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* nsISupports GetData (in string dataId); */
+ NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override;
+
+ NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override;
+
+ NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override;
+
+ NS_IMETHOD WantsProgress(bool *_retval) override;
+
+ NS_IMETHODIMP BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override;
+
+ NS_IMETHOD ContinueImport(bool *_retval) override;
+
+ NS_IMETHOD GetProgress(int32_t *_retval) override;
+
+ NS_IMETHOD CancelImport(void) override;
+
+private:
+ virtual ~nsImportGenericMail();
+ bool CreateFolder(nsIMsgFolder **ppFolder);
+ void GetDefaultMailboxes(void);
+ void GetDefaultLocation(void);
+ void GetDefaultDestination(void);
+ void GetMailboxName(uint32_t index, nsISupportsString *pStr);
+
+public:
+ static void SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError);
+ static void ReportError(int32_t id, const char16_t *pName, nsString *pStream, nsIStringBundle* aBundle);
+
+private:
+ nsString m_pName; // module name that created this interface
+ nsIMsgFolder * m_pDestFolder;
+ bool m_deleteDestFolder;
+ bool m_createdFolder;
+ nsCOMPtr <nsIFile> m_pSrcLocation;
+ bool m_gotLocation;
+ bool m_found;
+ bool m_userVerify;
+ nsIImportMail *m_pInterface;
+ nsIArray * m_pMailboxes;
+ nsISupportsString *m_pSuccessLog;
+ nsISupportsString *m_pErrorLog;
+ uint32_t m_totalSize;
+ bool m_doImport;
+ ImportThreadData * m_pThreadData;
+ bool m_performingMigration;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+class ImportThreadData {
+public:
+ bool driverAlive;
+ bool threadAlive;
+ bool abort;
+ bool fatalError;
+ uint32_t currentTotal;
+ uint32_t currentSize;
+ nsIMsgFolder * destRoot;
+ bool ownsDestRoot;
+ nsIArray *boxes;
+ nsIImportMail * mailImport;
+ nsISupportsString * successLog;
+ nsISupportsString * errorLog;
+ uint32_t currentMailbox;
+ bool performingMigration;
+ nsIStringBundle *stringBundle;
+
+ ImportThreadData();
+ ~ImportThreadData();
+ void DriverDelete();
+ void ThreadDelete();
+ void DriverAbort();
+};
+
+// forward decl for proxy methods
+nsresult ProxyGetSubFolders(nsIMsgFolder *aFolder);
+nsresult ProxyGetChildNamed(nsIMsgFolder *aFolder,const nsAString & aName,
+ nsIMsgFolder **aChild);
+nsresult ProxyGetParent(nsIMsgFolder *aFolder, nsIMsgFolder **aParent);
+nsresult ProxyContainsChildNamed(nsIMsgFolder *aFolder, const nsAString &aName,
+ bool *aResult);
+nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder *aFolder,
+ const nsAString& aPrefix,
+ nsIMsgFolder *aOtherFolder,
+ nsAString& aName);
+nsresult ProxyCreateSubfolder(nsIMsgFolder *aFolder, const nsAString &aName);
+nsresult ProxyForceDBClosed(nsIMsgFolder *aFolder);
+
+nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric)
+{
+ NS_PRECONDITION(aImportGeneric != nullptr, "null ptr");
+ if (! aImportGeneric)
+ return NS_ERROR_NULL_POINTER;
+
+ nsImportGenericMail *pGen = new nsImportGenericMail();
+
+ if (pGen == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(pGen);
+ nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric);
+ NS_RELEASE(pGen);
+
+ return rv;
+}
+
+nsImportGenericMail::nsImportGenericMail()
+{
+ m_found = false;
+ m_userVerify = false;
+ m_gotLocation = false;
+ m_pInterface = nullptr;
+ m_pMailboxes = nullptr;
+ m_pSuccessLog = nullptr;
+ m_pErrorLog = nullptr;
+ m_totalSize = 0;
+ m_doImport = false;
+ m_pThreadData = nullptr;
+
+ m_pDestFolder = nullptr;
+ m_deleteDestFolder = false;
+ m_createdFolder = false;
+ m_performingMigration = false;
+
+ // Init logging module.
+ if (!IMPORTLOGMODULE)
+ IMPORTLOGMODULE = PR_NewLogModule("IMPORT");
+
+ nsresult rv = nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
+ if (NS_FAILED(rv))
+ IMPORT_LOG0("Failed to get string bundle for Importing Mail");
+}
+
+
+nsImportGenericMail::~nsImportGenericMail()
+{
+ if (m_pThreadData) {
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ NS_IF_RELEASE(m_pDestFolder);
+ NS_IF_RELEASE(m_pInterface);
+ NS_IF_RELEASE(m_pMailboxes);
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+}
+
+
+
+NS_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric)
+
+
+NS_IMETHODIMP nsImportGenericMail::GetData(const char *dataId, nsISupports **_retval)
+{
+ nsresult rv = NS_OK;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = nullptr;
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ *_retval = m_pInterface;
+ NS_IF_ADDREF(m_pInterface);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailBoxes")) {
+ if (!m_pMailboxes)
+ GetDefaultMailboxes();
+ *_retval = m_pMailboxes;
+ NS_IF_ADDREF(m_pMailboxes);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailLocation")) {
+ if (!m_pSrcLocation)
+ GetDefaultLocation();
+ NS_IF_ADDREF(*_retval = m_pSrcLocation);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailDestination")) {
+ if (!m_pDestFolder)
+ GetDefaultDestination();
+ NS_IF_ADDREF(*_retval = m_pDestFolder);
+ }
+
+ if (!PL_strcasecmp(dataId, "migration")) {
+ nsCOMPtr<nsISupportsPRBool> migrationString = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ migrationString->SetData(m_performingMigration);
+ NS_IF_ADDREF(*_retval = migrationString);
+ }
+
+ if (!PL_strcasecmp(dataId, "currentMailbox")) {
+ // create an nsISupportsString, get the current mailbox
+ // name being imported and put it in the string
+ nsCOMPtr<nsISupportsString> data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+ if (m_pThreadData) {
+ GetMailboxName(m_pThreadData->currentMailbox, data);
+ }
+ NS_ADDREF(*_retval = data);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::SetData(const char *dataId, nsISupports *item)
+{
+ nsresult rv = NS_OK;
+ NS_PRECONDITION(dataId != nullptr, "null ptr");
+ if (!dataId)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ NS_IF_RELEASE(m_pInterface);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIImportMail), (void **) &m_pInterface);
+ }
+ if (!PL_strcasecmp(dataId, "mailBoxes")) {
+ NS_IF_RELEASE(m_pMailboxes);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_pMailboxes);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailLocation")) {
+ NS_IF_RELEASE(m_pMailboxes);
+ m_pSrcLocation = nullptr;
+ if (item) {
+ nsresult rv;
+ nsCOMPtr <nsIFile> location = do_QueryInterface(item, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ m_pSrcLocation = location;
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "mailDestination")) {
+ NS_IF_RELEASE(m_pDestFolder);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) &m_pDestFolder);
+ m_deleteDestFolder = false;
+ }
+
+ if (!PL_strcasecmp(dataId, "name")) {
+ nsCOMPtr<nsISupportsString> nameString;
+ if (item) {
+ item->QueryInterface(NS_GET_IID(nsISupportsString), getter_AddRefs(nameString));
+ rv = nameString->GetData(m_pName);
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "migration")) {
+ nsCOMPtr<nsISupportsPRBool> migrationString;
+ if (item) {
+ item->QueryInterface(NS_GET_IID(nsISupportsPRBool), getter_AddRefs(migrationString));
+ rv = migrationString->GetData(&m_performingMigration);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::GetStatus(const char *statusKind, int32_t *_retval)
+{
+ NS_PRECONDITION(statusKind != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!statusKind || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = 0;
+
+ if (!PL_strcasecmp(statusKind, "isInstalled")) {
+ GetDefaultLocation();
+ *_retval = (int32_t) m_found;
+ }
+
+ if (!PL_strcasecmp(statusKind, "canUserSetLocation")) {
+ GetDefaultLocation();
+ *_retval = (int32_t) m_userVerify;
+ }
+
+ return NS_OK;
+}
+
+
+void nsImportGenericMail::GetDefaultLocation(void)
+{
+ if (!m_pInterface)
+ return;
+
+ if (m_pSrcLocation && m_gotLocation)
+ return;
+
+ m_gotLocation = true;
+
+ nsCOMPtr <nsIFile> pLoc;
+ m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify);
+ if (!m_pSrcLocation)
+ m_pSrcLocation = pLoc;
+}
+
+void nsImportGenericMail::GetDefaultMailboxes(void)
+{
+ if (!m_pInterface || m_pMailboxes || !m_pSrcLocation)
+ return;
+
+ m_pInterface->FindMailboxes(m_pSrcLocation, &m_pMailboxes);
+}
+
+void nsImportGenericMail::GetDefaultDestination(void)
+{
+ if (m_pDestFolder)
+ return;
+ if (!m_pInterface)
+ return;
+
+ nsIMsgFolder * rootFolder;
+ m_deleteDestFolder = false;
+ m_createdFolder = false;
+ if (CreateFolder(&rootFolder)) {
+ m_pDestFolder = rootFolder;
+ m_deleteDestFolder = true;
+ m_createdFolder = true;
+ return;
+ }
+ IMPORT_LOG0("*** GetDefaultDestination: Failed to create a default import destination folder.");
+}
+
+NS_IMETHODIMP nsImportGenericMail::WantsProgress(bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (m_pThreadData) {
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ if (!m_pMailboxes) {
+ GetDefaultLocation();
+ GetDefaultMailboxes();
+ }
+
+ if (!m_pDestFolder) {
+ GetDefaultDestination();
+ }
+
+ bool result = false;
+
+ if (m_pMailboxes) {
+ uint32_t i;
+ bool import;
+ uint32_t count = 0;
+ uint32_t size;
+ uint32_t totalSize = 0;
+
+ (void) m_pMailboxes->GetLength(&count);
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box =
+ do_QueryElementAt(m_pMailboxes, i);
+ if (box) {
+ import = false;
+ size = 0;
+ nsresult rv = box->GetImport(&import);
+ if (NS_SUCCEEDED(rv) && import) {
+ (void) box->GetSize(&size);
+ result = true;
+ }
+ totalSize += size;
+ }
+ }
+
+ m_totalSize = totalSize;
+ }
+
+ m_doImport = result;
+
+ *_retval = result;
+
+ return NS_OK;
+}
+
+void nsImportGenericMail::GetMailboxName(uint32_t index, nsISupportsString *pStr)
+{
+ if (m_pMailboxes) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box(do_QueryElementAt(m_pMailboxes, index));
+ if (box) {
+ nsAutoString name;
+ box->GetDisplayName(getter_Copies(name));
+ if (!name.IsEmpty()) {
+ pStr->SetData(name);
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ nsString success;
+ nsString error;
+
+ if (!m_doImport) {
+ nsImportStringBundle::GetStringByID(IMPORT_NO_MAILBOXES,
+ m_stringBundle, success);
+ SetLogs(success, error, successLog, errorLog);
+ *_retval = true;
+ return NS_OK;
+ }
+
+ if (!m_pInterface || !m_pMailboxes) {
+ IMPORT_LOG0("*** BeginImport: Either the interface or source mailbox is not set properly.");
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTINITIALIZED,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (!m_pDestFolder) {
+ IMPORT_LOG0("*** BeginImport: The destination mailbox is not set properly.");
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NODESTFOLDER,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (m_pThreadData) {
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+ m_pSuccessLog = successLog;
+ m_pErrorLog = errorLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+ NS_IF_ADDREF(m_pErrorLog);
+
+
+ // kick off the thread to do the import!!!!
+ m_pThreadData = new ImportThreadData();
+ m_pThreadData->boxes = m_pMailboxes;
+ NS_ADDREF(m_pMailboxes);
+ m_pThreadData->mailImport = m_pInterface;
+ NS_ADDREF(m_pInterface);
+ m_pThreadData->errorLog = m_pErrorLog;
+ NS_IF_ADDREF(m_pErrorLog);
+ m_pThreadData->successLog = m_pSuccessLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+
+ m_pThreadData->ownsDestRoot = m_deleteDestFolder;
+ m_pThreadData->destRoot = m_pDestFolder;
+ m_pThreadData->performingMigration = m_performingMigration;
+ NS_IF_ADDREF(m_pDestFolder);
+
+ NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle);
+
+ PRThread *pThread = PR_CreateThread(PR_USER_THREAD, &ImportMailThread, m_pThreadData,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!pThread) {
+ m_pThreadData->ThreadDelete();
+ m_pThreadData->abort = true;
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ *_retval = false;
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTHREAD,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ }
+ else
+ *_retval = true;
+
+ return NS_OK;
+
+}
+
+
+NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = true;
+ if (m_pThreadData) {
+ if (m_pThreadData->fatalError)
+ *_retval = false;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsImportGenericMail::GetProgress(int32_t *_retval)
+{
+ // This returns the progress from the the currently
+ // running import mail or import address book thread.
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!m_pThreadData || !(m_pThreadData->threadAlive)) {
+ *_retval = 100;
+ return NS_OK;
+ }
+
+ uint32_t sz = 0;
+ if (m_pThreadData->currentSize && m_pInterface) {
+ if (NS_FAILED(m_pInterface->GetImportProgress(&sz)))
+ sz = 0;
+ }
+
+
+ // *_retval = (int32_t) (((uint32_t)(m_pThreadData->currentTotal + sz) * (uint32_t)100) / m_totalSize);
+
+ if (m_totalSize) {
+ double perc;
+ perc = (double) m_pThreadData->currentTotal;
+ perc += sz;
+ perc *= 100;
+ perc /= m_totalSize;
+ *_retval = (int32_t) perc;
+ if (*_retval > 100)
+ *_retval = 100;
+ }
+ else
+ *_retval = 0;
+
+ // never return 100% while the thread is still alive
+ if (*_retval > 99)
+ *_retval = 99;
+
+ return NS_OK;
+}
+
+void nsImportGenericMail::ReportError(int32_t id, const char16_t *pName, nsString *pStream, nsIStringBundle *aBundle)
+{
+ if (!pStream)
+ return;
+
+ // load the error string
+ char16_t *pFmt = nsImportStringBundle::GetStringByID(id, aBundle);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, pName);
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->Append(NS_ConvertASCIItoUTF16(MSG_LINEBREAK));
+}
+
+
+void nsImportGenericMail::SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError)
+{
+ nsAutoString str;
+ if (pSuccess) {
+ pSuccess->GetData(str);
+ str.Append(success);
+ pSuccess->SetData(str);
+ }
+ if (pError) {
+ pError->GetData(str);
+ str.Append(error);
+ pError->SetData(str);
+ }
+}
+
+NS_IMETHODIMP nsImportGenericMail::CancelImport(void)
+{
+ if (m_pThreadData) {
+ m_pThreadData->abort = true;
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ return NS_OK;
+}
+
+
+ImportThreadData::ImportThreadData()
+{
+ fatalError = false;
+ driverAlive = true;
+ threadAlive = true;
+ abort = false;
+ currentTotal = 0;
+ currentSize = 0;
+ destRoot = nullptr;
+ ownsDestRoot = false;
+ boxes = nullptr;
+ mailImport = nullptr;
+ successLog = nullptr;
+ errorLog = nullptr;
+ stringBundle = nullptr;
+}
+
+ImportThreadData::~ImportThreadData()
+{
+ NS_IF_RELEASE(destRoot);
+ NS_IF_RELEASE(boxes);
+ NS_IF_RELEASE(mailImport);
+ NS_IF_RELEASE(errorLog);
+ NS_IF_RELEASE(successLog);
+ NS_IF_RELEASE(stringBundle);
+}
+
+void ImportThreadData::DriverDelete(void)
+{
+ driverAlive = false;
+ if (!driverAlive && !threadAlive)
+ delete this;
+}
+
+void ImportThreadData::ThreadDelete()
+{
+ threadAlive = false;
+ if (!driverAlive && !threadAlive)
+ delete this;
+}
+
+void ImportThreadData::DriverAbort()
+{
+ if (abort && !threadAlive && destRoot) {
+ if (ownsDestRoot) {
+ destRoot->RecursiveDelete(true, nullptr);
+ }
+ else {
+ // FIXME: just delete the stuff we created?
+ }
+ }
+ else
+ abort = true;
+ DriverDelete();
+}
+
+
+
+static void
+ImportMailThread(void *stuff)
+{
+ ImportThreadData *pData = (ImportThreadData *)stuff;
+
+ IMPORT_LOG0("ImportMailThread: Starting...");
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMsgFolder> destRoot(pData->destRoot);
+
+ uint32_t count = 0;
+ rv = pData->boxes->GetLength(&count);
+
+ uint32_t i;
+ bool import;
+ uint32_t size;
+ uint32_t depth = 1;
+ uint32_t newDepth;
+ nsString lastName;
+ char16_t * pName;
+
+ nsCOMPtr<nsIMsgFolder> curFolder(destRoot);
+
+ nsCOMPtr<nsIMsgFolder> newFolder;
+ nsCOMPtr<nsIMsgFolder> subFolder;
+
+ bool exists;
+
+ nsString success;
+ nsString error;
+
+ // GetSubFolders() will initialize folders if they are not already initialized.
+ ProxyGetSubFolders(curFolder);
+
+ IMPORT_LOG1("ImportMailThread: Total number of folders to import = %d.", count);
+
+ // Note that the front-end js script only displays one import result string so
+ // we combine both good and bad import status into one string (in var 'success').
+
+ for (i = 0; (i < count) && !(pData->abort); i++) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box =
+ do_QueryElementAt(pData->boxes, i);
+ if (box) {
+ pData->currentMailbox = i;
+
+ import = false;
+ size = 0;
+ rv = box->GetImport(&import);
+ if (import)
+ rv = box->GetSize(&size);
+ rv = box->GetDepth(&newDepth);
+ if (newDepth > depth) {
+ // OK, we are going to add a subfolder under the last/previous folder we processed, so
+ // find this folder (stored in 'lastName') who is going to be the new parent folder.
+ IMPORT_LOG1("ImportMailThread: Processing child folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get());
+ rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(subFolder));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** ImportMailThread: Failed to get the interface for child folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get());
+ nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD,
+ lastName.get(),
+ &error, pData->stringBundle);
+ pData->fatalError = true;
+ break;
+ }
+ curFolder = subFolder;
+ // Make sure this new parent folder obj has the correct subfolder list so far.
+ rv = ProxyGetSubFolders(curFolder);
+ }
+ else if (newDepth < depth) {
+ rv = NS_OK;
+ while ((newDepth < depth) && NS_SUCCEEDED(rv)) {
+ rv = curFolder->GetParent(getter_AddRefs(curFolder));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** ImportMailThread: Failed to get the interface for parent folder '%s'.", lastName.get());
+ nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD,
+ lastName.get(), &error,
+ pData->stringBundle);
+ pData->fatalError = true;
+ break;
+ }
+ depth--;
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** ImportMailThread: Failed to get the proxy interface for parent folder '%s'.", lastName.get());
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY,
+ pData->stringBundle, error);
+ pData->fatalError = true;
+ break;
+ }
+ }
+ depth = newDepth;
+ pName = nullptr;
+ box->GetDisplayName(&pName);
+ if (pName) {
+ lastName = pName;
+ NS_Free(pName);
+ }
+ else
+ lastName.AssignLiteral("Unknown!");
+
+ // translate the folder name if we are doing migration, but
+ // only for special folders which are at the root level
+ if (pData->performingMigration && depth == 1)
+ pData->mailImport->TranslateFolderName(lastName, lastName);
+
+ exists = false;
+ rv = ProxyContainsChildNamed(curFolder, lastName, &exists);
+
+ // If we are performing profile migration (as opposed to importing) then we are starting
+ // with empty local folders. In that case, always choose to over-write the existing local folder
+ // with this name. Don't create a unique subfolder name. Otherwise you end up with "Inbox, Inbox0"
+ // or "Unsent Folders, UnsentFolders0"
+ if (exists && !pData->performingMigration) {
+ nsString subName;
+ ProxyGenerateUniqueSubfolderName(curFolder, lastName, nullptr, subName);
+ if (!subName.IsEmpty())
+ lastName.Assign(subName);
+ }
+
+ IMPORT_LOG1("ImportMailThread: Creating new import folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get());
+ ProxyCreateSubfolder(curFolder, lastName); // this may fail if the folder already exists..that's ok
+
+ rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(newFolder));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** ImportMailThread: Failed to locate subfolder '%s' after it's been created.", lastName.get());
+ nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(),
+ &error, pData->stringBundle);
+ }
+
+ if (size && import && newFolder && NS_SUCCEEDED(rv)) {
+ bool fatalError = false;
+ pData->currentSize = size;
+ char16_t *pSuccess = nullptr;
+ char16_t *pError = nullptr;
+ rv = pData->mailImport->ImportMailbox(box, newFolder, &pError, &pSuccess, &fatalError);
+ if (pError) {
+ error.Append(pError);
+ NS_Free(pError);
+ }
+ if (pSuccess) {
+ success.Append(pSuccess);
+ NS_Free(pSuccess);
+ }
+
+ pData->currentSize = 0;
+ pData->currentTotal += size;
+
+ // commit to the db synchronously, but using a proxy since it doesn't like being used
+ // elsewhere than from the main thread.
+ // OK, we've copied the actual folder/file over if the folder size is not 0
+ // (ie, the msg summary is no longer valid) so close the msg database so that
+ // when the folder is reopened the folder db can be reconstructed (which
+ // validates msg summary and forces folder to be reparsed).
+ rv = ProxyForceDBClosed(newFolder);
+ fatalError = NS_FAILED(rv);
+
+ if (fatalError) {
+ IMPORT_LOG1("*** ImportMailThread: ImportMailbox returned fatalError, mailbox #%d\n", (int) i);
+ pData->fatalError = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Now save the new acct info to pref file.
+ nsCOMPtr <nsIMsgAccountManager> accMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && accMgr) {
+ rv = accMgr->SaveAccountInfo();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file");
+ }
+
+ nsImportGenericMail::SetLogs(success, error, pData->successLog, pData->errorLog);
+
+ if (pData->abort || pData->fatalError) {
+ IMPORT_LOG0("*** ImportMailThread: Abort or fatalError flag was set\n");
+ if (pData->ownsDestRoot) {
+ IMPORT_LOG0("Calling destRoot->RecursiveDelete\n");
+ destRoot->RecursiveDelete(true, nullptr);
+ }
+ else {
+ // FIXME: just delete the stuff we created?
+ }
+ }
+
+ IMPORT_LOG1("Import mailbox thread done: %d\n", (int) pData->currentTotal);
+
+ pData->ThreadDelete();
+
+}
+
+// Creates a folder in Local Folders with the module name + mail
+// for e.g: Outlook Mail
+bool nsImportGenericMail::CreateFolder(nsIMsgFolder **ppFolder)
+{
+ nsresult rv;
+ *ppFolder = nullptr;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return false;
+ rv = bundleService->CreateBundle(IMPORT_MSGS_URL, getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ return false;
+ nsString folderName;
+ if (!m_pName.IsEmpty()) {
+ const char16_t *moduleName[] = { m_pName.get() };
+ rv = bundle->FormatStringFromName(u"ImportModuleFolderName",
+ moduleName, 1,
+ getter_Copies(folderName));
+ }
+ else {
+ rv = bundle->GetStringFromName(u"DefaultFolderName",
+ getter_Copies(folderName));
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to get Folder Name!\n");
+ return false;
+ }
+ nsCOMPtr <nsIMsgAccountManager> accMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create account manager!\n");
+ return false;
+ }
+
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server));
+ // if Local Folders does not exist already, create it
+ if (NS_FAILED(rv) || !server)
+ {
+ rv = accMgr->CreateLocalMailAccount();
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create Local Folders!\n");
+ return false;
+ }
+
+ rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server));
+ }
+
+ if (NS_SUCCEEDED(rv) && server) {
+ nsCOMPtr <nsIMsgFolder> localRootFolder;
+ rv = server->GetRootMsgFolder(getter_AddRefs(localRootFolder));
+ if (localRootFolder) {
+ // we need to call GetSubFolders() so that the folders get initialized
+ // if they are not initialized yet.
+ nsCOMPtr<nsISimpleEnumerator> aEnumerator;
+ rv = localRootFolder->GetSubFolders(getter_AddRefs(aEnumerator));
+ if (NS_SUCCEEDED(rv)) {
+ // check if the folder name we picked already exists.
+ bool exists = false;
+ rv = localRootFolder->ContainsChildNamed(folderName, &exists);
+ if (exists) {
+ nsString name;
+ localRootFolder->GenerateUniqueSubfolderName(folderName, nullptr, name);
+ if (!name.IsEmpty())
+ folderName.Assign(name);
+ else {
+ IMPORT_LOG0("*** Failed to find a unique folder name!\n");
+ return false;
+ }
+ }
+ IMPORT_LOG1("Creating folder for importing mail: '%s'\n", NS_ConvertUTF16toUTF8(folderName).get());
+
+ // Bug 564162 identifies a dataloss design flaw.
+ // A working Thunderbird client can have mail in Local Folders and a
+ // subsequent import 'Everything' will trigger a migration which
+ // overwrites existing mailboxes with the imported mailboxes.
+ rv = localRootFolder->CreateSubfolder(folderName, nullptr);
+ if (NS_SUCCEEDED(rv)) {
+ rv = localRootFolder->GetChildNamed(folderName, ppFolder);
+ if (*ppFolder) {
+ IMPORT_LOG1("Folder '%s' created successfully\n", NS_ConvertUTF16toUTF8(folderName).get());
+ return true;
+ }
+ }
+ }
+ } // if localRootFolder
+ } // if server
+ IMPORT_LOG0("****** FAILED TO CREATE FOLDER FOR IMPORT\n");
+ return false;
+}
+
+/**
+ * These are the proxy objects we use to proxy nsIMsgFolder methods back
+ * the the main thread. Since there are only five, we can hand roll them.
+ * A better design might be a co-routine-ish design where the ui thread
+ * hands off each folder to the import thread and when the thread finishes
+ * the folder, the main thread hands it the next folder.
+ */
+
+class GetSubFoldersRunnable : public mozilla::Runnable
+{
+public:
+ explicit GetSubFoldersRunnable(nsIMsgFolder *aFolder);
+ NS_DECL_NSIRUNNABLE
+private:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder *aFolder) :
+ m_folder(aFolder)
+{
+}
+
+NS_IMETHODIMP GetSubFoldersRunnable::Run()
+{
+ nsCOMPtr<nsISimpleEnumerator> dummy;
+ return m_folder->GetSubFolders(getter_AddRefs(dummy));
+}
+
+
+nsresult ProxyGetSubFolders(nsIMsgFolder *aFolder)
+{
+ RefPtr<GetSubFoldersRunnable> getSubFolders =
+ new GetSubFoldersRunnable(aFolder);
+ return NS_DispatchToMainThread(getSubFolders, NS_DISPATCH_SYNC);
+}
+
+class GetChildNamedRunnable : public mozilla::Runnable
+{
+public:
+ GetChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, nsIMsgFolder **aChild);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ nsIMsgFolder **m_child;
+};
+
+GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder *aFolder,
+ const nsAString & aName,
+ nsIMsgFolder **aChild) :
+ m_folder(aFolder), m_name(aName), m_child(aChild)
+{
+}
+
+NS_IMETHODIMP GetChildNamedRunnable::Run()
+{
+ return m_folder->GetChildNamed(m_name, m_child);
+}
+
+
+nsresult ProxyGetChildNamed(nsIMsgFolder *aFolder, const nsAString & aName,
+ nsIMsgFolder **aChild)
+{
+ RefPtr<GetChildNamedRunnable> getChildNamed =
+ new GetChildNamedRunnable(aFolder, aName, aChild);
+ return NS_DispatchToMainThread(getChildNamed, NS_DISPATCH_SYNC);
+}
+
+class GetParentRunnable : public mozilla::Runnable
+{
+public:
+ GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsIMsgFolder **m_parent;
+};
+
+GetParentRunnable::GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent) :
+ m_folder(aFolder), m_parent(aParent)
+{
+}
+
+NS_IMETHODIMP GetParentRunnable::Run()
+{
+ return m_folder->GetParent(m_parent);
+}
+
+
+nsresult ProxyGetParent(nsIMsgFolder *aFolder, nsIMsgFolder **aParent)
+{
+ RefPtr<GetParentRunnable> getParent =
+ new GetParentRunnable(aFolder, aParent);
+ return NS_DispatchToMainThread(getParent, NS_DISPATCH_SYNC);
+}
+
+class ContainsChildNamedRunnable : public mozilla::Runnable
+{
+public:
+ ContainsChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, bool *aResult);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ bool *m_result;
+};
+
+ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder *aFolder,
+ const nsAString &aName,
+ bool *aResult) :
+ m_folder(aFolder), m_name(aName), m_result(aResult)
+{
+}
+
+NS_IMETHODIMP ContainsChildNamedRunnable::Run()
+{
+ return m_folder->ContainsChildNamed(m_name, m_result);
+}
+
+
+nsresult ProxyContainsChildNamed(nsIMsgFolder *aFolder, const nsAString &aName,
+ bool *aResult)
+{
+ RefPtr<ContainsChildNamedRunnable> containsChildNamed =
+ new ContainsChildNamedRunnable(aFolder, aName, aResult);
+ return NS_DispatchToMainThread(containsChildNamed, NS_DISPATCH_SYNC);
+}
+
+
+class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable
+{
+public:
+ GenerateUniqueSubfolderNameRunnable(nsIMsgFolder *aFolder,
+ const nsAString& prefix,
+ nsIMsgFolder *otherFolder,
+ nsAString& name);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_prefix;
+ nsCOMPtr<nsIMsgFolder> m_otherFolder;
+ nsString m_name;
+};
+
+GenerateUniqueSubfolderNameRunnable::GenerateUniqueSubfolderNameRunnable(
+ nsIMsgFolder *aFolder, const nsAString& aPrefix, nsIMsgFolder *aOtherFolder,
+ nsAString& aName)
+ : m_folder(aFolder), m_prefix(aPrefix), m_otherFolder(aOtherFolder), m_name(aName)
+{
+}
+
+NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run()
+{
+ return m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name);
+}
+
+
+nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder *aFolder,
+ const nsAString& aPrefix,
+ nsIMsgFolder *aOtherFolder,
+ nsAString& aName)
+
+{
+ RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName =
+ new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder, aName);
+ return NS_DispatchToMainThread(generateUniqueSubfolderName, NS_DISPATCH_SYNC);
+}
+
+class CreateSubfolderRunnable : public mozilla::Runnable
+{
+public:
+ CreateSubfolderRunnable(nsIMsgFolder *aFolder, const nsAString& aName);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+};
+
+CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder *aFolder,
+ const nsAString &aName) :
+ m_folder(aFolder), m_name(aName)
+{
+}
+
+NS_IMETHODIMP CreateSubfolderRunnable::Run()
+{
+ return m_folder->CreateSubfolder(m_name, nullptr);
+}
+
+
+nsresult ProxyCreateSubfolder(nsIMsgFolder *aFolder, const nsAString &aName)
+{
+ RefPtr<CreateSubfolderRunnable> createSubfolder =
+ new CreateSubfolderRunnable(aFolder, aName);
+ return NS_DispatchToMainThread(createSubfolder, NS_DISPATCH_SYNC);
+}
+
+class ForceDBClosedRunnable : public mozilla::Runnable
+{
+public:
+ explicit ForceDBClosedRunnable(nsIMsgFolder *aFolder);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder *aFolder) :
+ m_folder(aFolder)
+{
+}
+
+NS_IMETHODIMP ForceDBClosedRunnable::Run()
+{
+ return m_folder->ForceDBClosed();
+}
+
+nsresult ProxyForceDBClosed(nsIMsgFolder *aFolder)
+{
+ RefPtr<ForceDBClosedRunnable> forceDBClosed =
+ new ForceDBClosedRunnable(aFolder);
+ return NS_DispatchToMainThread(forceDBClosed, NS_DISPATCH_SYNC);
+}
+
+
diff --git a/mailnews/import/src/nsImportMailboxDescriptor.cpp b/mailnews/import/src/nsImportMailboxDescriptor.cpp
new file mode 100644
index 000000000..ab0ea5db4
--- /dev/null
+++ b/mailnews/import/src/nsImportMailboxDescriptor.cpp
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+
+
+#include "nscore.h"
+#include "nsImportMailboxDescriptor.h"
+#include "nsComponentManagerUtils.h"
+
+////////////////////////////////////////////////////////////////////////
+
+
+
+NS_METHOD nsImportMailboxDescriptor::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsImportMailboxDescriptor *it = new nsImportMailboxDescriptor();
+ if (it == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(it);
+ nsresult rv = it->QueryInterface(aIID, aResult);
+ NS_RELEASE(it);
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsImportMailboxDescriptor, nsIImportMailboxDescriptor)
+
+nsImportMailboxDescriptor::nsImportMailboxDescriptor()
+{
+ m_import = true;
+ m_size = 0;
+ m_depth = 0;
+ m_id = 0;
+ m_pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+}
diff --git a/mailnews/import/src/nsImportMailboxDescriptor.h b/mailnews/import/src/nsImportMailboxDescriptor.h
new file mode 100644
index 000000000..1f4c30b31
--- /dev/null
+++ b/mailnews/import/src/nsImportMailboxDescriptor.h
@@ -0,0 +1,63 @@
+/* -*- 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/. */
+
+#ifndef nsImportMailboxDescriptor_h___
+#define nsImportMailboxDescriptor_h___
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////
+
+
+class nsImportMailboxDescriptor : public nsIImportMailboxDescriptor
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD GetIdentifier(uint32_t *pIdentifier) override { *pIdentifier = m_id; return NS_OK;}
+ NS_IMETHOD SetIdentifier(uint32_t ident) override { m_id = ident; return NS_OK;}
+
+ /* attribute unsigned long depth; */
+ NS_IMETHOD GetDepth(uint32_t *pDepth) override { *pDepth = m_depth; return NS_OK;}
+ NS_IMETHOD SetDepth(uint32_t theDepth) override { m_depth = theDepth; return NS_OK;}
+
+ /* attribute unsigned long size; */
+ NS_IMETHOD GetSize(uint32_t *pSize) override { *pSize = m_size; return NS_OK;}
+ NS_IMETHOD SetSize(uint32_t theSize) override { m_size = theSize; return NS_OK;}
+
+ /* attribute wstring displayName; */
+ NS_IMETHOD GetDisplayName(char16_t **pName) override { *pName = ToNewUnicode(m_displayName); return NS_OK;}
+ NS_IMETHOD SetDisplayName(const char16_t * pName) override { m_displayName = pName; return NS_OK;}
+
+ /* attribute boolean import; */
+ NS_IMETHOD GetImport(bool *pImport) override { *pImport = m_import; return NS_OK;}
+ NS_IMETHOD SetImport(bool doImport) override { m_import = doImport; return NS_OK;}
+
+ /* readonly attribute nsIFile file; */
+ NS_IMETHOD GetFile(nsIFile * *aFile) override { if (m_pFile) { NS_ADDREF(*aFile = m_pFile); return NS_OK;} else return NS_ERROR_FAILURE; }
+
+
+
+ nsImportMailboxDescriptor();
+
+ static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ virtual ~nsImportMailboxDescriptor() {}
+ uint32_t m_id; // used by creator of the structure
+ uint32_t m_depth; // depth in the hierarchy
+ nsString m_displayName;// name of this mailbox
+ nsCOMPtr <nsIFile> m_pFile; // source file (if applicable)
+ uint32_t m_size;
+ bool m_import; // import it or not?
+};
+
+
+#endif
diff --git a/mailnews/import/src/nsImportMimeEncode.cpp b/mailnews/import/src/nsImportMimeEncode.cpp
new file mode 100644
index 000000000..e13ea1f94
--- /dev/null
+++ b/mailnews/import/src/nsImportMimeEncode.cpp
@@ -0,0 +1,411 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "nsImportMimeEncode.h"
+
+#include "ImportCharSet.h"
+#include "ImportTranslate.h"
+
+#define kNoState 0
+#define kStartState 1
+#define kEncodeState 2
+#define kDoneState 3
+
+#define kEncodeBufferSz (8192 * 8)
+
+nsImportMimeEncode::nsImportMimeEncode()
+{
+ m_pOut = nullptr;
+ m_state = kNoState;
+ m_bytesProcessed = 0;
+ m_pInputBuf = nullptr;
+}
+
+nsImportMimeEncode::~nsImportMimeEncode()
+{
+ delete [] m_pInputBuf;
+}
+
+void nsImportMimeEncode::EncodeFile(nsIFile *pInFile, ImportOutFile *pOut, const char *pFileName, const char *pMimeType)
+{
+ m_fileName = pFileName;
+ m_mimeType = pMimeType;
+
+ m_pMimeFile = pInFile;
+
+ m_pOut = pOut;
+ m_state = kStartState;
+}
+
+void nsImportMimeEncode::CleanUp(void)
+{
+ CleanUpEncodeScan();
+}
+
+bool nsImportMimeEncode::SetUpEncode(void)
+{
+ nsCString errStr;
+ if (!m_pInputBuf) {
+ m_pInputBuf = new uint8_t[kEncodeBufferSz];
+ }
+
+ m_appleSingle = false;
+
+#ifdef _MAC_IMPORT_CODE
+ // First let's see just what kind of beast we have?
+ // For files with only a data fork and a known mime type
+ // proceed with normal mime encoding just as on the PC.
+ // For unknown mime types and files with both forks,
+ // encode as AppleSingle format.
+ if (m_filePath.GetMacFileSize(UFileLocation::eResourceFork) || !pMimeType) {
+ m_appleSingle = TRUE;
+ m_mimeType = "application/applefile";
+ }
+#endif
+
+ if (!InitEncodeScan(m_appleSingle, m_pMimeFile, m_fileName.get(), m_pInputBuf, kEncodeBufferSz)) {
+ return false;
+ }
+
+ m_state = kEncodeState;
+ m_lineLen = 0;
+
+ // Write out the boundary header
+ bool bResult = true;
+ bResult = m_pOut->WriteStr("Content-type: ");
+ if (bResult)
+ bResult = m_pOut->WriteStr(m_mimeType.get());
+
+#ifdef _MAC_IMPORT_CODE
+ // include the type an creator here
+ if (bResult)
+ bResult = m_pOut->WriteStr("; x-mac-type=\"");
+ U8 hex[8];
+ LongToHexBytes(m_filePath.GetFileType(), hex);
+ if (bResult)
+ bResult = m_pOut->WriteData(hex, 8);
+ LongToHexBytes(m_filePath.GetFileCreator(), hex);
+ if (bResult)
+ bResult = m_pOut->WriteStr("\"; x-mac-creator=\"");
+ if (bResult)
+ bResult = m_pOut->WriteData(hex, 8);
+ if (bResult)
+ bResult = m_pOut->WriteStr("\"");
+#endif
+
+ /*
+ if (bResult)
+ bResult = m_pOut->WriteStr(gMimeTypeFileName);
+ */
+ if (bResult)
+ bResult = m_pOut->WriteStr(";\x0D\x0A");
+
+ nsCString fName;
+ bool trans = TranslateFileName(m_fileName, fName);
+ if (bResult)
+ bResult = WriteFileName(fName, trans, "name");
+ if (bResult)
+ bResult = m_pOut->WriteStr("Content-transfer-encoding: base64");
+ if (bResult)
+ bResult = m_pOut->WriteEol();
+ if (bResult)
+ bResult = m_pOut->WriteStr("Content-Disposition: attachment;\x0D\x0A");
+ if (bResult)
+ bResult = WriteFileName(fName, trans, "filename");
+ if (bResult)
+ bResult = m_pOut->WriteEol();
+
+ if (!bResult) {
+ CleanUp();
+ }
+
+ return bResult;
+}
+
+bool nsImportMimeEncode::DoWork(bool *pDone)
+{
+ *pDone = false;
+ switch(m_state) {
+ case kNoState:
+ return false;
+ break;
+ case kStartState:
+ return SetUpEncode();
+ break;
+ case kEncodeState:
+ if (!Scan(pDone)) {
+ CleanUp();
+ return false;
+ }
+ if (*pDone) {
+ *pDone = false;
+ m_state = kDoneState;
+ }
+ break;
+ case kDoneState:
+ CleanUp();
+ m_state = kNoState;
+ *pDone = true;
+ break;
+ }
+
+ return true;
+}
+
+static uint8_t gBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+bool nsImportMimeEncode::ScanBuffer(bool *pDone)
+{
+
+ uint32_t pos = m_pos;
+ uint32_t start = pos;
+ uint8_t * pChar = m_pBuf + pos;
+ uint32_t max = m_bytesInBuf;
+ uint8_t byte[4];
+ uint32_t lineLen = m_lineLen;
+
+ while ((pos + 2) < max) {
+ // Encode 3 bytes
+ byte[0] = gBase64[*pChar >> 2];
+ byte[1] = gBase64[(((*pChar) & 0x3)<< 4) | (((*(pChar + 1)) & 0xF0) >> 4)];
+ pChar++;
+ byte[2] = gBase64[(((*pChar) & 0xF) << 2) | (((*(pChar + 1)) & 0xC0) >>6)];
+ pChar++;
+ byte[3] = gBase64[(*pChar) & 0x3F];
+ if (!m_pOut->WriteData(byte, 4))
+ return false;
+ pos += 3;
+ pChar++;
+ lineLen += 4;
+ if (lineLen > 71) {
+ if (!m_pOut->WriteEol())
+ return false;
+ lineLen = 0;
+ }
+ }
+
+ if ((pos < max) && m_eof) {
+ // Get the last few bytes!
+ byte[0] = gBase64[*pChar >> 2];
+ pos++;
+ if (pos < max) {
+ byte[1] = gBase64[(((*pChar) & 0x3)<< 4) | (((*(pChar + 1)) & 0xF0) >> 4)];
+ pChar++;
+ pos++;
+ if (pos < max) {
+ // Should be dead code!! (Then why is it here doofus?)
+ byte[2] = gBase64[(((*pChar) & 0xF) << 2) | (((*(pChar + 1)) & 0xC0) >>6)];
+ pChar++;
+ byte[3] = gBase64[(*pChar) & 0x3F];
+ pos++;
+ }
+ else {
+ byte[2] = gBase64[(((*pChar) & 0xF) << 2)];
+ byte[3] = '=';
+ }
+ }
+ else {
+ byte[1] = gBase64[(((*pChar) & 0x3)<< 4)];
+ byte[2] = '=';
+ byte[3] = '=';
+ }
+
+ if (!m_pOut->WriteData(byte, 4))
+ return false;
+ if (!m_pOut->WriteEol())
+ return false;
+ }
+ else if (m_eof) {
+ /*
+ byte[0] = '=';
+ if (!m_pOut->WriteData(byte, 1))
+ return FALSE;
+ */
+ if (!m_pOut->WriteEol())
+ return false;
+ }
+
+ m_lineLen = (int) lineLen;
+ m_pos = pos;
+ m_bytesProcessed += (pos - start);
+ return true;
+}
+
+bool nsImportMimeEncode::TranslateFileName(nsCString& inFile, nsCString& outFile)
+{
+ const uint8_t * pIn = (const uint8_t *) inFile.get();
+ int len = inFile.Length();
+
+ while (len) {
+ if (!ImportCharSet::IsUSAscii(*pIn))
+ break;
+ len--;
+ pIn++;
+ }
+ if (len) {
+ // non US ascii!
+ // assume this string needs translating...
+ if (!ImportTranslate::ConvertString(inFile, outFile, true)) {
+ outFile = inFile;
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+ else {
+ outFile = inFile;
+ return false;
+ }
+}
+
+bool nsImportMimeEncode::WriteFileName(nsCString& fName, bool wasTrans, const char *pTag)
+{
+ int tagNum = 0;
+ int idx = 0;
+ bool result = true;
+ int len;
+ nsCString numStr;
+
+ while ((((fName.Length() - idx) + strlen(pTag)) > 70) && result) {
+ len = 68 - strlen(pTag) - 5;
+ if (wasTrans) {
+ if (fName.CharAt(idx + len - 1) == '%')
+ len--;
+ else if (fName.CharAt(idx + len - 2) == '%')
+ len -= 2;
+ }
+
+ if (result)
+ result = m_pOut->WriteStr("\x09");
+ if (result)
+ result = m_pOut->WriteStr(pTag);
+ numStr = "*";
+ numStr.AppendInt(tagNum);
+ if (result)
+ result = m_pOut->WriteStr(numStr.get());
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("*=");
+ else if (result)
+ result = m_pOut->WriteStr("=\"");
+ if (result)
+ result = m_pOut->WriteData(((const uint8_t *)fName.get()) + idx, len);
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("\x0D\x0A");
+ else if (result)
+ result = m_pOut->WriteStr("\"\x0D\x0A");
+ idx += len;
+ tagNum++;
+ }
+
+ if (idx) {
+ if ((fName.Length() - idx) > 0) {
+ if (result)
+ result = m_pOut->WriteStr("\x09");
+ if (result)
+ result = m_pOut->WriteStr(pTag);
+ numStr = "*";
+ numStr.AppendInt(tagNum);
+ if (result)
+ result = m_pOut->WriteStr(numStr.get());
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("*=");
+ else if (result)
+ result = m_pOut->WriteStr("=\"");
+ if (result)
+ result = m_pOut->WriteData(((const uint8_t *)fName.get()) + idx, fName.Length() - idx);
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("\x0D\x0A");
+ else if (result)
+ result = m_pOut->WriteStr("\"\x0D\x0A");
+ }
+ }
+ else {
+ if (result)
+ result = m_pOut->WriteStr("\x09");
+ if (result)
+ result = m_pOut->WriteStr(pTag);
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("*=");
+ else if (result)
+ result = m_pOut->WriteStr("=\"");
+ if (result)
+ result = m_pOut->WriteStr(fName.get());
+ if (wasTrans && result)
+ result = m_pOut->WriteStr("\x0D\x0A");
+ else if (result)
+ result = m_pOut->WriteStr("\"\x0D\x0A");
+ }
+
+ return result;
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+nsIImportMimeEncodeImpl::nsIImportMimeEncodeImpl()
+{
+ m_pOut = nullptr;
+ m_pEncode = nullptr;
+}
+
+nsIImportMimeEncodeImpl::~nsIImportMimeEncodeImpl()
+{
+ if (m_pOut)
+ delete m_pOut;
+ if (m_pEncode)
+ delete m_pEncode;
+}
+
+NS_IMPL_ISUPPORTS(nsIImportMimeEncodeImpl, nsIImportMimeEncode)
+
+NS_METHOD nsIImportMimeEncodeImpl::EncodeFile(nsIFile *inFile, nsIFile *outFile, const char *fileName, const char *mimeType)
+{
+ return Initialize(inFile, outFile, fileName, mimeType);
+}
+
+NS_METHOD nsIImportMimeEncodeImpl::DoWork(bool *done, bool *_retval)
+{
+ if (done && _retval && m_pEncode) {
+ *_retval = m_pEncode->DoWork(done);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_METHOD nsIImportMimeEncodeImpl::NumBytesProcessed(int32_t *_retval)
+{
+ if (m_pEncode && _retval)
+ *_retval = m_pEncode->NumBytesProcessed();
+ return NS_OK;
+}
+
+NS_METHOD nsIImportMimeEncodeImpl::DoEncoding(bool *_retval)
+{
+ if (_retval && m_pEncode) {
+ bool done = false;
+ while (m_pEncode->DoWork(&done) && !done);
+ *_retval = done;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_METHOD nsIImportMimeEncodeImpl::Initialize(nsIFile *inFile, nsIFile *outFile, const char *fileName, const char *mimeType)
+{
+ delete m_pEncode;
+ delete m_pOut;
+
+ m_pOut = new ImportOutFile();
+ m_pOut->InitOutFile(outFile);
+
+ m_pEncode = new nsImportMimeEncode();
+ m_pEncode->EncodeFile(inFile, m_pOut, fileName, mimeType);
+
+ return NS_OK;
+}
+
diff --git a/mailnews/import/src/nsImportMimeEncode.h b/mailnews/import/src/nsImportMimeEncode.h
new file mode 100644
index 000000000..1447d11c4
--- /dev/null
+++ b/mailnews/import/src/nsImportMimeEncode.h
@@ -0,0 +1,73 @@
+/* -*- 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/. */
+#ifndef nsImportMimeEncode_h__
+#define nsImportMimeEncode_h__
+
+#include "mozilla/Attributes.h"
+#include "nsImportScanFile.h"
+#include "ImportOutFile.h"
+#include "nsImportEncodeScan.h"
+#include "nsStringGlue.h"
+#include "nsIImportMimeEncode.h"
+
+
+// Content-Type: image/gif; name="blah.xyz"
+// Content-Transfer-Encoding: base64
+// Content-Disposition: attachment; filename="blah.xyz"
+
+class nsImportMimeEncode : public nsImportEncodeScan {
+public:
+ nsImportMimeEncode();
+ ~nsImportMimeEncode();
+
+ void EncodeFile(nsIFile *pInFile, ImportOutFile *pOut, const char *pFileName, const char *pMimeType);
+
+ bool DoWork(bool *pDone);
+
+ long NumBytesProcessed(void) { long val = m_bytesProcessed; m_bytesProcessed = 0; return val;}
+
+protected:
+ void CleanUp(void);
+ bool SetUpEncode(void);
+ bool WriteFileName(nsCString& fName, bool wasTrans, const char *pTag);
+ bool TranslateFileName(nsCString& inFile, nsCString& outFile);
+
+
+ virtual bool ScanBuffer(bool *pDone) override;
+
+
+protected:
+ nsCString m_fileName;
+ nsCOMPtr <nsIFile> m_pMimeFile;
+ ImportOutFile * m_pOut;
+ nsCString m_mimeType;
+
+ int m_state;
+ long m_bytesProcessed;
+ uint8_t * m_pInputBuf;
+ bool m_appleSingle;
+
+ // Actual encoding variables
+ int m_lineLen;
+};
+
+
+class nsIImportMimeEncodeImpl : public nsIImportMimeEncode {
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIIMPORTMIMEENCODE
+
+ nsIImportMimeEncodeImpl();
+
+private:
+ virtual ~nsIImportMimeEncodeImpl();
+ ImportOutFile * m_pOut;
+ nsImportMimeEncode * m_pEncode;
+};
+
+
+#endif /* nsImportMimeEncode_h__ */
+
diff --git a/mailnews/import/src/nsImportScanFile.cpp b/mailnews/import/src/nsImportScanFile.cpp
new file mode 100644
index 000000000..c4eefef3b
--- /dev/null
+++ b/mailnews/import/src/nsImportScanFile.cpp
@@ -0,0 +1,172 @@
+/* -*- 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/. */
+
+#include "nscore.h"
+#include "nsIFile.h"
+#include "nsImportScanFile.h"
+#include "ImportCharSet.h"
+
+nsImportScanFile::nsImportScanFile()
+{
+ m_allocated = false;
+ m_eof = false;
+ m_pBuf = nullptr;
+}
+
+nsImportScanFile::~nsImportScanFile()
+{
+ if (m_allocated)
+ CleanUpScan();
+}
+
+void nsImportScanFile::InitScan(nsIInputStream *pInputStream, uint8_t * pBuf, uint32_t sz)
+{
+ m_pInputStream = pInputStream;
+ m_pBuf = pBuf;
+ m_bufSz = sz;
+ m_bytesInBuf = 0;
+ m_pos = 0;
+}
+
+void nsImportScanFile::CleanUpScan(void)
+{
+ m_pInputStream = nullptr;
+ if (m_allocated) {
+ delete [] m_pBuf;
+ m_pBuf = NULL;
+ }
+}
+
+void nsImportScanFile::ShiftBuffer(void)
+{
+ uint8_t * pTop;
+ uint8_t * pCurrent;
+
+ if (m_pos < m_bytesInBuf) {
+ pTop = m_pBuf;
+ pCurrent = pTop + m_pos;
+ uint32_t cnt = m_bytesInBuf - m_pos;
+ while (cnt) {
+ *pTop = *pCurrent;
+ pTop++; pCurrent++;
+ cnt--;
+ }
+ }
+
+ m_bytesInBuf -= m_pos;
+ m_pos = 0;
+}
+
+bool nsImportScanFile::FillBufferFromFile(void)
+{
+ uint64_t available;
+ nsresult rv = m_pInputStream->Available(&available);
+ if (NS_FAILED(rv))
+ return false;
+
+ // Fill up a buffer and scan it
+ ShiftBuffer();
+
+ // Read in some more bytes
+ uint32_t cnt = m_bufSz - m_bytesInBuf;
+ // To distinguish from disk errors
+ // Check first for end of file?
+ // Set a done flag if true...
+ uint32_t read;
+ char *pBuf = (char *)m_pBuf;
+ pBuf += m_bytesInBuf;
+ rv = m_pInputStream->Read(pBuf, (int32_t) cnt, &read);
+
+ if (NS_FAILED(rv))
+ return false;
+ rv = m_pInputStream->Available(&available);
+ if (NS_FAILED(rv))
+ m_eof = true;
+
+ m_bytesInBuf += cnt;
+ return true;
+}
+
+bool nsImportScanFile::Scan(bool *pDone)
+{
+ uint64_t available;
+ nsresult rv = m_pInputStream->Available(&available);
+ if (NS_FAILED(rv))
+ {
+ if (m_pos < m_bytesInBuf)
+ ScanBuffer(pDone);
+ *pDone = true;
+ return true;
+ }
+
+ // Fill up a buffer and scan it
+ if (!FillBufferFromFile())
+ return false;
+
+ return ScanBuffer(pDone);
+}
+
+bool nsImportScanFile::ScanBuffer(bool *)
+{
+ return true;
+}
+
+
+bool nsImportScanFileLines::ScanBuffer(bool *pDone)
+{
+ // m_pos, m_bytesInBuf, m_eof, m_pBuf are relevant
+
+ uint32_t pos = m_pos;
+ uint32_t max = m_bytesInBuf;
+ uint8_t * pChar = m_pBuf + pos;
+ uint32_t startPos;
+
+ while (pos < max) {
+ if (m_needEol) {
+ // Find the next eol...
+ while ((pos < max) && (*pChar != ImportCharSet::cCRChar) && (*pChar != ImportCharSet::cLinefeedChar)) {
+ pos++;
+ pChar++;
+ }
+ m_pos = pos;
+ if (pos < max)
+ m_needEol = false;
+ if (pos == max) // need more buffer for an end of line
+ break;
+ }
+ // Skip past any eol characters
+ while ((pos < max) && ((*pChar == ImportCharSet::cCRChar) || (*pChar == ImportCharSet::cLinefeedChar))) {
+ pos++;
+ pChar++;
+ }
+ m_pos = pos;
+ if (pos == max)
+ break;
+ // Make sure we can find either the eof or the
+ // next end of line
+ startPos = pos;
+ while ((pos < max) && (*pChar != ImportCharSet::cCRChar) && (*pChar != ImportCharSet::cLinefeedChar)) {
+ pos++;
+ pChar++;
+ }
+
+ // Is line too big for our buffer?
+ if ((pos == max) && !m_eof) {
+ if (!m_pos) { // line too big for our buffer
+ m_pos = pos;
+ m_needEol = true;
+ }
+ break;
+ }
+
+ if (!ProcessLine(m_pBuf + startPos, pos - startPos, pDone)) {
+ return false;
+ }
+ m_pos = pos;
+ }
+
+ return true;
+}
+
diff --git a/mailnews/import/src/nsImportScanFile.h b/mailnews/import/src/nsImportScanFile.h
new file mode 100644
index 000000000..abe5b1cdd
--- /dev/null
+++ b/mailnews/import/src/nsImportScanFile.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+#ifndef nsImportScanFile_h__
+#define nsImportScanFile_h__
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
+#include "nsIInputStream.h"
+
+class nsImportScanFile {
+public:
+ nsImportScanFile();
+ virtual ~nsImportScanFile();
+
+ void InitScan(nsIInputStream *pInputStream, uint8_t * pBuf, uint32_t sz);
+
+ void CleanUpScan(void);
+
+ virtual bool Scan(bool *pDone);
+
+protected:
+ void ShiftBuffer(void);
+ bool FillBufferFromFile(void);
+ virtual bool ScanBuffer(bool *pDone);
+
+protected:
+ nsCOMPtr <nsIInputStream> m_pInputStream;
+ uint8_t * m_pBuf;
+ uint32_t m_bufSz;
+ uint32_t m_bytesInBuf;
+ uint32_t m_pos;
+ bool m_eof;
+ bool m_allocated;
+};
+
+class nsImportScanFileLines : public nsImportScanFile {
+public:
+ nsImportScanFileLines() {m_needEol = false;}
+
+ void ResetLineScan(void) { m_needEol = false;}
+
+ virtual bool ProcessLine(uint8_t * /* pLine */, uint32_t /* len */, bool * /* pDone */) {return true;}
+
+protected:
+ virtual bool ScanBuffer(bool *pDone) override;
+
+ bool m_needEol;
+
+};
+
+
+#endif /* nsImportScanFile_h__ */
diff --git a/mailnews/import/src/nsImportService.cpp b/mailnews/import/src/nsImportService.cpp
new file mode 100644
index 000000000..0013c1146
--- /dev/null
+++ b/mailnews/import/src/nsImportService.cpp
@@ -0,0 +1,583 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsICharsetConverterManager.h"
+#include "nsIPlatformCharset.h"
+#include "nsICharsetConverterManager.h"
+
+#include "nsStringGlue.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsMemory.h"
+#include "nsIImportModule.h"
+#include "nsIImportService.h"
+#include "nsImportMailboxDescriptor.h"
+#include "nsImportABDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsImportFieldMap.h"
+#include "nsICategoryManager.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "nsMsgCompCID.h"
+#include "nsThreadUtils.h"
+#include "nsIEditor.h"
+#include "ImportDebug.h"
+#include "nsImportService.h"
+#include "nsImportStringBundle.h"
+#include "nsCRTGlue.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsIArray.h"
+#include "nsIMsgSend.h"
+#include "nsMsgUtils.h"
+
+PRLogModuleInfo *IMPORTLOGMODULE = nullptr;
+
+static nsIImportService * gImportService = nullptr;
+static const char * kWhitespace = "\b\t\r\n ";
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+nsImportService::nsImportService() : m_pModules(nullptr)
+{
+ // Init logging module.
+ if (!IMPORTLOGMODULE)
+ IMPORTLOGMODULE = PR_NewLogModule("IMPORT");
+ IMPORT_LOG0("* nsImport Service Created\n");
+
+ m_didDiscovery = false;
+ m_pDecoder = nullptr;
+ m_pEncoder = nullptr;
+
+ nsresult rv = nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
+ if (NS_FAILED(rv))
+ IMPORT_LOG0("Failed to get string bundle for Importing Mail");
+}
+
+
+nsImportService::~nsImportService()
+{
+ NS_IF_RELEASE(m_pDecoder);
+ NS_IF_RELEASE(m_pEncoder);
+
+ gImportService = nullptr;
+
+ if (m_pModules != nullptr)
+ delete m_pModules;
+
+ IMPORT_LOG0("* nsImport Service Deleted\n");
+}
+
+
+
+NS_IMPL_ISUPPORTS(nsImportService, nsIImportService)
+
+
+NS_IMETHODIMP nsImportService::DiscoverModules(void)
+{
+ m_didDiscovery = false;
+ return DoDiscover();
+}
+
+NS_IMETHODIMP nsImportService::CreateNewFieldMap(nsIImportFieldMap **_retval)
+{
+ return nsImportFieldMap::Create(m_stringBundle, nullptr, NS_GET_IID(nsIImportFieldMap), (void**)_retval);
+}
+
+NS_IMETHODIMP nsImportService::CreateNewMailboxDescriptor(nsIImportMailboxDescriptor **_retval)
+{
+ return nsImportMailboxDescriptor::Create(nullptr, NS_GET_IID(nsIImportMailboxDescriptor), (void**)_retval);
+}
+
+NS_IMETHODIMP nsImportService::CreateNewABDescriptor(nsIImportABDescriptor **_retval)
+{
+ return nsImportABDescriptor::Create(nullptr, NS_GET_IID(nsIImportABDescriptor), (void**)_retval);
+}
+
+extern nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric);
+
+NS_IMETHODIMP nsImportService::CreateNewGenericMail(nsIImportGeneric **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ return NS_NewGenericMail(_retval);
+}
+
+extern nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric);
+
+NS_IMETHODIMP nsImportService::CreateNewGenericAddressBooks(nsIImportGeneric **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ return NS_NewGenericAddressBooks(_retval);
+}
+
+
+NS_IMETHODIMP nsImportService::GetModuleCount(const char *filter, int32_t *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! _retval)
+ return NS_ERROR_NULL_POINTER;
+
+ DoDiscover();
+
+ if (m_pModules != nullptr) {
+ ImportModuleDesc * pDesc;
+ int32_t count = 0;
+ for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (pDesc->SupportsThings(filter))
+ count++;
+ }
+ *_retval = count;
+ }
+ else
+ *_retval = 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleWithCID(const nsCID& cid, nsIImportModule **ppModule)
+{
+ NS_PRECONDITION(ppModule != nullptr, "null ptr");
+ if (!ppModule)
+ return NS_ERROR_NULL_POINTER;
+
+ *ppModule = nullptr;
+ nsresult rv = DoDiscover();
+ if (NS_FAILED(rv))
+ return rv;
+ if (m_pModules == nullptr)
+ return NS_ERROR_FAILURE;
+ int32_t cnt = m_pModules->GetCount();
+ ImportModuleDesc *pDesc;
+ for (int32_t i = 0; i < cnt; i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (!pDesc)
+ return NS_ERROR_FAILURE;
+ if (pDesc->GetCID().Equals(cid)) {
+ *ppModule = pDesc->GetModule();
+
+ IMPORT_LOG0("* nsImportService::GetSpecificModule - attempted to load module\n");
+
+ if (*ppModule == nullptr)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+ }
+ }
+
+ IMPORT_LOG0("* nsImportService::GetSpecificModule - module not found\n");
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleInfo(const char *filter, int32_t index, char16_t **name, char16_t **moduleDescription)
+{
+ NS_PRECONDITION(name != nullptr, "null ptr");
+ NS_PRECONDITION(moduleDescription != nullptr, "null ptr");
+ if (!name || !moduleDescription)
+ return NS_ERROR_NULL_POINTER;
+
+ *name = nullptr;
+ *moduleDescription = nullptr;
+
+ DoDiscover();
+ if (!m_pModules)
+ return NS_ERROR_FAILURE;
+
+ if ((index < 0) || (index >= m_pModules->GetCount()))
+ return NS_ERROR_FAILURE;
+
+ ImportModuleDesc * pDesc;
+ int32_t count = 0;
+ for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (pDesc->SupportsThings(filter)) {
+ if (count == index) {
+ *name = NS_strdup(pDesc->GetName());
+ *moduleDescription = NS_strdup(pDesc->GetDescription());
+ return NS_OK;
+ }
+ else
+ count++;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleName(const char *filter, int32_t index, char16_t **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = nullptr;
+
+ DoDiscover();
+ if (!m_pModules)
+ return NS_ERROR_FAILURE;
+
+ if ((index < 0) || (index >= m_pModules->GetCount()))
+ return NS_ERROR_FAILURE;
+
+ ImportModuleDesc * pDesc;
+ int32_t count = 0;
+ for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (pDesc->SupportsThings(filter)) {
+ if (count == index) {
+ *_retval = NS_strdup(pDesc->GetName());
+ return NS_OK;
+ }
+ else
+ count++;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+
+NS_IMETHODIMP nsImportService::GetModuleDescription(const char *filter, int32_t index, char16_t **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = nullptr;
+
+ DoDiscover();
+ if (!m_pModules)
+ return NS_ERROR_FAILURE;
+
+ if ((index < 0) || (index >= m_pModules->GetCount()))
+ return NS_ERROR_FAILURE;
+
+ ImportModuleDesc * pDesc;
+ int32_t count = 0;
+ for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (pDesc->SupportsThings(filter)) {
+ if (count == index) {
+ *_retval = NS_strdup(pDesc->GetDescription());
+ return NS_OK;
+ }
+ else
+ count++;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+class nsProxySendRunnable : public mozilla::Runnable
+{
+public:
+ nsProxySendRunnable(nsIMsgIdentity *aIdentity,
+ nsIMsgCompFields *aMsgFields,
+ const char *attachment1_type,
+ const nsACString &attachment1_body,
+ bool aIsDraft,
+ nsIArray *aLoadedAttachments,
+ nsIArray *aEmbeddedAttachments,
+ nsIMsgSendListener *aListener);
+ NS_DECL_NSIRUNNABLE
+private:
+ nsCOMPtr<nsIMsgIdentity> m_identity;
+ nsCOMPtr<nsIMsgCompFields> m_compFields;
+ bool m_isDraft;
+ nsCString m_bodyType;
+ nsCString m_body;
+ nsCOMPtr<nsIArray> m_loadedAttachments;
+ nsCOMPtr<nsIArray> m_embeddedAttachments;
+ nsCOMPtr<nsIMsgSendListener> m_listener;
+
+};
+
+nsProxySendRunnable::nsProxySendRunnable(nsIMsgIdentity *aIdentity,
+ nsIMsgCompFields *aMsgFields,
+ const char *aBodyType,
+ const nsACString &aBody,
+ bool aIsDraft,
+ nsIArray *aLoadedAttachments,
+ nsIArray *aEmbeddedAttachments,
+ nsIMsgSendListener *aListener) :
+ m_identity(aIdentity), m_compFields(aMsgFields),
+ m_isDraft(aIsDraft), m_bodyType(aBodyType),
+ m_body(aBody), m_loadedAttachments(aLoadedAttachments),
+ m_embeddedAttachments(aEmbeddedAttachments),
+ m_listener(aListener)
+{
+}
+
+NS_IMETHODIMP nsProxySendRunnable::Run()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgSend> msgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return msgSend->CreateRFC822Message(m_identity, m_compFields,
+ m_bodyType.get(), m_body,
+ m_isDraft, m_loadedAttachments,
+ m_embeddedAttachments,
+ m_listener);
+}
+
+
+NS_IMETHODIMP
+nsImportService::CreateRFC822Message(nsIMsgIdentity *aIdentity,
+ nsIMsgCompFields *aMsgFields,
+ const char *aBodyType,
+ const nsACString &aBody,
+ bool aIsDraft,
+ nsIArray *aLoadedAttachments,
+ nsIArray *aEmbeddedAttachments,
+ nsIMsgSendListener *aListener)
+{
+ RefPtr<nsProxySendRunnable> runnable =
+ new nsProxySendRunnable(aIdentity,
+ aMsgFields,
+ aBodyType,
+ aBody,
+ aIsDraft,
+ aLoadedAttachments,
+ aEmbeddedAttachments,
+ aListener);
+ // invoke the callback
+ return NS_DispatchToMainThread(runnable);
+}
+
+NS_IMETHODIMP nsImportService::GetModule(const char *filter, int32_t index, nsIImportModule **_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+ *_retval = nullptr;
+
+ DoDiscover();
+ if (!m_pModules)
+ return NS_ERROR_FAILURE;
+
+ if ((index < 0) || (index >= m_pModules->GetCount()))
+ return NS_ERROR_FAILURE;
+
+ ImportModuleDesc * pDesc;
+ int32_t count = 0;
+ for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
+ pDesc = m_pModules->GetModuleDesc(i);
+ if (pDesc->SupportsThings(filter)) {
+ if (count == index) {
+ *_retval = pDesc->GetModule();
+ break;
+ }
+ else
+ count++;
+ }
+ }
+ if (! (*_retval))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+nsresult nsImportService::DoDiscover(void)
+{
+ if (m_didDiscovery)
+ return NS_OK;
+
+ if (m_pModules != nullptr)
+ m_pModules->ClearList();
+
+ nsresult rv;
+
+ nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ rv = catMan->EnumerateCategory("mailnewsimport", getter_AddRefs(e));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsISupportsCString> contractid;
+ rv = e->GetNext(getter_AddRefs(supports));
+ while (NS_SUCCEEDED(rv) && supports)
+ {
+ contractid = do_QueryInterface(supports);
+ if (!contractid)
+ break;
+
+ nsCString contractIdStr;
+ contractid->ToString(getter_Copies(contractIdStr));
+ nsCString supportsStr;
+ rv = catMan->GetCategoryEntry("mailnewsimport", contractIdStr.get(), getter_Copies(supportsStr));
+ if (NS_SUCCEEDED(rv))
+ LoadModuleInfo(contractIdStr.get(), supportsStr.get());
+ rv = e->GetNext(getter_AddRefs(supports));
+ }
+
+ m_didDiscovery = true;
+
+ return NS_OK;
+}
+
+nsresult nsImportService::LoadModuleInfo(const char *pClsId, const char *pSupports)
+{
+ if (!pClsId || !pSupports)
+ return NS_OK;
+
+ if (m_pModules == nullptr)
+ m_pModules = new nsImportModuleList();
+
+ // load the component and get all of the info we need from it....
+ // then call AddModule
+ nsresult rv;
+
+ nsCID clsId;
+ clsId.Parse(pClsId);
+ nsIImportModule * module;
+ rv = CallCreateInstance(clsId, &module);
+ if (NS_FAILED(rv)) return rv;
+
+ nsString theTitle;
+ nsString theDescription;
+ rv = module->GetName(getter_Copies(theTitle));
+ if (NS_FAILED(rv))
+ theTitle.AssignLiteral("Unknown");
+
+ rv = module->GetDescription(getter_Copies(theDescription));
+ if (NS_FAILED(rv))
+ theDescription.AssignLiteral("Unknown description");
+
+ // call the module to get the info we need
+ m_pModules->AddModule(clsId, pSupports, theTitle.get(), theDescription.get());
+
+ module->Release();
+
+ return NS_OK;
+}
+
+
+nsIImportModule *ImportModuleDesc::GetModule(bool keepLoaded)
+{
+ if (m_pModule)
+ {
+ m_pModule->AddRef();
+ return m_pModule;
+ }
+
+ nsresult rv;
+ rv = CallCreateInstance(m_cid, &m_pModule);
+ if (NS_FAILED(rv))
+ {
+ m_pModule = nullptr;
+ return nullptr;
+ }
+
+ if (keepLoaded)
+ {
+ m_pModule->AddRef();
+ return m_pModule;
+ }
+ else
+ {
+ nsIImportModule *pModule = m_pModule;
+ m_pModule = nullptr;
+ return pModule;
+ }
+}
+
+void ImportModuleDesc::ReleaseModule(void)
+{
+ if (m_pModule)
+ {
+ m_pModule->Release();
+ m_pModule = nullptr;
+ }
+}
+
+bool ImportModuleDesc::SupportsThings(const char *pThings)
+{
+ if (!pThings || !*pThings)
+ return true;
+
+ nsCString thing(pThings);
+ nsCString item;
+ int32_t idx;
+
+ while ((idx = thing.FindChar(',')) != -1)
+ {
+ item = StringHead(thing, idx);
+ item.Trim(kWhitespace);
+ ToLowerCase(item);
+ if (item.Length() && (m_supports.Find(item) == -1))
+ return false;
+ thing = Substring(thing, idx + 1);
+ }
+ thing.Trim(kWhitespace);
+ ToLowerCase(thing);
+ return thing.IsEmpty() || (m_supports.Find(thing) != -1);
+}
+
+void nsImportModuleList::ClearList(void)
+{
+ if (m_pList)
+ {
+ for (int i = 0; i < m_count; i++)
+ {
+ delete m_pList[i];
+ m_pList[i] = nullptr;
+ }
+ m_count = 0;
+ delete [] m_pList;
+ m_pList = nullptr;
+ m_alloc = 0;
+ }
+
+}
+
+void nsImportModuleList::AddModule(const nsCID& cid, const char *pSupports, const char16_t *pName, const char16_t *pDesc)
+{
+ if (!m_pList)
+ {
+ m_alloc = 10;
+ m_pList = new ImportModuleDesc *[m_alloc];
+ m_count = 0;
+ memset(m_pList, 0, sizeof(ImportModuleDesc *) * m_alloc);
+ }
+
+ if (m_count == m_alloc)
+ {
+ ImportModuleDesc **pList = new ImportModuleDesc *[m_alloc + 10];
+ memset(&(pList[m_alloc]), 0, sizeof(ImportModuleDesc *) * 10);
+ memcpy(pList, m_pList, sizeof(ImportModuleDesc *) * m_alloc);
+ for(int i = 0; i < m_count; i++)
+ delete m_pList[i];
+ delete [] m_pList;
+ m_pList = pList;
+ m_alloc += 10;
+ }
+
+ m_pList[m_count] = new ImportModuleDesc();
+ m_pList[m_count]->SetCID(cid);
+ m_pList[m_count]->SetSupports(pSupports);
+ m_pList[m_count]->SetName(pName);
+ m_pList[m_count]->SetDescription(pDesc);
+
+ m_count++;
+#ifdef IMPORT_DEBUG
+ IMPORT_LOG3("* nsImportService registered import module: %s, %s, %s\n", NS_LossyConvertUTF16toASCII(pName).get(), NS_LossyConvertUTF16toASCII(pDesc).get(), pSupports);
+#endif
+}
+
diff --git a/mailnews/import/src/nsImportService.h b/mailnews/import/src/nsImportService.h
new file mode 100644
index 000000000..889cfa6c4
--- /dev/null
+++ b/mailnews/import/src/nsImportService.h
@@ -0,0 +1,96 @@
+/* -*- 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/. */
+
+#ifndef nsImportService_h__
+#define nsImportService_h__
+
+#include "nsICharsetConverterManager.h"
+
+#include "nsStringGlue.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsMemory.h"
+#include "nsIImportModule.h"
+#include "nsIImportService.h"
+#include "nsICategoryManager.h"
+#include "nsIStringBundle.h"
+
+class nsImportModuleList;
+
+class nsImportService : public nsIImportService
+{
+public:
+
+ nsImportService();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIIMPORTSERVICE
+
+private:
+ virtual ~nsImportService();
+ nsresult LoadModuleInfo(const char*pClsId, const char *pSupports);
+ nsresult DoDiscover(void);
+
+private:
+ nsImportModuleList * m_pModules;
+ bool m_didDiscovery;
+ nsCString m_sysCharset;
+ nsIUnicodeDecoder * m_pDecoder;
+ nsIUnicodeEncoder * m_pEncoder;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+class ImportModuleDesc {
+public:
+ ImportModuleDesc() { m_pModule = nullptr;}
+ ~ImportModuleDesc() { ReleaseModule(); }
+
+ void SetCID(const nsCID& cid) { m_cid = cid;}
+ void SetName(const char16_t *pName) { m_name = pName;}
+ void SetDescription(const char16_t *pDesc) { m_description = pDesc;}
+ void SetSupports(const char *pSupports) { m_supports = pSupports;}
+
+ nsCID GetCID(void) { return m_cid;}
+ const char16_t *GetName(void) { return m_name.get();}
+ const char16_t *GetDescription(void) { return m_description.get();}
+ const char * GetSupports(void) { return m_supports.get();}
+
+ nsIImportModule * GetModule(bool keepLoaded = false); // Adds ref
+ void ReleaseModule(void);
+
+ bool SupportsThings(const char *pThings);
+
+private:
+ nsCID m_cid;
+ nsString m_name;
+ nsString m_description;
+ nsCString m_supports;
+ nsIImportModule *m_pModule;
+};
+
+class nsImportModuleList {
+public:
+ nsImportModuleList() { m_pList = nullptr; m_alloc = 0; m_count = 0;}
+ ~nsImportModuleList() { ClearList(); }
+
+ void AddModule(const nsCID& cid, const char *pSupports, const char16_t *pName, const char16_t *pDesc);
+
+ void ClearList(void);
+
+ int32_t GetCount(void) { return m_count;}
+
+ ImportModuleDesc * GetModuleDesc(int32_t idx)
+ { if ((idx < 0) || (idx >= m_count)) return nullptr; else return m_pList[idx];}
+
+private:
+
+private:
+ ImportModuleDesc ** m_pList;
+ int32_t m_alloc;
+ int32_t m_count;
+};
+
+#endif // nsImportService_h__
diff --git a/mailnews/import/src/nsImportStringBundle.cpp b/mailnews/import/src/nsImportStringBundle.cpp
new file mode 100644
index 000000000..3adb6655a
--- /dev/null
+++ b/mailnews/import/src/nsImportStringBundle.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "prprf.h"
+#include "prmem.h"
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+#include "nsImportStringBundle.h"
+#include "nsIServiceManager.h"
+#include "nsIURI.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/Services.h"
+
+nsresult nsImportStringBundle::GetStringBundle(const char *aPropertyURL,
+ nsIStringBundle **aBundle)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(sBundleService, NS_ERROR_UNEXPECTED);
+ rv = sBundleService->CreateBundle(aPropertyURL, aBundle);
+
+ return rv;
+}
+
+void nsImportStringBundle::GetStringByID(int32_t aStringID,
+ nsIStringBundle *aBundle,
+ nsString &aResult)
+{
+ aResult.Adopt(GetStringByID(aStringID, aBundle));
+}
+
+char16_t *nsImportStringBundle::GetStringByID(int32_t aStringID,
+ nsIStringBundle *aBundle)
+{
+ if (aBundle)
+ {
+ char16_t *ptrv = nullptr;
+ nsresult rv = aBundle->GetStringFromID(aStringID, &ptrv);
+
+ if (NS_SUCCEEDED(rv) && ptrv)
+ return ptrv;
+ }
+
+ nsString resultString(NS_LITERAL_STRING("[StringID "));
+ resultString.AppendInt(aStringID);
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
+
+void nsImportStringBundle::GetStringByName(const char *aName,
+ nsIStringBundle *aBundle,
+ nsString &aResult)
+{
+ aResult.Adopt(GetStringByName(aName, aBundle));
+}
+
+char16_t *nsImportStringBundle::GetStringByName(const char *aName,
+ nsIStringBundle *aBundle)
+{
+ if (aBundle)
+ {
+ char16_t *ptrv = nullptr;
+ nsresult rv = aBundle->GetStringFromName(
+ NS_ConvertUTF8toUTF16(aName).get(), &ptrv);
+
+ if (NS_SUCCEEDED(rv) && ptrv)
+ return ptrv;
+ }
+
+ nsString resultString(NS_LITERAL_STRING("[StringName "));
+ resultString.Append(NS_ConvertUTF8toUTF16(aName).get());
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
diff --git a/mailnews/import/src/nsImportStringBundle.h b/mailnews/import/src/nsImportStringBundle.h
new file mode 100644
index 000000000..c9db012e6
--- /dev/null
+++ b/mailnews/import/src/nsImportStringBundle.h
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsImportStringBundle_H__
+#define _nsImportStringBundle_H__
+
+#include "nsStringGlue.h"
+
+class nsIStringBundle;
+
+class nsImportStringBundle
+{
+public:
+ static char16_t* GetStringByID(int32_t aStringID,
+ nsIStringBundle *aBundle = nullptr);
+ static void GetStringByID(int32_t aStringID,
+ nsIStringBundle *aBundle,
+ nsString &aResult);
+ static char16_t* GetStringByName(const char *aName,
+ nsIStringBundle *aBundle = nullptr);
+ static void GetStringByName(const char *aName,
+ nsIStringBundle *aBundle,
+ nsString &aResult);
+ static nsresult GetStringBundle(const char *aPropertyURL,
+ nsIStringBundle **aBundle);
+};
+
+#define IMPORT_MSGS_URL "chrome://messenger/locale/importMsgs.properties"
+
+
+#define IMPORT_NO_ADDRBOOKS 2000
+#define IMPORT_ERROR_AB_NOTINITIALIZED 2001
+#define IMPORT_ERROR_AB_NOTHREAD 2002
+#define IMPORT_ERROR_GETABOOK 2003
+#define IMPORT_NO_MAILBOXES 2004
+#define IMPORT_ERROR_MB_NOTINITIALIZED 2005
+#define IMPORT_ERROR_MB_NOTHREAD 2006
+#define IMPORT_ERROR_MB_NOPROXY 2007
+#define IMPORT_ERROR_MB_FINDCHILD 2008
+#define IMPORT_ERROR_MB_CREATE 2009
+#define IMPORT_ERROR_MB_NODESTFOLDER 2010
+
+#define IMPORT_FIELD_DESC_START 2100
+#define IMPORT_FIELD_DESC_END 2136
+
+
+#endif /* _nsImportStringBundle_H__ */
diff --git a/mailnews/import/src/nsImportTranslator.cpp b/mailnews/import/src/nsImportTranslator.cpp
new file mode 100644
index 000000000..beec8b93a
--- /dev/null
+++ b/mailnews/import/src/nsImportTranslator.cpp
@@ -0,0 +1,296 @@
+/* -*- 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/. */
+
+#include "ImportOutFile.h"
+#include "nsImportTranslator.h"
+
+#include "ImportCharSet.h"
+
+
+bool nsImportTranslator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed)
+{
+ if (pProcessed)
+ *pProcessed = inLen;
+ return (pOutFile->WriteData(pIn, inLen));
+}
+
+void CMHTranslator::ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut)
+{
+ while (inLen) {
+ if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) ||
+ (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '*') || (*pIn == '\'') ||
+ (*pIn == '%')) {
+ // needs to be encode as %hex val
+ *pOut = '%'; pOut++;
+ ImportCharSet::ByteToHex(*pIn, pOut);
+ pOut += 2;
+ }
+ else {
+ *pOut = *pIn;
+ pOut++;
+ }
+ pIn++; inLen--;
+ }
+ *pOut = 0;
+}
+
+bool CMHTranslator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed)
+{
+ uint8_t hex[2];
+ while (inLen) {
+ if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) ||
+ (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '*') || (*pIn == '\'') ||
+ (*pIn == '%')) {
+ // needs to be encode as %hex val
+ if (!pOutFile->WriteByte('%'))
+ return false;
+ ImportCharSet::ByteToHex(*pIn, hex);
+ if (!pOutFile->WriteData(hex, 2))
+ return false;
+ }
+ else {
+ if (!pOutFile->WriteByte(*pIn))
+ return false;
+ }
+ pIn++; inLen--;
+ }
+
+ if (pProcessed)
+ *pProcessed = inLen;
+
+ return true;
+}
+
+
+bool C2047Translator::ConvertToFileQ(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed)
+{
+ if (!inLen)
+ return true;
+
+ int maxLineLen = 64;
+ int curLineLen = m_startLen;
+ bool startLine = true;
+
+ uint8_t hex[2];
+ while (inLen) {
+ if (startLine) {
+ if (!pOutFile->WriteStr(" =?"))
+ return false;
+ if (!pOutFile->WriteStr(m_charset.get()))
+ return false;
+ if (!pOutFile->WriteStr("?q?"))
+ return false;
+ curLineLen += (6 + m_charset.Length());
+ startLine = false;
+ }
+
+ if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) ||
+ (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '?') || (*pIn == '=')) {
+ // needs to be encode as =hex val
+ if (!pOutFile->WriteByte('='))
+ return false;
+ ImportCharSet::ByteToHex(*pIn, hex);
+ if (!pOutFile->WriteData(hex, 2))
+ return false;
+ curLineLen += 3;
+ }
+ else {
+ if (!pOutFile->WriteByte(*pIn))
+ return false;
+ curLineLen++;
+ }
+ pIn++; inLen--;
+ if (curLineLen > maxLineLen) {
+ if (!pOutFile->WriteStr("?="))
+ return false;
+ if (inLen) {
+ if (!pOutFile->WriteStr("\x0D\x0A "))
+ return false;
+ }
+
+ startLine = true;
+ curLineLen = 0;
+ }
+ }
+
+ if (!startLine) {
+ // end the encoding!
+ if (!pOutFile->WriteStr("?="))
+ return false;
+ }
+
+ if (pProcessed)
+ *pProcessed = inLen;
+
+ return true;
+}
+
+bool C2047Translator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed)
+{
+ if (m_useQuotedPrintable)
+ return ConvertToFileQ(pIn, inLen, pOutFile, pProcessed);
+
+ if (!inLen)
+ return true;
+
+ int maxLineLen = 64;
+ int curLineLen = m_startLen;
+ bool startLine = true;
+ int encodeMax;
+ uint8_t * pEncoded = new uint8_t[maxLineLen * 2];
+
+ while (inLen) {
+ if (startLine) {
+ if (!pOutFile->WriteStr(" =?")) {
+ delete [] pEncoded;
+ return false;
+ }
+ if (!pOutFile->WriteStr(m_charset.get())) {
+ delete [] pEncoded;
+ return false;
+ }
+ if (!pOutFile->WriteStr("?b?")) {
+ delete [] pEncoded;
+ return false;
+ }
+ curLineLen += (6 + m_charset.Length());
+ startLine = false;
+ }
+ encodeMax = maxLineLen - curLineLen;
+ encodeMax *= 3;
+ encodeMax /= 4;
+ if ((uint32_t)encodeMax > inLen)
+ encodeMax = (int)inLen;
+
+ // encode the line, end the line
+ // then continue. Update curLineLen, pIn, startLine, and inLen
+ UMimeEncode::ConvertBuffer(pIn, encodeMax, pEncoded, maxLineLen, maxLineLen, "\x0D\x0A");
+
+ if (!pOutFile->WriteStr((const char *)pEncoded)) {
+ delete [] pEncoded;
+ return false;
+ }
+
+ pIn += encodeMax;
+ inLen -= encodeMax;
+ startLine = true;
+ curLineLen = 0;
+ if (!pOutFile->WriteStr("?=")) {
+ delete [] pEncoded;
+ return false;
+ }
+ if (inLen) {
+ if (!pOutFile->WriteStr("\x0D\x0A ")) {
+ delete [] pEncoded;
+ return false;
+ }
+ }
+ }
+
+ delete [] pEncoded;
+
+ if (pProcessed)
+ *pProcessed = inLen;
+
+ return true;
+}
+
+
+uint32_t UMimeEncode::GetBufferSize(uint32_t inBytes)
+{
+ // it takes 4 base64 bytes to represent 3 regular bytes
+ inBytes += 3;
+ inBytes /= 3;
+ inBytes *= 4;
+ // This should be plenty, but just to be safe
+ inBytes += 4;
+
+ // now allow for end of line characters
+ inBytes += ((inBytes + 39) / 40) * 4;
+
+ return inBytes;
+}
+
+static uint8_t gBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+uint32_t UMimeEncode::ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut, uint32_t maxLen, uint32_t firstLineLen, const char * pEolStr)
+{
+
+ uint32_t pos = 0;
+ uint32_t len = 0;
+ uint32_t lineLen = 0;
+ uint32_t maxLine = firstLineLen;
+ int eolLen = 0;
+ if (pEolStr)
+ eolLen = strlen(pEolStr);
+
+ while ((pos + 2) < inLen) {
+ // Encode 3 bytes
+ *pOut = gBase64[*pIn >> 2];
+ pOut++; len++; lineLen++;
+ *pOut = gBase64[(((*pIn) & 0x3)<< 4) | (((*(pIn + 1)) & 0xF0) >> 4)];
+ pIn++; pOut++; len++; lineLen++;
+ *pOut = gBase64[(((*pIn) & 0xF) << 2) | (((*(pIn + 1)) & 0xC0) >>6)];
+ pIn++; pOut++; len++; lineLen++;
+ *pOut = gBase64[(*pIn) & 0x3F];
+ pIn++; pOut++; len++; lineLen++;
+ pos += 3;
+ if (lineLen >= maxLine) {
+ lineLen = 0;
+ maxLine = maxLen;
+ if (pEolStr) {
+ memcpy(pOut, pEolStr, eolLen);
+ pOut += eolLen;
+ len += eolLen;
+ }
+ }
+ }
+
+ if ((pos < inLen) && ((lineLen + 3) > maxLine)) {
+ lineLen = 0;
+ maxLine = maxLen;
+ if (pEolStr) {
+ memcpy(pOut, pEolStr, eolLen);
+ pOut += eolLen;
+ len += eolLen;
+ }
+ }
+
+ if (pos < inLen) {
+ // Get the last few bytes!
+ *pOut = gBase64[*pIn >> 2];
+ pOut++; len++;
+ pos++;
+ if (pos < inLen) {
+ *pOut = gBase64[(((*pIn) & 0x3)<< 4) | (((*(pIn + 1)) & 0xF0) >> 4)];
+ pIn++; pOut++; pos++; len++;
+ if (pos < inLen) {
+ // Should be dead code!! (Then why is it here doofus?)
+ *pOut = gBase64[(((*pIn) & 0xF) << 2) | (((*(pIn + 1)) & 0xC0) >>6)];
+ pIn++; pOut++; len++;
+ *pOut = gBase64[(*pIn) & 0x3F];
+ pos++; pOut++; len++;
+ }
+ else {
+ *pOut = gBase64[(((*pIn) & 0xF) << 2)];
+ pOut++; len++;
+ *pOut = '=';
+ pOut++; len++;
+ }
+ }
+ else {
+ *pOut = gBase64[(((*pIn) & 0x3)<< 4)];
+ pOut++; len++;
+ *pOut = '=';
+ pOut++; len++;
+ *pOut = '=';
+ pOut++; len++;
+ }
+ }
+
+ *pOut = 0;
+
+ return len;
+}
diff --git a/mailnews/import/src/nsImportTranslator.h b/mailnews/import/src/nsImportTranslator.h
new file mode 100644
index 000000000..998616063
--- /dev/null
+++ b/mailnews/import/src/nsImportTranslator.h
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#ifndef nsImportTranslator_h___
+#define nsImportTranslator_h___
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+
+class ImportOutFile;
+
+class UMimeEncode {
+public:
+ static uint32_t GetBufferSize(uint32_t inByes);
+ static uint32_t ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t *pOut, uint32_t maxLen = 72, uint32_t firstLineLen = 72, const char * pEolStr = nullptr);
+};
+
+
+class nsImportTranslator {
+public:
+ virtual ~nsImportTranslator() {}
+ virtual bool Supports8bitEncoding(void) { return false;}
+ virtual uint32_t GetMaxBufferSize(uint32_t inLen) { return inLen + 1;}
+ virtual void ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut) { memcpy(pOut, pIn, inLen); pOut[inLen] = 0;}
+ virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr);
+ virtual bool FinishConvertToFile(ImportOutFile * /* pOutFile */) { return true;}
+
+ virtual void GetCharset(nsCString& charSet) { charSet = "us-ascii";}
+ virtual void GetLanguage(nsCString& lang) { lang = "en";}
+ virtual void GetEncoding(nsCString& encoding) { encoding.Truncate();}
+};
+
+// Specialized encoder, not a vaild language translator, used for Mime headers.
+// rfc2231
+class CMHTranslator : public nsImportTranslator {
+public:
+ virtual uint32_t GetMaxBufferSize(uint32_t inLen) override { return (inLen * 3) + 1;}
+ virtual void ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut) override;
+ virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr) override;
+};
+
+// Specialized encoder, not a vaild language translator, used for mail headers
+// rfc2047
+class C2047Translator : public nsImportTranslator {
+public:
+ virtual ~C2047Translator() {}
+
+ C2047Translator(const char *pCharset, uint32_t headerLen) { m_charset = pCharset; m_startLen = headerLen; m_useQuotedPrintable = false;}
+
+ void SetUseQuotedPrintable(void) { m_useQuotedPrintable = true;}
+
+ virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr) override;
+ bool ConvertToFileQ(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed);
+
+protected:
+ bool m_useQuotedPrintable;
+ nsCString m_charset;
+ uint32_t m_startLen;
+};
+
+#endif /* nsImportTranslator_h__ */
+
diff --git a/mailnews/import/text/src/TextDebugLog.h b/mailnews/import/text/src/TextDebugLog.h
new file mode 100644
index 000000000..3f9bf1ec4
--- /dev/null
+++ b/mailnews/import/text/src/TextDebugLog.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TextDebugLog_h___
+#define TextDebugLog_h___
+
+// Use PR_LOG for logging.
+#include "mozilla/Logging.h"
+extern PRLogModuleInfo *TEXTIMPORTLOGMODULE; // Logging module
+
+#define IMPORT_LOG0(x) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+
+
+#endif /* TextDebugLog_h___ */
diff --git a/mailnews/import/text/src/moz.build b/mailnews/import/text/src/moz.build
new file mode 100644
index 000000000..1bdbb07c9
--- /dev/null
+++ b/mailnews/import/text/src/moz.build
@@ -0,0 +1,16 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsTextAddress.cpp',
+ 'nsTextImport.cpp',
+]
+
+FINAL_LIBRARY = 'import'
+
+LOCAL_INCLUDES += [
+ '../../src'
+]
+
diff --git a/mailnews/import/text/src/nsTextAddress.cpp b/mailnews/import/text/src/nsTextAddress.cpp
new file mode 100644
index 000000000..8a22498d2
--- /dev/null
+++ b/mailnews/import/text/src/nsTextAddress.cpp
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsTextAddress.h"
+#include "nsIAddrDatabase.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsNetUtil.h"
+#include "nsMsgI18N.h"
+#include "nsMsgUtils.h"
+#include "mdb.h"
+#include "nsIConverterInputStream.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsMsgUtils.h"
+
+#include "TextDebugLog.h"
+#include "plstr.h"
+#include "msgCore.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#include "nsMsgI18N.h"
+#define NS_CopyNativeToUnicode(source, dest) \
+ nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest)
+#endif
+
+#define kWhitespace " \t\b\r\n"
+
+nsTextAddress::nsTextAddress()
+{
+ m_database = nullptr;
+ m_fieldMap = nullptr;
+ m_LFCount = 0;
+ m_CRCount = 0;
+}
+
+nsTextAddress::~nsTextAddress()
+{
+ NS_IF_RELEASE(m_database);
+ NS_IF_RELEASE(m_fieldMap);
+}
+
+nsresult nsTextAddress::GetUnicharLineStreamForFile(nsIFile *aFile,
+ nsIInputStream *aInputStream,
+ nsIUnicharLineInputStream **aStream)
+{
+ nsAutoCString charset;
+ nsresult rv = MsgDetectCharsetFromFile(aFile, charset);
+ if (NS_FAILED(rv)) {
+ charset.Assign(nsMsgI18NFileSystemCharset());
+ }
+
+ nsCOMPtr<nsIConverterInputStream> converterStream =
+ do_CreateInstance("@mozilla.org/intl/converter-input-stream;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = converterStream->Init(aInputStream,
+ charset.get(),
+ 8192,
+ nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
+ }
+
+ return CallQueryInterface(converterStream, aStream);
+}
+
+nsresult nsTextAddress::ImportAddresses(bool *pAbort, const char16_t *pName, nsIFile *pSrc, nsIAddrDatabase *pDb, nsIImportFieldMap *fieldMap, nsString& errors, uint32_t *pProgress)
+{
+ // Open the source file for reading, read each line and process it!
+ NS_IF_RELEASE(m_database);
+ NS_IF_RELEASE(m_fieldMap);
+ m_database = pDb;
+ m_fieldMap = fieldMap;
+ NS_ADDREF(m_fieldMap);
+ NS_ADDREF(m_database);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), pSrc);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening address file for reading\n");
+ return rv;
+ }
+
+ // Here we use this to work out the size of the file, so we can update
+ // an integer as we go through the file which will update a progress
+ // bar if required by the caller.
+ uint64_t bytesLeft = 0;
+ rv = inputStream->Available(&bytesLeft);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error checking address file for size\n");
+ inputStream->Close();
+ return rv;
+ }
+
+ uint64_t totalBytes = bytesLeft;
+ bool skipRecord = false;
+
+ rv = m_fieldMap->GetSkipFirstRecord(&skipRecord);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error checking to see if we should skip the first record\n");
+ return rv;
+ }
+
+ nsCOMPtr<nsIUnicharLineInputStream> lineStream;
+ rv = GetUnicharLineStreamForFile(pSrc, inputStream, getter_AddRefs(lineStream));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening converter stream for importer\n");
+ return rv;
+ }
+
+ bool more = true;
+ nsAutoString line;
+
+ // Skip the first record if the user has requested it.
+ if (skipRecord)
+ rv = ReadRecord(lineStream, line, &more);
+
+ while (!(*pAbort) && more && NS_SUCCEEDED(rv)) {
+ // Read the line in
+ rv = ReadRecord(lineStream, line, &more);
+ if (NS_SUCCEEDED(rv)) {
+ // Now proces it to add it to the database
+ rv = ProcessLine(line, errors);
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error processing text record.\n");
+ }
+ }
+ if (NS_SUCCEEDED(rv) && pProgress) {
+ // This won't be totally accurate, but its the best we can do
+ // considering that lineStream won't give us how many bytes
+ // are actually left.
+ bytesLeft -= line.Length();
+ *pProgress = std::min(totalBytes - bytesLeft, uint64_t(PR_UINT32_MAX));
+ }
+ }
+
+ inputStream->Close();
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error reading the address book - probably incorrect ending\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ return pDb->Commit(nsAddrDBCommitType::kLargeCommit);
+}
+
+nsresult nsTextAddress::ReadRecord(nsIUnicharLineInputStream *aLineStream,
+ nsAString &aLine,
+ bool *aMore)
+{
+ bool more = true;
+ uint32_t numQuotes = 0;
+ nsresult rv;
+ nsAutoString line;
+
+ // ensure aLine is empty
+ aLine.Truncate();
+
+ do {
+ if (!more) {
+ // No more, so we must have an incorrect file.
+ rv = NS_ERROR_FAILURE;
+ }
+ else {
+ // Read the line and append it
+ rv = aLineStream->ReadLine(line, &more);
+ if (NS_SUCCEEDED(rv)) {
+ if (!aLine.IsEmpty())
+ aLine.AppendLiteral(MSG_LINEBREAK);
+ aLine.Append(line);
+
+ numQuotes += MsgCountChar(line, char16_t('"'));
+ }
+ }
+ // Continue whilst everything is ok, and we have an odd number of quotes.
+ } while (NS_SUCCEEDED(rv) && (numQuotes % 2 != 0));
+
+ *aMore = more;
+ return rv;
+}
+
+nsresult nsTextAddress::ReadRecordNumber(nsIFile *aSrc, nsAString &aLine, int32_t rNum)
+{
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aSrc);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening address file for reading\n");
+ return rv;
+ }
+
+ int32_t rIndex = 0;
+ uint64_t bytesLeft = 0;
+
+ rv = inputStream->Available(&bytesLeft);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error checking address file for eof\n");
+ inputStream->Close();
+ return rv;
+ }
+
+ nsCOMPtr<nsIUnicharLineInputStream> lineStream;
+ rv = GetUnicharLineStreamForFile(aSrc, inputStream, getter_AddRefs(lineStream));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening converter stream for importer\n");
+ return rv;
+ }
+
+ bool more = true;
+
+ while (more && (rIndex <= rNum)) {
+ rv = ReadRecord(lineStream, aLine, &more);
+ if (NS_FAILED(rv)) {
+ inputStream->Close();
+ return rv;
+ }
+ if (rIndex == rNum) {
+ inputStream->Close();
+ return NS_OK;
+ }
+
+ rIndex++;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+int32_t nsTextAddress::CountFields(const nsAString &aLine, char16_t delim)
+{
+ int32_t pos = 0;
+ int32_t maxLen = aLine.Length();
+ int32_t count = 0;
+ char16_t tab = char16_t('\t');
+ char16_t doubleQuote = char16_t('"');
+
+ if (delim == tab)
+ tab = char16_t('\0');
+
+ while (pos < maxLen) {
+ while (((aLine[pos] == char16_t(' ')) || (aLine[pos] == tab)) &&
+ (pos < maxLen)) {
+ pos++;
+ }
+ if ((pos < maxLen) && (aLine[pos] == doubleQuote)) {
+ pos++;
+ while ((pos < maxLen) && (aLine[pos] != doubleQuote)) {
+ pos++;
+ if (((pos + 1) < maxLen) &&
+ (aLine[pos] == doubleQuote) &&
+ (aLine[pos + 1] == doubleQuote)) {
+ pos += 2;
+ }
+ }
+ if (pos < maxLen)
+ pos++;
+ }
+ while ((pos < maxLen) && (aLine[pos] != delim))
+ pos++;
+
+ count++;
+ pos++;
+ }
+
+ return count;
+}
+
+bool nsTextAddress::GetField(const nsAString &aLine,
+ int32_t index,
+ nsString &field,
+ char16_t delim)
+{
+ bool result = false;
+ int32_t pos = 0;
+ int32_t maxLen = aLine.Length();
+ char16_t tab = char16_t('\t');
+ char16_t doubleQuote = char16_t('"');
+
+ field.Truncate();
+
+ if (delim == tab)
+ tab = 0;
+
+ while (index && (pos < maxLen)) {
+ while (((aLine[pos] == char16_t(' ')) || (aLine[pos] == tab)) &&
+ (pos < maxLen)) {
+ pos++;
+ }
+ if (pos >= maxLen)
+ break;
+ if (aLine[pos] == doubleQuote) {
+ do {
+ pos++;
+ if (((pos + 1) < maxLen) &&
+ (aLine[pos] == doubleQuote) &&
+ (aLine[pos + 1] == doubleQuote)) {
+ pos += 2;
+ }
+ } while ((pos < maxLen) && (aLine[pos] != doubleQuote));
+ if (pos < maxLen)
+ pos++;
+ }
+ if (pos >= maxLen)
+ break;
+
+ while ((pos < maxLen) && (aLine[pos] != delim))
+ pos++;
+
+ if (pos >= maxLen)
+ break;
+
+ index--;
+ pos++;
+ }
+
+ if (pos >= maxLen)
+ return result;
+
+ result = true;
+
+ while ((pos < maxLen) && ((aLine[pos] == ' ') || (aLine[pos] == tab)))
+ pos++;
+
+ int32_t fLen = 0;
+ int32_t startPos = pos;
+ bool quoted = false;
+ if (aLine[pos] == '"') {
+ startPos++;
+ fLen = -1;
+ do {
+ pos++;
+ fLen++;
+ if (((pos + 1) < maxLen) &&
+ (aLine[pos] == doubleQuote) &&
+ (aLine[pos + 1] == doubleQuote)) {
+ quoted = true;
+ pos += 2;
+ fLen += 2;
+ }
+ } while ((pos < maxLen) && (aLine[pos] != doubleQuote));
+ }
+ else {
+ while ((pos < maxLen) && (aLine[pos] != delim)) {
+ pos++;
+ fLen++;
+ }
+ }
+
+ if (!fLen) {
+ return result;
+ }
+
+ field.Append(nsDependentSubstring(aLine, startPos, fLen));
+ field.Trim(kWhitespace);
+
+ if (quoted) {
+ int32_t offset = field.Find("\"\"");
+ while (offset != -1) {
+ field.Cut(offset, 1);
+ offset = MsgFind(field, "\"\"", false, offset + 1);
+ }
+ }
+
+ return result;
+}
+
+nsresult nsTextAddress::DetermineDelim(nsIFile *aSrc)
+{
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aSrc);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening address file for reading\n");
+ return rv;
+ }
+
+ int32_t lineCount = 0;
+ int32_t tabCount = 0;
+ int32_t commaCount = 0;
+ int32_t tabLines = 0;
+ int32_t commaLines = 0;
+ nsAutoString line;
+ bool more = true;
+
+ nsCOMPtr<nsIUnicharLineInputStream> lineStream;
+ rv = GetUnicharLineStreamForFile(aSrc, inputStream, getter_AddRefs(lineStream));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening converter stream for importer\n");
+ return rv;
+ }
+
+ while (more && NS_SUCCEEDED(rv) && (lineCount < 100)) {
+ rv = lineStream->ReadLine(line, &more);
+ if (NS_SUCCEEDED(rv)) {
+ tabCount = CountFields(line, char16_t('\t'));
+ commaCount = CountFields(line, char16_t(','));
+ if (tabCount > commaCount)
+ tabLines++;
+ else if (commaCount)
+ commaLines++;
+ }
+ lineCount++;
+ }
+
+ rv = inputStream->Close();
+
+ if (tabLines > commaLines)
+ m_delim = char16_t('\t');
+ else
+ m_delim = char16_t(',');
+
+ IMPORT_LOG2( "Tab count = %d, Comma count = %d\n", tabLines, commaLines);
+
+ return rv;
+}
+
+/*
+ This is where the real work happens!
+ Go through the field map and set the data in a new database row
+*/
+nsresult nsTextAddress::ProcessLine(const nsAString &aLine, nsString& errors)
+{
+ if (!m_fieldMap) {
+ IMPORT_LOG0("*** Error, text import needs a field map\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ // Wait until we get our first non-empty field, then create a new row,
+ // fill in the data, then add the row to the database.
+ nsCOMPtr<nsIMdbRow> newRow;
+ nsAutoString fieldVal;
+ int32_t fieldNum;
+ int32_t numFields = 0;
+ bool active;
+ rv = m_fieldMap->GetMapSize(&numFields);
+ for (int32_t i = 0; (i < numFields) && NS_SUCCEEDED(rv); i++) {
+ active = false;
+ rv = m_fieldMap->GetFieldMap(i, &fieldNum);
+ if (NS_SUCCEEDED(rv))
+ rv = m_fieldMap->GetFieldActive(i, &active);
+ if (NS_SUCCEEDED(rv) && active) {
+ if (GetField(aLine, i, fieldVal, m_delim)) {
+ if (!fieldVal.IsEmpty()) {
+ if (!newRow) {
+ rv = m_database->GetNewRow(getter_AddRefs(newRow));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error getting new address database row\n");
+ }
+ }
+ if (newRow) {
+ rv = m_fieldMap->SetFieldValue(m_database, newRow, fieldNum, fieldVal.get());
+ }
+ }
+ }
+ else
+ break;
+ }
+ else if (active) {
+ IMPORT_LOG1("*** Error getting field map for index %ld\n", (long) i);
+ }
+ }
+
+ if (NS_SUCCEEDED(rv) && newRow)
+ rv = m_database->AddCardRowToDB(newRow);
+
+ return rv;
+}
+
diff --git a/mailnews/import/text/src/nsTextAddress.h b/mailnews/import/text/src/nsTextAddress.h
new file mode 100644
index 000000000..69a311be4
--- /dev/null
+++ b/mailnews/import/text/src/nsTextAddress.h
@@ -0,0 +1,57 @@
+/* -*- 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/. */
+
+#ifndef nsTextAddress_h__
+#define nsTextAddress_h__
+
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsIImportFieldMap.h"
+#include "nsIImportService.h"
+
+class nsIAddrDatabase;
+class nsIFile;
+class nsIInputStream;
+class nsIUnicharLineInputStream;
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+class nsTextAddress {
+public:
+ nsTextAddress();
+ virtual ~nsTextAddress();
+
+ nsresult ImportAddresses(bool *pAbort, const char16_t *pName, nsIFile *pSrc, nsIAddrDatabase *pDb, nsIImportFieldMap *fieldMap, nsString& errors, uint32_t *pProgress);
+
+ nsresult DetermineDelim(nsIFile *pSrc);
+ char16_t GetDelim(void) { return m_delim; }
+
+ static nsresult ReadRecordNumber(nsIFile *pSrc, nsAString &aLine, int32_t rNum);
+ static bool GetField(const nsAString &aLine, int32_t index, nsString &field, char16_t delim);
+
+private:
+ nsresult ProcessLine(const nsAString &aLine, nsString &errors);
+
+ static int32_t CountFields(const nsAString &aLine, char16_t delim);
+ static nsresult ReadRecord(nsIUnicharLineInputStream *pSrc, nsAString &aLine, bool *aMore);
+ static nsresult GetUnicharLineStreamForFile(nsIFile *aFile,
+ nsIInputStream *aInputStream,
+ nsIUnicharLineInputStream **aStream);
+
+ char16_t m_delim;
+ int32_t m_LFCount;
+ int32_t m_CRCount;
+ nsIAddrDatabase *m_database;
+ nsIImportFieldMap *m_fieldMap;
+ nsCOMPtr<nsIImportService> m_pService;
+};
+
+
+
+#endif /* nsTextAddress_h__ */
+
diff --git a/mailnews/import/text/src/nsTextImport.cpp b/mailnews/import/text/src/nsTextImport.cpp
new file mode 100644
index 000000000..0b99e652b
--- /dev/null
+++ b/mailnews/import/text/src/nsTextImport.cpp
@@ -0,0 +1,714 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+
+/*
+
+ Text import addressbook interfaces
+
+*/
+#include "nscore.h"
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+#include "nsIImportService.h"
+#include "nsMsgI18N.h"
+#include "nsIComponentManager.h"
+#include "nsTextImport.h"
+#include "nsIMemory.h"
+#include "nsIMutableArray.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsIOutputStream.h"
+#include "nsIAddrDatabase.h"
+#include "nsIAbLDIFService.h"
+#include "nsAbBaseCID.h"
+#include "nsTextFormatter.h"
+#include "nsImportStringBundle.h"
+#include "nsTextAddress.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "TextDebugLog.h"
+#include "nsNetUtil.h"
+#include "nsMsgUtils.h"
+
+#define TEXT_MSGS_URL "chrome://messenger/locale/textImportMsgs.properties"
+#define TEXTIMPORT_NAME 2000
+#define TEXTIMPORT_DESCRIPTION 2001
+#define TEXTIMPORT_ADDRESS_NAME 2002
+#define TEXTIMPORT_ADDRESS_SUCCESS 2003
+#define TEXTIMPORT_ADDRESS_BADPARAM 2004
+#define TEXTIMPORT_ADDRESS_BADSOURCEFILE 2005
+#define TEXTIMPORT_ADDRESS_CONVERTERROR 2006
+
+static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+PRLogModuleInfo* TEXTIMPORTLOGMODULE;
+
+class ImportAddressImpl final : public nsIImportAddressBooks
+{
+public:
+ explicit ImportAddressImpl(nsIStringBundle* aStringBundle);
+
+ static nsresult Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle *aStringBundle);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIImportAddressBooks interface
+
+ NS_IMETHOD GetSupportsMultiple(bool *_retval) override { *_retval = false; return NS_OK;}
+
+ NS_IMETHOD GetAutoFind(char16_t **description, bool *_retval) override;
+
+ NS_IMETHOD GetNeedsFieldMap(nsIFile *location, bool *_retval) override;
+
+ NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify) override;
+
+ NS_IMETHOD FindAddressBooks(nsIFile *location, nsIArray **_retval) override;
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap *fieldMap) override;
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor *source,
+ nsIAddrDatabase *destination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t **errorLog,
+ char16_t **successLog,
+ bool *fatalError) override;
+
+ NS_IMETHOD GetImportProgress(uint32_t *_retval) override;
+
+ NS_IMETHOD GetSampleData(int32_t index, bool *pFound, char16_t **pStr) override;
+
+ NS_IMETHOD SetSampleLocation(nsIFile *) override;
+
+private:
+ void ClearSampleFile(void);
+ void SaveFieldMap(nsIImportFieldMap *pMap);
+
+ static void ReportSuccess(nsString& name, nsString *pStream,
+ nsIStringBundle* pBundle);
+ static void SetLogs(nsString& success, nsString& error, char16_t **pError,
+ char16_t **pSuccess);
+ static void ReportError(int32_t errorNum, nsString& name, nsString *pStream,
+ nsIStringBundle* pBundle);
+ static void SanitizeSampleData(nsString& val);
+
+private:
+ ~ImportAddressImpl() {}
+ nsTextAddress m_text;
+ bool m_haveDelim;
+ nsCOMPtr<nsIFile> m_fileLoc;
+ nsCOMPtr<nsIStringBundle> m_notProxyBundle;
+ char16_t m_delim;
+ uint32_t m_bytesImported;
+};
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+nsTextImport::nsTextImport()
+{
+ // Init logging module.
+ if (!TEXTIMPORTLOGMODULE)
+ TEXTIMPORTLOGMODULE = PR_NewLogModule("IMPORT");
+ IMPORT_LOG0("nsTextImport Module Created\n");
+
+ nsImportStringBundle::GetStringBundle(TEXT_MSGS_URL,
+ getter_AddRefs(m_stringBundle));
+}
+
+nsTextImport::~nsTextImport()
+{
+ IMPORT_LOG0("nsTextImport Module Deleted\n");
+}
+
+NS_IMPL_ISUPPORTS(nsTextImport, nsIImportModule)
+
+NS_IMETHODIMP nsTextImport::GetName(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+ *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_NAME, m_stringBundle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsTextImport::GetDescription(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+ *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_DESCRIPTION,
+ m_stringBundle);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsTextImport::GetSupports(char **supports)
+{
+ NS_ENSURE_ARG_POINTER(supports);
+ *supports = strdup(kTextSupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsTextImport::GetSupportsUpgrade(bool *pUpgrade)
+{
+ NS_PRECONDITION(pUpgrade != nullptr, "null ptr");
+ if (! pUpgrade)
+ return NS_ERROR_NULL_POINTER;
+
+ *pUpgrade = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsTextImport::GetImportInterface(const char *pImportType, nsISupports **ppInterface)
+{
+ NS_ENSURE_ARG_POINTER(pImportType);
+ NS_ENSURE_ARG_POINTER(ppInterface);
+
+ *ppInterface = nullptr;
+ nsresult rv;
+
+ if (!strcmp(pImportType, "addressbook")) {
+ // create the nsIImportMail interface and return it!
+ nsIImportAddressBooks * pAddress = nullptr;
+ nsIImportGeneric * pGeneric = nullptr;
+ rv = ImportAddressImpl::Create(&pAddress, m_stringBundle);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(&pGeneric);
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface);
+ }
+ }
+ }
+ NS_IF_RELEASE(pAddress);
+ NS_IF_RELEASE(pGeneric);
+ return rv;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+
+
+nsresult ImportAddressImpl::Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle* aStringBundle)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+ *aImport = new ImportAddressImpl(aStringBundle);
+ if (! *aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+ImportAddressImpl::ImportAddressImpl(nsIStringBundle* aStringBundle) :
+ m_notProxyBundle(aStringBundle)
+{
+ m_haveDelim = false;
+}
+
+NS_IMPL_ISUPPORTS(ImportAddressImpl, nsIImportAddressBooks)
+
+
+NS_IMETHODIMP ImportAddressImpl::GetAutoFind(char16_t **addrDescription, bool *_retval)
+{
+ NS_PRECONDITION(addrDescription != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (! addrDescription || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ nsString str;
+ *_retval = false;
+
+ if (!m_notProxyBundle)
+ return NS_ERROR_FAILURE;
+
+ nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_NAME, m_notProxyBundle, str);
+ *addrDescription = ToNewUnicode(str);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP ImportAddressImpl::GetDefaultLocation(nsIFile **ppLoc, bool *found, bool *userVerify)
+{
+ NS_PRECONDITION(found != nullptr, "null ptr");
+ NS_PRECONDITION(ppLoc != nullptr, "null ptr");
+ NS_PRECONDITION(userVerify != nullptr, "null ptr");
+ if (! found || !userVerify || !ppLoc)
+ return NS_ERROR_NULL_POINTER;
+
+ *ppLoc = nullptr;
+ *found = false;
+ *userVerify = true;
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP ImportAddressImpl::FindAddressBooks(nsIFile *pLoc, nsIArray **ppArray)
+{
+ NS_PRECONDITION(pLoc != nullptr, "null ptr");
+ NS_PRECONDITION(ppArray != nullptr, "null ptr");
+ if (!pLoc || !ppArray)
+ return NS_ERROR_NULL_POINTER;
+
+ ClearSampleFile();
+
+ *ppArray = nullptr;
+ bool exists = false;
+ nsresult rv = pLoc->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ return NS_ERROR_FAILURE;
+
+ bool isFile = false;
+ rv = pLoc->IsFile(&isFile);
+ if (NS_FAILED(rv) || !isFile)
+ return NS_ERROR_FAILURE;
+
+ rv = m_text.DetermineDelim(pLoc);
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error determining delimitter\n");
+ return rv;
+ }
+ m_haveDelim = true;
+ m_delim = m_text.GetDelim();
+
+ m_fileLoc = do_QueryInterface(pLoc);
+
+ /* Build an address book descriptor based on the file passed in! */
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("FAILED to allocate the nsIMutableArray\n");
+ return rv;
+ }
+
+ nsString name;
+ m_fileLoc->GetLeafName(name);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed getting leaf name of file\n");
+ return rv;
+ }
+
+ int32_t idx = name.RFindChar('.');
+ if ((idx != -1) && (idx > 0) && ((name.Length() - idx - 1) < 5)) {
+ name.SetLength(idx);
+ }
+
+ nsCOMPtr<nsIImportABDescriptor> desc;
+ nsISupports * pInterface;
+
+ nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to obtain the import service\n");
+ return rv;
+ }
+
+ rv = impSvc->CreateNewABDescriptor(getter_AddRefs(desc));
+ if (NS_SUCCEEDED(rv)) {
+ int64_t sz = 0;
+ pLoc->GetFileSize(&sz);
+ desc->SetPreferredName(name);
+ desc->SetSize((uint32_t) sz);
+ desc->SetAbFile(m_fileLoc);
+ rv = desc->QueryInterface(kISupportsIID, (void **) &pInterface);
+ array->AppendElement(pInterface, false);
+ pInterface->Release();
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error creating address book descriptor for text import\n");
+ return rv;
+ }
+ array.forget(ppArray);
+ return NS_OK;
+}
+
+void ImportAddressImpl::ReportSuccess(nsString& name, nsString *pStream,
+ nsIStringBundle* pBundle)
+{
+ if (!pStream)
+ return;
+
+ // load the success string
+ char16_t *pFmt =
+ nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_SUCCESS, pBundle);
+
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportAddressImpl::ReportError(int32_t errorNum, nsString& name,
+ nsString *pStream, nsIStringBundle* pBundle)
+{
+ if (!pStream)
+ return;
+
+ // load the error string
+ char16_t *pFmt = nsImportStringBundle::GetStringByID(errorNum, pBundle);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportAddressImpl::SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess)
+{
+ if (pError)
+ *pError = ToNewUnicode(error);
+ if (pSuccess)
+ *pSuccess = ToNewUnicode(success);
+}
+
+
+NS_IMETHODIMP
+ImportAddressImpl::ImportAddressBook(nsIImportABDescriptor *pSource,
+ nsIAddrDatabase *pDestination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t ** pErrorLog,
+ char16_t ** pSuccessLog,
+ bool * fatalError)
+{
+ NS_PRECONDITION(pSource != nullptr, "null ptr");
+ NS_PRECONDITION(pDestination != nullptr, "null ptr");
+ NS_PRECONDITION(fatalError != nullptr, "null ptr");
+
+ m_bytesImported = 0;
+
+ nsString success, error;
+ if (!pSource || !pDestination || !fatalError) {
+ IMPORT_LOG0("*** Bad param passed to text address import\n");
+ nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_BADPARAM,
+ m_notProxyBundle,
+ error);
+
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+
+ if (fatalError)
+ *fatalError = true;
+
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ ClearSampleFile();
+
+ bool addrAbort = false;
+ nsString name;
+ pSource->GetPreferredName(name);
+
+ uint32_t addressSize = 0;
+ pSource->GetSize(&addressSize);
+ if (addressSize == 0) {
+ IMPORT_LOG0("Address book size is 0, skipping import.\n");
+ ReportSuccess(name, &success, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> inFile;
+ if (NS_FAILED(pSource->GetAbFile(getter_AddRefs(inFile)))) {
+ ReportError(TEXTIMPORT_ADDRESS_BADSOURCEFILE, name, &error, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aSupportService) {
+ IMPORT_LOG0("Missing support service to import call");
+ return NS_ERROR_FAILURE;
+ }
+
+ bool isLDIF = false;
+ nsresult rv;
+ nsCOMPtr<nsIAbLDIFService> ldifService(do_QueryInterface(aSupportService, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = ldifService->IsLDIFFile(inFile, &isLDIF);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error reading address file\n");
+ }
+ }
+
+ if (NS_FAILED(rv)) {
+ ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return rv;
+ }
+
+ if (isLDIF) {
+ if (ldifService)
+ rv = ldifService->ImportLDIFFile(pDestination, inFile, false, &m_bytesImported);
+ else
+ return NS_ERROR_FAILURE;
+ }
+ else {
+ rv = m_text.ImportAddresses(&addrAbort, name.get(), inFile, pDestination, fieldMap, error, &m_bytesImported);
+ SaveFieldMap(fieldMap);
+ }
+
+ if (NS_SUCCEEDED(rv) && error.IsEmpty()) {
+ ReportSuccess(name, &success, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ }
+ else {
+ ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ }
+
+ IMPORT_LOG0("*** Text address import done\n");
+ return rv;
+}
+
+
+NS_IMETHODIMP ImportAddressImpl::GetImportProgress(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_bytesImported;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP ImportAddressImpl::GetNeedsFieldMap(nsIFile *aLocation, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ *_retval = true;
+ bool exists = false;
+ bool isFile = false;
+
+ nsresult rv = aLocation->Exists(&exists);
+ rv = aLocation->IsFile(&isFile);
+
+ if (!exists || !isFile)
+ return NS_ERROR_FAILURE;
+
+ bool isLDIF = false;
+ nsCOMPtr<nsIAbLDIFService> ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv))
+ rv = ldifService->IsLDIFFile(aLocation, &isLDIF);
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error determining if file is of type LDIF\n");
+ return rv;
+ }
+
+ if (isLDIF)
+ *_retval = false;
+
+ return NS_OK;
+}
+
+void ImportAddressImpl::SanitizeSampleData(nsString& val)
+{
+ // remove any line-feeds...
+ int32_t offset = val.Find(NS_LITERAL_STRING("\x0D\x0A"));
+ while (offset != -1) {
+ val.Replace(offset, 2, NS_LITERAL_STRING(", "));
+ offset = val.Find(NS_LITERAL_STRING("\x0D\x0A"), offset + 2);
+ }
+ offset = val.FindChar(13);
+ while (offset != -1) {
+ val.Replace(offset, 1, ',');
+ offset = val.FindChar(13, offset + 2);
+ }
+ offset = val.FindChar(10);
+ while (offset != -1) {
+ val.Replace(offset, 1, ',');
+ offset = val.FindChar(10, offset + 2);
+ }
+}
+
+NS_IMETHODIMP ImportAddressImpl::GetSampleData(int32_t index, bool *pFound, char16_t **pStr)
+{
+ NS_PRECONDITION(pFound != nullptr, "null ptr");
+ NS_PRECONDITION(pStr != nullptr, "null ptr");
+ if (!pFound || !pStr)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!m_fileLoc) {
+ IMPORT_LOG0("*** Error, called GetSampleData before SetSampleLocation\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ *pStr = nullptr;
+ char16_t term = 0;
+
+ if (!m_haveDelim) {
+ rv = m_text.DetermineDelim(m_fileLoc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_haveDelim = true;
+ m_delim = m_text.GetDelim();
+ }
+
+ bool fileExists;
+ rv = m_fileLoc->Exists(&fileExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!fileExists) {
+ *pFound = false;
+ *pStr = NS_strdup(&term);
+ return NS_OK;
+ }
+
+ nsAutoString line;
+ rv = nsTextAddress::ReadRecordNumber(m_fileLoc, line, index);
+ if (NS_SUCCEEDED(rv)) {
+ nsString str;
+ nsString field;
+ int32_t fNum = 0;
+ while (nsTextAddress::GetField(line, fNum, field, m_delim)) {
+ if (fNum)
+ str.Append(char16_t('\n'));
+ SanitizeSampleData(field);
+ str.Append(field);
+ fNum++;
+ field.Truncate();
+ }
+
+ *pStr = ToNewUnicode(str);
+ *pFound = true;
+
+ /* IMPORT_LOG1("Sample data: %S\n", str.get()); */
+ }
+ else {
+ *pFound = false;
+ *pStr = NS_strdup(&term);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportAddressImpl::SetSampleLocation(nsIFile *pLocation)
+{
+ NS_ENSURE_ARG_POINTER(pLocation);
+
+ m_fileLoc = do_QueryInterface(pLocation);
+ m_haveDelim = false;
+ return NS_OK;
+}
+
+void ImportAddressImpl::ClearSampleFile(void)
+{
+ m_fileLoc = nullptr;
+ m_haveDelim = false;
+}
+
+NS_IMETHODIMP ImportAddressImpl::InitFieldMap(nsIImportFieldMap *fieldMap)
+{
+ // Let's remember the last one the user used!
+ // This should be normal for someone importing multiple times, it's usually
+ // from the same file format.
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsCString prefStr;
+ rv = prefs->GetCharPref("mailnews.import.text.fieldmap", getter_Copies(prefStr));
+ if (NS_SUCCEEDED(rv)) {
+ const char *pStr = prefStr.get();
+ if (pStr) {
+ fieldMap->SetFieldMapSize(0);
+ long fNum;
+ bool active;
+ long fIndex = 0;
+ while (*pStr) {
+ while (*pStr && (*pStr != '+') && (*pStr != '-'))
+ pStr++;
+ if (*pStr == '+')
+ active = true;
+ else if (*pStr == '-')
+ active = false;
+ else
+ break;
+ fNum = 0;
+ while (*pStr && ((*pStr < '0') || (*pStr > '9')))
+ pStr++;
+ if (!(*pStr))
+ break;
+ while (*pStr && (*pStr >= '0') && (*pStr <= '9')) {
+ fNum *= 10;
+ fNum += (*pStr - '0');
+ pStr++;
+ }
+ while (*pStr && (*pStr != ','))
+ pStr++;
+ if (*pStr == ',')
+ pStr++;
+ fieldMap->SetFieldMap(-1, fNum);
+ fieldMap->SetFieldActive(fIndex, active);
+ fIndex++;
+ }
+ if (!fIndex) {
+ int num;
+ fieldMap->GetNumMozFields(&num);
+ fieldMap->DefaultFieldMap(num);
+ }
+ }
+ }
+
+ // Now also get the last used skip first record value.
+ bool skipFirstRecord = false;
+ rv = prefs->GetBoolPref("mailnews.import.text.skipfirstrecord", &skipFirstRecord);
+ if (NS_SUCCEEDED(rv))
+ fieldMap->SetSkipFirstRecord(skipFirstRecord);
+ }
+
+ return NS_OK;
+}
+
+
+void ImportAddressImpl::SaveFieldMap(nsIImportFieldMap *pMap)
+{
+ if (!pMap)
+ return;
+
+ int size;
+ int index;
+ bool active;
+ nsCString str;
+
+ pMap->GetMapSize(&size);
+ for (long i = 0; i < size; i++) {
+ index = i;
+ active = false;
+ pMap->GetFieldMap(i, &index);
+ pMap->GetFieldActive(i, &active);
+ if (active)
+ str.Append('+');
+ else
+ str.Append('-');
+
+ str.AppendInt(index);
+ str.Append(',');
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCString prefStr;
+ rv = prefs->GetCharPref("mailnews.import.text.fieldmap", getter_Copies(prefStr));
+ if (NS_FAILED(rv) || !str.Equals(prefStr))
+ rv = prefs->SetCharPref("mailnews.import.text.fieldmap", str.get());
+ }
+
+ // Now also save last used skip first record value.
+ bool skipFirstRecord = false;
+ rv = pMap->GetSkipFirstRecord(&skipFirstRecord);
+ if (NS_SUCCEEDED(rv))
+ prefs->SetBoolPref("mailnews.import.text.skipfirstrecord", skipFirstRecord);
+}
diff --git a/mailnews/import/text/src/nsTextImport.h b/mailnews/import/text/src/nsTextImport.h
new file mode 100644
index 000000000..4c3c440e0
--- /dev/null
+++ b/mailnews/import/text/src/nsTextImport.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTextImport_h___
+#define nsTextImport_h___
+
+#include "nsIImportModule.h"
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+
+#define NS_TEXTIMPORT_CID \
+{ /* A5991D01-ADA7-11d3-A9C2-00A0CC26DA63 */ \
+ 0xa5991d01, 0xada7, 0x11d3, \
+ {0xa9, 0xc2, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63 }}
+
+#define kTextSupportsString NS_IMPORT_ADDRESS_STR
+
+class nsTextImport : public nsIImportModule
+{
+public:
+ nsTextImport();
+
+ NS_DECL_ISUPPORTS
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+protected:
+ virtual ~nsTextImport();
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+#endif /* nsTextImport_h___ */
diff --git a/mailnews/import/vcard/src/moz.build b/mailnews/import/vcard/src/moz.build
new file mode 100644
index 000000000..9e6c49698
--- /dev/null
+++ b/mailnews/import/vcard/src/moz.build
@@ -0,0 +1,20 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsVCardAddress.cpp',
+ 'nsVCardImport.cpp',
+]
+
+EXPORTS += [
+ 'nsVCardAddress.h',
+]
+
+FINAL_LIBRARY = 'import'
+
+LOCAL_INCLUDES += [
+ '../../src'
+]
+
diff --git a/mailnews/import/vcard/src/nsVCardAddress.cpp b/mailnews/import/vcard/src/nsVCardAddress.cpp
new file mode 100644
index 000000000..7495d4c26
--- /dev/null
+++ b/mailnews/import/vcard/src/nsVCardAddress.cpp
@@ -0,0 +1,139 @@
+/* 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 "nsAbBaseCID.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsNetUtil.h"
+#include "nsVCardAddress.h"
+
+#include "nsIAbCard.h"
+#include "nsIAbManager.h"
+#include "nsIAddrDatabase.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+
+#include "plstr.h"
+#include "msgCore.h"
+#include "nsMsgUtils.h"
+
+nsVCardAddress::nsVCardAddress()
+{
+}
+
+nsVCardAddress::~nsVCardAddress()
+{
+}
+
+nsresult nsVCardAddress::ImportAddresses(
+ bool *pAbort,
+ const char16_t *pName,
+ nsIFile *pSrc,
+ nsIAddrDatabase *pDb,
+ nsString& errors,
+ uint32_t *pProgress)
+{
+ // Open the source file for reading, read each line and process it!
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), pSrc);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error opening address file for reading\n");
+ return rv;
+ }
+
+ // Open the source file for reading, read each line and process it!
+ // Here we use this to work out the size of the file, so we can update
+ // an integer as we go through the file which will update a progress
+ // bar if required by the caller.
+ uint64_t bytesLeft = 0;
+ rv = inputStream->Available(&bytesLeft);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error checking address file for size\n");
+ inputStream->Close();
+ return rv;
+ }
+
+ uint64_t totalBytes = bytesLeft;
+ nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(inputStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbManager> ab = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ nsCString record;
+ while (!(*pAbort) && more && NS_SUCCEEDED(rv)) {
+ rv = ReadRecord(lineStream, record, &more);
+ if (NS_SUCCEEDED(rv) && !record.IsEmpty()) {
+ // Parse the vCard and build an nsIAbCard from it
+ nsCOMPtr<nsIAbCard> cardFromVCard;
+ rv = ab->EscapedVCardToAbCard(record.get(), getter_AddRefs(cardFromVCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pDb->CreateNewCardAndAddToDB(cardFromVCard, false, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error processing vCard record.\n");
+ }
+ }
+ if (NS_SUCCEEDED(rv) && pProgress) {
+ // This won't be totally accurate, but its the best we can do
+ // considering that lineStream won't give us how many bytes
+ // are actually left.
+ bytesLeft -= record.Length();
+ *pProgress = totalBytes - bytesLeft;
+ }
+ }
+ inputStream->Close();
+
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error reading the address book - probably incorrect ending\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ return pDb->Commit(nsAddrDBCommitType::kLargeCommit);
+}
+
+nsresult nsVCardAddress::ReadRecord(
+ nsILineInputStream *aLineStream, nsCString &aRecord, bool *aMore)
+{
+ bool more = true;
+ nsresult rv;
+ nsCString line;
+
+ aRecord.Truncate();
+
+ // remove the empty lines.
+ do {
+ rv = aLineStream->ReadLine(line, aMore);
+ }
+ while (line.IsEmpty() && *aMore);
+ if (!*aMore)
+ return rv;
+
+ // read BEGIN:VCARD
+ if (!line.LowerCaseEqualsLiteral("begin:vcard")) {
+ IMPORT_LOG0("*** Expected case-insensitive BEGIN:VCARD at start of vCard\n");
+ rv = NS_ERROR_FAILURE;
+ *aMore = more;
+ return rv;
+ }
+ aRecord.Append(line);
+
+ // read until END:VCARD
+ do {
+ if (!more) {
+ IMPORT_LOG0("*** Expected case-insensitive END:VCARD at start of vCard\n");
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ rv = aLineStream->ReadLine(line, &more);
+ aRecord.AppendLiteral(MSG_LINEBREAK);
+ aRecord.Append(line);
+ } while (!line.LowerCaseEqualsLiteral("end:vcard"));
+
+ *aMore = more;
+ return rv;
+}
diff --git a/mailnews/import/vcard/src/nsVCardAddress.h b/mailnews/import/vcard/src/nsVCardAddress.h
new file mode 100644
index 000000000..bc5e2bd06
--- /dev/null
+++ b/mailnews/import/vcard/src/nsVCardAddress.h
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsVCardAddress_h__
+#define nsVCardAddress_h__
+
+#include "mozilla/Logging.h"
+
+extern PRLogModuleInfo *VCARDLOGMODULE; // Logging module
+
+#define IMPORT_LOG0(x) MOZ_LOG(VCARDLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(VCARDLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(VCARDLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(VCARDLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+class nsIAddrDatabase;
+class nsIFile;
+class nsILineInputStream;
+
+class nsVCardAddress {
+public:
+ nsVCardAddress();
+ virtual ~nsVCardAddress();
+
+ nsresult ImportAddresses(
+ bool *pAbort,
+ const char16_t *pName,
+ nsIFile *pSrc,
+ nsIAddrDatabase *pDb,
+ nsString& errors,
+ uint32_t *pProgress);
+
+private:
+ static nsresult ReadRecord(
+ nsILineInputStream *aLineStream, nsCString &aRecord, bool *aMore);
+};
+
+#endif /* nsVCardAddress_h__ */
+
diff --git a/mailnews/import/vcard/src/nsVCardImport.cpp b/mailnews/import/vcard/src/nsVCardImport.cpp
new file mode 100644
index 000000000..fc674e496
--- /dev/null
+++ b/mailnews/import/vcard/src/nsVCardImport.cpp
@@ -0,0 +1,398 @@
+/* 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/. */
+
+/*
+ VCard import addressbook interfaces
+*/
+#include "nscore.h"
+#include "nsIAddrDatabase.h"
+#include "nsIFile.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportFieldMap.h"
+#include "nsIImportGeneric.h"
+#include "nsIMutableArray.h"
+#include "nsCOMPtr.h"
+#include "nsIImportService.h"
+#include "nsIFile.h"
+#include "nsImportStringBundle.h"
+#include "nsMsgUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTextFormatter.h"
+#include "nsVCardAddress.h"
+#include "nsVCardImport.h"
+
+PRLogModuleInfo *VCARDLOGMODULE = nullptr;
+static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+
+class ImportVCardAddressImpl : public nsIImportAddressBooks
+{
+public:
+ explicit ImportVCardAddressImpl(nsIStringBundle* aStringBundle);
+
+ static nsresult Create(
+ nsIImportAddressBooks** aImport, nsIStringBundle* aStringBundle);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIImportAddressBooks interface
+
+ // TODO: support multiple vCard files in future - shouldn't be too hard,
+ // since you just import each file in turn.
+ NS_IMETHOD GetSupportsMultiple(bool *_retval) override
+ { *_retval = false; return NS_OK;}
+
+ NS_IMETHOD GetAutoFind(char16_t **description, bool *_retval) override;
+
+ NS_IMETHOD GetNeedsFieldMap(nsIFile *location, bool *_retval) override
+ { *_retval = false; return NS_OK;}
+
+ NS_IMETHOD GetDefaultLocation(
+ nsIFile **location, bool *found, bool *userVerify) override;
+
+ NS_IMETHOD FindAddressBooks(nsIFile *location, nsIArray **_retval) override;
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap *fieldMap) override
+ { return NS_ERROR_FAILURE;}
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor *source,
+ nsIAddrDatabase *destination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t **errorLog,
+ char16_t **successLog,
+ bool *fatalError) override;
+
+ NS_IMETHOD GetImportProgress(uint32_t *_retval) override;
+
+ NS_IMETHOD GetSampleData(int32_t index, bool *pFound, char16_t **pStr) override
+ { return NS_ERROR_FAILURE;}
+
+ NS_IMETHOD SetSampleLocation(nsIFile *) override
+ { return NS_ERROR_FAILURE; }
+
+private:
+ virtual ~ImportVCardAddressImpl();
+ static void ReportSuccess(
+ nsString& name, nsString *pStream, nsIStringBundle* pBundle);
+ static void SetLogs(
+ nsString& success, nsString& error,
+ char16_t **pError, char16_t **pSuccess);
+ static void ReportError(
+ const char *errorName, nsString& name, nsString *pStream,
+ nsIStringBundle* pBundle);
+
+private:
+ nsVCardAddress m_vCard;
+ nsCOMPtr<nsIFile> m_fileLoc;
+ uint32_t m_bytesImported;
+ nsCOMPtr<nsIStringBundle> m_notProxyBundle;
+};
+
+nsVCardImport::nsVCardImport()
+{
+ if (!VCARDLOGMODULE)
+ VCARDLOGMODULE = PR_NewLogModule("IMPORT");
+
+ nsImportStringBundle::GetStringBundle(
+ VCARDIMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
+
+ IMPORT_LOG0("nsVCardImport Module Created\n");
+}
+
+nsVCardImport::~nsVCardImport()
+{
+ IMPORT_LOG0("nsVCardImport Module Deleted\n");
+}
+
+NS_IMPL_ISUPPORTS(nsVCardImport, nsIImportModule)
+
+NS_IMETHODIMP nsVCardImport::GetName(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+ *name = nsImportStringBundle::GetStringByName(
+ "vCardImportName", m_stringBundle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsVCardImport::GetDescription(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+ *name = nsImportStringBundle::GetStringByName(
+ "vCardImportDescription", m_stringBundle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsVCardImport::GetSupports(char **supports)
+{
+ NS_ENSURE_ARG_POINTER(supports);
+ *supports = strdup(NS_IMPORT_ADDRESS_STR);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsVCardImport::GetSupportsUpgrade(bool *pUpgrade)
+{
+ NS_ENSURE_ARG_POINTER(pUpgrade);
+ *pUpgrade = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsVCardImport::GetImportInterface(
+ const char *pImportType, nsISupports **ppInterface)
+{
+ NS_ENSURE_ARG_POINTER(pImportType);
+ NS_ENSURE_ARG_POINTER(ppInterface);
+ *ppInterface = nullptr;
+ if (!strcmp(pImportType, "addressbook")) {
+ nsresult rv;
+ // create the nsIImportMail interface and return it!
+ nsIImportAddressBooks *pAddress = nullptr;
+ nsIImportGeneric *pGeneric = nullptr;
+ rv = ImportVCardAddressImpl::Create(&pAddress, m_stringBundle);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(&pGeneric);
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface);
+ }
+ }
+ }
+ NS_IF_RELEASE(pAddress);
+ NS_IF_RELEASE(pGeneric);
+ return rv;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult ImportVCardAddressImpl::Create(
+ nsIImportAddressBooks** aImport, nsIStringBundle* aStringBundle)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+ *aImport = new ImportVCardAddressImpl(aStringBundle);
+ if (!*aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+ImportVCardAddressImpl::ImportVCardAddressImpl(
+ nsIStringBundle* aStringBundle) : m_notProxyBundle(aStringBundle)
+{
+}
+
+ImportVCardAddressImpl::~ImportVCardAddressImpl()
+{
+}
+
+NS_IMPL_ISUPPORTS(ImportVCardAddressImpl, nsIImportAddressBooks)
+
+NS_IMETHODIMP ImportVCardAddressImpl::GetAutoFind(
+ char16_t **addrDescription, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(addrDescription);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsString str;
+ *_retval = false;
+
+ if (!m_notProxyBundle)
+ return NS_ERROR_FAILURE;
+
+ nsImportStringBundle::GetStringByName("vCardImportAddressName", m_notProxyBundle, str);
+ *addrDescription = ToNewUnicode(str);
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportVCardAddressImpl::GetDefaultLocation(
+ nsIFile **ppLoc, bool *found, bool *userVerify)
+{
+ NS_ENSURE_ARG_POINTER(found);
+ NS_ENSURE_ARG_POINTER(ppLoc);
+ NS_ENSURE_ARG_POINTER(userVerify);
+
+ *ppLoc = nullptr;
+ *found = false;
+ *userVerify = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportVCardAddressImpl::FindAddressBooks(
+ nsIFile *pLoc, nsIArray **ppArray)
+{
+ NS_ENSURE_ARG_POINTER(pLoc);
+ NS_ENSURE_ARG_POINTER(ppArray);
+
+ *ppArray = nullptr;
+ bool exists = false;
+ nsresult rv = pLoc->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ return NS_ERROR_FAILURE;
+
+ bool isFile = false;
+ rv = pLoc->IsFile(&isFile);
+ if (NS_FAILED(rv) || !isFile)
+ return NS_ERROR_FAILURE;
+
+ m_fileLoc = do_QueryInterface(pLoc);
+
+ /* Build an address book descriptor based on the file passed in! */
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("FAILED to allocate the nsIMutableArray\n");
+ return rv;
+ }
+
+ nsString name;
+ m_fileLoc->GetLeafName(name);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed getting leaf name of file\n");
+ return rv;
+ }
+
+ int32_t idx = name.RFindChar('.');
+ if ((idx != -1) && (idx > 0) && ((name.Length() - idx - 1) < 5)) {
+ name.SetLength(idx);
+ }
+
+ nsCOMPtr<nsIImportABDescriptor> desc;
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to obtain the import service\n");
+ return rv;
+ }
+
+ rv = impSvc->CreateNewABDescriptor(getter_AddRefs(desc));
+ if (NS_SUCCEEDED(rv)) {
+ int64_t sz = 0;
+ pLoc->GetFileSize(&sz);
+ desc->SetPreferredName(name);
+ desc->SetSize((uint32_t) sz);
+ desc->SetAbFile(m_fileLoc);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(desc, &rv));
+ array->AppendElement(pInterface, false);
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0(
+ "*** Error creating address book descriptor for vCard import\n");
+ return rv;
+ }
+
+ array.forget(ppArray);
+ return NS_OK;
+}
+
+void ImportVCardAddressImpl::ReportSuccess(
+ nsString& name, nsString *pStream, nsIStringBundle* pBundle)
+{
+ if (!pStream)
+ return;
+
+ // load the success string
+ char16_t *pFmt = nsImportStringBundle::GetStringByName(
+ "vCardImportAddressSuccess", pBundle);
+
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportVCardAddressImpl::ReportError(
+ const char *errorName, nsString& name, nsString *pStream,
+ nsIStringBundle* pBundle)
+{
+ if (!pStream)
+ return;
+
+ // load the error string
+ char16_t *pFmt = nsImportStringBundle::GetStringByName(errorName, pBundle);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_Free(pFmt);
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportVCardAddressImpl::SetLogs(
+ nsString& success, nsString& error,
+ char16_t **pError, char16_t **pSuccess)
+{
+ if (pError)
+ *pError = ToNewUnicode(error);
+ if (pSuccess)
+ *pSuccess = ToNewUnicode(success);
+}
+
+NS_IMETHODIMP ImportVCardAddressImpl::ImportAddressBook(
+ nsIImportABDescriptor *pSource,
+ nsIAddrDatabase *pDestination,
+ nsIImportFieldMap *fieldMap,
+ nsISupports *aSupportService,
+ char16_t ** pErrorLog,
+ char16_t ** pSuccessLog,
+ bool * fatalError)
+{
+ NS_ENSURE_ARG_POINTER(pSource);
+ NS_ENSURE_ARG_POINTER(pDestination);
+ NS_ENSURE_ARG_POINTER(fatalError);
+
+ if (!m_notProxyBundle)
+ return NS_ERROR_FAILURE;
+
+ m_bytesImported = 0;
+ nsString success, error;
+ bool addrAbort = false;
+ nsString name;
+ pSource->GetPreferredName(name);
+
+ uint32_t addressSize = 0;
+ pSource->GetSize(&addressSize);
+ if (addressSize == 0) {
+ IMPORT_LOG0("Address book size is 0, skipping import.\n");
+ ReportSuccess(name, &success, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> inFile;
+ if (NS_FAILED(pSource->GetAbFile(getter_AddRefs(inFile)))) {
+ ReportError("vCardImportAddressBadSourceFile", name, &error, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aSupportService) {
+ IMPORT_LOG0("Missing support service to import call\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = m_vCard.ImportAddresses(
+ &addrAbort, name.get(), inFile, pDestination, error, &m_bytesImported);
+
+ if (NS_SUCCEEDED(rv) && error.IsEmpty()) {
+ ReportSuccess(name, &success, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ }
+ else {
+ ReportError("vCardImportAddressConvertError", name, &error, m_notProxyBundle);
+ SetLogs(success, error, pErrorLog, pSuccessLog);
+ }
+
+ IMPORT_LOG0("*** VCard address import done\n");
+ return rv;
+}
+
+NS_IMETHODIMP ImportVCardAddressImpl::GetImportProgress(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_bytesImported;
+ return NS_OK;
+}
diff --git a/mailnews/import/vcard/src/nsVCardImport.h b/mailnews/import/vcard/src/nsVCardImport.h
new file mode 100644
index 000000000..3204412a2
--- /dev/null
+++ b/mailnews/import/vcard/src/nsVCardImport.h
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsVCardImport_h___
+#define nsVCardImport_h___
+
+#include "nsIImportModule.h"
+#include "nsIStringBundle.h"
+#include "nsCOMPtr.h"
+
+#define NS_VCARDIMPORT_CID \
+{ /* 0EB034A3-964A-4E2F-92EBCC55D9AE9DD2 */ \
+ 0x0eb034a3, 0x964a, 0x4e2f, \
+ {0x92, 0xeb, 0xcc, 0x55, 0xd9, 0xae, 0x9d, 0xd2}}
+
+#define VCARDIMPORT_MSGS_URL "chrome://messenger/locale/vCardImportMsgs.properties"
+
+class nsVCardImport : public nsIImportModule
+{
+public:
+
+ nsVCardImport();
+
+ NS_DECL_ISUPPORTS
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+protected:
+ virtual ~nsVCardImport();
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+#endif /* nsVCardImport_h___ */
diff --git a/mailnews/import/winlivemail/WMDebugLog.h b/mailnews/import/winlivemail/WMDebugLog.h
new file mode 100644
index 000000000..c565880a5
--- /dev/null
+++ b/mailnews/import/winlivemail/WMDebugLog.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WMDebugLog_h___
+#define WMDebugLog_h___
+
+// Use PR_LOG for logging.
+#include "mozilla/Logging.h"
+extern PRLogModuleInfo *WMLOGMODULE; // Logging module
+
+#define IMPORT_LOG0(x) MOZ_LOG(WMLOGMODULE, mozilla::LogLevel::Debug, (x))
+#define IMPORT_LOG1(x, y) MOZ_LOG(WMLOGMODULE, mozilla::LogLevel::Debug, (x, y))
+#define IMPORT_LOG2(x, y, z) MOZ_LOG(WMLOGMODULE, mozilla::LogLevel::Debug, (x, y, z))
+#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(WMLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d))
+
+
+
+#endif /* WMDebugLog_h___ */
diff --git a/mailnews/import/winlivemail/moz.build b/mailnews/import/winlivemail/moz.build
new file mode 100644
index 000000000..cb69b1548
--- /dev/null
+++ b/mailnews/import/winlivemail/moz.build
@@ -0,0 +1,14 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsWMImport.cpp',
+ 'nsWMSettings.cpp',
+ 'nsWMStringBundle.cpp',
+ 'nsWMUtils.cpp',
+]
+
+FINAL_LIBRARY = 'import'
+
diff --git a/mailnews/import/winlivemail/nsWMImport.cpp b/mailnews/import/winlivemail/nsWMImport.cpp
new file mode 100644
index 000000000..f9795816a
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMImport.cpp
@@ -0,0 +1,248 @@
+/* -*- 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/. */
+
+
+/*
+
+ Windows Live Mail (Win32) import mail and addressbook interfaces
+
+*/
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsIServiceManager.h"
+#include "nsIImportService.h"
+#include "nsWMImport.h"
+#include "nsIMemory.h"
+#include "nsIImportService.h"
+#include "nsIImportMail.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIOutputStream.h"
+#include "nsIAddrDatabase.h"
+#include "nsWMSettings.h"
+#include "nsTextFormatter.h"
+#include "nsWMStringBundle.h"
+#include "nsIStringBundle.h"
+#include "nsUnicharUtils.h"
+
+#include "WMDebugLog.h"
+
+static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
+PRLogModuleInfo *WMLOGMODULE = nullptr;
+
+class ImportWMMailImpl : public nsIImportMail
+{
+public:
+ ImportWMMailImpl();
+
+ static nsresult Create(nsIImportMail** aImport);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIImportmail interface
+
+ /* void GetDefaultLocation (out nsIFile location, out boolean found, out boolean userVerify); */
+ NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify);
+
+ /* nsIArray FindMailboxes (in nsIFile location); */
+ NS_IMETHOD FindMailboxes(nsIFile *location, nsIArray **_retval);
+
+ NS_IMETHOD ImportMailbox(nsIImportMailboxDescriptor *source,
+ nsIMsgFolder *dstFolder,
+ char16_t **pErrorLog, char16_t **pSuccessLog,
+ bool *fatalError);
+
+ /* unsigned long GetImportProgress (); */
+ NS_IMETHOD GetImportProgress(uint32_t *_retval);
+
+ NS_IMETHOD TranslateFolderName(const nsAString & aFolderName, nsAString & _retval);
+
+public:
+ static void ReportSuccess(nsString& name, int32_t count, nsString *pStream);
+ static void ReportError(int32_t errorNum, nsString& name, nsString *pStream);
+ static void AddLinebreak(nsString *pStream);
+ static void SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess);
+
+private:
+ virtual ~ImportWMMailImpl();
+ uint32_t m_bytesDone;
+};
+
+nsWMImport::nsWMImport()
+{
+ // Init logging module.
+ if (!WMLOGMODULE)
+ WMLOGMODULE = PR_NewLogModule("IMPORT");
+ IMPORT_LOG0("nsWMImport Module Created\n");
+ nsWMStringBundle::GetStringBundle();
+}
+
+nsWMImport::~nsWMImport()
+{
+ IMPORT_LOG0("nsWMImport Module Deleted\n");
+}
+
+NS_IMPL_ISUPPORTS(nsWMImport, nsIImportModule)
+
+NS_IMETHODIMP nsWMImport::GetName(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+ // nsString title = "Windows Live Mail";
+ // *name = ToNewUnicode(title);
+ *name = nsWMStringBundle::GetStringByID(WMIMPORT_NAME);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMImport::GetDescription(char16_t **name)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ // nsString desc = "Windows Live Mail mail and address books";
+ // *name = ToNewUnicode(desc);
+ *name = nsWMStringBundle::GetStringByID(WMIMPORT_DESCRIPTION);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMImport::GetSupports(char **supports)
+{
+ NS_PRECONDITION(supports != nullptr, "null ptr");
+ if (! supports)
+ return NS_ERROR_NULL_POINTER;
+
+ *supports = strdup(kWMSupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMImport::GetSupportsUpgrade(bool *pUpgrade)
+{
+ NS_PRECONDITION(pUpgrade != nullptr, "null ptr");
+ if (! pUpgrade)
+ return NS_ERROR_NULL_POINTER;
+
+ *pUpgrade = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMImport::GetImportInterface(const char *pImportType,
+ nsISupports **ppInterface)
+{
+ NS_ENSURE_ARG_POINTER(pImportType);
+ NS_ENSURE_ARG_POINTER(ppInterface);
+
+ *ppInterface = nullptr;
+ nsresult rv;
+
+ if (!strcmp(pImportType, "settings")) {
+ nsIImportSettings *pSettings = nullptr;
+ rv = nsWMSettings::Create(&pSettings);
+ if (NS_SUCCEEDED(rv)) {
+ pSettings->QueryInterface(kISupportsIID, (void **)ppInterface);
+ }
+ NS_IF_RELEASE(pSettings);
+ return rv;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+nsresult ImportWMMailImpl::Create(nsIImportMail** aImport)
+{
+ NS_ENSURE_ARG_POINTER(aImport);
+ *aImport = new ImportWMMailImpl();
+ NS_ENSURE_TRUE(*aImport, NS_ERROR_OUT_OF_MEMORY);
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+ImportWMMailImpl::ImportWMMailImpl()
+{
+}
+
+ImportWMMailImpl::~ImportWMMailImpl()
+{
+}
+
+NS_IMPL_ISUPPORTS(ImportWMMailImpl, nsIImportMail)
+
+NS_IMETHODIMP ImportWMMailImpl::TranslateFolderName(const nsAString & aFolderName, nsAString & _retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP ImportWMMailImpl::GetDefaultLocation(nsIFile **ppLoc, bool *found,
+ bool *userVerify)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP ImportWMMailImpl::FindMailboxes(nsIFile *pLoc,
+ nsIArray **ppArray)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void ImportWMMailImpl::AddLinebreak(nsString *pStream)
+{
+ if (pStream)
+ pStream->Append(char16_t('\n'));
+}
+
+void ImportWMMailImpl::ReportSuccess(nsString& name, int32_t count, nsString *pStream)
+{
+ if (!pStream)
+ return;
+ // load the success string
+ char16_t *pFmt = nsWMStringBundle::GetStringByID(WMIMPORT_MAILBOX_SUCCESS);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get(), count);
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ nsWMStringBundle::FreeString(pFmt);
+ AddLinebreak(pStream);
+}
+
+void ImportWMMailImpl::ReportError(int32_t errorNum, nsString& name, nsString *pStream)
+{
+ if (!pStream)
+ return;
+ // load the error string
+ char16_t *pFmt = nsWMStringBundle::GetStringByID(errorNum);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get());
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ nsWMStringBundle::FreeString(pFmt);
+ AddLinebreak(pStream);
+}
+
+void ImportWMMailImpl::SetLogs(nsString& success, nsString& error,
+ char16_t **pError, char16_t **pSuccess)
+{
+ if (pError)
+ *pError = ToNewUnicode(error);
+ if (pSuccess)
+ *pSuccess = ToNewUnicode(success);
+}
+
+NS_IMETHODIMP ImportWMMailImpl::ImportMailbox(nsIImportMailboxDescriptor *pSource,
+ nsIMsgFolder *pDstFolder,
+ char16_t **pErrorLog,
+ char16_t **pSuccessLog,
+ bool *fatalError)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP ImportWMMailImpl::GetImportProgress(uint32_t *pDoneSoFar)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/mailnews/import/winlivemail/nsWMImport.h b/mailnews/import/winlivemail/nsWMImport.h
new file mode 100644
index 000000000..60b34047c
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMImport.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWMImport_h___
+#define nsWMImport_h___
+
+#include "nsIImportModule.h"
+#include "nsCOMPtr.h"
+
+#define NS_WMIMPORT_CID \
+{ /* 42bc82bc-8e9f-4597-8b6e-e529daaf3af1 */ \
+ 0x42bc82bc, 0x8e9f, 0x4597, \
+ {0x8b, 0x6e, 0xe5, 0x29, 0xda, 0xaf, 0x3a, 0xf1}}
+
+// currently only support setting import
+#define kWMSupportsString NS_IMPORT_SETTINGS_STR
+
+class nsWMImport : public nsIImportModule
+{
+public:
+
+ nsWMImport();
+
+ NS_DECL_ISUPPORTS
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+protected:
+ virtual ~nsWMImport();
+};
+
+#endif /* nsWMImport_h___ */
diff --git a/mailnews/import/winlivemail/nsWMSettings.cpp b/mailnews/import/winlivemail/nsWMSettings.cpp
new file mode 100644
index 000000000..383a31bb8
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMSettings.cpp
@@ -0,0 +1,758 @@
+/* -*- 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/. */
+
+/*
+
+ Windows Live Mail (Win32) settings
+
+*/
+
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsWMImport.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsIImportSettings.h"
+#include "nsWMSettings.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsMsgI18N.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsWMStringBundle.h"
+#include "WMDebugLog.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsIImapIncomingServer.h"
+#include "nsINntpIncomingServer.h"
+#include "stdlib.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIMutableArray.h"
+#include "nsIDOMDocument.h"
+#include "nsNetUtil.h"
+#include "nsIDOMNodeList.h"
+#include "nsIFileStreams.h"
+#include "nsIDOMParser.h"
+#include "nsIDOMElement.h"
+#include "nsTArray.h"
+#include <windows.h>
+#include "nsIWindowsRegKey.h"
+#include "nsCOMArray.h"
+#include "nsWMUtils.h"
+
+class WMSettings {
+public:
+ static bool DoImport(nsIMsgAccount **ppAccount);
+ static bool DoIMAPServer(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount);
+ static bool DoPOP3Server(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount);
+ static bool DoNNTPServer(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount);
+ static void SetIdentities(nsIMsgAccountManager *pMgr, nsIMsgAccount *pAcc,
+ nsIDOMDocument *xmlDoc, nsAutoString &userName,
+ int32_t authMethodIncoming, bool isNNTP);
+ static void SetSmtpServer(nsIDOMDocument *xmlDoc, nsIMsgIdentity *id,
+ nsAutoString& inUserName, int32_t authMethodIncoming);
+};
+
+static int32_t checkNewMailTime;// WM global setting, let's default to 30
+static bool checkNewMail; // WM global setting, let's default to false
+ // This won't cause unwanted autodownloads-
+ // user can set prefs after import
+
+////////////////////////////////////////////////////////////////////////
+nsresult nsWMSettings::Create(nsIImportSettings** aImport)
+{
+ NS_PRECONDITION(aImport != nullptr, "null ptr");
+ if (! aImport)
+ return NS_ERROR_NULL_POINTER;
+
+ *aImport = new nsWMSettings();
+ if (! *aImport)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aImport);
+ return NS_OK;
+}
+
+nsWMSettings::nsWMSettings()
+{
+}
+
+nsWMSettings::~nsWMSettings()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsWMSettings, nsIImportSettings)
+
+NS_IMETHODIMP nsWMSettings::AutoLocate(char16_t **description,
+ nsIFile **location, bool *_retval)
+{
+ NS_PRECONDITION(description != nullptr, "null ptr");
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!description || !_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *description = nsWMStringBundle::GetStringByID(WMIMPORT_NAME);
+ *_retval = false;
+
+ if (location)
+ *location = nullptr;
+ nsCOMPtr<nsIWindowsRegKey> key;
+ if (NS_SUCCEEDED(nsWMUtils::FindWMKey(getter_AddRefs(key))))
+ *_retval = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMSettings::SetLocation(nsIFile *location)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMSettings::Import(nsIMsgAccount **localMailAccount,
+ bool *_retval)
+{
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+
+ if (WMSettings::DoImport(localMailAccount)) {
+ *_retval = true;
+ IMPORT_LOG0("Settings import appears successful\n");
+ }
+ else {
+ *_retval = false;
+ IMPORT_LOG0("Settings import returned FALSE\n");
+ }
+
+ return NS_OK;
+}
+
+bool WMSettings::DoImport(nsIMsgAccount **ppAccount)
+{
+ // do the windows registry stuff first
+ nsCOMPtr<nsIWindowsRegKey> key;
+ if (NS_FAILED(nsWMUtils::FindWMKey(getter_AddRefs(key)))) {
+ IMPORT_LOG0("*** Error finding Windows Live Mail registry account keys\n");
+ return false;
+ }
+ // 'poll for messages' setting in WM is a global setting-Like OE
+ // for all accounts dword ==0xffffffff for don't poll else 1/60000 = minutes
+ checkNewMailTime = 30;
+ checkNewMail = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> subKey;
+ if (NS_SUCCEEDED(key->OpenChild(NS_LITERAL_STRING("mail"),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+ getter_AddRefs(subKey)))) {
+ uint32_t dwordResult = -1;
+ rv = subKey->ReadIntValue(NS_LITERAL_STRING("Poll For Mail"), &dwordResult); // reg_dword
+ subKey->Close();
+ if (NS_SUCCEEDED(rv) && dwordResult != -1){
+ checkNewMail = true;
+ checkNewMailTime = dwordResult / 60000;
+ }
+ }
+ // these are in main windowsmail key and if they don't exist-not to worry
+ // (less than 64 chars) e.g. account{4A18B81E-83CA-472A-8D7F-5301C0B97B8D}.oeaccount
+ nsAutoString defMailAcct, defNewsAcct;
+ key->ReadStringValue(NS_LITERAL_STRING("Default Mail Account"), defMailAcct); // ref_sz
+ key->ReadStringValue(NS_LITERAL_STRING("Default News Account"), defNewsAcct); // ref_sz
+
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create an account manager!\n");
+ return false;
+ }
+
+ nsCOMArray<nsIFile> fileArray;
+ if (NS_FAILED(nsWMUtils::GetOEAccountFiles(fileArray))) {
+ IMPORT_LOG0("*** Failed to get .oeaccount file!\n");
+ return false;
+ }
+
+ // Loop through *.oeaccounts files looking for POP3 & IMAP & NNTP accounts
+ // Ignore LDAP for now!
+ int accounts = 0;
+ nsCOMPtr<nsIDOMDocument> xmlDoc;
+
+ for (int32_t i = fileArray.Count() - 1 ; i >= 0; i--){
+ nsWMUtils::MakeXMLdoc(getter_AddRefs(xmlDoc), fileArray[i]);
+
+ nsAutoCString name;
+ fileArray[i]->GetNativeLeafName(name);
+ nsAutoString value;
+ nsCOMPtr<nsIMsgAccount> anAccount;
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_Server",
+ value)))
+ if (DoIMAPServer(accMgr, xmlDoc, value, getter_AddRefs(anAccount)))
+ accounts++;
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "NNTP_Server",
+ value)))
+ if (DoNNTPServer(accMgr, xmlDoc, value, getter_AddRefs(anAccount)))
+ accounts++;
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_Server",
+ value)))
+ if (DoPOP3Server(accMgr, xmlDoc, value, getter_AddRefs(anAccount)))
+ accounts++;
+
+ if (anAccount) {
+ nsString name;
+ // Is this the default account?
+ fileArray[i]->GetLeafName(name);
+ if (defMailAcct.Equals(name))
+ accMgr->SetDefaultAccount(anAccount);
+ }
+ }
+
+ // Now save the new acct info to pref file.
+ rv = accMgr->SaveAccountInfo();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file");
+
+ return accounts != 0;
+}
+
+bool WMSettings::DoIMAPServer(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount)
+{
+ int32_t authMethod; // Secure Password Authentication (SPA)
+ nsresult errorCode;
+ if (ppAccount)
+ *ppAccount = nullptr;
+
+ nsAutoString userName, value;
+ if (NS_FAILED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_User_Name",
+ userName)))
+ return false;
+ bool result = false;
+ // I now have a user name/server name pair, find out if it already exists?
+ nsCOMPtr<nsIMsgIncomingServer> in;
+ nsresult rv = pMgr->FindServer(NS_ConvertUTF16toUTF8(userName),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("imap"),
+ getter_AddRefs(in));
+ if (NS_FAILED(rv) || (in == nullptr)) {
+ // Create the incoming server and an account for it?
+ rv = pMgr->CreateIncomingServer(NS_ConvertUTF16toUTF8(userName),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("imap"),
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv) && in) {
+ nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(in);
+ if (!imapServer){
+ IMPORT_LOG1("*** Failed to create nsIImapIncomingServer for %S!\n",
+ serverName.get());
+ return false;
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_Root_Folder",
+ value))) {
+ imapServer->SetServerDirectory(NS_ConvertUTF16toUTF8(value));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_Secure_Connection",
+ value))) {
+ if (value.ToInteger(&errorCode, 16))
+ in->SetSocketType(nsMsgSocketType::SSL);
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_Use_Sicily",
+ value))) {
+ bool secAuth = (bool)value.ToInteger(&errorCode, 16);
+ authMethod = secAuth ? nsMsgAuthMethod::secure :
+ nsMsgAuthMethod::passwordCleartext;
+ in->SetAuthMethod(authMethod);
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "IMAP_Port",
+ value))) {
+ in->SetPort(value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Account_Name",
+ value))) {
+ rv = in->SetPrettyName(value);
+ }
+ in->SetDoBiff(checkNewMail);
+ in->SetBiffMinutes(checkNewMailTime);
+
+ IMPORT_LOG2("Created IMAP server named: %S, userName: %S\n",
+ serverName.get(), userName.get());
+
+ // We have a server, create an account.
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->CreateAccount(getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ rv = account->SetIncomingServer(in);
+
+ IMPORT_LOG0("Created an account and set the IMAP server "
+ "as the incoming server\n");
+
+ // Fiddle with the identities
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, false);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ }
+ else if (NS_SUCCEEDED(rv) && in) {
+ // for an existing server we create another identity,
+ // TB lists under 'manage identities'
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->FindAccountForServer(in, getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ IMPORT_LOG0("Created an identity and added to existing "
+ "IMAP incoming server\n");
+ // Fiddle with the identities
+ in->GetAuthMethod(&authMethod);
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, false);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ else
+ result = true;
+ return result;
+}
+
+bool WMSettings::DoPOP3Server(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount)
+{
+ int32_t authMethod; // Secure Password Authentication (SPA)
+ nsresult errorCode;
+ if (ppAccount)
+ *ppAccount = nullptr;
+
+ nsAutoString userName, value;
+ if (NS_FAILED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_User_Name",
+ userName)))
+ return false;
+ bool result = false;
+ // I now have a user name/server name pair, find out if it already exists?
+ nsCOMPtr<nsIMsgIncomingServer> in;
+ nsresult rv = pMgr->FindServer(NS_ConvertUTF16toUTF8(userName),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("pop3"),
+ getter_AddRefs(in));
+ if (NS_FAILED(rv) || (in == nullptr)) {
+ // Create the incoming server and an account for it?
+ rv = pMgr->CreateIncomingServer(NS_ConvertUTF16toUTF8(userName),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("pop3"),
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv) && in) {
+ nsCOMPtr<nsIPop3IncomingServer> pop3Server = do_QueryInterface(in);
+ if (!pop3Server){
+ IMPORT_LOG1("*** Failed to create nsIPop3IncomingServer for %S!\n",
+ serverName.get());
+ return false;
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_Secure_Connection",
+ value)) &&
+ value.ToInteger(&errorCode, 16)) {
+ in->SetSocketType(nsMsgSocketType::SSL);
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_Use_Sicily",
+ value))) {
+ bool secAuth = (bool)value.ToInteger(&errorCode, 16);
+ authMethod = secAuth ? nsMsgAuthMethod::secure :
+ nsMsgAuthMethod::passwordCleartext;
+ in->SetAuthMethod(authMethod);
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_Port",
+ value))) {
+ in->SetPort(value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "POP3_Skip_Account",
+ value))) {
+ if (!value.IsEmpty())
+ // OE:0=='Include this account when receiving mail or synchronizing'==
+ // TB:1==ActMgr:Server:advanced:Include this server when getting new mail
+ pop3Server->SetDeferGetNewMail(value.ToInteger(&errorCode, 16) == 0);
+ else
+ pop3Server->SetDeferGetNewMail(false);
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Leave_Mail_On_Server",
+ value))) {
+ pop3Server->SetLeaveMessagesOnServer((bool)value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Remove_When_Deleted",
+ value))) {
+ pop3Server->SetDeleteMailLeftOnServer((bool)value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Remove_When_Expired",
+ value))) {
+ pop3Server->SetDeleteByAgeFromServer((bool)value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Expire_Days",
+ value))) {
+ pop3Server->SetNumDaysToLeaveOnServer(value.ToInteger(&errorCode, 16));
+ }
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Account_Name",
+ value))) {
+ rv = in->SetPrettyName(value);
+ }
+
+ in->SetDoBiff(checkNewMail);
+ in->SetBiffMinutes(checkNewMailTime);
+
+ // set local folders as the Inbox to use for this POP3 server
+ nsCOMPtr<nsIMsgIncomingServer> localFoldersServer;
+ pMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
+ if (!localFoldersServer) {
+ // XXX: We may need to move this local folder creation
+ // code to the generic nsImportSettings code
+ // if the other import modules end up needing to do this too.
+ // if Local Folders does not exist already, create it
+ rv = pMgr->CreateLocalMailAccount();
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to create Local Folders!\n");
+ return false;
+ }
+ pMgr->GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
+ }
+
+ // now get the account for this server
+ nsCOMPtr<nsIMsgAccount> localFoldersAccount;
+ pMgr->FindAccountForServer(localFoldersServer,
+ getter_AddRefs(localFoldersAccount));
+ if (localFoldersAccount) {
+ nsCString localFoldersAcctKey;
+ localFoldersAccount->GetKey(localFoldersAcctKey);
+ pop3Server->SetDeferredToAccount(localFoldersAcctKey);
+ }
+
+ IMPORT_LOG2("Created POP3 server named: %S, userName: %S\n",
+ serverName.get(), userName.get());
+
+ // We have a server, create an account.
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->CreateAccount(getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ rv = account->SetIncomingServer(in);
+ IMPORT_LOG0("Created a new account and set the incoming "
+ "server to the POP3 server.\n");
+
+ // Fiddle with the identities
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, false);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ }
+ else if (NS_SUCCEEDED(rv) && in) {
+ IMPORT_LOG2("Existing POP3 server named: %S, userName: %S\n",
+ serverName.get(), userName.get());
+ // for an existing server we create another identity,
+ // TB listed under 'manage identities'
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->FindAccountForServer(in, getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ IMPORT_LOG0("Created identity and added to existing POP3 incoming server.\n");
+ // Fiddle with the identities
+ in->GetAuthMethod(&authMethod);
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, false);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ else
+ result = true;
+ return result;
+}
+
+bool WMSettings::DoNNTPServer(nsIMsgAccountManager *pMgr,
+ nsIDOMDocument *xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount **ppAccount)
+{
+ int32_t authMethod;
+ nsresult errorCode;
+ if (ppAccount)
+ *ppAccount = nullptr;
+
+ nsAutoString userName, value;
+ // this only exists if NNTP server requires it or not, anonymous login
+ nsWMUtils::GetValueForTag(xmlDoc, "NNTP_User_Name", userName);
+ bool result = false;
+
+ // I now have a user name/server name pair, find out if it already exists?
+ // NNTP can have empty user name. This is wild card in findserver
+ nsCOMPtr<nsIMsgIncomingServer> in;
+ nsresult rv = pMgr->FindServer(EmptyCString(),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("nntp"),
+ getter_AddRefs(in));
+ if (NS_FAILED(rv) || (in == nullptr)) {
+ // Create the incoming server and an account for it?
+ rv = pMgr->CreateIncomingServer(nsDependentCString(""),
+ NS_ConvertUTF16toUTF8(serverName),
+ NS_LITERAL_CSTRING("nntp"),
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv) && in) {
+
+ nsCOMPtr<nsINntpIncomingServer> nntpServer = do_QueryInterface(in);
+ if (!nntpServer) {
+ IMPORT_LOG1("*** Failed to create nsINnntpIncomingServer for %S!\n",
+ serverName.get());
+ return false;
+ }
+ if (!userName.IsEmpty()) { // if username req'd then auth req'd
+ nntpServer->SetPushAuth(true);
+ in->SetUsername(NS_ConvertUTF16toUTF8(userName));
+ }
+
+ nsAutoString value;
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "NNTP_Port",
+ value))) {
+ in->SetPort(value.ToInteger(&errorCode, 16));
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "Account_Name",
+ value))) {
+ in->SetPrettyName(value);
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "NNTP_Use_Sicily",
+ value))) {
+ bool secAuth = (bool)value.ToInteger(&errorCode, 16);
+ authMethod = secAuth ? nsMsgAuthMethod::secure :
+ nsMsgAuthMethod::passwordCleartext;
+ in->SetAuthMethod(authMethod);
+ }
+
+ IMPORT_LOG2("Created NNTP server named: %S, userName: %S\n",
+ serverName.get(), userName.get());
+
+ // We have a server, create an account.
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->CreateAccount(getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ rv = account->SetIncomingServer(in);
+
+ IMPORT_LOG0("Created an account and set the NNTP server "
+ "as the incoming server\n");
+
+ // Fiddle with the identities
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, true);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ }
+ else if (NS_SUCCEEDED(rv) && in) {
+ // for the existing server...
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = pMgr->FindAccountForServer(in, getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account) {
+ IMPORT_LOG0("Using existing account and set the "
+ "NNTP server as the incoming server\n");
+ // Fiddle with the identities
+ in->GetAuthMethod(&authMethod);
+ SetIdentities(pMgr, account, xmlDoc, userName, authMethod, true);
+ result = true;
+ if (ppAccount)
+ account.forget(ppAccount);
+ }
+ }
+ else
+ result = true;
+ return result;
+}
+
+void WMSettings::SetIdentities(nsIMsgAccountManager *pMgr, nsIMsgAccount *pAcc,
+ nsIDOMDocument *xmlDoc, nsAutoString &inUserName,
+ int32_t authMethodIncoming, bool isNNTP)
+{
+ // Get the relevant information for an identity
+ nsresult rv;
+ nsAutoString value;
+
+ nsCOMPtr<nsIMsgIdentity> id;
+ rv = pMgr->CreateIdentity(getter_AddRefs(id));
+ if (id) {
+ IMPORT_LOG0("Created identity and added to the account\n");
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ isNNTP ?
+ "NNTP_Display_Name" :
+ "SMTP_Display_Name",
+ value))) {
+ id->SetFullName(value);
+ IMPORT_LOG1("\tname: %S\n", value.get());
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ isNNTP ?
+ "NNTP_Organization_Name" :
+ "SMTP_Organization_Name",
+ value))) {
+ id->SetOrganization(value);
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ isNNTP ?
+ "NNTP_Email_Address" :
+ "SMTP_Email_Address",
+ value))) {
+ id->SetEmail(NS_ConvertUTF16toUTF8(value));
+ IMPORT_LOG1("\temail: %S\n", value.get());
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ isNNTP ?
+ "NNTP_Reply_To_Email_Address" :
+ "SMTP_Reply_To_Email_Address",
+ value))) {
+ id->SetReplyTo(NS_ConvertUTF16toUTF8(value));
+ }
+
+ // Windows users are used to top style quoting.
+ id->SetReplyOnTop(isNNTP ? 0 : 1);
+ pAcc->AddIdentity(id);
+ }
+
+ if (!isNNTP) // NNTP does not use SMTP in OE or TB
+ SetSmtpServer(xmlDoc, id, inUserName, authMethodIncoming);
+}
+
+void WMSettings::SetSmtpServer(nsIDOMDocument *xmlDoc, nsIMsgIdentity *id,
+ nsAutoString& inUserName, int32_t authMethodIncoming)
+{
+ nsresult errorCode;
+
+ // set the id.smtpserver accordingly
+ if (!id)
+ return;
+ nsCString smtpServerKey, userName;
+ nsAutoString value, smtpName;
+ if (NS_FAILED(nsWMUtils::GetValueForTag(xmlDoc, "SMTP_Server", smtpName)))
+ return;
+
+ // first we have to calculate the smtp user name which is based on sicily
+ // smtp user name depends on sicily which may or not exist
+ int32_t useSicily = 0;
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "SMTP_Use_Sicily",
+ value))) {
+ useSicily = (int32_t)value.ToInteger(&errorCode,16);
+ }
+ switch (useSicily) {
+ case 1 : case 3 :
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "SMTP_User_Name",
+ value))) {
+ CopyUTF16toUTF8(value, userName);
+ }
+ else {
+ CopyUTF16toUTF8(inUserName, userName);
+ }
+ break;
+ case 2 :
+ CopyUTF16toUTF8(inUserName, userName);
+ break;
+ default :
+ break; // initial userName == ""
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISmtpService>
+ smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && smtpService) {
+ nsCOMPtr<nsISmtpServer> extgServer;
+ // don't try to make another server
+ // regardless if username doesn't match
+ rv = smtpService->FindServer(userName.get(),
+ NS_ConvertUTF16toUTF8(smtpName).get(),
+ getter_AddRefs(extgServer));
+ if (NS_SUCCEEDED(rv) && extgServer) {
+ // set our account keyed to this smptserver key
+ extgServer->GetKey(getter_Copies(smtpServerKey));
+ id->SetSmtpServerKey(smtpServerKey);
+
+ IMPORT_LOG1("SMTP server already exists: %S\n", smtpName);
+ }
+ else {
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpService->CreateServer(getter_AddRefs(smtpServer));
+ if (NS_SUCCEEDED(rv) && smtpServer) {
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "SMTP_Port",
+ value))) {
+ smtpServer->SetPort(value.ToInteger(&errorCode,16));
+ }
+
+ if (NS_SUCCEEDED(nsWMUtils::GetValueForTag(xmlDoc,
+ "SMTP_Secure_Connection",
+ value))) {
+ if (value.ToInteger(&errorCode, 16) == 1)
+ smtpServer->SetSocketType(nsMsgSocketType::SSL);
+ else
+ smtpServer->SetSocketType(nsMsgSocketType::plain);
+ }
+ smtpServer->SetUsername(userName);
+ switch (useSicily) {
+ case 1 :
+ smtpServer->SetAuthMethod(nsMsgAuthMethod::secure);
+ break;
+ case 2 : // requires SMTP authentication to use the incoming server settings
+ smtpServer->SetAuthMethod(authMethodIncoming);
+ break;
+ case 3 :
+ smtpServer->SetAuthMethod(nsMsgAuthMethod::passwordCleartext);
+ break;
+ default:
+ smtpServer->SetAuthMethod(nsMsgAuthMethod::none);
+ }
+
+ smtpServer->SetHostname(NS_ConvertUTF16toUTF8(smtpName));
+
+ smtpServer->GetKey(getter_Copies(smtpServerKey));
+ id->SetSmtpServerKey(smtpServerKey);
+
+ IMPORT_LOG1("Created new SMTP server: %S\n", smtpName);
+ }
+ }
+ }
+}
diff --git a/mailnews/import/winlivemail/nsWMSettings.h b/mailnews/import/winlivemail/nsWMSettings.h
new file mode 100644
index 000000000..3a17e7999
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMSettings.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWMSettings_h___
+#define nsWMSettings_h___
+
+#include "nsIImportSettings.h"
+
+class nsWMSettings : public nsIImportSettings {
+public:
+ nsWMSettings();
+ static nsresult Create(nsIImportSettings** aImport);
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTSETTINGS
+
+private:
+ virtual ~nsWMSettings();
+};
+
+#endif /* nsWMSettings_h___ */
diff --git a/mailnews/import/winlivemail/nsWMStringBundle.cpp b/mailnews/import/winlivemail/nsWMStringBundle.cpp
new file mode 100644
index 000000000..8edd21513
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMStringBundle.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "prprf.h"
+#include "prmem.h"
+#include "nsCOMPtr.h"
+#include "nsMsgUtils.h"
+#include "nsIStringBundle.h"
+#include "nsWMStringBundle.h"
+#include "nsIServiceManager.h"
+#include "nsIURI.h"
+#include "mozilla/Services.h"
+
+#define WM_MSGS_URL "chrome://messenger/locale/wmImportMsgs.properties"
+
+nsIStringBundle * nsWMStringBundle::m_pBundle = nullptr;
+
+nsIStringBundle *nsWMStringBundle::GetStringBundle(void)
+{
+ if (m_pBundle)
+ return m_pBundle;
+
+ char* propertyURL = WM_MSGS_URL;
+ nsIStringBundle* sBundle = nullptr;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ if (sBundleService) {
+ sBundleService->CreateBundle(propertyURL, &sBundle);
+ }
+
+ m_pBundle = sBundle;
+
+ return sBundle;
+}
+
+void nsWMStringBundle::GetStringByID(int32_t stringID, nsString& result)
+{
+ char16_t *ptrv = GetStringByID(stringID);
+ result = ptrv;
+ FreeString(ptrv);
+}
+
+char16_t *nsWMStringBundle::GetStringByID(int32_t stringID)
+{
+ if (!m_pBundle)
+ m_pBundle = GetStringBundle();
+
+ if (m_pBundle) {
+ char16_t *ptrv = nullptr;
+ nsresult rv = m_pBundle->GetStringFromID(stringID, &ptrv);
+
+ if (NS_SUCCEEDED(rv) && ptrv)
+ return ptrv;
+ }
+
+ nsString resultString;
+ resultString.AppendLiteral("[StringID ");
+ resultString.AppendInt(stringID);
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
+
+void nsWMStringBundle::Cleanup(void)
+{
+ if (m_pBundle)
+ m_pBundle->Release();
+ m_pBundle = nullptr;
+}
diff --git a/mailnews/import/winlivemail/nsWMStringBundle.h b/mailnews/import/winlivemail/nsWMStringBundle.h
new file mode 100644
index 000000000..414d66435
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMStringBundle.h
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsWMStringBundle_H__
+#define _nsWMStringBundle_H__
+
+#include "nsStringGlue.h"
+
+class nsIStringBundle;
+
+class nsWMStringBundle {
+public:
+ static char16_t * GetStringByID(int32_t stringID);
+ static void GetStringByID(int32_t stringID, nsString& result);
+ static nsIStringBundle * GetStringBundle(void); // don't release
+ static void FreeString(char16_t *pStr) { NS_Free(pStr);}
+ static void Cleanup(void);
+
+private:
+ static nsIStringBundle * m_pBundle;
+};
+
+
+
+#define WMIMPORT_NAME 2000
+#define WMIMPORT_DESCRIPTION 2001
+#define WMIMPORT_MAILBOX_SUCCESS 2002
+#define WMIMPORT_MAILBOX_BADPARAM 2003
+#define WMIMPORT_MAILBOX_BADSOURCEFILE 2004
+#define WMIMPORT_MAILBOX_CONVERTERROR 2005
+#define WMIMPORT_DEFAULT_NAME 2006
+#define WMIMPORT_AUTOFIND 2007
+#define WMIMPORT_ADDRESS_SUCCESS 2008
+#define WMIMPORT_ADDRESS_CONVERTERROR 2009
+#define WMIMPORT_ADDRESS_BADPARAM 2010
+
+#endif /* _nsWMStringBundle_H__ */
diff --git a/mailnews/import/winlivemail/nsWMUtils.cpp b/mailnews/import/winlivemail/nsWMUtils.cpp
new file mode 100644
index 000000000..3e60597b8
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMUtils.cpp
@@ -0,0 +1,164 @@
+/* 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 "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetCID.h"
+#include "nsStringGlue.h"
+#include "nsWMUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMParser.h"
+#include "nsIFileStreams.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "WMDebugLog.h"
+#include "prio.h"
+
+nsresult
+nsWMUtils::FindWMKey(nsIWindowsRegKey **aKey)
+{
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> key =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING("Software\\Microsoft\\Windows Live Mail"),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*aKey = key);
+ return rv;
+ }
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ NS_LITERAL_STRING("Software\\Microsoft\\Windows Mail"),
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+ key.forget(aKey);
+ return rv;
+}
+
+nsresult
+nsWMUtils::GetRootFolder(nsIFile **aRootFolder)
+{
+ nsCOMPtr<nsIWindowsRegKey> key;
+ if (NS_FAILED(nsWMUtils::FindWMKey(getter_AddRefs(key)))) {
+ IMPORT_LOG0("*** Error finding Windows Live Mail registry account keys\n");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ // This is essential to proceed; it is the location on disk of xml-type account files;
+ // it is in reg_expand_sz so it will need expanding to absolute path.
+ nsString storeRoot;
+ nsresult rv = key->ReadStringValue(NS_LITERAL_STRING("Store Root"), storeRoot);
+ key->Close(); // Finished with windows registry key. We do not want to return before this closing
+ if (NS_FAILED(rv) || storeRoot.IsEmpty()) {
+ IMPORT_LOG0("*** Error finding Windows Live Mail Store Root\n");
+ return rv;
+ }
+
+ uint32_t size = ::ExpandEnvironmentStringsW((LPCWSTR)storeRoot.get(), nullptr, 0);
+ nsString expandedStoreRoot;
+ expandedStoreRoot.SetLength(size - 1);
+ if (expandedStoreRoot.Length() != size - 1)
+ return NS_ERROR_FAILURE;
+ ::ExpandEnvironmentStringsW((LPCWSTR)storeRoot.get(),
+ (LPWSTR)expandedStoreRoot.BeginWriting(),
+ size);
+ storeRoot = expandedStoreRoot;
+
+ nsCOMPtr<nsIFile> rootFolder(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = rootFolder->InitWithPath(storeRoot);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rootFolder.forget(aRootFolder);
+
+ return NS_OK;
+}
+
+nsresult
+nsWMUtils::GetOEAccountFiles(nsCOMArray<nsIFile> &aFileArray)
+{
+ nsCOMPtr<nsIFile> rootFolder;
+
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return GetOEAccountFilesInFolder(rootFolder, aFileArray);
+}
+
+nsresult
+nsWMUtils::GetOEAccountFilesInFolder(nsIFile *aFolder,
+ nsCOMArray<nsIFile> &aFileArray)
+{
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ nsresult rv = aFolder->GetDirectoryEntries(getter_AddRefs(entries));
+ if (NS_FAILED(rv) || !entries)
+ return NS_ERROR_FAILURE;
+
+ bool hasMore;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ rv = entries->GetNext(getter_AddRefs(supports));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDirectory;
+ rv = file->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isDirectory) {
+ GetOEAccountFilesInFolder(file, aFileArray);
+ }
+ else {
+ nsString name;
+ rv = file->GetLeafName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (StringEndsWith(name, NS_LITERAL_STRING(".oeaccount")))
+ aFileArray.AppendObject(file);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsWMUtils::MakeXMLdoc(nsIDOMDocument **aXmlDoc,
+ nsIFile *aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFileInputStream> stream =
+ do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stream->Init(aFile, PR_RDONLY, -1, 0);
+ nsCOMPtr<nsIDOMParser> parser = do_CreateInstance(NS_DOMPARSER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t filesize;
+ aFile->GetFileSize(&filesize);
+ return parser->ParseFromStream(stream, nullptr, int32_t(filesize),
+ "application/xml", aXmlDoc);
+}
+
+nsresult
+nsWMUtils::GetValueForTag(nsIDOMDocument *aXmlDoc,
+ const char *aTagName,
+ nsAString &aValue)
+{
+ nsAutoString tagName;
+ tagName.AssignASCII(aTagName);
+ nsCOMPtr<nsIDOMNodeList> list;
+ if (NS_FAILED(aXmlDoc->GetElementsByTagName(tagName, getter_AddRefs(list))))
+ return NS_ERROR_FAILURE;
+ nsCOMPtr<nsIDOMNode> domNode;
+ list->Item(0, getter_AddRefs(domNode));
+ if (!domNode)
+ return NS_ERROR_FAILURE;
+ return domNode->GetTextContent(aValue);
+}
+
diff --git a/mailnews/import/winlivemail/nsWMUtils.h b/mailnews/import/winlivemail/nsWMUtils.h
new file mode 100644
index 000000000..e1bf54286
--- /dev/null
+++ b/mailnews/import/winlivemail/nsWMUtils.h
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWMUtils_h___
+#define nsWMUtils_h___
+
+#include <windows.h>
+#include "nsIWindowsRegKey.h"
+
+class nsIDOMDocument;
+
+class nsWMUtils {
+public:
+ static nsresult FindWMKey(nsIWindowsRegKey **aKey);
+ static nsresult GetRootFolder(nsIFile **aRootFolder);
+ static nsresult GetOEAccountFiles(nsCOMArray<nsIFile> &aFileArray);
+ static nsresult GetOEAccountFilesInFolder(nsIFile *aFolder,
+ nsCOMArray<nsIFile> &aFileArray);
+ static nsresult MakeXMLdoc(nsIDOMDocument **aXmlDoc,
+ nsIFile *aFile);
+ static nsresult GetValueForTag(nsIDOMDocument *aXmlDoc,
+ const char *aTagName,
+ nsAString &aValue);
+};
+
+#endif /* nsWMUtils_h___ */