diff options
author | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
commit | 302bf1b523012e11b60425d6eee1221ebc2724eb (patch) | |
tree | b191a895f8716efcbe42f454f37597a545a6f421 /mailnews/import/src | |
parent | 21b3f6247403c06f85e1f45d219f87549862198f (diff) | |
download | UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip |
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/import/src')
30 files changed, 5958 insertions, 0 deletions
diff --git a/mailnews/import/src/ImportCharSet.cpp b/mailnews/import/src/ImportCharSet.cpp new file mode 100644 index 000000000..a8bc48d19 --- /dev/null +++ b/mailnews/import/src/ImportCharSet.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "ImportCharSet.h" + +char ImportCharSet::m_upperCaseMap[256]; +char ImportCharSet::m_Ascii[256] = {0}; // the initialiser makes it strong + +class UInitMaps { +public: + UInitMaps(); +}; + +UInitMaps gInitMaps; + +UInitMaps::UInitMaps() +{ + int i; + + for (i = 0; i < 256; i++) + ImportCharSet::m_upperCaseMap[i] = i; + for (i = 'a'; i <= 'z'; i++) + ImportCharSet::m_upperCaseMap[i] = i - 'a' + 'A'; + + for (i = 0; i < 256; i++) + ImportCharSet::m_Ascii[i] = 0; + + for (i = ImportCharSet::cUpperAChar; i <= ImportCharSet::cUpperZChar; i++) + ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cAlphaChar); + for (i = ImportCharSet::cLowerAChar; i <= ImportCharSet::cLowerZChar; i++) + ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cAlphaChar); + for (i = ImportCharSet::cZeroChar; i <= ImportCharSet::cNineChar; i++) + ImportCharSet::m_Ascii[i] |= (ImportCharSet::cAlphaNumChar | ImportCharSet::cDigitChar); + + ImportCharSet::m_Ascii[ImportCharSet::cTabChar] |= ImportCharSet::cWhiteSpaceChar; + ImportCharSet::m_Ascii[ImportCharSet::cCRChar] |= ImportCharSet::cWhiteSpaceChar; + ImportCharSet::m_Ascii[ImportCharSet::cLinefeedChar] |= ImportCharSet::cWhiteSpaceChar; + ImportCharSet::m_Ascii[ImportCharSet::cSpaceChar] |= ImportCharSet::cWhiteSpaceChar; + + ImportCharSet::m_Ascii[uint8_t('(')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t(')')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('<')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('>')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('@')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t(',')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t(';')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t(':')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('\\')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('"')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('.')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t('[')] |= ImportCharSet::c822SpecialChar; + ImportCharSet::m_Ascii[uint8_t(']')] |= ImportCharSet::c822SpecialChar; + + +} diff --git a/mailnews/import/src/ImportCharSet.h b/mailnews/import/src/ImportCharSet.h new file mode 100644 index 000000000..c1f649ef2 --- /dev/null +++ b/mailnews/import/src/ImportCharSet.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ImportCharSet_h___ +#define ImportCharSet_h___ + +#include "nscore.h" + + +// Some useful ASCII values +// 'A' = 65, 0x41 +// 'Z' = 90, 0x5a +// '_' = 95, 0x5f +// 'a' = 97, 0x61 +// 'z' = 122, 0x7a +// '0' = 48, 0x30 +// '1' = 49, 0x31 +// '9' = 57, 0x39 +// ' ' = 32, 0x20 +// whitespace, 10, 13, 32, 9 (linefeed, cr, space, tab) - 0x0a, 0x0d, 0x20, 0x09 +// ':' = 58, 0x3a + + +// a typedef enum would be nicer but some compilers still have trouble with treating +// enum's as plain numbers when needed + +class ImportCharSet { +public: + enum { + cTabChar = 9, + cLinefeedChar = 10, + cCRChar = 13, + cSpaceChar = 32, + cUpperAChar = 65, + cUpperZChar = 90, + cUnderscoreChar = 95, + cLowerAChar = 97, + cLowerZChar = 122, + cZeroChar = 48, + cNineChar = 57, + + cAlphaNumChar = 1, + cAlphaChar = 2, + cWhiteSpaceChar = 4, + cDigitChar = 8, + c822SpecialChar = 16 + }; + + static char m_upperCaseMap[256]; + static char m_Ascii[256]; + + inline static bool IsUSAscii(uint8_t ch) { return (((ch & (uint8_t)0x80) == 0));} + inline static bool Is822CtlChar(uint8_t ch) { return (ch < 32);} + inline static bool Is822SpecialChar(uint8_t ch) { return ((m_Ascii[ch] & c822SpecialChar) == c822SpecialChar);} + inline static bool IsWhiteSpace(uint8_t ch) { return ((m_Ascii[ch] & cWhiteSpaceChar) == cWhiteSpaceChar); } + inline static bool IsAlphaNum(uint8_t ch) { return ((m_Ascii[ch] & cAlphaNumChar) == cAlphaNumChar); } + inline static bool IsDigit(uint8_t ch) { return ((m_Ascii[ch] & cDigitChar) == cDigitChar); } + + inline static uint8_t ToLower(uint8_t ch) { if ((m_Ascii[ch] & cAlphaChar) == cAlphaChar) { return cLowerAChar + (m_upperCaseMap[ch] - cUpperAChar); } else return ch; } + + inline static long AsciiToLong(const uint8_t * pChar, uint32_t len) { + long num = 0; + while (len) { + if ((m_Ascii[*pChar] & cDigitChar) == 0) + return num; + num *= 10; + num += (*pChar - cZeroChar); + len--; + pChar++; + } + return num; + } + + inline static void ByteToHex(uint8_t byte, uint8_t * pHex) { + uint8_t val = byte; + val /= 16; + if (val < 10) + *pHex = '0' + val; + else + *pHex = 'A' + (val - 10); + pHex++; + val = byte; + val &= 0x0F; + if (val < 10) + *pHex = '0' + val; + else + *pHex = 'A' + (val - 10); + } + + inline static void LongToHexBytes(uint32_t type, uint8_t * pStr) { + ByteToHex((uint8_t) (type >> 24), pStr); + pStr += 2; + ByteToHex((uint8_t) ((type >> 16) & 0x0FF), pStr); + pStr += 2; + ByteToHex((uint8_t) ((type >> 8) & 0x0FF), pStr); + pStr += 2; + ByteToHex((uint8_t) (type & 0x0FF), pStr); + } + + inline static void SkipWhiteSpace(const uint8_t * & pChar, uint32_t & pos, uint32_t max) { + while ((pos < max) && (IsWhiteSpace(*pChar))) { + pos++; pChar++; + } + } + + inline static void SkipSpaceTab(const uint8_t * & pChar, uint32_t& pos, uint32_t max) { + while ((pos < max) && ((*pChar == (uint8_t)cSpaceChar) || (*pChar == (uint8_t)cTabChar))) { + pos++; pChar++; + } + } + + inline static void SkipTilSpaceTab(const uint8_t * & pChar, uint32_t& pos, uint32_t max) { + while ((pos < max) && (*pChar != (uint8_t)cSpaceChar) && (*pChar != (uint8_t)cTabChar)) { + pos++; + pChar++; + } + } + + inline static bool StrNICmp(const uint8_t * pChar, const uint8_t * pSrc, uint32_t len) { + while (len && (m_upperCaseMap[*pChar] == m_upperCaseMap[*pSrc])) { + pChar++; pSrc++; len--; + } + return len == 0; + } + + inline static bool StrNCmp(const uint8_t * pChar, const uint8_t *pSrc, uint32_t len) { + while (len && (*pChar == *pSrc)) { + pChar++; pSrc++; len--; + } + return len == 0; + } + + inline static int FindChar(const uint8_t * pChar, uint8_t ch, uint32_t max) { + uint32_t pos = 0; + while ((pos < max) && (*pChar != ch)) { + pos++; pChar++; + } + if (pos < max) + return (int) pos; + else + return -1; + } + + inline static bool NextChar(const uint8_t * & pChar, uint8_t ch, uint32_t& pos, uint32_t max) { + if ((pos < max) && (*pChar == ch)) { + pos++; + pChar++; + return true; + } + return false; + } + + inline static int32_t strcmp(const char * pS1, const char * pS2) { + while (*pS1 && *pS2 && (*pS1 == *pS2)) { + pS1++; + pS2++; + } + return *pS1 - *pS2; + } + + inline static int32_t stricmp(const char * pS1, const char * pS2) { + while (*pS1 && *pS2 && (m_upperCaseMap[uint8_t(*pS1)] == m_upperCaseMap[uint8_t(*pS2)])) { + pS1++; + pS2++; + } + return m_upperCaseMap[uint8_t(*pS1)] - m_upperCaseMap[uint8_t(*pS2)]; + } + +}; + + +#endif /* ImportCharSet_h__ */ + diff --git a/mailnews/import/src/ImportDebug.h b/mailnews/import/src/ImportDebug.h new file mode 100644 index 000000000..b905aa4a9 --- /dev/null +++ b/mailnews/import/src/ImportDebug.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ImportDebug_h___ +#define ImportDebug_h___ + +#ifdef NS_DEBUG +#define IMPORT_DEBUG 1 +#endif + +// Use MOZ_LOG for logging. +#include "mozilla/Logging.h" +extern PRLogModuleInfo *IMPORTLOGMODULE; // Logging module + +#define IMPORT_LOG0(x) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x)) +#define IMPORT_LOG1(x, y) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y)) +#define IMPORT_LOG2(x, y, z) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y, z)) +#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(IMPORTLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d)) + +#endif diff --git a/mailnews/import/src/ImportOutFile.cpp b/mailnews/import/src/ImportOutFile.cpp new file mode 100644 index 000000000..beeb8903a --- /dev/null +++ b/mailnews/import/src/ImportOutFile.cpp @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsStringGlue.h" +#include "prio.h" +#include "nsNetUtil.h" +#include "nsISeekableStream.h" +#include "nsMsgUtils.h" +#include "ImportOutFile.h" +#include "ImportCharSet.h" + +#include "ImportDebug.h" + +/* +#ifdef _MAC +#define kMacNoCreator '????' +#define kMacTextFile 'TEXT' +#else +#define kMacNoCreator 0 +#define kMacTextFile 0 +#endif +*/ + +ImportOutFile::ImportOutFile() +{ + m_ownsFileAndBuffer = false; + m_pos = 0; + m_pBuf = nullptr; + m_bufSz = 0; + m_pTrans = nullptr; + m_pTransOut = nullptr; + m_pTransBuf = nullptr; +} + +ImportOutFile::ImportOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz) +{ + m_pTransBuf = nullptr; + m_pTransOut = nullptr; + m_pTrans = nullptr; + m_ownsFileAndBuffer = false; + InitOutFile(pFile, pBuf, sz); +} + +ImportOutFile::~ImportOutFile() +{ + if (m_ownsFileAndBuffer) + { + Flush(); + delete [] m_pBuf; + } + + delete m_pTrans; + delete m_pTransOut; + delete [] m_pTransBuf; +} + +bool ImportOutFile::Set8bitTranslator(nsImportTranslator *pTrans) +{ + if (!Flush()) + return false; + + m_engaged = false; + m_pTrans = pTrans; + m_supports8to7 = pTrans->Supports8bitEncoding(); + + + return true; +} + +bool ImportOutFile::End8bitTranslation(bool *pEngaged, nsCString& useCharset, nsCString& encoding) +{ + if (!m_pTrans) + return false; + + + bool bResult = Flush(); + if (m_supports8to7 && m_pTransOut) { + if (bResult) + bResult = m_pTrans->FinishConvertToFile(m_pTransOut); + if (bResult) + bResult = Flush(); + } + + if (m_supports8to7) { + m_pTrans->GetCharset(useCharset); + m_pTrans->GetEncoding(encoding); + } + else + useCharset.Truncate(); + *pEngaged = m_engaged; + delete m_pTrans; + m_pTrans = nullptr; + delete m_pTransOut; + m_pTransOut = nullptr; + delete [] m_pTransBuf; + m_pTransBuf = nullptr; + + return bResult; +} + +bool ImportOutFile::InitOutFile(nsIFile *pFile, uint32_t bufSz) +{ + if (!bufSz) + bufSz = 32 * 1024; + if (!m_pBuf) + m_pBuf = new uint8_t[ bufSz]; + + if (!m_outputStream) + { + nsresult rv; + rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_outputStream), + pFile, + PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, + 0644); + + if (NS_FAILED(rv)) + { + IMPORT_LOG0("Couldn't create outfile\n"); + delete [] m_pBuf; + m_pBuf = nullptr; + return false; + } + } + m_pFile = pFile; + m_ownsFileAndBuffer = true; + m_pos = 0; + m_bufSz = bufSz; + return true; +} + +void ImportOutFile::InitOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz) +{ + m_ownsFileAndBuffer = false; + m_pFile = pFile; + m_pBuf = pBuf; + m_bufSz = sz; + m_pos = 0; +} + + + +bool ImportOutFile::Flush(void) +{ + if (!m_pos) + return true; + + uint32_t transLen; + bool duddleyDoWrite = false; + + // handle translations if appropriate + if (m_pTrans) { + if (m_engaged && m_supports8to7) { + // Markers can get confused by this crap!!! + // TLR: FIXME: Need to update the markers based on + // the difference between the translated len and untranslated len + + if (!m_pTrans->ConvertToFile( m_pBuf, m_pos, m_pTransOut, &transLen)) + return false; + if (!m_pTransOut->Flush()) + return false; + // now update our buffer... + if (transLen < m_pos) { + memcpy(m_pBuf, m_pBuf + transLen, m_pos - transLen); + } + m_pos -= transLen; + } + else if (m_engaged) { + // does not actually support translation! + duddleyDoWrite = true; + } + else { + // should we engage? + uint8_t * pChar = m_pBuf; + uint32_t len = m_pos; + while (len) { + if (!ImportCharSet::IsUSAscii(*pChar)) + break; + pChar++; + len--; + } + if (len) { + m_engaged = true; + if (m_supports8to7) { + // allocate our translation output buffer and file... + m_pTransBuf = new uint8_t[m_bufSz]; + m_pTransOut = new ImportOutFile(m_pFile, m_pTransBuf, m_bufSz); + return Flush(); + } + else + duddleyDoWrite = true; + } + else { + duddleyDoWrite = true; + } + } + } + else + duddleyDoWrite = true; + + if (duddleyDoWrite) { + uint32_t written = 0; + nsresult rv = m_outputStream->Write((const char *)m_pBuf, (int32_t)m_pos, &written); + if (NS_FAILED(rv) || ((uint32_t)written != m_pos)) + return false; + m_pos = 0; + } + + return true; +} + +bool ImportOutFile::WriteU8NullTerm(const uint8_t * pSrc, bool includeNull) +{ + while (*pSrc) { + if (m_pos >= m_bufSz) { + if (!Flush()) + return false; + } + *(m_pBuf + m_pos) = *pSrc; + m_pos++; + pSrc++; + } + if (includeNull) { + if (m_pos >= m_bufSz) { + if (!Flush()) + return false; + } + *(m_pBuf + m_pos) = 0; + m_pos++; + } + + return true; +} + +bool ImportOutFile::SetMarker(int markerID) +{ + if (!Flush()) { + return false; + } + + if (markerID < kMaxMarkers) { + int64_t pos = 0; + if (m_outputStream) + { + // do we need to flush for the seek to give us the right pos? + m_outputStream->Flush(); + nsresult rv; + nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(m_outputStream, &rv); + NS_ENSURE_SUCCESS(rv, false); + rv = seekStream->Tell(&pos); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error, Tell failed on output stream\n"); + return false; + } + } + m_markers[markerID] = (uint32_t)pos + m_pos; + } + + return true; +} + +void ImportOutFile::ClearMarker(int markerID) +{ + if (markerID < kMaxMarkers) + m_markers[markerID] = 0; +} + +bool ImportOutFile::WriteStrAtMarker(int markerID, const char *pStr) +{ + if (markerID >= kMaxMarkers) + return false; + + if (!Flush()) + return false; + int64_t pos; + m_outputStream->Flush(); + nsresult rv; + nsCOMPtr <nsISeekableStream> seekStream = do_QueryInterface(m_outputStream, &rv); + NS_ENSURE_SUCCESS(rv, false); + rv = seekStream->Tell(&pos); + if (NS_FAILED(rv)) + return false; + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, (int32_t) m_markers[markerID]); + if (NS_FAILED(rv)) + return false; + uint32_t written; + rv = m_outputStream->Write(pStr, strlen(pStr), &written); + if (NS_FAILED(rv)) + return false; + + rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, pos); + if (NS_FAILED(rv)) + return false; + + return true; +} + diff --git a/mailnews/import/src/ImportOutFile.h b/mailnews/import/src/ImportOutFile.h new file mode 100644 index 000000000..461728c22 --- /dev/null +++ b/mailnews/import/src/ImportOutFile.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef ImportOutFile_h___ +#define ImportOutFile_h___ + +#include "nsImportTranslator.h" +#include "nsIOutputStream.h" +#include "nsIFile.h" + +#define kMaxMarkers 10 + +class ImportOutFile; + +class ImportOutFile { +public: + ImportOutFile(); + ImportOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz); + ~ImportOutFile(); + + bool InitOutFile(nsIFile *pFile, uint32_t bufSz = 4096); + void InitOutFile(nsIFile *pFile, uint8_t * pBuf, uint32_t sz); + inline bool WriteData(const uint8_t * pSrc, uint32_t len); + inline bool WriteByte(uint8_t byte); + bool WriteStr(const char *pStr) {return WriteU8NullTerm((const uint8_t *) pStr, false); } + bool WriteU8NullTerm(const uint8_t * pSrc, bool includeNull); + bool WriteEol(void) { return WriteStr("\x0D\x0A"); } + bool Done(void) {return Flush();} + + // Marker support + bool SetMarker(int markerID); + void ClearMarker(int markerID); + bool WriteStrAtMarker(int markerID, const char *pStr); + + // 8-bit to 7-bit translation + bool Set8bitTranslator(nsImportTranslator *pTrans); + bool End8bitTranslation(bool *pEngaged, nsCString& useCharset, nsCString& encoding); + +protected: + bool Flush(void); + +protected: + nsCOMPtr <nsIFile> m_pFile; + nsCOMPtr <nsIOutputStream> m_outputStream; + uint8_t * m_pBuf; + uint32_t m_bufSz; + uint32_t m_pos; + bool m_ownsFileAndBuffer; + + // markers + uint32_t m_markers[kMaxMarkers]; + + // 8 bit to 7 bit translations + nsImportTranslator * m_pTrans; + bool m_engaged; + bool m_supports8to7; + ImportOutFile * m_pTransOut; + uint8_t * m_pTransBuf; +}; + +inline bool ImportOutFile::WriteData(const uint8_t * pSrc, uint32_t len) { + while ((len + m_pos) > m_bufSz) { + if ((m_bufSz - m_pos)) { + memcpy(m_pBuf + m_pos, pSrc, m_bufSz - m_pos); + len -= (m_bufSz - m_pos); + pSrc += (m_bufSz - m_pos); + m_pos = m_bufSz; + } + if (!Flush()) + return false; + } + + if (len) { + memcpy(m_pBuf + m_pos, pSrc, len); + m_pos += len; + } + + return true; +} + +inline bool ImportOutFile::WriteByte(uint8_t byte) { + if (m_pos == m_bufSz) { + if (!Flush()) + return false; + } + *(m_pBuf + m_pos) = byte; + m_pos++; + return true; +} + +#endif /* ImportOutFile_h__ */ + + diff --git a/mailnews/import/src/ImportTranslate.cpp b/mailnews/import/src/ImportTranslate.cpp new file mode 100644 index 000000000..f7f32f552 --- /dev/null +++ b/mailnews/import/src/ImportTranslate.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ImportTranslate.h" + +int ImportTranslate::m_useTranslator = -1; + + +bool ImportTranslate::ConvertString(const nsCString& inStr, nsCString& outStr, bool mimeHeader) +{ + if (inStr.IsEmpty()) { + outStr = inStr; + return true; + } + + nsImportTranslator *pTrans = GetTranslator(); + // int maxLen = (int) pTrans->GetMaxBufferSize(inStr.Length()); + // int hLen = 0; + nsCString set; + nsCString lang; + + if (mimeHeader) { + // add the charset and language + pTrans->GetCharset(set); + pTrans->GetLanguage(lang); + } + + // Unfortunatly, we didn't implement ConvertBuffer for all translators, + // just ConvertToFile. This means that this data will not always + // be converted to the charset of pTrans. In that case... + // We don't always have the data in the same charset as the current + // translator... + // It is safer to leave the charset and language field blank + set.Truncate(); + lang.Truncate(); + + uint8_t * pBuf; + /* + pBuf = (P_U8) outStr.GetBuffer(maxLen); + if (!pBuf) { + delete pTrans; + return FALSE; + } + pTrans->ConvertBuffer((PC_U8)(PC_S8)inStr, inStr.GetLength(), pBuf); + outStr.ReleaseBuffer(); + */ + outStr = inStr; + delete pTrans; + + + // Now I need to run the string through the mime-header special char + // encoder. + + pTrans = new CMHTranslator; + pBuf = new uint8_t[pTrans->GetMaxBufferSize(outStr.Length())]; + pTrans->ConvertBuffer((const uint8_t *)(outStr.get()), outStr.Length(), pBuf); + delete pTrans; + outStr.Truncate(); + if (mimeHeader) { + outStr = set; + outStr += "'"; + outStr += lang; + outStr += "'"; + } + outStr += (const char *)pBuf; + delete [] pBuf; + + return true; +} + + +nsImportTranslator *ImportTranslate::GetTranslator(void) +{ + if (m_useTranslator == -1) { + // get the translator to use... + // CString trans; + // trans.LoadString(IDS_LANGUAGE_TRANSLATION); + m_useTranslator = 0; + // if (!trans.CompareNoCase("iso-2022-jp")) + // gWizData.m_useTranslator = 1; + } + + switch(m_useTranslator) { + case 0: + return new nsImportTranslator; + //case 1: + // return new CSJis2JisTranslator; + default: + return new nsImportTranslator; + } +} + +nsImportTranslator *ImportTranslate::GetMatchingTranslator(const char *pCharSet) +{ +/* + CString jp = "iso-2022-jp"; + if (!jp.CompareNoCase(pCharSet)) + return new CSJis2JisTranslator; +*/ + + return nullptr; +} + diff --git a/mailnews/import/src/ImportTranslate.h b/mailnews/import/src/ImportTranslate.h new file mode 100644 index 000000000..3e6c596d4 --- /dev/null +++ b/mailnews/import/src/ImportTranslate.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ImportTranslate_h___ +#define ImportTranslate_h___ + +#include "nsStringGlue.h" +#include "nsImportTranslator.h" + +class ImportTranslate { +public: + static bool ConvertString(const nsCString& inStr, nsCString& outStr, bool mimeHeader); + static nsImportTranslator *GetTranslator(void); + static nsImportTranslator *GetMatchingTranslator(const char *pCharSet); + +protected: + static int m_useTranslator; +}; + + +#endif /* ImportTranslate_h__ */ diff --git a/mailnews/import/src/moz.build b/mailnews/import/src/moz.build new file mode 100644 index 000000000..2df4926d5 --- /dev/null +++ b/mailnews/import/src/moz.build @@ -0,0 +1,25 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'ImportCharSet.cpp', + 'ImportOutFile.cpp', + 'ImportTranslate.cpp', + 'nsImportABDescriptor.cpp', + 'nsImportAddressBooks.cpp', + 'nsImportEmbeddedImageData.cpp', + 'nsImportEncodeScan.cpp', + 'nsImportFieldMap.cpp', + 'nsImportMail.cpp', + 'nsImportMailboxDescriptor.cpp', + 'nsImportMimeEncode.cpp', + 'nsImportScanFile.cpp', + 'nsImportService.cpp', + 'nsImportStringBundle.cpp', + 'nsImportTranslator.cpp', +] + +FINAL_LIBRARY = 'import' + diff --git a/mailnews/import/src/nsImportABDescriptor.cpp b/mailnews/import/src/nsImportABDescriptor.cpp new file mode 100644 index 000000000..05605d09e --- /dev/null +++ b/mailnews/import/src/nsImportABDescriptor.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nscore.h" +#include "nsImportABDescriptor.h" + +//////////////////////////////////////////////////////////////////////// + +NS_METHOD nsImportABDescriptor::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsImportABDescriptor *it = new nsImportABDescriptor(); + if (it == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(it); + nsresult rv = it->QueryInterface(aIID, aResult); + NS_RELEASE(it); + return rv; +} + +NS_IMPL_ISUPPORTS(nsImportABDescriptor, nsIImportABDescriptor) + +nsImportABDescriptor::nsImportABDescriptor() + : mId(0), mRef(0), mSize(0), mImport(true) +{ +} diff --git a/mailnews/import/src/nsImportABDescriptor.h b/mailnews/import/src/nsImportABDescriptor.h new file mode 100644 index 000000000..a8fa6fbad --- /dev/null +++ b/mailnews/import/src/nsImportABDescriptor.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportABDescriptor_h___ +#define nsImportABDescriptor_h___ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsIImportABDescriptor.h" +#include "nsIFile.h" +#include "nsCOMPtr.h" + +//////////////////////////////////////////////////////////////////////// + +class nsImportABDescriptor : public nsIImportABDescriptor +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_IMETHOD GetIdentifier(uint32_t *pIdentifier) override { + *pIdentifier = mId; + return NS_OK; + } + NS_IMETHOD SetIdentifier(uint32_t ident) override { + mId = ident; + return NS_OK; + } + + NS_IMETHOD GetRef(uint32_t *pRef) override { + *pRef = mRef; + return NS_OK; + } + NS_IMETHOD SetRef(uint32_t ref) override { + mRef = ref; + return NS_OK; + } + + /* attribute unsigned long size; */ + NS_IMETHOD GetSize(uint32_t *pSize) override { + *pSize = mSize; + return NS_OK; + } + NS_IMETHOD SetSize(uint32_t theSize) override { + mSize = theSize; + return NS_OK; + } + + /* attribute AString displayName; */ + NS_IMETHOD GetPreferredName(nsAString &aName) override { + aName = mDisplayName; + return NS_OK; + } + NS_IMETHOD SetPreferredName(const nsAString &aName) override { + mDisplayName = aName; + return NS_OK; + } + + /* readonly attribute nsIFile fileSpec; */ + NS_IMETHOD GetAbFile(nsIFile **aFile) override { + if (!mFile) + return NS_ERROR_NULL_POINTER; + + return mFile->Clone(aFile); + } + + NS_IMETHOD SetAbFile(nsIFile *aFile) override { + if (!aFile) { + mFile = nullptr; + return NS_OK; + } + + return aFile->Clone(getter_AddRefs(mFile)); + } + + /* attribute boolean import; */ + NS_IMETHOD GetImport(bool *pImport) override { + *pImport = mImport; + return NS_OK; + } + NS_IMETHOD SetImport(bool doImport) override { + mImport = doImport; + return NS_OK; + } + + nsImportABDescriptor(); + + static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + +private: + virtual ~nsImportABDescriptor() {} + uint32_t mId; // used by creator of the structure + uint32_t mRef; // depth in the hierarchy + nsString mDisplayName; // name of this mailbox + nsCOMPtr<nsIFile> mFile; // source file (if applicable) + uint32_t mSize; // size + bool mImport; // import it or not? +}; + + +#endif diff --git a/mailnews/import/src/nsImportAddressBooks.cpp b/mailnews/import/src/nsImportAddressBooks.cpp new file mode 100644 index 000000000..8791efb42 --- /dev/null +++ b/mailnews/import/src/nsImportAddressBooks.cpp @@ -0,0 +1,894 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prprf.h" +#include "plstr.h" +#include "nsCOMPtr.h" +#include "nsMsgUtils.h" +#include "nsIImportService.h" +#include "nsIImportAddressBooks.h" +#include "nsIImportGeneric.h" +#include "nsISupportsPrimitives.h" +#include "nsIImportABDescriptor.h" +#include "nsIImportFieldMap.h" +#include "nsStringGlue.h" +#include "nsIFile.h" +#include "nsIAddrDatabase.h" +#include "nsIAbManager.h" +#include "nsIAbLDIFService.h" +#include "nsAbBaseCID.h" +#include "nsIStringBundle.h" +#include "nsImportStringBundle.h" +#include "nsTextFormatter.h" +#include "nsServiceManagerUtils.h" +#include "msgCore.h" +#include "ImportDebug.h" +#include "nsIAbMDBDirectory.h" +#include "nsComponentManagerUtils.h" +#include "nsIArray.h" +#include "nsCOMArray.h" +#include "nsArrayUtils.h" + +static void ImportAddressThread(void *stuff); + +class AddressThreadData; + +class nsImportGenericAddressBooks : public nsIImportGeneric +{ +public: + + nsImportGenericAddressBooks(); + + NS_DECL_THREADSAFE_ISUPPORTS + + /* nsISupports GetData (in string dataId); */ + NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override; + + NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override; + + NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override; + + NS_IMETHOD WantsProgress(bool *_retval) override; + + NS_IMETHOD BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override; + + NS_IMETHOD ContinueImport(bool *_retval) override; + + NS_IMETHOD GetProgress(int32_t *_retval) override; + + NS_IMETHOD CancelImport(void) override; + +private: + virtual ~nsImportGenericAddressBooks(); + void GetDefaultLocation(void); + void GetDefaultBooks(void); + void GetDefaultFieldMap(void); + +public: + static void SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError); + static void ReportError(const char16_t *pName, nsString *pStream, + nsIStringBundle *aBundle); + +private: + nsIImportAddressBooks * m_pInterface; + nsCOMPtr<nsIArray> m_Books; + nsCOMArray<nsIAddrDatabase> m_DBs; + nsCOMPtr <nsIFile> m_pLocation; + nsIImportFieldMap * m_pFieldMap; + bool m_autoFind; + char16_t * m_description; + bool m_gotLocation; + bool m_found; + bool m_userVerify; + nsISupportsString * m_pSuccessLog; + nsISupportsString * m_pErrorLog; + uint32_t m_totalSize; + bool m_doImport; + AddressThreadData * m_pThreadData; + char * m_pDestinationUri; + nsCOMPtr<nsIStringBundle> m_stringBundle; +}; + +class AddressThreadData { +public: + bool driverAlive; + bool threadAlive; + bool abort; + bool fatalError; + uint32_t currentTotal; + uint32_t currentSize; + nsIArray *books; + nsCOMArray<nsIAddrDatabase>* dBs; + nsCOMPtr<nsIAbLDIFService> ldifService; + nsIImportAddressBooks * addressImport; + nsIImportFieldMap * fieldMap; + nsISupportsString * successLog; + nsISupportsString * errorLog; + char * pDestinationUri; + nsIStringBundle* stringBundle; + + AddressThreadData(); + ~AddressThreadData(); +}; + + +nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric) +{ + NS_PRECONDITION(aImportGeneric != nullptr, "null ptr"); + if (! aImportGeneric) + return NS_ERROR_NULL_POINTER; + + nsImportGenericAddressBooks *pGen = new nsImportGenericAddressBooks(); + + if (pGen == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(pGen); + nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric); + NS_RELEASE(pGen); + + return rv; +} + +nsImportGenericAddressBooks::nsImportGenericAddressBooks() +{ + m_pInterface = nullptr; + m_pSuccessLog = nullptr; + m_pErrorLog = nullptr; + m_totalSize = 0; + m_doImport = false; + m_pThreadData = nullptr; + m_pDestinationUri = nullptr; + m_pFieldMap = nullptr; + + m_autoFind = false; + m_description = nullptr; + m_gotLocation = false; + m_found = false; + m_userVerify = false; + + nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle)); +} + + +nsImportGenericAddressBooks::~nsImportGenericAddressBooks() +{ + if (m_pDestinationUri) + NS_Free(m_pDestinationUri); + + if (m_description) + NS_Free(m_description); + + NS_IF_RELEASE(m_pFieldMap); + NS_IF_RELEASE(m_pInterface); + NS_IF_RELEASE(m_pSuccessLog); + NS_IF_RELEASE(m_pErrorLog); +} + + + +NS_IMPL_ISUPPORTS(nsImportGenericAddressBooks, nsIImportGeneric) + + +NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char *dataId, nsISupports **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + *_retval = nullptr; + if (!PL_strcasecmp(dataId, "addressInterface")) { + *_retval = m_pInterface; + NS_IF_ADDREF(m_pInterface); + } + + if (!PL_strcasecmp(dataId, "addressLocation")) { + if (!m_pLocation) + GetDefaultLocation(); + NS_IF_ADDREF(*_retval = m_pLocation); + } + + if (!PL_strcasecmp(dataId, "addressBooks")) { + if (!m_pLocation) + GetDefaultLocation(); + if (!m_Books) + GetDefaultBooks(); + *_retval = m_Books; + } + + if (!PL_strcasecmp(dataId, "addressDestination")) { + if (m_pDestinationUri) { + nsCOMPtr<nsISupportsCString> abString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + abString->SetData(nsDependentCString(m_pDestinationUri)); + NS_IF_ADDREF(*_retval = abString); + } + } + + if (!PL_strcasecmp(dataId, "fieldMap")) { + if (m_pFieldMap) { + *_retval = m_pFieldMap; + m_pFieldMap->AddRef(); + } + else { + if (m_pInterface && m_pLocation) { + bool needsIt = false; + m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt); + if (needsIt) { + GetDefaultFieldMap(); + if (m_pFieldMap) { + *_retval = m_pFieldMap; + m_pFieldMap->AddRef(); + } + } + } + } + } + + if (!PL_strncasecmp(dataId, "sampleData-", 11)) { + // extra the record number + const char *pNum = dataId + 11; + int32_t rNum = 0; + while (*pNum) { + rNum *= 10; + rNum += (*pNum - '0'); + pNum++; + } + IMPORT_LOG1("Requesting sample data #: %ld\n", (long)rNum); + if (m_pInterface) { + nsCOMPtr<nsISupportsString> data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + char16_t * pData = nullptr; + bool found = false; + rv = m_pInterface->GetSampleData(rNum, &found, &pData); + if (NS_FAILED(rv)) + return rv; + if (found) { + data->SetData(nsDependentString(pData)); + *_retval = data; + NS_ADDREF(*_retval); + } + NS_Free(pData); + } + } + + return NS_OK; +} + + +NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char *dataId, nsISupports *item) +{ + NS_PRECONDITION(dataId != nullptr, "null ptr"); + if (!dataId) + return NS_ERROR_NULL_POINTER; + + if (!PL_strcasecmp(dataId, "addressInterface")) { + NS_IF_RELEASE(m_pInterface); + if (item) + item->QueryInterface(NS_GET_IID(nsIImportAddressBooks), (void **) &m_pInterface); + } + if (!PL_strcasecmp(dataId, "addressBooks")) { + if (item) + item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_Books); + } + + if (!PL_strcasecmp(dataId, "addressLocation")) { + m_pLocation = nullptr; + + if (item) { + nsresult rv; + m_pLocation = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv,rv); + } + + if (m_pInterface) + m_pInterface->SetSampleLocation(m_pLocation); + } + + if (!PL_strcasecmp(dataId, "addressDestination")) { + if (item) { + nsCOMPtr<nsISupportsCString> abString; + item->QueryInterface(NS_GET_IID(nsISupportsCString), getter_AddRefs(abString)); + if (abString) { + if (m_pDestinationUri) + NS_Free(m_pDestinationUri); + m_pDestinationUri = nullptr; + nsAutoCString tempUri; + abString->GetData(tempUri); + m_pDestinationUri = ToNewCString(tempUri); + } + } + } + + if (!PL_strcasecmp(dataId, "fieldMap")) { + NS_IF_RELEASE(m_pFieldMap); + if (item) + item->QueryInterface(NS_GET_IID(nsIImportFieldMap), (void **) &m_pFieldMap); + } + + return NS_OK; +} + +NS_IMETHODIMP nsImportGenericAddressBooks::GetStatus(const char *statusKind, int32_t *_retval) +{ + NS_PRECONDITION(statusKind != nullptr, "null ptr"); + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!statusKind || !_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = 0; + + if (!PL_strcasecmp(statusKind, "isInstalled")) { + GetDefaultLocation(); + *_retval = (int32_t) m_found; + } + + if (!PL_strcasecmp(statusKind, "canUserSetLocation")) { + GetDefaultLocation(); + *_retval = (int32_t) m_userVerify; + } + + if (!PL_strcasecmp(statusKind, "autoFind")) { + GetDefaultLocation(); + *_retval = (int32_t) m_autoFind; + } + + if (!PL_strcasecmp(statusKind, "supportsMultiple")) { + bool multi = false; + if (m_pInterface) + m_pInterface->GetSupportsMultiple(&multi); + *_retval = (int32_t) multi; + } + + if (!PL_strcasecmp(statusKind, "needsFieldMap")) { + bool needs = false; + if (m_pInterface && m_pLocation) + m_pInterface->GetNeedsFieldMap(m_pLocation, &needs); + *_retval = (int32_t) needs; + } + + return NS_OK; +} + +void nsImportGenericAddressBooks::GetDefaultLocation(void) +{ + if (!m_pInterface) + return; + + if ((m_pLocation && m_gotLocation) || m_autoFind) + return; + + if (m_description) + NS_Free(m_description); + m_description = nullptr; + m_pInterface->GetAutoFind(&m_description, &m_autoFind); + m_gotLocation = true; + if (m_autoFind) { + m_found = true; + m_userVerify = false; + return; + } + + nsCOMPtr <nsIFile> pLoc; + m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify); + if (!m_pLocation) + m_pLocation = pLoc; +} + +void nsImportGenericAddressBooks::GetDefaultBooks(void) +{ + if (!m_pInterface || m_Books) + return; + + if (!m_pLocation && !m_autoFind) + return; + + nsresult rv = m_pInterface->FindAddressBooks(m_pLocation, getter_AddRefs(m_Books)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error: FindAddressBooks failed\n"); + } +} + +void nsImportGenericAddressBooks::GetDefaultFieldMap(void) +{ + if (!m_pInterface || !m_pLocation) + return; + + NS_IF_RELEASE(m_pFieldMap); + + nsresult rv; + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Unable to get nsIImportService.\n"); + return; + } + + rv = impSvc->CreateNewFieldMap(&m_pFieldMap); + if (NS_FAILED(rv)) + return; + + int32_t sz = 0; + rv = m_pFieldMap->GetNumMozFields(&sz); + if (NS_SUCCEEDED(rv)) + rv = m_pFieldMap->DefaultFieldMap(sz); + if (NS_SUCCEEDED(rv)) + rv = m_pInterface->InitFieldMap(m_pFieldMap); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error: Unable to initialize field map\n"); + NS_IF_RELEASE(m_pFieldMap); + } +} + + +NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + NS_ENSURE_ARG_POINTER(_retval); + + GetDefaultLocation(); + GetDefaultBooks(); + + bool result = false; + + if (m_Books) { + uint32_t count = 0; + uint32_t i; + bool import; + uint32_t size; + uint32_t totalSize = 0; + + m_Books->GetLength(&count); + + for (i = 0; i < count; i++) { + nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i); + if (book) { + import = false; + size = 0; + nsresult rv = book->GetImport(&import); + if (NS_SUCCEEDED(rv) && import) { + (void) book->GetSize(&size); + result = true; + } + totalSize += size; + } + } + + m_totalSize = totalSize; + } + + m_doImport = result; + + *_retval = result; + + return NS_OK; +} + +void nsImportGenericAddressBooks::SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError) +{ + nsAutoString str; + if (pSuccess) { + pSuccess->GetData(str); + str.Append(success); + pSuccess->SetData(success); + } + if (pError) { + pError->GetData(str); + str.Append(error); + pError->SetData(error); + } +} + +already_AddRefed<nsIAddrDatabase> GetAddressBookFromUri(const char *pUri) +{ + if (!pUri) + return nullptr; + + nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID); + if (!abManager) + return nullptr; + + nsCOMPtr<nsIAbDirectory> directory; + abManager->GetDirectory(nsDependentCString(pUri), + getter_AddRefs(directory)); + if (!directory) + return nullptr; + + nsCOMPtr<nsIAbMDBDirectory> mdbDirectory = do_QueryInterface(directory); + if (!mdbDirectory) + return nullptr; + + nsCOMPtr<nsIAddrDatabase> pDatabase; + mdbDirectory->GetDatabase(getter_AddRefs(pDatabase)); + return pDatabase.forget(); +} + +already_AddRefed<nsIAddrDatabase> GetAddressBook(const char16_t *name, + bool makeNew) +{ + if (!makeNew) { + // FIXME: How do I get the list of address books and look for a + // specific name. Major bogosity! + // For now, assume we didn't find anything with that name + } + + IMPORT_LOG0("In GetAddressBook\n"); + + nsresult rv; + nsCOMPtr<nsIAddrDatabase> pDatabase; + nsCOMPtr<nsIFile> dbPath; + nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + { + /* Get the profile directory */ + rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath)); + if (NS_SUCCEEDED(rv)) + { + // Create a new address book file - we don't care what the file + // name is, as long as it's unique + rv = dbPath->Append(NS_LITERAL_STRING("impab.mab")); + if (NS_SUCCEEDED(rv)) + { + rv = dbPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); + + if (NS_SUCCEEDED(rv)) + { + IMPORT_LOG0("Getting the address database factory\n"); + + nsCOMPtr<nsIAddrDatabase> addrDBFactory = + do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return nullptr; + + IMPORT_LOG0("Opening the new address book\n"); + rv = addrDBFactory->Open(dbPath, true, true, + getter_AddRefs(pDatabase)); + } + } + } + } + if (NS_FAILED(rv)) + { + IMPORT_LOG0("Failed to get the user profile directory from the address book session\n"); + } + + if (pDatabase && dbPath) + { + // We made a database, add it to the UI?!?!?!?!?!?! + // This is major bogosity again! Why doesn't the address book + // just handle this properly for me? Uggggg... + + nsCOMPtr<nsIAbDirectory> parentDir; + abManager->GetDirectory(NS_LITERAL_CSTRING(kAllDirectoryRoot), + getter_AddRefs(parentDir)); + if (parentDir) + { + nsAutoCString URI("moz-abmdbdirectory://"); + nsAutoCString leafName; + rv = dbPath->GetNativeLeafName(leafName); + if (NS_FAILED(rv)) + IMPORT_LOG0("*** Error: Unable to get name of database file\n"); + else + { + URI.Append(leafName); + rv = parentDir->CreateDirectoryByURI(nsDependentString(name), URI); + if (NS_FAILED(rv)) + IMPORT_LOG0("*** Error: Unable to create address book directory\n"); + } + } + + if (NS_SUCCEEDED(rv)) + IMPORT_LOG0("Added new address book to the UI\n"); + else + IMPORT_LOG0("*** Error: An error occurred while adding the address book to the UI\n"); + } + + return pDatabase.forget(); +} + +NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsString success; + nsString error; + + if (!m_doImport) { + *_retval = true; + nsImportStringBundle::GetStringByID(IMPORT_NO_ADDRBOOKS, m_stringBundle, + success); + SetLogs(success, error, successLog, errorLog); + return NS_OK; + } + + if (!m_pInterface || !m_Books) { + nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + bool needsFieldMap = false; + + if (NS_FAILED(m_pInterface->GetNeedsFieldMap(m_pLocation, &needsFieldMap)) || + (needsFieldMap && !m_pFieldMap)) { + nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + NS_IF_RELEASE(m_pSuccessLog); + NS_IF_RELEASE(m_pErrorLog); + m_pSuccessLog = successLog; + m_pErrorLog = errorLog; + NS_IF_ADDREF(m_pSuccessLog); + NS_IF_ADDREF(m_pErrorLog); + + + // create the info need to drive address book import. We're + // not going to create a new thread for this since address books + // don't tend to be large, and import is rare. + m_pThreadData = new AddressThreadData(); + m_pThreadData->books = m_Books; + NS_ADDREF(m_Books); + m_pThreadData->addressImport = m_pInterface; + NS_ADDREF(m_pInterface); + m_pThreadData->fieldMap = m_pFieldMap; + NS_IF_ADDREF(m_pFieldMap); + m_pThreadData->errorLog = m_pErrorLog; + NS_IF_ADDREF(m_pErrorLog); + m_pThreadData->successLog = m_pSuccessLog; + NS_IF_ADDREF(m_pSuccessLog); + if (m_pDestinationUri) + m_pThreadData->pDestinationUri = strdup(m_pDestinationUri); + + uint32_t count = 0; + m_Books->GetLength(&count); + // Create/obtain any address books that we need here, so that we don't need + // to do so inside the import thread which would just proxy the create + // operations back to the main thread anyway. + nsCOMPtr<nsIAddrDatabase> db = GetAddressBookFromUri(m_pDestinationUri); + for (uint32_t i = 0; i < count; ++i) + { + nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i); + if (book) + { + if (!db) + { + nsString name; + book->GetPreferredName(name); + db = GetAddressBook(name.get(), true); + } + m_DBs.AppendObject(db); + } + } + m_pThreadData->dBs = &m_DBs; + + NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle); + + nsresult rv; + m_pThreadData->ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv); + + ImportAddressThread(m_pThreadData); + delete m_pThreadData; + m_pThreadData = nullptr; + *_retval = true; + + return NS_OK; +} + +NS_IMETHODIMP nsImportGenericAddressBooks::ContinueImport(bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = true; + if (m_pThreadData) { + if (m_pThreadData->fatalError) + *_retval = false; + } + + return NS_OK; +} + + +NS_IMETHODIMP nsImportGenericAddressBooks::GetProgress(int32_t *_retval) +{ + // This returns the progress from the the currently + // running import mail or import address book thread. + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + if (!m_pThreadData || !(m_pThreadData->threadAlive)) { + *_retval = 100; + return NS_OK; + } + + uint32_t sz = 0; + if (m_pThreadData->currentSize && m_pInterface) { + if (NS_FAILED(m_pInterface->GetImportProgress(&sz))) + sz = 0; + } + + if (m_totalSize) + *_retval = ((m_pThreadData->currentTotal + sz) * 100) / m_totalSize; + else + *_retval = 0; + + // never return less than 5 so it looks like we are doing something! + if (*_retval < 5) + *_retval = 5; + + // as long as the thread is alive don't return completely + // done. + if (*_retval > 99) + *_retval = 99; + + return NS_OK; +} + + +NS_IMETHODIMP nsImportGenericAddressBooks::CancelImport(void) +{ + if (m_pThreadData) { + m_pThreadData->abort = true; + m_pThreadData = nullptr; + } + + return NS_OK; +} + + +AddressThreadData::AddressThreadData() +{ + fatalError = false; + driverAlive = true; + threadAlive = true; + abort = false; + currentTotal = 0; + currentSize = 0; + books = nullptr; + addressImport = nullptr; + successLog = nullptr; + errorLog = nullptr; + pDestinationUri = nullptr; + fieldMap = nullptr; + stringBundle = nullptr; + ldifService = nullptr; +} + +AddressThreadData::~AddressThreadData() +{ + if (pDestinationUri) + NS_Free(pDestinationUri); + + NS_IF_RELEASE(books); + NS_IF_RELEASE(addressImport); + NS_IF_RELEASE(errorLog); + NS_IF_RELEASE(successLog); + NS_IF_RELEASE(fieldMap); + NS_IF_RELEASE(stringBundle); +} + +void nsImportGenericAddressBooks::ReportError(const char16_t *pName, + nsString *pStream, + nsIStringBundle* aBundle) +{ + if (!pStream) + return; + // load the error string + char16_t *pFmt = nsImportStringBundle::GetStringByID(IMPORT_ERROR_GETABOOK, aBundle); + char16_t *pText = nsTextFormatter::smprintf(pFmt, pName); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + NS_Free(pFmt); + pStream->AppendLiteral(MSG_LINEBREAK); +} + +static void ImportAddressThread(void *stuff) +{ + IMPORT_LOG0("In Begin ImportAddressThread\n"); + + AddressThreadData *pData = (AddressThreadData *)stuff; + uint32_t count = 0; + uint32_t i; + bool import; + uint32_t size; + + nsString success; + nsString error; + + (void) pData->books->GetLength(&count); + + for (i = 0; (i < count) && !(pData->abort); i++) { + nsCOMPtr<nsIImportABDescriptor> book = + do_QueryElementAt(pData->books, i); + + if (book) { + import = false; + size = 0; + nsresult rv = book->GetImport(&import); + if (NS_SUCCEEDED(rv) && import) + rv = book->GetSize(&size); + + if (NS_SUCCEEDED(rv) && size && import) { + nsString name; + book->GetPreferredName(name); + + nsCOMPtr<nsIAddrDatabase> db = pData->dBs->ObjectAt(i); + + bool fatalError = false; + pData->currentSize = size; + if (db) { + char16_t *pSuccess = nullptr; + char16_t *pError = nullptr; + + /* + if (pData->fieldMap) { + int32_t sz = 0; + int32_t mapIndex; + bool active; + pData->fieldMap->GetMapSize(&sz); + IMPORT_LOG1("**** Field Map Size: %d\n", (int) sz); + for (int32_t i = 0; i < sz; i++) { + pData->fieldMap->GetFieldMap(i, &mapIndex); + pData->fieldMap->GetFieldActive(i, &active); + IMPORT_LOG3("Field map #%d: index=%d, active=%d\n", (int) i, (int) mapIndex, (int) active); + } + } + */ + + rv = pData->addressImport->ImportAddressBook(book, + db, + pData->fieldMap, + pData->ldifService, + &pError, + &pSuccess, + &fatalError); + if (NS_SUCCEEDED(rv) && pSuccess) { + success.Append(pSuccess); + NS_Free(pSuccess); + } + if (pError) { + error.Append(pError); + NS_Free(pError); + } + } + else { + nsImportGenericAddressBooks::ReportError(name.get(), &error, pData->stringBundle); + } + + pData->currentSize = 0; + pData->currentTotal += size; + + if (db) + db->Close(true); + + if (fatalError) { + pData->fatalError = true; + break; + } + } + } + } + + + nsImportGenericAddressBooks::SetLogs(success, error, pData->successLog, pData->errorLog); + + if (pData->abort || pData->fatalError) { + // FIXME: do what is necessary to get rid of what has been imported so far. + // Nothing if we went into an existing address book! Otherwise, delete + // the ones we created? + } + +} diff --git a/mailnews/import/src/nsImportEmbeddedImageData.cpp b/mailnews/import/src/nsImportEmbeddedImageData.cpp new file mode 100644 index 000000000..526f9c21c --- /dev/null +++ b/mailnews/import/src/nsImportEmbeddedImageData.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsImportEmbeddedImageData.h" + +NS_IMPL_ISUPPORTS(nsImportEmbeddedImageData, nsIMsgEmbeddedImageData) + +nsImportEmbeddedImageData::nsImportEmbeddedImageData() +{ +} + +nsImportEmbeddedImageData::nsImportEmbeddedImageData( + nsIURI *aUri, const nsACString &aCid) : m_uri(aUri), m_cid(aCid) +{ +} + +nsImportEmbeddedImageData::nsImportEmbeddedImageData( + nsIURI *aUri, const nsACString &aCid, const nsACString &aName) + : m_uri(aUri), m_cid(aCid), m_name(aName) +{ +} + +nsImportEmbeddedImageData::~nsImportEmbeddedImageData() +{ +} + +NS_IMETHODIMP nsImportEmbeddedImageData::GetUri(nsIURI **aUri) +{ + NS_ENSURE_ARG_POINTER(aUri); + NS_IF_ADDREF(*aUri = m_uri); + return NS_OK; +} + +NS_IMETHODIMP nsImportEmbeddedImageData::SetUri(nsIURI *aUri) +{ + m_uri = aUri; + return NS_OK; +} + +NS_IMETHODIMP nsImportEmbeddedImageData::GetCid(nsACString &aCid) +{ + aCid = m_cid; + return NS_OK; +} + +NS_IMETHODIMP nsImportEmbeddedImageData::SetCid(const nsACString &aCid) +{ + m_cid = aCid; + return NS_OK; +} + +NS_IMETHODIMP nsImportEmbeddedImageData::GetName(nsACString &aName) +{ + aName = m_name; + return NS_OK; +} + +NS_IMETHODIMP nsImportEmbeddedImageData::SetName(const nsACString &aName) +{ + m_name = aName; + return NS_OK; +} diff --git a/mailnews/import/src/nsImportEmbeddedImageData.h b/mailnews/import/src/nsImportEmbeddedImageData.h new file mode 100644 index 000000000..5635dc512 --- /dev/null +++ b/mailnews/import/src/nsImportEmbeddedImageData.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __IMPORTEMBEDDEDIMAGETDATA_H__ +#define __IMPORTEMBEDDEDIMAGETDATA_H__ + +#include "nsIMsgSend.h" +#include "nsStringGlue.h" +#include "nsCOMPtr.h" +#include "nsIURI.h" + +class nsImportEmbeddedImageData final : public nsIMsgEmbeddedImageData +{ +public: + nsImportEmbeddedImageData(nsIURI *aUri, const nsACString &aCID); + nsImportEmbeddedImageData(nsIURI *aUri, const nsACString &aCID, const nsACString &aName); + nsImportEmbeddedImageData(); + NS_DECL_NSIMSGEMBEDDEDIMAGEDATA + NS_DECL_ISUPPORTS + + nsCOMPtr<nsIURI> m_uri; + nsCString m_cid; + nsCString m_name; + +private: + ~nsImportEmbeddedImageData(); +}; + + +#endif diff --git a/mailnews/import/src/nsImportEncodeScan.cpp b/mailnews/import/src/nsImportEncodeScan.cpp new file mode 100644 index 000000000..77e89198d --- /dev/null +++ b/mailnews/import/src/nsImportEncodeScan.cpp @@ -0,0 +1,374 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsImportEncodeScan.h" +#include "nsNetUtil.h" + +#define kBeginAppleSingle 0 +#define kBeginDataFork 1 +#define kBeginResourceFork 2 +#define kAddEntries 3 +#define kScanningDataFork 4 +#define kScanningRsrcFork 5 +#define kDoneWithFile 6 + +uint32_t gAppleSingleHeader[6] = {0x00051600, 0x00020000, 0, 0, 0, 0}; +#define kAppleSingleHeaderSize (6 * sizeof(uint32_t)) + +#ifdef _MAC_IMPORT_CODE +#include "MoreFilesExtras.h" +#include "MoreDesktopMgr.h" + +CInfoPBRec gCatInfoPB; +U32 g2000Secs = 0; +long gGMTDelta = 0; + +long GetGmtDelta(void); +U32 Get2000Secs(void); + + +long GetGmtDelta(void) +{ + MachineLocation myLocation; + ReadLocation(&myLocation); + long myDelta = BitAnd(myLocation.u.gmtDelta, 0x00FFFFFF); + if (BitTst(&myDelta, 23)) + myDelta = BitOr(myDelta, 0xFF000000); + return myDelta; +} + +U32 Get2000Secs(void) +{ + DateTimeRec dr; + dr.year = 2000; + dr.month = 1; + dr.day = 1; + dr.hour = 0; + dr.minute = 0; + dr.second = 0; + dr.dayOfWeek = 0; + U32 result; + DateToSeconds(&dr, &result); + return result; +} +#endif + +nsImportEncodeScan::nsImportEncodeScan() +{ + m_isAppleSingle = false; + m_encodeScanState = 0; + m_resourceForkSize = 0; + m_dataForkSize = 0; +} + +nsImportEncodeScan::~nsImportEncodeScan() +{ +} + +bool nsImportEncodeScan::InitEncodeScan(bool appleSingleEncode, nsIFile *fileLoc, const char *pName, uint8_t * pBuf, uint32_t sz) +{ + CleanUpEncodeScan(); + m_isAppleSingle = appleSingleEncode; + m_encodeScanState = kBeginAppleSingle; + m_pInputFile = do_QueryInterface(fileLoc); + m_useFileName = pName; + m_pBuf = pBuf; + m_bufSz = sz; + if (!m_isAppleSingle) + { + if (!m_inputStream) + { + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(m_inputStream), m_pInputFile); + NS_ENSURE_SUCCESS(rv, false); + } + + InitScan(m_inputStream, pBuf, sz); + } + else { + #ifdef _MAC_IMPORT_CODE + // Fill in the file sizes + m_resourceForkSize = fileLoc.GetMacFileSize(UFileLocation::eResourceFork); + m_dataForkSize = fileLoc.GetMacFileSize(UFileLocation::eDataFork); + #endif + } + + return true; +} + +void nsImportEncodeScan::CleanUpEncodeScan(void) +{ + m_pInputStream->Close(); + m_pInputStream = nullptr; + m_pInputFile = nullptr; +} + + +// 26 + 12 per entry + +void nsImportEncodeScan::FillInEntries(int numEntries) +{ +#ifdef _MAC_IMPORT_CODE + int len = m_useFileName.GetLength(); + if (len < 32) + len = 32; + long entry[3]; + long fileOffset = 26 + (12 * numEntries); + entry[0] = 3; + entry[1] = fileOffset; + entry[2] = m_useFileName.GetLength(); + fileOffset += len; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + + + Str255 comment; + comment[0] = 0; + OSErr err = FSpDTGetComment(m_inputFileLoc, comment); + if (comment[0] > 200) + comment[0] = 200; + entry[0] = 4; + entry[1] = fileOffset; + entry[2] = comment[0]; + fileOffset += 200; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + + + entry[0] = 8; + entry[1] = fileOffset; + entry[2] = 16; + fileOffset += 16; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + + entry[0] = 9; + entry[1] = fileOffset; + entry[2] = 32; + fileOffset += 32; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + + + entry[0] = 10; + entry[1] = fileOffset; + entry[2] = 4; + fileOffset += 4; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + + if (m_resourceForkSize) { + entry[0] = 2; + entry[1] = fileOffset; + entry[2] = m_resourceForkSize; + fileOffset += m_resourceForkSize; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + } + + if (m_dataForkSize) { + entry[0] = 1; + entry[1] = fileOffset; + entry[2] = m_dataForkSize; + fileOffset += m_dataForkSize; + MemCpy(m_pBuf + m_bytesInBuf, entry, 12); + m_bytesInBuf += 12; + } + +#endif +} + +bool nsImportEncodeScan::AddEntries(void) +{ +#ifdef _MAC_IMPORT_CODE + if (!g2000Secs) { + g2000Secs = Get2000Secs(); + gGMTDelta = GetGmtDelta(); + } + MemCpy(m_pBuf + m_bytesInBuf, (PC_S8) m_useFileName, m_useFileName.GetLength()); + m_bytesInBuf += m_useFileName.GetLength(); + if (m_useFileName.GetLength() < 32) { + int len = m_useFileName.GetLength(); + while (len < 32) { + *((P_S8)m_pBuf + m_bytesInBuf) = 0; + m_bytesInBuf++; + len++; + } + } + + Str255 comment; + comment[0] = 0; + OSErr err = FSpDTGetComment(m_inputFileLoc, comment); + comment[0] = 200; + MemCpy(m_pBuf + m_bytesInBuf, &(comment[1]), comment[0]); + m_bytesInBuf += comment[0]; + + long dates[4]; + dates[0] = gCatInfoPB.hFileInfo.ioFlCrDat; + dates[1] = gCatInfoPB.hFileInfo.ioFlMdDat; + dates[2] = gCatInfoPB.hFileInfo.ioFlBkDat; + dates[3] = 0x80000000; + for (short i = 0; i < 3; i++) { + dates[i] -= g2000Secs; + dates[i] += gGMTDelta; + } + MemCpy(m_pBuf + m_bytesInBuf, dates, 16); + m_bytesInBuf += 16; + + + FInfo fInfo = gCatInfoPB.hFileInfo.ioFlFndrInfo; + FXInfo fxInfo = gCatInfoPB.hFileInfo.ioFlXFndrInfo; + fInfo.fdFlags = 0; + fInfo.fdLocation.h = 0; + fInfo.fdLocation.v = 0; + fInfo.fdFldr = 0; + MemSet(&fxInfo, 0, sizeof(fxInfo)); + MemCpy(m_pBuf + m_bytesInBuf, &fInfo, 16); + m_bytesInBuf += 16; + MemCpy(m_pBuf + m_bytesInBuf, &fxInfo, 16); + m_bytesInBuf += 16; + + + dates[0] = 0; + if ((gCatInfoPB.hFileInfo.ioFlAttrib & 1) != 0) + dates[0] |= 1; + MemCpy(m_pBuf + m_bytesInBuf, dates, 4); + m_bytesInBuf += 4; + + +#endif + return true; +} + +bool nsImportEncodeScan::Scan(bool *pDone) +{ + nsresult rv; + + *pDone = false; + if (m_isAppleSingle) { + // Stuff the buffer with things needed to encode the file... + // then just allow UScanFile to handle each fork, but be careful + // when handling eof. + switch(m_encodeScanState) { + case kBeginAppleSingle: { +#ifdef _MAC_IMPORT_CODE + OSErr err = GetCatInfoNoName(m_inputFileLoc.GetVRefNum(), m_inputFileLoc.GetParID(), m_inputFileLoc.GetFileNamePtr(), &gCatInfoPB); + if (err != noErr) + return FALSE; +#endif + m_eof = false; + m_pos = 0; + memcpy(m_pBuf, gAppleSingleHeader, kAppleSingleHeaderSize); + m_bytesInBuf = kAppleSingleHeaderSize; + int numEntries = 5; + if (m_dataForkSize) + numEntries++; + if (m_resourceForkSize) + numEntries++; + memcpy(m_pBuf + m_bytesInBuf, &numEntries, sizeof(numEntries)); + m_bytesInBuf += sizeof(numEntries); + FillInEntries(numEntries); + m_encodeScanState = kAddEntries; + return ScanBuffer(pDone); + } + break; + + case kBeginDataFork: { + if (!m_dataForkSize) { + m_encodeScanState = kDoneWithFile; + return true; + } + // Initialize the scan of the data fork... + if (!m_inputStream) + { + rv = NS_NewLocalFileInputStream(getter_AddRefs(m_inputStream), m_pInputFile); + NS_ENSURE_SUCCESS(rv, false); + } + m_encodeScanState = kScanningDataFork; + return true; + } + break; + + case kScanningDataFork: { + bool result = FillBufferFromFile(); + if (!result) + return false; + if (m_eof) { + m_eof = false; + result = ScanBuffer(pDone); + if (!result) + return false; + m_inputStream->Close(); + m_inputStream = nullptr; + m_encodeScanState = kDoneWithFile; + return true; + } + else + return ScanBuffer(pDone); + } + break; + + case kScanningRsrcFork: { + bool result = FillBufferFromFile(); + if (!result) + return false; + if (m_eof) { + m_eof = false; + result = ScanBuffer(pDone); + if (!result) + return false; + m_inputStream->Close(); + m_inputStream = nullptr; + m_encodeScanState = kBeginDataFork; + return true; + } + else + return ScanBuffer(pDone); + } + break; + + case kBeginResourceFork: { + if (!m_resourceForkSize) { + m_encodeScanState = kBeginDataFork; + return true; + } + /* + // FIXME: Open the resource fork on the Mac!!! + m_fH = UFile::OpenRsrcFileRead(m_inputFileLoc); + if (m_fH == TR_FILE_ERROR) + return FALSE; + */ + m_encodeScanState = kScanningRsrcFork; + return true; + } + break; + + case kAddEntries: { + ShiftBuffer(); + if (!AddEntries()) + return false; + m_encodeScanState = kBeginResourceFork; + return ScanBuffer(pDone); + } + break; + + case kDoneWithFile: { + ShiftBuffer(); + m_eof = true; + if (!ScanBuffer(pDone)) + return false; + *pDone = true; + return true; + } + break; + } + + } + else + return nsImportScanFile::Scan(pDone); + + return false; +} + diff --git a/mailnews/import/src/nsImportEncodeScan.h b/mailnews/import/src/nsImportEncodeScan.h new file mode 100644 index 000000000..3f0e246f1 --- /dev/null +++ b/mailnews/import/src/nsImportEncodeScan.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportEncodeScan_h___ +#define nsImportEncodeScan_h___ + +#include "mozilla/Attributes.h" +#include "nsIFile.h" +#include "nsImportScanFile.h" +#include "nsStringGlue.h" + +class nsImportEncodeScan : public nsImportScanFile { +public: + nsImportEncodeScan(); + ~nsImportEncodeScan(); + + bool InitEncodeScan(bool appleSingleEncode, nsIFile *pFile, const char *pName, uint8_t * pBuf, uint32_t sz); + void CleanUpEncodeScan(void); + + virtual bool Scan(bool *pDone) override; + +protected: + void FillInEntries(int numEntries); + bool AddEntries(void); + +protected: + bool m_isAppleSingle; + nsCOMPtr<nsIFile> m_pInputFile; + nsCOMPtr<nsIInputStream> m_inputStream; + int m_encodeScanState; + long m_resourceForkSize; + long m_dataForkSize; + nsCString m_useFileName; +}; + +#endif /* nsImportEncodeScan_h__ */ + diff --git a/mailnews/import/src/nsImportFieldMap.cpp b/mailnews/import/src/nsImportFieldMap.cpp new file mode 100644 index 000000000..d5e9748dc --- /dev/null +++ b/mailnews/import/src/nsImportFieldMap.cpp @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nscore.h" +#include "nsIStringBundle.h" +#include "nsImportFieldMap.h" +#include "nsImportStringBundle.h" +#include "nsCRTGlue.h" +#include "ImportDebug.h" +#include "nsCOMPtr.h" + +//////////////////////////////////////////////////////////////////////// + +NS_METHOD nsImportFieldMap::Create(nsIStringBundle *aBundle, nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsImportFieldMap *it = new nsImportFieldMap(aBundle); + if (it == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(it); + nsresult rv = it->QueryInterface(aIID, aResult); + NS_RELEASE(it); + return rv; +} + +NS_IMPL_ISUPPORTS(nsImportFieldMap, nsIImportFieldMap) + +NS_IMETHODIMP nsImportFieldMap::GetSkipFirstRecord(bool *result) +{ + NS_ENSURE_ARG_POINTER(result); + *result = m_skipFirstRecord; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::SetSkipFirstRecord(bool aResult) +{ + m_skipFirstRecord = aResult; + return NS_OK; +} + +nsImportFieldMap::nsImportFieldMap(nsIStringBundle *aBundle) +{ + m_numFields = 0; + m_pFields = nullptr; + m_pActive = nullptr; + m_allocated = 0; + // need to init the description array + m_mozFieldCount = 0; + m_skipFirstRecord = false; + nsCOMPtr<nsIStringBundle> pBundle = aBundle; + + nsString *pStr; + for (int32_t i = IMPORT_FIELD_DESC_START; i <= IMPORT_FIELD_DESC_END; i++, m_mozFieldCount++) { + pStr = new nsString(); + if (pBundle) { + nsImportStringBundle::GetStringByID(i, pBundle, *pStr); + } + else + pStr->AppendInt(i); + m_descriptions.AppendElement(pStr); + } +} + +nsImportFieldMap::~nsImportFieldMap() +{ + if (m_pFields) + delete [] m_pFields; + if (m_pActive) + delete [] m_pActive; + + nsString * pStr; + for (int32_t i = 0; i < m_mozFieldCount; i++) { + pStr = m_descriptions.ElementAt(i); + delete pStr; + } + m_descriptions.Clear(); +} + + +NS_IMETHODIMP nsImportFieldMap::GetNumMozFields(int32_t *aNumFields) +{ + NS_PRECONDITION(aNumFields != nullptr, "null ptr"); + if (!aNumFields) + return NS_ERROR_NULL_POINTER; + + *aNumFields = m_mozFieldCount; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::GetMapSize(int32_t *aNumFields) +{ + NS_PRECONDITION(aNumFields != nullptr, "null ptr"); + if (!aNumFields) + return NS_ERROR_NULL_POINTER; + + *aNumFields = m_numFields; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::GetFieldDescription(int32_t index, char16_t **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = nullptr; + if ((index < 0) || ((size_t)index >= m_descriptions.Length())) + return NS_ERROR_FAILURE; + + *_retval = ToNewUnicode(*(m_descriptions.ElementAt(index))); + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::SetFieldMapSize(int32_t size) +{ + nsresult rv = Allocate(size); + if (NS_FAILED(rv)) + return rv; + + m_numFields = size; + + return NS_OK; +} + + +NS_IMETHODIMP nsImportFieldMap::DefaultFieldMap(int32_t size) +{ + nsresult rv = SetFieldMapSize(size); + if (NS_FAILED(rv)) + return rv; + for (int32_t i = 0; i < size; i++) { + m_pFields[i] = i; + m_pActive[i] = true; + } + + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::GetFieldMap(int32_t index, int32_t *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + + if ((index < 0) || (index >= m_numFields)) + return NS_ERROR_FAILURE; + + *_retval = m_pFields[index]; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::SetFieldMap(int32_t index, int32_t fieldNum) +{ + if (index == -1) { + nsresult rv = Allocate(m_numFields + 1); + if (NS_FAILED(rv)) + return rv; + index = m_numFields; + m_numFields++; + } + else { + if ((index < 0) || (index >= m_numFields)) + return NS_ERROR_FAILURE; + } + + if ((fieldNum != -1) && ((fieldNum < 0) || (fieldNum >= m_mozFieldCount))) + return NS_ERROR_FAILURE; + + m_pFields[index] = fieldNum; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::GetFieldActive(int32_t index, bool *active) +{ + NS_PRECONDITION(active != nullptr, "null ptr"); + if (!active) + return NS_ERROR_NULL_POINTER; + if ((index < 0) || (index >= m_numFields)) + return NS_ERROR_FAILURE; + + *active = m_pActive[index]; + return NS_OK; +} + +NS_IMETHODIMP nsImportFieldMap::SetFieldActive(int32_t index, bool active) +{ + if ((index < 0) || (index >= m_numFields)) + return NS_ERROR_FAILURE; + + m_pActive[index] = active; + return NS_OK; +} + + +NS_IMETHODIMP nsImportFieldMap::SetFieldValue(nsIAddrDatabase *database, nsIMdbRow *row, int32_t fieldNum, const char16_t *value) +{ + NS_PRECONDITION(database != nullptr, "null ptr"); + NS_PRECONDITION(row != nullptr, "null ptr"); + NS_PRECONDITION(value != nullptr, "null ptr"); + if (!database || !row || !value) + return NS_ERROR_NULL_POINTER; + + // Allow the special value for a null field + if (fieldNum == -1) + return NS_OK; + + if ((fieldNum < 0) || (fieldNum >= m_mozFieldCount)) + return NS_ERROR_FAILURE; + + // UGGG!!!!! lot's of typing here! + nsresult rv; + + nsString str(value); + char *pVal = ToNewUTF8String(str); + + switch(fieldNum) { + case 0: + rv = database->AddFirstName(row, pVal); + break; + case 1: + rv = database->AddLastName(row, pVal); + break; + case 2: + rv = database->AddDisplayName(row, pVal); + break; + case 3: + rv = database->AddNickName(row, pVal); + break; + case 4: + rv = database->AddPrimaryEmail(row, pVal); + break; + case 5: + rv = database->Add2ndEmail(row, pVal); + break; + case 6: + rv = database->AddWorkPhone(row, pVal); + break; + case 7: + rv = database->AddHomePhone(row, pVal); + break; + case 8: + rv = database->AddFaxNumber(row, pVal); + break; + case 9: + rv = database->AddPagerNumber(row, pVal); + break; + case 10: + rv = database->AddCellularNumber(row, pVal); + break; + case 11: + rv = database->AddHomeAddress(row, pVal); + break; + case 12: + rv = database->AddHomeAddress2(row, pVal); + break; + case 13: + rv = database->AddHomeCity(row, pVal); + break; + case 14: + rv = database->AddHomeState(row, pVal); + break; + case 15: + rv = database->AddHomeZipCode(row, pVal); + break; + case 16: + rv = database->AddHomeCountry(row, pVal); + break; + case 17: + rv = database->AddWorkAddress(row, pVal); + break; + case 18: + rv = database->AddWorkAddress2(row, pVal); + break; + case 19: + rv = database->AddWorkCity(row, pVal); + break; + case 20: + rv = database->AddWorkState(row, pVal); + break; + case 21: + rv = database->AddWorkZipCode(row, pVal); + break; + case 22: + rv = database->AddWorkCountry(row, pVal); + break; + case 23: + rv = database->AddJobTitle(row, pVal); + break; + case 24: + rv = database->AddDepartment(row, pVal); + break; + case 25: + rv = database->AddCompany(row, pVal); + break; + case 26: + rv = database->AddWebPage1(row, pVal); + break; + case 27: + rv = database->AddWebPage2(row, pVal); + break; + case 28: + rv = database->AddBirthYear(row, pVal); + break; + case 29: + rv = database->AddBirthMonth(row, pVal); + break; + case 30: + rv = database->AddBirthDay(row, pVal); + break; + case 31: + rv = database->AddCustom1(row, pVal); + break; + case 32: + rv = database->AddCustom2(row, pVal); + break; + case 33: + rv = database->AddCustom3(row, pVal); + break; + case 34: + rv = database->AddCustom4(row, pVal); + break; + case 35: + rv = database->AddNotes(row, pVal); + break; + case 36: + rv = database->AddAimScreenName(row, pVal); + break; + default: + /* Get the field description, and add it as an anonymous attr? */ + /* OR WHAT???? */ + { + rv = NS_ERROR_FAILURE; + } + } + + NS_Free(pVal); + + return rv; +} + + +nsresult nsImportFieldMap::Allocate(int32_t newSize) +{ + if (newSize <= m_allocated) + return NS_OK; + + int32_t sz = m_allocated; + while (sz < newSize) + sz += 30; + + int32_t *pData = new int32_t[ sz]; + if (!pData) + return NS_ERROR_OUT_OF_MEMORY; + bool *pActive = new bool[sz]; + if (!pActive) { + delete [] pData; + return NS_ERROR_OUT_OF_MEMORY; + } + + int32_t i; + for (i = 0; i < sz; i++) { + pData[i] = -1; + pActive[i] = true; + } + if (m_numFields) { + for (i = 0; i < m_numFields; i++) { + pData[i] = m_pFields[i]; + pActive[i] = m_pActive[i]; + } + delete [] m_pFields; + delete [] m_pActive; + } + m_allocated = sz; + m_pFields = pData; + m_pActive = pActive; + return NS_OK; +} diff --git a/mailnews/import/src/nsImportFieldMap.h b/mailnews/import/src/nsImportFieldMap.h new file mode 100644 index 000000000..a25069b1e --- /dev/null +++ b/mailnews/import/src/nsImportFieldMap.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportFieldMap_h___ +#define nsImportFieldMap_h___ + +#include "nscore.h" +#include "nsIImportFieldMap.h" +#include "nsIAddrDatabase.h" +#include "nsTArray.h" +#include "nsString.h" + + +//////////////////////////////////////////////////////////////////////// + +class nsIStringBundle; + +class nsImportFieldMap : public nsIImportFieldMap +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_DECL_NSIIMPORTFIELDMAP + + nsImportFieldMap(nsIStringBundle *aBundle); + + static NS_METHOD Create(nsIStringBundle *aBundle, nsISupports *aOuter, REFNSIID aIID, void **aResult); + +private: + virtual ~nsImportFieldMap(); + nsresult Allocate(int32_t newSize); + +private: + int32_t m_numFields; + int32_t * m_pFields; + bool * m_pActive; + int32_t m_allocated; + nsTArray<nsString*> m_descriptions; + int32_t m_mozFieldCount; + bool m_skipFirstRecord; +}; + + +#endif diff --git a/mailnews/import/src/nsImportMail.cpp b/mailnews/import/src/nsImportMail.cpp new file mode 100644 index 000000000..ad584b8a6 --- /dev/null +++ b/mailnews/import/src/nsImportMail.cpp @@ -0,0 +1,1208 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +*/ + +#include "prthread.h" +#include "prprf.h" +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsIArray.h" +#include "nsArrayUtils.h" + +#include "nsIImportMail.h" +#include "nsIImportGeneric.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsIImportMailboxDescriptor.h" + +#include "nsStringGlue.h" +#include "nsUnicharUtils.h" + +#include "nsMsgUtils.h" +#include "nsIMsgAccountManager.h" +#include "nsMsgBaseCID.h" +#include "nsIMsgFolder.h" +#include "nsImportStringBundle.h" +#include "nsIStringBundle.h" +#include "nsTextFormatter.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIImportService.h" +#include "ImportDebug.h" +#include "plstr.h" +#include "MailNewsTypes.h" +#include "nsThreadUtils.h" +#include "mozilla/Services.h" + +#define IMPORT_MSGS_URL "chrome://messenger/locale/importMsgs.properties" + +//////////////////////////////////////////////////////////////////////// + +static void ImportMailThread(void *stuff); + +class ImportThreadData; + +class nsImportGenericMail : public nsIImportGeneric +{ +public: + + nsImportGenericMail(); + + NS_DECL_THREADSAFE_ISUPPORTS + + /* nsISupports GetData (in string dataId); */ + NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override; + + NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override; + + NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override; + + NS_IMETHOD WantsProgress(bool *_retval) override; + + NS_IMETHODIMP BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override; + + NS_IMETHOD ContinueImport(bool *_retval) override; + + NS_IMETHOD GetProgress(int32_t *_retval) override; + + NS_IMETHOD CancelImport(void) override; + +private: + virtual ~nsImportGenericMail(); + bool CreateFolder(nsIMsgFolder **ppFolder); + void GetDefaultMailboxes(void); + void GetDefaultLocation(void); + void GetDefaultDestination(void); + void GetMailboxName(uint32_t index, nsISupportsString *pStr); + +public: + static void SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError); + static void ReportError(int32_t id, const char16_t *pName, nsString *pStream, nsIStringBundle* aBundle); + +private: + nsString m_pName; // module name that created this interface + nsIMsgFolder * m_pDestFolder; + bool m_deleteDestFolder; + bool m_createdFolder; + nsCOMPtr <nsIFile> m_pSrcLocation; + bool m_gotLocation; + bool m_found; + bool m_userVerify; + nsIImportMail *m_pInterface; + nsIArray * m_pMailboxes; + nsISupportsString *m_pSuccessLog; + nsISupportsString *m_pErrorLog; + uint32_t m_totalSize; + bool m_doImport; + ImportThreadData * m_pThreadData; + bool m_performingMigration; + nsCOMPtr<nsIStringBundle> m_stringBundle; +}; + +class ImportThreadData { +public: + bool driverAlive; + bool threadAlive; + bool abort; + bool fatalError; + uint32_t currentTotal; + uint32_t currentSize; + nsIMsgFolder * destRoot; + bool ownsDestRoot; + nsIArray *boxes; + nsIImportMail * mailImport; + nsISupportsString * successLog; + nsISupportsString * errorLog; + uint32_t currentMailbox; + bool performingMigration; + nsIStringBundle *stringBundle; + + ImportThreadData(); + ~ImportThreadData(); + void DriverDelete(); + void ThreadDelete(); + void DriverAbort(); +}; + +// forward decl for proxy methods +nsresult ProxyGetSubFolders(nsIMsgFolder *aFolder); +nsresult ProxyGetChildNamed(nsIMsgFolder *aFolder,const nsAString & aName, + nsIMsgFolder **aChild); +nsresult ProxyGetParent(nsIMsgFolder *aFolder, nsIMsgFolder **aParent); +nsresult ProxyContainsChildNamed(nsIMsgFolder *aFolder, const nsAString &aName, + bool *aResult); +nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder *aFolder, + const nsAString& aPrefix, + nsIMsgFolder *aOtherFolder, + nsAString& aName); +nsresult ProxyCreateSubfolder(nsIMsgFolder *aFolder, const nsAString &aName); +nsresult ProxyForceDBClosed(nsIMsgFolder *aFolder); + +nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric) +{ + NS_PRECONDITION(aImportGeneric != nullptr, "null ptr"); + if (! aImportGeneric) + return NS_ERROR_NULL_POINTER; + + nsImportGenericMail *pGen = new nsImportGenericMail(); + + if (pGen == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(pGen); + nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric); + NS_RELEASE(pGen); + + return rv; +} + +nsImportGenericMail::nsImportGenericMail() +{ + m_found = false; + m_userVerify = false; + m_gotLocation = false; + m_pInterface = nullptr; + m_pMailboxes = nullptr; + m_pSuccessLog = nullptr; + m_pErrorLog = nullptr; + m_totalSize = 0; + m_doImport = false; + m_pThreadData = nullptr; + + m_pDestFolder = nullptr; + m_deleteDestFolder = false; + m_createdFolder = false; + m_performingMigration = false; + + // Init logging module. + if (!IMPORTLOGMODULE) + IMPORTLOGMODULE = PR_NewLogModule("IMPORT"); + + nsresult rv = nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle)); + if (NS_FAILED(rv)) + IMPORT_LOG0("Failed to get string bundle for Importing Mail"); +} + + +nsImportGenericMail::~nsImportGenericMail() +{ + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + NS_IF_RELEASE(m_pDestFolder); + NS_IF_RELEASE(m_pInterface); + NS_IF_RELEASE(m_pMailboxes); + NS_IF_RELEASE(m_pSuccessLog); + NS_IF_RELEASE(m_pErrorLog); +} + + + +NS_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric) + + +NS_IMETHODIMP nsImportGenericMail::GetData(const char *dataId, nsISupports **_retval) +{ + nsresult rv = NS_OK; + + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = nullptr; + if (!PL_strcasecmp(dataId, "mailInterface")) { + *_retval = m_pInterface; + NS_IF_ADDREF(m_pInterface); + } + + if (!PL_strcasecmp(dataId, "mailBoxes")) { + if (!m_pMailboxes) + GetDefaultMailboxes(); + *_retval = m_pMailboxes; + NS_IF_ADDREF(m_pMailboxes); + } + + if (!PL_strcasecmp(dataId, "mailLocation")) { + if (!m_pSrcLocation) + GetDefaultLocation(); + NS_IF_ADDREF(*_retval = m_pSrcLocation); + } + + if (!PL_strcasecmp(dataId, "mailDestination")) { + if (!m_pDestFolder) + GetDefaultDestination(); + NS_IF_ADDREF(*_retval = m_pDestFolder); + } + + if (!PL_strcasecmp(dataId, "migration")) { + nsCOMPtr<nsISupportsPRBool> migrationString = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + migrationString->SetData(m_performingMigration); + NS_IF_ADDREF(*_retval = migrationString); + } + + if (!PL_strcasecmp(dataId, "currentMailbox")) { + // create an nsISupportsString, get the current mailbox + // name being imported and put it in the string + nsCOMPtr<nsISupportsString> data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + if (m_pThreadData) { + GetMailboxName(m_pThreadData->currentMailbox, data); + } + NS_ADDREF(*_retval = data); + } + + return rv; +} + +NS_IMETHODIMP nsImportGenericMail::SetData(const char *dataId, nsISupports *item) +{ + nsresult rv = NS_OK; + NS_PRECONDITION(dataId != nullptr, "null ptr"); + if (!dataId) + return NS_ERROR_NULL_POINTER; + + if (!PL_strcasecmp(dataId, "mailInterface")) { + NS_IF_RELEASE(m_pInterface); + if (item) + item->QueryInterface(NS_GET_IID(nsIImportMail), (void **) &m_pInterface); + } + if (!PL_strcasecmp(dataId, "mailBoxes")) { + NS_IF_RELEASE(m_pMailboxes); + if (item) + item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_pMailboxes); + } + + if (!PL_strcasecmp(dataId, "mailLocation")) { + NS_IF_RELEASE(m_pMailboxes); + m_pSrcLocation = nullptr; + if (item) { + nsresult rv; + nsCOMPtr <nsIFile> location = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv,rv); + m_pSrcLocation = location; + } + } + + if (!PL_strcasecmp(dataId, "mailDestination")) { + NS_IF_RELEASE(m_pDestFolder); + if (item) + item->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) &m_pDestFolder); + m_deleteDestFolder = false; + } + + if (!PL_strcasecmp(dataId, "name")) { + nsCOMPtr<nsISupportsString> nameString; + if (item) { + item->QueryInterface(NS_GET_IID(nsISupportsString), getter_AddRefs(nameString)); + rv = nameString->GetData(m_pName); + } + } + + if (!PL_strcasecmp(dataId, "migration")) { + nsCOMPtr<nsISupportsPRBool> migrationString; + if (item) { + item->QueryInterface(NS_GET_IID(nsISupportsPRBool), getter_AddRefs(migrationString)); + rv = migrationString->GetData(&m_performingMigration); + } + } + return rv; +} + +NS_IMETHODIMP nsImportGenericMail::GetStatus(const char *statusKind, int32_t *_retval) +{ + NS_PRECONDITION(statusKind != nullptr, "null ptr"); + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!statusKind || !_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = 0; + + if (!PL_strcasecmp(statusKind, "isInstalled")) { + GetDefaultLocation(); + *_retval = (int32_t) m_found; + } + + if (!PL_strcasecmp(statusKind, "canUserSetLocation")) { + GetDefaultLocation(); + *_retval = (int32_t) m_userVerify; + } + + return NS_OK; +} + + +void nsImportGenericMail::GetDefaultLocation(void) +{ + if (!m_pInterface) + return; + + if (m_pSrcLocation && m_gotLocation) + return; + + m_gotLocation = true; + + nsCOMPtr <nsIFile> pLoc; + m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify); + if (!m_pSrcLocation) + m_pSrcLocation = pLoc; +} + +void nsImportGenericMail::GetDefaultMailboxes(void) +{ + if (!m_pInterface || m_pMailboxes || !m_pSrcLocation) + return; + + m_pInterface->FindMailboxes(m_pSrcLocation, &m_pMailboxes); +} + +void nsImportGenericMail::GetDefaultDestination(void) +{ + if (m_pDestFolder) + return; + if (!m_pInterface) + return; + + nsIMsgFolder * rootFolder; + m_deleteDestFolder = false; + m_createdFolder = false; + if (CreateFolder(&rootFolder)) { + m_pDestFolder = rootFolder; + m_deleteDestFolder = true; + m_createdFolder = true; + return; + } + IMPORT_LOG0("*** GetDefaultDestination: Failed to create a default import destination folder."); +} + +NS_IMETHODIMP nsImportGenericMail::WantsProgress(bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + NS_ENSURE_ARG_POINTER(_retval); + + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + if (!m_pMailboxes) { + GetDefaultLocation(); + GetDefaultMailboxes(); + } + + if (!m_pDestFolder) { + GetDefaultDestination(); + } + + bool result = false; + + if (m_pMailboxes) { + uint32_t i; + bool import; + uint32_t count = 0; + uint32_t size; + uint32_t totalSize = 0; + + (void) m_pMailboxes->GetLength(&count); + for (i = 0; i < count; i++) { + nsCOMPtr<nsIImportMailboxDescriptor> box = + do_QueryElementAt(m_pMailboxes, i); + if (box) { + import = false; + size = 0; + nsresult rv = box->GetImport(&import); + if (NS_SUCCEEDED(rv) && import) { + (void) box->GetSize(&size); + result = true; + } + totalSize += size; + } + } + + m_totalSize = totalSize; + } + + m_doImport = result; + + *_retval = result; + + return NS_OK; +} + +void nsImportGenericMail::GetMailboxName(uint32_t index, nsISupportsString *pStr) +{ + if (m_pMailboxes) { + nsCOMPtr<nsIImportMailboxDescriptor> box(do_QueryElementAt(m_pMailboxes, index)); + if (box) { + nsAutoString name; + box->GetDisplayName(getter_Copies(name)); + if (!name.IsEmpty()) { + pStr->SetData(name); + } + } + } +} + +NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsString success; + nsString error; + + if (!m_doImport) { + nsImportStringBundle::GetStringByID(IMPORT_NO_MAILBOXES, + m_stringBundle, success); + SetLogs(success, error, successLog, errorLog); + *_retval = true; + return NS_OK; + } + + if (!m_pInterface || !m_pMailboxes) { + IMPORT_LOG0("*** BeginImport: Either the interface or source mailbox is not set properly."); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTINITIALIZED, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + if (!m_pDestFolder) { + IMPORT_LOG0("*** BeginImport: The destination mailbox is not set properly."); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NODESTFOLDER, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + NS_IF_RELEASE(m_pSuccessLog); + NS_IF_RELEASE(m_pErrorLog); + m_pSuccessLog = successLog; + m_pErrorLog = errorLog; + NS_IF_ADDREF(m_pSuccessLog); + NS_IF_ADDREF(m_pErrorLog); + + + // kick off the thread to do the import!!!! + m_pThreadData = new ImportThreadData(); + m_pThreadData->boxes = m_pMailboxes; + NS_ADDREF(m_pMailboxes); + m_pThreadData->mailImport = m_pInterface; + NS_ADDREF(m_pInterface); + m_pThreadData->errorLog = m_pErrorLog; + NS_IF_ADDREF(m_pErrorLog); + m_pThreadData->successLog = m_pSuccessLog; + NS_IF_ADDREF(m_pSuccessLog); + + m_pThreadData->ownsDestRoot = m_deleteDestFolder; + m_pThreadData->destRoot = m_pDestFolder; + m_pThreadData->performingMigration = m_performingMigration; + NS_IF_ADDREF(m_pDestFolder); + + NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle); + + PRThread *pThread = PR_CreateThread(PR_USER_THREAD, &ImportMailThread, m_pThreadData, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (!pThread) { + m_pThreadData->ThreadDelete(); + m_pThreadData->abort = true; + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + *_retval = false; + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTHREAD, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + } + else + *_retval = true; + + return NS_OK; + +} + + +NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = true; + if (m_pThreadData) { + if (m_pThreadData->fatalError) + *_retval = false; + } + + return NS_OK; +} + + +NS_IMETHODIMP nsImportGenericMail::GetProgress(int32_t *_retval) +{ + // This returns the progress from the the currently + // running import mail or import address book thread. + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + if (!m_pThreadData || !(m_pThreadData->threadAlive)) { + *_retval = 100; + return NS_OK; + } + + uint32_t sz = 0; + if (m_pThreadData->currentSize && m_pInterface) { + if (NS_FAILED(m_pInterface->GetImportProgress(&sz))) + sz = 0; + } + + + // *_retval = (int32_t) (((uint32_t)(m_pThreadData->currentTotal + sz) * (uint32_t)100) / m_totalSize); + + if (m_totalSize) { + double perc; + perc = (double) m_pThreadData->currentTotal; + perc += sz; + perc *= 100; + perc /= m_totalSize; + *_retval = (int32_t) perc; + if (*_retval > 100) + *_retval = 100; + } + else + *_retval = 0; + + // never return 100% while the thread is still alive + if (*_retval > 99) + *_retval = 99; + + return NS_OK; +} + +void nsImportGenericMail::ReportError(int32_t id, const char16_t *pName, nsString *pStream, nsIStringBundle *aBundle) +{ + if (!pStream) + return; + + // load the error string + char16_t *pFmt = nsImportStringBundle::GetStringByID(id, aBundle); + char16_t *pText = nsTextFormatter::smprintf(pFmt, pName); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + NS_Free(pFmt); + pStream->Append(NS_ConvertASCIItoUTF16(MSG_LINEBREAK)); +} + + +void nsImportGenericMail::SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError) +{ + nsAutoString str; + if (pSuccess) { + pSuccess->GetData(str); + str.Append(success); + pSuccess->SetData(str); + } + if (pError) { + pError->GetData(str); + str.Append(error); + pError->SetData(str); + } +} + +NS_IMETHODIMP nsImportGenericMail::CancelImport(void) +{ + if (m_pThreadData) { + m_pThreadData->abort = true; + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + return NS_OK; +} + + +ImportThreadData::ImportThreadData() +{ + fatalError = false; + driverAlive = true; + threadAlive = true; + abort = false; + currentTotal = 0; + currentSize = 0; + destRoot = nullptr; + ownsDestRoot = false; + boxes = nullptr; + mailImport = nullptr; + successLog = nullptr; + errorLog = nullptr; + stringBundle = nullptr; +} + +ImportThreadData::~ImportThreadData() +{ + NS_IF_RELEASE(destRoot); + NS_IF_RELEASE(boxes); + NS_IF_RELEASE(mailImport); + NS_IF_RELEASE(errorLog); + NS_IF_RELEASE(successLog); + NS_IF_RELEASE(stringBundle); +} + +void ImportThreadData::DriverDelete(void) +{ + driverAlive = false; + if (!driverAlive && !threadAlive) + delete this; +} + +void ImportThreadData::ThreadDelete() +{ + threadAlive = false; + if (!driverAlive && !threadAlive) + delete this; +} + +void ImportThreadData::DriverAbort() +{ + if (abort && !threadAlive && destRoot) { + if (ownsDestRoot) { + destRoot->RecursiveDelete(true, nullptr); + } + else { + // FIXME: just delete the stuff we created? + } + } + else + abort = true; + DriverDelete(); +} + + + +static void +ImportMailThread(void *stuff) +{ + ImportThreadData *pData = (ImportThreadData *)stuff; + + IMPORT_LOG0("ImportMailThread: Starting..."); + + nsresult rv = NS_OK; + + nsCOMPtr<nsIMsgFolder> destRoot(pData->destRoot); + + uint32_t count = 0; + rv = pData->boxes->GetLength(&count); + + uint32_t i; + bool import; + uint32_t size; + uint32_t depth = 1; + uint32_t newDepth; + nsString lastName; + char16_t * pName; + + nsCOMPtr<nsIMsgFolder> curFolder(destRoot); + + nsCOMPtr<nsIMsgFolder> newFolder; + nsCOMPtr<nsIMsgFolder> subFolder; + + bool exists; + + nsString success; + nsString error; + + // GetSubFolders() will initialize folders if they are not already initialized. + ProxyGetSubFolders(curFolder); + + IMPORT_LOG1("ImportMailThread: Total number of folders to import = %d.", count); + + // Note that the front-end js script only displays one import result string so + // we combine both good and bad import status into one string (in var 'success'). + + for (i = 0; (i < count) && !(pData->abort); i++) { + nsCOMPtr<nsIImportMailboxDescriptor> box = + do_QueryElementAt(pData->boxes, i); + if (box) { + pData->currentMailbox = i; + + import = false; + size = 0; + rv = box->GetImport(&import); + if (import) + rv = box->GetSize(&size); + rv = box->GetDepth(&newDepth); + if (newDepth > depth) { + // OK, we are going to add a subfolder under the last/previous folder we processed, so + // find this folder (stored in 'lastName') who is going to be the new parent folder. + IMPORT_LOG1("ImportMailThread: Processing child folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get()); + rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(subFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1("*** ImportMailThread: Failed to get the interface for child folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD, + lastName.get(), + &error, pData->stringBundle); + pData->fatalError = true; + break; + } + curFolder = subFolder; + // Make sure this new parent folder obj has the correct subfolder list so far. + rv = ProxyGetSubFolders(curFolder); + } + else if (newDepth < depth) { + rv = NS_OK; + while ((newDepth < depth) && NS_SUCCEEDED(rv)) { + rv = curFolder->GetParent(getter_AddRefs(curFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1("*** ImportMailThread: Failed to get the interface for parent folder '%s'.", lastName.get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD, + lastName.get(), &error, + pData->stringBundle); + pData->fatalError = true; + break; + } + depth--; + } + if (NS_FAILED(rv)) { + IMPORT_LOG1("*** ImportMailThread: Failed to get the proxy interface for parent folder '%s'.", lastName.get()); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY, + pData->stringBundle, error); + pData->fatalError = true; + break; + } + } + depth = newDepth; + pName = nullptr; + box->GetDisplayName(&pName); + if (pName) { + lastName = pName; + NS_Free(pName); + } + else + lastName.AssignLiteral("Unknown!"); + + // translate the folder name if we are doing migration, but + // only for special folders which are at the root level + if (pData->performingMigration && depth == 1) + pData->mailImport->TranslateFolderName(lastName, lastName); + + exists = false; + rv = ProxyContainsChildNamed(curFolder, lastName, &exists); + + // If we are performing profile migration (as opposed to importing) then we are starting + // with empty local folders. In that case, always choose to over-write the existing local folder + // with this name. Don't create a unique subfolder name. Otherwise you end up with "Inbox, Inbox0" + // or "Unsent Folders, UnsentFolders0" + if (exists && !pData->performingMigration) { + nsString subName; + ProxyGenerateUniqueSubfolderName(curFolder, lastName, nullptr, subName); + if (!subName.IsEmpty()) + lastName.Assign(subName); + } + + IMPORT_LOG1("ImportMailThread: Creating new import folder '%s'.", NS_ConvertUTF16toUTF8(lastName).get()); + ProxyCreateSubfolder(curFolder, lastName); // this may fail if the folder already exists..that's ok + + rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(newFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1("*** ImportMailThread: Failed to locate subfolder '%s' after it's been created.", lastName.get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(), + &error, pData->stringBundle); + } + + if (size && import && newFolder && NS_SUCCEEDED(rv)) { + bool fatalError = false; + pData->currentSize = size; + char16_t *pSuccess = nullptr; + char16_t *pError = nullptr; + rv = pData->mailImport->ImportMailbox(box, newFolder, &pError, &pSuccess, &fatalError); + if (pError) { + error.Append(pError); + NS_Free(pError); + } + if (pSuccess) { + success.Append(pSuccess); + NS_Free(pSuccess); + } + + pData->currentSize = 0; + pData->currentTotal += size; + + // commit to the db synchronously, but using a proxy since it doesn't like being used + // elsewhere than from the main thread. + // OK, we've copied the actual folder/file over if the folder size is not 0 + // (ie, the msg summary is no longer valid) so close the msg database so that + // when the folder is reopened the folder db can be reconstructed (which + // validates msg summary and forces folder to be reparsed). + rv = ProxyForceDBClosed(newFolder); + fatalError = NS_FAILED(rv); + + if (fatalError) { + IMPORT_LOG1("*** ImportMailThread: ImportMailbox returned fatalError, mailbox #%d\n", (int) i); + pData->fatalError = true; + break; + } + } + } + } + + // Now save the new acct info to pref file. + nsCOMPtr <nsIMsgAccountManager> accMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv) && accMgr) { + rv = accMgr->SaveAccountInfo(); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file"); + } + + nsImportGenericMail::SetLogs(success, error, pData->successLog, pData->errorLog); + + if (pData->abort || pData->fatalError) { + IMPORT_LOG0("*** ImportMailThread: Abort or fatalError flag was set\n"); + if (pData->ownsDestRoot) { + IMPORT_LOG0("Calling destRoot->RecursiveDelete\n"); + destRoot->RecursiveDelete(true, nullptr); + } + else { + // FIXME: just delete the stuff we created? + } + } + + IMPORT_LOG1("Import mailbox thread done: %d\n", (int) pData->currentTotal); + + pData->ThreadDelete(); + +} + +// Creates a folder in Local Folders with the module name + mail +// for e.g: Outlook Mail +bool nsImportGenericMail::CreateFolder(nsIMsgFolder **ppFolder) +{ + nsresult rv; + *ppFolder = nullptr; + + nsCOMPtr<nsIStringBundle> bundle; + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + if (!bundleService) + return false; + rv = bundleService->CreateBundle(IMPORT_MSGS_URL, getter_AddRefs(bundle)); + if (NS_FAILED(rv)) + return false; + nsString folderName; + if (!m_pName.IsEmpty()) { + const char16_t *moduleName[] = { m_pName.get() }; + rv = bundle->FormatStringFromName(u"ImportModuleFolderName", + moduleName, 1, + getter_Copies(folderName)); + } + else { + rv = bundle->GetStringFromName(u"DefaultFolderName", + getter_Copies(folderName)); + } + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to get Folder Name!\n"); + return false; + } + nsCOMPtr <nsIMsgAccountManager> accMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to create account manager!\n"); + return false; + } + + nsCOMPtr <nsIMsgIncomingServer> server; + rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server)); + // if Local Folders does not exist already, create it + if (NS_FAILED(rv) || !server) + { + rv = accMgr->CreateLocalMailAccount(); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to create Local Folders!\n"); + return false; + } + + rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server)); + } + + if (NS_SUCCEEDED(rv) && server) { + nsCOMPtr <nsIMsgFolder> localRootFolder; + rv = server->GetRootMsgFolder(getter_AddRefs(localRootFolder)); + if (localRootFolder) { + // we need to call GetSubFolders() so that the folders get initialized + // if they are not initialized yet. + nsCOMPtr<nsISimpleEnumerator> aEnumerator; + rv = localRootFolder->GetSubFolders(getter_AddRefs(aEnumerator)); + if (NS_SUCCEEDED(rv)) { + // check if the folder name we picked already exists. + bool exists = false; + rv = localRootFolder->ContainsChildNamed(folderName, &exists); + if (exists) { + nsString name; + localRootFolder->GenerateUniqueSubfolderName(folderName, nullptr, name); + if (!name.IsEmpty()) + folderName.Assign(name); + else { + IMPORT_LOG0("*** Failed to find a unique folder name!\n"); + return false; + } + } + IMPORT_LOG1("Creating folder for importing mail: '%s'\n", NS_ConvertUTF16toUTF8(folderName).get()); + + // Bug 564162 identifies a dataloss design flaw. + // A working Thunderbird client can have mail in Local Folders and a + // subsequent import 'Everything' will trigger a migration which + // overwrites existing mailboxes with the imported mailboxes. + rv = localRootFolder->CreateSubfolder(folderName, nullptr); + if (NS_SUCCEEDED(rv)) { + rv = localRootFolder->GetChildNamed(folderName, ppFolder); + if (*ppFolder) { + IMPORT_LOG1("Folder '%s' created successfully\n", NS_ConvertUTF16toUTF8(folderName).get()); + return true; + } + } + } + } // if localRootFolder + } // if server + IMPORT_LOG0("****** FAILED TO CREATE FOLDER FOR IMPORT\n"); + return false; +} + +/** + * These are the proxy objects we use to proxy nsIMsgFolder methods back + * the the main thread. Since there are only five, we can hand roll them. + * A better design might be a co-routine-ish design where the ui thread + * hands off each folder to the import thread and when the thread finishes + * the folder, the main thread hands it the next folder. + */ + +class GetSubFoldersRunnable : public mozilla::Runnable +{ +public: + GetSubFoldersRunnable(nsIMsgFolder *aFolder); + NS_DECL_NSIRUNNABLE +private: + nsCOMPtr<nsIMsgFolder> m_folder; +}; + +GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder *aFolder) : + m_folder(aFolder) +{ +} + +NS_IMETHODIMP GetSubFoldersRunnable::Run() +{ + nsCOMPtr<nsISimpleEnumerator> dummy; + return m_folder->GetSubFolders(getter_AddRefs(dummy)); +} + + +nsresult ProxyGetSubFolders(nsIMsgFolder *aFolder) +{ + RefPtr<GetSubFoldersRunnable> getSubFolders = + new GetSubFoldersRunnable(aFolder); + return NS_DispatchToMainThread(getSubFolders, NS_DISPATCH_SYNC); +} + +class GetChildNamedRunnable : public mozilla::Runnable +{ +public: + GetChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, nsIMsgFolder **aChild); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; + nsIMsgFolder **m_child; +}; + +GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder *aFolder, + const nsAString & aName, + nsIMsgFolder **aChild) : + m_folder(aFolder), m_name(aName), m_child(aChild) +{ +} + +NS_IMETHODIMP GetChildNamedRunnable::Run() +{ + return m_folder->GetChildNamed(m_name, m_child); +} + + +nsresult ProxyGetChildNamed(nsIMsgFolder *aFolder, const nsAString & aName, + nsIMsgFolder **aChild) +{ + RefPtr<GetChildNamedRunnable> getChildNamed = + new GetChildNamedRunnable(aFolder, aName, aChild); + return NS_DispatchToMainThread(getChildNamed, NS_DISPATCH_SYNC); +} + +class GetParentRunnable : public mozilla::Runnable +{ +public: + GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsIMsgFolder **m_parent; +}; + +GetParentRunnable::GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent) : + m_folder(aFolder), m_parent(aParent) +{ +} + +NS_IMETHODIMP GetParentRunnable::Run() +{ + return m_folder->GetParent(m_parent); +} + + +nsresult ProxyGetParent(nsIMsgFolder *aFolder, nsIMsgFolder **aParent) +{ + RefPtr<GetParentRunnable> getParent = + new GetParentRunnable(aFolder, aParent); + return NS_DispatchToMainThread(getParent, NS_DISPATCH_SYNC); +} + +class ContainsChildNamedRunnable : public mozilla::Runnable +{ +public: + ContainsChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, bool *aResult); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; + bool *m_result; +}; + +ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder *aFolder, + const nsAString &aName, + bool *aResult) : + m_folder(aFolder), m_name(aName), m_result(aResult) +{ +} + +NS_IMETHODIMP ContainsChildNamedRunnable::Run() +{ + return m_folder->ContainsChildNamed(m_name, m_result); +} + + +nsresult ProxyContainsChildNamed(nsIMsgFolder *aFolder, const nsAString &aName, + bool *aResult) +{ + RefPtr<ContainsChildNamedRunnable> containsChildNamed = + new ContainsChildNamedRunnable(aFolder, aName, aResult); + return NS_DispatchToMainThread(containsChildNamed, NS_DISPATCH_SYNC); +} + + +class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable +{ +public: + GenerateUniqueSubfolderNameRunnable(nsIMsgFolder *aFolder, + const nsAString& prefix, + nsIMsgFolder *otherFolder, + nsAString& name); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_prefix; + nsCOMPtr<nsIMsgFolder> m_otherFolder; + nsString m_name; +}; + +GenerateUniqueSubfolderNameRunnable::GenerateUniqueSubfolderNameRunnable( + nsIMsgFolder *aFolder, const nsAString& aPrefix, nsIMsgFolder *aOtherFolder, + nsAString& aName) + : m_folder(aFolder), m_prefix(aPrefix), m_otherFolder(aOtherFolder), m_name(aName) +{ +} + +NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run() +{ + return m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name); +} + + +nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder *aFolder, + const nsAString& aPrefix, + nsIMsgFolder *aOtherFolder, + nsAString& aName) + +{ + RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName = + new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder, aName); + return NS_DispatchToMainThread(generateUniqueSubfolderName, NS_DISPATCH_SYNC); +} + +class CreateSubfolderRunnable : public mozilla::Runnable +{ +public: + CreateSubfolderRunnable(nsIMsgFolder *aFolder, const nsAString& aName); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; +}; + +CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder *aFolder, + const nsAString &aName) : + m_folder(aFolder), m_name(aName) +{ +} + +NS_IMETHODIMP CreateSubfolderRunnable::Run() +{ + return m_folder->CreateSubfolder(m_name, nullptr); +} + + +nsresult ProxyCreateSubfolder(nsIMsgFolder *aFolder, const nsAString &aName) +{ + RefPtr<CreateSubfolderRunnable> createSubfolder = + new CreateSubfolderRunnable(aFolder, aName); + return NS_DispatchToMainThread(createSubfolder, NS_DISPATCH_SYNC); +} + +class ForceDBClosedRunnable : public mozilla::Runnable +{ +public: + ForceDBClosedRunnable(nsIMsgFolder *aFolder); + NS_DECL_NSIRUNNABLE +protected: + nsCOMPtr<nsIMsgFolder> m_folder; +}; + +ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder *aFolder) : + m_folder(aFolder) +{ +} + +NS_IMETHODIMP ForceDBClosedRunnable::Run() +{ + return m_folder->ForceDBClosed(); +} + +nsresult ProxyForceDBClosed(nsIMsgFolder *aFolder) +{ + RefPtr<ForceDBClosedRunnable> forceDBClosed = + new ForceDBClosedRunnable(aFolder); + return NS_DispatchToMainThread(forceDBClosed, NS_DISPATCH_SYNC); +} + + diff --git a/mailnews/import/src/nsImportMailboxDescriptor.cpp b/mailnews/import/src/nsImportMailboxDescriptor.cpp new file mode 100644 index 000000000..ab0ea5db4 --- /dev/null +++ b/mailnews/import/src/nsImportMailboxDescriptor.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nscore.h" +#include "nsImportMailboxDescriptor.h" +#include "nsComponentManagerUtils.h" + +//////////////////////////////////////////////////////////////////////// + + + +NS_METHOD nsImportMailboxDescriptor::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsImportMailboxDescriptor *it = new nsImportMailboxDescriptor(); + if (it == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(it); + nsresult rv = it->QueryInterface(aIID, aResult); + NS_RELEASE(it); + return rv; +} + +NS_IMPL_ISUPPORTS(nsImportMailboxDescriptor, nsIImportMailboxDescriptor) + +nsImportMailboxDescriptor::nsImportMailboxDescriptor() +{ + m_import = true; + m_size = 0; + m_depth = 0; + m_id = 0; + m_pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); +} diff --git a/mailnews/import/src/nsImportMailboxDescriptor.h b/mailnews/import/src/nsImportMailboxDescriptor.h new file mode 100644 index 000000000..1f4c30b31 --- /dev/null +++ b/mailnews/import/src/nsImportMailboxDescriptor.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportMailboxDescriptor_h___ +#define nsImportMailboxDescriptor_h___ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsIFile.h" +#include "nsCOMPtr.h" + +//////////////////////////////////////////////////////////////////////// + + +class nsImportMailboxDescriptor : public nsIImportMailboxDescriptor +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_IMETHOD GetIdentifier(uint32_t *pIdentifier) override { *pIdentifier = m_id; return NS_OK;} + NS_IMETHOD SetIdentifier(uint32_t ident) override { m_id = ident; return NS_OK;} + + /* attribute unsigned long depth; */ + NS_IMETHOD GetDepth(uint32_t *pDepth) override { *pDepth = m_depth; return NS_OK;} + NS_IMETHOD SetDepth(uint32_t theDepth) override { m_depth = theDepth; return NS_OK;} + + /* attribute unsigned long size; */ + NS_IMETHOD GetSize(uint32_t *pSize) override { *pSize = m_size; return NS_OK;} + NS_IMETHOD SetSize(uint32_t theSize) override { m_size = theSize; return NS_OK;} + + /* attribute wstring displayName; */ + NS_IMETHOD GetDisplayName(char16_t **pName) override { *pName = ToNewUnicode(m_displayName); return NS_OK;} + NS_IMETHOD SetDisplayName(const char16_t * pName) override { m_displayName = pName; return NS_OK;} + + /* attribute boolean import; */ + NS_IMETHOD GetImport(bool *pImport) override { *pImport = m_import; return NS_OK;} + NS_IMETHOD SetImport(bool doImport) override { m_import = doImport; return NS_OK;} + + /* readonly attribute nsIFile file; */ + NS_IMETHOD GetFile(nsIFile * *aFile) override { if (m_pFile) { NS_ADDREF(*aFile = m_pFile); return NS_OK;} else return NS_ERROR_FAILURE; } + + + + nsImportMailboxDescriptor(); + + static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + +private: + virtual ~nsImportMailboxDescriptor() {} + uint32_t m_id; // used by creator of the structure + uint32_t m_depth; // depth in the hierarchy + nsString m_displayName;// name of this mailbox + nsCOMPtr <nsIFile> m_pFile; // source file (if applicable) + uint32_t m_size; + bool m_import; // import it or not? +}; + + +#endif diff --git a/mailnews/import/src/nsImportMimeEncode.cpp b/mailnews/import/src/nsImportMimeEncode.cpp new file mode 100644 index 000000000..e13ea1f94 --- /dev/null +++ b/mailnews/import/src/nsImportMimeEncode.cpp @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsImportMimeEncode.h" + +#include "ImportCharSet.h" +#include "ImportTranslate.h" + +#define kNoState 0 +#define kStartState 1 +#define kEncodeState 2 +#define kDoneState 3 + +#define kEncodeBufferSz (8192 * 8) + +nsImportMimeEncode::nsImportMimeEncode() +{ + m_pOut = nullptr; + m_state = kNoState; + m_bytesProcessed = 0; + m_pInputBuf = nullptr; +} + +nsImportMimeEncode::~nsImportMimeEncode() +{ + delete [] m_pInputBuf; +} + +void nsImportMimeEncode::EncodeFile(nsIFile *pInFile, ImportOutFile *pOut, const char *pFileName, const char *pMimeType) +{ + m_fileName = pFileName; + m_mimeType = pMimeType; + + m_pMimeFile = pInFile; + + m_pOut = pOut; + m_state = kStartState; +} + +void nsImportMimeEncode::CleanUp(void) +{ + CleanUpEncodeScan(); +} + +bool nsImportMimeEncode::SetUpEncode(void) +{ + nsCString errStr; + if (!m_pInputBuf) { + m_pInputBuf = new uint8_t[kEncodeBufferSz]; + } + + m_appleSingle = false; + +#ifdef _MAC_IMPORT_CODE + // First let's see just what kind of beast we have? + // For files with only a data fork and a known mime type + // proceed with normal mime encoding just as on the PC. + // For unknown mime types and files with both forks, + // encode as AppleSingle format. + if (m_filePath.GetMacFileSize(UFileLocation::eResourceFork) || !pMimeType) { + m_appleSingle = TRUE; + m_mimeType = "application/applefile"; + } +#endif + + if (!InitEncodeScan(m_appleSingle, m_pMimeFile, m_fileName.get(), m_pInputBuf, kEncodeBufferSz)) { + return false; + } + + m_state = kEncodeState; + m_lineLen = 0; + + // Write out the boundary header + bool bResult = true; + bResult = m_pOut->WriteStr("Content-type: "); + if (bResult) + bResult = m_pOut->WriteStr(m_mimeType.get()); + +#ifdef _MAC_IMPORT_CODE + // include the type an creator here + if (bResult) + bResult = m_pOut->WriteStr("; x-mac-type=\""); + U8 hex[8]; + LongToHexBytes(m_filePath.GetFileType(), hex); + if (bResult) + bResult = m_pOut->WriteData(hex, 8); + LongToHexBytes(m_filePath.GetFileCreator(), hex); + if (bResult) + bResult = m_pOut->WriteStr("\"; x-mac-creator=\""); + if (bResult) + bResult = m_pOut->WriteData(hex, 8); + if (bResult) + bResult = m_pOut->WriteStr("\""); +#endif + + /* + if (bResult) + bResult = m_pOut->WriteStr(gMimeTypeFileName); + */ + if (bResult) + bResult = m_pOut->WriteStr(";\x0D\x0A"); + + nsCString fName; + bool trans = TranslateFileName(m_fileName, fName); + if (bResult) + bResult = WriteFileName(fName, trans, "name"); + if (bResult) + bResult = m_pOut->WriteStr("Content-transfer-encoding: base64"); + if (bResult) + bResult = m_pOut->WriteEol(); + if (bResult) + bResult = m_pOut->WriteStr("Content-Disposition: attachment;\x0D\x0A"); + if (bResult) + bResult = WriteFileName(fName, trans, "filename"); + if (bResult) + bResult = m_pOut->WriteEol(); + + if (!bResult) { + CleanUp(); + } + + return bResult; +} + +bool nsImportMimeEncode::DoWork(bool *pDone) +{ + *pDone = false; + switch(m_state) { + case kNoState: + return false; + break; + case kStartState: + return SetUpEncode(); + break; + case kEncodeState: + if (!Scan(pDone)) { + CleanUp(); + return false; + } + if (*pDone) { + *pDone = false; + m_state = kDoneState; + } + break; + case kDoneState: + CleanUp(); + m_state = kNoState; + *pDone = true; + break; + } + + return true; +} + +static uint8_t gBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +bool nsImportMimeEncode::ScanBuffer(bool *pDone) +{ + + uint32_t pos = m_pos; + uint32_t start = pos; + uint8_t * pChar = m_pBuf + pos; + uint32_t max = m_bytesInBuf; + uint8_t byte[4]; + uint32_t lineLen = m_lineLen; + + while ((pos + 2) < max) { + // Encode 3 bytes + byte[0] = gBase64[*pChar >> 2]; + byte[1] = gBase64[(((*pChar) & 0x3)<< 4) | (((*(pChar + 1)) & 0xF0) >> 4)]; + pChar++; + byte[2] = gBase64[(((*pChar) & 0xF) << 2) | (((*(pChar + 1)) & 0xC0) >>6)]; + pChar++; + byte[3] = gBase64[(*pChar) & 0x3F]; + if (!m_pOut->WriteData(byte, 4)) + return false; + pos += 3; + pChar++; + lineLen += 4; + if (lineLen > 71) { + if (!m_pOut->WriteEol()) + return false; + lineLen = 0; + } + } + + if ((pos < max) && m_eof) { + // Get the last few bytes! + byte[0] = gBase64[*pChar >> 2]; + pos++; + if (pos < max) { + byte[1] = gBase64[(((*pChar) & 0x3)<< 4) | (((*(pChar + 1)) & 0xF0) >> 4)]; + pChar++; + pos++; + if (pos < max) { + // Should be dead code!! (Then why is it here doofus?) + byte[2] = gBase64[(((*pChar) & 0xF) << 2) | (((*(pChar + 1)) & 0xC0) >>6)]; + pChar++; + byte[3] = gBase64[(*pChar) & 0x3F]; + pos++; + } + else { + byte[2] = gBase64[(((*pChar) & 0xF) << 2)]; + byte[3] = '='; + } + } + else { + byte[1] = gBase64[(((*pChar) & 0x3)<< 4)]; + byte[2] = '='; + byte[3] = '='; + } + + if (!m_pOut->WriteData(byte, 4)) + return false; + if (!m_pOut->WriteEol()) + return false; + } + else if (m_eof) { + /* + byte[0] = '='; + if (!m_pOut->WriteData(byte, 1)) + return FALSE; + */ + if (!m_pOut->WriteEol()) + return false; + } + + m_lineLen = (int) lineLen; + m_pos = pos; + m_bytesProcessed += (pos - start); + return true; +} + +bool nsImportMimeEncode::TranslateFileName(nsCString& inFile, nsCString& outFile) +{ + const uint8_t * pIn = (const uint8_t *) inFile.get(); + int len = inFile.Length(); + + while (len) { + if (!ImportCharSet::IsUSAscii(*pIn)) + break; + len--; + pIn++; + } + if (len) { + // non US ascii! + // assume this string needs translating... + if (!ImportTranslate::ConvertString(inFile, outFile, true)) { + outFile = inFile; + return false; + } + else { + return true; + } + } + else { + outFile = inFile; + return false; + } +} + +bool nsImportMimeEncode::WriteFileName(nsCString& fName, bool wasTrans, const char *pTag) +{ + int tagNum = 0; + int idx = 0; + bool result = true; + int len; + nsCString numStr; + + while ((((fName.Length() - idx) + strlen(pTag)) > 70) && result) { + len = 68 - strlen(pTag) - 5; + if (wasTrans) { + if (fName.CharAt(idx + len - 1) == '%') + len--; + else if (fName.CharAt(idx + len - 2) == '%') + len -= 2; + } + + if (result) + result = m_pOut->WriteStr("\x09"); + if (result) + result = m_pOut->WriteStr(pTag); + numStr = "*"; + numStr.AppendInt(tagNum); + if (result) + result = m_pOut->WriteStr(numStr.get()); + if (wasTrans && result) + result = m_pOut->WriteStr("*="); + else if (result) + result = m_pOut->WriteStr("=\""); + if (result) + result = m_pOut->WriteData(((const uint8_t *)fName.get()) + idx, len); + if (wasTrans && result) + result = m_pOut->WriteStr("\x0D\x0A"); + else if (result) + result = m_pOut->WriteStr("\"\x0D\x0A"); + idx += len; + tagNum++; + } + + if (idx) { + if ((fName.Length() - idx) > 0) { + if (result) + result = m_pOut->WriteStr("\x09"); + if (result) + result = m_pOut->WriteStr(pTag); + numStr = "*"; + numStr.AppendInt(tagNum); + if (result) + result = m_pOut->WriteStr(numStr.get()); + if (wasTrans && result) + result = m_pOut->WriteStr("*="); + else if (result) + result = m_pOut->WriteStr("=\""); + if (result) + result = m_pOut->WriteData(((const uint8_t *)fName.get()) + idx, fName.Length() - idx); + if (wasTrans && result) + result = m_pOut->WriteStr("\x0D\x0A"); + else if (result) + result = m_pOut->WriteStr("\"\x0D\x0A"); + } + } + else { + if (result) + result = m_pOut->WriteStr("\x09"); + if (result) + result = m_pOut->WriteStr(pTag); + if (wasTrans && result) + result = m_pOut->WriteStr("*="); + else if (result) + result = m_pOut->WriteStr("=\""); + if (result) + result = m_pOut->WriteStr(fName.get()); + if (wasTrans && result) + result = m_pOut->WriteStr("\x0D\x0A"); + else if (result) + result = m_pOut->WriteStr("\"\x0D\x0A"); + } + + return result; + +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +nsIImportMimeEncodeImpl::nsIImportMimeEncodeImpl() +{ + m_pOut = nullptr; + m_pEncode = nullptr; +} + +nsIImportMimeEncodeImpl::~nsIImportMimeEncodeImpl() +{ + if (m_pOut) + delete m_pOut; + if (m_pEncode) + delete m_pEncode; +} + +NS_IMPL_ISUPPORTS(nsIImportMimeEncodeImpl, nsIImportMimeEncode) + +NS_METHOD nsIImportMimeEncodeImpl::EncodeFile(nsIFile *inFile, nsIFile *outFile, const char *fileName, const char *mimeType) +{ + return Initialize(inFile, outFile, fileName, mimeType); +} + +NS_METHOD nsIImportMimeEncodeImpl::DoWork(bool *done, bool *_retval) +{ + if (done && _retval && m_pEncode) { + *_retval = m_pEncode->DoWork(done); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_METHOD nsIImportMimeEncodeImpl::NumBytesProcessed(int32_t *_retval) +{ + if (m_pEncode && _retval) + *_retval = m_pEncode->NumBytesProcessed(); + return NS_OK; +} + +NS_METHOD nsIImportMimeEncodeImpl::DoEncoding(bool *_retval) +{ + if (_retval && m_pEncode) { + bool done = false; + while (m_pEncode->DoWork(&done) && !done); + *_retval = done; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_METHOD nsIImportMimeEncodeImpl::Initialize(nsIFile *inFile, nsIFile *outFile, const char *fileName, const char *mimeType) +{ + delete m_pEncode; + delete m_pOut; + + m_pOut = new ImportOutFile(); + m_pOut->InitOutFile(outFile); + + m_pEncode = new nsImportMimeEncode(); + m_pEncode->EncodeFile(inFile, m_pOut, fileName, mimeType); + + return NS_OK; +} + diff --git a/mailnews/import/src/nsImportMimeEncode.h b/mailnews/import/src/nsImportMimeEncode.h new file mode 100644 index 000000000..1447d11c4 --- /dev/null +++ b/mailnews/import/src/nsImportMimeEncode.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef nsImportMimeEncode_h__ +#define nsImportMimeEncode_h__ + +#include "mozilla/Attributes.h" +#include "nsImportScanFile.h" +#include "ImportOutFile.h" +#include "nsImportEncodeScan.h" +#include "nsStringGlue.h" +#include "nsIImportMimeEncode.h" + + +// Content-Type: image/gif; name="blah.xyz" +// Content-Transfer-Encoding: base64 +// Content-Disposition: attachment; filename="blah.xyz" + +class nsImportMimeEncode : public nsImportEncodeScan { +public: + nsImportMimeEncode(); + ~nsImportMimeEncode(); + + void EncodeFile(nsIFile *pInFile, ImportOutFile *pOut, const char *pFileName, const char *pMimeType); + + bool DoWork(bool *pDone); + + long NumBytesProcessed(void) { long val = m_bytesProcessed; m_bytesProcessed = 0; return val;} + +protected: + void CleanUp(void); + bool SetUpEncode(void); + bool WriteFileName(nsCString& fName, bool wasTrans, const char *pTag); + bool TranslateFileName(nsCString& inFile, nsCString& outFile); + + + virtual bool ScanBuffer(bool *pDone) override; + + +protected: + nsCString m_fileName; + nsCOMPtr <nsIFile> m_pMimeFile; + ImportOutFile * m_pOut; + nsCString m_mimeType; + + int m_state; + long m_bytesProcessed; + uint8_t * m_pInputBuf; + bool m_appleSingle; + + // Actual encoding variables + int m_lineLen; +}; + + +class nsIImportMimeEncodeImpl : public nsIImportMimeEncode { +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIIMPORTMIMEENCODE + + nsIImportMimeEncodeImpl(); + +private: + virtual ~nsIImportMimeEncodeImpl(); + ImportOutFile * m_pOut; + nsImportMimeEncode * m_pEncode; +}; + + +#endif /* nsImportMimeEncode_h__ */ + diff --git a/mailnews/import/src/nsImportScanFile.cpp b/mailnews/import/src/nsImportScanFile.cpp new file mode 100644 index 000000000..c4eefef3b --- /dev/null +++ b/mailnews/import/src/nsImportScanFile.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsIFile.h" +#include "nsImportScanFile.h" +#include "ImportCharSet.h" + +nsImportScanFile::nsImportScanFile() +{ + m_allocated = false; + m_eof = false; + m_pBuf = nullptr; +} + +nsImportScanFile::~nsImportScanFile() +{ + if (m_allocated) + CleanUpScan(); +} + +void nsImportScanFile::InitScan(nsIInputStream *pInputStream, uint8_t * pBuf, uint32_t sz) +{ + m_pInputStream = pInputStream; + m_pBuf = pBuf; + m_bufSz = sz; + m_bytesInBuf = 0; + m_pos = 0; +} + +void nsImportScanFile::CleanUpScan(void) +{ + m_pInputStream = nullptr; + if (m_allocated) { + delete [] m_pBuf; + m_pBuf = NULL; + } +} + +void nsImportScanFile::ShiftBuffer(void) +{ + uint8_t * pTop; + uint8_t * pCurrent; + + if (m_pos < m_bytesInBuf) { + pTop = m_pBuf; + pCurrent = pTop + m_pos; + uint32_t cnt = m_bytesInBuf - m_pos; + while (cnt) { + *pTop = *pCurrent; + pTop++; pCurrent++; + cnt--; + } + } + + m_bytesInBuf -= m_pos; + m_pos = 0; +} + +bool nsImportScanFile::FillBufferFromFile(void) +{ + uint64_t available; + nsresult rv = m_pInputStream->Available(&available); + if (NS_FAILED(rv)) + return false; + + // Fill up a buffer and scan it + ShiftBuffer(); + + // Read in some more bytes + uint32_t cnt = m_bufSz - m_bytesInBuf; + // To distinguish from disk errors + // Check first for end of file? + // Set a done flag if true... + uint32_t read; + char *pBuf = (char *)m_pBuf; + pBuf += m_bytesInBuf; + rv = m_pInputStream->Read(pBuf, (int32_t) cnt, &read); + + if (NS_FAILED(rv)) + return false; + rv = m_pInputStream->Available(&available); + if (NS_FAILED(rv)) + m_eof = true; + + m_bytesInBuf += cnt; + return true; +} + +bool nsImportScanFile::Scan(bool *pDone) +{ + uint64_t available; + nsresult rv = m_pInputStream->Available(&available); + if (NS_FAILED(rv)) + { + if (m_pos < m_bytesInBuf) + ScanBuffer(pDone); + *pDone = true; + return true; + } + + // Fill up a buffer and scan it + if (!FillBufferFromFile()) + return false; + + return ScanBuffer(pDone); +} + +bool nsImportScanFile::ScanBuffer(bool *) +{ + return true; +} + + +bool nsImportScanFileLines::ScanBuffer(bool *pDone) +{ + // m_pos, m_bytesInBuf, m_eof, m_pBuf are relevant + + uint32_t pos = m_pos; + uint32_t max = m_bytesInBuf; + uint8_t * pChar = m_pBuf + pos; + uint32_t startPos; + + while (pos < max) { + if (m_needEol) { + // Find the next eol... + while ((pos < max) && (*pChar != ImportCharSet::cCRChar) && (*pChar != ImportCharSet::cLinefeedChar)) { + pos++; + pChar++; + } + m_pos = pos; + if (pos < max) + m_needEol = false; + if (pos == max) // need more buffer for an end of line + break; + } + // Skip past any eol characters + while ((pos < max) && ((*pChar == ImportCharSet::cCRChar) || (*pChar == ImportCharSet::cLinefeedChar))) { + pos++; + pChar++; + } + m_pos = pos; + if (pos == max) + break; + // Make sure we can find either the eof or the + // next end of line + startPos = pos; + while ((pos < max) && (*pChar != ImportCharSet::cCRChar) && (*pChar != ImportCharSet::cLinefeedChar)) { + pos++; + pChar++; + } + + // Is line too big for our buffer? + if ((pos == max) && !m_eof) { + if (!m_pos) { // line too big for our buffer + m_pos = pos; + m_needEol = true; + } + break; + } + + if (!ProcessLine(m_pBuf + startPos, pos - startPos, pDone)) { + return false; + } + m_pos = pos; + } + + return true; +} + diff --git a/mailnews/import/src/nsImportScanFile.h b/mailnews/import/src/nsImportScanFile.h new file mode 100644 index 000000000..abe5b1cdd --- /dev/null +++ b/mailnews/import/src/nsImportScanFile.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportScanFile_h__ +#define nsImportScanFile_h__ +#include "mozilla/Attributes.h" +#include "nsCOMPtr.h" +#include "nsIInputStream.h" + +class nsImportScanFile { +public: + nsImportScanFile(); + virtual ~nsImportScanFile(); + + void InitScan(nsIInputStream *pInputStream, uint8_t * pBuf, uint32_t sz); + + void CleanUpScan(void); + + virtual bool Scan(bool *pDone); + +protected: + void ShiftBuffer(void); + bool FillBufferFromFile(void); + virtual bool ScanBuffer(bool *pDone); + +protected: + nsCOMPtr <nsIInputStream> m_pInputStream; + uint8_t * m_pBuf; + uint32_t m_bufSz; + uint32_t m_bytesInBuf; + uint32_t m_pos; + bool m_eof; + bool m_allocated; +}; + +class nsImportScanFileLines : public nsImportScanFile { +public: + nsImportScanFileLines() {m_needEol = false;} + + void ResetLineScan(void) { m_needEol = false;} + + virtual bool ProcessLine(uint8_t * /* pLine */, uint32_t /* len */, bool * /* pDone */) {return true;} + +protected: + virtual bool ScanBuffer(bool *pDone) override; + + bool m_needEol; + +}; + + +#endif /* nsImportScanFile_h__ */ diff --git a/mailnews/import/src/nsImportService.cpp b/mailnews/import/src/nsImportService.cpp new file mode 100644 index 000000000..0013c1146 --- /dev/null +++ b/mailnews/import/src/nsImportService.cpp @@ -0,0 +1,583 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsICharsetConverterManager.h" +#include "nsIPlatformCharset.h" +#include "nsICharsetConverterManager.h" + +#include "nsStringGlue.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsMemory.h" +#include "nsIImportModule.h" +#include "nsIImportService.h" +#include "nsImportMailboxDescriptor.h" +#include "nsImportABDescriptor.h" +#include "nsIImportGeneric.h" +#include "nsImportFieldMap.h" +#include "nsICategoryManager.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "plstr.h" +#include "prmem.h" +#include "nsMsgCompCID.h" +#include "nsThreadUtils.h" +#include "nsIEditor.h" +#include "ImportDebug.h" +#include "nsImportService.h" +#include "nsImportStringBundle.h" +#include "nsCRTGlue.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" +#include "nsIArray.h" +#include "nsIMsgSend.h" +#include "nsMsgUtils.h" + +PRLogModuleInfo *IMPORTLOGMODULE = nullptr; + +static nsIImportService * gImportService = nullptr; +static const char * kWhitespace = "\b\t\r\n "; + + +//////////////////////////////////////////////////////////////////////// + + +nsImportService::nsImportService() : m_pModules(nullptr) +{ + // Init logging module. + if (!IMPORTLOGMODULE) + IMPORTLOGMODULE = PR_NewLogModule("IMPORT"); + IMPORT_LOG0("* nsImport Service Created\n"); + + m_didDiscovery = false; + m_pDecoder = nullptr; + m_pEncoder = nullptr; + + nsresult rv = nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle)); + if (NS_FAILED(rv)) + IMPORT_LOG0("Failed to get string bundle for Importing Mail"); +} + + +nsImportService::~nsImportService() +{ + NS_IF_RELEASE(m_pDecoder); + NS_IF_RELEASE(m_pEncoder); + + gImportService = nullptr; + + if (m_pModules != nullptr) + delete m_pModules; + + IMPORT_LOG0("* nsImport Service Deleted\n"); +} + + + +NS_IMPL_ISUPPORTS(nsImportService, nsIImportService) + + +NS_IMETHODIMP nsImportService::DiscoverModules(void) +{ + m_didDiscovery = false; + return DoDiscover(); +} + +NS_IMETHODIMP nsImportService::CreateNewFieldMap(nsIImportFieldMap **_retval) +{ + return nsImportFieldMap::Create(m_stringBundle, nullptr, NS_GET_IID(nsIImportFieldMap), (void**)_retval); +} + +NS_IMETHODIMP nsImportService::CreateNewMailboxDescriptor(nsIImportMailboxDescriptor **_retval) +{ + return nsImportMailboxDescriptor::Create(nullptr, NS_GET_IID(nsIImportMailboxDescriptor), (void**)_retval); +} + +NS_IMETHODIMP nsImportService::CreateNewABDescriptor(nsIImportABDescriptor **_retval) +{ + return nsImportABDescriptor::Create(nullptr, NS_GET_IID(nsIImportABDescriptor), (void**)_retval); +} + +extern nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric); + +NS_IMETHODIMP nsImportService::CreateNewGenericMail(nsIImportGeneric **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! _retval) + return NS_ERROR_NULL_POINTER; + + return NS_NewGenericMail(_retval); +} + +extern nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric); + +NS_IMETHODIMP nsImportService::CreateNewGenericAddressBooks(nsIImportGeneric **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! _retval) + return NS_ERROR_NULL_POINTER; + + return NS_NewGenericAddressBooks(_retval); +} + + +NS_IMETHODIMP nsImportService::GetModuleCount(const char *filter, int32_t *_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! _retval) + return NS_ERROR_NULL_POINTER; + + DoDiscover(); + + if (m_pModules != nullptr) { + ImportModuleDesc * pDesc; + int32_t count = 0; + for (int32_t i = 0; i < m_pModules->GetCount(); i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (pDesc->SupportsThings(filter)) + count++; + } + *_retval = count; + } + else + *_retval = 0; + + return NS_OK; +} + +NS_IMETHODIMP nsImportService::GetModuleWithCID(const nsCID& cid, nsIImportModule **ppModule) +{ + NS_PRECONDITION(ppModule != nullptr, "null ptr"); + if (!ppModule) + return NS_ERROR_NULL_POINTER; + + *ppModule = nullptr; + nsresult rv = DoDiscover(); + if (NS_FAILED(rv)) + return rv; + if (m_pModules == nullptr) + return NS_ERROR_FAILURE; + int32_t cnt = m_pModules->GetCount(); + ImportModuleDesc *pDesc; + for (int32_t i = 0; i < cnt; i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (!pDesc) + return NS_ERROR_FAILURE; + if (pDesc->GetCID().Equals(cid)) { + *ppModule = pDesc->GetModule(); + + IMPORT_LOG0("* nsImportService::GetSpecificModule - attempted to load module\n"); + + if (*ppModule == nullptr) + return NS_ERROR_FAILURE; + return NS_OK; + } + } + + IMPORT_LOG0("* nsImportService::GetSpecificModule - module not found\n"); + + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP nsImportService::GetModuleInfo(const char *filter, int32_t index, char16_t **name, char16_t **moduleDescription) +{ + NS_PRECONDITION(name != nullptr, "null ptr"); + NS_PRECONDITION(moduleDescription != nullptr, "null ptr"); + if (!name || !moduleDescription) + return NS_ERROR_NULL_POINTER; + + *name = nullptr; + *moduleDescription = nullptr; + + DoDiscover(); + if (!m_pModules) + return NS_ERROR_FAILURE; + + if ((index < 0) || (index >= m_pModules->GetCount())) + return NS_ERROR_FAILURE; + + ImportModuleDesc * pDesc; + int32_t count = 0; + for (int32_t i = 0; i < m_pModules->GetCount(); i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (pDesc->SupportsThings(filter)) { + if (count == index) { + *name = NS_strdup(pDesc->GetName()); + *moduleDescription = NS_strdup(pDesc->GetDescription()); + return NS_OK; + } + else + count++; + } + } + + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsImportService::GetModuleName(const char *filter, int32_t index, char16_t **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = nullptr; + + DoDiscover(); + if (!m_pModules) + return NS_ERROR_FAILURE; + + if ((index < 0) || (index >= m_pModules->GetCount())) + return NS_ERROR_FAILURE; + + ImportModuleDesc * pDesc; + int32_t count = 0; + for (int32_t i = 0; i < m_pModules->GetCount(); i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (pDesc->SupportsThings(filter)) { + if (count == index) { + *_retval = NS_strdup(pDesc->GetName()); + return NS_OK; + } + else + count++; + } + } + + return NS_ERROR_FAILURE; +} + + +NS_IMETHODIMP nsImportService::GetModuleDescription(const char *filter, int32_t index, char16_t **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + + *_retval = nullptr; + + DoDiscover(); + if (!m_pModules) + return NS_ERROR_FAILURE; + + if ((index < 0) || (index >= m_pModules->GetCount())) + return NS_ERROR_FAILURE; + + ImportModuleDesc * pDesc; + int32_t count = 0; + for (int32_t i = 0; i < m_pModules->GetCount(); i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (pDesc->SupportsThings(filter)) { + if (count == index) { + *_retval = NS_strdup(pDesc->GetDescription()); + return NS_OK; + } + else + count++; + } + } + + return NS_ERROR_FAILURE; +} + +class nsProxySendRunnable : public mozilla::Runnable +{ +public: + nsProxySendRunnable(nsIMsgIdentity *aIdentity, + nsIMsgCompFields *aMsgFields, + const char *attachment1_type, + const nsACString &attachment1_body, + bool aIsDraft, + nsIArray *aLoadedAttachments, + nsIArray *aEmbeddedAttachments, + nsIMsgSendListener *aListener); + NS_DECL_NSIRUNNABLE +private: + nsCOMPtr<nsIMsgIdentity> m_identity; + nsCOMPtr<nsIMsgCompFields> m_compFields; + bool m_isDraft; + nsCString m_bodyType; + nsCString m_body; + nsCOMPtr<nsIArray> m_loadedAttachments; + nsCOMPtr<nsIArray> m_embeddedAttachments; + nsCOMPtr<nsIMsgSendListener> m_listener; + +}; + +nsProxySendRunnable::nsProxySendRunnable(nsIMsgIdentity *aIdentity, + nsIMsgCompFields *aMsgFields, + const char *aBodyType, + const nsACString &aBody, + bool aIsDraft, + nsIArray *aLoadedAttachments, + nsIArray *aEmbeddedAttachments, + nsIMsgSendListener *aListener) : + m_identity(aIdentity), m_compFields(aMsgFields), + m_isDraft(aIsDraft), m_bodyType(aBodyType), + m_body(aBody), m_loadedAttachments(aLoadedAttachments), + m_embeddedAttachments(aEmbeddedAttachments), + m_listener(aListener) +{ +} + +NS_IMETHODIMP nsProxySendRunnable::Run() +{ + nsresult rv; + nsCOMPtr<nsIMsgSend> msgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return msgSend->CreateRFC822Message(m_identity, m_compFields, + m_bodyType.get(), m_body, + m_isDraft, m_loadedAttachments, + m_embeddedAttachments, + m_listener); +} + + +NS_IMETHODIMP +nsImportService::CreateRFC822Message(nsIMsgIdentity *aIdentity, + nsIMsgCompFields *aMsgFields, + const char *aBodyType, + const nsACString &aBody, + bool aIsDraft, + nsIArray *aLoadedAttachments, + nsIArray *aEmbeddedAttachments, + nsIMsgSendListener *aListener) +{ + RefPtr<nsProxySendRunnable> runnable = + new nsProxySendRunnable(aIdentity, + aMsgFields, + aBodyType, + aBody, + aIsDraft, + aLoadedAttachments, + aEmbeddedAttachments, + aListener); + // invoke the callback + return NS_DispatchToMainThread(runnable); +} + +NS_IMETHODIMP nsImportService::GetModule(const char *filter, int32_t index, nsIImportModule **_retval) +{ + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (!_retval) + return NS_ERROR_NULL_POINTER; + *_retval = nullptr; + + DoDiscover(); + if (!m_pModules) + return NS_ERROR_FAILURE; + + if ((index < 0) || (index >= m_pModules->GetCount())) + return NS_ERROR_FAILURE; + + ImportModuleDesc * pDesc; + int32_t count = 0; + for (int32_t i = 0; i < m_pModules->GetCount(); i++) { + pDesc = m_pModules->GetModuleDesc(i); + if (pDesc->SupportsThings(filter)) { + if (count == index) { + *_retval = pDesc->GetModule(); + break; + } + else + count++; + } + } + if (! (*_retval)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + + +nsresult nsImportService::DoDiscover(void) +{ + if (m_didDiscovery) + return NS_OK; + + if (m_pModules != nullptr) + m_pModules->ClearList(); + + nsresult rv; + + nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISimpleEnumerator> e; + rv = catMan->EnumerateCategory("mailnewsimport", getter_AddRefs(e)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISupports> supports; + nsCOMPtr<nsISupportsCString> contractid; + rv = e->GetNext(getter_AddRefs(supports)); + while (NS_SUCCEEDED(rv) && supports) + { + contractid = do_QueryInterface(supports); + if (!contractid) + break; + + nsCString contractIdStr; + contractid->ToString(getter_Copies(contractIdStr)); + nsCString supportsStr; + rv = catMan->GetCategoryEntry("mailnewsimport", contractIdStr.get(), getter_Copies(supportsStr)); + if (NS_SUCCEEDED(rv)) + LoadModuleInfo(contractIdStr.get(), supportsStr.get()); + rv = e->GetNext(getter_AddRefs(supports)); + } + + m_didDiscovery = true; + + return NS_OK; +} + +nsresult nsImportService::LoadModuleInfo(const char *pClsId, const char *pSupports) +{ + if (!pClsId || !pSupports) + return NS_OK; + + if (m_pModules == nullptr) + m_pModules = new nsImportModuleList(); + + // load the component and get all of the info we need from it.... + // then call AddModule + nsresult rv; + + nsCID clsId; + clsId.Parse(pClsId); + nsIImportModule * module; + rv = CallCreateInstance(clsId, &module); + if (NS_FAILED(rv)) return rv; + + nsString theTitle; + nsString theDescription; + rv = module->GetName(getter_Copies(theTitle)); + if (NS_FAILED(rv)) + theTitle.AssignLiteral("Unknown"); + + rv = module->GetDescription(getter_Copies(theDescription)); + if (NS_FAILED(rv)) + theDescription.AssignLiteral("Unknown description"); + + // call the module to get the info we need + m_pModules->AddModule(clsId, pSupports, theTitle.get(), theDescription.get()); + + module->Release(); + + return NS_OK; +} + + +nsIImportModule *ImportModuleDesc::GetModule(bool keepLoaded) +{ + if (m_pModule) + { + m_pModule->AddRef(); + return m_pModule; + } + + nsresult rv; + rv = CallCreateInstance(m_cid, &m_pModule); + if (NS_FAILED(rv)) + { + m_pModule = nullptr; + return nullptr; + } + + if (keepLoaded) + { + m_pModule->AddRef(); + return m_pModule; + } + else + { + nsIImportModule *pModule = m_pModule; + m_pModule = nullptr; + return pModule; + } +} + +void ImportModuleDesc::ReleaseModule(void) +{ + if (m_pModule) + { + m_pModule->Release(); + m_pModule = nullptr; + } +} + +bool ImportModuleDesc::SupportsThings(const char *pThings) +{ + if (!pThings || !*pThings) + return true; + + nsCString thing(pThings); + nsCString item; + int32_t idx; + + while ((idx = thing.FindChar(',')) != -1) + { + item = StringHead(thing, idx); + item.Trim(kWhitespace); + ToLowerCase(item); + if (item.Length() && (m_supports.Find(item) == -1)) + return false; + thing = Substring(thing, idx + 1); + } + thing.Trim(kWhitespace); + ToLowerCase(thing); + return thing.IsEmpty() || (m_supports.Find(thing) != -1); +} + +void nsImportModuleList::ClearList(void) +{ + if (m_pList) + { + for (int i = 0; i < m_count; i++) + { + delete m_pList[i]; + m_pList[i] = nullptr; + } + m_count = 0; + delete [] m_pList; + m_pList = nullptr; + m_alloc = 0; + } + +} + +void nsImportModuleList::AddModule(const nsCID& cid, const char *pSupports, const char16_t *pName, const char16_t *pDesc) +{ + if (!m_pList) + { + m_alloc = 10; + m_pList = new ImportModuleDesc *[m_alloc]; + m_count = 0; + memset(m_pList, 0, sizeof(ImportModuleDesc *) * m_alloc); + } + + if (m_count == m_alloc) + { + ImportModuleDesc **pList = new ImportModuleDesc *[m_alloc + 10]; + memset(&(pList[m_alloc]), 0, sizeof(ImportModuleDesc *) * 10); + memcpy(pList, m_pList, sizeof(ImportModuleDesc *) * m_alloc); + for(int i = 0; i < m_count; i++) + delete m_pList[i]; + delete [] m_pList; + m_pList = pList; + m_alloc += 10; + } + + m_pList[m_count] = new ImportModuleDesc(); + m_pList[m_count]->SetCID(cid); + m_pList[m_count]->SetSupports(pSupports); + m_pList[m_count]->SetName(pName); + m_pList[m_count]->SetDescription(pDesc); + + m_count++; +#ifdef IMPORT_DEBUG + IMPORT_LOG3("* nsImportService registered import module: %s, %s, %s\n", NS_LossyConvertUTF16toASCII(pName).get(), NS_LossyConvertUTF16toASCII(pDesc).get(), pSupports); +#endif +} + diff --git a/mailnews/import/src/nsImportService.h b/mailnews/import/src/nsImportService.h new file mode 100644 index 000000000..889cfa6c4 --- /dev/null +++ b/mailnews/import/src/nsImportService.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportService_h__ +#define nsImportService_h__ + +#include "nsICharsetConverterManager.h" + +#include "nsStringGlue.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsMemory.h" +#include "nsIImportModule.h" +#include "nsIImportService.h" +#include "nsICategoryManager.h" +#include "nsIStringBundle.h" + +class nsImportModuleList; + +class nsImportService : public nsIImportService +{ +public: + + nsImportService(); + + NS_DECL_THREADSAFE_ISUPPORTS + + NS_DECL_NSIIMPORTSERVICE + +private: + virtual ~nsImportService(); + nsresult LoadModuleInfo(const char*pClsId, const char *pSupports); + nsresult DoDiscover(void); + +private: + nsImportModuleList * m_pModules; + bool m_didDiscovery; + nsCString m_sysCharset; + nsIUnicodeDecoder * m_pDecoder; + nsIUnicodeEncoder * m_pEncoder; + nsCOMPtr<nsIStringBundle> m_stringBundle; +}; + +class ImportModuleDesc { +public: + ImportModuleDesc() { m_pModule = nullptr;} + ~ImportModuleDesc() { ReleaseModule(); } + + void SetCID(const nsCID& cid) { m_cid = cid;} + void SetName(const char16_t *pName) { m_name = pName;} + void SetDescription(const char16_t *pDesc) { m_description = pDesc;} + void SetSupports(const char *pSupports) { m_supports = pSupports;} + + nsCID GetCID(void) { return m_cid;} + const char16_t *GetName(void) { return m_name.get();} + const char16_t *GetDescription(void) { return m_description.get();} + const char * GetSupports(void) { return m_supports.get();} + + nsIImportModule * GetModule(bool keepLoaded = false); // Adds ref + void ReleaseModule(void); + + bool SupportsThings(const char *pThings); + +private: + nsCID m_cid; + nsString m_name; + nsString m_description; + nsCString m_supports; + nsIImportModule *m_pModule; +}; + +class nsImportModuleList { +public: + nsImportModuleList() { m_pList = nullptr; m_alloc = 0; m_count = 0;} + ~nsImportModuleList() { ClearList(); } + + void AddModule(const nsCID& cid, const char *pSupports, const char16_t *pName, const char16_t *pDesc); + + void ClearList(void); + + int32_t GetCount(void) { return m_count;} + + ImportModuleDesc * GetModuleDesc(int32_t idx) + { if ((idx < 0) || (idx >= m_count)) return nullptr; else return m_pList[idx];} + +private: + +private: + ImportModuleDesc ** m_pList; + int32_t m_alloc; + int32_t m_count; +}; + +#endif // nsImportService_h__ diff --git a/mailnews/import/src/nsImportStringBundle.cpp b/mailnews/import/src/nsImportStringBundle.cpp new file mode 100644 index 000000000..3adb6655a --- /dev/null +++ b/mailnews/import/src/nsImportStringBundle.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "prprf.h" +#include "prmem.h" +#include "nsCOMPtr.h" +#include "nsIStringBundle.h" +#include "nsImportStringBundle.h" +#include "nsIServiceManager.h" +#include "nsIURI.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/Services.h" + +nsresult nsImportStringBundle::GetStringBundle(const char *aPropertyURL, + nsIStringBundle **aBundle) +{ + nsresult rv; + + nsCOMPtr<nsIStringBundleService> sBundleService = + mozilla::services::GetStringBundleService(); + NS_ENSURE_TRUE(sBundleService, NS_ERROR_UNEXPECTED); + rv = sBundleService->CreateBundle(aPropertyURL, aBundle); + + return rv; +} + +void nsImportStringBundle::GetStringByID(int32_t aStringID, + nsIStringBundle *aBundle, + nsString &aResult) +{ + aResult.Adopt(GetStringByID(aStringID, aBundle)); +} + +char16_t *nsImportStringBundle::GetStringByID(int32_t aStringID, + nsIStringBundle *aBundle) +{ + if (aBundle) + { + char16_t *ptrv = nullptr; + nsresult rv = aBundle->GetStringFromID(aStringID, &ptrv); + + if (NS_SUCCEEDED(rv) && ptrv) + return ptrv; + } + + nsString resultString(NS_LITERAL_STRING("[StringID ")); + resultString.AppendInt(aStringID); + resultString.AppendLiteral("?]"); + + return ToNewUnicode(resultString); +} + +void nsImportStringBundle::GetStringByName(const char *aName, + nsIStringBundle *aBundle, + nsString &aResult) +{ + aResult.Adopt(GetStringByName(aName, aBundle)); +} + +char16_t *nsImportStringBundle::GetStringByName(const char *aName, + nsIStringBundle *aBundle) +{ + if (aBundle) + { + char16_t *ptrv = nullptr; + nsresult rv = aBundle->GetStringFromName( + NS_ConvertUTF8toUTF16(aName).get(), &ptrv); + + if (NS_SUCCEEDED(rv) && ptrv) + return ptrv; + } + + nsString resultString(NS_LITERAL_STRING("[StringName ")); + resultString.Append(NS_ConvertUTF8toUTF16(aName).get()); + resultString.AppendLiteral("?]"); + + return ToNewUnicode(resultString); +} diff --git a/mailnews/import/src/nsImportStringBundle.h b/mailnews/import/src/nsImportStringBundle.h new file mode 100644 index 000000000..c9db012e6 --- /dev/null +++ b/mailnews/import/src/nsImportStringBundle.h @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsImportStringBundle_H__ +#define _nsImportStringBundle_H__ + +#include "nsStringGlue.h" + +class nsIStringBundle; + +class nsImportStringBundle +{ +public: + static char16_t* GetStringByID(int32_t aStringID, + nsIStringBundle *aBundle = nullptr); + static void GetStringByID(int32_t aStringID, + nsIStringBundle *aBundle, + nsString &aResult); + static char16_t* GetStringByName(const char *aName, + nsIStringBundle *aBundle = nullptr); + static void GetStringByName(const char *aName, + nsIStringBundle *aBundle, + nsString &aResult); + static nsresult GetStringBundle(const char *aPropertyURL, + nsIStringBundle **aBundle); +}; + +#define IMPORT_MSGS_URL "chrome://messenger/locale/importMsgs.properties" + + +#define IMPORT_NO_ADDRBOOKS 2000 +#define IMPORT_ERROR_AB_NOTINITIALIZED 2001 +#define IMPORT_ERROR_AB_NOTHREAD 2002 +#define IMPORT_ERROR_GETABOOK 2003 +#define IMPORT_NO_MAILBOXES 2004 +#define IMPORT_ERROR_MB_NOTINITIALIZED 2005 +#define IMPORT_ERROR_MB_NOTHREAD 2006 +#define IMPORT_ERROR_MB_NOPROXY 2007 +#define IMPORT_ERROR_MB_FINDCHILD 2008 +#define IMPORT_ERROR_MB_CREATE 2009 +#define IMPORT_ERROR_MB_NODESTFOLDER 2010 + +#define IMPORT_FIELD_DESC_START 2100 +#define IMPORT_FIELD_DESC_END 2136 + + +#endif /* _nsImportStringBundle_H__ */ diff --git a/mailnews/import/src/nsImportTranslator.cpp b/mailnews/import/src/nsImportTranslator.cpp new file mode 100644 index 000000000..beec8b93a --- /dev/null +++ b/mailnews/import/src/nsImportTranslator.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ImportOutFile.h" +#include "nsImportTranslator.h" + +#include "ImportCharSet.h" + + +bool nsImportTranslator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed) +{ + if (pProcessed) + *pProcessed = inLen; + return (pOutFile->WriteData(pIn, inLen)); +} + +void CMHTranslator::ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut) +{ + while (inLen) { + if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) || + (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '*') || (*pIn == '\'') || + (*pIn == '%')) { + // needs to be encode as %hex val + *pOut = '%'; pOut++; + ImportCharSet::ByteToHex(*pIn, pOut); + pOut += 2; + } + else { + *pOut = *pIn; + pOut++; + } + pIn++; inLen--; + } + *pOut = 0; +} + +bool CMHTranslator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed) +{ + uint8_t hex[2]; + while (inLen) { + if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) || + (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '*') || (*pIn == '\'') || + (*pIn == '%')) { + // needs to be encode as %hex val + if (!pOutFile->WriteByte('%')) + return false; + ImportCharSet::ByteToHex(*pIn, hex); + if (!pOutFile->WriteData(hex, 2)) + return false; + } + else { + if (!pOutFile->WriteByte(*pIn)) + return false; + } + pIn++; inLen--; + } + + if (pProcessed) + *pProcessed = inLen; + + return true; +} + + +bool C2047Translator::ConvertToFileQ(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed) +{ + if (!inLen) + return true; + + int maxLineLen = 64; + int curLineLen = m_startLen; + bool startLine = true; + + uint8_t hex[2]; + while (inLen) { + if (startLine) { + if (!pOutFile->WriteStr(" =?")) + return false; + if (!pOutFile->WriteStr(m_charset.get())) + return false; + if (!pOutFile->WriteStr("?q?")) + return false; + curLineLen += (6 + m_charset.Length()); + startLine = false; + } + + if (!ImportCharSet::IsUSAscii(*pIn) || ImportCharSet::Is822SpecialChar(*pIn) || ImportCharSet::Is822CtlChar(*pIn) || + (*pIn == ImportCharSet::cSpaceChar) || (*pIn == '?') || (*pIn == '=')) { + // needs to be encode as =hex val + if (!pOutFile->WriteByte('=')) + return false; + ImportCharSet::ByteToHex(*pIn, hex); + if (!pOutFile->WriteData(hex, 2)) + return false; + curLineLen += 3; + } + else { + if (!pOutFile->WriteByte(*pIn)) + return false; + curLineLen++; + } + pIn++; inLen--; + if (curLineLen > maxLineLen) { + if (!pOutFile->WriteStr("?=")) + return false; + if (inLen) { + if (!pOutFile->WriteStr("\x0D\x0A ")) + return false; + } + + startLine = true; + curLineLen = 0; + } + } + + if (!startLine) { + // end the encoding! + if (!pOutFile->WriteStr("?=")) + return false; + } + + if (pProcessed) + *pProcessed = inLen; + + return true; +} + +bool C2047Translator::ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed) +{ + if (m_useQuotedPrintable) + return ConvertToFileQ(pIn, inLen, pOutFile, pProcessed); + + if (!inLen) + return true; + + int maxLineLen = 64; + int curLineLen = m_startLen; + bool startLine = true; + int encodeMax; + uint8_t * pEncoded = new uint8_t[maxLineLen * 2]; + + while (inLen) { + if (startLine) { + if (!pOutFile->WriteStr(" =?")) { + delete [] pEncoded; + return false; + } + if (!pOutFile->WriteStr(m_charset.get())) { + delete [] pEncoded; + return false; + } + if (!pOutFile->WriteStr("?b?")) { + delete [] pEncoded; + return false; + } + curLineLen += (6 + m_charset.Length()); + startLine = false; + } + encodeMax = maxLineLen - curLineLen; + encodeMax *= 3; + encodeMax /= 4; + if ((uint32_t)encodeMax > inLen) + encodeMax = (int)inLen; + + // encode the line, end the line + // then continue. Update curLineLen, pIn, startLine, and inLen + UMimeEncode::ConvertBuffer(pIn, encodeMax, pEncoded, maxLineLen, maxLineLen, "\x0D\x0A"); + + if (!pOutFile->WriteStr((const char *)pEncoded)) { + delete [] pEncoded; + return false; + } + + pIn += encodeMax; + inLen -= encodeMax; + startLine = true; + curLineLen = 0; + if (!pOutFile->WriteStr("?=")) { + delete [] pEncoded; + return false; + } + if (inLen) { + if (!pOutFile->WriteStr("\x0D\x0A ")) { + delete [] pEncoded; + return false; + } + } + } + + delete [] pEncoded; + + if (pProcessed) + *pProcessed = inLen; + + return true; +} + + +uint32_t UMimeEncode::GetBufferSize(uint32_t inBytes) +{ + // it takes 4 base64 bytes to represent 3 regular bytes + inBytes += 3; + inBytes /= 3; + inBytes *= 4; + // This should be plenty, but just to be safe + inBytes += 4; + + // now allow for end of line characters + inBytes += ((inBytes + 39) / 40) * 4; + + return inBytes; +} + +static uint8_t gBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +uint32_t UMimeEncode::ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut, uint32_t maxLen, uint32_t firstLineLen, const char * pEolStr) +{ + + uint32_t pos = 0; + uint32_t len = 0; + uint32_t lineLen = 0; + uint32_t maxLine = firstLineLen; + int eolLen = 0; + if (pEolStr) + eolLen = strlen(pEolStr); + + while ((pos + 2) < inLen) { + // Encode 3 bytes + *pOut = gBase64[*pIn >> 2]; + pOut++; len++; lineLen++; + *pOut = gBase64[(((*pIn) & 0x3)<< 4) | (((*(pIn + 1)) & 0xF0) >> 4)]; + pIn++; pOut++; len++; lineLen++; + *pOut = gBase64[(((*pIn) & 0xF) << 2) | (((*(pIn + 1)) & 0xC0) >>6)]; + pIn++; pOut++; len++; lineLen++; + *pOut = gBase64[(*pIn) & 0x3F]; + pIn++; pOut++; len++; lineLen++; + pos += 3; + if (lineLen >= maxLine) { + lineLen = 0; + maxLine = maxLen; + if (pEolStr) { + memcpy(pOut, pEolStr, eolLen); + pOut += eolLen; + len += eolLen; + } + } + } + + if ((pos < inLen) && ((lineLen + 3) > maxLine)) { + lineLen = 0; + maxLine = maxLen; + if (pEolStr) { + memcpy(pOut, pEolStr, eolLen); + pOut += eolLen; + len += eolLen; + } + } + + if (pos < inLen) { + // Get the last few bytes! + *pOut = gBase64[*pIn >> 2]; + pOut++; len++; + pos++; + if (pos < inLen) { + *pOut = gBase64[(((*pIn) & 0x3)<< 4) | (((*(pIn + 1)) & 0xF0) >> 4)]; + pIn++; pOut++; pos++; len++; + if (pos < inLen) { + // Should be dead code!! (Then why is it here doofus?) + *pOut = gBase64[(((*pIn) & 0xF) << 2) | (((*(pIn + 1)) & 0xC0) >>6)]; + pIn++; pOut++; len++; + *pOut = gBase64[(*pIn) & 0x3F]; + pos++; pOut++; len++; + } + else { + *pOut = gBase64[(((*pIn) & 0xF) << 2)]; + pOut++; len++; + *pOut = '='; + pOut++; len++; + } + } + else { + *pOut = gBase64[(((*pIn) & 0x3)<< 4)]; + pOut++; len++; + *pOut = '='; + pOut++; len++; + *pOut = '='; + pOut++; len++; + } + } + + *pOut = 0; + + return len; +} diff --git a/mailnews/import/src/nsImportTranslator.h b/mailnews/import/src/nsImportTranslator.h new file mode 100644 index 000000000..998616063 --- /dev/null +++ b/mailnews/import/src/nsImportTranslator.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsImportTranslator_h___ +#define nsImportTranslator_h___ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsCOMPtr.h" + +class ImportOutFile; + +class UMimeEncode { +public: + static uint32_t GetBufferSize(uint32_t inByes); + static uint32_t ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t *pOut, uint32_t maxLen = 72, uint32_t firstLineLen = 72, const char * pEolStr = nullptr); +}; + + +class nsImportTranslator { +public: + virtual ~nsImportTranslator() {} + virtual bool Supports8bitEncoding(void) { return false;} + virtual uint32_t GetMaxBufferSize(uint32_t inLen) { return inLen + 1;} + virtual void ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut) { memcpy(pOut, pIn, inLen); pOut[inLen] = 0;} + virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr); + virtual bool FinishConvertToFile(ImportOutFile * /* pOutFile */) { return true;} + + virtual void GetCharset(nsCString& charSet) { charSet = "us-ascii";} + virtual void GetLanguage(nsCString& lang) { lang = "en";} + virtual void GetEncoding(nsCString& encoding) { encoding.Truncate();} +}; + +// Specialized encoder, not a vaild language translator, used for Mime headers. +// rfc2231 +class CMHTranslator : public nsImportTranslator { +public: + virtual uint32_t GetMaxBufferSize(uint32_t inLen) override { return (inLen * 3) + 1;} + virtual void ConvertBuffer(const uint8_t * pIn, uint32_t inLen, uint8_t * pOut) override; + virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr) override; +}; + +// Specialized encoder, not a vaild language translator, used for mail headers +// rfc2047 +class C2047Translator : public nsImportTranslator { +public: + virtual ~C2047Translator() {} + + C2047Translator(const char *pCharset, uint32_t headerLen) { m_charset = pCharset; m_startLen = headerLen; m_useQuotedPrintable = false;} + + void SetUseQuotedPrintable(void) { m_useQuotedPrintable = true;} + + virtual bool ConvertToFile(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed = nullptr) override; + bool ConvertToFileQ(const uint8_t * pIn, uint32_t inLen, ImportOutFile *pOutFile, uint32_t *pProcessed); + +protected: + bool m_useQuotedPrintable; + nsCString m_charset; + uint32_t m_startLen; +}; + +#endif /* nsImportTranslator_h__ */ + |