summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/import/src
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/import/src')
-rw-r--r--comm/mailnews/import/src/ImportCharSet.cpp58
-rw-r--r--comm/mailnews/import/src/ImportCharSet.h201
-rw-r--r--comm/mailnews/import/src/ImportDebug.h25
-rw-r--r--comm/mailnews/import/src/ImportOutFile.cpp257
-rw-r--r--comm/mailnews/import/src/ImportOutFile.h94
-rw-r--r--comm/mailnews/import/src/ImportTranslate.cpp100
-rw-r--r--comm/mailnews/import/src/ImportTranslate.h23
-rw-r--r--comm/mailnews/import/src/MapiApi.cpp1842
-rw-r--r--comm/mailnews/import/src/MapiApi.h284
-rw-r--r--comm/mailnews/import/src/MapiDbgLog.h36
-rw-r--r--comm/mailnews/import/src/MapiMessage.cpp1383
-rw-r--r--comm/mailnews/import/src/MapiMessage.h290
-rw-r--r--comm/mailnews/import/src/MapiMimeTypes.cpp81
-rw-r--r--comm/mailnews/import/src/MapiMimeTypes.h27
-rw-r--r--comm/mailnews/import/src/MapiTagStrs.cpp1473
-rw-r--r--comm/mailnews/import/src/MorkImport.cpp343
-rw-r--r--comm/mailnews/import/src/MorkImport.h50
-rw-r--r--comm/mailnews/import/src/SeamonkeyImport.jsm253
-rw-r--r--comm/mailnews/import/src/ThunderbirdImport.jsm145
-rw-r--r--comm/mailnews/import/src/components.conf104
-rw-r--r--comm/mailnews/import/src/moz.build86
-rw-r--r--comm/mailnews/import/src/nsAddrDatabase.cpp864
-rw-r--r--comm/mailnews/import/src/nsAddrDatabase.h158
-rw-r--r--comm/mailnews/import/src/nsAppleMailImport.cpp609
-rw-r--r--comm/mailnews/import/src/nsAppleMailImport.h86
-rw-r--r--comm/mailnews/import/src/nsBeckyAddressBooks.cpp311
-rw-r--r--comm/mailnews/import/src/nsBeckyAddressBooks.h36
-rw-r--r--comm/mailnews/import/src/nsBeckyFilters.cpp711
-rw-r--r--comm/mailnews/import/src/nsBeckyFilters.h73
-rw-r--r--comm/mailnews/import/src/nsBeckyImport.cpp143
-rw-r--r--comm/mailnews/import/src/nsBeckyImport.h38
-rw-r--r--comm/mailnews/import/src/nsBeckyMail.cpp566
-rw-r--r--comm/mailnews/import/src/nsBeckyMail.h41
-rw-r--r--comm/mailnews/import/src/nsBeckySettings.cpp379
-rw-r--r--comm/mailnews/import/src/nsBeckySettings.h50
-rw-r--r--comm/mailnews/import/src/nsBeckyStringBundle.cpp55
-rw-r--r--comm/mailnews/import/src/nsBeckyStringBundle.h32
-rw-r--r--comm/mailnews/import/src/nsBeckyUtils.cpp302
-rw-r--r--comm/mailnews/import/src/nsBeckyUtils.h35
-rw-r--r--comm/mailnews/import/src/nsEmlxHelperUtils.h61
-rw-r--r--comm/mailnews/import/src/nsEmlxHelperUtils.mm230
-rw-r--r--comm/mailnews/import/src/nsImportABDescriptor.cpp19
-rw-r--r--comm/mailnews/import/src/nsImportABDescriptor.h100
-rw-r--r--comm/mailnews/import/src/nsImportAddressBooks.cpp571
-rw-r--r--comm/mailnews/import/src/nsImportAddressBooks.h81
-rw-r--r--comm/mailnews/import/src/nsImportEmbeddedImageData.cpp52
-rw-r--r--comm/mailnews/import/src/nsImportEmbeddedImageData.h31
-rw-r--r--comm/mailnews/import/src/nsImportEncodeScan.cpp334
-rw-r--r--comm/mailnews/import/src/nsImportEncodeScan.h39
-rw-r--r--comm/mailnews/import/src/nsImportFieldMap.cpp325
-rw-r--r--comm/mailnews/import/src/nsImportFieldMap.h44
-rw-r--r--comm/mailnews/import/src/nsImportMail.cpp1007
-rw-r--r--comm/mailnews/import/src/nsImportMail.h86
-rw-r--r--comm/mailnews/import/src/nsImportMailboxDescriptor.cpp25
-rw-r--r--comm/mailnews/import/src/nsImportMailboxDescriptor.h94
-rw-r--r--comm/mailnews/import/src/nsImportScanFile.cpp154
-rw-r--r--comm/mailnews/import/src/nsImportScanFile.h56
-rw-r--r--comm/mailnews/import/src/nsImportService.cpp293
-rw-r--r--comm/mailnews/import/src/nsImportService.h54
-rw-r--r--comm/mailnews/import/src/nsImportStringBundle.cpp67
-rw-r--r--comm/mailnews/import/src/nsImportStringBundle.h43
-rw-r--r--comm/mailnews/import/src/nsImportTranslator.cpp308
-rw-r--r--comm/mailnews/import/src/nsImportTranslator.h87
-rw-r--r--comm/mailnews/import/src/nsOutlookCompose.cpp669
-rw-r--r--comm/mailnews/import/src/nsOutlookCompose.h63
-rw-r--r--comm/mailnews/import/src/nsOutlookImport.cpp522
-rw-r--r--comm/mailnews/import/src/nsOutlookImport.h38
-rw-r--r--comm/mailnews/import/src/nsOutlookMail.cpp830
-rw-r--r--comm/mailnews/import/src/nsOutlookMail.h84
-rw-r--r--comm/mailnews/import/src/nsOutlookSettings.cpp500
-rw-r--r--comm/mailnews/import/src/nsOutlookSettings.h27
-rw-r--r--comm/mailnews/import/src/nsOutlookStringBundle.cpp53
-rw-r--r--comm/mailnews/import/src/nsOutlookStringBundle.h36
-rw-r--r--comm/mailnews/import/src/nsTextAddress.cpp426
-rw-r--r--comm/mailnews/import/src/nsTextAddress.h60
-rw-r--r--comm/mailnews/import/src/nsTextImport.cpp646
-rw-r--r--comm/mailnews/import/src/nsTextImport.h40
-rw-r--r--comm/mailnews/import/src/nsVCardAddress.cpp151
-rw-r--r--comm/mailnews/import/src/nsVCardAddress.h28
-rw-r--r--comm/mailnews/import/src/nsVCardImport.cpp352
-rw-r--r--comm/mailnews/import/src/nsVCardImport.h39
-rw-r--r--comm/mailnews/import/src/nsWMImport.cpp199
-rw-r--r--comm/mailnews/import/src/nsWMImport.h38
-rw-r--r--comm/mailnews/import/src/nsWMSettings.cpp679
-rw-r--r--comm/mailnews/import/src/nsWMSettings.h22
-rw-r--r--comm/mailnews/import/src/nsWMStringBundle.cpp52
-rw-r--r--comm/mailnews/import/src/nsWMStringBundle.h36
-rw-r--r--comm/mailnews/import/src/nsWMUtils.cpp153
-rw-r--r--comm/mailnews/import/src/nsWMUtils.h23
-rw-r--r--comm/mailnews/import/src/rtfDecoder.cpp561
-rw-r--r--comm/mailnews/import/src/rtfDecoder.h21
-rw-r--r--comm/mailnews/import/src/rtfMailDecoder.cpp71
-rw-r--r--comm/mailnews/import/src/rtfMailDecoder.h44
93 files changed, 22268 insertions, 0 deletions
diff --git a/comm/mailnews/import/src/ImportCharSet.cpp b/comm/mailnews/import/src/ImportCharSet.cpp
new file mode 100644
index 0000000000..ea79221210
--- /dev/null
+++ b/comm/mailnews/import/src/ImportCharSet.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "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/comm/mailnews/import/src/ImportCharSet.h b/comm/mailnews/import/src/ImportCharSet.h
new file mode 100644
index 0000000000..0feb8d2a98
--- /dev/null
+++ b/comm/mailnews/import/src/ImportCharSet.h
@@ -0,0 +1,201 @@
+/* -*- 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/comm/mailnews/import/src/ImportDebug.h b/comm/mailnews/import/src/ImportDebug.h
new file mode 100644
index 0000000000..f1698ed442
--- /dev/null
+++ b/comm/mailnews/import/src/ImportDebug.h
@@ -0,0 +1,25 @@
+/* -*- 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
+
+#include "mozilla/Logging.h"
+extern mozilla::LazyLogModule
+ IMPORTLOGMODULE; // defined in nsImportService.cpp
+
+#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/comm/mailnews/import/src/ImportOutFile.cpp b/comm/mailnews/import/src/ImportOutFile.cpp
new file mode 100644
index 0000000000..3622b56ad7
--- /dev/null
+++ b/comm/mailnews/import/src/ImportOutFile.cpp
@@ -0,0 +1,257 @@
+/* -*- 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 "nsString.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/comm/mailnews/import/src/ImportOutFile.h b/comm/mailnews/import/src/ImportOutFile.h
new file mode 100644
index 0000000000..f682c4dc4f
--- /dev/null
+++ b/comm/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/comm/mailnews/import/src/ImportTranslate.cpp b/comm/mailnews/import/src/ImportTranslate.cpp
new file mode 100644
index 0000000000..64fc09bce5
--- /dev/null
+++ b/comm/mailnews/import/src/ImportTranslate.cpp
@@ -0,0 +1,100 @@
+/* -*- 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 "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);
+ }
+
+ // Unfortunately, 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/comm/mailnews/import/src/ImportTranslate.h b/comm/mailnews/import/src/ImportTranslate.h
new file mode 100644
index 0000000000..5e31207395
--- /dev/null
+++ b/comm/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 "nsString.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/comm/mailnews/import/src/MapiApi.cpp b/comm/mailnews/import/src/MapiApi.cpp
new file mode 100644
index 0000000000..5490b6bc11
--- /dev/null
+++ b/comm/mailnews/import/src/MapiApi.cpp
@@ -0,0 +1,1842 @@
+/* -*- 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 "MapiDbgLog.h"
+#include "MapiApi.h"
+
+#include <sstream>
+#include "rtfMailDecoder.h"
+
+#include "prprf.h"
+#include "nsMemory.h"
+#include "nsMsgUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsNativeCharsetUtils.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;
+/*
+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,
+ LPCTSTR 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 = ::LoadLibraryW(L"MAPI32.DLL");
+ if (!hInst) return FALSE;
+ FARPROC pProc = GetProcAddress(hInst, "MAPIGetNetscapeVersion");
+ if (pProc) {
+ ::FreeLibrary(hInst);
+ hInst = ::LoadLibraryW(L"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();
+ }
+}
+
+void CMapiApi::CStrToUnicode(const char* pStr, nsString& result) {
+ NS_CopyNativeToUnicode(nsDependentCString(pStr), result);
+}
+
+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,
+ reinterpret_cast<wchar_t*>(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));
+ // Despite being used as wchar_t*, result it allocated as "new char[]" in
+ // GetLargeProperty().
+ delete[] static_cast<char*>(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);
+ // Despite being used as wchar_t*, result it allocated as "new char[]" in
+ // GetLargeProperty().
+ delete[] static_cast<char*>(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);
+ const char* pStr = uid.ToString().get();
+ if (pStr) {
+ MAPI_TRACE2("%s %s\n", pTag, 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 += "...";
+ }
+ strVal.ReplaceSubstring("\r", "\\r");
+ strVal.ReplaceSubstring("\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.Assign('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.Append('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);
+ free(ansiStr);
+#endif
+ pFolder->GetFilePath(str);
+#ifdef MAPI_DEBUG
+ ansiStr = ToNewCString(str);
+ MAPI_TRACE2("depth=%d, filePath=%s\n", pFolder->GetDepth(), ansiStr);
+ 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;
+ m_lpMdb->StoreLogoff(&flags);
+ m_lpMdb->Release();
+ m_lpMdb = NULL;
+ }
+}
+
+void CMsgStore::SetEntryID(ULONG cbEid, LPENTRYID lpEid) {
+ 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;
+ 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/comm/mailnews/import/src/MapiApi.h b/comm/mailnews/import/src/MapiApi.h
new file mode 100644
index 0000000000..4d9dc7be2a
--- /dev/null
+++ b/comm/mailnews/import/src/MapiApi.h
@@ -0,0 +1,284 @@
+/* -*- 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 "nsString.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,
+ LPCTSTR 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();
+ explicit 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:
+ explicit 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/comm/mailnews/import/src/MapiDbgLog.h b/comm/mailnews/import/src/MapiDbgLog.h
new file mode 100644
index 0000000000..56580920ea
--- /dev/null
+++ b/comm/mailnews/import/src/MapiDbgLog.h
@@ -0,0 +1,36 @@
+/* -*- 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/comm/mailnews/import/src/MapiMessage.cpp b/comm/mailnews/import/src/MapiMessage.cpp
new file mode 100644
index 0000000000..af57aa1d4c
--- /dev/null
+++ b/comm/mailnews/import/src/MapiMessage.cpp
@@ -0,0 +1,1383 @@
+/* -*- 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 INITGUID
+# define INITGUID
+#endif
+
+#ifndef USES_IID_IMessage
+# define USES_IID_IMessage
+#endif
+
+#include "nscore.h"
+#include <time.h>
+#include "nsString.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsMsgUtils.h"
+#include "nsMimeTypes.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIOutputStream.h"
+
+#include "MapiDbgLog.h"
+#include "MapiApi.h"
+
+#include "MapiMimeTypes.h"
+
+#include "nsMsgI18N.h"
+#include "nsCRT.h"
+#include "nsNetUtil.h"
+#include "MapiMessage.h"
+
+#include "nsOutlookMail.h"
+
+#include "mozilla/Encoding.h"
+
+#include <stdlib.h>
+#include <tuple>
+
+// 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
+ 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;
+
+ 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.
+ 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
+}
+
+// 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;
+
+ auto encoding =
+ mozilla::Encoding::ForLabelNoReplacement(nsDependentCString(charset));
+ if (!encoding) return false;
+ auto encoder = encoding->NewEncoder();
+
+ uint8_t buffer[512];
+ auto src = mozilla::Span(m_body);
+ auto dst = mozilla::Span(buffer);
+ while (true) {
+ uint32_t result;
+ size_t read;
+ std::tie(result, read, std::ignore) =
+ encoder->EncodeFromUTF16WithoutReplacement(src, dst, false);
+ if (result == mozilla::kInputEmpty) {
+ // All converted successfully.
+ break;
+ } else if (result != mozilla::kOutputFull) {
+ // Didn't use all the input but the output isn't full, hence
+ // there was an unencodable character.
+ return false;
+ }
+ src = src.From(read);
+ }
+
+ 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 initial string 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(char16ptr_t(chset_pos), char16ptr_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(u"<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) // Everything else failed, let's use the lossless
+ // utf-8...
+ m_mimeCharset.AssignLiteral("utf-8");
+
+ MAPI_DUMP_STRING(m_body.get());
+ MAPI_TRACE0("\r\n");
+
+ return true;
+}
+
+void CMapiMessage::GetBody(nsCString& dest) const {
+ nsMsgI18NConvertFromUnicode(m_mimeCharset, 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) {
+ 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 = ImportMailboxRunnable::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);
+
+ nsString tmpPath = _tmp_file->NativePath();
+ // We have to use native charset unless we migrate to Outlook 2013 "W" API.
+ nsCString tmpNativePath;
+ rv = NS_CopyUnicodeToNative(tmpPath, tmpNativePath);
+ NS_ENSURE_SUCCESS(rv, false);
+ LPSTREAM lpStreamFile;
+ HRESULT hr = CMapiApi::OpenStreamOnFile(
+ gpMapiAllocateBuffer, gpMapiFreeBuffer, STGM_READWRITE | STGM_CREATE,
+ tmpNativePath.get(), NULL, &lpStreamFile);
+ if (HR_FAILED(hr)) {
+ MAPI_TRACE1("~~ERROR~~ OpenStreamOnFile failed - temp path: %s\r\n",
+ tmpNativePath.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_xstrdup(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_xstrdup(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_xstrdup((PC_S8)pType);
+ else
+ data->type = NS_xstrdup(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 occurrence 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) free(data->type);
+ if (data->encoding) free(data->encoding);
+ if (data->real_name) free(data->real_name);
+ if (data->cid) 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(
+ nsTArray<RefPtr<nsIMsgAttachedFile>>& attachments) {
+ attachments.Clear();
+ attachments.SetCapacity(m_stdattachments.size());
+
+ for (std::vector<attach_data*>::const_iterator it = m_stdattachments.begin();
+ it != m_stdattachments.end(); it++) {
+ nsresult rv;
+ nsCOMPtr<nsIMsgAttachedFile> a(
+ do_CreateInstance("@mozilla.org/messengercompose/attachedfile;1", &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);
+ }
+ return NS_OK;
+}
+
+bool CMapiMessage::GetEmbeddedAttachmentInfo(unsigned int i, nsIURI** uri,
+ const char** cid,
+ const char** name) const {
+ if (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 ? nsDependentCString(fallbackCharset) : EmptyCString(),
+ 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 CMapiMessageHeaders::Delete(CHeaderField* 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/comm/mailnews/import/src/MapiMessage.h b/comm/mailnews/import/src/MapiMessage.h
new file mode 100644
index 0000000000..c06a84c57a
--- /dev/null
+++ b/comm/mailnews/import/src/MapiMessage.h
@@ -0,0 +1,290 @@
+/* -*- 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 "nsString.h"
+#include "nsIFile.h"
+#include "nsIMsgSend.h"
+#include "MapiApi.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
+ };
+
+ explicit 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:
+ explicit 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:
+ explicit 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 void Delete(CHeaderField* p);
+ 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:
+ explicit CMapiMessage(LPMESSAGE lpMsg);
+ ~CMapiMessage();
+
+ // Attachments
+ // Ordinary (not embedded) attachments.
+ nsresult GetAttachments(nsTArray<RefPtr<nsIMsgAttachedFile>>& attachments);
+ // 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/comm/mailnews/import/src/MapiMimeTypes.cpp b/comm/mailnews/import/src/MapiMimeTypes.cpp
new file mode 100644
index 0000000000..554c5694d9
--- /dev/null
+++ b/comm/mailnews/import/src/MapiMimeTypes.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 "nsString.h"
+#include "MapiMimeTypes.h"
+
+uint8_t CMimeTypes::m_mimeBuffer[kMaxMimeTypeSize];
+
+BOOL CMimeTypes::GetKey(HKEY root, LPCWSTR pName, PHKEY pKey) {
+ LONG result = RegOpenKeyExW(root, pName, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, pKey);
+ return result == ERROR_SUCCESS;
+}
+
+BOOL CMimeTypes::GetValueBytes(HKEY rootKey, LPCWSTR pValName,
+ LPBYTE* ppBytes) {
+ LONG err;
+ DWORD bufSz;
+
+ *ppBytes = NULL;
+ // Get the installed directory
+ err = RegQueryValueExW(rootKey, pValName, NULL, NULL, NULL, &bufSz);
+ if (err == ERROR_SUCCESS) {
+ *ppBytes = new BYTE[bufSz];
+ err = RegQueryValueExW(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 nsString& ext, LPBYTE* ppBytes) {
+ HKEY extensionKey;
+ BOOL result = FALSE;
+ *ppBytes = NULL;
+ if (GetKey(HKEY_CLASSES_ROOT, ext.get(), &extensionKey)) {
+ result = GetValueBytes(extensionKey, L"Content Type", ppBytes);
+ RegCloseKey(extensionKey);
+ }
+
+ return result;
+}
+
+uint8_t* CMimeTypes::GetMimeType(const nsString& theExt) {
+ nsString ext = theExt;
+ if (ext.Length()) {
+ if (ext.First() != '.') {
+ ext = L".";
+ 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/comm/mailnews/import/src/MapiMimeTypes.h b/comm/mailnews/import/src/MapiMimeTypes.h
new file mode 100644
index 0000000000..d870893559
--- /dev/null
+++ b/comm/mailnews/import/src/MapiMimeTypes.h
@@ -0,0 +1,27 @@
+/* -*- 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 nsString& theExt);
+
+ protected:
+ // Registry stuff
+ static BOOL GetKey(HKEY root, LPCWSTR pName, PHKEY pKey);
+ static BOOL GetValueBytes(HKEY rootKey, LPCWSTR pValName, LPBYTE* ppBytes);
+ static void ReleaseValueBytes(LPBYTE pBytes);
+ static BOOL GetMimeTypeFromReg(const nsString& ext, LPBYTE* ppBytes);
+
+ static uint8_t m_mimeBuffer[kMaxMimeTypeSize];
+};
+
+#endif /* MapiMimeTypes_h__ */
diff --git a/comm/mailnews/import/src/MapiTagStrs.cpp b/comm/mailnews/import/src/MapiTagStrs.cpp
new file mode 100644
index 0000000000..43a796ae24
--- /dev/null
+++ b/comm/mailnews/import/src/MapiTagStrs.cpp
@@ -0,0 +1,1473 @@
+ /* -*- 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/. */
+
+ /*
+ * 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/comm/mailnews/import/src/MorkImport.cpp b/comm/mailnews/import/src/MorkImport.cpp
new file mode 100644
index 0000000000..4cdecb8067
--- /dev/null
+++ b/comm/mailnews/import/src/MorkImport.cpp
@@ -0,0 +1,343 @@
+/* -*- 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/. */
+
+/*
+ * Mork import addressbook interfaces
+ */
+
+#include "MorkImport.h"
+
+#include "nsCOMPtr.h"
+#include "nsIImportService.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsImportStringBundle.h"
+#include "nsIComponentManager.h"
+#include "nsIAbDirectory.h"
+#include "nsAddrDatabase.h"
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+
+static const char kRowIDProperty[] = "DbRowID";
+
+class MorkImportAddressImpl final : public nsIImportAddressBooks {
+ public:
+ explicit MorkImportAddressImpl(nsIStringBundle* aStringBundle);
+
+ static nsresult Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle* aStringBundle);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIMPORTADDRESSBOOKS
+
+ private:
+ ~MorkImportAddressImpl() {}
+ nsCOMPtr<nsIFile> mFileLocation;
+ nsCOMPtr<nsIStringBundle> mStringBundle;
+};
+
+MorkImport::MorkImport() {
+ nsImportStringBundle::GetStringBundle(
+ "chrome://messenger/locale/morkImportMsgs.properties",
+ getter_AddRefs(mStringBundle));
+}
+
+MorkImport::~MorkImport() {}
+
+NS_IMPL_ISUPPORTS(MorkImport, nsIImportModule)
+
+NS_IMETHODIMP MorkImport::GetName(char16_t** name) {
+ NS_ENSURE_ARG_POINTER(name);
+ *name =
+ nsImportStringBundle::GetStringByName("morkImportName", mStringBundle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImport::GetDescription(char16_t** description) {
+ NS_ENSURE_ARG_POINTER(description);
+ *description = nsImportStringBundle::GetStringByName("morkImportDescription",
+ mStringBundle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImport::GetSupports(char** supports) {
+ NS_ENSURE_ARG_POINTER(supports);
+ *supports = strdup(NS_IMPORT_ADDRESS_STR);
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImport::GetSupportsUpgrade(bool* upgrade) {
+ NS_ENSURE_ARG_POINTER(upgrade);
+ *upgrade = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImport::GetImportInterface(const char* importType,
+ nsISupports** interface) {
+ NS_ENSURE_ARG_POINTER(importType);
+ NS_ENSURE_ARG_POINTER(interface);
+
+ *interface = nullptr;
+ nsresult rv;
+
+ if (strcmp(importType, "addressbook")) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIImportAddressBooks> pAddress;
+ nsCOMPtr<nsIImportGeneric> pGeneric;
+ rv = MorkImportAddressImpl::Create(getter_AddRefs(pAddress), mStringBundle);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = impSvc->CreateNewGenericAddressBooks(getter_AddRefs(pGeneric));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pGeneric->SetData("addressInterface", pAddress);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric));
+ pInterface.forget(interface);
+
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+nsresult MorkImportAddressImpl::Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle* aStringBundle) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new MorkImportAddressImpl(aStringBundle));
+ return NS_OK;
+}
+
+MorkImportAddressImpl::MorkImportAddressImpl(nsIStringBundle* aStringBundle)
+ : mStringBundle(aStringBundle) {}
+
+NS_IMPL_ISUPPORTS(MorkImportAddressImpl, nsIImportAddressBooks)
+
+NS_IMETHODIMP MorkImportAddressImpl::GetSupportsMultiple(bool* _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::GetAutoFind(char16_t** addrDescription,
+ bool* _retval) {
+ NS_ENSURE_ARG_POINTER(addrDescription);
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::GetNeedsFieldMap(nsIFile* aLocation,
+ bool* _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::GetDefaultLocation(nsIFile** ppLoc,
+ bool* found,
+ bool* userVerify) {
+ NS_ENSURE_ARG_POINTER(ppLoc);
+ NS_ENSURE_ARG_POINTER(found);
+ NS_ENSURE_ARG_POINTER(userVerify);
+
+ *ppLoc = nullptr;
+ *found = false;
+ *userVerify = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::FindAddressBooks(
+ nsIFile* pLoc, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ NS_ENSURE_ARG_POINTER(pLoc);
+
+ books.Clear();
+ 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;
+
+ mFileLocation = pLoc;
+
+ /* Build an address book descriptor based on the file passed in! */
+ nsString name;
+ rv = mFileLocation->GetLeafName(name);
+ NS_ENSURE_SUCCESS(rv, 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));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = impSvc->CreateNewABDescriptor(getter_AddRefs(desc));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t sz = 0;
+ pLoc->GetFileSize(&sz);
+ desc->SetPreferredName(name);
+ desc->SetSize((uint32_t)sz);
+ desc->SetAbFile(mFileLocation);
+ books.AppendElement(desc);
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::InitFieldMap(nsIImportFieldMap* fieldMap) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MorkImportAddressImpl::ImportAddressBook(
+ nsIImportABDescriptor* pSource, nsIAbDirectory* 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);
+
+ nsCOMPtr<nsIFile> oldFile;
+ pSource->GetAbFile(getter_AddRefs(oldFile));
+
+ nsresult rv = ReadMABToDirectory(oldFile, pDestination);
+
+ *pSuccessLog =
+ nsImportStringBundle::GetStringByName("morkImportSuccess", mStringBundle);
+ return rv;
+}
+
+nsresult ReadMABToDirectory(nsIFile* oldFile, nsIAbDirectory* newDirectory) {
+ nsresult rv;
+
+ nsAddrDatabase database = nsAddrDatabase();
+ database.SetDbPath(oldFile);
+ database.OpenMDB(oldFile, false);
+
+ nsInterfaceHashtable<nsUint32HashKey, nsIAbCard> cardMap;
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ database.EnumerateCards(getter_AddRefs(enumerator));
+
+ nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsIAbCard> card;
+ bool isMailList;
+ while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(supports))) &&
+ supports) {
+ card = do_QueryInterface(supports);
+
+ card->GetIsMailList(&isMailList);
+ if (isMailList) {
+ continue;
+ }
+
+ uint32_t rowId;
+ card->GetPropertyAsUint32(kRowIDProperty, &rowId);
+ cardMap.InsertOrUpdate(rowId, card);
+
+ nsIAbCard* outCard;
+ newDirectory->AddCard(card, &outCard);
+ }
+
+ database.EnumerateCards(getter_AddRefs(enumerator));
+
+ while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(supports))) &&
+ supports) {
+ card = do_QueryInterface(supports);
+ card->GetIsMailList(&isMailList);
+
+ if (!isMailList) {
+ continue;
+ }
+
+ nsCOMPtr<nsIAbDirectory> mailList =
+ do_CreateInstance("@mozilla.org/addressbook/directoryproperty;1");
+ mailList->SetIsMailList(true);
+
+ nsAutoString listName;
+ card->GetDisplayName(listName);
+ mailList->SetDirName(listName);
+
+ nsAutoString nickName;
+ rv = card->GetPropertyAsAString("NickName", nickName);
+ if (NS_SUCCEEDED(rv)) {
+ mailList->SetListNickName(nickName);
+ }
+
+ nsAutoString description;
+ rv = card->GetPropertyAsAString("Notes", description);
+ if (NS_SUCCEEDED(rv)) {
+ mailList->SetDescription(description);
+ }
+
+ nsIAbDirectory* outList;
+ rv = newDirectory->AddMailList(mailList, &outList);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ uint32_t listRowId;
+ card->GetPropertyAsUint32(kRowIDProperty, &listRowId);
+
+ nsCOMPtr<nsISimpleEnumerator> listEnumerator;
+ database.EnumerateListAddresses(listRowId, getter_AddRefs(listEnumerator));
+
+ nsCOMPtr<nsISupports> listSupports;
+ nsCOMPtr<nsIAbCard> listCard;
+ while (
+ NS_SUCCEEDED(listEnumerator->GetNext(getter_AddRefs(listSupports))) &&
+ listSupports) {
+ listCard = do_QueryInterface(listSupports);
+
+ uint32_t rowId;
+ listCard->GetPropertyAsUint32(kRowIDProperty, &rowId);
+ cardMap.Get(rowId, getter_AddRefs(listCard));
+
+ nsIAbCard* outCard;
+ outList->AddCard(listCard, &outCard);
+ }
+ }
+
+ database.ForceClosed();
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::GetImportProgress(uint32_t* _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::SetSampleLocation(nsIFile* pLocation) {
+ NS_ENSURE_ARG_POINTER(pLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP MorkImportAddressImpl::GetSampleData(int32_t index, bool* pFound,
+ char16_t** pStr) {
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsImportABFromMab, nsIImportABFile)
+
+nsImportABFromMab::nsImportABFromMab() {}
+
+NS_IMETHODIMP
+nsImportABFromMab::ReadFileToDirectory(nsIFile* sourceFile,
+ nsIAbDirectory* targetDirectory) {
+ return ReadMABToDirectory(sourceFile, targetDirectory);
+}
diff --git a/comm/mailnews/import/src/MorkImport.h b/comm/mailnews/import/src/MorkImport.h
new file mode 100644
index 0000000000..80a0d25bec
--- /dev/null
+++ b/comm/mailnews/import/src/MorkImport.h
@@ -0,0 +1,50 @@
+/* -*- 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 MorkImport_h___
+#define MorkImport_h___
+
+#include "nsIImportABFile.h"
+#include "nsIImportModule.h"
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+
+#include "nsIFile.h"
+#include "nsIAbDirectory.h"
+
+#define MORKIMPORT_CID \
+ { /* 54d48d9f-1bac-47be-9190-c4dc74e837e2 */ \
+ 0x54d48d9f, 0x1bac, 0x47be, { \
+ 0x91, 0x90, 0xc4, 0xdc, 0x74, 0xe8, 0x37, 0xe2 \
+ } \
+ }
+
+nsresult ReadMABToDirectory(nsIFile* oldFile, nsIAbDirectory* newDirectory);
+
+class nsImportABFromMab : public nsIImportABFile {
+ public:
+ nsImportABFromMab();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTABFILE
+
+ protected:
+ virtual ~nsImportABFromMab(){};
+};
+
+class MorkImport : public nsIImportModule {
+ public:
+ MorkImport();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIMPORTMODULE
+
+ protected:
+ virtual ~MorkImport();
+ nsCOMPtr<nsIStringBundle> mStringBundle;
+};
+
+#endif /* MorkImport_h___ */
diff --git a/comm/mailnews/import/src/SeamonkeyImport.jsm b/comm/mailnews/import/src/SeamonkeyImport.jsm
new file mode 100644
index 0000000000..c88f1830d7
--- /dev/null
+++ b/comm/mailnews/import/src/SeamonkeyImport.jsm
@@ -0,0 +1,253 @@
+/* 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/. */
+
+let { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+let seamonkeyImportMsgs = Services.strings.createBundle(
+ "chrome://messenger/locale/seamonkeyImportMsgs.properties"
+);
+
+var EXPORTED_SYMBOLS = ["SeamonkeyImport"];
+
+/**
+ * Implements nsIImportGeneric instead of nsIImportAddressBook. The actual
+ * importing is delegated to nsSeamonkeyProfileMigrator.
+ */
+function SeamonkeyImportAddressbook() {
+ this.migrator = Cc[
+ "@mozilla.org/profile/migrator;1?app=mail&type=seamonkey"
+ ].createInstance(Ci.nsIMailProfileMigrator);
+ this.sourceProfileName = null;
+ this.sourceProfileLocation = null;
+}
+
+SeamonkeyImportAddressbook.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIImportGeneric"]),
+
+ /**
+ * Return the location of addressbook.
+ */
+ GetData() {
+ if (!this.sourceProfileName || !this.sourceProfileLocation) {
+ try {
+ this.sourceProfileName = this.migrator.sourceProfiles[0];
+ this.sourceProfileLocation = this.migrator.sourceProfileLocations[0];
+ } catch (e) {
+ return null;
+ }
+ }
+
+ return this.sourceProfileLocation;
+ },
+
+ SetData() {
+ return 0;
+ },
+
+ WantsProgress() {
+ return false;
+ },
+
+ GetProgress() {
+ return 0;
+ },
+
+ GetStatus() {
+ return 0;
+ },
+
+ CancelImport() {
+ return 0;
+ },
+
+ ContinueImport() {
+ return 0;
+ },
+
+ BeginImport(successLog, errorLog) {
+ this.migrator.migrate(
+ Ci.nsIMailProfileMigrator.ADDRESSBOOK_DATA,
+ null,
+ this.sourceProfileName
+ );
+ successLog.data = seamonkeyImportMsgs.GetStringFromName(
+ "SeamonkeyImportAddressSuccess"
+ );
+ return true;
+ },
+};
+
+/**
+ * Implements nsIImportMail. The importing process is managed by nsImportMail.
+ */
+function SeamonkeyImportMail() {}
+
+SeamonkeyImportMail.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIImportMail"]),
+
+ GetDefaultLocation(location, found, userVerify) {
+ let migrator = Cc[
+ "@mozilla.org/profile/migrator;1?app=mail&type=seamonkey"
+ ].createInstance(Ci.nsIMailProfileMigrator);
+
+ try {
+ let sourceProfile = migrator.sourceProfileLocations[0];
+ location.value = sourceProfile;
+ found.value = true;
+ } catch (e) {
+ found.value = false;
+ }
+ userVerify.value = false;
+ },
+
+ _createMailboxDescriptor(path, name, depth) {
+ let importService = Cc[
+ "@mozilla.org/import/import-service;1"
+ ].createInstance(Ci.nsIImportService);
+ let descriptor = importService.CreateNewMailboxDescriptor();
+ descriptor.size = 100;
+ descriptor.depth = depth;
+ descriptor.SetDisplayName(name);
+ descriptor.file.initWithPath(path);
+
+ return descriptor;
+ },
+
+ _collectMailboxesInDirectory(directory, depth) {
+ let result = [];
+ let name = directory.leafName;
+ if (depth > 0 && !name.endsWith(".msf") && !name.endsWith(".dat")) {
+ if (name.endsWith(".sbd")) {
+ name = name.slice(0, name.lastIndexOf("."));
+ }
+ let descriptor = this._createMailboxDescriptor(
+ directory.path,
+ name,
+ depth
+ );
+ result.push(descriptor);
+ }
+ if (directory.isDirectory()) {
+ for (let entry of directory.directoryEntries) {
+ if (
+ (depth == 0 &&
+ entry.leafName != "ImapMail" &&
+ entry.leafName != "Mail") ||
+ (depth == 1 && entry.leafName == "Feeds")
+ ) {
+ continue;
+ }
+ result.push(...this._collectMailboxesInDirectory(entry, depth + 1));
+ }
+ }
+ return result;
+ },
+
+ // Collect mailboxes in a Seamonkey profile.
+ findMailboxes(location) {
+ return this._collectMailboxesInDirectory(location, 0);
+ },
+
+ // Copy mailboxes a Seamonkey profile to Thunderbird profile.
+ ImportMailbox(source, dstFolder, errorLog, successLog, fatalError) {
+ if (source.file.isFile()) {
+ source.file.copyTo(
+ dstFolder.filePath.parent,
+ dstFolder.filePath.leafName
+ );
+ successLog.value = `Import ${source.file.leafName} succeeded.\n`;
+ }
+ },
+};
+
+/**
+ * Implements nsIImportSettings. The actual importing is delegated to
+ * nsSeamonkeyProfileMigrator.
+ */
+function SeamonkeyImportSettings() {
+ this.migrator = Cc[
+ "@mozilla.org/profile/migrator;1?app=mail&type=seamonkey"
+ ].createInstance(Ci.nsIMailProfileMigrator);
+ this.sourceProfileName = null;
+ this.sourceProfileLocation = null;
+}
+
+SeamonkeyImportSettings.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIImportSettings"]),
+
+ AutoLocate(desc, loc) {
+ if (!this.sourceProfileName || !this.sourceProfileLocation) {
+ try {
+ this.sourceProfileName = this.migrator.sourceProfiles[0];
+ this.sourceProfileLocation = this.migrator.sourceProfileLocations[0];
+ } catch (e) {
+ return false;
+ }
+ }
+ loc = this.sourceProfileLocation;
+ return true;
+ },
+
+ Import() {
+ this.migrator.migrate(
+ Ci.nsIMailProfileMigrator.SETTINGS,
+ null,
+ this.sourceProfileName
+ );
+
+ // Reload accounts so that `CheckIfLocalFolderExists` in importDialog works
+ MailServices.accounts.unloadAccounts();
+ MailServices.accounts.loadAccounts();
+ return true;
+ },
+};
+
+/**
+ * Implements nsIImportModule so that Seamonkey is shown as an option in the
+ * importDialog.xhtml. Currently supports importing addressbook and mail, see
+ * the GetImportInterface function.
+ */
+function SeamonkeyImport() {}
+
+SeamonkeyImport.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIImportModule"]),
+
+ get name() {
+ return seamonkeyImportMsgs.GetStringFromName("SeamonkeyImportName");
+ },
+
+ get description() {
+ return seamonkeyImportMsgs.GetStringFromName("SeamonkeyImportDescription");
+ },
+
+ get supports() {
+ return "addressbook,mail,settings";
+ },
+
+ get supportsUpgrade() {
+ return false;
+ },
+
+ GetImportInterface(type) {
+ if (type == "addressbook") {
+ return new SeamonkeyImportAddressbook();
+ } else if (type == "mail") {
+ let importService = Cc[
+ "@mozilla.org/import/import-service;1"
+ ].createInstance(Ci.nsIImportService);
+ let genericInterface = importService.CreateNewGenericMail();
+ genericInterface.SetData("mailInterface", new SeamonkeyImportMail());
+ let name = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ name.data = "SeaMonkey";
+ genericInterface.SetData("name", name);
+ return genericInterface;
+ } else if (type == "settings") {
+ return new SeamonkeyImportSettings();
+ }
+ return null;
+ },
+};
diff --git a/comm/mailnews/import/src/ThunderbirdImport.jsm b/comm/mailnews/import/src/ThunderbirdImport.jsm
new file mode 100644
index 0000000000..8263fa7098
--- /dev/null
+++ b/comm/mailnews/import/src/ThunderbirdImport.jsm
@@ -0,0 +1,145 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["ThunderbirdImport"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const lazy = {};
+
+XPCOMUtils.defineLazyGetter(
+ lazy,
+ "l10n",
+ () => new Localization(["messenger/importDialog.ftl"], true)
+);
+
+/**
+ * The importing process is managed by importDialog.js and nsImportMail.cpp.
+ *
+ * @implements {nsIImportMail}
+ */
+class ThunderbirdImportMail {
+ QueryInterface = ChromeUtils.generateQI(["nsIImportMail"]);
+
+ GetDefaultLocation(location, found, userVerify) {
+ userVerify.value = true;
+ }
+
+ /**
+ * Create a nsIImportMailboxDescriptor instance.
+ *
+ * @param {string} path - The mailbox path.
+ * @param {string} name - The mailbox name.
+ * @param {number} depth - The depth of the mailbox folder.
+ * @returns {nsIImportMailboxDescriptor}
+ */
+ _createMailboxDescriptor(path, name, depth) {
+ let importService = Cc[
+ "@mozilla.org/import/import-service;1"
+ ].createInstance(Ci.nsIImportService);
+ let descriptor = importService.CreateNewMailboxDescriptor();
+ descriptor.size = 100;
+ descriptor.depth = depth;
+ descriptor.SetDisplayName(name);
+ descriptor.file.initWithPath(path);
+
+ return descriptor;
+ }
+
+ /**
+ * Recursively find mailboxes in a directory.
+ *
+ * @param {nsIFile} directory - The directory to find mailboxes.
+ * @param {number} depth - The depth of the current directory.
+ * @returns {nsIImportMailboxDescriptor[]} - All mailboxes found.
+ */
+ _collectMailboxesInDirectory(directory, depth) {
+ let result = [];
+ let name = directory.leafName;
+ if (depth > 0 && !name.endsWith(".msf") && !name.endsWith(".dat")) {
+ if (name.endsWith(".sbd")) {
+ name = name.slice(0, name.lastIndexOf("."));
+ }
+ let descriptor = this._createMailboxDescriptor(
+ directory.path,
+ name,
+ depth
+ );
+ result.push(descriptor);
+ }
+ if (directory.isDirectory()) {
+ for (let entry of directory.directoryEntries) {
+ if (
+ (depth == 0 &&
+ entry.leafName != "ImapMail" &&
+ entry.leafName != "Mail") ||
+ (depth == 1 && entry.leafName == "Feeds")
+ ) {
+ continue;
+ }
+ result.push(...this._collectMailboxesInDirectory(entry, depth + 1));
+ }
+ }
+ return result;
+ }
+
+ findMailboxes(location) {
+ return this._collectMailboxesInDirectory(location, 0);
+ }
+
+ ImportMailbox(source, dstFolder, errorLog, successLog, fatalError) {
+ if (source.file.isFile()) {
+ source.file.copyTo(
+ dstFolder.filePath.parent,
+ dstFolder.filePath.leafName
+ );
+ successLog.value = `Import ${source.file.leafName} succeeded.\n`;
+ }
+ }
+}
+
+/**
+ * With this class, Thunderbird is shown as an option in the importDialog.xhtml.
+ * Currently supports importing mail, see the GetImportInterface function.
+ *
+ * @implements {nsIImportModule}
+ */
+class ThunderbirdImport {
+ QueryInterface = ChromeUtils.generateQI(["nsIImportModule"]);
+
+ get name() {
+ return lazy.l10n.formatValueSync("thunderbird-import-name");
+ }
+
+ get description() {
+ return lazy.l10n.formatValueSync("thunderbird-import-description");
+ }
+
+ get supports() {
+ return "mail";
+ }
+
+ get supportsUpgrade() {
+ return false;
+ }
+
+ GetImportInterface(type) {
+ if (type == "mail") {
+ let importService = Cc[
+ "@mozilla.org/import/import-service;1"
+ ].createInstance(Ci.nsIImportService);
+ let genericInterface = importService.CreateNewGenericMail();
+ genericInterface.SetData("mailInterface", new ThunderbirdImportMail());
+ let name = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ name.data = "Thunderbird";
+ genericInterface.SetData("name", name);
+ return genericInterface;
+ }
+ return null;
+ }
+}
diff --git a/comm/mailnews/import/src/components.conf b/comm/mailnews/import/src/components.conf
new file mode 100644
index 0000000000..e8891fca57
--- /dev/null
+++ b/comm/mailnews/import/src/components.conf
@@ -0,0 +1,104 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+Classes = [
+ {
+ "cid": "{991f078e-a6d5-44f2-b91e-c52efcfa3360}",
+ "contract_ids": ["@mozilla.org/import/import-seamonkey;1"],
+ "jsm": "resource:///modules/SeamonkeyImport.jsm",
+ "constructor": "SeamonkeyImport",
+ "categories": {"mailnewsimport": "seamonkey"},
+ },
+ {
+ "cid": "{c6988841-d916-44a3-bb4d-f0838a98e95a}",
+ "contract_ids": ["@mozilla.org/import/import-thunderbird;1"],
+ "jsm": "resource:///modules/ThunderbirdImport.jsm",
+ "constructor": "ThunderbirdImport",
+ "categories": {"mailnewsimport": "thunderbird"},
+ },
+ {
+ "cid": "{a6629718-9a97-4073-ab48-442fcceaea5d}",
+ "contract_ids": ["@mozilla.org/import/import-ab-file;1?type=mab"],
+ "type": "nsImportABFromMab",
+ "headers": ["/comm/mailnews/import/src/MorkImport.h"],
+ },
+ {
+ "cid": "{5df96d60-1726-11d3-a206-00a0cc26da63}",
+ "contract_ids": ["@mozilla.org/import/import-service;1"],
+ "type": "nsImportService",
+ "headers": ["/comm/mailnews/import/src/nsImportService.h"],
+ "name": "Import",
+ "interfaces": ["nsIImportService"],
+ },
+ {
+ "cid": "{a5991d01-ada7-11d3-a9c2-00a0cc26da63}",
+ "contract_ids": ["@mozilla.org/import/import-text;1"],
+ "type": "nsTextImport",
+ "headers": ["/comm/mailnews/import/src/nsTextImport.h"],
+ "categories": {"mailnewsimport": "text"},
+ },
+ {
+ "cid": "{0eb034a3-964a-4e2f-92ebcc55d9ae9dd2}",
+ "contract_ids": ["@mozilla.org/import/import-vcard;1"],
+ "type": "nsVCardImport",
+ "headers": ["/comm/mailnews/import/src/nsVCardImport.h"],
+ "categories": {"mailnewsimport": "vcard"},
+ },
+ {
+ "cid": "{54d48d9f-1bac-47be-9190-c4dc74e837e2}",
+ "contract_ids": ["@mozilla.org/import/import-mork;1"],
+ "type": "MorkImport",
+ "headers": ["/comm/mailnews/import/src/MorkImport.h"],
+ "categories": {"mailnewsimport": "mork"},
+ },
+]
+
+if buildconfig.substs["OS_ARCH"] == "Darwin":
+ Classes += [
+ {
+ "cid": "{6d3f101c-70ec-4e04-b68d-9908d1aeddf3}",
+ "contract_ids": ["@mozilla.org/import/import-applemail;1"],
+ "type": "nsAppleMailImportModule",
+ "headers": ["/comm/mailnews/import/src/nsAppleMailImport.h"],
+ "categories": {"mailnewsimport": "applemail"},
+ },
+ {
+ "cid": "{9117a1ea-e012-43b5-a020-cb8a66cc09e1}",
+ "contract_ids": ["@mozilla.org/import/import-appleMailImpl;1"],
+ "type": "nsAppleMailImportMail",
+ "init_method": "Initialize",
+ "headers": ["/comm/mailnews/import/src/nsAppleMailImport.h"],
+ },
+ ]
+
+if buildconfig.substs["OS_ARCH"] == "WINNT":
+ Classes += [
+ {
+ "cid": "{42bc82bc-8e9f-4597-8b6e-e529daaf3af1}",
+ "contract_ids": ["@mozilla.org/import/import-wm;1"],
+ "type": "nsWMImport",
+ "headers": ["/comm/mailnews/import/src/nsWMImport.h"],
+ "categories": {"mailnewsimport": "winlivemail"},
+ },
+ {
+ "cid": "{7952a6cf-2442-4c04-9f02-150b15a0a841}",
+ "contract_ids": ["@mozilla.org/import/import-becky;1"],
+ "type": "nsBeckyImport",
+ "headers": ["/comm/mailnews/import/src/nsBeckyImport.h"],
+ "categories": {"mailnewsimport": "becky"},
+ },
+ ]
+
+ if buildconfig.substs["MOZ_MAPI_SUPPORT"]:
+ Classes += [
+ {
+ "cid": "{1db469a0-8b00-11d3-a206-00a0cc26da63}",
+ "contract_ids": ["@mozilla.org/import/import-outlook;1"],
+ "type": "nsOutlookImport",
+ "headers": ["/comm/mailnews/import/src/nsOutlookImport.h"],
+ "categories": {"mailnewsimport": "outlook"},
+ },
+ ]
diff --git a/comm/mailnews/import/src/moz.build b/comm/mailnews/import/src/moz.build
new file mode 100644
index 0000000000..d587d87fa5
--- /dev/null
+++ b/comm/mailnews/import/src/moz.build
@@ -0,0 +1,86 @@
+# 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",
+ "MorkImport.cpp",
+ "nsAddrDatabase.cpp",
+ "nsImportABDescriptor.cpp",
+ "nsImportAddressBooks.cpp",
+ "nsImportEmbeddedImageData.cpp",
+ "nsImportEncodeScan.cpp",
+ "nsImportFieldMap.cpp",
+ "nsImportMail.cpp",
+ "nsImportMailboxDescriptor.cpp",
+ "nsImportScanFile.cpp",
+ "nsImportService.cpp",
+ "nsImportStringBundle.cpp",
+ "nsImportTranslator.cpp",
+ "nsTextAddress.cpp",
+ "nsTextImport.cpp",
+ "nsVCardAddress.cpp",
+ "nsVCardImport.cpp",
+]
+
+if not CONFIG["MOZ_SUITE"]:
+ EXTRA_JS_MODULES += [
+ "SeamonkeyImport.jsm",
+ "ThunderbirdImport.jsm",
+ ]
+
+ XPCOM_MANIFESTS += [
+ "components.conf",
+ ]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ SOURCES += [
+ "nsAppleMailImport.cpp",
+ "nsEmlxHelperUtils.mm",
+ ]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "nsBeckyAddressBooks.cpp",
+ "nsBeckyFilters.cpp",
+ "nsBeckyImport.cpp",
+ "nsBeckyMail.cpp",
+ "nsBeckySettings.cpp",
+ "nsBeckyStringBundle.cpp",
+ "nsBeckyUtils.cpp",
+ ]
+
+ if CONFIG["MOZ_MAPI_SUPPORT"]:
+ SOURCES += [
+ "MapiApi.cpp",
+ "MapiMessage.cpp",
+ "MapiMimeTypes.cpp",
+ "nsOutlookCompose.cpp",
+ "nsOutlookImport.cpp",
+ "nsOutlookMail.cpp",
+ "nsOutlookSettings.cpp",
+ "nsOutlookStringBundle.cpp",
+ "rtfDecoder.cpp",
+ "rtfMailDecoder.cpp",
+ ]
+
+ SOURCES["rtfDecoder.cpp"].flags += ["-Wno-switch"]
+ LOCAL_INCLUDES += ["/comm/mailnews/mapi/include"]
+
+ if CONFIG["CC_TYPE"] in ("msvc", "clang-cl"):
+ SOURCES += [
+ "nsWMImport.cpp",
+ "nsWMSettings.cpp",
+ "nsWMStringBundle.cpp",
+ "nsWMUtils.cpp",
+ ]
+
+EXPORTS += [
+ "ImportDebug.h",
+ "nsVCardAddress.h",
+]
+
+FINAL_LIBRARY = "import"
diff --git a/comm/mailnews/import/src/nsAddrDatabase.cpp b/comm/mailnews/import/src/nsAddrDatabase.cpp
new file mode 100644
index 0000000000..1bc0df1795
--- /dev/null
+++ b/comm/mailnews/import/src/nsAddrDatabase.cpp
@@ -0,0 +1,864 @@
+/* -*- 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/. */
+
+// this file implements the nsAddrDatabase interface using the MDB Interface.
+
+#include "nsAddrDatabase.h"
+#include "nsMsgUtils.h"
+#include "nsIMdbFactoryFactory.h"
+#include "nsSimpleEnumerator.h"
+
+#define kAddressCharSetColumn "AddrCharSet"
+#define kMailListName "ListName"
+#define kMailListNickName "ListNickName"
+#define kMailListDescription "ListDescription"
+#define kMailListTotalAddresses "ListTotalAddresses"
+// not shown in the UI
+#define kLowerPriEmailColumn "LowercasePrimaryEmail"
+#define kLower2ndEmailColumn "LowercaseSecondEmail"
+
+#define ID_PAB_TABLE 1
+
+static const char kPabTableKind[] = "ns:addrbk:db:table:kind:pab";
+static const char kDeletedCardsTableKind[] =
+ "ns:addrbk:db:table:kind:deleted"; // this table is used to keep the
+ // deleted cards
+
+static const char kCardRowScope[] = "ns:addrbk:db:row:scope:card:all";
+static const char kListRowScope[] = "ns:addrbk:db:row:scope:list:all";
+static const char kDataRowScope[] = "ns:addrbk:db:row:scope:data:all";
+
+#define COLUMN_STR_MAX 16
+
+static const char kRecordKeyColumn[] = "RecordKey";
+static const char kLastRecordKeyColumn[] = "LastRecordKey";
+static const char kRowIDProperty[] = "DbRowID";
+
+static const char kLowerListNameColumn[] = "LowercaseListName";
+
+struct mdbOid gAddressBookTableOID;
+
+static const char kMailListAddressFormat[] = "Address%d";
+
+nsAddrDatabase::nsAddrDatabase()
+ : m_mdbEnv(nullptr),
+ m_mdbStore(nullptr),
+ m_mdbPabTable(nullptr),
+ m_mdbTokensInitialized(false),
+ m_PabTableKind(0),
+ m_DeletedCardsTableKind(0),
+ m_CardRowScopeToken(0),
+ m_UIDColumnToken(0),
+ m_FirstNameColumnToken(0),
+ m_LastNameColumnToken(0),
+ m_PhoneticFirstNameColumnToken(0),
+ m_PhoneticLastNameColumnToken(0),
+ m_DisplayNameColumnToken(0),
+ m_NickNameColumnToken(0),
+ m_PriEmailColumnToken(0),
+ m_2ndEmailColumnToken(0),
+ m_WorkPhoneColumnToken(0),
+ m_HomePhoneColumnToken(0),
+ m_FaxColumnToken(0),
+ m_PagerColumnToken(0),
+ m_CellularColumnToken(0),
+ m_WorkPhoneTypeColumnToken(0),
+ m_HomePhoneTypeColumnToken(0),
+ m_FaxTypeColumnToken(0),
+ m_PagerTypeColumnToken(0),
+ m_CellularTypeColumnToken(0),
+ m_HomeAddressColumnToken(0),
+ m_HomeAddress2ColumnToken(0),
+ m_HomeCityColumnToken(0),
+ m_HomeStateColumnToken(0),
+ m_HomeZipCodeColumnToken(0),
+ m_HomeCountryColumnToken(0),
+ m_WorkAddressColumnToken(0),
+ m_WorkAddress2ColumnToken(0),
+ m_WorkCityColumnToken(0),
+ m_WorkStateColumnToken(0),
+ m_WorkZipCodeColumnToken(0),
+ m_WorkCountryColumnToken(0),
+ m_CompanyColumnToken(0),
+ m_AimScreenNameColumnToken(0),
+ m_AnniversaryYearColumnToken(0),
+ m_AnniversaryMonthColumnToken(0),
+ m_AnniversaryDayColumnToken(0),
+ m_SpouseNameColumnToken(0),
+ m_FamilyNameColumnToken(0),
+ m_DefaultAddressColumnToken(0),
+ m_CategoryColumnToken(0),
+ m_WebPage1ColumnToken(0),
+ m_WebPage2ColumnToken(0),
+ m_BirthYearColumnToken(0),
+ m_BirthMonthColumnToken(0),
+ m_BirthDayColumnToken(0),
+ m_Custom1ColumnToken(0),
+ m_Custom2ColumnToken(0),
+ m_Custom3ColumnToken(0),
+ m_Custom4ColumnToken(0),
+ m_NotesColumnToken(0),
+ m_LastModDateColumnToken(0),
+ m_PopularityIndexColumnToken(0),
+ m_AddressCharSetColumnToken(0) {}
+
+nsAddrDatabase::~nsAddrDatabase() {
+ Close(false); // better have already been closed.
+
+ // RemoveFromCache(this);
+ // clean up after ourself!
+ if (m_mdbPabTable) m_mdbPabTable->Release();
+ NS_IF_RELEASE(m_mdbStore);
+ NS_IF_RELEASE(m_mdbEnv);
+}
+
+nsresult nsAddrDatabase::GetMDBFactory(nsIMdbFactory** aMdbFactory) {
+ if (!mMdbFactory) {
+ nsresult rv;
+ nsCOMPtr<nsIMdbFactoryService> mdbFactoryService =
+ do_GetService("@mozilla.org/db/mork;1", &rv);
+ if (NS_SUCCEEDED(rv) && mdbFactoryService) {
+ rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!mMdbFactory) return NS_ERROR_FAILURE;
+ }
+ }
+ NS_ADDREF(*aMdbFactory = mMdbFactory);
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::SetDbPath(nsIFile* aDbPath) {
+ return aDbPath->Clone(getter_AddRefs(m_dbName));
+}
+
+// Open the MDB database synchronously. If successful, this routine
+// will set up the m_mdbStore and m_mdbEnv of the database object
+// so other database calls can work.
+nsresult nsAddrDatabase::OpenMDB(nsIFile* dbName, bool create) {
+ nsCOMPtr<nsIMdbFactory> mdbFactory;
+ nsresult ret = GetMDBFactory(getter_AddRefs(mdbFactory));
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv);
+ if (NS_SUCCEEDED(ret)) {
+ nsIMdbThumb* thumb = nullptr;
+
+ PathString filePath = dbName->NativePath();
+
+ nsIMdbHeap* dbHeap = nullptr;
+
+ if (m_mdbEnv) m_mdbEnv->SetAutoClear(true);
+
+ bool dbNameExists = false;
+ ret = dbName->Exists(&dbNameExists);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ if (!dbNameExists)
+ ret = NS_ERROR_FILE_NOT_FOUND;
+ else {
+ mdbOpenPolicy inOpenPolicy;
+ mdb_bool canOpen;
+ mdbYarn outFormatVersion;
+ nsIMdbFile* oldFile = nullptr;
+ int64_t fileSize;
+ ret = dbName->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ ret = mdbFactory->OpenOldFile(
+ m_mdbEnv, dbHeap, filePath.get(),
+ mdbBool_kFalse, // not readonly, we want modifiable
+ &oldFile);
+ if (oldFile) {
+ if (NS_SUCCEEDED(ret)) {
+ ret = mdbFactory->CanOpenFilePort(m_mdbEnv,
+ oldFile, // the file to investigate
+ &canOpen, &outFormatVersion);
+ if (NS_SUCCEEDED(ret) && canOpen) {
+ inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
+ inOpenPolicy.mOpenPolicy_MinMemory = 0;
+ inOpenPolicy.mOpenPolicy_MaxLazy = 0;
+
+ ret = mdbFactory->OpenFileStore(m_mdbEnv, dbHeap, oldFile,
+ &inOpenPolicy, &thumb);
+ } else if (fileSize != 0)
+ ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ NS_RELEASE(oldFile); // always release our file ref, store has own
+ }
+ if (NS_FAILED(ret)) ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
+ if (NS_SUCCEEDED(ret) && thumb) {
+ mdb_count outTotal; // total somethings to do in operation
+ mdb_count outCurrent; // subportion of total completed so far
+ mdb_bool outDone = false; // is operation finished?
+ mdb_bool outBroken; // is operation irreparably dead and broken?
+ do {
+ ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone,
+ &outBroken);
+ if (NS_FAILED(ret)) {
+ outDone = true;
+ break;
+ }
+ } while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
+ if (NS_SUCCEEDED(ret) && outDone) {
+ ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
+ if (NS_SUCCEEDED(ret) && m_mdbStore) {
+ ret = InitExistingDB();
+ create = false;
+ }
+ }
+ } else if (create && ret != NS_ERROR_FILE_ACCESS_DENIED) {
+ ret = NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IF_RELEASE(thumb);
+ }
+ return ret;
+}
+
+nsresult nsAddrDatabase::CloseMDB(bool commit) {
+ if (commit) return NS_ERROR_NOT_IMPLEMENTED;
+ //??? RemoveFromCache(this); // if we've closed it, better not leave it in
+ // the cache.
+ return NS_OK;
+}
+
+// force the database to close - this'll flush out anybody holding onto
+// a database without having a listener!
+// This is evil in the com world, but there are times we need to delete the
+// file.
+nsresult nsAddrDatabase::ForceClosed() {
+ nsresult err = NS_OK;
+
+ // make sure someone has a reference so object won't get deleted out from
+ // under us.
+ // NS_ADDREF_THIS();
+ // OK, remove from cache first and close the store.
+ // RemoveFromCache(this);
+
+ err = CloseMDB(false); // since we're about to delete it, no need to commit.
+ NS_IF_RELEASE(m_mdbStore);
+ // NS_RELEASE_THIS();
+ return err;
+}
+
+nsresult nsAddrDatabase::Close(bool forceCommit /* = TRUE */) {
+ return CloseMDB(forceCommit);
+}
+
+nsresult nsAddrDatabase::InitExistingDB() {
+ nsresult err = InitMDBInfo();
+ if (NS_SUCCEEDED(err)) {
+ if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
+
+ err = m_mdbStore->GetTable(m_mdbEnv, &gAddressBookTableOID, &m_mdbPabTable);
+ if (NS_SUCCEEDED(err) && m_mdbPabTable) {
+ // This code has always run here. Removing it fails an assertion in the
+ // Mork code which indicates a bad state. In the interest of saving
+ // effort, and since this whole file is doomed after the next release,
+ // I'm leaving it behind.
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsIMdbRow* findRow = nullptr;
+ mdb_pos rowPos = 0;
+
+ err = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+ if (NS_SUCCEEDED(err) && rowCursor) {
+ do {
+ err = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
+ } while (NS_SUCCEEDED(err) && findRow);
+ rowCursor->Release();
+ }
+ }
+ }
+ return err;
+}
+
+// initialize the various tokens and tables in our db's env
+nsresult nsAddrDatabase::InitMDBInfo() {
+ nsresult err = NS_OK;
+
+ if (!m_mdbTokensInitialized && m_mdbStore && m_mdbEnv) {
+ m_mdbTokensInitialized = true;
+ err = m_mdbStore->StringToToken(m_mdbEnv, kCardRowScope,
+ &m_CardRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kListRowScope,
+ &m_ListRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kDataRowScope,
+ &m_DataRowScopeToken);
+ gAddressBookTableOID.mOid_Scope = m_CardRowScopeToken;
+ gAddressBookTableOID.mOid_Id = ID_PAB_TABLE;
+ if (NS_SUCCEEDED(err)) {
+ m_mdbStore->StringToToken(m_mdbEnv, kUIDProperty, &m_UIDColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFirstNameProperty,
+ &m_FirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastNameProperty,
+ &m_LastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticFirstNameProperty,
+ &m_PhoneticFirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticLastNameProperty,
+ &m_PhoneticLastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDisplayNameProperty,
+ &m_DisplayNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNicknameProperty,
+ &m_NickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPriEmailProperty,
+ &m_PriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerPriEmailColumn,
+ &m_LowerPriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, k2ndEmailProperty,
+ &m_2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLower2ndEmailColumn,
+ &m_Lower2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPopularityIndexProperty,
+ &m_PopularityIndexColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneProperty,
+ &m_WorkPhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneProperty,
+ &m_HomePhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxProperty, &m_FaxColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerProperty, &m_PagerColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularProperty,
+ &m_CellularColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneTypeProperty,
+ &m_WorkPhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneTypeProperty,
+ &m_HomePhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxTypeProperty,
+ &m_FaxTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerTypeProperty,
+ &m_PagerTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularTypeProperty,
+ &m_CellularTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddressProperty,
+ &m_HomeAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddress2Property,
+ &m_HomeAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCityProperty,
+ &m_HomeCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeStateProperty,
+ &m_HomeStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeZipCodeProperty,
+ &m_HomeZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCountryProperty,
+ &m_HomeCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddressProperty,
+ &m_WorkAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddress2Property,
+ &m_WorkAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCityProperty,
+ &m_WorkCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkStateProperty,
+ &m_WorkStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkZipCodeProperty,
+ &m_WorkZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCountryProperty,
+ &m_WorkCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kJobTitleProperty,
+ &m_JobTitleColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDepartmentProperty,
+ &m_DepartmentColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCompanyProperty,
+ &m_CompanyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kScreenNameProperty,
+ &m_AimScreenNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryYearProperty,
+ &m_AnniversaryYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryMonthProperty,
+ &m_AnniversaryMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryDayProperty,
+ &m_AnniversaryDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kSpouseNameProperty,
+ &m_SpouseNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFamilyNameProperty,
+ &m_FamilyNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkWebPageProperty,
+ &m_WebPage1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeWebPageProperty,
+ &m_WebPage2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthYearProperty,
+ &m_BirthYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthMonthProperty,
+ &m_BirthMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthDayProperty,
+ &m_BirthDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom1Property,
+ &m_Custom1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom2Property,
+ &m_Custom2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom3Property,
+ &m_Custom3ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom4Property,
+ &m_Custom4ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNotesProperty, &m_NotesColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastModifiedDateProperty,
+ &m_LastModDateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kRecordKeyColumn,
+ &m_RecordKeyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAddressCharSetColumn,
+ &m_AddressCharSetColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastRecordKeyColumn,
+ &m_LastRecordKeyColumnToken);
+
+ err = m_mdbStore->StringToToken(m_mdbEnv, kPabTableKind, &m_PabTableKind);
+
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListName,
+ &m_ListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListNickName,
+ &m_ListNickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListDescription,
+ &m_ListDescriptionColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListTotalAddresses,
+ &m_ListTotalColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerListNameColumn,
+ &m_LowerListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDeletedCardsTableKind,
+ &m_DeletedCardsTableKind);
+ }
+ }
+ return err;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+uint32_t nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow) {
+ uint32_t count = 0;
+ GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0);
+ return count;
+}
+
+nsresult nsAddrDatabase::GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos,
+ nsIMdbRow** cardRow) {
+ if (!m_mdbStore || !listRow || !cardRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdb_token listAddressColumnToken;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ nsAutoString tempString;
+ mdb_id rowID;
+ nsresult err =
+ GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+
+ return GetCardRowByRowID(rowID, cardRow);
+}
+
+nsresult nsAddrDatabase::GetStringColumn(nsIMdbRow* cardRow, mdb_token outToken,
+ nsString& str) {
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell* cardCell;
+
+ if (cardRow && m_mdbEnv) {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell) {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ NS_ConvertUTF8toUTF16 uniStr((const char*)yarn.mYarn_Buf,
+ yarn.mYarn_Fill);
+ if (!uniStr.IsEmpty())
+ str.Assign(uniStr);
+ else
+ err = NS_ERROR_FAILURE;
+ cardCell->Release(); // always release ref
+ } else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+void nsAddrDatabase::YarnToUInt32(struct mdbYarn* yarn, uint32_t* pResult) {
+ uint8_t numChars = std::min<mdb_fill>(8, yarn->mYarn_Fill);
+ *pResult = MsgUnhex((char*)yarn->mYarn_Buf, numChars);
+}
+
+nsresult nsAddrDatabase::GetIntColumn(nsIMdbRow* cardRow, mdb_token outToken,
+ uint32_t* pValue, uint32_t defaultValue) {
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell* cardCell;
+
+ if (pValue) *pValue = defaultValue;
+ if (cardRow && m_mdbEnv) {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell) {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ YarnToUInt32(&yarn, pValue);
+ cardCell->Release();
+ } else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::InitCardFromRow(nsIAbCard* newCard,
+ nsIMdbRow* cardRow) {
+ nsresult rv = NS_OK;
+ if (!newCard || !cardRow || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIMdbRowCellCursor> cursor;
+ nsCOMPtr<nsIMdbCell> cell;
+
+ rv = cardRow->GetRowCellCursor(m_mdbEnv, -1, getter_AddRefs(cursor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mdb_column columnNumber;
+ char columnName[100];
+ struct mdbYarn colYarn = {columnName, 0, sizeof(columnName), 0, 0, nullptr};
+ struct mdbYarn cellYarn;
+
+ do {
+ rv = cursor->NextCell(m_mdbEnv, getter_AddRefs(cell), &columnNumber,
+ nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cell) break;
+
+ // Get the value of the cell
+ cell->AliasYarn(m_mdbEnv, &cellYarn);
+ NS_ConvertUTF8toUTF16 value(static_cast<const char*>(cellYarn.mYarn_Buf),
+ cellYarn.mYarn_Fill);
+
+ if (!value.IsEmpty()) {
+ // Get the column of the cell
+ // Mork makes this so hard...
+ rv = m_mdbStore->TokenToString(m_mdbEnv, columnNumber, &colYarn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char* name =
+ PL_strndup(static_cast<char*>(colYarn.mYarn_Buf), colYarn.mYarn_Fill);
+ newCard->SetPropertyAsAString(name, value);
+ PL_strfree(name);
+ }
+ } while (true);
+
+ uint32_t key = 0;
+ rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(rv)) newCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::GetListCardFromDB(nsIAbCard* listCard,
+ nsIMdbRow* listRow) {
+ nsresult err = NS_OK;
+ if (!listCard || !listRow) return NS_ERROR_NULL_POINTER;
+
+ nsAutoString tempString;
+
+ err = GetStringColumn(listRow, m_UIDColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
+ listCard->SetPropertyAsAString(kUIDProperty, tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
+ listCard->SetDisplayName(tempString);
+ listCard->SetLastName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
+ listCard->SetPropertyAsAString(kNicknameProperty, tempString);
+ }
+ err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
+ listCard->SetPropertyAsAString(kNotesProperty, tempString);
+ }
+ uint32_t key = 0;
+ err = GetIntColumn(listRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(err)) listCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+ return err;
+}
+
+class nsAddrDBEnumerator : public nsSimpleEnumerator {
+ public:
+ const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); }
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsAddrDBEnumerator methods:
+ explicit nsAddrDBEnumerator(nsAddrDatabase* aDb);
+ void Clear();
+
+ protected:
+ nsAddrDatabase* mDb;
+ nsIMdbTable* mDbTable;
+ nsCOMPtr<nsIMdbTableRowCursor> mRowCursor;
+ nsCOMPtr<nsIMdbRow> mCurrentRow;
+ mdb_pos mRowPos;
+};
+
+nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* aDb)
+ : mDb(aDb), mDbTable(aDb->GetPabTable()), mRowPos(-1) {}
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::HasMoreElements(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv()) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), mRowPos,
+ getter_AddRefs(rowCursor));
+ NS_ENSURE_TRUE(rowCursor, NS_ERROR_FAILURE);
+
+ mdbOid rowOid;
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ while (rowOid.mOid_Id != (mdb_id)-1) {
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope) ||
+ mDb->IsCardRowScopeToken(rowOid.mOid_Scope)) {
+ *aResult = true;
+
+ return NS_OK;
+ }
+
+ if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::GetNext(nsISupports** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv()) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mRowCursor) {
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), -1, getter_AddRefs(mRowCursor));
+ NS_ENSURE_TRUE(mRowCursor, NS_ERROR_FAILURE);
+ }
+
+ nsCOMPtr<nsIAbCard> resultCard;
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
+ while (mCurrentRow) {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(mCurrentRow->GetOid(mDb->GetEnv(), &rowOid))) {
+ nsresult rv;
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope)) {
+ rv = mDb->CreateABListCard(mCurrentRow, getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (mDb->IsCardRowScopeToken(rowOid.mOid_Scope)) {
+ rv = mDb->CreateABCard(mCurrentRow, 0, getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (resultCard) {
+ return CallQueryInterface(resultCard, aResult);
+ }
+ }
+
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+class nsListAddressEnumerator final : public nsSimpleEnumerator {
+ public:
+ const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); }
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsListAddressEnumerator methods:
+ nsListAddressEnumerator(nsAddrDatabase* aDb, mdb_id aRowID);
+
+ protected:
+ ~nsListAddressEnumerator() override = default;
+ nsAddrDatabase* mDb;
+ nsIMdbTable* mDbTable;
+ nsCOMPtr<nsIMdbRow> mListRow;
+ mdb_id mListRowID;
+ uint32_t mAddressTotal;
+ uint16_t mAddressPos;
+};
+
+nsListAddressEnumerator::nsListAddressEnumerator(nsAddrDatabase* aDb,
+ mdb_id aRowID)
+ : mDb(aDb),
+ mDbTable(aDb->GetPabTable()),
+ mListRowID(aRowID),
+ mAddressPos(0) {
+ mDb->GetListRowByRowID(mListRowID, getter_AddRefs(mListRow));
+ mAddressTotal = aDb->GetListAddressTotal(mListRow);
+}
+
+NS_IMETHODIMP
+nsListAddressEnumerator::HasMoreElements(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv()) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // In some cases it is possible that GetAddressRowByPos returns success,
+ // but currentRow is null. This is typically due to the fact that a card
+ // has been deleted from the parent and not the list. Whilst we have fixed
+ // that there are still a few dbs around there that we need to support
+ // correctly. Therefore, whilst processing lists ensure that we don't return
+ // false if the only thing stopping us is a blank row, just skip it and try
+ // the next one.
+ while (mAddressPos < mAddressTotal) {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos + 1,
+ getter_AddRefs(currentRow));
+
+ if (NS_SUCCEEDED(rv) && currentRow) {
+ *aResult = true;
+ break;
+ }
+
+ ++mAddressPos;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsListAddressEnumerator::GetNext(nsISupports** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv()) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ while (++mAddressPos <= mAddressTotal) {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos,
+ getter_AddRefs(currentRow));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIAbCard> resultCard;
+ rv =
+ mDb->CreateABCard(currentRow, mListRowID, getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(resultCard, aResult);
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsAddrDatabase::EnumerateCards(nsISimpleEnumerator** result) {
+ NS_ADDREF(*result = new nsAddrDBEnumerator(this));
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::EnumerateListAddresses(uint32_t listRowID,
+ nsISimpleEnumerator** result) {
+ NS_ADDREF(*result = new nsListAddressEnumerator(this, listRowID));
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::CreateCard(nsIMdbRow* cardRow, mdb_id listRowID,
+ nsIAbCard** result) {
+ if (!cardRow || !m_mdbEnv || !result) return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid))) rowID = outOid.mOid_Id;
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard =
+ do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ InitCardFromRow(personCard, cardRow);
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+
+ personCard.forget(result);
+ }
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID,
+ nsIAbCard** result) {
+ return CreateCard(cardRow, listRowID, result);
+}
+
+/* create a card for mailing list in the address book */
+nsresult nsAddrDatabase::CreateABListCard(nsIMdbRow* listRow,
+ nsIAbCard** result) {
+ if (!listRow || !m_mdbEnv || !result) return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid))) rowID = outOid.mOid_Id;
+
+ char* listURI = nullptr;
+
+ nsAutoString fileName;
+ rv = m_dbName->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ listURI = PR_smprintf("MailList%ld", rowID);
+
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard =
+ do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (personCard) {
+ GetListCardFromDB(personCard, listRow);
+
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+ personCard->SetIsMailList(true);
+ personCard->SetMailListURI(listURI);
+ }
+
+ personCard.forget(result);
+ if (listURI) PR_smprintf_free(listURI);
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::GetCardRowByRowID(mdb_id rowID, nsIMdbRow** dbRow) {
+ if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
+
+nsresult nsAddrDatabase::GetListRowByRowID(mdb_id rowID, nsIMdbRow** dbRow) {
+ if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
diff --git a/comm/mailnews/import/src/nsAddrDatabase.h b/comm/mailnews/import/src/nsAddrDatabase.h
new file mode 100644
index 0000000000..d667c92e47
--- /dev/null
+++ b/comm/mailnews/import/src/nsAddrDatabase.h
@@ -0,0 +1,158 @@
+/* -*- 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 _nsAddrDatabase_H_
+#define _nsAddrDatabase_H_
+
+#include "nsIAbCard.h"
+#include "nsIFile.h"
+#include "mdb.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+
+class nsAddrDatabase {
+ using PathString = mozilla::PathString;
+
+ public:
+ nsresult SetDbPath(nsIFile* aDbPath);
+ nsresult Close(bool forceCommit);
+ nsresult OpenMDB(nsIFile* dbName, bool create);
+ nsresult CloseMDB(bool commit);
+ nsresult ForceClosed(void);
+ nsresult EnumerateCards(nsISimpleEnumerator** _retval);
+ nsresult EnumerateListAddresses(uint32_t listRowID,
+ nsISimpleEnumerator** _retval);
+
+ nsAddrDatabase();
+ virtual ~nsAddrDatabase();
+
+ nsresult GetMDBFactory(nsIMdbFactory** aMdbFactory);
+ nsIMdbEnv* GetEnv() { return m_mdbEnv; }
+ uint32_t GetCurVersion();
+ nsIMdbTableRowCursor* GetTableRowCursor();
+ nsIMdbTable* GetPabTable() { return m_mdbPabTable; }
+
+ nsresult CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID,
+ nsIAbCard** result);
+ nsresult CreateABListCard(nsIMdbRow* listRow, nsIAbCard** result);
+
+ bool IsListRowScopeToken(mdb_scope scope) {
+ return (scope == m_ListRowScopeToken) ? true : false;
+ }
+ bool IsCardRowScopeToken(mdb_scope scope) {
+ return (scope == m_CardRowScopeToken) ? true : false;
+ }
+ bool IsDataRowScopeToken(mdb_scope scope) {
+ return (scope == m_DataRowScopeToken) ? true : false;
+ }
+ nsresult GetCardRowByRowID(mdb_id rowID, nsIMdbRow** dbRow);
+ nsresult GetListRowByRowID(mdb_id rowID, nsIMdbRow** dbRow);
+
+ uint32_t GetListAddressTotal(nsIMdbRow* listRow);
+ nsresult GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos,
+ nsIMdbRow** cardRow);
+
+ nsresult InitCardFromRow(nsIAbCard* aNewCard, nsIMdbRow* aCardRow);
+
+ protected:
+ void YarnToUInt32(struct mdbYarn* yarn, uint32_t* pResult);
+ nsresult GetStringColumn(nsIMdbRow* cardRow, mdb_token outToken,
+ nsString& str);
+ nsresult GetIntColumn(nsIMdbRow* cardRow, mdb_token outToken,
+ uint32_t* pValue, uint32_t defaultValue);
+ nsresult GetListCardFromDB(nsIAbCard* listCard, nsIMdbRow* listRow);
+ nsresult CreateCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard** result);
+
+ // mdb bookkeeping stuff
+ nsresult InitExistingDB();
+ nsresult InitMDBInfo();
+
+ nsIMdbEnv* m_mdbEnv; // to be used in all the db calls.
+ nsIMdbStore* m_mdbStore;
+ nsIMdbTable* m_mdbPabTable;
+ nsCOMPtr<nsIFile> m_dbName;
+ bool m_mdbTokensInitialized;
+
+ mdb_kind m_PabTableKind;
+ mdb_kind m_DeletedCardsTableKind;
+
+ mdb_scope m_CardRowScopeToken;
+ mdb_scope m_ListRowScopeToken;
+ mdb_scope m_DataRowScopeToken;
+
+ mdb_token m_UIDColumnToken;
+ mdb_token m_FirstNameColumnToken;
+ mdb_token m_LastNameColumnToken;
+ mdb_token m_PhoneticFirstNameColumnToken;
+ mdb_token m_PhoneticLastNameColumnToken;
+ mdb_token m_DisplayNameColumnToken;
+ mdb_token m_NickNameColumnToken;
+ mdb_token m_PriEmailColumnToken;
+ mdb_token m_2ndEmailColumnToken;
+ mdb_token m_DefaultEmailColumnToken;
+ mdb_token m_CardTypeColumnToken;
+ mdb_token m_WorkPhoneColumnToken;
+ mdb_token m_HomePhoneColumnToken;
+ mdb_token m_FaxColumnToken;
+ mdb_token m_PagerColumnToken;
+ mdb_token m_CellularColumnToken;
+ mdb_token m_WorkPhoneTypeColumnToken;
+ mdb_token m_HomePhoneTypeColumnToken;
+ mdb_token m_FaxTypeColumnToken;
+ mdb_token m_PagerTypeColumnToken;
+ mdb_token m_CellularTypeColumnToken;
+ mdb_token m_HomeAddressColumnToken;
+ mdb_token m_HomeAddress2ColumnToken;
+ mdb_token m_HomeCityColumnToken;
+ mdb_token m_HomeStateColumnToken;
+ mdb_token m_HomeZipCodeColumnToken;
+ mdb_token m_HomeCountryColumnToken;
+ mdb_token m_WorkAddressColumnToken;
+ mdb_token m_WorkAddress2ColumnToken;
+ mdb_token m_WorkCityColumnToken;
+ mdb_token m_WorkStateColumnToken;
+ mdb_token m_WorkZipCodeColumnToken;
+ mdb_token m_WorkCountryColumnToken;
+ mdb_token m_JobTitleColumnToken;
+ mdb_token m_DepartmentColumnToken;
+ mdb_token m_CompanyColumnToken;
+ mdb_token m_AimScreenNameColumnToken;
+ mdb_token m_AnniversaryYearColumnToken;
+ mdb_token m_AnniversaryMonthColumnToken;
+ mdb_token m_AnniversaryDayColumnToken;
+ mdb_token m_SpouseNameColumnToken;
+ mdb_token m_FamilyNameColumnToken;
+ mdb_token m_DefaultAddressColumnToken;
+ mdb_token m_CategoryColumnToken;
+ mdb_token m_WebPage1ColumnToken;
+ mdb_token m_WebPage2ColumnToken;
+ mdb_token m_BirthYearColumnToken;
+ mdb_token m_BirthMonthColumnToken;
+ mdb_token m_BirthDayColumnToken;
+ mdb_token m_Custom1ColumnToken;
+ mdb_token m_Custom2ColumnToken;
+ mdb_token m_Custom3ColumnToken;
+ mdb_token m_Custom4ColumnToken;
+ mdb_token m_NotesColumnToken;
+ mdb_token m_LastModDateColumnToken;
+ mdb_token m_RecordKeyColumnToken;
+ mdb_token m_LowerPriEmailColumnToken;
+ mdb_token m_Lower2ndEmailColumnToken;
+
+ mdb_token m_PopularityIndexColumnToken;
+
+ mdb_token m_AddressCharSetColumnToken;
+ mdb_token m_LastRecordKeyColumnToken;
+
+ mdb_token m_ListNameColumnToken;
+ mdb_token m_ListNickNameColumnToken;
+ mdb_token m_ListDescriptionColumnToken;
+ mdb_token m_ListTotalColumnToken;
+ mdb_token m_LowerListNameColumnToken;
+
+ nsCOMPtr<nsIMdbFactory> mMdbFactory;
+};
+
+#endif
diff --git a/comm/mailnews/import/src/nsAppleMailImport.cpp b/comm/mailnews/import/src/nsAppleMailImport.cpp
new file mode 100644
index 0000000000..0404014527
--- /dev/null
+++ b/comm/mailnews/import/src/nsAppleMailImport.cpp
@@ -0,0 +1,609 @@
+/* -*- 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 "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportService.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportGeneric.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIFile.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsNetUtil.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Components.h"
+
+#include "nsEmlxHelperUtils.h"
+#include "nsAppleMailImport.h"
+#include "nsIOutputStream.h"
+
+// 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() {
+ IMPORT_LOG0("nsAppleMailImportModule Created");
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::components::StringBundle::Service();
+ 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) {
+ if (!mBundle) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoString name;
+ nsresult rv = mBundle->GetStringFromName("ApplemailImportName", name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aName = ToNewUnicode(name);
+ return rv;
+}
+
+NS_IMETHODIMP nsAppleMailImportModule::GetDescription(char16_t** aName) {
+ if (!mBundle) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoString name;
+ nsresult rv = mBundle->GetStringFromName("ApplemailImportDescription", name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aName = ToNewUnicode(name);
+ return rv;
+}
+
+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("ApplemailImportName", 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::components::StringBundle::Service();
+ 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(nsLiteralCString(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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes) {
+ NS_ENSURE_ARG_POINTER(aMailboxFile);
+
+ IMPORT_LOG0("FindMailboxes for Apple mail invoked");
+
+ boxes.Clear();
+ 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);
+
+ mCurDepth = 1;
+
+ // 1. look for accounts with mailboxes
+ FindAccountMailDirs(aMailboxFile, boxes, 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(u"Mailboxes"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ IMPORT_LOG0("Looking for global Apple mailboxes");
+
+ mCurDepth++;
+ rv = FindMboxDirs(mailboxesDir, boxes, importService);
+ mCurDepth--;
+ }
+ }
+ }
+ 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, nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aMailboxDescs,
+ nsIImportService* aImportService) {
+ nsCOMPtr<nsIDirectoryEnumerator> 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;
+ directoryEnumerator->GetNextFile(getter_AddRefs(currentEntry));
+ 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, u"POP-"_ns)) {
+ // cut off "POP-" prefix so we get a nice folder name
+ folderName.Cut(0, 4);
+ isAccountFolder = true;
+ } else if (StringBeginsWith(folderName, u"IMAP-"_ns)) {
+ // 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;
+ rv = aImportService->CreateNewMailboxDescriptor(getter_AddRefs(desc));
+ if (NS_FAILED(rv)) continue;
+ desc->SetSize(1);
+ desc->SetDepth(mCurDepth);
+ desc->SetDisplayName(folderName.get());
+ desc->SetIdentifier(kAccountMailboxID);
+
+ nsCOMPtr<nsIFile> mailboxDescFile;
+ rv = desc->GetFile(getter_AddRefs(mailboxDescFile));
+ if (NS_FAILED(rv) || !mailboxDescFile) continue;
+
+ mailboxDescFile->InitWithFile(currentEntry);
+
+ // add this mailbox descriptor to the list
+ aMailboxDescs.AppendElement(desc);
+
+ // 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& 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_FROM_CSTRING(POP_MBOX_SUFFIX)))
+ folderName.SetLength(folderName.Length() - 5);
+ else if (StringEndsWith(folderName,
+ NS_LITERAL_STRING_FROM_CSTRING(IMAP_MBOX_SUFFIX)))
+ folderName.SetLength(folderName.Length() - 9);
+ else if (StringBeginsWith(folderName, u"POP-"_ns))
+ folderName.Cut(4, folderName.Length());
+ else if (StringBeginsWith(folderName, u"IMAP-"_ns))
+ 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(u"Messages"_ns);
+ 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<nsIDirectoryEnumerator> dirEnumerator;
+ messagesFolder->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
+ if (dirEnumerator) {
+ bool hasMore = false;
+ while (NS_SUCCEEDED(dirEnumerator->HasMoreElements(&hasMore)) &&
+ hasMore) {
+ nsCOMPtr<nsIFile> file;
+ dirEnumerator->GetNextFile(getter_AddRefs(file));
+ 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);
+ }
+
+ 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aMailboxDescs,
+ nsIImportService* aImportService) {
+ NS_ENSURE_ARG_POINTER(aFolder);
+ 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<nsIDirectoryEnumerator> 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;
+ directoryEnumerator->GetNextFile(getter_AddRefs(currentEntry));
+ 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_FROM_CSTRING(POP_MBOX_SUFFIX)) ||
+ StringEndsWith(currentFolderName,
+ NS_LITERAL_STRING_FROM_CSTRING(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_FROM_CSTRING(IMAP_MBOX_SUFFIX)))
+ siblingMailboxDirPath.SetLength(siblingMailboxDirPath.Length() - 9);
+ else if (StringEndsWith(siblingMailboxDirPath,
+ NS_LITERAL_STRING_FROM_CSTRING(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(u"Messages"_ns);
+ 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<nsIDirectoryEnumerator> 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;
+ directoryEnumerator->GetNextFile(getter_AddRefs(currentEntry));
+ 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, u".emlx"_ns)) continue;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = msgStore->GetNewMsgOutputStream(aDstFolder, getter_AddRefs(msgHdr),
+ 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);
+ outStream = nullptr;
+ } else {
+ msgStore->DiscardNewMessage(outStream, msgHdr);
+ outStream = nullptr;
+ break;
+ }
+ }
+ }
+ // 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;
+ AutoTArray<nsString, 1> fmt = {aName};
+ nsresult rv = mBundle->FormatStringFromName(
+ NS_ConvertUTF16toUTF8(aErrorName).get(), fmt, 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/comm/mailnews/import/src/nsAppleMailImport.h b/comm/mailnews/import/src/nsAppleMailImport.h
new file mode 100644
index 0000000000..dd799b06be
--- /dev/null
+++ b/comm/mailnews/import/src/nsAppleMailImport.h
@@ -0,0 +1,86 @@
+/* -*- 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"
+#include "ImportDebug.h"
+
+#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 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aMailboxDescs,
+ nsIImportService* aImportService);
+ nsresult FindMboxDirs(
+ nsIFile* aFolder,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aMailboxDescs,
+ nsIImportService* aImportService);
+ nsresult AddMboxDir(
+ nsIFile* aFolder,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& 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/comm/mailnews/import/src/nsBeckyAddressBooks.cpp b/comm/mailnews/import/src/nsBeckyAddressBooks.cpp
new file mode 100644
index 0000000000..651bbbeb94
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyAddressBooks.cpp
@@ -0,0 +1,311 @@
+/* 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 "nsIDirectoryEnumerator.h"
+#include "nsString.h"
+#include "nsIImportService.h"
+#include "nsIImportABDescriptor.h"
+#include "nsMsgUtils.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);
+ NS_ADDREF(*aImport = new nsBeckyAddressBooks());
+ 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("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(u"AddrBook"_ns);
+ 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, u".bab"_ns);
+}
+
+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<nsIDirectoryEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ 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<nsIDirectoryEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ uint32_t total = 0;
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ 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, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ 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);
+
+ books.AppendElement(descriptor);
+ return NS_OK;
+}
+
+// Recursively descend down the dirs, appending to the books array.
+nsresult nsBeckyAddressBooks::CollectAddressBooks(
+ nsIFile* aTarget, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ nsresult rv = AppendAddressBookDescriptor(aTarget, books);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDirectoryEnumerator> entries;
+ rv = aTarget->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDirectory = false;
+ rv = file->IsDirectory(&isDirectory);
+ if (NS_SUCCEEDED(rv) && isDirectory) {
+ rv = CollectAddressBooks(file, books);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::FindAddressBooks(
+ nsIFile* aLocation, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ books.Clear();
+ bool isDirectory = false;
+ nsresult rv = aLocation->IsDirectory(&isDirectory);
+ if (NS_FAILED(rv) || !isDirectory) return NS_ERROR_FAILURE;
+
+ rv = CollectAddressBooks(aLocation, books);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::InitFieldMap(nsIImportFieldMap* aFieldMap) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsBeckyAddressBooks::ImportAddressBook(
+ nsIImportABDescriptor* aSource, nsIAbDirectory* 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<nsIDirectoryEnumerator> entries;
+ rv = file->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ nsAutoString error;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ 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("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/comm/mailnews/import/src/nsBeckyAddressBooks.h b/comm/mailnews/import/src/nsBeckyAddressBooks.h
new file mode 100644
index 0000000000..af19ea1917
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyAddressBooks.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 nsBeckyAddressBooks_h___
+#define nsBeckyAddressBooks_h___
+
+#include "nsIImportAddressBooks.h"
+#include "nsIFile.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,
+ nsTArray<RefPtr<nsIImportABDescriptor>>& books);
+ nsresult FindAddressBookDirectory(nsIFile** aAddressBookDirectory);
+ nsresult AppendAddressBookDescriptor(
+ nsIFile* aEntry, nsTArray<RefPtr<nsIImportABDescriptor>>& books);
+ uint32_t CountAddressBookSize(nsIFile* aDirectory);
+ bool HasAddressBookFile(nsIFile* aDirectory);
+ bool IsAddressBookFile(nsIFile* aFile);
+ nsresult CreateAddressBookDescriptor(nsIImportABDescriptor** aDescriptor);
+};
+
+#endif /* nsBeckyAddressBooks_h___ */
diff --git a/comm/mailnews/import/src/nsBeckyFilters.cpp b/comm/mailnews/import/src/nsBeckyFilters.cpp
new file mode 100644
index 0000000000..196560311b
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyFilters.cpp
@@ -0,0 +1,711 @@
+/* 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 "nsILineInputStream.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 "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);
+ NS_ADDREF(*aImport = new nsBeckyFilters());
+ 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(u"IFilter.def"_ns);
+ else
+ rv = filter->Append(u"OFilter.def"_ns);
+ 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("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(u"No name"_ns);
+ 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;
+ mServer->GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_STATE(filterList);
+
+ nsCOMPtr<nsIMsgFilter> filter;
+ nsresult 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;
+ mServer->GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_STATE(filterList);
+
+ uint32_t count;
+ nsresult 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, "$X"_ns) && 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) {
+ NS_ADDREF(*_retval = found);
+ return NS_OK;
+ }
+
+ nsTArray<RefPtr<nsIMsgFolder>> children;
+ rv = aParentFolder->GetSubFolders(children);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (nsIMsgFolder* child : children) {
+ rv = FindMessageFolder(aName, child, getter_AddRefs(found));
+ if (found) {
+ NS_ADDREF(*_retval = found);
+ 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("@mozilla.org/messenger/account-manager;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsTArray<RefPtr<nsIMsgAccount>> accounts;
+ rv = accountManager->GetAccounts(accounts);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> found;
+ for (auto account : accounts) {
+ 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("@mozilla.org/messenger/account-manager;1", &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/comm/mailnews/import/src/nsBeckyFilters.h b/comm/mailnews/import/src/nsBeckyFilters.h
new file mode 100644
index 0000000000..91ee2ed813
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyFilters.h
@@ -0,0 +1,73 @@
+/* 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 "nsString.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsMsgFilterCore.h"
+
+class nsIMsgFilter;
+class nsIMsgRuleAction;
+
+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/comm/mailnews/import/src/nsBeckyImport.cpp b/comm/mailnews/import/src/nsBeckyImport.cpp
new file mode 100644
index 0000000000..8b56ba95d3
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyImport.cpp
@@ -0,0 +1,143 @@
+/* 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 "nsIImportService.h"
+#include "nsIImportMail.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportSettings.h"
+#include "nsIImportFilters.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsTextFormatter.h"
+#include "nsUnicharUtils.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("BeckyImportName");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBeckyImport::GetDescription(char16_t** aDescription) {
+ NS_ENSURE_ARG_POINTER(aDescription);
+ *aDescription =
+ nsBeckyStringBundle::GetStringByName("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("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/comm/mailnews/import/src/nsBeckyImport.h b/comm/mailnews/import/src/nsBeckyImport.h
new file mode 100644
index 0000000000..0884d417fb
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyImport.h
@@ -0,0 +1,38 @@
+/* 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/comm/mailnews/import/src/nsBeckyMail.cpp b/comm/mailnews/import/src/nsBeckyMail.cpp
new file mode 100644
index 0000000000..36ab1e1104
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyMail.cpp
@@ -0,0 +1,566 @@
+/* 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 "nsIImportService.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsMsgUtils.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgMessageFlags.h"
+#include "nsTArray.h"
+#include "nspr.h"
+#include "nsThreadUtils.h"
+#include "nsIDirectoryEnumerator.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);
+ NS_ADDREF(*aImport = new nsBeckyMail());
+ 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& 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);
+
+ return NS_OK;
+}
+
+nsresult nsBeckyMail::CollectMailboxesInFolderListFile(
+ nsIFile* aListFile, uint32_t aDepth,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& 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<nsIDirectoryEnumerator> entries;
+ rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString name;
+ rv = file->GetLeafName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (StringEndsWith(name, u".bmf"_ns)) {
+ 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, u"#"_ns) || StringBeginsWith(name, u"!"_ns))
+ 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, nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ boxes.Clear();
+ nsresult rv = CollectMailboxesInDirectory(aLocation, 0, boxes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ 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.EqualsLiteral(".");
+}
+
+class ImportMessageRunnable : public mozilla::Runnable {
+ public:
+ ImportMessageRunnable(nsIFile* aMessageFile, nsIMsgFolder* aFolder);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ 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)
+ : mozilla::Runnable("ImportMessageRunnable"),
+ 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(u"#Attach"_ns);
+ 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;
+ mResult = mFolder->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(mResult, NS_OK);
+
+ nsCOMPtr<nsILineInputStream> lineStream;
+ mResult = nsBeckyUtils::CreateLineInputStream(mMessageFile,
+ getter_AddRefs(lineStream));
+ NS_ENSURE_SUCCESS(mResult, NS_OK);
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ nsCOMPtr<nsIOutputStream> outputStream;
+ mResult = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr),
+ getter_AddRefs(outputStream));
+ NS_ENSURE_SUCCESS(mResult, NS_OK);
+
+ bool inHeader = true;
+ bool more = true;
+ nsAutoCString headers;
+ while (NS_SUCCEEDED(mResult) && more) {
+ nsAutoCString line;
+ mResult = lineStream->ReadLine(line, &more);
+ if (NS_FAILED(mResult)) break;
+
+ if (inHeader) {
+ if (IsEndOfHeaders(line)) {
+ inHeader = false;
+ mResult = WriteHeaders(headers, outputStream);
+ } else {
+ mResult = HandleHeaderLine(line, headers);
+ }
+ } else if (IsEndOfMessage(line)) {
+ inHeader = true;
+ mResult = msgStore->FinishNewMessage(outputStream, msgHdr);
+ // outputStream is closed by FinishNewMessage().
+ outputStream = nullptr;
+ mResult = msgStore->GetNewMsgOutputStream(mFolder, getter_AddRefs(msgHdr),
+ getter_AddRefs(outputStream));
+ } else if (IsBeckyIncludeLine(line)) {
+ mResult = WriteAttachmentFile(mMessageFile, line, outputStream);
+ } else {
+ uint32_t writtenBytes = 0;
+ if (StringBeginsWith(line, ".."_ns))
+ line.Cut(0, 1);
+ else if (CheckHeaderKey(line, "From"))
+ line.Insert('>', 0);
+
+ line.AppendLiteral(MSG_LINEBREAK);
+ mResult = outputStream->Write(line.get(), line.Length(), &writtenBytes);
+ }
+ }
+
+ if (outputStream) {
+ // DiscardNewMessage() closes outputStream.
+ if (NS_FAILED(mResult))
+ msgStore->DiscardNewMessage(outputStream, msgHdr);
+ else
+ outputStream->Close(); /* No check? */
+ }
+
+ return NS_OK;
+}
+
+static nsresult ProxyImportMessage(nsIFile* aMessageFile,
+ nsIMsgFolder* aFolder) {
+ RefPtr<ImportMessageRunnable> importMessage =
+ new ImportMessageRunnable(aMessageFile, aFolder);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyImportMessage"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(importMessage));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return importMessage->mResult;
+}
+
+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;
+ AutoTArray<nsString, 1> format = {name};
+ rv = nsBeckyStringBundle::FormatStringFromName("BeckyImportMailboxSuccess",
+ format, 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/comm/mailnews/import/src/nsBeckyMail.h b/comm/mailnews/import/src/nsBeckyMail.h
new file mode 100644
index 0000000000..e0b445b1fe
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyMail.h
@@ -0,0 +1,41 @@
+/* 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 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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aCollected);
+ nsresult CollectMailboxesInFolderListFile(
+ nsIFile* aListFile, uint32_t aDepth,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aCollected);
+ nsresult AppendMailboxDescriptor(
+ nsIFile* aEntry, const nsString& aName, uint32_t aDepth,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& aCollected);
+ nsresult ImportMailFile(nsIFile* aMailFile, nsIMsgFolder* aDestination);
+ nsresult CreateMailboxDescriptor(nsIImportMailboxDescriptor** aDescriptor);
+ nsresult GetMailboxName(nsIFile* aMailbox, nsAString& aName);
+};
+
+#endif /* nsBeckyMail_h___ */
diff --git a/comm/mailnews/import/src/nsBeckySettings.cpp b/comm/mailnews/import/src/nsBeckySettings.cpp
new file mode 100644
index 0000000000..9722a62007
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckySettings.cpp
@@ -0,0 +1,379 @@
+/* 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 "nsIMsgAccountManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIINIParser.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "msgCore.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);
+ NS_ADDREF(*aImport = new nsBeckySettings());
+ 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("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("@mozilla.org/messengercompose/smtp;1", &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("@mozilla.org/messenger/account-manager;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIncomingServer> incomingServer;
+ accountManager->FindServer(aUserName, aServerName, aProtocol, 0,
+ getter_AddRefs(incomingServer));
+
+ if (!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("Account"_ns, "SMTPServer"_ns, serverName);
+ mParser->GetString("Account"_ns, "UserID"_ns, 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("Account"_ns, "SMTPPort"_ns, value);
+ int32_t port = 25;
+ if (NS_SUCCEEDED(rv)) {
+ nsresult errorCode;
+ port = value.ToInteger(&errorCode, 10);
+ }
+ server->SetPort(port);
+
+ mParser->GetString("Account"_ns, "SSLSMTP"_ns, value);
+ if (value.EqualsLiteral("1")) server->SetSocketType(nsMsgSocketType::SSL);
+
+ mParser->GetString("Account"_ns, "SMTPAUTH"_ns, value);
+ if (value.EqualsLiteral("1")) {
+ mParser->GetString("Account"_ns, "SMTPAUTHMODE"_ns, value);
+ nsMsgAuthMethodValue authMethod = nsMsgAuthMethod::none;
+ if (value.EqualsLiteral("1")) {
+ authMethod = nsMsgAuthMethod::passwordEncrypted;
+ } else if (value.EqualsLiteral("2") || value.EqualsLiteral("4") ||
+ value.EqualsLiteral("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("Account"_ns, "POP3Auth"_ns,
+ value); // 0: plain, 1: APOP, 2: CRAM-MD5, 3: NTLM
+ nsMsgAuthMethodValue authMethod;
+ if (value.IsEmpty() || value.EqualsLiteral("0")) {
+ authMethod = nsMsgAuthMethod::passwordCleartext;
+ } else if (value.EqualsLiteral("1")) {
+ authMethod = nsMsgAuthMethod::old;
+ } else if (value.EqualsLiteral("2")) {
+ authMethod = nsMsgAuthMethod::passwordEncrypted;
+ } else if (value.EqualsLiteral("3")) {
+ authMethod = nsMsgAuthMethod::NTLM;
+ } else {
+ authMethod = nsMsgAuthMethod::none;
+ }
+ aServer->SetAuthMethod(authMethod);
+
+ mParser->GetString("Account"_ns, "LeaveServer"_ns, value);
+ if (value.EqualsLiteral("1")) {
+ pop3Server->SetLeaveMessagesOnServer(true);
+ nsresult rv = mParser->GetString("Account"_ns, "KeepDays"_ns, 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("Account"_ns, "Protocol"_ns, value);
+ nsCString protocol;
+ if (value.EqualsLiteral("1")) {
+ protocol = "imap"_ns;
+ } else {
+ protocol = "pop3"_ns;
+ }
+
+ nsAutoCString userName, serverName;
+ mParser->GetString("Account"_ns, "MailServer"_ns, serverName);
+ mParser->GetString("Account"_ns, "UserID"_ns, 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("Account"_ns, "POP3Port"_ns, value);
+ if (NS_SUCCEEDED(rv))
+ port = value.ToInteger(&errorCode, 10);
+ else
+ port = 110;
+ mParser->GetString("Account"_ns, "SSLPOP"_ns, value);
+ if (value.EqualsLiteral("1")) isSecure = true;
+ } else if (protocol.EqualsLiteral("imap")) {
+ rv = mParser->GetString("Account"_ns, "IMAP4Port"_ns, value);
+ if (NS_SUCCEEDED(rv))
+ port = value.ToInteger(&errorCode, 10);
+ else
+ port = 143;
+ mParser->GetString("Account"_ns, "SSLIMAP"_ns, value);
+ if (value.EqualsLiteral("1")) isSecure = true;
+ }
+
+ server->SetPort(port);
+ if (isSecure) server->SetSocketType(nsMsgSocketType::SSL);
+
+ mParser->GetString("Account"_ns, "CheckInt"_ns, value);
+ if (value.EqualsLiteral("1")) server->SetDoBiff(true);
+ rv = mParser->GetString("Account"_ns, "CheckEvery"_ns, 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("Account"_ns, "Name"_ns, identityName);
+ mParser->GetString("Account"_ns, "YourName"_ns, fullName);
+ mParser->GetString("Account"_ns, "MailAddress"_ns, email);
+ mParser->GetString("Account"_ns, "PermBcc"_ns, bccAddress);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &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("@mozilla.org/messenger/account-manager;1", &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/comm/mailnews/import/src/nsBeckySettings.h b/comm/mailnews/import/src/nsBeckySettings.h
new file mode 100644
index 0000000000..bbbe9fe268
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckySettings.h
@@ -0,0 +1,50 @@
+/* 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/comm/mailnews/import/src/nsBeckyStringBundle.cpp b/comm/mailnews/import/src/nsBeckyStringBundle.cpp
new file mode 100644
index 0000000000..1037fdafb2
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyStringBundle.cpp
@@ -0,0 +1,55 @@
+/* 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 "nsServiceManagerUtils.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "nsBeckyStringBundle.h"
+
+#define BECKY_MESSAGES_URL \
+ "chrome://messenger/locale/beckyImportMsgs.properties"
+
+nsCOMPtr<nsIStringBundle> nsBeckyStringBundle::mBundle = nullptr;
+
+void nsBeckyStringBundle::GetStringBundle(void) {
+ if (mBundle) return;
+
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && bundleService)
+ rv = bundleService->CreateBundle(BECKY_MESSAGES_URL,
+ getter_AddRefs(mBundle));
+}
+
+void nsBeckyStringBundle::EnsureStringBundle(void) {
+ if (!mBundle) GetStringBundle();
+}
+
+char16_t* nsBeckyStringBundle::GetStringByName(const char* aName) {
+ EnsureStringBundle();
+
+ if (mBundle) {
+ nsAutoString string;
+ mBundle->GetStringFromName(aName, string);
+ return ToNewUnicode(string);
+ }
+
+ return nullptr;
+}
+
+nsresult nsBeckyStringBundle::FormatStringFromName(const char* name,
+ nsTArray<nsString>& params,
+ nsAString& _retval) {
+ EnsureStringBundle();
+
+ return mBundle->FormatStringFromName(name, params, _retval);
+}
+
+void nsBeckyStringBundle::Cleanup(void) { mBundle = nullptr; }
diff --git a/comm/mailnews/import/src/nsBeckyStringBundle.h b/comm/mailnews/import/src/nsBeckyStringBundle.h
new file mode 100644
index 0000000000..2b7045ebcc
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyStringBundle.h
@@ -0,0 +1,32 @@
+/* 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 char* name);
+ static nsresult FormatStringFromName(const char* name,
+ nsTArray<nsString>& params,
+ nsAString& _retval);
+ static void GetStringBundle(void);
+ static void EnsureStringBundle(void);
+ static void Cleanup(void);
+
+ private:
+ static nsCOMPtr<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/comm/mailnews/import/src/nsBeckyUtils.cpp b/comm/mailnews/import/src/nsBeckyUtils.cpp
new file mode 100644
index 0000000000..ecb1c35a53
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyUtils.cpp
@@ -0,0 +1,302 @@
+/* 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 "nsIDirectoryEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsString.h"
+#include "nsMsgI18N.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"
+#include "SpecialSystemDirectory.h"
+
+nsresult nsBeckyUtils::FindUserDirectoryOnWindows7(nsIFile** aLocation) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> directory;
+ rv = GetSpecialSystemDirectory(Win_Documents, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = directory->AppendNative("Becky"_ns);
+ 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(u"C:\\Becky!"_ns);
+ 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<nsIDirectoryEnumerator> entries;
+ rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more;
+ while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ 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(u"Folder.lst"_ns);
+ 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(u"Mailbox.ini"_ns);
+ 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("Account"_ns, "Name"_ns, 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);
+
+ nsAutoCString sourceCharset;
+ rv = MsgDetectCharsetFromFile(aSourceFile, sourceCharset);
+ 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, sourceCharset.get(), kBlock, 0x0000);
+
+ nsCOMPtr<nsIConverterOutputStream> convertedOutput =
+ do_CreateInstance("@mozilla.org/intl/converter-output-stream;1");
+ convertedOutput->Init(destination, "UTF-8");
+
+ 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_FROM_CSTRING(kDestTrashFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!inbox"))
+ _retval = NS_LITERAL_STRING_FROM_CSTRING(kDestInboxFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!outbox"))
+ _retval = NS_LITERAL_STRING_FROM_CSTRING(kDestSentFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("!!!!unsent"))
+ _retval = NS_LITERAL_STRING_FROM_CSTRING(kDestUnsentMessagesFolderName);
+ else
+ _retval = aFolderName;
+
+ return NS_OK;
+}
diff --git a/comm/mailnews/import/src/nsBeckyUtils.h b/comm/mailnews/import/src/nsBeckyUtils.h
new file mode 100644
index 0000000000..9a0529b5dc
--- /dev/null
+++ b/comm/mailnews/import/src/nsBeckyUtils.h
@@ -0,0 +1,35 @@
+/* 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__
+
+#include "nsString.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/comm/mailnews/import/src/nsEmlxHelperUtils.h b/comm/mailnews/import/src/nsEmlxHelperUtils.h
new file mode 100644
index 0000000000..a7e0e69f11
--- /dev/null
+++ b/comm/mailnews/import/src/nsEmlxHelperUtils.h
@@ -0,0 +1,61 @@
+/* -*- 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 "nsString.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/comm/mailnews/import/src/nsEmlxHelperUtils.mm b/comm/mailnews/import/src/nsEmlxHelperUtils.mm
new file mode 100644
index 0000000000..a0c5aaaee2
--- /dev/null
+++ b/comm/mailnews/import/src/nsEmlxHelperUtils.mm
@@ -0,0 +1,230 @@
+/* -*- 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 "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);
+
+ chunkStart = foundFromLines[i];
+ }
+ aOutBuffer.Append(chunkStart, (aMessageBufferEnd - chunkStart));
+ }
+
+ return NS_OK;
+}
+
+nsresult nsEmlxHelperUtils::AddEmlxMessageToStream(nsIFile* aMessage, nsIOutputStream* aOut) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ // 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;
+
+ constexpr auto kBogusFromLine = "From \n"_ns;
+ constexpr auto kEndOfMessage = "\n\n"_ns;
+
+ // 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_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
diff --git a/comm/mailnews/import/src/nsImportABDescriptor.cpp b/comm/mailnews/import/src/nsImportABDescriptor.cpp
new file mode 100644
index 0000000000..983f1c6ffb
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportABDescriptor.cpp
@@ -0,0 +1,19 @@
+/* -*- 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"
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult nsImportABDescriptor::Create(REFNSIID aIID, void** aResult) {
+ RefPtr<nsImportABDescriptor> it = new nsImportABDescriptor();
+ return it->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(nsImportABDescriptor, nsIImportABDescriptor)
+
+nsImportABDescriptor::nsImportABDescriptor()
+ : mId(0), mRef(0), mSize(0), mImport(true) {}
diff --git a/comm/mailnews/import/src/nsImportABDescriptor.h b/comm/mailnews/import/src/nsImportABDescriptor.h
new file mode 100644
index 0000000000..681ed8e83f
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportABDescriptor.h
@@ -0,0 +1,100 @@
+/* -*- 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 "nsString.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 nsresult Create(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/comm/mailnews/import/src/nsImportAddressBooks.cpp b/comm/mailnews/import/src/nsImportAddressBooks.cpp
new file mode 100644
index 0000000000..b39e7c68ef
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportAddressBooks.cpp
@@ -0,0 +1,571 @@
+/* -*- 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 "nsImportAddressBooks.h"
+
+#include "plstr.h"
+#include "nsIImportService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIAbManager.h"
+#include "nsImportStringBundle.h"
+#include "nsTextFormatter.h"
+#include "msgCore.h"
+#include "ImportDebug.h"
+
+nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric) {
+ NS_ASSERTION(aImportGeneric != nullptr, "null ptr");
+ if (!aImportGeneric) return NS_ERROR_NULL_POINTER;
+
+ RefPtr<nsImportGenericAddressBooks> pGen = new nsImportGenericAddressBooks();
+ return pGen->QueryInterface(NS_GET_IID(nsIImportGeneric),
+ (void**)aImportGeneric);
+}
+
+nsImportGenericAddressBooks::nsImportGenericAddressBooks() {
+ m_totalSize = 0;
+ m_doImport = false;
+ m_pThreadData = 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_description) free(m_description);
+}
+
+NS_IMPL_ISUPPORTS(nsImportGenericAddressBooks, nsIImportGeneric)
+
+NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char* dataId,
+ nsISupports** _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ nsresult rv;
+ *_retval = nullptr;
+ if (!PL_strcasecmp(dataId, "addressInterface")) {
+ NS_IF_ADDREF(*_retval = m_pInterface);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressLocation")) {
+ if (!m_pLocation) GetDefaultLocation();
+ NS_IF_ADDREF(*_retval = m_pLocation);
+ }
+
+ if (!PL_strcasecmp(dataId, "addressDestination")) {
+ if (!m_pDestinationUri.IsEmpty()) {
+ nsCOMPtr<nsISupportsCString> abString =
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ abString->SetData(m_pDestinationUri);
+ abString.forget(_retval);
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "fieldMap")) {
+ if (m_pFieldMap) {
+ NS_ADDREF(*_retval = m_pFieldMap);
+ } else {
+ if (m_pInterface && m_pLocation) {
+ bool needsIt = false;
+ m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt);
+ if (needsIt) {
+ GetDefaultFieldMap();
+ if (m_pFieldMap) {
+ NS_ADDREF(*_retval = m_pFieldMap);
+ }
+ }
+ }
+ }
+ }
+
+ 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));
+ data.forget(_retval);
+ }
+ free(pData);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char* dataId,
+ nsISupports* item) {
+ NS_ASSERTION(dataId != nullptr, "null ptr");
+ if (!dataId) return NS_ERROR_NULL_POINTER;
+
+ if (!PL_strcasecmp(dataId, "addressInterface")) {
+ m_pInterface = nullptr;
+ if (item) m_pInterface = do_QueryInterface(item);
+ }
+
+ 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 = do_QueryInterface(item);
+ if (abString) {
+ abString->GetData(m_pDestinationUri);
+ }
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "fieldMap")) {
+ m_pFieldMap = nullptr;
+ if (item) m_pFieldMap = do_QueryInterface(item);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::GetStatus(const char* statusKind,
+ int32_t* _retval) {
+ NS_ASSERTION(statusKind != nullptr, "null ptr");
+ NS_ASSERTION(_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) 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) return;
+
+ if (!m_pLocation && !m_autoFind) return;
+
+ nsresult rv = m_pInterface->FindAddressBooks(m_pLocation, m_Books);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error: FindAddressBooks failed\n");
+ }
+}
+
+void nsImportGenericAddressBooks::GetDefaultFieldMap(void) {
+ if (!m_pInterface || !m_pLocation) return;
+
+ 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(getter_AddRefs(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");
+ m_pFieldMap = nullptr;
+ }
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool* _retval) {
+ NS_ASSERTION(_retval != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ GetDefaultLocation();
+ GetDefaultBooks();
+
+ bool result = false;
+ uint32_t totalSize = 0;
+
+ for (nsIImportABDescriptor* book : m_Books) {
+ bool doImport = false;
+ nsresult rv = book->GetImport(&doImport);
+ if (NS_SUCCEEDED(rv) && doImport) {
+ uint32_t size = 0;
+ (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<nsIAbDirectory> GetAddressBookFromUri(const char* pUri) {
+ if (!pUri) return nullptr;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1");
+ if (!abManager) return nullptr;
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ abManager->GetDirectory(nsDependentCString(pUri), getter_AddRefs(directory));
+ if (!directory) return nullptr;
+
+ return directory.forget();
+}
+
+already_AddRefed<nsIAbDirectory> GetAddressBook(nsString 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<nsIAbDirectory> directory;
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService("@mozilla.org/abmanager;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString dirPrefId;
+ rv = abManager->NewAddressBook(name, EmptyCString(),
+ nsIAbManager::JS_DIRECTORY_TYPE,
+ EmptyCString(), dirPrefId);
+ if (NS_SUCCEEDED(rv)) {
+ rv = abManager->GetDirectoryFromId(dirPrefId, getter_AddRefs(directory));
+ }
+ }
+
+ return directory.forget();
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport(
+ nsISupportsString* successLog, nsISupportsString* errorLog, bool* _retval) {
+ NS_ASSERTION(_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) {
+ 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;
+ }
+
+ m_pSuccessLog = successLog;
+ m_pErrorLog = errorLog;
+
+ // 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.Clone();
+ m_pThreadData->addressImport = m_pInterface;
+ m_pThreadData->fieldMap = m_pFieldMap;
+ m_pThreadData->errorLog = m_pErrorLog;
+ m_pThreadData->successLog = m_pSuccessLog;
+ m_pThreadData->pDestinationUri = m_pDestinationUri;
+
+ // 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<nsIAbDirectory> db;
+ if (!m_pDestinationUri.IsEmpty()) {
+ db = GetAddressBookFromUri(m_pDestinationUri.get());
+ }
+ for (nsIImportABDescriptor* book : m_Books) {
+ if (!db) {
+ nsString name;
+ book->GetPreferredName(name);
+ db = GetAddressBook(name, true);
+ }
+ m_DBs.AppendObject(db);
+ }
+ m_pThreadData->dBs = &m_DBs;
+
+ m_pThreadData->stringBundle = m_stringBundle;
+
+ nsresult rv;
+ m_pThreadData->ldifService =
+ do_GetService("@mozilla.org/addressbook/abldifservice;1", &rv);
+
+ ImportAddressThread(m_pThreadData);
+ delete m_pThreadData;
+ m_pThreadData = nullptr;
+ *_retval = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericAddressBooks::ContinueImport(bool* _retval) {
+ NS_ASSERTION(_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_ASSERTION(_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;
+}
+
+AddressThreadData::~AddressThreadData() {}
+
+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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, pName);
+ pStream->Append(pText);
+ free(pFmt);
+ pStream->AppendLiteral(MSG_LINEBREAK);
+}
+
+static void ImportAddressThread(void* stuff) {
+ IMPORT_LOG0("In Begin ImportAddressThread\n");
+
+ AddressThreadData* pData = (AddressThreadData*)stuff;
+
+ nsString success;
+ nsString error;
+
+ uint32_t count = pData->books.Length();
+ for (uint32_t i = 0; (i < count) && !(pData->abort); i++) {
+ nsIImportABDescriptor* book = pData->books[i];
+
+ uint32_t size = 0;
+ bool doImport = false;
+ nsresult rv = book->GetImport(&doImport);
+ if (NS_SUCCEEDED(rv) && doImport) rv = book->GetSize(&size);
+
+ if (NS_SUCCEEDED(rv) && size && doImport) {
+ nsString name;
+ book->GetPreferredName(name);
+
+ nsCOMPtr<nsIAbDirectory> 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);
+ free(pSuccess);
+ }
+ if (pError) {
+ error.Append(pError);
+ free(pError);
+ }
+ } else {
+ nsImportGenericAddressBooks::ReportError(name.get(), &error,
+ pData->stringBundle);
+ }
+
+ pData->currentSize = 0;
+ pData->currentTotal += size;
+
+ 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/comm/mailnews/import/src/nsImportAddressBooks.h b/comm/mailnews/import/src/nsImportAddressBooks.h
new file mode 100644
index 0000000000..8d0edd2c67
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportAddressBooks.h
@@ -0,0 +1,81 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportFieldMap.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsIAbDirectory.h"
+#include "nsIAbLDIFService.h"
+#include "nsIStringBundle.h"
+#include "nsIArray.h"
+#include "nsCOMArray.h"
+
+static void ImportAddressThread(void* stuff);
+
+class AddressThreadData;
+
+class nsImportGenericAddressBooks : public nsIImportGeneric {
+ public:
+ nsImportGenericAddressBooks();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIMPORTGENERIC
+
+ 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:
+ nsCOMPtr<nsIImportAddressBooks> m_pInterface;
+ nsTArray<RefPtr<nsIImportABDescriptor>> m_Books;
+ nsCOMArray<nsIAbDirectory> m_DBs;
+ nsCOMPtr<nsIFile> m_pLocation;
+ nsCOMPtr<nsIImportFieldMap> m_pFieldMap;
+ bool m_autoFind;
+ char16_t* m_description;
+ bool m_gotLocation;
+ bool m_found;
+ bool m_userVerify;
+ nsCOMPtr<nsISupportsString> m_pSuccessLog;
+ nsCOMPtr<nsISupportsString> m_pErrorLog;
+ uint32_t m_totalSize;
+ bool m_doImport;
+ AddressThreadData* m_pThreadData;
+ nsCString m_pDestinationUri;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+class AddressThreadData {
+ public:
+ bool driverAlive;
+ bool threadAlive;
+ bool abort;
+ bool fatalError;
+ uint32_t currentTotal;
+ uint32_t currentSize;
+ nsTArray<RefPtr<nsIImportABDescriptor>> books;
+ nsCOMArray<nsIAbDirectory>* dBs;
+ nsCOMPtr<nsIAbLDIFService> ldifService;
+ nsCOMPtr<nsIImportAddressBooks> addressImport;
+ nsCOMPtr<nsIImportFieldMap> fieldMap;
+ nsCOMPtr<nsISupportsString> successLog;
+ nsCOMPtr<nsISupportsString> errorLog;
+ nsCString pDestinationUri;
+ nsCOMPtr<nsIStringBundle> stringBundle;
+
+ AddressThreadData();
+ ~AddressThreadData();
+};
diff --git a/comm/mailnews/import/src/nsImportEmbeddedImageData.cpp b/comm/mailnews/import/src/nsImportEmbeddedImageData.cpp
new file mode 100644
index 0000000000..d49bccdbfc
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportEmbeddedImageData.cpp
@@ -0,0 +1,52 @@
+/* -*- 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/comm/mailnews/import/src/nsImportEmbeddedImageData.h b/comm/mailnews/import/src/nsImportEmbeddedImageData.h
new file mode 100644
index 0000000000..0eaa08b113
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportEmbeddedImageData.h
@@ -0,0 +1,31 @@
+/* -*- 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 "nsString.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/comm/mailnews/import/src/nsImportEncodeScan.cpp b/comm/mailnews/import/src/nsImportEncodeScan.cpp
new file mode 100644
index 0000000000..64607688ef
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportEncodeScan.cpp
@@ -0,0 +1,334 @@
+/* -*- 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 "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 "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 = 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/comm/mailnews/import/src/nsImportEncodeScan.h b/comm/mailnews/import/src/nsImportEncodeScan.h
new file mode 100644
index 0000000000..4c9b784fc6
--- /dev/null
+++ b/comm/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 "nsString.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/comm/mailnews/import/src/nsImportFieldMap.cpp b/comm/mailnews/import/src/nsImportFieldMap.cpp
new file mode 100644
index 0000000000..74e15f11fe
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportFieldMap.cpp
@@ -0,0 +1,325 @@
+/* -*- 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 "nsIAbCard.h"
+#include "nsIStringBundle.h"
+#include "nsImportFieldMap.h"
+#include "nsImportStringBundle.h"
+#include "nsCRTGlue.h"
+#include "ImportDebug.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult nsImportFieldMap::Create(nsIStringBundle* aBundle, REFNSIID aIID,
+ void** aResult) {
+ RefPtr<nsImportFieldMap> it = new nsImportFieldMap(aBundle);
+ return it->QueryInterface(aIID, aResult);
+}
+
+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_ASSERTION(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_ASSERTION(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_ASSERTION(_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_ASSERTION(_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_ASSERTION(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(nsIAbDirectory* database,
+ nsIAbCard* row, int32_t fieldNum,
+ const nsAString& value) {
+ // 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;
+
+ switch (fieldNum) {
+ case 0:
+ rv = row->SetFirstName(value);
+ break;
+ case 1:
+ rv = row->SetLastName(value);
+ break;
+ case 2:
+ rv = row->SetDisplayName(value);
+ break;
+ case 3:
+ rv = row->SetPropertyAsAString(kNicknameProperty, value);
+ break;
+ case 4:
+ rv = row->SetPrimaryEmail(value);
+ break;
+ case 5:
+ rv = row->SetPropertyAsAString(k2ndEmailProperty, value);
+ break;
+ case 6:
+ rv = row->SetPropertyAsAString(kWorkPhoneProperty, value);
+ break;
+ case 7:
+ rv = row->SetPropertyAsAString(kHomePhoneProperty, value);
+ break;
+ case 8:
+ rv = row->SetPropertyAsAString(kFaxProperty, value);
+ break;
+ case 9:
+ rv = row->SetPropertyAsAString(kPagerProperty, value);
+ break;
+ case 10:
+ rv = row->SetPropertyAsAString(kCellularProperty, value);
+ break;
+ case 11:
+ rv = row->SetPropertyAsAString(kHomeAddressProperty, value);
+ break;
+ case 12:
+ rv = row->SetPropertyAsAString(kHomeAddress2Property, value);
+ break;
+ case 13:
+ rv = row->SetPropertyAsAString(kHomeCityProperty, value);
+ break;
+ case 14:
+ rv = row->SetPropertyAsAString(kHomeStateProperty, value);
+ break;
+ case 15:
+ rv = row->SetPropertyAsAString(kHomeZipCodeProperty, value);
+ break;
+ case 16:
+ rv = row->SetPropertyAsAString(kHomeCountryProperty, value);
+ break;
+ case 17:
+ rv = row->SetPropertyAsAString(kWorkAddressProperty, value);
+ break;
+ case 18:
+ rv = row->SetPropertyAsAString(kWorkAddress2Property, value);
+ break;
+ case 19:
+ rv = row->SetPropertyAsAString(kWorkCityProperty, value);
+ break;
+ case 20:
+ rv = row->SetPropertyAsAString(kWorkStateProperty, value);
+ break;
+ case 21:
+ rv = row->SetPropertyAsAString(kWorkZipCodeProperty, value);
+ break;
+ case 22:
+ rv = row->SetPropertyAsAString(kWorkCountryProperty, value);
+ break;
+ case 23:
+ rv = row->SetPropertyAsAString(kJobTitleProperty, value);
+ break;
+ case 24:
+ rv = row->SetPropertyAsAString(kDepartmentProperty, value);
+ break;
+ case 25:
+ rv = row->SetPropertyAsAString(kCompanyProperty, value);
+ break;
+ case 26:
+ rv = row->SetPropertyAsAString(kWorkWebPageProperty, value);
+ break;
+ case 27:
+ rv = row->SetPropertyAsAString(kHomeWebPageProperty, value);
+ break;
+ case 28:
+ rv = row->SetPropertyAsAString(kBirthYearProperty, value);
+ break;
+ case 29:
+ rv = row->SetPropertyAsAString(kBirthMonthProperty, value);
+ break;
+ case 30:
+ rv = row->SetPropertyAsAString(kBirthDayProperty, value);
+ break;
+ case 31:
+ rv = row->SetPropertyAsAString(kCustom1Property, value);
+ break;
+ case 32:
+ rv = row->SetPropertyAsAString(kCustom2Property, value);
+ break;
+ case 33:
+ rv = row->SetPropertyAsAString(kCustom3Property, value);
+ break;
+ case 34:
+ rv = row->SetPropertyAsAString(kCustom4Property, value);
+ break;
+ case 35:
+ rv = row->SetPropertyAsAString(kNotesProperty, value);
+ break;
+ case 36:
+ rv = row->SetPropertyAsAString(kAIMProperty, value);
+ break;
+ default:
+ /* Get the field description, and add it as an anonymous attr? */
+ /* OR WHAT???? */
+ { rv = NS_ERROR_FAILURE; }
+ }
+
+ 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/comm/mailnews/import/src/nsImportFieldMap.h b/comm/mailnews/import/src/nsImportFieldMap.h
new file mode 100644
index 0000000000..1d13df70a1
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportFieldMap.h
@@ -0,0 +1,44 @@
+/* -*- 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 "nsTArray.h"
+#include "nsString.h"
+#include "nsIStringBundle.h"
+
+////////////////////////////////////////////////////////////////////////
+
+class nsIStringBundle;
+
+class nsImportFieldMap : public nsIImportFieldMap {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIIMPORTFIELDMAP
+
+ explicit nsImportFieldMap(nsIStringBundle* aBundle);
+
+ static nsresult Create(nsIStringBundle* aBundle, 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/comm/mailnews/import/src/nsImportMail.cpp b/comm/mailnews/import/src/nsImportMail.cpp
new file mode 100644
index 0000000000..e845f773f8
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportMail.cpp
@@ -0,0 +1,1007 @@
+/* -*- 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 "nsImportMail.h"
+
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIMsgAccountManager.h"
+#include "nsImportStringBundle.h"
+#include "nsTextFormatter.h"
+#include "ImportDebug.h"
+#include "plstr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Components.h"
+#include "msgCore.h"
+
+// 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_ASSERTION(aImportGeneric != nullptr, "null ptr");
+ if (!aImportGeneric) return NS_ERROR_NULL_POINTER;
+
+ RefPtr<nsImportGenericMail> pGen = new nsImportGenericMail();
+ return pGen->QueryInterface(NS_GET_IID(nsIImportGeneric),
+ (void**)aImportGeneric);
+}
+
+nsImportGenericMail::nsImportGenericMail() {
+ m_found = false;
+ m_userVerify = false;
+ m_gotLocation = false;
+ m_gotDefaultMailboxes = false;
+ m_totalSize = 0;
+ m_doImport = false;
+ m_pThreadData = nullptr;
+
+ m_pDestFolder = nullptr;
+ m_deleteDestFolder = false;
+ m_createdFolder = false;
+ m_performingMigration = false;
+
+ 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_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric)
+
+NS_IMETHODIMP nsImportGenericMail::GetData(const char* dataId,
+ nsISupports** _retval) {
+ nsresult rv = NS_OK;
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = nullptr;
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ NS_IF_ADDREF(*_retval = m_pInterface);
+ }
+
+ 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);
+ migrationString.forget(_retval);
+ }
+
+ 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);
+ }
+ data.forget(_retval);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::SetData(const char* dataId,
+ nsISupports* item) {
+ nsresult rv = NS_OK;
+ NS_ASSERTION(dataId != nullptr, "null ptr");
+ if (!dataId) return NS_ERROR_NULL_POINTER;
+
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ m_pInterface = nullptr;
+ if (item) m_pInterface = do_QueryInterface(item);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailLocation")) {
+ m_mailboxes.Clear();
+ m_gotDefaultMailboxes = false;
+ 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")) {
+ m_pDestFolder = nullptr;
+ if (item) m_pDestFolder = do_QueryInterface(item);
+ m_deleteDestFolder = false;
+ }
+
+ if (!PL_strcasecmp(dataId, "name")) {
+ if (item) {
+ nsCOMPtr<nsISupportsString> nameString = do_QueryInterface(item, &rv);
+ if (NS_SUCCEEDED(rv)) rv = nameString->GetData(m_pName);
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "migration")) {
+ if (item) {
+ nsCOMPtr<nsISupportsPRBool> migrationString =
+ do_QueryInterface(item, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = migrationString->GetData(&m_performingMigration);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::GetStatus(const char* statusKind,
+ int32_t* _retval) {
+ NS_ASSERTION(statusKind != nullptr, "null ptr");
+ NS_ASSERTION(_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_pSrcLocation) return;
+ if (m_gotDefaultMailboxes) return;
+ m_pInterface->FindMailboxes(m_pSrcLocation, m_mailboxes);
+ m_gotDefaultMailboxes = true;
+}
+
+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_ASSERTION(_retval != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (m_pThreadData) {
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ GetDefaultLocation();
+ GetDefaultMailboxes();
+
+ if (!m_pDestFolder) {
+ GetDefaultDestination();
+ }
+
+ bool result = false;
+ uint32_t totalSize = 0;
+ for (nsIImportMailboxDescriptor* box : m_mailboxes) {
+ bool doImport = false;
+ uint32_t size = 0;
+ nsresult rv = box->GetImport(&doImport);
+ if (NS_SUCCEEDED(rv) && doImport) {
+ (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 (index >= m_mailboxes.Length()) {
+ return;
+ }
+ nsAutoString name;
+ m_mailboxes[index]->GetDisplayName(getter_Copies(name));
+ if (!name.IsEmpty()) {
+ pStr->SetData(name);
+ }
+}
+
+NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString* successLog,
+ nsISupportsString* errorLog,
+ bool* _retval) {
+ NS_ASSERTION(_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_gotDefaultMailboxes) {
+ 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;
+ }
+
+ m_pSuccessLog = successLog;
+ m_pErrorLog = errorLog;
+
+ // kick off the thread to do the import!!!!
+ m_pThreadData = new ImportThreadData();
+ m_pThreadData->boxes = m_mailboxes.Clone();
+ m_pThreadData->mailImport = m_pInterface;
+ m_pThreadData->errorLog = m_pErrorLog;
+ m_pThreadData->successLog = m_pSuccessLog;
+
+ m_pThreadData->ownsDestRoot = m_deleteDestFolder;
+ m_pThreadData->destRoot = m_pDestFolder;
+ m_pThreadData->performingMigration = m_performingMigration;
+
+ m_pThreadData->stringBundle = m_stringBundle;
+
+ // Previously this was run in a sub-thread, after introducing
+ // SeamonkeyImport.jsm and because JS XPCOM can only run in the main thread,
+ // this has been changed to run in the main thread.
+ ImportMailThread(m_pThreadData);
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool* _retval) {
+ NS_ASSERTION(_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_ASSERTION(_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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, pName);
+ pStream->Append(pText);
+ 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;
+}
+
+ImportThreadData::~ImportThreadData() {}
+
+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);
+ } 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 = pData->boxes.Length();
+
+ uint32_t size;
+ uint32_t depth = 1;
+ uint32_t newDepth;
+ nsString lastName;
+
+ 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 (uint32_t i = 0; (i < count) && !(pData->abort); i++) {
+ nsIImportMailboxDescriptor* box = pData->boxes[i];
+ pData->currentMailbox = i;
+
+ bool doImport = false;
+ size = 0;
+ rv = box->GetImport(&doImport);
+ if (doImport) 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'.",
+ NS_ConvertUTF16toUTF8(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'.",
+ NS_ConvertUTF16toUTF8(lastName).get());
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY,
+ pData->stringBundle, error);
+ pData->fatalError = true;
+ break;
+ }
+ }
+ depth = newDepth;
+ char16_t* pName = nullptr;
+ box->GetDisplayName(&pName);
+ if (pName) {
+ lastName = pName;
+ 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.",
+ NS_ConvertUTF16toUTF8(lastName).get());
+ nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(),
+ &error, pData->stringBundle);
+ }
+
+ if (size && doImport && 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);
+ free(pError);
+ }
+ if (pSuccess) {
+ success.Append(pSuccess);
+ 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("@mozilla.org/messenger/account-manager;1", &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);
+ } 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::components::StringBundle::Service();
+ 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()) {
+ AutoTArray<nsString, 1> moduleName = {m_pName};
+ rv = bundle->FormatStringFromName("ImportModuleFolderName", moduleName,
+ folderName);
+ } else {
+ rv = bundle->GetStringFromName("DefaultFolderName", folderName);
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to get Folder Name!\n");
+ return false;
+ }
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &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.
+ nsTArray<RefPtr<nsIMsgFolder>> dummy;
+ rv = localRootFolder->GetSubFolders(dummy);
+ 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
+ nsresult mResult;
+
+ private:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder* aFolder)
+ : mozilla::Runnable("GetSubFoldersRunnable"), m_folder(aFolder) {}
+
+NS_IMETHODIMP GetSubFoldersRunnable::Run() {
+ nsTArray<RefPtr<nsIMsgFolder>> dummy;
+ mResult = m_folder->GetSubFolders(dummy);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyGetSubFolders(nsIMsgFolder* aFolder) {
+ RefPtr<GetSubFoldersRunnable> getSubFolders =
+ new GetSubFoldersRunnable(aFolder);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyGetSubFolders"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(getSubFolders));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return getSubFolders->mResult;
+}
+
+class GetChildNamedRunnable : public mozilla::Runnable {
+ public:
+ GetChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName,
+ nsIMsgFolder** aChild);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ nsIMsgFolder** m_child;
+};
+
+GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder* aFolder,
+ const nsAString& aName,
+ nsIMsgFolder** aChild)
+ : mozilla::Runnable("GetChildNamedRunnable"),
+ mResult(NS_OK),
+ m_folder(aFolder),
+ m_name(aName),
+ m_child(aChild) {}
+
+NS_IMETHODIMP GetChildNamedRunnable::Run() {
+ mResult = m_folder->GetChildNamed(m_name, m_child);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyGetChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
+ nsIMsgFolder** aChild) {
+ RefPtr<GetChildNamedRunnable> getChildNamed =
+ new GetChildNamedRunnable(aFolder, aName, aChild);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyGetChildNamed"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(getChildNamed));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return getChildNamed->mResult;
+}
+
+class GetParentRunnable : public mozilla::Runnable {
+ public:
+ GetParentRunnable(nsIMsgFolder* aFolder, nsIMsgFolder** aParent);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsIMsgFolder** m_parent;
+};
+
+GetParentRunnable::GetParentRunnable(nsIMsgFolder* aFolder,
+ nsIMsgFolder** aParent)
+ : mozilla::Runnable("GetParentRunnable"),
+ mResult(NS_OK),
+ m_folder(aFolder),
+ m_parent(aParent) {}
+
+NS_IMETHODIMP GetParentRunnable::Run() {
+ mResult = m_folder->GetParent(m_parent);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyGetParent(nsIMsgFolder* aFolder, nsIMsgFolder** aParent) {
+ RefPtr<GetParentRunnable> getParent = new GetParentRunnable(aFolder, aParent);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyGetParent"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(getParent));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return getParent->mResult;
+}
+
+class ContainsChildNamedRunnable : public mozilla::Runnable {
+ public:
+ ContainsChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName,
+ bool* aResult);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ bool* m_result;
+};
+
+ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder* aFolder,
+ const nsAString& aName,
+ bool* aResult)
+ : mozilla::Runnable("ContainsChildNamedRunnable"),
+ mResult(NS_OK),
+ m_folder(aFolder),
+ m_name(aName),
+ m_result(aResult) {}
+
+NS_IMETHODIMP ContainsChildNamedRunnable::Run() {
+ mResult = m_folder->ContainsChildNamed(m_name, m_result);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyContainsChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
+ bool* aResult) {
+ NS_ENSURE_ARG(aFolder);
+ RefPtr<ContainsChildNamedRunnable> containsChildNamed =
+ new ContainsChildNamedRunnable(aFolder, aName, aResult);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyContainsChildNamed"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(containsChildNamed));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return containsChildNamed->mResult;
+}
+
+class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable {
+ public:
+ GenerateUniqueSubfolderNameRunnable(nsIMsgFolder* aFolder,
+ const nsAString& prefix,
+ nsIMsgFolder* otherFolder,
+ nsAString& name);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ 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)
+ : mozilla::Runnable("GenerateUniqueSubfolderNameRunnable"),
+ mResult(NS_OK),
+ m_folder(aFolder),
+ m_prefix(aPrefix),
+ m_otherFolder(aOtherFolder),
+ m_name(aName) {}
+
+NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run() {
+ mResult =
+ m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder* aFolder,
+ const nsAString& aPrefix,
+ nsIMsgFolder* aOtherFolder,
+ nsAString& aName)
+
+{
+ RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName =
+ new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder,
+ aName);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyGenerateUniqueSubfolderName"_ns,
+ mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(generateUniqueSubfolderName));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return generateUniqueSubfolderName->mResult;
+}
+
+class CreateSubfolderRunnable : public mozilla::Runnable {
+ public:
+ CreateSubfolderRunnable(nsIMsgFolder* aFolder, const nsAString& aName);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+};
+
+CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder* aFolder,
+ const nsAString& aName)
+ : mozilla::Runnable("CreateSubfolderRunnable"),
+ mResult(NS_OK),
+ m_folder(aFolder),
+ m_name(aName) {}
+
+NS_IMETHODIMP CreateSubfolderRunnable::Run() {
+ mResult = m_folder->CreateSubfolder(m_name, nullptr);
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyCreateSubfolder(nsIMsgFolder* aFolder, const nsAString& aName) {
+ NS_ENSURE_ARG_POINTER(aFolder);
+ RefPtr<CreateSubfolderRunnable> createSubfolder =
+ new CreateSubfolderRunnable(aFolder, aName);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyCreateSubfolder"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(createSubfolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return createSubfolder->mResult;
+}
+
+class ForceDBClosedRunnable : public mozilla::Runnable {
+ public:
+ explicit ForceDBClosedRunnable(nsIMsgFolder* aFolder);
+ NS_DECL_NSIRUNNABLE
+ nsresult mResult;
+
+ protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder* aFolder)
+ : mozilla::Runnable("ForceDBClosedRunnable"), m_folder(aFolder) {}
+
+NS_IMETHODIMP ForceDBClosedRunnable::Run() {
+ mResult = m_folder->ForceDBClosed();
+ return NS_OK; // Sync runnable must return OK.
+}
+
+nsresult ProxyForceDBClosed(nsIMsgFolder* aFolder) {
+ RefPtr<ForceDBClosedRunnable> forceDBClosed =
+ new ForceDBClosedRunnable(aFolder);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyForceDBClosed"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(forceDBClosed));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return forceDBClosed->mResult;
+}
diff --git a/comm/mailnews/import/src/nsImportMail.h b/comm/mailnews/import/src/nsImportMail.h
new file mode 100644
index 0000000000..fcd2a0c40c
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportMail.h
@@ -0,0 +1,86 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsIImportMail.h"
+#include "nsIImportGeneric.h"
+#include "nsString.h"
+#include "nsIMsgFolder.h"
+#include "nsIStringBundle.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
+ NS_DECL_NSIIMPORTGENERIC
+
+ 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
+ nsCOMPtr<nsIMsgFolder> m_pDestFolder;
+ bool m_deleteDestFolder;
+ bool m_createdFolder;
+ nsCOMPtr<nsIFile> m_pSrcLocation;
+ bool m_gotLocation;
+ bool m_gotDefaultMailboxes;
+ bool m_found;
+ bool m_userVerify;
+ nsCOMPtr<nsIImportMail> m_pInterface;
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>> m_mailboxes;
+ nsCOMPtr<nsISupportsString> m_pSuccessLog;
+ nsCOMPtr<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;
+ nsCOMPtr<nsIMsgFolder> destRoot;
+ bool ownsDestRoot;
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>> boxes;
+ nsCOMPtr<nsIImportMail> mailImport;
+ nsCOMPtr<nsISupportsString> successLog;
+ nsCOMPtr<nsISupportsString> errorLog;
+ uint32_t currentMailbox;
+ bool performingMigration;
+ nsCOMPtr<nsIStringBundle> stringBundle;
+
+ ImportThreadData();
+ ~ImportThreadData();
+ void DriverDelete();
+ void ThreadDelete();
+ void DriverAbort();
+};
diff --git a/comm/mailnews/import/src/nsImportMailboxDescriptor.cpp b/comm/mailnews/import/src/nsImportMailboxDescriptor.cpp
new file mode 100644
index 0000000000..8dc17b9317
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportMailboxDescriptor.cpp
@@ -0,0 +1,25 @@
+/* -*- 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 "nsImportMailboxDescriptor.h"
+#include "nsComponentManagerUtils.h"
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult nsImportMailboxDescriptor::Create(REFNSIID aIID, void** aResult) {
+ RefPtr<nsImportMailboxDescriptor> it = new nsImportMailboxDescriptor();
+ return it->QueryInterface(aIID, aResult);
+}
+
+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/comm/mailnews/import/src/nsImportMailboxDescriptor.h b/comm/mailnews/import/src/nsImportMailboxDescriptor.h
new file mode 100644
index 0000000000..b760cbd266
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportMailboxDescriptor.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 nsImportMailboxDescriptor_h___
+#define nsImportMailboxDescriptor_h___
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsString.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 nsresult Create(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/comm/mailnews/import/src/nsImportScanFile.cpp b/comm/mailnews/import/src/nsImportScanFile.cpp
new file mode 100644
index 0000000000..79971ae10a
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportScanFile.cpp
@@ -0,0 +1,154 @@
+/* -*- 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 "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/comm/mailnews/import/src/nsImportScanFile.h b/comm/mailnews/import/src/nsImportScanFile.h
new file mode 100644
index 0000000000..e5704aaf36
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportScanFile.h
@@ -0,0 +1,56 @@
+/* -*- 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/comm/mailnews/import/src/nsImportService.cpp b/comm/mailnews/import/src/nsImportService.cpp
new file mode 100644
index 0000000000..062e7d664c
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportService.cpp
@@ -0,0 +1,293 @@
+/* -*- 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 "nsString.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 "nsThreadUtils.h"
+#include "ImportDebug.h"
+#include "nsImportService.h"
+#include "nsImportStringBundle.h"
+#include "nsCRTGlue.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMsgSend.h"
+#include "nsMsgUtils.h"
+#include "mozilla/SimpleEnumerator.h"
+
+mozilla::LazyLogModule IMPORTLOGMODULE("Import");
+
+////////////////////////////////////////////////////////////////////////
+
+nsImportService::nsImportService() {
+ IMPORT_LOG0("* nsImport Service Created\n");
+
+ m_didDiscovery = false;
+
+ 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() {
+ 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, NS_GET_IID(nsIImportFieldMap),
+ (void**)_retval);
+}
+
+NS_IMETHODIMP nsImportService::CreateNewMailboxDescriptor(
+ nsIImportMailboxDescriptor** _retval) {
+ return nsImportMailboxDescriptor::Create(
+ NS_GET_IID(nsIImportMailboxDescriptor), (void**)_retval);
+}
+
+NS_IMETHODIMP nsImportService::CreateNewABDescriptor(
+ nsIImportABDescriptor** _retval) {
+ return nsImportABDescriptor::Create(NS_GET_IID(nsIImportABDescriptor),
+ (void**)_retval);
+}
+
+extern nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric);
+
+NS_IMETHODIMP nsImportService::CreateNewGenericMail(
+ nsIImportGeneric** _retval) {
+ NS_ASSERTION(_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_ASSERTION(_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_ASSERTION(_retval != nullptr, "null ptr");
+ if (!_retval) return NS_ERROR_NULL_POINTER;
+
+ DoDiscover();
+
+ nsCString filterStr(filter);
+ int32_t count = 0;
+ for (auto& importModule : m_importModules) {
+ if (importModule.SupportsThings(filterStr)) count++;
+ }
+ *_retval = count;
+
+ return NS_OK;
+}
+
+ImportModuleDesc* nsImportService::GetImportModule(const char* filter,
+ int32_t index) {
+ DoDiscover();
+
+ nsCString filterStr(filter);
+ int32_t count = 0;
+ for (auto& importModule : m_importModules) {
+ if (importModule.SupportsThings(filterStr)) {
+ if (count++ == index) {
+ return &importModule;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleInfo(const char* filter, int32_t index,
+ nsAString& name,
+ nsAString& moduleDescription) {
+ ImportModuleDesc* importModule = GetImportModule(filter, index);
+ if (!importModule) return NS_ERROR_FAILURE;
+
+ name = importModule->GetName();
+ moduleDescription = importModule->GetDescription();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleName(const char* filter, int32_t index,
+ nsAString& _retval) {
+ ImportModuleDesc* importModule = GetImportModule(filter, index);
+ if (!importModule) return NS_ERROR_FAILURE;
+
+ _retval = importModule->GetName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImportService::GetModuleDescription(const char* filter,
+ int32_t index,
+ nsAString& _retval) {
+ ImportModuleDesc* importModule = GetImportModule(filter, index);
+ if (!importModule) return NS_ERROR_FAILURE;
+
+ _retval = importModule->GetDescription();
+ return NS_OK;
+}
+
+class nsProxySendRunnable : public mozilla::Runnable {
+ public:
+ nsProxySendRunnable(
+ nsIMsgIdentity* aIdentity, nsIMsgCompFields* aMsgFields,
+ const char* attachment1_type, const nsACString& attachment1_body,
+ bool aIsDraft,
+ nsTArray<RefPtr<nsIMsgAttachedFile>> const& aLoadedAttachments,
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> const& aEmbeddedAttachments,
+ nsIMsgSendListener* aListener);
+ NS_DECL_NSIRUNNABLE
+ private:
+ nsCOMPtr<nsIMsgIdentity> m_identity;
+ nsCOMPtr<nsIMsgCompFields> m_compFields;
+ bool m_isDraft;
+ nsCString m_bodyType;
+ nsCString m_body;
+ nsTArray<RefPtr<nsIMsgAttachedFile>> m_loadedAttachments;
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> m_embeddedAttachments;
+ nsCOMPtr<nsIMsgSendListener> m_listener;
+};
+
+nsProxySendRunnable::nsProxySendRunnable(
+ nsIMsgIdentity* aIdentity, nsIMsgCompFields* aMsgFields,
+ const char* aBodyType, const nsACString& aBody, bool aIsDraft,
+ nsTArray<RefPtr<nsIMsgAttachedFile>> const& aLoadedAttachments,
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> const& aEmbeddedAttachments,
+ nsIMsgSendListener* aListener)
+ : mozilla::Runnable("nsProxySendRunnable"),
+ m_identity(aIdentity),
+ m_compFields(aMsgFields),
+ m_isDraft(aIsDraft),
+ m_bodyType(aBodyType),
+ m_body(aBody),
+ m_loadedAttachments(aLoadedAttachments.Clone()),
+ m_embeddedAttachments(aEmbeddedAttachments.Clone()),
+ m_listener(aListener) {}
+
+NS_IMETHODIMP nsProxySendRunnable::Run() {
+ nsresult rv;
+ nsCOMPtr<nsIMsgSend> msgSend =
+ do_CreateInstance("@mozilla.org/messengercompose/send;1", &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,
+ nsTArray<RefPtr<nsIMsgAttachedFile>> const& aLoadedAttachments,
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> const& 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_ASSERTION(_retval != nullptr, "null ptr");
+ if (!_retval) return NS_ERROR_NULL_POINTER;
+ *_retval = nullptr;
+
+ ImportModuleDesc* importModule = GetImportModule(filter, index);
+ if (!importModule) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIImportModule> modulePtr = importModule->GetModule();
+ modulePtr.forget(_retval);
+ return NS_OK;
+}
+
+nsresult nsImportService::DoDiscover(void) {
+ if (m_didDiscovery) return NS_OK;
+
+ m_importModules.Clear();
+
+ 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);
+ for (auto& key : mozilla::SimpleEnumerator<nsISupportsCString>(e)) {
+ nsCString keyStr;
+ key->ToString(getter_Copies(keyStr));
+ nsCString contractIdStr;
+ rv = catMan->GetCategoryEntry("mailnewsimport", keyStr, contractIdStr);
+ if (NS_SUCCEEDED(rv)) LoadModuleInfo(contractIdStr);
+ }
+
+ m_didDiscovery = true;
+
+ return NS_OK;
+}
+
+nsresult nsImportService::LoadModuleInfo(const nsCString& contractId) {
+ // load the component and get all of the info we need from it....
+ nsresult rv;
+ nsCOMPtr<nsIImportModule> module = do_CreateInstance(contractId.get(), &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ m_importModules.EmplaceBack(module);
+
+ return NS_OK;
+}
+
+ImportModuleDesc::ImportModuleDesc(nsIImportModule* importModule)
+ : m_pModule(importModule) {
+ nsresult rv;
+ rv = importModule->GetName(getter_Copies(m_name));
+ if (NS_FAILED(rv)) m_name.AssignLiteral("Unknown");
+
+ rv = importModule->GetDescription(getter_Copies(m_description));
+ if (NS_FAILED(rv)) m_description.AssignLiteral("Unknown description");
+
+ importModule->GetSupports(getter_Copies(m_supports));
+
+#ifdef IMPORT_DEBUG
+ IMPORT_LOG3("* nsImportService registered import module: %s, %s, %s\n",
+ NS_LossyConvertUTF16toASCII(m_name).get(),
+ NS_LossyConvertUTF16toASCII(m_description).get(),
+ m_supports.get());
+#endif
+}
+
+bool ImportModuleDesc::SupportsThings(const nsACString& thing) {
+ for (auto& item : m_supports.Split(',')) {
+ if (item == thing) return true;
+ }
+ return false;
+}
diff --git a/comm/mailnews/import/src/nsImportService.h b/comm/mailnews/import/src/nsImportService.h
new file mode 100644
index 0000000000..37dd95b935
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportService.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 nsImportService_h__
+#define nsImportService_h__
+
+#include "nsString.h"
+#include "nsMemory.h"
+#include "nsIImportModule.h"
+#include "nsIImportService.h"
+#include "nsIStringBundle.h"
+#include "nsTArray.h"
+
+class ImportModuleDesc {
+ public:
+ explicit ImportModuleDesc(nsIImportModule* importModule);
+
+ const nsAString& GetName(void) { return m_name; }
+ const nsAString& GetDescription(void) { return m_description; }
+
+ nsCOMPtr<nsIImportModule>& GetModule() { return m_pModule; }
+
+ bool SupportsThings(const nsACString& pThings);
+
+ private:
+ nsString m_name;
+ nsString m_description;
+ nsCString m_supports;
+ nsCOMPtr<nsIImportModule> m_pModule;
+};
+
+class nsImportService : public nsIImportService {
+ public:
+ nsImportService();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIMPORTSERVICE
+
+ private:
+ virtual ~nsImportService();
+ nsresult LoadModuleInfo(const nsCString& contractId);
+ nsresult DoDiscover(void);
+ ImportModuleDesc* GetImportModule(const char* filter, int32_t index);
+
+ private:
+ AutoTArray<ImportModuleDesc, 10> m_importModules;
+ bool m_didDiscovery;
+ nsCString m_sysCharset;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+#endif // nsImportService_h__
diff --git a/comm/mailnews/import/src/nsImportStringBundle.cpp b/comm/mailnews/import/src/nsImportStringBundle.cpp
new file mode 100644
index 0000000000..0ef79bfcfc
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportStringBundle.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/Components.h"
+
+nsresult nsImportStringBundle::GetStringBundle(const char* aPropertyURL,
+ nsIStringBundle** aBundle) {
+ nsresult rv;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::components::StringBundle::Service();
+ 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) {
+ nsAutoString str;
+ nsresult rv = aBundle->GetStringFromID(aStringID, str);
+ if (NS_SUCCEEDED(rv)) return ToNewUnicode(str);
+ }
+
+ nsString resultString(u"[StringID "_ns);
+ 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) {
+ nsAutoString str;
+ nsresult rv = aBundle->GetStringFromName(aName, str);
+ if (NS_SUCCEEDED(rv)) return ToNewUnicode(str);
+ }
+
+ nsString resultString(u"[StringName "_ns);
+ resultString.Append(NS_ConvertUTF8toUTF16(aName).get());
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
diff --git a/comm/mailnews/import/src/nsImportStringBundle.h b/comm/mailnews/import/src/nsImportStringBundle.h
new file mode 100644
index 0000000000..ba234888fc
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportStringBundle.h
@@ -0,0 +1,43 @@
+/* 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 "nsString.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/comm/mailnews/import/src/nsImportTranslator.cpp b/comm/mailnews/import/src/nsImportTranslator.cpp
new file mode 100644
index 0000000000..f988e7035d
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportTranslator.cpp
@@ -0,0 +1,308 @@
+/* -*- 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 "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/comm/mailnews/import/src/nsImportTranslator.h b/comm/mailnews/import/src/nsImportTranslator.h
new file mode 100644
index 0000000000..c475700ad6
--- /dev/null
+++ b/comm/mailnews/import/src/nsImportTranslator.h
@@ -0,0 +1,87 @@
+/* -*- 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 "nsString.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 valid 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 valid 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/comm/mailnews/import/src/nsOutlookCompose.cpp b/comm/mailnews/import/src/nsOutlookCompose.cpp
new file mode 100644
index 0000000000..1098dcbab7
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookCompose.cpp
@@ -0,0 +1,669 @@
+/* -*- 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 "prthread.h"
+#include "nsString.h"
+#include "nsMsgUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIURI.h"
+#include "nsMsgI18N.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsMsgAttachmentData.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgSend.h"
+#include "nsImportEmbeddedImageData.h"
+#include "nsNetCID.h"
+#include "nsCRT.h"
+#include "nsOutlookCompose.h"
+#include "nsTArray.h"
+
+#include "ImportDebug.h"
+
+#include "nsMimeTypes.h"
+#include "nsMsgUtils.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+#include "nsMsgMessageFlags.h"
+#include "nsMsgLocalFolderHdrs.h"
+
+#define NS_MSGCOMPFIELDS_CID \
+ { /* e64b0f51-0d7b-4e2f-8c60-3862ee8c174f */ \
+ 0xe64b0f51, 0x0d7b, 0x4e2f, { \
+ 0x8c, 0x60, 0x38, 0x62, 0xee, 0x8c, 0x17, 0x4f \
+ } \
+ }
+static NS_DEFINE_CID(kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID);
+
+#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);
+
+ explicit 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; }
+
+ // 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;
+ m_location = returnFile;
+ return NS_OK;
+ }
+
+ /* void OnTransportSecurityError( in string msgID, in nsresult status, in
+ * nsITransportSecurityInfo secInfo, in ACString location); */
+ NS_IMETHOD OnTransportSecurityError(const char* msgID, nsresult status,
+ nsITransportSecurityInfo* secInfo,
+ nsACString const& location) {
+ return NS_OK;
+ }
+
+ /* void OnSendNotPerformed */
+ NS_IMETHOD OnSendNotPerformed(const char* aMsgID, nsresult aStatus) {
+ return NS_OK;
+ }
+
+ /* void OnGetDraftFolderURI (); */
+ NS_IMETHOD OnGetDraftFolderURI(const char* aMsgID,
+ const nsACString& aFolderURI) {
+ return NS_OK;
+ }
+
+ static nsresult CreateSendListener(nsIMsgSendListener** ppListener);
+ void Reset() {
+ m_done = false;
+ m_location = nullptr;
+ }
+
+ public:
+ virtual ~OutlookSendListener() {}
+
+ bool m_done;
+ nsCOMPtr<nsIFile> m_location;
+};
+
+NS_IMPL_ISUPPORTS(OutlookSendListener, nsIMsgSendListener)
+
+nsresult OutlookSendListener::CreateSendListener(
+ nsIMsgSendListener** ppListener) {
+ NS_ENSURE_ARG_POINTER(ppListener);
+ NS_ADDREF(*ppListener = new OutlookSendListener());
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+nsOutlookCompose::nsOutlookCompose() {
+ m_optimizationBuffer = new char[FILE_IO_BUFFER_SIZE];
+}
+
+nsOutlookCompose::~nsOutlookCompose() {
+ 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;
+}
+
+nsCOMPtr<nsIMsgIdentity> nsOutlookCompose::m_pIdentity = nullptr;
+
+nsresult nsOutlookCompose::CreateIdentity(void) {
+ if (m_pIdentity) return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = accMgr->CreateIdentity(getter_AddRefs(m_pIdentity));
+ nsString name;
+ name.AssignLiteral("Import Identity");
+ if (m_pIdentity) {
+ m_pIdentity->SetFullName(name);
+ m_pIdentity->SetEmail("import@service.invalid"_ns);
+ }
+ return rv;
+}
+
+void nsOutlookCompose::ReleaseIdentity() { m_pIdentity = nullptr; }
+
+nsresult nsOutlookCompose::CreateComponents(void) {
+ nsresult rv = NS_OK;
+
+ m_pMsgFields = nullptr;
+ if (!m_pListener)
+ rv = OutlookSendListener::CreateSendListener(getter_AddRefs(m_pListener));
+
+ if (NS_SUCCEEDED(rv)) {
+ m_pMsgFields = do_CreateInstance(kMsgCompFieldsCID, &rv);
+ 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);
+ 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().
+
+ nsTArray<RefPtr<nsIMsgAttachedFile>> attachments;
+ msg.GetAttachments(attachments);
+
+ nsString bodyW;
+ bodyW = msg.GetBody();
+
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> 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)) {
+ nsCOMPtr<nsIMsgEmbeddedImageData> imageData =
+ new nsImportEmbeddedImageData(uri, nsDependentCString(cid),
+ nsDependentCString(name));
+ embeddedObjects.AppendElement(imageData);
+ }
+ }
+ }
+
+ nsCString bodyA;
+ const char* charset = msg.GetBodyCharset();
+ nsMsgI18NConvertFromUnicode(
+ charset ? nsDependentCString(charset) : EmptyCString(), bodyW, bodyA);
+
+ nsCOMPtr<nsIImportService> impService(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIImportService::CreateRFC822Message creates a runnable and dispatches to
+ // the main thread.
+ rv = impService->CreateRFC822Message(
+ m_pIdentity, // dummy identity
+ m_pMsgFields, // message fields
+ msg.BodyIsHtml() ? "text/html" : "text/plain",
+ bodyA, // body pointer
+ mode == nsIMsgSend::nsMsgSaveAsDraft,
+ attachments, // local attachments
+ embeddedObjects,
+ m_pListener); // listener
+
+ OutlookSendListener* pListen =
+ static_cast<OutlookSendListener*>(m_pListener.get());
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** Error, CreateAndSendMessage FAILED: 0x%x\n", rv);
+ } else {
+ // Wait for the listener to get done.
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+ while (!pListen->m_done) {
+ NS_ProcessNextEvent(thread, true);
+ }
+ }
+
+ 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(),
+ CMapiMessageHeaders(newHeadersStr.get()));
+ rv = origMsg.GetHeaders()->ToStream(pDst);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+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);
+
+ // We already know the required string size, so reduce future reallocations
+ if (!guard.IsChecking() && !m_convertCRs)
+ dest.SetCapacity(m_fileSize - m_fileReadPos);
+
+ 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:
+ explicit dest_nsCString(nsCString& str) : m_str(str) { m_str.Truncate(); }
+ void SetCapacity(int32_t sz) { m_str.SetCapacity(sz); }
+ nsresult Append(const char* buf, uint32_t count) {
+ m_str.Append(buf, count);
+ return NS_OK;
+ }
+
+ private:
+ nsCString& m_str;
+};
+
+class dest_Stream {
+ public:
+ explicit dest_Stream(nsIOutputStream* dest) : m_stream(dest) {}
+ void SetCapacity(int32_t) { /*do nothing*/
+ }
+ // 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/comm/mailnews/import/src/nsOutlookCompose.h b/comm/mailnews/import/src/nsOutlookCompose.h
new file mode 100644
index 0000000000..8d49157e68
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookCompose.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 nsOutlookCompose_h__
+#define nsOutlookCompose_h__
+
+#include "nscore.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsIImportService.h"
+#include "nsIOutputStream.h"
+
+class nsIMsgSend;
+class nsIMsgCompFields;
+class nsIMsgIdentity;
+class nsIMsgSendListener;
+
+#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);
+
+ private:
+ nsCOMPtr<nsIMsgSendListener> m_pListener;
+ nsCOMPtr<nsIMsgCompFields> m_pMsgFields;
+ static nsCOMPtr<nsIMsgIdentity> m_pIdentity;
+ char* m_optimizationBuffer;
+ nsCOMPtr<nsIImportService> m_pImportService;
+};
+
+#endif /* nsOutlookCompose_h__ */
diff --git a/comm/mailnews/import/src/nsOutlookImport.cpp b/comm/mailnews/import/src/nsOutlookImport.cpp
new file mode 100644
index 0000000000..ceafa2d7a1
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookImport.cpp
@@ -0,0 +1,522 @@
+/* -*- 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/. */
+
+/*
+ Outlook (Win32) import mail and addressbook interfaces
+*/
+#include "nscore.h"
+#include "nsString.h"
+#include "nsMsgUtils.h"
+#include "nsIImportService.h"
+#include "nsOutlookImport.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 "nsIAbDirectory.h"
+#include "nsOutlookSettings.h"
+#include "nsTextFormatter.h"
+#include "nsOutlookStringBundle.h"
+#include "ImportDebug.h"
+#include "nsUnicharUtils.h"
+
+#include "nsOutlookMail.h"
+
+#include "MapiApi.h"
+
+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);
+
+ NS_IMETHOD FindMailboxes(nsIFile* location,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes);
+
+ 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,
+ nsTArray<RefPtr<nsIImportABDescriptor>>& books);
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap* fieldMap) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor* source,
+ nsIAbDirectory* 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() {
+ 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_ASSERTION(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_ASSERTION(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_ASSERTION(supports != nullptr, "null ptr");
+ if (!supports) return NS_ERROR_NULL_POINTER;
+
+ *supports = strdup(kOutlookSupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookImport::GetSupportsUpgrade(bool* pUpgrade) {
+ NS_ASSERTION(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_ASSERTION(pImportType != nullptr, "null ptr");
+ if (!pImportType) return NS_ERROR_NULL_POINTER;
+ NS_ASSERTION(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!
+ nsCOMPtr<nsIImportMail> pMail;
+ nsCOMPtr<nsIImportGeneric> pGeneric;
+ rv = ImportOutlookMailImpl::Create(getter_AddRefs(pMail));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericMail(getter_AddRefs(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);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric));
+ pInterface.forget(ppInterface);
+ }
+ }
+ }
+ }
+ return rv;
+ }
+
+ if (!strcmp(pImportType, "addressbook")) {
+ // create the nsIImportAddressBook interface and return it!
+ nsCOMPtr<nsIImportAddressBooks> pAddress;
+ nsCOMPtr<nsIImportGeneric> pGeneric;
+ rv = ImportOutlookAddressImpl::Create(getter_AddRefs(pAddress));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(getter_AddRefs(pGeneric));
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric));
+ pInterface.forget(ppInterface);
+ }
+ }
+ }
+ return rv;
+ }
+
+ if (!strcmp(pImportType, "settings")) {
+ nsCOMPtr<nsIImportSettings> pSettings;
+ rv = nsOutlookSettings::Create(getter_AddRefs(pSettings));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pSettings));
+ pInterface.forget(ppInterface);
+ }
+ return rv;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+nsresult ImportOutlookMailImpl::Create(nsIImportMail** aImport) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new ImportOutlookMailImpl());
+ 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_ASSERTION(ppLoc != nullptr, "null ptr");
+ NS_ASSERTION(found != nullptr, "null ptr");
+ NS_ASSERTION(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;
+ resultFile.forget(ppLoc);
+ *userVerify = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportOutlookMailImpl::FindMailboxes(
+ nsIFile* pLoc, nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes) {
+ NS_ASSERTION(pLoc != nullptr, "null ptr");
+ if (!pLoc) return NS_ERROR_NULL_POINTER;
+ return m_mail.GetMailFolders(boxes);
+}
+
+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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get(), count);
+ pStream->Append(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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(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;
+ 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_ASSERTION(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_FROM_CSTRING(kDestTrashFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("sent items"))
+ _retval = NS_LITERAL_STRING_FROM_CSTRING(kDestSentFolderName);
+ else if (aFolderName.LowerCaseEqualsLiteral("outbox"))
+ _retval = NS_LITERAL_STRING_FROM_CSTRING(kDestUnsentMessagesFolderName);
+ else
+ _retval = aFolderName;
+ return NS_OK;
+}
+
+nsresult ImportOutlookAddressImpl::Create(nsIImportAddressBooks** aImport) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new ImportOutlookAddressImpl());
+ 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_ASSERTION(description != nullptr, "null ptr");
+ NS_ASSERTION(_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, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ return m_address.GetAddressBooks(books);
+}
+
+NS_IMETHODIMP ImportOutlookAddressImpl::ImportAddressBook(
+ nsIImportABDescriptor* source, nsIAbDirectory* destination,
+ nsIImportFieldMap* fieldMap, nsISupports* aSupportService,
+ char16_t** pErrorLog, char16_t** pSuccessLog, bool* fatalError) {
+ m_msgCount = 0;
+ m_msgTotal = 0;
+ NS_ASSERTION(source != nullptr, "null ptr");
+ NS_ASSERTION(destination != nullptr, "null ptr");
+ NS_ASSERTION(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 NS_OK;
+}
+
+NS_IMETHODIMP ImportOutlookAddressImpl::GetImportProgress(uint32_t* _retval) {
+ NS_ASSERTION(_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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(pText);
+ nsOutlookStringBundle::FreeString(pFmt);
+ ImportOutlookMailImpl::AddLinebreak(pStream);
+}
diff --git a/comm/mailnews/import/src/nsOutlookImport.h b/comm/mailnews/import/src/nsOutlookImport.h
new file mode 100644
index 0000000000..fb94b31502
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookImport.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 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 support the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+ protected:
+ virtual ~nsOutlookImport();
+};
+
+#endif /* nsOutlookImport_h___ */
diff --git a/comm/mailnews/import/src/nsOutlookMail.cpp b/comm/mailnews/import/src/nsOutlookMail.cpp
new file mode 100644
index 0000000000..3569c9096d
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookMail.cpp
@@ -0,0 +1,830 @@
+/* -*- 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/. */
+
+/*
+ Outlook mail import
+*/
+
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsMsgUtils.h"
+#include "nsIImportService.h"
+#include "nsIImportFieldMap.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsIImportABDescriptor.h"
+#include "nsOutlookStringBundle.h"
+#include "nsIAbCard.h"
+#include "mdb.h"
+#include "ImportDebug.h"
+#include "nsOutlookMail.h"
+#include "nsUnicharUtils.h"
+#include "nsIOutputStream.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgFolder.h"
+#include "nsMsgI18N.h"
+#include "nsNetUtil.h"
+
+/* ------------ 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(
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes) {
+ if (!m_haveMapi) {
+ IMPORT_LOG0("GetMailFolders called before Mapi is initialized\n");
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv;
+ boxes.Clear();
+
+ 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
+ nsCOMPtr<nsIImportMailboxDescriptor> pID;
+ nsString name;
+ nsString uniName;
+
+ for (i = 0; i < m_folderList.GetSize(); i++) {
+ pFolder = m_folderList.GetItem(i);
+ rv = impSvc->CreateNewMailboxDescriptor(getter_AddRefs(pID));
+ if (NS_SUCCEEDED(rv)) {
+ pID->SetDepth(pFolder->GetDepth());
+ pID->SetIdentifier(i);
+
+ pFolder->GetDisplayName(name);
+ pID->SetDisplayName(name.get());
+
+ pID->SetSize(1000);
+ boxes.AppendElement(pID);
+ }
+ }
+ return NS_OK;
+}
+
+bool nsOutlookMail::IsAddressBookNameUnique(nsString& name, nsString& list) {
+ nsString usedName;
+ usedName.Append('[');
+ 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.Append('[');
+ list.Append(name);
+ list.AppendLiteral("],");
+}
+
+nsresult nsOutlookMail::GetAddressBooks(
+ nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ books.Clear();
+ if (!m_haveMapi) {
+ IMPORT_LOG0("GetAddressBooks called before Mapi is initialized\n");
+ return NS_ERROR_FAILURE;
+ }
+ nsresult 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
+ nsCOMPtr<nsIImportABDescriptor> pID;
+ nsString name;
+ nsString list;
+
+ for (i = 0; i < m_addressList.GetSize(); i++) {
+ pFolder = m_addressList.GetItem(i);
+ if (!pFolder->IsStore()) {
+ rv = impSvc->CreateNewABDescriptor(getter_AddRefs(pID));
+ if (NS_SUCCEEDED(rv)) {
+ pID->SetIdentifier(i);
+ pFolder->GetDisplayName(name);
+ MakeAddressBookNameUnique(name, list);
+ pID->SetPreferredName(name);
+ pID->SetSize(100);
+ books.AppendElement(pID);
+ }
+ }
+ }
+ 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
+
+ImportMailboxRunnable::ImportMailboxRunnable(
+ uint32_t* pDoneSoFar, bool* pAbort, int32_t index, const char16_t* pName,
+ nsIMsgFolder* dstFolder, int32_t* pMsgCount, nsOutlookMail* aCaller)
+ : mozilla::Runnable("ImportMailboxRunnable"),
+ mResult(NS_OK),
+ mCaller(aCaller),
+ mDoneSoFar(pDoneSoFar),
+ mAbort(pAbort),
+ mIndex(index),
+ mName(pName),
+ mDstFolder(dstFolder),
+ mMsgCount(pMsgCount) {}
+NS_IMETHODIMP ImportMailboxRunnable::Run() {
+ if ((mIndex < 0) || (mIndex >= mCaller->m_folderList.GetSize())) {
+ IMPORT_LOG0("*** Bad mailbox identifier, unable to import\n");
+ *mAbort = true;
+ mResult = NS_ERROR_FAILURE;
+ return NS_OK; // Sync runnable must return OK.
+ }
+
+ int32_t dummyMsgCount = 0;
+ if (mMsgCount)
+ *mMsgCount = 0;
+ else
+ mMsgCount = &dummyMsgCount;
+
+ CMapiFolder* pFolder = mCaller->m_folderList.GetItem(mIndex);
+ mCaller->OpenMessageStore(pFolder);
+ if (!mCaller->m_lpMdb) {
+ IMPORT_LOG1("*** Unable to obtain mapi message store for mailbox: %S\n",
+ (const wchar_t*)mName);
+ mResult = NS_ERROR_FAILURE;
+ return NS_OK; // Sync runnable must return OK.
+ }
+
+ if (pFolder->IsStore()) return NS_OK;
+
+ // now what?
+ CMapiFolderContents contents(mCaller->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 = mDstFolder->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", (const wchar_t*)mName);
+ mResult = NS_ERROR_FAILURE;
+ return NS_OK; // Sync runnable must return OK.
+ }
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = msgStore->GetNewMsgOutputStream(mDstFolder, getter_AddRefs(msgHdr),
+ getter_AddRefs(outputStream));
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** Error getting nsIOutputStream of mailbox: %S\n",
+ (const wchar_t*)mName);
+ mResult = rv;
+ return NS_OK; // Sync runnable must return OK.
+ }
+ totalCount = contents.GetCount();
+ doneCalc = *mMsgCount;
+ doneCalc /= totalCount;
+ doneCalc *= 1000;
+ if (mDoneSoFar) {
+ *mDoneSoFar = (uint32_t)doneCalc;
+ if (*mDoneSoFar > 1000) *mDoneSoFar = 1000;
+ }
+
+ if (!done && (oType == MAPI_MESSAGE)) {
+ if (!mCaller->m_mapi.OpenMdbEntry(mCaller->m_lpMdb, cbEid, lpEid,
+ (LPUNKNOWN*)&lpMsg)) {
+ IMPORT_LOG1("*** Error opening messages in mailbox: %S\n",
+ (const wchar_t*)mName);
+ mResult = NS_ERROR_FAILURE;
+ return NS_OK; // Sync runnable must return OK.
+ }
+
+ // 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(mName);
+ 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
+ (*mMsgCount)++;
+ msgStore->FinishNewMessage(outputStream, msgHdr);
+ outputStream = nullptr;
+ } else {
+ IMPORT_LOG1("*** Error reading message from mailbox: %S\n",
+ (const wchar_t*)mName);
+ msgStore->DiscardNewMessage(outputStream, msgHdr);
+ outputStream = nullptr;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult ProxyImportMailbox(uint32_t* pDoneSoFar, bool* pAbort, int32_t index,
+ const char16_t* pName, nsIMsgFolder* dstFolder,
+ int32_t* pMsgCount, nsOutlookMail* aCaller) {
+ RefPtr<ImportMailboxRunnable> importMailbox = new ImportMailboxRunnable(
+ pDoneSoFar, pAbort, index, pName, dstFolder, pMsgCount, aCaller);
+ nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete(
+ "ProxyImportMailbox"_ns, mozilla::GetMainThreadSerialEventTarget(),
+ do_AddRef(importMailbox));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return importMailbox->mResult;
+}
+
+nsresult nsOutlookMail::ImportMailbox(uint32_t* pDoneSoFar, bool* pAbort,
+ int32_t index, const char16_t* pName,
+ nsIMsgFolder* dstFolder,
+ int32_t* pMsgCount) {
+ return ProxyImportMailbox(pDoneSoFar, pAbort, index, pName, dstFolder,
+ pMsgCount, this);
+}
+
+nsresult ImportMailboxRunnable::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
+ nsOutlookMail::WriteData(pDest, "\x0D\x0A", 2);
+
+ return rv;
+}
+
+BOOL nsOutlookMail::WriteData(nsIOutputStream* pDest, const char* pData,
+ uint32_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,
+ nsIAbDirectory* pDirectory,
+ 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",
+ (const wchar_t*)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",
+ (const wchar_t*)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",
+ (const wchar_t*)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);
+
+ nsCOMPtr<nsIAbCard> newCard =
+ do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv);
+ if (newCard) {
+ if (BuildCard(subject.get(), pDirectory, newCard, lpMsg,
+ pFieldMap)) {
+ nsIAbCard* outCard;
+ pDirectory->AddCard(newCard, &outCard);
+ }
+ }
+ } 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, pDirectory, lpMsg, pFieldMap);
+ }
+ }
+
+ lpMsg->Release();
+ }
+ }
+
+ return rv;
+}
+nsresult nsOutlookMail::CreateList(const nsString& pName,
+ nsIAbDirectory* pDirectory,
+ LPMAPIPROP pUserList,
+ nsIImportFieldMap* pFieldMap) {
+ // If no name provided then we're done.
+ if (pName.IsEmpty()) return NS_OK;
+
+ nsresult rv = NS_ERROR_FAILURE;
+ // Make sure we have db to work with.
+ if (!pDirectory) return rv;
+
+ nsCOMPtr<nsIAbDirectory> newList =
+ do_CreateInstance("@mozilla.org/addressbook/directoryproperty;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = newList->SetDirName(pName);
+ 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",
+ static_cast<const wchar_t*>(pName.get()));
+ 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<nsIAbCard> newCard =
+ do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv);
+ if (newCard) {
+ if (BuildCard(subject.get(), pDirectory, newCard, lpMsg, pFieldMap)) {
+ nsIAbCard* outCard;
+ newList->AddCard(newCard, &outCard);
+ }
+ }
+ }
+ m_mapi.MAPIFreeBuffer(value);
+
+ nsIAbDirectory* outList;
+ rv = pDirectory->AddMailList(newList, &outList);
+ return rv;
+}
+
+void nsOutlookMail::SanitizeValue(nsString& val) {
+ val.ReplaceSubstring(u"\r\n"_ns, u", "_ns);
+ val.ReplaceChar(u"\r\n", u',');
+}
+
+void nsOutlookMail::SplitString(nsString& val1, nsString& val2) {
+ // Find the last line if there is more than one!
+ int32_t idx = val1.RFind(u"\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, nsIAbDirectory* pDirectory,
+ nsIAbCard* newCard, 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()) {
+ newCard->SetDisplayName(displayName);
+ }
+ if (!firstName.IsEmpty()) {
+ newCard->SetFirstName(firstName);
+ }
+ if (!lastName.IsEmpty()) {
+ newCard->SetLastName(lastName);
+ }
+ if (!nickName.IsEmpty()) {
+ newCard->SetPropertyAsAString(kNicknameProperty, nickName);
+ }
+ if (!eMail.IsEmpty()) {
+ newCard->SetPrimaryEmail(eMail);
+ }
+ if (!secondEMail.IsEmpty()) {
+ newCard->SetPropertyAsAString(k2ndEmailProperty, secondEMail);
+ }
+
+ // 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(pDirectory, newCard,
+ gMapiFields[i].mozField, value);
+ } else if (gMapiFields[i].multiLine == kIsMultiLine) {
+ pFieldMap->SetFieldValue(pDirectory, newCard,
+ gMapiFields[i].mozField, value);
+ } else {
+ line2.Truncate();
+ SplitString(value, line2);
+ if (!value.IsEmpty())
+ pFieldMap->SetFieldValue(pDirectory, newCard,
+ gMapiFields[i].mozField, value);
+ if (!line2.IsEmpty())
+ pFieldMap->SetFieldValue(pDirectory, newCard,
+ gMapiFields[i].multiLine, line2);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/comm/mailnews/import/src/nsOutlookMail.h b/comm/mailnews/import/src/nsOutlookMail.h
new file mode 100644
index 0000000000..8f28d6425d
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookMail.h
@@ -0,0 +1,84 @@
+/* -*- 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 "nsTArray.h"
+#include "nsString.h"
+#include "nsOutlookCompose.h"
+#include "nsIFile.h"
+#include "MapiApi.h"
+#include "MapiMessage.h"
+#include "nsIAbDirectory.h"
+#include "nsThreadUtils.h"
+
+class nsIImportFieldMap;
+
+class nsOutlookMail {
+ public:
+ nsOutlookMail();
+ ~nsOutlookMail();
+
+ nsresult GetMailFolders(nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes);
+ nsresult GetAddressBooks(nsTArray<RefPtr<nsIImportABDescriptor>>& books);
+ nsresult ImportMailbox(uint32_t* pDoneSoFar, bool* pAbort, int32_t index,
+ const char16_t* pName, nsIMsgFolder* pDest,
+ int32_t* pMsgCount);
+ nsresult ImportAddresses(uint32_t* pCount, uint32_t* pTotal,
+ const char16_t* pName, uint32_t id,
+ nsIAbDirectory* pDirectory, nsString& errors);
+ void OpenMessageStore(CMapiFolder* pNextFolder);
+ static BOOL WriteData(nsIOutputStream* pDest, const char* pData,
+ uint32_t len);
+
+ private:
+ 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, nsIAbDirectory* pDirectory,
+ nsIAbCard* newCard, LPMAPIPROP pUser,
+ nsIImportFieldMap* pFieldMap);
+ nsresult CreateList(const nsString& pName, nsIAbDirectory* pDirectory,
+ LPMAPIPROP pUserList, nsIImportFieldMap* pFieldMap);
+
+ private:
+ bool m_gotFolders;
+ bool m_gotAddresses;
+ bool m_haveMapi;
+ CMapiFolderList m_addressList;
+ CMapiFolderList m_storeList;
+
+ public:
+ // Needed for the proxy class.
+ CMapiApi m_mapi;
+ CMapiFolderList m_folderList;
+ LPMDB m_lpMdb;
+};
+
+class ImportMailboxRunnable : public mozilla::Runnable {
+ public:
+ ImportMailboxRunnable(uint32_t* pDoneSoFar, bool* pAbort, int32_t index,
+ const char16_t* pName, nsIMsgFolder* dstFolder,
+ int32_t* pMsgCount, nsOutlookMail* aCaller);
+ NS_DECL_NSIRUNNABLE
+ static nsresult ImportMessage(LPMESSAGE lpMsg, nsIOutputStream* pDest,
+ nsMsgDeliverMode mode);
+ nsresult mResult;
+
+ private:
+ nsOutlookMail* mCaller;
+ uint32_t* mDoneSoFar;
+ bool* mAbort;
+ int32_t mIndex;
+ const char16_t* mName;
+ nsCOMPtr<nsIFile> mMessageFile;
+ nsCOMPtr<nsIMsgFolder> mDstFolder;
+ int32_t* mMsgCount;
+};
+
+#endif /* nsOutlookMail_h___ */
diff --git a/comm/mailnews/import/src/nsOutlookSettings.cpp b/comm/mailnews/import/src/nsOutlookSettings.cpp
new file mode 100644
index 0000000000..2e202a5600
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookSettings.cpp
@@ -0,0 +1,500 @@
+/* -*- 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/. */
+
+/*
+ Outlook (Win32) settings
+*/
+
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsMsgUtils.h"
+#include "nsOutlookImport.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsIImportSettings.h"
+#include "nsOutlookSettings.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsOutlookStringBundle.h"
+#include "ImportDebug.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsMsgI18N.h"
+#include <windows.h>
+#include "nsIWindowsRegKey.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNativeCharsetUtils.h"
+
+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_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new nsOutlookSettings());
+ return NS_OK;
+}
+
+nsOutlookSettings::nsOutlookSettings() {}
+
+nsOutlookSettings::~nsOutlookSettings() {}
+
+NS_IMPL_ISUPPORTS(nsOutlookSettings, nsIImportSettings)
+
+NS_IMETHODIMP nsOutlookSettings::AutoLocate(char16_t** description,
+ nsIFile** location, bool* _retval) {
+ NS_ASSERTION(description != nullptr, "null ptr");
+ NS_ASSERTION(_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_ASSERTION(_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_FROM_CSTRING(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_FROM_CSTRING(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_FROM_CSTRING(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_FROM_CSTRING(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(u"Default Mail Account"_ns, 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("@mozilla.org/messenger/account-manager;1", &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(u"IMAP Server"_ns, value);
+ if (NS_SUCCEEDED(rv) &&
+ DoIMAPServer(accMgr, subKey, value, getter_AddRefs(account)))
+ accounts++;
+
+ rv = subKey->ReadStringValue(u"POP3 Server"_ns, 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(u"Account Name"_ns, 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(u"IMAP User Name"_ns, 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;
+ aMgr->FindServer(nativeUserName, nativeServerName, "imap"_ns, 0,
+ getter_AddRefs(in));
+ if (!in) {
+ // Create the incoming server and an account for it?
+ rv = aMgr->CreateIncomingServer(nativeUserName, nativeServerName, "imap"_ns,
+ getter_AddRefs(in));
+ if (NS_SUCCEEDED(rv) && in) {
+ rv = in->SetType("imap"_ns);
+ // 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(u"POP3 User Name"_ns, 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;
+ aMgr->FindServer(nativeUserName, nativeServerName, "pop3"_ns, 0,
+ getter_AddRefs(in));
+ if (in) return true;
+
+ // Create the incoming server and an account for it?
+ rv = aMgr->CreateIncomingServer(nativeUserName, nativeServerName, "pop3"_ns,
+ getter_AddRefs(in));
+ rv = in->SetType("pop3"_ns);
+
+ // 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(u"Leave Mail On Server"_ns, &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(u"SMTP Display Name"_ns, name);
+
+ nsAutoString server;
+ aKey->ReadStringValue(u"SMTP Server"_ns, server);
+
+ nsAutoString email;
+ aKey->ReadStringValue(u"SMTP Email Address"_ns, email);
+
+ nsAutoString reply;
+ aKey->ReadStringValue(u"SMTP Reply To Email Address"_ns, reply);
+
+ nsAutoString userName;
+ aKey->ReadStringValue(u"SMTP User Name"_ns, userName);
+
+ nsAutoString orgName;
+ aKey->ReadStringValue(u"SMTP Organization Name"_ns, 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("@mozilla.org/messengercompose/smtp;1", &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("Created new SMTP server: %s\n", nativeServerName.get());
+ return NS_OK;
+}
diff --git a/comm/mailnews/import/src/nsOutlookSettings.h b/comm/mailnews/import/src/nsOutlookSettings.h
new file mode 100644
index 0000000000..7410200c3d
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookSettings.h
@@ -0,0 +1,27 @@
+/* -*- 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/comm/mailnews/import/src/nsOutlookStringBundle.cpp b/comm/mailnews/import/src/nsOutlookStringBundle.cpp
new file mode 100644
index 0000000000..140eb9a541
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookStringBundle.cpp
@@ -0,0 +1,53 @@
+/* -*- 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 "mozilla/Components.h"
+
+#define OUTLOOK_MSGS_URL \
+ "chrome://messenger/locale/outlookImportMsgs.properties"
+
+nsCOMPtr<nsIStringBundle> nsOutlookStringBundle::m_pBundle = nullptr;
+
+void nsOutlookStringBundle::GetStringBundle(void) {
+ if (m_pBundle) return;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::components::StringBundle::Service();
+ if (sBundleService) {
+ sBundleService->CreateBundle(OUTLOOK_MSGS_URL, getter_AddRefs(m_pBundle));
+ }
+}
+
+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) GetStringBundle();
+
+ if (m_pBundle) {
+ nsAutoString str;
+ nsresult rv = m_pBundle->GetStringFromID(stringID, str);
+
+ if (NS_SUCCEEDED(rv)) return ToNewUnicode(str);
+ }
+
+ nsString resultString;
+ resultString.AppendLiteral("[StringID ");
+ resultString.AppendInt(stringID);
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
+
+void nsOutlookStringBundle::Cleanup(void) { m_pBundle = nullptr; }
diff --git a/comm/mailnews/import/src/nsOutlookStringBundle.h b/comm/mailnews/import/src/nsOutlookStringBundle.h
new file mode 100644
index 0000000000..491bbfaa64
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookStringBundle.h
@@ -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/. */
+
+#ifndef _nsOutlookStringBundle_H__
+#define _nsOutlookStringBundle_H__
+
+#include "nsCRTGlue.h"
+#include "nsString.h"
+
+class nsIStringBundle;
+
+class nsOutlookStringBundle {
+ public:
+ static char16_t* GetStringByID(int32_t stringID);
+ static void GetStringByID(int32_t stringID, nsString& result);
+ static void GetStringBundle(void);
+ static void FreeString(char16_t* pStr) { free(pStr); }
+ static void Cleanup(void);
+
+ private:
+ static nsCOMPtr<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/comm/mailnews/import/src/nsTextAddress.cpp b/comm/mailnews/import/src/nsTextAddress.cpp
new file mode 100644
index 0000000000..497bd5b398
--- /dev/null
+++ b/comm/mailnews/import/src/nsTextAddress.cpp
@@ -0,0 +1,426 @@
+/* -*- 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 "nsIAbCard.h"
+#include "nsIAbDirectory.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsNetUtil.h"
+#include "nsMsgI18N.h"
+#include "nsMsgUtils.h"
+#include "nsIConverterInputStream.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsMsgUtils.h"
+
+#include "ImportDebug.h"
+#include "plstr.h"
+#include "msgCore.h"
+
+#define kWhitespace " \t\b\r\n"
+
+nsTextAddress::nsTextAddress() {
+ m_LFCount = 0;
+ m_CRCount = 0;
+}
+
+nsTextAddress::~nsTextAddress() {}
+
+nsresult nsTextAddress::GetUnicharLineStreamForFile(
+ nsIFile* aFile, nsIInputStream* aInputStream,
+ nsIUnicharLineInputStream** aStream) {
+ nsAutoCString charset;
+ nsresult rv = MsgDetectCharsetFromFile(aFile, charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ 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,
+ nsIAbDirectory* pDirectory,
+ nsIImportFieldMap* fieldMap,
+ nsString& errors, uint32_t* pProgress) {
+ // Open the source file for reading, read each line and process it!
+ m_directory = pDirectory;
+ m_fieldMap = fieldMap;
+
+ 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 process 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 NS_OK;
+}
+
+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 += line.CountChar(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(u"\"\"");
+ while (offset != -1) {
+ field.Cut(offset, 1);
+ offset = field.Find(u"\"\"", 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<nsIAbCard> newCard;
+ 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 (!newCard) {
+ newCard = do_CreateInstance(
+ "@mozilla.org/addressbook/cardproperty;1", &rv);
+ }
+ if (newCard) {
+ rv = m_fieldMap->SetFieldValue(m_directory, newCard, fieldNum,
+ fieldVal);
+ }
+ }
+ } else {
+ break;
+ }
+ } else if (active) {
+ IMPORT_LOG1("*** Error getting field map for index %ld\n", (long)i);
+ }
+ }
+
+ nsIAbCard* outCard;
+ rv = m_directory->AddCard(newCard, &outCard);
+
+ return rv;
+}
diff --git a/comm/mailnews/import/src/nsTextAddress.h b/comm/mailnews/import/src/nsTextAddress.h
new file mode 100644
index 0000000000..58b37755b3
--- /dev/null
+++ b/comm/mailnews/import/src/nsTextAddress.h
@@ -0,0 +1,60 @@
+/* -*- 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 "nsString.h"
+#include "nsIImportFieldMap.h"
+#include "nsIImportService.h"
+
+class nsIAbDirectory;
+class nsIFile;
+class nsIInputStream;
+class nsIUnicharLineInputStream;
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+class nsTextAddress {
+ public:
+ nsTextAddress();
+ virtual ~nsTextAddress();
+
+ nsresult ImportAddresses(bool* pAbort, const char16_t* pName, nsIFile* pSrc,
+ nsIAbDirectory* pDirectory,
+ 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;
+ nsCOMPtr<nsIAbDirectory> m_directory;
+ nsCOMPtr<nsIImportFieldMap> m_fieldMap;
+ nsCOMPtr<nsIImportService> m_pService;
+};
+
+#endif /* nsTextAddress_h__ */
diff --git a/comm/mailnews/import/src/nsTextImport.cpp b/comm/mailnews/import/src/nsTextImport.cpp
new file mode 100644
index 0000000000..961f1631ae
--- /dev/null
+++ b/comm/mailnews/import/src/nsTextImport.cpp
@@ -0,0 +1,646 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsIImportService.h"
+#include "nsMsgI18N.h"
+#include "nsTextImport.h"
+#include "nsIImportGeneric.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportFieldMap.h"
+#include "nsIAbLDIFService.h"
+#include "nsTextFormatter.h"
+#include "nsImportStringBundle.h"
+#include "nsTextAddress.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "ImportDebug.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
+
+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,
+ nsTArray<RefPtr<nsIImportABDescriptor>>& books) override;
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap* fieldMap) override;
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor* source,
+ nsIAbDirectory* 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() {
+ 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_ASSERTION(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!
+ nsCOMPtr<nsIImportAddressBooks> pAddress;
+ nsCOMPtr<nsIImportGeneric> pGeneric;
+ rv = ImportAddressImpl::Create(getter_AddRefs(pAddress), m_stringBundle);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(getter_AddRefs(pGeneric));
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric));
+ pInterface.forget(ppInterface);
+ }
+ }
+ }
+ return rv;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+nsresult ImportAddressImpl::Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle* aStringBundle) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new ImportAddressImpl(aStringBundle));
+ 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_ASSERTION(addrDescription != nullptr, "null ptr");
+ NS_ASSERTION(_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_ASSERTION(found != nullptr, "null ptr");
+ NS_ASSERTION(ppLoc != nullptr, "null ptr");
+ NS_ASSERTION(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, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ NS_ASSERTION(pLoc != nullptr, "null ptr");
+ if (!pLoc) return NS_ERROR_NULL_POINTER;
+
+ books.Clear();
+ ClearSampleFile();
+
+ 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 = pLoc;
+
+ /* Build an address book descriptor based on the file passed in! */
+ 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);
+ books.AppendElement(desc);
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error creating address book descriptor for text import\n");
+ return rv;
+ }
+ 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);
+
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(pText);
+ 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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(pText);
+ 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,
+ nsIAbDirectory* pDestination,
+ nsIImportFieldMap* fieldMap,
+ nsISupports* aSupportService,
+ char16_t** pErrorLog,
+ char16_t** pSuccessLog, bool* fatalError) {
+ NS_ASSERTION(pSource != nullptr, "null ptr");
+ NS_ASSERTION(pDestination != nullptr, "null ptr");
+ NS_ASSERTION(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("@mozilla.org/addressbook/abldifservice;1", &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(u"\x0D\x0A"_ns);
+ while (offset != -1) {
+ val.Replace(offset, 2, u", "_ns);
+ offset = val.Find(u"\x0D\x0A"_ns, 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_ASSERTION(pFound != nullptr, "null ptr");
+ NS_ASSERTION(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_xstrdup(&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_xstrdup(&term);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP ImportAddressImpl::SetSampleLocation(nsIFile* pLocation) {
+ NS_ENSURE_ARG_POINTER(pLocation);
+
+ m_fileLoc = 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", 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++;
+ if (!active) {
+ fNum *= -1; // Re-add the stripped minus sign.
+ }
+ 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", prefStr);
+ if (NS_FAILED(rv) || !str.Equals(prefStr))
+ rv = prefs->SetCharPref("mailnews.import.text.fieldmap", str);
+ }
+
+ // 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/comm/mailnews/import/src/nsTextImport.h b/comm/mailnews/import/src/nsTextImport.h
new file mode 100644
index 0000000000..1106a0d098
--- /dev/null
+++ b/comm/mailnews/import/src/nsTextImport.h
@@ -0,0 +1,40 @@
+/* -*- 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 support the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+ protected:
+ virtual ~nsTextImport();
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+#endif /* nsTextImport_h___ */
diff --git a/comm/mailnews/import/src/nsVCardAddress.cpp b/comm/mailnews/import/src/nsVCardAddress.cpp
new file mode 100644
index 0000000000..2ced1d7c0b
--- /dev/null
+++ b/comm/mailnews/import/src/nsVCardAddress.cpp
@@ -0,0 +1,151 @@
+/* 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 "nsNativeCharsetUtils.h"
+#include "nsNetUtil.h"
+#include "nsVCardAddress.h"
+
+#include "nsIAbCard.h"
+#include "nsIAbDirectory.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsIConverterInputStream.h"
+#include "nsIMsgVCardService.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,
+ nsIAbDirectory* pDirectory,
+ 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;
+
+ // Try to detect the character set and decode. Only UTF-8 is valid from
+ // vCard 4.0, but we support older versions, so other charsets are possible.
+
+ nsAutoCString sourceCharset;
+ rv = MsgDetectCharsetFromFile(pSrc, sourceCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIConverterInputStream> converterStream =
+ do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
+ NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
+
+ rv = converterStream->Init(
+ inputStream, sourceCharset.get(), 8192,
+ nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIUnicharLineInputStream> lineStream(
+ do_QueryInterface(converterStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgVCardService> vCardService =
+ do_GetService("@mozilla.org/addressbook/msgvcardservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ nsAutoString 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 = vCardService->VCardToAbCard(record, getter_AddRefs(cardFromVCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIAbCard* outCard;
+ rv = pDirectory->AddCard(cardFromVCard, &outCard);
+ 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 converterStream 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 NS_OK;
+}
+
+nsresult nsVCardAddress::ReadRecord(nsIUnicharLineInputStream* aLineStream,
+ nsString& aRecord, bool* aMore) {
+ bool more = true;
+ nsresult rv;
+ nsAutoString 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/comm/mailnews/import/src/nsVCardAddress.h b/comm/mailnews/import/src/nsVCardAddress.h
new file mode 100644
index 0000000000..4cdb4de2b4
--- /dev/null
+++ b/comm/mailnews/import/src/nsVCardAddress.h
@@ -0,0 +1,28 @@
+/* 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 "ImportDebug.h"
+
+class nsIAbDirectory;
+class nsIFile;
+class nsIUnicharLineInputStream;
+
+class nsVCardAddress {
+ public:
+ nsVCardAddress();
+ virtual ~nsVCardAddress();
+
+ nsresult ImportAddresses(bool* pAbort, const char16_t* pName, nsIFile* pSrc,
+ nsIAbDirectory* pDirectory, nsString& errors,
+ uint32_t* pProgress);
+
+ private:
+ static nsresult ReadRecord(nsIUnicharLineInputStream* aLineStream,
+ nsString& aRecord, bool* aMore);
+};
+
+#endif /* nsVCardAddress_h__ */
diff --git a/comm/mailnews/import/src/nsVCardImport.cpp b/comm/mailnews/import/src/nsVCardImport.cpp
new file mode 100644
index 0000000000..98f7482ddf
--- /dev/null
+++ b/comm/mailnews/import/src/nsVCardImport.cpp
@@ -0,0 +1,352 @@
+/* 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 "nsIFile.h"
+#include "nsIImportABDescriptor.h"
+#include "nsIImportAddressBooks.h"
+#include "nsIImportFieldMap.h"
+#include "nsIImportGeneric.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"
+
+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,
+ nsTArray<RefPtr<nsIImportABDescriptor>>& books) override;
+
+ NS_IMETHOD InitFieldMap(nsIImportFieldMap* fieldMap) override {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_IMETHOD ImportAddressBook(nsIImportABDescriptor* source,
+ nsIAbDirectory* 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() {
+ 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!
+ nsCOMPtr<nsIImportAddressBooks> pAddress;
+ nsCOMPtr<nsIImportGeneric> pGeneric;
+ rv = ImportVCardAddressImpl::Create(getter_AddRefs(pAddress),
+ m_stringBundle);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIImportService> impSvc(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = impSvc->CreateNewGenericAddressBooks(getter_AddRefs(pGeneric));
+ if (NS_SUCCEEDED(rv)) {
+ pGeneric->SetData("addressInterface", pAddress);
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric));
+ pInterface.forget(ppInterface);
+ }
+ }
+ }
+ return rv;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult ImportVCardAddressImpl::Create(nsIImportAddressBooks** aImport,
+ nsIStringBundle* aStringBundle) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new ImportVCardAddressImpl(aStringBundle));
+ 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, nsTArray<RefPtr<nsIImportABDescriptor>>& books) {
+ NS_ENSURE_ARG_POINTER(pLoc);
+
+ books.Clear();
+ 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 = pLoc;
+
+ /* Build an address book descriptor based on the file passed in! */
+ 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);
+ books.AppendElement(desc);
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0(
+ "*** Error creating address book descriptor for vCard import\n");
+ return rv;
+ }
+
+ 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);
+
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(pText);
+ 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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(pText);
+ 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, nsIAbDirectory* 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/comm/mailnews/import/src/nsVCardImport.h b/comm/mailnews/import/src/nsVCardImport.h
new file mode 100644
index 0000000000..f35e5eb3d3
--- /dev/null
+++ b/comm/mailnews/import/src/nsVCardImport.h
@@ -0,0 +1,39 @@
+/* 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 support the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+ protected:
+ virtual ~nsVCardImport();
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+#endif /* nsVCardImport_h___ */
diff --git a/comm/mailnews/import/src/nsWMImport.cpp b/comm/mailnews/import/src/nsWMImport.cpp
new file mode 100644
index 0000000000..a3b5dd45b2
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMImport.cpp
@@ -0,0 +1,199 @@
+/* -*- 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/. */
+
+/*
+ * Windows Live Mail (Win32) import mail and addressbook interfaces
+ */
+
+#include "nscore.h"
+#include "nsString.h"
+#include "nsMsgUtils.h"
+#include "nsWMImport.h"
+#include "nsIImportMail.h"
+#include "nsIImportMailboxDescriptor.h"
+#include "nsXPCOM.h"
+#include "nsWMSettings.h"
+#include "nsTextFormatter.h"
+#include "nsWMStringBundle.h"
+#include "nsUnicharUtils.h"
+
+#include "ImportDebug.h"
+
+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,
+ nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes);
+
+ 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();
+};
+
+nsWMImport::nsWMImport() {
+ 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_ASSERTION(supports != nullptr, "null ptr");
+ if (!supports) return NS_ERROR_NULL_POINTER;
+
+ *supports = strdup(kWMSupportsString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWMImport::GetSupportsUpgrade(bool* pUpgrade) {
+ NS_ASSERTION(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")) {
+ nsCOMPtr<nsIImportSettings> pSettings;
+ rv = nsWMSettings::Create(getter_AddRefs(pSettings));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pSettings));
+ pInterface.forget(ppInterface);
+ }
+ return rv;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+nsresult ImportWMMailImpl::Create(nsIImportMail** aImport) {
+ NS_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new ImportWMMailImpl());
+ 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, nsTArray<RefPtr<nsIImportMailboxDescriptor>>& boxes) {
+ 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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get(), count);
+ pStream->Append(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);
+ nsString pText;
+ nsTextFormatter::ssprintf(pText, pFmt, name.get());
+ pStream->Append(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/comm/mailnews/import/src/nsWMImport.h b/comm/mailnews/import/src/nsWMImport.h
new file mode 100644
index 0000000000..1f14b1331f
--- /dev/null
+++ b/comm/mailnews/import/src/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 support the nsIImportModule interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_DECL_NSIIMPORTMODULE
+
+ protected:
+ virtual ~nsWMImport();
+};
+
+#endif /* nsWMImport_h___ */
diff --git a/comm/mailnews/import/src/nsWMSettings.cpp b/comm/mailnews/import/src/nsWMSettings.cpp
new file mode 100644
index 0000000000..ee741fa053
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMSettings.cpp
@@ -0,0 +1,679 @@
+/* -*- 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/. */
+
+/*
+ Windows Live Mail (Win32) settings
+*/
+
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsString.h"
+#include "nsMsgUtils.h"
+#include "nsWMImport.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsIImportSettings.h"
+#include "nsWMSettings.h"
+#include "nsMsgI18N.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsWMStringBundle.h"
+#include "ImportDebug.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsIImapIncomingServer.h"
+#include "nsINntpIncomingServer.h"
+#include "stdlib.h"
+#include "mozilla/dom/Document.h"
+#include "nsIFile.h"
+#include "nsNetUtil.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,
+ mozilla::dom::Document* xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount** ppAccount);
+ static bool DoPOP3Server(nsIMsgAccountManager* pMgr,
+ mozilla::dom::Document* xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount** ppAccount);
+ static bool DoNNTPServer(nsIMsgAccountManager* pMgr,
+ mozilla::dom::Document* xmlDoc,
+ const nsString& serverName,
+ nsIMsgAccount** ppAccount);
+ static void SetIdentities(nsIMsgAccountManager* pMgr, nsIMsgAccount* pAcc,
+ mozilla::dom::Document* xmlDoc,
+ nsAutoString& userName, int32_t authMethodIncoming,
+ bool isNNTP);
+ static void SetSmtpServer(mozilla::dom::Document* 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_ENSURE_ARG_POINTER(aImport);
+ NS_ADDREF(*aImport = new nsWMSettings());
+ return NS_OK;
+}
+
+nsWMSettings::nsWMSettings() {}
+
+nsWMSettings::~nsWMSettings() {}
+
+NS_IMPL_ISUPPORTS(nsWMSettings, nsIImportSettings)
+
+NS_IMETHODIMP nsWMSettings::AutoLocate(char16_t** description,
+ nsIFile** location, bool* _retval) {
+ NS_ASSERTION(description != nullptr, "null ptr");
+ NS_ASSERTION(_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_ASSERTION(_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(u"mail"_ns,
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+ getter_AddRefs(subKey)))) {
+ uint32_t dwordResult = 0xffffffff;
+ rv = subKey->ReadIntValue(u"Poll For Mail"_ns,
+ &dwordResult); // reg_dword
+ subKey->Close();
+ if (NS_SUCCEEDED(rv) && dwordResult != 0xffffffff) {
+ 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(u"Default Mail Account"_ns,
+ defMailAcct); // ref_sz
+ key->ReadStringValue(u"Default News Account"_ns,
+ defNewsAcct); // ref_sz
+
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &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<mozilla::dom::Document> 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,
+ mozilla::dom::Document* 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), "imap"_ns,
+ 0, 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),
+ "imap"_ns, 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",
+ static_cast<const wchar_t*>(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",
+ static_cast<const wchar_t*>(serverName.get()),
+ static_cast<const wchar_t*>(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,
+ mozilla::dom::Document* 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), "pop3"_ns,
+ 0, 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),
+ "pop3"_ns, 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",
+ static_cast<const wchar_t*>(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",
+ static_cast<const wchar_t*>(serverName.get()),
+ static_cast<const wchar_t*>(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",
+ static_cast<const wchar_t*>(serverName.get()),
+ static_cast<const wchar_t*>(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,
+ mozilla::dom::Document* 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),
+ "nntp"_ns, 0, getter_AddRefs(in));
+ if (NS_FAILED(rv) || (in == nullptr)) {
+ // Create the incoming server and an account for it?
+ rv = pMgr->CreateIncomingServer(EmptyCString(),
+ NS_ConvertUTF16toUTF8(serverName),
+ "nntp"_ns, 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",
+ static_cast<const wchar_t*>(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",
+ static_cast<const wchar_t*>(serverName.get()),
+ static_cast<const wchar_t*>(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,
+ mozilla::dom::Document* xmlDoc,
+ nsAutoString& inUserName,
+ int32_t authMethodIncoming, bool isNNTP) {
+ // Get the relevant information for an identity
+ nsAutoString value;
+
+ nsCOMPtr<nsIMsgIdentity> id;
+ 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", static_cast<const wchar_t*>(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", static_cast<const wchar_t*>(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(mozilla::dom::Document* 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("@mozilla.org/messengercompose/smtp;1", &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",
+ NS_ConvertUTF16toUTF8(smtpName).get());
+ } 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",
+ NS_ConvertUTF16toUTF8(smtpName).get());
+ }
+ }
+ }
+}
diff --git a/comm/mailnews/import/src/nsWMSettings.h b/comm/mailnews/import/src/nsWMSettings.h
new file mode 100644
index 0000000000..d1b770d0a1
--- /dev/null
+++ b/comm/mailnews/import/src/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/comm/mailnews/import/src/nsWMStringBundle.cpp b/comm/mailnews/import/src/nsWMStringBundle.cpp
new file mode 100644
index 0000000000..bd60597cbc
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMStringBundle.cpp
@@ -0,0 +1,52 @@
+/* -*- 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 "mozilla/Components.h"
+
+#define WM_MSGS_URL "chrome://messenger/locale/wmImportMsgs.properties"
+
+nsCOMPtr<nsIStringBundle> nsWMStringBundle::m_pBundle = nullptr;
+
+void nsWMStringBundle::GetStringBundle(void) {
+ if (m_pBundle) return;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::components::StringBundle::Service();
+ if (sBundleService) {
+ sBundleService->CreateBundle(WM_MSGS_URL, getter_AddRefs(m_pBundle));
+ }
+}
+
+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) GetStringBundle();
+
+ if (m_pBundle) {
+ nsAutoString str;
+ nsresult rv = m_pBundle->GetStringFromID(stringID, str);
+
+ if (NS_SUCCEEDED(rv)) return ToNewUnicode(str);
+ }
+
+ nsString resultString;
+ resultString.AppendLiteral("[StringID ");
+ resultString.AppendInt(stringID);
+ resultString.AppendLiteral("?]");
+
+ return ToNewUnicode(resultString);
+}
+
+void nsWMStringBundle::Cleanup(void) { m_pBundle = nullptr; }
diff --git a/comm/mailnews/import/src/nsWMStringBundle.h b/comm/mailnews/import/src/nsWMStringBundle.h
new file mode 100644
index 0000000000..45c92f75d6
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMStringBundle.h
@@ -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/. */
+
+#ifndef _nsWMStringBundle_H__
+#define _nsWMStringBundle_H__
+
+#include "nsString.h"
+
+class nsIStringBundle;
+
+class nsWMStringBundle {
+ public:
+ static char16_t* GetStringByID(int32_t stringID);
+ static void GetStringByID(int32_t stringID, nsString& result);
+ static void GetStringBundle(void);
+ static void FreeString(char16_t* pStr) { free(pStr); }
+ static void Cleanup(void);
+
+ private:
+ static nsCOMPtr<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/comm/mailnews/import/src/nsWMUtils.cpp b/comm/mailnews/import/src/nsWMUtils.cpp
new file mode 100644
index 0000000000..0172a30a82
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMUtils.cpp
@@ -0,0 +1,153 @@
+/* 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 "nsString.h"
+#include "mozilla/dom/Document.h"
+#include "nsWMUtils.h"
+#include "nsINodeList.h"
+#include "nsContentList.h"
+#include "nsINode.h"
+#include "nsIFileStreams.h"
+#include "nsIFile.h"
+#include "nsIDirectoryEnumerator.h"
+#include "ImportDebug.h"
+#include "prio.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/DOMParser.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,
+ u"Software\\Microsoft\\Windows Live Mail"_ns,
+ nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+ if (NS_SUCCEEDED(rv)) {
+ key.forget(aKey);
+ return rv;
+ }
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ u"Software\\Microsoft\\Windows Mail"_ns,
+ 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(u"Store Root"_ns, 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<nsIDirectoryEnumerator> 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<nsIFile> file;
+ rv = entries->GetNextFile(getter_AddRefs(file));
+ 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, u".oeaccount"_ns)) aFileArray.AppendObject(file);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsWMUtils::MakeXMLdoc(mozilla::dom::Document** 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);
+ mozilla::ErrorResult rv2;
+ RefPtr<mozilla::dom::DOMParser> parser =
+ mozilla::dom::DOMParser::CreateWithoutGlobal(rv2);
+ if (rv2.Failed()) {
+ return rv2.StealNSResult();
+ }
+ int64_t filesize;
+ aFile->GetFileSize(&filesize);
+ nsCOMPtr<mozilla::dom::Document> xmldoc = parser->ParseFromStream(
+ stream, EmptyString(), int32_t(filesize),
+ mozilla::dom::SupportedType::Application_xml, rv2);
+ xmldoc.forget(aXmlDoc);
+ return rv2.StealNSResult();
+}
+
+nsresult nsWMUtils::GetValueForTag(mozilla::dom::Document* aXmlDoc,
+ const char* aTagName, nsAString& aValue) {
+ nsAutoString tagName;
+ tagName.AssignASCII(aTagName);
+ nsCOMPtr<nsINodeList> list = aXmlDoc->GetElementsByTagName(tagName);
+ nsCOMPtr<nsINode> node = list->Item(0);
+ if (!node) return NS_ERROR_FAILURE;
+ mozilla::ErrorResult rv2;
+ node->GetTextContent(aValue, rv2);
+ return rv2.StealNSResult();
+}
diff --git a/comm/mailnews/import/src/nsWMUtils.h b/comm/mailnews/import/src/nsWMUtils.h
new file mode 100644
index 0000000000..02f15c6379
--- /dev/null
+++ b/comm/mailnews/import/src/nsWMUtils.h
@@ -0,0 +1,23 @@
+/* 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 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(mozilla::dom::Document** aXmlDoc, nsIFile* aFile);
+ static nsresult GetValueForTag(mozilla::dom::Document* aXmlDoc,
+ const char* aTagName, nsAString& aValue);
+};
+
+#endif /* nsWMUtils_h___ */
diff --git a/comm/mailnews/import/src/rtfDecoder.cpp b/comm/mailnews/import/src/rtfDecoder.cpp
new file mode 100644
index 0000000000..86a8151618
--- /dev/null
+++ b/comm/mailnews/import/src/rtfDecoder.cpp
@@ -0,0 +1,561 @@
+/* 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;
+
+ explicit 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
+ };
+ explicit Lexem(Type t = ltError) : m_type(t) {}
+ Lexem(Lexem& from) {
+ 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; // Move pointers when copying.
+ from.m_type = ltError; // Invalidate the original. Not nice.
+ 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; // Move pointers when copying.
+ from.m_type = ltError; // Invalidate the original. Not nice.
+ 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;
+}
+
+void 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;
+ }
+ }
+}
+
+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 = 0, ch1 = 0;
+ 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, lexem);
+ if (lexem.type() != Lexem::ltGroupBegin) return;
+ decoder.BeginGroup();
+ GetLexem(rtf, lexem);
+ 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
+ GetLexem(state.stream, lexem);
+ 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/comm/mailnews/import/src/rtfDecoder.h b/comm/mailnews/import/src/rtfDecoder.h
new file mode 100644
index 0000000000..1b547c77b8
--- /dev/null
+++ b/comm/mailnews/import/src/rtfDecoder.h
@@ -0,0 +1,21 @@
+/* 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/comm/mailnews/import/src/rtfMailDecoder.cpp b/comm/mailnews/import/src/rtfMailDecoder.cpp
new file mode 100644
index 0000000000..c5a234320e
--- /dev/null
+++ b/comm/mailnews/import/src/rtfMailDecoder.cpp
@@ -0,0 +1,71 @@
+/* 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/comm/mailnews/import/src/rtfMailDecoder.h b/comm/mailnews/import/src/rtfMailDecoder.h
new file mode 100644
index 0000000000..7f4063c5ae
--- /dev/null
+++ b/comm/mailnews/import/src/rtfMailDecoder.h
@@ -0,0 +1,44 @@
+/* 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));
+};