/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsImportAddressBooks.h" #include "plstr.h" #include "nsIImportService.h" #include "nsISupportsPrimitives.h" #include "nsIImportABDescriptor.h" #include "nsIAbManager.h" #include "nsImportStringBundle.h" #include "nsTextFormatter.h" #include "msgCore.h" #include "ImportDebug.h" nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric) { NS_ASSERTION(aImportGeneric != nullptr, "null ptr"); if (!aImportGeneric) return NS_ERROR_NULL_POINTER; RefPtr pGen = new nsImportGenericAddressBooks(); return pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void**)aImportGeneric); } nsImportGenericAddressBooks::nsImportGenericAddressBooks() { m_totalSize = 0; m_doImport = false; m_pThreadData = nullptr; m_autoFind = false; m_description = nullptr; m_gotLocation = false; m_found = false; m_userVerify = false; nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle)); } nsImportGenericAddressBooks::~nsImportGenericAddressBooks() { if (m_description) free(m_description); } NS_IMPL_ISUPPORTS(nsImportGenericAddressBooks, nsIImportGeneric) NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char* dataId, nsISupports** _retval) { NS_ENSURE_ARG_POINTER(_retval); nsresult rv; *_retval = nullptr; if (!PL_strcasecmp(dataId, "addressInterface")) { NS_IF_ADDREF(*_retval = m_pInterface); } if (!PL_strcasecmp(dataId, "addressLocation")) { if (!m_pLocation) GetDefaultLocation(); NS_IF_ADDREF(*_retval = m_pLocation); } if (!PL_strcasecmp(dataId, "addressDestination")) { if (!m_pDestinationUri.IsEmpty()) { nsCOMPtr abString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); abString->SetData(m_pDestinationUri); abString.forget(_retval); } } if (!PL_strcasecmp(dataId, "fieldMap")) { if (m_pFieldMap) { NS_ADDREF(*_retval = m_pFieldMap); } else { if (m_pInterface && m_pLocation) { bool needsIt = false; m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt); if (needsIt) { GetDefaultFieldMap(); if (m_pFieldMap) { NS_ADDREF(*_retval = m_pFieldMap); } } } } } if (!PL_strncasecmp(dataId, "sampleData-", 11)) { // extra the record number const char* pNum = dataId + 11; int32_t rNum = 0; while (*pNum) { rNum *= 10; rNum += (*pNum - '0'); pNum++; } IMPORT_LOG1("Requesting sample data #: %ld\n", (long)rNum); if (m_pInterface) { nsCOMPtr data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; char16_t* pData = nullptr; bool found = false; rv = m_pInterface->GetSampleData(rNum, &found, &pData); if (NS_FAILED(rv)) return rv; if (found) { data->SetData(nsDependentString(pData)); data.forget(_retval); } free(pData); } } return NS_OK; } NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char* dataId, nsISupports* item) { NS_ASSERTION(dataId != nullptr, "null ptr"); if (!dataId) return NS_ERROR_NULL_POINTER; if (!PL_strcasecmp(dataId, "addressInterface")) { m_pInterface = nullptr; if (item) m_pInterface = do_QueryInterface(item); } if (!PL_strcasecmp(dataId, "addressLocation")) { m_pLocation = nullptr; if (item) { nsresult rv; m_pLocation = do_QueryInterface(item, &rv); NS_ENSURE_SUCCESS(rv, rv); } if (m_pInterface) m_pInterface->SetSampleLocation(m_pLocation); } if (!PL_strcasecmp(dataId, "addressDestination")) { if (item) { nsCOMPtr abString = do_QueryInterface(item); if (abString) { abString->GetData(m_pDestinationUri); } } } if (!PL_strcasecmp(dataId, "fieldMap")) { m_pFieldMap = nullptr; if (item) m_pFieldMap = do_QueryInterface(item); } return NS_OK; } NS_IMETHODIMP nsImportGenericAddressBooks::GetStatus(const char* statusKind, int32_t* _retval) { NS_ASSERTION(statusKind != nullptr, "null ptr"); NS_ASSERTION(_retval != nullptr, "null ptr"); if (!statusKind || !_retval) return NS_ERROR_NULL_POINTER; *_retval = 0; if (!PL_strcasecmp(statusKind, "isInstalled")) { GetDefaultLocation(); *_retval = (int32_t)m_found; } if (!PL_strcasecmp(statusKind, "canUserSetLocation")) { GetDefaultLocation(); *_retval = (int32_t)m_userVerify; } if (!PL_strcasecmp(statusKind, "autoFind")) { GetDefaultLocation(); *_retval = (int32_t)m_autoFind; } if (!PL_strcasecmp(statusKind, "supportsMultiple")) { bool multi = false; if (m_pInterface) m_pInterface->GetSupportsMultiple(&multi); *_retval = (int32_t)multi; } if (!PL_strcasecmp(statusKind, "needsFieldMap")) { bool needs = false; if (m_pInterface && m_pLocation) m_pInterface->GetNeedsFieldMap(m_pLocation, &needs); *_retval = (int32_t)needs; } return NS_OK; } void nsImportGenericAddressBooks::GetDefaultLocation(void) { if (!m_pInterface) return; if ((m_pLocation && m_gotLocation) || m_autoFind) return; if (m_description) free(m_description); m_description = nullptr; m_pInterface->GetAutoFind(&m_description, &m_autoFind); m_gotLocation = true; if (m_autoFind) { m_found = true; m_userVerify = false; return; } nsCOMPtr pLoc; m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify); if (!m_pLocation) m_pLocation = pLoc; } void nsImportGenericAddressBooks::GetDefaultBooks(void) { if (!m_pInterface) return; if (!m_pLocation && !m_autoFind) return; nsresult rv = m_pInterface->FindAddressBooks(m_pLocation, m_Books); if (NS_FAILED(rv)) { IMPORT_LOG0("*** Error: FindAddressBooks failed\n"); } } void nsImportGenericAddressBooks::GetDefaultFieldMap(void) { if (!m_pInterface || !m_pLocation) return; nsresult rv; nsCOMPtr impSvc( do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv)); if (NS_FAILED(rv)) { IMPORT_LOG0("*** Unable to get nsIImportService.\n"); return; } rv = impSvc->CreateNewFieldMap(getter_AddRefs(m_pFieldMap)); if (NS_FAILED(rv)) return; int32_t sz = 0; rv = m_pFieldMap->GetNumMozFields(&sz); if (NS_SUCCEEDED(rv)) rv = m_pFieldMap->DefaultFieldMap(sz); if (NS_SUCCEEDED(rv)) rv = m_pInterface->InitFieldMap(m_pFieldMap); if (NS_FAILED(rv)) { IMPORT_LOG0("*** Error: Unable to initialize field map\n"); m_pFieldMap = nullptr; } } NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool* _retval) { NS_ASSERTION(_retval != nullptr, "null ptr"); NS_ENSURE_ARG_POINTER(_retval); GetDefaultLocation(); GetDefaultBooks(); bool result = false; uint32_t totalSize = 0; for (nsIImportABDescriptor* book : m_Books) { bool doImport = false; nsresult rv = book->GetImport(&doImport); if (NS_SUCCEEDED(rv) && doImport) { uint32_t size = 0; (void)book->GetSize(&size); result = true; totalSize += size; } } m_totalSize = totalSize; m_doImport = result; *_retval = result; return NS_OK; } void nsImportGenericAddressBooks::SetLogs(nsString& success, nsString& error, nsISupportsString* pSuccess, nsISupportsString* pError) { nsAutoString str; if (pSuccess) { pSuccess->GetData(str); str.Append(success); pSuccess->SetData(success); } if (pError) { pError->GetData(str); str.Append(error); pError->SetData(error); } } already_AddRefed GetAddressBookFromUri(const char* pUri) { if (!pUri) return nullptr; nsCOMPtr abManager = do_GetService("@mozilla.org/abmanager;1"); if (!abManager) return nullptr; nsCOMPtr directory; abManager->GetDirectory(nsDependentCString(pUri), getter_AddRefs(directory)); if (!directory) return nullptr; return directory.forget(); } already_AddRefed GetAddressBook(nsString name, bool makeNew) { if (!makeNew) { // FIXME: How do I get the list of address books and look for a // specific name. Major bogosity! // For now, assume we didn't find anything with that name } IMPORT_LOG0("In GetAddressBook\n"); nsresult rv; nsCOMPtr directory; nsCOMPtr abManager = do_GetService("@mozilla.org/abmanager;1", &rv); if (NS_SUCCEEDED(rv)) { nsAutoCString dirPrefId; rv = abManager->NewAddressBook(name, EmptyCString(), nsIAbManager::JS_DIRECTORY_TYPE, EmptyCString(), dirPrefId); if (NS_SUCCEEDED(rv)) { rv = abManager->GetDirectoryFromId(dirPrefId, getter_AddRefs(directory)); } } return directory.forget(); } NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport( nsISupportsString* successLog, nsISupportsString* errorLog, bool* _retval) { NS_ASSERTION(_retval != nullptr, "null ptr"); if (!_retval) return NS_ERROR_NULL_POINTER; nsString success; nsString error; if (!m_doImport) { *_retval = true; nsImportStringBundle::GetStringByID(IMPORT_NO_ADDRBOOKS, m_stringBundle, success); SetLogs(success, error, successLog, errorLog); return NS_OK; } if (!m_pInterface) { nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED, m_stringBundle, error); SetLogs(success, error, successLog, errorLog); *_retval = false; return NS_OK; } bool needsFieldMap = false; if (NS_FAILED(m_pInterface->GetNeedsFieldMap(m_pLocation, &needsFieldMap)) || (needsFieldMap && !m_pFieldMap)) { nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED, m_stringBundle, error); SetLogs(success, error, successLog, errorLog); *_retval = false; return NS_OK; } m_pSuccessLog = successLog; m_pErrorLog = errorLog; // create the info need to drive address book import. We're // not going to create a new thread for this since address books // don't tend to be large, and import is rare. m_pThreadData = new AddressThreadData(); m_pThreadData->books = m_Books.Clone(); m_pThreadData->addressImport = m_pInterface; m_pThreadData->fieldMap = m_pFieldMap; m_pThreadData->errorLog = m_pErrorLog; m_pThreadData->successLog = m_pSuccessLog; m_pThreadData->pDestinationUri = m_pDestinationUri; // Create/obtain any address books that we need here, so that we don't need // to do so inside the import thread which would just proxy the create // operations back to the main thread anyway. nsCOMPtr db; if (!m_pDestinationUri.IsEmpty()) { db = GetAddressBookFromUri(m_pDestinationUri.get()); } for (nsIImportABDescriptor* book : m_Books) { if (!db) { nsString name; book->GetPreferredName(name); db = GetAddressBook(name, true); } m_DBs.AppendObject(db); } m_pThreadData->dBs = &m_DBs; m_pThreadData->stringBundle = m_stringBundle; nsresult rv; m_pThreadData->ldifService = do_GetService("@mozilla.org/addressbook/abldifservice;1", &rv); ImportAddressThread(m_pThreadData); delete m_pThreadData; m_pThreadData = nullptr; *_retval = true; return NS_OK; } NS_IMETHODIMP nsImportGenericAddressBooks::ContinueImport(bool* _retval) { NS_ASSERTION(_retval != nullptr, "null ptr"); if (!_retval) return NS_ERROR_NULL_POINTER; *_retval = true; if (m_pThreadData) { if (m_pThreadData->fatalError) *_retval = false; } return NS_OK; } NS_IMETHODIMP nsImportGenericAddressBooks::GetProgress(int32_t* _retval) { // This returns the progress from the the currently // running import mail or import address book thread. NS_ASSERTION(_retval != nullptr, "null ptr"); if (!_retval) return NS_ERROR_NULL_POINTER; if (!m_pThreadData || !(m_pThreadData->threadAlive)) { *_retval = 100; return NS_OK; } uint32_t sz = 0; if (m_pThreadData->currentSize && m_pInterface) { if (NS_FAILED(m_pInterface->GetImportProgress(&sz))) sz = 0; } if (m_totalSize) *_retval = ((m_pThreadData->currentTotal + sz) * 100) / m_totalSize; else *_retval = 0; // never return less than 5 so it looks like we are doing something! if (*_retval < 5) *_retval = 5; // as long as the thread is alive don't return completely // done. if (*_retval > 99) *_retval = 99; return NS_OK; } NS_IMETHODIMP nsImportGenericAddressBooks::CancelImport(void) { if (m_pThreadData) { m_pThreadData->abort = true; m_pThreadData = nullptr; } return NS_OK; } AddressThreadData::AddressThreadData() { fatalError = false; driverAlive = true; threadAlive = true; abort = false; currentTotal = 0; currentSize = 0; } AddressThreadData::~AddressThreadData() {} void nsImportGenericAddressBooks::ReportError(const char16_t* pName, nsString* pStream, nsIStringBundle* aBundle) { if (!pStream) return; // load the error string char16_t* pFmt = nsImportStringBundle::GetStringByID(IMPORT_ERROR_GETABOOK, aBundle); nsString pText; nsTextFormatter::ssprintf(pText, pFmt, pName); pStream->Append(pText); free(pFmt); pStream->AppendLiteral(MSG_LINEBREAK); } static void ImportAddressThread(void* stuff) { IMPORT_LOG0("In Begin ImportAddressThread\n"); AddressThreadData* pData = (AddressThreadData*)stuff; nsString success; nsString error; uint32_t count = pData->books.Length(); for (uint32_t i = 0; (i < count) && !(pData->abort); i++) { nsIImportABDescriptor* book = pData->books[i]; uint32_t size = 0; bool doImport = false; nsresult rv = book->GetImport(&doImport); if (NS_SUCCEEDED(rv) && doImport) rv = book->GetSize(&size); if (NS_SUCCEEDED(rv) && size && doImport) { nsString name; book->GetPreferredName(name); nsCOMPtr db = pData->dBs->ObjectAt(i); bool fatalError = false; pData->currentSize = size; if (db) { char16_t* pSuccess = nullptr; char16_t* pError = nullptr; /* if (pData->fieldMap) { int32_t sz = 0; int32_t mapIndex; bool active; pData->fieldMap->GetMapSize(&sz); IMPORT_LOG1("**** Field Map Size: %d\n", (int) sz); for (int32_t i = 0; i < sz; i++) { pData->fieldMap->GetFieldMap(i, &mapIndex); pData->fieldMap->GetFieldActive(i, &active); IMPORT_LOG3("Field map #%d: index=%d, active=%d\n", (int) i, (int) mapIndex, (int) active); } } */ rv = pData->addressImport->ImportAddressBook( book, db, pData->fieldMap, pData->ldifService, &pError, &pSuccess, &fatalError); if (NS_SUCCEEDED(rv) && pSuccess) { success.Append(pSuccess); free(pSuccess); } if (pError) { error.Append(pError); free(pError); } } else { nsImportGenericAddressBooks::ReportError(name.get(), &error, pData->stringBundle); } pData->currentSize = 0; pData->currentTotal += size; if (fatalError) { pData->fatalError = true; break; } } } nsImportGenericAddressBooks::SetLogs(success, error, pData->successLog, pData->errorLog); if (pData->abort || pData->fatalError) { // FIXME: do what is necessary to get rid of what has been imported so far. // Nothing if we went into an existing address book! Otherwise, delete // the ones we created? } }