diff options
Diffstat (limited to 'mailnews/import/text')
-rw-r--r-- | mailnews/import/text/src/TextDebugLog.h | 21 | ||||
-rw-r--r-- | mailnews/import/text/src/moz.build | 16 | ||||
-rw-r--r-- | mailnews/import/text/src/nsTextAddress.cpp | 470 | ||||
-rw-r--r-- | mailnews/import/text/src/nsTextAddress.h | 57 | ||||
-rw-r--r-- | mailnews/import/text/src/nsTextImport.cpp | 714 | ||||
-rw-r--r-- | mailnews/import/text/src/nsTextImport.h | 39 |
6 files changed, 1317 insertions, 0 deletions
diff --git a/mailnews/import/text/src/TextDebugLog.h b/mailnews/import/text/src/TextDebugLog.h new file mode 100644 index 000000000..3f9bf1ec4 --- /dev/null +++ b/mailnews/import/text/src/TextDebugLog.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TextDebugLog_h___ +#define TextDebugLog_h___ + +// Use PR_LOG for logging. +#include "mozilla/Logging.h" +extern PRLogModuleInfo *TEXTIMPORTLOGMODULE; // Logging module + +#define IMPORT_LOG0(x) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x)) +#define IMPORT_LOG1(x, y) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y)) +#define IMPORT_LOG2(x, y, z) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (x, y, z)) +#define IMPORT_LOG3(a, b, c, d) MOZ_LOG(TEXTIMPORTLOGMODULE, mozilla::LogLevel::Debug, (a, b, c, d)) + + + +#endif /* TextDebugLog_h___ */ diff --git a/mailnews/import/text/src/moz.build b/mailnews/import/text/src/moz.build new file mode 100644 index 000000000..1bdbb07c9 --- /dev/null +++ b/mailnews/import/text/src/moz.build @@ -0,0 +1,16 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'nsTextAddress.cpp', + 'nsTextImport.cpp', +] + +FINAL_LIBRARY = 'import' + +LOCAL_INCLUDES += [ + '../../src' +] + diff --git a/mailnews/import/text/src/nsTextAddress.cpp b/mailnews/import/text/src/nsTextAddress.cpp new file mode 100644 index 000000000..8a22498d2 --- /dev/null +++ b/mailnews/import/text/src/nsTextAddress.cpp @@ -0,0 +1,470 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTextAddress.h" +#include "nsIAddrDatabase.h" +#include "nsNativeCharsetUtils.h" +#include "nsIFile.h" +#include "nsIInputStream.h" +#include "nsILineInputStream.h" +#include "nsNetUtil.h" +#include "nsMsgI18N.h" +#include "nsMsgUtils.h" +#include "mdb.h" +#include "nsIConverterInputStream.h" +#include "nsIUnicharLineInputStream.h" +#include "nsMsgUtils.h" + +#include "TextDebugLog.h" +#include "plstr.h" +#include "msgCore.h" + +#ifndef MOZILLA_INTERNAL_API +#include "nsMsgI18N.h" +#define NS_CopyNativeToUnicode(source, dest) \ + nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), source, dest) +#endif + +#define kWhitespace " \t\b\r\n" + +nsTextAddress::nsTextAddress() +{ + m_database = nullptr; + m_fieldMap = nullptr; + m_LFCount = 0; + m_CRCount = 0; +} + +nsTextAddress::~nsTextAddress() +{ + NS_IF_RELEASE(m_database); + NS_IF_RELEASE(m_fieldMap); +} + +nsresult nsTextAddress::GetUnicharLineStreamForFile(nsIFile *aFile, + nsIInputStream *aInputStream, + nsIUnicharLineInputStream **aStream) +{ + nsAutoCString charset; + nsresult rv = MsgDetectCharsetFromFile(aFile, charset); + if (NS_FAILED(rv)) { + charset.Assign(nsMsgI18NFileSystemCharset()); + } + + nsCOMPtr<nsIConverterInputStream> converterStream = + do_CreateInstance("@mozilla.org/intl/converter-input-stream;1", &rv); + if (NS_SUCCEEDED(rv)) { + rv = converterStream->Init(aInputStream, + charset.get(), + 8192, + nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER); + } + + return CallQueryInterface(converterStream, aStream); +} + +nsresult nsTextAddress::ImportAddresses(bool *pAbort, const char16_t *pName, nsIFile *pSrc, nsIAddrDatabase *pDb, nsIImportFieldMap *fieldMap, nsString& errors, uint32_t *pProgress) +{ + // Open the source file for reading, read each line and process it! + NS_IF_RELEASE(m_database); + NS_IF_RELEASE(m_fieldMap); + m_database = pDb; + m_fieldMap = fieldMap; + NS_ADDREF(m_fieldMap); + NS_ADDREF(m_database); + + nsCOMPtr<nsIInputStream> inputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), pSrc); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening address file for reading\n"); + return rv; + } + + // Here we use this to work out the size of the file, so we can update + // an integer as we go through the file which will update a progress + // bar if required by the caller. + uint64_t bytesLeft = 0; + rv = inputStream->Available(&bytesLeft); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error checking address file for size\n"); + inputStream->Close(); + return rv; + } + + uint64_t totalBytes = bytesLeft; + bool skipRecord = false; + + rv = m_fieldMap->GetSkipFirstRecord(&skipRecord); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error checking to see if we should skip the first record\n"); + return rv; + } + + nsCOMPtr<nsIUnicharLineInputStream> lineStream; + rv = GetUnicharLineStreamForFile(pSrc, inputStream, getter_AddRefs(lineStream)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening converter stream for importer\n"); + return rv; + } + + bool more = true; + nsAutoString line; + + // Skip the first record if the user has requested it. + if (skipRecord) + rv = ReadRecord(lineStream, line, &more); + + while (!(*pAbort) && more && NS_SUCCEEDED(rv)) { + // Read the line in + rv = ReadRecord(lineStream, line, &more); + if (NS_SUCCEEDED(rv)) { + // Now proces it to add it to the database + rv = ProcessLine(line, errors); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error processing text record.\n"); + } + } + if (NS_SUCCEEDED(rv) && pProgress) { + // This won't be totally accurate, but its the best we can do + // considering that lineStream won't give us how many bytes + // are actually left. + bytesLeft -= line.Length(); + *pProgress = std::min(totalBytes - bytesLeft, uint64_t(PR_UINT32_MAX)); + } + } + + inputStream->Close(); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error reading the address book - probably incorrect ending\n"); + return NS_ERROR_FAILURE; + } + + return pDb->Commit(nsAddrDBCommitType::kLargeCommit); +} + +nsresult nsTextAddress::ReadRecord(nsIUnicharLineInputStream *aLineStream, + nsAString &aLine, + bool *aMore) +{ + bool more = true; + uint32_t numQuotes = 0; + nsresult rv; + nsAutoString line; + + // ensure aLine is empty + aLine.Truncate(); + + do { + if (!more) { + // No more, so we must have an incorrect file. + rv = NS_ERROR_FAILURE; + } + else { + // Read the line and append it + rv = aLineStream->ReadLine(line, &more); + if (NS_SUCCEEDED(rv)) { + if (!aLine.IsEmpty()) + aLine.AppendLiteral(MSG_LINEBREAK); + aLine.Append(line); + + numQuotes += MsgCountChar(line, char16_t('"')); + } + } + // Continue whilst everything is ok, and we have an odd number of quotes. + } while (NS_SUCCEEDED(rv) && (numQuotes % 2 != 0)); + + *aMore = more; + return rv; +} + +nsresult nsTextAddress::ReadRecordNumber(nsIFile *aSrc, nsAString &aLine, int32_t rNum) +{ + nsCOMPtr<nsIInputStream> inputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aSrc); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening address file for reading\n"); + return rv; + } + + int32_t rIndex = 0; + uint64_t bytesLeft = 0; + + rv = inputStream->Available(&bytesLeft); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error checking address file for eof\n"); + inputStream->Close(); + return rv; + } + + nsCOMPtr<nsIUnicharLineInputStream> lineStream; + rv = GetUnicharLineStreamForFile(aSrc, inputStream, getter_AddRefs(lineStream)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening converter stream for importer\n"); + return rv; + } + + bool more = true; + + while (more && (rIndex <= rNum)) { + rv = ReadRecord(lineStream, aLine, &more); + if (NS_FAILED(rv)) { + inputStream->Close(); + return rv; + } + if (rIndex == rNum) { + inputStream->Close(); + return NS_OK; + } + + rIndex++; + } + + return NS_ERROR_FAILURE; +} + +int32_t nsTextAddress::CountFields(const nsAString &aLine, char16_t delim) +{ + int32_t pos = 0; + int32_t maxLen = aLine.Length(); + int32_t count = 0; + char16_t tab = char16_t('\t'); + char16_t doubleQuote = char16_t('"'); + + if (delim == tab) + tab = char16_t('\0'); + + while (pos < maxLen) { + while (((aLine[pos] == char16_t(' ')) || (aLine[pos] == tab)) && + (pos < maxLen)) { + pos++; + } + if ((pos < maxLen) && (aLine[pos] == doubleQuote)) { + pos++; + while ((pos < maxLen) && (aLine[pos] != doubleQuote)) { + pos++; + if (((pos + 1) < maxLen) && + (aLine[pos] == doubleQuote) && + (aLine[pos + 1] == doubleQuote)) { + pos += 2; + } + } + if (pos < maxLen) + pos++; + } + while ((pos < maxLen) && (aLine[pos] != delim)) + pos++; + + count++; + pos++; + } + + return count; +} + +bool nsTextAddress::GetField(const nsAString &aLine, + int32_t index, + nsString &field, + char16_t delim) +{ + bool result = false; + int32_t pos = 0; + int32_t maxLen = aLine.Length(); + char16_t tab = char16_t('\t'); + char16_t doubleQuote = char16_t('"'); + + field.Truncate(); + + if (delim == tab) + tab = 0; + + while (index && (pos < maxLen)) { + while (((aLine[pos] == char16_t(' ')) || (aLine[pos] == tab)) && + (pos < maxLen)) { + pos++; + } + if (pos >= maxLen) + break; + if (aLine[pos] == doubleQuote) { + do { + pos++; + if (((pos + 1) < maxLen) && + (aLine[pos] == doubleQuote) && + (aLine[pos + 1] == doubleQuote)) { + pos += 2; + } + } while ((pos < maxLen) && (aLine[pos] != doubleQuote)); + if (pos < maxLen) + pos++; + } + if (pos >= maxLen) + break; + + while ((pos < maxLen) && (aLine[pos] != delim)) + pos++; + + if (pos >= maxLen) + break; + + index--; + pos++; + } + + if (pos >= maxLen) + return result; + + result = true; + + while ((pos < maxLen) && ((aLine[pos] == ' ') || (aLine[pos] == tab))) + pos++; + + int32_t fLen = 0; + int32_t startPos = pos; + bool quoted = false; + if (aLine[pos] == '"') { + startPos++; + fLen = -1; + do { + pos++; + fLen++; + if (((pos + 1) < maxLen) && + (aLine[pos] == doubleQuote) && + (aLine[pos + 1] == doubleQuote)) { + quoted = true; + pos += 2; + fLen += 2; + } + } while ((pos < maxLen) && (aLine[pos] != doubleQuote)); + } + else { + while ((pos < maxLen) && (aLine[pos] != delim)) { + pos++; + fLen++; + } + } + + if (!fLen) { + return result; + } + + field.Append(nsDependentSubstring(aLine, startPos, fLen)); + field.Trim(kWhitespace); + + if (quoted) { + int32_t offset = field.Find("\"\""); + while (offset != -1) { + field.Cut(offset, 1); + offset = MsgFind(field, "\"\"", false, offset + 1); + } + } + + return result; +} + +nsresult nsTextAddress::DetermineDelim(nsIFile *aSrc) +{ + nsCOMPtr<nsIInputStream> inputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aSrc); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening address file for reading\n"); + return rv; + } + + int32_t lineCount = 0; + int32_t tabCount = 0; + int32_t commaCount = 0; + int32_t tabLines = 0; + int32_t commaLines = 0; + nsAutoString line; + bool more = true; + + nsCOMPtr<nsIUnicharLineInputStream> lineStream; + rv = GetUnicharLineStreamForFile(aSrc, inputStream, getter_AddRefs(lineStream)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error opening converter stream for importer\n"); + return rv; + } + + while (more && NS_SUCCEEDED(rv) && (lineCount < 100)) { + rv = lineStream->ReadLine(line, &more); + if (NS_SUCCEEDED(rv)) { + tabCount = CountFields(line, char16_t('\t')); + commaCount = CountFields(line, char16_t(',')); + if (tabCount > commaCount) + tabLines++; + else if (commaCount) + commaLines++; + } + lineCount++; + } + + rv = inputStream->Close(); + + if (tabLines > commaLines) + m_delim = char16_t('\t'); + else + m_delim = char16_t(','); + + IMPORT_LOG2( "Tab count = %d, Comma count = %d\n", tabLines, commaLines); + + return rv; +} + +/* + This is where the real work happens! + Go through the field map and set the data in a new database row +*/ +nsresult nsTextAddress::ProcessLine(const nsAString &aLine, nsString& errors) +{ + if (!m_fieldMap) { + IMPORT_LOG0("*** Error, text import needs a field map\n"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + + // Wait until we get our first non-empty field, then create a new row, + // fill in the data, then add the row to the database. + nsCOMPtr<nsIMdbRow> newRow; + nsAutoString fieldVal; + int32_t fieldNum; + int32_t numFields = 0; + bool active; + rv = m_fieldMap->GetMapSize(&numFields); + for (int32_t i = 0; (i < numFields) && NS_SUCCEEDED(rv); i++) { + active = false; + rv = m_fieldMap->GetFieldMap(i, &fieldNum); + if (NS_SUCCEEDED(rv)) + rv = m_fieldMap->GetFieldActive(i, &active); + if (NS_SUCCEEDED(rv) && active) { + if (GetField(aLine, i, fieldVal, m_delim)) { + if (!fieldVal.IsEmpty()) { + if (!newRow) { + rv = m_database->GetNewRow(getter_AddRefs(newRow)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error getting new address database row\n"); + } + } + if (newRow) { + rv = m_fieldMap->SetFieldValue(m_database, newRow, fieldNum, fieldVal.get()); + } + } + } + else + break; + } + else if (active) { + IMPORT_LOG1("*** Error getting field map for index %ld\n", (long) i); + } + } + + if (NS_SUCCEEDED(rv) && newRow) + rv = m_database->AddCardRowToDB(newRow); + + return rv; +} + diff --git a/mailnews/import/text/src/nsTextAddress.h b/mailnews/import/text/src/nsTextAddress.h new file mode 100644 index 000000000..69a311be4 --- /dev/null +++ b/mailnews/import/text/src/nsTextAddress.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTextAddress_h__ +#define nsTextAddress_h__ + +#include "nsCOMPtr.h" +#include "nsStringGlue.h" +#include "nsIImportFieldMap.h" +#include "nsIImportService.h" + +class nsIAddrDatabase; +class nsIFile; +class nsIInputStream; +class nsIUnicharLineInputStream; + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +class nsTextAddress { +public: + nsTextAddress(); + virtual ~nsTextAddress(); + + nsresult ImportAddresses(bool *pAbort, const char16_t *pName, nsIFile *pSrc, nsIAddrDatabase *pDb, nsIImportFieldMap *fieldMap, nsString& errors, uint32_t *pProgress); + + nsresult DetermineDelim(nsIFile *pSrc); + char16_t GetDelim(void) { return m_delim; } + + static nsresult ReadRecordNumber(nsIFile *pSrc, nsAString &aLine, int32_t rNum); + static bool GetField(const nsAString &aLine, int32_t index, nsString &field, char16_t delim); + +private: + nsresult ProcessLine(const nsAString &aLine, nsString &errors); + + static int32_t CountFields(const nsAString &aLine, char16_t delim); + static nsresult ReadRecord(nsIUnicharLineInputStream *pSrc, nsAString &aLine, bool *aMore); + static nsresult GetUnicharLineStreamForFile(nsIFile *aFile, + nsIInputStream *aInputStream, + nsIUnicharLineInputStream **aStream); + + char16_t m_delim; + int32_t m_LFCount; + int32_t m_CRCount; + nsIAddrDatabase *m_database; + nsIImportFieldMap *m_fieldMap; + nsCOMPtr<nsIImportService> m_pService; +}; + + + +#endif /* nsTextAddress_h__ */ + diff --git a/mailnews/import/text/src/nsTextImport.cpp b/mailnews/import/text/src/nsTextImport.cpp new file mode 100644 index 000000000..0b99e652b --- /dev/null +++ b/mailnews/import/text/src/nsTextImport.cpp @@ -0,0 +1,714 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + + Text import addressbook interfaces + +*/ +#include "nscore.h" +#include "nsIServiceManager.h" +#include "nsCOMPtr.h" +#include "nsIImportService.h" +#include "nsMsgI18N.h" +#include "nsIComponentManager.h" +#include "nsTextImport.h" +#include "nsIMemory.h" +#include "nsIMutableArray.h" +#include "nsIImportGeneric.h" +#include "nsIImportAddressBooks.h" +#include "nsIImportABDescriptor.h" +#include "nsIImportFieldMap.h" +#include "nsIOutputStream.h" +#include "nsIAddrDatabase.h" +#include "nsIAbLDIFService.h" +#include "nsAbBaseCID.h" +#include "nsTextFormatter.h" +#include "nsImportStringBundle.h" +#include "nsTextAddress.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "TextDebugLog.h" +#include "nsNetUtil.h" +#include "nsMsgUtils.h" + +#define TEXT_MSGS_URL "chrome://messenger/locale/textImportMsgs.properties" +#define TEXTIMPORT_NAME 2000 +#define TEXTIMPORT_DESCRIPTION 2001 +#define TEXTIMPORT_ADDRESS_NAME 2002 +#define TEXTIMPORT_ADDRESS_SUCCESS 2003 +#define TEXTIMPORT_ADDRESS_BADPARAM 2004 +#define TEXTIMPORT_ADDRESS_BADSOURCEFILE 2005 +#define TEXTIMPORT_ADDRESS_CONVERTERROR 2006 + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +PRLogModuleInfo* TEXTIMPORTLOGMODULE; + +class ImportAddressImpl final : public nsIImportAddressBooks +{ +public: + explicit ImportAddressImpl(nsIStringBundle* aStringBundle); + + static nsresult Create(nsIImportAddressBooks** aImport, + nsIStringBundle *aStringBundle); + + // nsISupports interface + NS_DECL_THREADSAFE_ISUPPORTS + + // nsIImportAddressBooks interface + + NS_IMETHOD GetSupportsMultiple(bool *_retval) override { *_retval = false; return NS_OK;} + + NS_IMETHOD GetAutoFind(char16_t **description, bool *_retval) override; + + NS_IMETHOD GetNeedsFieldMap(nsIFile *location, bool *_retval) override; + + NS_IMETHOD GetDefaultLocation(nsIFile **location, bool *found, bool *userVerify) override; + + NS_IMETHOD FindAddressBooks(nsIFile *location, nsIArray **_retval) override; + + NS_IMETHOD InitFieldMap(nsIImportFieldMap *fieldMap) override; + + NS_IMETHOD ImportAddressBook(nsIImportABDescriptor *source, + nsIAddrDatabase *destination, + nsIImportFieldMap *fieldMap, + nsISupports *aSupportService, + char16_t **errorLog, + char16_t **successLog, + bool *fatalError) override; + + NS_IMETHOD GetImportProgress(uint32_t *_retval) override; + + NS_IMETHOD GetSampleData(int32_t index, bool *pFound, char16_t **pStr) override; + + NS_IMETHOD SetSampleLocation(nsIFile *) override; + +private: + void ClearSampleFile(void); + void SaveFieldMap(nsIImportFieldMap *pMap); + + static void ReportSuccess(nsString& name, nsString *pStream, + nsIStringBundle* pBundle); + static void SetLogs(nsString& success, nsString& error, char16_t **pError, + char16_t **pSuccess); + static void ReportError(int32_t errorNum, nsString& name, nsString *pStream, + nsIStringBundle* pBundle); + static void SanitizeSampleData(nsString& val); + +private: + ~ImportAddressImpl() {} + nsTextAddress m_text; + bool m_haveDelim; + nsCOMPtr<nsIFile> m_fileLoc; + nsCOMPtr<nsIStringBundle> m_notProxyBundle; + char16_t m_delim; + uint32_t m_bytesImported; +}; + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +nsTextImport::nsTextImport() +{ + // Init logging module. + if (!TEXTIMPORTLOGMODULE) + TEXTIMPORTLOGMODULE = PR_NewLogModule("IMPORT"); + IMPORT_LOG0("nsTextImport Module Created\n"); + + nsImportStringBundle::GetStringBundle(TEXT_MSGS_URL, + getter_AddRefs(m_stringBundle)); +} + +nsTextImport::~nsTextImport() +{ + IMPORT_LOG0("nsTextImport Module Deleted\n"); +} + +NS_IMPL_ISUPPORTS(nsTextImport, nsIImportModule) + +NS_IMETHODIMP nsTextImport::GetName(char16_t **name) +{ + NS_ENSURE_ARG_POINTER(name); + *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_NAME, m_stringBundle); + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetDescription(char16_t **name) +{ + NS_ENSURE_ARG_POINTER(name); + *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_DESCRIPTION, + m_stringBundle); + + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetSupports(char **supports) +{ + NS_ENSURE_ARG_POINTER(supports); + *supports = strdup(kTextSupportsString); + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetSupportsUpgrade(bool *pUpgrade) +{ + NS_PRECONDITION(pUpgrade != nullptr, "null ptr"); + if (! pUpgrade) + return NS_ERROR_NULL_POINTER; + + *pUpgrade = false; + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetImportInterface(const char *pImportType, nsISupports **ppInterface) +{ + NS_ENSURE_ARG_POINTER(pImportType); + NS_ENSURE_ARG_POINTER(ppInterface); + + *ppInterface = nullptr; + nsresult rv; + + if (!strcmp(pImportType, "addressbook")) { + // create the nsIImportMail interface and return it! + nsIImportAddressBooks * pAddress = nullptr; + nsIImportGeneric * pGeneric = nullptr; + rv = ImportAddressImpl::Create(&pAddress, m_stringBundle); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewGenericAddressBooks(&pGeneric); + if (NS_SUCCEEDED(rv)) { + pGeneric->SetData("addressInterface", pAddress); + rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface); + } + } + } + NS_IF_RELEASE(pAddress); + NS_IF_RELEASE(pGeneric); + return rv; + } + return NS_ERROR_NOT_AVAILABLE; +} + +///////////////////////////////////////////////////////////////////////////////// + + + +nsresult ImportAddressImpl::Create(nsIImportAddressBooks** aImport, + nsIStringBundle* aStringBundle) +{ + NS_ENSURE_ARG_POINTER(aImport); + *aImport = new ImportAddressImpl(aStringBundle); + if (! *aImport) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aImport); + return NS_OK; +} + +ImportAddressImpl::ImportAddressImpl(nsIStringBundle* aStringBundle) : + m_notProxyBundle(aStringBundle) +{ + m_haveDelim = false; +} + +NS_IMPL_ISUPPORTS(ImportAddressImpl, nsIImportAddressBooks) + + +NS_IMETHODIMP ImportAddressImpl::GetAutoFind(char16_t **addrDescription, bool *_retval) +{ + NS_PRECONDITION(addrDescription != nullptr, "null ptr"); + NS_PRECONDITION(_retval != nullptr, "null ptr"); + if (! addrDescription || !_retval) + return NS_ERROR_NULL_POINTER; + + nsString str; + *_retval = false; + + if (!m_notProxyBundle) + return NS_ERROR_FAILURE; + + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_NAME, m_notProxyBundle, str); + *addrDescription = ToNewUnicode(str); + return NS_OK; +} + + +NS_IMETHODIMP ImportAddressImpl::GetDefaultLocation(nsIFile **ppLoc, bool *found, bool *userVerify) +{ + NS_PRECONDITION(found != nullptr, "null ptr"); + NS_PRECONDITION(ppLoc != nullptr, "null ptr"); + NS_PRECONDITION(userVerify != nullptr, "null ptr"); + if (! found || !userVerify || !ppLoc) + return NS_ERROR_NULL_POINTER; + + *ppLoc = nullptr; + *found = false; + *userVerify = true; + return NS_OK; +} + + + +NS_IMETHODIMP ImportAddressImpl::FindAddressBooks(nsIFile *pLoc, nsIArray **ppArray) +{ + NS_PRECONDITION(pLoc != nullptr, "null ptr"); + NS_PRECONDITION(ppArray != nullptr, "null ptr"); + if (!pLoc || !ppArray) + return NS_ERROR_NULL_POINTER; + + ClearSampleFile(); + + *ppArray = nullptr; + bool exists = false; + nsresult rv = pLoc->Exists(&exists); + if (NS_FAILED(rv) || !exists) + return NS_ERROR_FAILURE; + + bool isFile = false; + rv = pLoc->IsFile(&isFile); + if (NS_FAILED(rv) || !isFile) + return NS_ERROR_FAILURE; + + rv = m_text.DetermineDelim(pLoc); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error determining delimitter\n"); + return rv; + } + m_haveDelim = true; + m_delim = m_text.GetDelim(); + + m_fileLoc = do_QueryInterface(pLoc); + + /* Build an address book descriptor based on the file passed in! */ + nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("FAILED to allocate the nsIMutableArray\n"); + return rv; + } + + nsString name; + m_fileLoc->GetLeafName(name); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed getting leaf name of file\n"); + return rv; + } + + int32_t idx = name.RFindChar('.'); + if ((idx != -1) && (idx > 0) && ((name.Length() - idx - 1) < 5)) { + name.SetLength(idx); + } + + nsCOMPtr<nsIImportABDescriptor> desc; + nsISupports * pInterface; + + nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to obtain the import service\n"); + return rv; + } + + rv = impSvc->CreateNewABDescriptor(getter_AddRefs(desc)); + if (NS_SUCCEEDED(rv)) { + int64_t sz = 0; + pLoc->GetFileSize(&sz); + desc->SetPreferredName(name); + desc->SetSize((uint32_t) sz); + desc->SetAbFile(m_fileLoc); + rv = desc->QueryInterface(kISupportsIID, (void **) &pInterface); + array->AppendElement(pInterface, false); + pInterface->Release(); + } + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error creating address book descriptor for text import\n"); + return rv; + } + array.forget(ppArray); + return NS_OK; +} + +void ImportAddressImpl::ReportSuccess(nsString& name, nsString *pStream, + nsIStringBundle* pBundle) +{ + if (!pStream) + return; + + // load the success string + char16_t *pFmt = + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_SUCCESS, pBundle); + + char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get()); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + NS_Free(pFmt); + pStream->Append(char16_t('\n')); +} + +void ImportAddressImpl::ReportError(int32_t errorNum, nsString& name, + nsString *pStream, nsIStringBundle* pBundle) +{ + if (!pStream) + return; + + // load the error string + char16_t *pFmt = nsImportStringBundle::GetStringByID(errorNum, pBundle); + char16_t *pText = nsTextFormatter::smprintf(pFmt, name.get()); + pStream->Append(pText); + nsTextFormatter::smprintf_free(pText); + NS_Free(pFmt); + pStream->Append(char16_t('\n')); +} + +void ImportAddressImpl::SetLogs(nsString& success, nsString& error, char16_t **pError, char16_t **pSuccess) +{ + if (pError) + *pError = ToNewUnicode(error); + if (pSuccess) + *pSuccess = ToNewUnicode(success); +} + + +NS_IMETHODIMP +ImportAddressImpl::ImportAddressBook(nsIImportABDescriptor *pSource, + nsIAddrDatabase *pDestination, + nsIImportFieldMap *fieldMap, + nsISupports *aSupportService, + char16_t ** pErrorLog, + char16_t ** pSuccessLog, + bool * fatalError) +{ + NS_PRECONDITION(pSource != nullptr, "null ptr"); + NS_PRECONDITION(pDestination != nullptr, "null ptr"); + NS_PRECONDITION(fatalError != nullptr, "null ptr"); + + m_bytesImported = 0; + + nsString success, error; + if (!pSource || !pDestination || !fatalError) { + IMPORT_LOG0("*** Bad param passed to text address import\n"); + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_BADPARAM, + m_notProxyBundle, + error); + + SetLogs(success, error, pErrorLog, pSuccessLog); + + if (fatalError) + *fatalError = true; + + return NS_ERROR_NULL_POINTER; + } + + ClearSampleFile(); + + bool addrAbort = false; + nsString name; + pSource->GetPreferredName(name); + + uint32_t addressSize = 0; + pSource->GetSize(&addressSize); + if (addressSize == 0) { + IMPORT_LOG0("Address book size is 0, skipping import.\n"); + ReportSuccess(name, &success, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_OK; + } + + nsCOMPtr<nsIFile> inFile; + if (NS_FAILED(pSource->GetAbFile(getter_AddRefs(inFile)))) { + ReportError(TEXTIMPORT_ADDRESS_BADSOURCEFILE, name, &error, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_ERROR_FAILURE; + } + + if (!aSupportService) { + IMPORT_LOG0("Missing support service to import call"); + return NS_ERROR_FAILURE; + } + + bool isLDIF = false; + nsresult rv; + nsCOMPtr<nsIAbLDIFService> ldifService(do_QueryInterface(aSupportService, &rv)); + + if (NS_SUCCEEDED(rv)) { + rv = ldifService->IsLDIFFile(inFile, &isLDIF); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error reading address file\n"); + } + } + + if (NS_FAILED(rv)) { + ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return rv; + } + + if (isLDIF) { + if (ldifService) + rv = ldifService->ImportLDIFFile(pDestination, inFile, false, &m_bytesImported); + else + return NS_ERROR_FAILURE; + } + else { + rv = m_text.ImportAddresses(&addrAbort, name.get(), inFile, pDestination, fieldMap, error, &m_bytesImported); + SaveFieldMap(fieldMap); + } + + if (NS_SUCCEEDED(rv) && error.IsEmpty()) { + ReportSuccess(name, &success, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + } + else { + ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + } + + IMPORT_LOG0("*** Text address import done\n"); + return rv; +} + + +NS_IMETHODIMP ImportAddressImpl::GetImportProgress(uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = m_bytesImported; + return NS_OK; +} + + +NS_IMETHODIMP ImportAddressImpl::GetNeedsFieldMap(nsIFile *aLocation, bool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_ARG_POINTER(aLocation); + + *_retval = true; + bool exists = false; + bool isFile = false; + + nsresult rv = aLocation->Exists(&exists); + rv = aLocation->IsFile(&isFile); + + if (!exists || !isFile) + return NS_ERROR_FAILURE; + + bool isLDIF = false; + nsCOMPtr<nsIAbLDIFService> ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv); + + if (NS_SUCCEEDED(rv)) + rv = ldifService->IsLDIFFile(aLocation, &isLDIF); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error determining if file is of type LDIF\n"); + return rv; + } + + if (isLDIF) + *_retval = false; + + return NS_OK; +} + +void ImportAddressImpl::SanitizeSampleData(nsString& val) +{ + // remove any line-feeds... + int32_t offset = val.Find(NS_LITERAL_STRING("\x0D\x0A")); + while (offset != -1) { + val.Replace(offset, 2, NS_LITERAL_STRING(", ")); + offset = val.Find(NS_LITERAL_STRING("\x0D\x0A"), offset + 2); + } + offset = val.FindChar(13); + while (offset != -1) { + val.Replace(offset, 1, ','); + offset = val.FindChar(13, offset + 2); + } + offset = val.FindChar(10); + while (offset != -1) { + val.Replace(offset, 1, ','); + offset = val.FindChar(10, offset + 2); + } +} + +NS_IMETHODIMP ImportAddressImpl::GetSampleData(int32_t index, bool *pFound, char16_t **pStr) +{ + NS_PRECONDITION(pFound != nullptr, "null ptr"); + NS_PRECONDITION(pStr != nullptr, "null ptr"); + if (!pFound || !pStr) + return NS_ERROR_NULL_POINTER; + + if (!m_fileLoc) { + IMPORT_LOG0("*** Error, called GetSampleData before SetSampleLocation\n"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + *pStr = nullptr; + char16_t term = 0; + + if (!m_haveDelim) { + rv = m_text.DetermineDelim(m_fileLoc); + NS_ENSURE_SUCCESS(rv, rv); + m_haveDelim = true; + m_delim = m_text.GetDelim(); + } + + bool fileExists; + rv = m_fileLoc->Exists(&fileExists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!fileExists) { + *pFound = false; + *pStr = NS_strdup(&term); + return NS_OK; + } + + nsAutoString line; + rv = nsTextAddress::ReadRecordNumber(m_fileLoc, line, index); + if (NS_SUCCEEDED(rv)) { + nsString str; + nsString field; + int32_t fNum = 0; + while (nsTextAddress::GetField(line, fNum, field, m_delim)) { + if (fNum) + str.Append(char16_t('\n')); + SanitizeSampleData(field); + str.Append(field); + fNum++; + field.Truncate(); + } + + *pStr = ToNewUnicode(str); + *pFound = true; + + /* IMPORT_LOG1("Sample data: %S\n", str.get()); */ + } + else { + *pFound = false; + *pStr = NS_strdup(&term); + } + + return NS_OK; +} + +NS_IMETHODIMP ImportAddressImpl::SetSampleLocation(nsIFile *pLocation) +{ + NS_ENSURE_ARG_POINTER(pLocation); + + m_fileLoc = do_QueryInterface(pLocation); + m_haveDelim = false; + return NS_OK; +} + +void ImportAddressImpl::ClearSampleFile(void) +{ + m_fileLoc = nullptr; + m_haveDelim = false; +} + +NS_IMETHODIMP ImportAddressImpl::InitFieldMap(nsIImportFieldMap *fieldMap) +{ + // Let's remember the last one the user used! + // This should be normal for someone importing multiple times, it's usually + // from the same file format. + + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCString prefStr; + rv = prefs->GetCharPref("mailnews.import.text.fieldmap", getter_Copies(prefStr)); + if (NS_SUCCEEDED(rv)) { + const char *pStr = prefStr.get(); + if (pStr) { + fieldMap->SetFieldMapSize(0); + long fNum; + bool active; + long fIndex = 0; + while (*pStr) { + while (*pStr && (*pStr != '+') && (*pStr != '-')) + pStr++; + if (*pStr == '+') + active = true; + else if (*pStr == '-') + active = false; + else + break; + fNum = 0; + while (*pStr && ((*pStr < '0') || (*pStr > '9'))) + pStr++; + if (!(*pStr)) + break; + while (*pStr && (*pStr >= '0') && (*pStr <= '9')) { + fNum *= 10; + fNum += (*pStr - '0'); + pStr++; + } + while (*pStr && (*pStr != ',')) + pStr++; + if (*pStr == ',') + pStr++; + fieldMap->SetFieldMap(-1, fNum); + fieldMap->SetFieldActive(fIndex, active); + fIndex++; + } + if (!fIndex) { + int num; + fieldMap->GetNumMozFields(&num); + fieldMap->DefaultFieldMap(num); + } + } + } + + // Now also get the last used skip first record value. + bool skipFirstRecord = false; + rv = prefs->GetBoolPref("mailnews.import.text.skipfirstrecord", &skipFirstRecord); + if (NS_SUCCEEDED(rv)) + fieldMap->SetSkipFirstRecord(skipFirstRecord); + } + + return NS_OK; +} + + +void ImportAddressImpl::SaveFieldMap(nsIImportFieldMap *pMap) +{ + if (!pMap) + return; + + int size; + int index; + bool active; + nsCString str; + + pMap->GetMapSize(&size); + for (long i = 0; i < size; i++) { + index = i; + active = false; + pMap->GetFieldMap(i, &index); + pMap->GetFieldActive(i, &active); + if (active) + str.Append('+'); + else + str.Append('-'); + + str.AppendInt(index); + str.Append(','); + } + + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + + if (NS_SUCCEEDED(rv)) { + nsCString prefStr; + rv = prefs->GetCharPref("mailnews.import.text.fieldmap", getter_Copies(prefStr)); + if (NS_FAILED(rv) || !str.Equals(prefStr)) + rv = prefs->SetCharPref("mailnews.import.text.fieldmap", str.get()); + } + + // Now also save last used skip first record value. + bool skipFirstRecord = false; + rv = pMap->GetSkipFirstRecord(&skipFirstRecord); + if (NS_SUCCEEDED(rv)) + prefs->SetBoolPref("mailnews.import.text.skipfirstrecord", skipFirstRecord); +} diff --git a/mailnews/import/text/src/nsTextImport.h b/mailnews/import/text/src/nsTextImport.h new file mode 100644 index 000000000..4c3c440e0 --- /dev/null +++ b/mailnews/import/text/src/nsTextImport.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTextImport_h___ +#define nsTextImport_h___ + +#include "nsIImportModule.h" +#include "nsCOMPtr.h" +#include "nsIStringBundle.h" + +#define NS_TEXTIMPORT_CID \ +{ /* A5991D01-ADA7-11d3-A9C2-00A0CC26DA63 */ \ + 0xa5991d01, 0xada7, 0x11d3, \ + {0xa9, 0xc2, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63 }} + +#define kTextSupportsString NS_IMPORT_ADDRESS_STR + +class nsTextImport : public nsIImportModule +{ +public: + nsTextImport(); + + NS_DECL_ISUPPORTS + + //////////////////////////////////////////////////////////////////////////////////////// + // we suppport the nsIImportModule interface + //////////////////////////////////////////////////////////////////////////////////////// + + NS_DECL_NSIIMPORTMODULE + +protected: + virtual ~nsTextImport(); + nsCOMPtr<nsIStringBundle> m_stringBundle; +}; + +#endif /* nsTextImport_h___ */ |