diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/import/src/nsTextImport.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/import/src/nsTextImport.cpp')
-rw-r--r-- | comm/mailnews/import/src/nsTextImport.cpp | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/comm/mailnews/import/src/nsTextImport.cpp b/comm/mailnews/import/src/nsTextImport.cpp new file mode 100644 index 0000000000..961f1631ae --- /dev/null +++ b/comm/mailnews/import/src/nsTextImport.cpp @@ -0,0 +1,646 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Text import addressbook interfaces + */ + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsIImportService.h" +#include "nsMsgI18N.h" +#include "nsTextImport.h" +#include "nsIImportGeneric.h" +#include "nsIImportAddressBooks.h" +#include "nsIImportABDescriptor.h" +#include "nsIImportFieldMap.h" +#include "nsIAbLDIFService.h" +#include "nsTextFormatter.h" +#include "nsImportStringBundle.h" +#include "nsTextAddress.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "ImportDebug.h" +#include "nsNetUtil.h" +#include "nsMsgUtils.h" + +#define TEXT_MSGS_URL "chrome://messenger/locale/textImportMsgs.properties" +#define TEXTIMPORT_NAME 2000 +#define TEXTIMPORT_DESCRIPTION 2001 +#define TEXTIMPORT_ADDRESS_NAME 2002 +#define TEXTIMPORT_ADDRESS_SUCCESS 2003 +#define TEXTIMPORT_ADDRESS_BADPARAM 2004 +#define TEXTIMPORT_ADDRESS_BADSOURCEFILE 2005 +#define TEXTIMPORT_ADDRESS_CONVERTERROR 2006 + +class ImportAddressImpl final : public nsIImportAddressBooks { + public: + explicit ImportAddressImpl(nsIStringBundle* aStringBundle); + + static nsresult Create(nsIImportAddressBooks** aImport, + nsIStringBundle* aStringBundle); + + // nsISupports interface + NS_DECL_THREADSAFE_ISUPPORTS + + // nsIImportAddressBooks interface + + NS_IMETHOD GetSupportsMultiple(bool* _retval) override { + *_retval = false; + return NS_OK; + } + + NS_IMETHOD GetAutoFind(char16_t** description, bool* _retval) override; + + NS_IMETHOD GetNeedsFieldMap(nsIFile* location, bool* _retval) override; + + NS_IMETHOD GetDefaultLocation(nsIFile** location, bool* found, + bool* userVerify) override; + + NS_IMETHOD FindAddressBooks( + nsIFile* location, + nsTArray<RefPtr<nsIImportABDescriptor>>& books) override; + + NS_IMETHOD InitFieldMap(nsIImportFieldMap* fieldMap) override; + + NS_IMETHOD ImportAddressBook(nsIImportABDescriptor* source, + nsIAbDirectory* destination, + nsIImportFieldMap* fieldMap, + nsISupports* aSupportService, + char16_t** errorLog, char16_t** successLog, + bool* fatalError) override; + + NS_IMETHOD GetImportProgress(uint32_t* _retval) override; + + NS_IMETHOD GetSampleData(int32_t index, bool* pFound, + char16_t** pStr) override; + + NS_IMETHOD SetSampleLocation(nsIFile*) override; + + private: + void ClearSampleFile(void); + void SaveFieldMap(nsIImportFieldMap* pMap); + + static void ReportSuccess(nsString& name, nsString* pStream, + nsIStringBundle* pBundle); + static void SetLogs(nsString& success, nsString& error, char16_t** pError, + char16_t** pSuccess); + static void ReportError(int32_t errorNum, nsString& name, nsString* pStream, + nsIStringBundle* pBundle); + static void SanitizeSampleData(nsString& val); + + private: + ~ImportAddressImpl() {} + nsTextAddress m_text; + bool m_haveDelim; + nsCOMPtr<nsIFile> m_fileLoc; + nsCOMPtr<nsIStringBundle> m_notProxyBundle; + char16_t m_delim; + uint32_t m_bytesImported; +}; + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +nsTextImport::nsTextImport() { + IMPORT_LOG0("nsTextImport Module Created\n"); + + nsImportStringBundle::GetStringBundle(TEXT_MSGS_URL, + getter_AddRefs(m_stringBundle)); +} + +nsTextImport::~nsTextImport() { IMPORT_LOG0("nsTextImport Module Deleted\n"); } + +NS_IMPL_ISUPPORTS(nsTextImport, nsIImportModule) + +NS_IMETHODIMP nsTextImport::GetName(char16_t** name) { + NS_ENSURE_ARG_POINTER(name); + *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_NAME, m_stringBundle); + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetDescription(char16_t** name) { + NS_ENSURE_ARG_POINTER(name); + *name = nsImportStringBundle::GetStringByID(TEXTIMPORT_DESCRIPTION, + m_stringBundle); + + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetSupports(char** supports) { + NS_ENSURE_ARG_POINTER(supports); + *supports = strdup(kTextSupportsString); + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetSupportsUpgrade(bool* pUpgrade) { + NS_ASSERTION(pUpgrade != nullptr, "null ptr"); + if (!pUpgrade) return NS_ERROR_NULL_POINTER; + + *pUpgrade = false; + return NS_OK; +} + +NS_IMETHODIMP nsTextImport::GetImportInterface(const char* pImportType, + nsISupports** ppInterface) { + NS_ENSURE_ARG_POINTER(pImportType); + NS_ENSURE_ARG_POINTER(ppInterface); + + *ppInterface = nullptr; + nsresult rv; + + if (!strcmp(pImportType, "addressbook")) { + // create the nsIImportMail interface and return it! + nsCOMPtr<nsIImportAddressBooks> pAddress; + nsCOMPtr<nsIImportGeneric> pGeneric; + rv = ImportAddressImpl::Create(getter_AddRefs(pAddress), m_stringBundle); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIImportService> impSvc( + do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = impSvc->CreateNewGenericAddressBooks(getter_AddRefs(pGeneric)); + if (NS_SUCCEEDED(rv)) { + pGeneric->SetData("addressInterface", pAddress); + nsCOMPtr<nsISupports> pInterface(do_QueryInterface(pGeneric)); + pInterface.forget(ppInterface); + } + } + } + return rv; + } + return NS_ERROR_NOT_AVAILABLE; +} + +///////////////////////////////////////////////////////////////////////////////// + +nsresult ImportAddressImpl::Create(nsIImportAddressBooks** aImport, + nsIStringBundle* aStringBundle) { + NS_ENSURE_ARG_POINTER(aImport); + NS_ADDREF(*aImport = new ImportAddressImpl(aStringBundle)); + return NS_OK; +} + +ImportAddressImpl::ImportAddressImpl(nsIStringBundle* aStringBundle) + : m_notProxyBundle(aStringBundle) { + m_haveDelim = false; +} + +NS_IMPL_ISUPPORTS(ImportAddressImpl, nsIImportAddressBooks) + +NS_IMETHODIMP ImportAddressImpl::GetAutoFind(char16_t** addrDescription, + bool* _retval) { + NS_ASSERTION(addrDescription != nullptr, "null ptr"); + NS_ASSERTION(_retval != nullptr, "null ptr"); + if (!addrDescription || !_retval) return NS_ERROR_NULL_POINTER; + + nsString str; + *_retval = false; + + if (!m_notProxyBundle) return NS_ERROR_FAILURE; + + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_NAME, m_notProxyBundle, + str); + *addrDescription = ToNewUnicode(str); + return NS_OK; +} + +NS_IMETHODIMP ImportAddressImpl::GetDefaultLocation(nsIFile** ppLoc, + bool* found, + bool* userVerify) { + NS_ASSERTION(found != nullptr, "null ptr"); + NS_ASSERTION(ppLoc != nullptr, "null ptr"); + NS_ASSERTION(userVerify != nullptr, "null ptr"); + if (!found || !userVerify || !ppLoc) return NS_ERROR_NULL_POINTER; + + *ppLoc = nullptr; + *found = false; + *userVerify = true; + return NS_OK; +} + +NS_IMETHODIMP ImportAddressImpl::FindAddressBooks( + nsIFile* pLoc, nsTArray<RefPtr<nsIImportABDescriptor>>& books) { + NS_ASSERTION(pLoc != nullptr, "null ptr"); + if (!pLoc) return NS_ERROR_NULL_POINTER; + + books.Clear(); + ClearSampleFile(); + + bool exists = false; + nsresult rv = pLoc->Exists(&exists); + if (NS_FAILED(rv) || !exists) return NS_ERROR_FAILURE; + + bool isFile = false; + rv = pLoc->IsFile(&isFile); + if (NS_FAILED(rv) || !isFile) return NS_ERROR_FAILURE; + + rv = m_text.DetermineDelim(pLoc); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error determining delimitter\n"); + return rv; + } + m_haveDelim = true; + m_delim = m_text.GetDelim(); + + m_fileLoc = pLoc; + + /* Build an address book descriptor based on the file passed in! */ + nsString name; + m_fileLoc->GetLeafName(name); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed getting leaf name of file\n"); + return rv; + } + + int32_t idx = name.RFindChar('.'); + if ((idx != -1) && (idx > 0) && ((name.Length() - idx - 1) < 5)) { + name.SetLength(idx); + } + + nsCOMPtr<nsIImportABDescriptor> desc; + + nsCOMPtr<nsIImportService> impSvc( + do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to obtain the import service\n"); + return rv; + } + + rv = impSvc->CreateNewABDescriptor(getter_AddRefs(desc)); + if (NS_SUCCEEDED(rv)) { + int64_t sz = 0; + pLoc->GetFileSize(&sz); + desc->SetPreferredName(name); + desc->SetSize((uint32_t)sz); + desc->SetAbFile(m_fileLoc); + books.AppendElement(desc); + } + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error creating address book descriptor for text import\n"); + return rv; + } + return NS_OK; +} + +void ImportAddressImpl::ReportSuccess(nsString& name, nsString* pStream, + nsIStringBundle* pBundle) { + if (!pStream) return; + + // load the success string + char16_t* pFmt = + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_SUCCESS, pBundle); + + nsString pText; + nsTextFormatter::ssprintf(pText, pFmt, name.get()); + pStream->Append(pText); + free(pFmt); + pStream->Append(char16_t('\n')); +} + +void ImportAddressImpl::ReportError(int32_t errorNum, nsString& name, + nsString* pStream, + nsIStringBundle* pBundle) { + if (!pStream) return; + + // load the error string + char16_t* pFmt = nsImportStringBundle::GetStringByID(errorNum, pBundle); + nsString pText; + nsTextFormatter::ssprintf(pText, pFmt, name.get()); + pStream->Append(pText); + free(pFmt); + pStream->Append(char16_t('\n')); +} + +void ImportAddressImpl::SetLogs(nsString& success, nsString& error, + char16_t** pError, char16_t** pSuccess) { + if (pError) *pError = ToNewUnicode(error); + if (pSuccess) *pSuccess = ToNewUnicode(success); +} + +NS_IMETHODIMP +ImportAddressImpl::ImportAddressBook(nsIImportABDescriptor* pSource, + nsIAbDirectory* pDestination, + nsIImportFieldMap* fieldMap, + nsISupports* aSupportService, + char16_t** pErrorLog, + char16_t** pSuccessLog, bool* fatalError) { + NS_ASSERTION(pSource != nullptr, "null ptr"); + NS_ASSERTION(pDestination != nullptr, "null ptr"); + NS_ASSERTION(fatalError != nullptr, "null ptr"); + + m_bytesImported = 0; + + nsString success, error; + if (!pSource || !pDestination || !fatalError) { + IMPORT_LOG0("*** Bad param passed to text address import\n"); + nsImportStringBundle::GetStringByID(TEXTIMPORT_ADDRESS_BADPARAM, + m_notProxyBundle, error); + + SetLogs(success, error, pErrorLog, pSuccessLog); + + if (fatalError) *fatalError = true; + + return NS_ERROR_NULL_POINTER; + } + + ClearSampleFile(); + + bool addrAbort = false; + nsString name; + pSource->GetPreferredName(name); + + uint32_t addressSize = 0; + pSource->GetSize(&addressSize); + if (addressSize == 0) { + IMPORT_LOG0("Address book size is 0, skipping import.\n"); + ReportSuccess(name, &success, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_OK; + } + + nsCOMPtr<nsIFile> inFile; + if (NS_FAILED(pSource->GetAbFile(getter_AddRefs(inFile)))) { + ReportError(TEXTIMPORT_ADDRESS_BADSOURCEFILE, name, &error, + m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return NS_ERROR_FAILURE; + } + + if (!aSupportService) { + IMPORT_LOG0("Missing support service to import call"); + return NS_ERROR_FAILURE; + } + + bool isLDIF = false; + nsresult rv; + nsCOMPtr<nsIAbLDIFService> ldifService( + do_QueryInterface(aSupportService, &rv)); + + if (NS_SUCCEEDED(rv)) { + rv = ldifService->IsLDIFFile(inFile, &isLDIF); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error reading address file\n"); + } + } + + if (NS_FAILED(rv)) { + ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, + m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + return rv; + } + + if (isLDIF) { + if (ldifService) + rv = ldifService->ImportLDIFFile(pDestination, inFile, false, + &m_bytesImported); + else + return NS_ERROR_FAILURE; + } else { + rv = m_text.ImportAddresses(&addrAbort, name.get(), inFile, pDestination, + fieldMap, error, &m_bytesImported); + SaveFieldMap(fieldMap); + } + + if (NS_SUCCEEDED(rv) && error.IsEmpty()) { + ReportSuccess(name, &success, m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + } else { + ReportError(TEXTIMPORT_ADDRESS_CONVERTERROR, name, &error, + m_notProxyBundle); + SetLogs(success, error, pErrorLog, pSuccessLog); + } + + IMPORT_LOG0("*** Text address import done\n"); + return rv; +} + +NS_IMETHODIMP ImportAddressImpl::GetImportProgress(uint32_t* _retval) { + NS_ENSURE_ARG_POINTER(_retval); + *_retval = m_bytesImported; + return NS_OK; +} + +NS_IMETHODIMP ImportAddressImpl::GetNeedsFieldMap(nsIFile* aLocation, + bool* _retval) { + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_ARG_POINTER(aLocation); + + *_retval = true; + bool exists = false; + bool isFile = false; + + nsresult rv = aLocation->Exists(&exists); + rv = aLocation->IsFile(&isFile); + + if (!exists || !isFile) return NS_ERROR_FAILURE; + + bool isLDIF = false; + nsCOMPtr<nsIAbLDIFService> ldifService = + do_GetService("@mozilla.org/addressbook/abldifservice;1", &rv); + + if (NS_SUCCEEDED(rv)) rv = ldifService->IsLDIFFile(aLocation, &isLDIF); + + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Error determining if file is of type LDIF\n"); + return rv; + } + + if (isLDIF) *_retval = false; + + return NS_OK; +} + +void ImportAddressImpl::SanitizeSampleData(nsString& val) { + // remove any line-feeds... + int32_t offset = val.Find(u"\x0D\x0A"_ns); + while (offset != -1) { + val.Replace(offset, 2, u", "_ns); + offset = val.Find(u"\x0D\x0A"_ns, offset + 2); + } + offset = val.FindChar(13); + while (offset != -1) { + val.Replace(offset, 1, ','); + offset = val.FindChar(13, offset + 2); + } + offset = val.FindChar(10); + while (offset != -1) { + val.Replace(offset, 1, ','); + offset = val.FindChar(10, offset + 2); + } +} + +NS_IMETHODIMP ImportAddressImpl::GetSampleData(int32_t index, bool* pFound, + char16_t** pStr) { + NS_ASSERTION(pFound != nullptr, "null ptr"); + NS_ASSERTION(pStr != nullptr, "null ptr"); + if (!pFound || !pStr) return NS_ERROR_NULL_POINTER; + + if (!m_fileLoc) { + IMPORT_LOG0("*** Error, called GetSampleData before SetSampleLocation\n"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + *pStr = nullptr; + char16_t term = 0; + + if (!m_haveDelim) { + rv = m_text.DetermineDelim(m_fileLoc); + NS_ENSURE_SUCCESS(rv, rv); + m_haveDelim = true; + m_delim = m_text.GetDelim(); + } + + bool fileExists; + rv = m_fileLoc->Exists(&fileExists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!fileExists) { + *pFound = false; + *pStr = NS_xstrdup(&term); + return NS_OK; + } + + nsAutoString line; + rv = nsTextAddress::ReadRecordNumber(m_fileLoc, line, index); + if (NS_SUCCEEDED(rv)) { + nsString str; + nsString field; + int32_t fNum = 0; + while (nsTextAddress::GetField(line, fNum, field, m_delim)) { + if (fNum) str.Append(char16_t('\n')); + SanitizeSampleData(field); + str.Append(field); + fNum++; + field.Truncate(); + } + + *pStr = ToNewUnicode(str); + *pFound = true; + + /* IMPORT_LOG1("Sample data: %S\n", str.get()); */ + } else { + *pFound = false; + *pStr = NS_xstrdup(&term); + } + + return NS_OK; +} + +NS_IMETHODIMP ImportAddressImpl::SetSampleLocation(nsIFile* pLocation) { + NS_ENSURE_ARG_POINTER(pLocation); + + m_fileLoc = pLocation; + m_haveDelim = false; + return NS_OK; +} + +void ImportAddressImpl::ClearSampleFile(void) { + m_fileLoc = nullptr; + m_haveDelim = false; +} + +NS_IMETHODIMP ImportAddressImpl::InitFieldMap(nsIImportFieldMap* fieldMap) { + // Let's remember the last one the user used! + // This should be normal for someone importing multiple times, it's usually + // from the same file format. + + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCString prefStr; + rv = prefs->GetCharPref("mailnews.import.text.fieldmap", prefStr); + if (NS_SUCCEEDED(rv)) { + const char* pStr = prefStr.get(); + if (pStr) { + fieldMap->SetFieldMapSize(0); + long fNum; + bool active; + long fIndex = 0; + while (*pStr) { + while (*pStr && (*pStr != '+') && (*pStr != '-')) pStr++; + if (*pStr == '+') + active = true; + else if (*pStr == '-') + active = false; + else + break; + fNum = 0; + while (*pStr && ((*pStr < '0') || (*pStr > '9'))) pStr++; + if (!(*pStr)) break; + while (*pStr && (*pStr >= '0') && (*pStr <= '9')) { + fNum *= 10; + fNum += (*pStr - '0'); + pStr++; + } + while (*pStr && (*pStr != ',')) pStr++; + if (*pStr == ',') pStr++; + if (!active) { + fNum *= -1; // Re-add the stripped minus sign. + } + fieldMap->SetFieldMap(-1, fNum); + fieldMap->SetFieldActive(fIndex, active); + fIndex++; + } + if (!fIndex) { + int num; + fieldMap->GetNumMozFields(&num); + fieldMap->DefaultFieldMap(num); + } + } + } + + // Now also get the last used skip first record value. + bool skipFirstRecord = false; + rv = prefs->GetBoolPref("mailnews.import.text.skipfirstrecord", + &skipFirstRecord); + if (NS_SUCCEEDED(rv)) fieldMap->SetSkipFirstRecord(skipFirstRecord); + } + + return NS_OK; +} + +void ImportAddressImpl::SaveFieldMap(nsIImportFieldMap* pMap) { + if (!pMap) return; + + int size; + int index; + bool active; + nsCString str; + + pMap->GetMapSize(&size); + for (long i = 0; i < size; i++) { + index = i; + active = false; + pMap->GetFieldMap(i, &index); + pMap->GetFieldActive(i, &active); + if (active) + str.Append('+'); + else + str.Append('-'); + + str.AppendInt(index); + str.Append(','); + } + + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + + if (NS_SUCCEEDED(rv)) { + nsCString prefStr; + rv = prefs->GetCharPref("mailnews.import.text.fieldmap", prefStr); + if (NS_FAILED(rv) || !str.Equals(prefStr)) + rv = prefs->SetCharPref("mailnews.import.text.fieldmap", str); + } + + // Now also save last used skip first record value. + bool skipFirstRecord = false; + rv = pMap->GetSkipFirstRecord(&skipFirstRecord); + if (NS_SUCCEEDED(rv)) + prefs->SetBoolPref("mailnews.import.text.skipfirstrecord", skipFirstRecord); +} |