address-book-service-0.1.1+16.04.20151211.1/0000755000015300001610000000000012632610222020205 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/cmake/0000755000015300001610000000000012632610222021265 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/cmake/modules/0000755000015300001610000000000012632610222022735 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/cmake/modules/Translations.cmake0000644000015300001610000000334312632607666026445 0ustar pbuserpbgroup00000000000000# Translations.cmake, CMake macros written for Marlin, feel free to re-use them macro(add_translations_directory NLS_PACKAGE) add_custom_target (i18n ALL) find_program (MSGFMT_EXECUTABLE msgfmt) file (GLOB PO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.po) foreach (PO_INPUT ${PO_FILES}) get_filename_component (PO_INPUT_BASE ${PO_INPUT} NAME_WE) set (MO_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PO_INPUT_BASE}.mo) add_custom_command (TARGET i18n COMMAND ${MSGFMT_EXECUTABLE} -o ${MO_OUTPUT} ${PO_INPUT}) install (FILES ${MO_OUTPUT} DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${PO_INPUT_BASE}/LC_MESSAGES RENAME ${NLS_PACKAGE}.mo) endforeach (PO_INPUT ${PO_FILES}) endmacro(add_translations_directory) macro(add_translations_catalog NLS_PACKAGE) add_custom_target (pot COMMENT “Building translation catalog.”) find_program (XGETTEXT_EXECUTABLE xgettext) # init this list, which will hold all the sources across all dirs set(SOURCES "") # add each directory's sources to the overall sources list foreach(FILES_INPUT ${ARGN}) set (DIR ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT}) file(GLOB_RECURSE DIR_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${DIR}/*.c ${DIR}/*.cc ${DIR}/*.cpp ${DIR}/*.cxx ${DIR}/*.vala) set (SOURCES ${SOURCES} ${DIR_SOURCES}) endforeach() add_custom_command (TARGET pot COMMAND ${XGETTEXT_EXECUTABLE} -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot --c++ --qt --add-comments=TRANSLATORS --keyword="_" --keyword="N_" --from-code=UTF-8 --copyright-holder='Canonical Ltd.' -s -p ${CMAKE_CURRENT_SOURCE_DIR} -D ${CMAKE_CURRENT_SOURCE_DIR} ${SOURCES}) endmacro() address-book-service-0.1.1+16.04.20151211.1/cmake/modules/FindLibPhoneNumber.cmake0000644000015300001610000000141412632607666027433 0ustar pbuserpbgroup00000000000000set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) include(GNUInstallDirs) include(LibFindMacros) # Include dir find_path(LibPhoneNumber_INCLUDE_DIR NAMES phonenumberutil.h PATHS "/usr/local/${CMAKE_INSTALL_INCLUDEDIR}" ${CMAKE_INSTALL_FULL_INCLUDEDIR} PATH_SUFFIXES "phonenumbers" ) # library itself find_library(LibPhoneNumber_LIBRARY NAMES phonenumber PATHS "/usr/local/${CMAKE_INSTALL_LIBDIR}" ${CMAKE_INSTALL_FULL_LIBDIR} ) # Set the include dir variables and the libraries and let libfind_process do the rest. # NOTE: Singular variables for this library, plural for libraries this this lib depends on. set(LibPhoneNumber_PROCESS_INCLUDES LibPhoneNumber_INCLUDE_DIR) set(LibPhoneNumber_PROCESS_LIBS LibPhoneNumber_LIBRARY) libfind_process(LibPhoneNumber) address-book-service-0.1.1+16.04.20151211.1/cmake/modules/LibFindMacros.cmake0000644000015300001610000001106012632607666026433 0ustar pbuserpbgroup00000000000000# Version 1.0 (2013-04-12) # Public Domain, originally written by Lasse Kärkkäinen # Published at http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries # If you improve the script, please modify the forementioned wiki page because # I no longer maintain my scripts (hosted as static files at zi.fi). Feel free # to remove this entire header if you use real version control instead. # Changelog: # 2013-04-12 Added version number (1.0) and this header, no other changes # 2009-10-08 Originally published # Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments # used for the current package. For this to work, the first parameter must be the # prefix of the current package, then the prefix of the new package etc, which are # passed to find_package. macro (libfind_package PREFIX) set (LIBFIND_PACKAGE_ARGS ${ARGN}) if (${PREFIX}_FIND_QUIETLY) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) endif (${PREFIX}_FIND_QUIETLY) if (${PREFIX}_FIND_REQUIRED) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) endif (${PREFIX}_FIND_REQUIRED) find_package(${LIBFIND_PACKAGE_ARGS}) endmacro (libfind_package) # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) # where they added pkg_check_modules. Consequently I need to support both in my scripts # to avoid those deprecated warnings. Here's a helper that does just that. # Works identically to pkg_check_modules, except that no checks are needed prior to use. macro (libfind_pkg_check_modules PREFIX PKGNAME) if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) include(UsePkgConfig) pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(${PREFIX} ${PKGNAME}) endif (PKG_CONFIG_FOUND) endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) endmacro (libfind_pkg_check_modules) # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. macro (libfind_process PREFIX) # Skip processing if already processed during this run if (NOT ${PREFIX}_FOUND) # Start with the assumption that the library was found set (${PREFIX}_FOUND TRUE) # Process all includes and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_INCLUDES}) if (${i}) set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Process all libraries and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_LIBS}) if (${i}) set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Print message and/or exit on fatal error if (${PREFIX}_FOUND) if (NOT ${PREFIX}_FIND_QUIETLY) message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") endif (NOT ${PREFIX}_FIND_QUIETLY) else (${PREFIX}_FOUND) if (${PREFIX}_FIND_REQUIRED) foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) message("${i}=${${i}}") endforeach (i) message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") endif (${PREFIX}_FIND_REQUIRED) endif (${PREFIX}_FOUND) endif (NOT ${PREFIX}_FOUND) endmacro (libfind_process) macro(libfind_library PREFIX basename) set(TMP "") if(MSVC80) set(TMP -vc80) endif(MSVC80) if(MSVC90) set(TMP -vc90) endif(MSVC90) set(${PREFIX}_LIBNAMES ${basename}${TMP}) if(${ARGC} GREATER 2) set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) endif(${ARGC} GREATER 2) find_library(${PREFIX}_LIBRARY NAMES ${${PREFIX}_LIBNAMES} PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} ) endmacro(libfind_library) address-book-service-0.1.1+16.04.20151211.1/cmake/lcov.cmake0000644000015300001610000000506012632607666023255 0ustar pbuserpbgroup00000000000000# - This module creates a new 'lcov' target which generates # a coverage analysis html output. # LCOV is a graphical front-end for GCC's coverage testing tool gcov. Please see # http://ltp.sourceforge.net/coverage/lcov.php # # Usage: you must add an option to your CMakeLists.txt to build your application # with coverage support. Then you need to include this file to the lcov target. # # Example: # IF(BUILD_WITH_COVERAGE) # SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") # SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") # SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") # include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) # ENDIF(BUILD_WITH_COVERAGE) #============================================================================= # Copyright 2010 ascolab GmbH # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distributed this file outside of CMake, substitute the full # License text for the above reference.) set(REMOVE_PATTERN q*.h folks/*.h dummy-*.c internal_0_9_2.c *.moc moc_*.cpp locale_facets.h new move.h) ## lcov target ADD_CUSTOM_TARGET(lcov) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND mkdir -p coverage WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . --zerocounters WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND make test WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . --capture --output-file ./coverage/stap_all.info --checksum -f WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . -r ./coverage/stap_all.info ${REMOVE_PATTERN} --output-file ./coverage/stap.info WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND genhtml -o ./coverage --title "Code Coverage" --legend --show-details --demangle-cpp ./coverage/stap.info WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND echo "Open ${CMAKE_BINARY_DIR}/coverage/index.html to view the coverage analysis results." WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) address-book-service-0.1.1+16.04.20151211.1/cmake/vala/0000755000015300001610000000000012632610222022210 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/cmake/vala/FindVala.cmake0000644000015300001610000000611312632607666024721 0ustar pbuserpbgroup00000000000000## # Find module for the Vala compiler (valac) # # This module determines wheter a Vala compiler is installed on the current # system and where its executable is. # # Call the module using "find_package(Vala) from within your CMakeLists.txt. # # The following variables will be set after an invocation: # # VALA_FOUND Whether the vala compiler has been found or not # VALA_EXECUTABLE Full path to the valac executable if it has been found # VALA_VERSION Version number of the available valac # VALA_USE_FILE Include this file to define the vala_precompile function ## ## # Copyright 2009-2010 Jakob Westhoff. All rights reserved. # Copyright 2010-2011 Daniel Pfeifer # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are those # of the authors and should not be interpreted as representing official policies, # either expressed or implied, of Jakob Westhoff ## # Search for the valac executable in the usual system paths # Some distributions rename the valac to contain the major.minor in the binary name find_program(VALA_EXECUTABLE NAMES valac valac-0.20 valac-0.18 valac-0.16 valac-0.14 valac-0.12 valac-0.10) mark_as_advanced(VALA_EXECUTABLE) # Determine the valac version if(VALA_EXECUTABLE) execute_process(COMMAND ${VALA_EXECUTABLE} "--version" OUTPUT_VARIABLE VALA_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REPLACE "Vala " "" VALA_VERSION "${VALA_VERSION}") endif(VALA_EXECUTABLE) # Handle the QUIETLY and REQUIRED arguments, which may be given to the find call. # Furthermore set VALA_FOUND to TRUE if Vala has been found (aka. # VALA_EXECUTABLE is set) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Vala REQUIRED_VARS VALA_EXECUTABLE VERSION_VAR VALA_VERSION) set(VALA_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseVala.cmake") address-book-service-0.1.1+16.04.20151211.1/cmake/vala/UseVala.cmake0000644000015300001610000001557212632607666024606 0ustar pbuserpbgroup00000000000000## # Compile vala files to their c equivalents for further processing. # # The "vala_precompile" function takes care of calling the valac executable on # the given source to produce c files which can then be processed further using # default cmake functions. # # The first parameter provided is a variable, which will be filled with a list # of c files outputted by the vala compiler. This list can than be used in # conjuction with functions like "add_executable" or others to create the # neccessary compile rules with CMake. # # The following sections may be specified afterwards to provide certain options # to the vala compiler: # # SOURCES # A list of .vala files to be compiled. Please take care to add every vala # file belonging to the currently compiled project or library as Vala will # otherwise not be able to resolve all dependencies. # # PACKAGES # A list of vala packages/libraries to be used during the compile cycle. The # package names are exactly the same, as they would be passed to the valac # "--pkg=" option. # # OPTIONS # A list of optional options to be passed to the valac executable. This can be # used to pass "--thread" for example to enable multi-threading support. # # DEFINITIONS # A list of symbols to be used for conditional compilation. They are the same # as they would be passed using the valac "--define=" option. # # CUSTOM_VAPIS # A list of custom vapi files to be included for compilation. This can be # useful to include freshly created vala libraries without having to install # them in the system. # # GENERATE_VAPI # Pass all the needed flags to the compiler to create a vapi for # the compiled library. The provided name will be used for this and a # .vapi file will be created. # # GENERATE_HEADER # Let the compiler generate a header file for the compiled code. There will # be a header file as well as an internal header file being generated called # .h and _internal.h # # The following call is a simple example to the vala_precompile macro showing # an example to every of the optional sections: # # find_package(Vala "0.12" REQUIRED) # include(${VALA_USE_FILE}) # # vala_precompile(VALA_C # SOURCES # source1.vala # source2.vala # source3.vala # PACKAGES # gtk+-2.0 # gio-1.0 # posix # DIRECTORY # gen # OPTIONS # --thread # CUSTOM_VAPIS # some_vapi.vapi # GENERATE_VAPI # myvapi # GENERATE_HEADER # myheader # ) # # Most important is the variable VALA_C which will contain all the generated c # file names after the call. ## ## # Copyright 2009-2010 Jakob Westhoff. All rights reserved. # Copyright 2010-2011 Daniel Pfeifer # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are those # of the authors and should not be interpreted as representing official policies, # either expressed or implied, of Jakob Westhoff ## include(CMakeParseArguments) function(vala_precompile output) cmake_parse_arguments(ARGS "" "DIRECTORY;GENERATE_HEADER;GENERATE_VAPI" "SOURCES;PACKAGES;OPTIONS;DEFINITIONS;CUSTOM_VAPIS" ${ARGN}) if(ARGS_DIRECTORY) get_filename_component(DIRECTORY ${ARGS_DIRECTORY} ABSOLUTE) else(ARGS_DIRECTORY) set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif(ARGS_DIRECTORY) include_directories(${DIRECTORY}) set(vala_pkg_opts "") foreach(pkg ${ARGS_PACKAGES}) list(APPEND vala_pkg_opts "--pkg=${pkg}") endforeach(pkg ${ARGS_PACKAGES}) set(vala_define_opts "") foreach(def ${ARGS_DEFINTIONS}) list(APPEND vala_define_opts "--define=${def}") endforeach(def ${ARGS_DEFINTIONS}) set(in_files "") set(out_files "") foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) list(APPEND in_files "${CMAKE_CURRENT_SOURCE_DIR}/${src}") string(REPLACE ".vala" ".c" src ${src}) string(REPLACE ".gs" ".c" src ${src}) set(out_file "${DIRECTORY}/${src}") list(APPEND out_files "${DIRECTORY}/${src}") endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) set(custom_vapi_arguments "") if(ARGS_CUSTOM_VAPIS) foreach(vapi ${ARGS_CUSTOM_VAPIS}) list(APPEND custom_vapi_arguments ${vapi}) endforeach(vapi ${ARGS_CUSTOM_VAPIS}) endif(ARGS_CUSTOM_VAPIS) set(vapi_arguments "") if(ARGS_GENERATE_VAPI) list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI}.vapi") set(vapi_arguments "--vapi=${ARGS_GENERATE_VAPI}.vapi") # Header and internal header is needed to generate internal vapi if (NOT ARGS_GENERATE_HEADER) set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI}) endif(NOT ARGS_GENERATE_HEADER) endif(ARGS_GENERATE_VAPI) set(header_arguments "") if(ARGS_GENERATE_HEADER) list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}.h") list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h") list(APPEND header_arguments "--header=${DIRECTORY}/${ARGS_GENERATE_HEADER}.h") list(APPEND header_arguments "--internal-header=${DIRECTORY}/${ARGS_GENERATE_HEADER}_internal.h") endif(ARGS_GENERATE_HEADER) add_custom_command(OUTPUT ${out_files} COMMAND ${VALA_EXECUTABLE} ARGS "-C" ${header_arguments} ${vapi_arguments} "-b" ${CMAKE_CURRENT_SOURCE_DIR} "-d" ${DIRECTORY} ${vala_pkg_opts} ${vala_define_opts} ${ARGS_OPTIONS} ${in_files} ${custom_vapi_arguments} DEPENDS ${in_files} ${ARGS_CUSTOM_VAPIS} ) set(${output} ${out_files} PARENT_SCOPE) endfunction(vala_precompile) address-book-service-0.1.1+16.04.20151211.1/tests/0000755000015300001610000000000012632610222021347 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/unittest/0000755000015300001610000000000012632610222023226 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/unittest/scoped-loop.cpp0000644000015300001610000000204312632607666026177 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "scoped-loop.h" ScopedEventLoop::ScopedEventLoop(QEventLoop **proxy) { reset(proxy); } ScopedEventLoop::~ScopedEventLoop() { *m_proxy = 0; } void ScopedEventLoop::reset(QEventLoop **proxy) { *proxy = &m_eventLoop; m_proxy = proxy; } void ScopedEventLoop::exec() { if (*m_proxy) { m_eventLoop.exec(); *m_proxy = 0; } } address-book-service-0.1.1+16.04.20151211.1/tests/unittest/base-client-test.h0000644000015300001610000000210612632607666026563 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __BASE_DUMMY_TEST_H__ #define __BASE_DUMMY_TEST_H__ #include #include #include class BaseClientTest : public QObject { Q_OBJECT protected: QDBusInterface *m_serverIface; QDBusInterface *m_dummyIface; protected Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); }; #endif address-book-service-0.1.1+16.04.20151211.1/tests/unittest/base-client-test.cpp0000644000015300001610000000463112632607666027123 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "base-client-test.h" #include "dummy-backend-defs.h" #include "common/dbus-service-defs.h" #include "common/source.h" #include "lib/qindividual.h" #include #include #include void BaseClientTest::initTestCase() { QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); galera::Source::registerMetaType(); qRegisterMetaType >("QList"); QString serviceName; if (qEnvironmentVariableIsSet(ALTERNATIVE_CPIM_SERVICE_NAME)) { serviceName = qgetenv(ALTERNATIVE_CPIM_SERVICE_NAME); } else { serviceName = CPIM_SERVICE_NAME; } m_serverIface = new QDBusInterface(serviceName, CPIM_ADDRESSBOOK_OBJECT_PATH, CPIM_ADDRESSBOOK_IFACE_NAME); QVERIFY(!m_serverIface->lastError().isValid()); // wait for service to be ready QTRY_COMPARE_WITH_TIMEOUT(m_serverIface->property("isReady").toBool(), true, 10000); m_dummyIface = new QDBusInterface(DUMMY_SERVICE_NAME, DUMMY_OBJECT_PATH, DUMMY_IFACE_NAME); QVERIFY(!m_dummyIface->lastError().isValid()); // wait for service to be ready QTRY_COMPARE_WITH_TIMEOUT(m_dummyIface->property("isReady").toBool(), true, 10000); } void BaseClientTest::init() { } void BaseClientTest::cleanup() { m_dummyIface->call("reset"); QDBusReply reply = m_dummyIface->call("listContacts"); QCOMPARE(reply.value().count(), 0); } void BaseClientTest::cleanupTestCase() { m_dummyIface->call("quit"); delete m_dummyIface; delete m_serverIface; } address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contact-sort-test.cpp0000644000015300001610000002164512632607666027361 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include #include #include "config.h" using namespace QtContacts; class ContactSortTest : public BaseClientTest { Q_OBJECT private: QContactManager *m_manager; QString m_basicVcard; QString createContact(const QString &fullName, const QString &phoneNumber = QString()) { QContact contact; QContactName name; QStringList names = fullName.split(" "); name.setFirstName(names.value(0)); name.setLastName(names.value(1)); contact.saveDetail(&name); QContactPhoneNumber phone; if (phoneNumber.isEmpty()) { phone.setNumber(QUuid::createUuid().toString()); } else { phone.setNumber(phoneNumber); } contact.saveDetail(&phone); return galera::VCardParser::contactToVcardSync(QList() << contact)[0]; } private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); m_basicVcard = QStringLiteral("BEGIN:VCARD\n" "VERSION:3.0\n" "N:Tal;Fulano_;de;;\n" "EMAIL:fulano_@ubuntu.com\n" "TEL;PID=1.1;TYPE=ISDN:(999) 999-9999\n" "END:VCARD"); } void init() { BaseClientTest::init(); m_manager = new QContactManager("galera"); } void cleanup() { delete m_manager; BaseClientTest::cleanup(); } void testSortContacts() { QString copyVcard = createContact("Foo Bar"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Baz Quux"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Renato Araujo"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Fone Broke"); m_serverIface->call("createContact", copyVcard, "dummy-store"); // check if the cotact is listed in the correct order QDBusMessage result = m_serverIface->call("query", "", "", 0, false, QStringList()); QDBusObjectPath viewObjectPath = result.arguments()[0].value(); QDBusInterface *view = new QDBusInterface(m_serverIface->service(), viewObjectPath.path(), CPIM_ADDRESSBOOK_VIEW_IFACE_NAME); QDBusReply reply = view->call("contactsDetails", QStringList(), 0, 100); QCOMPARE(reply.value().count(), 4); QList contactsCreated = galera::VCardParser::vcardToContactSync(reply.value()); QCOMPARE(contactsCreated.count(), 4); // compare result order (different from creation) // Baz Quux, Fone Broke, Foo Bar, Renato Araujo QCOMPARE(contactsCreated[0].detail().label(), QStringLiteral("Baz Quux")); QCOMPARE(contactsCreated[1].detail().label(), QStringLiteral("Fone Broke")); QCOMPARE(contactsCreated[2].detail().label(), QStringLiteral("Foo Bar")); QCOMPARE(contactsCreated[3].detail().label(), QStringLiteral("Renato Araujo")); } /* * Test sort contact only with only phone number */ void testSortContactsWithOnlyPhoneNumber() { QString copyVcard = createContact("Fone Broke"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("", "555-5555"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Baz Quux"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Foo Bar"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("Spider Man"); m_serverIface->call("createContact", copyVcard, "dummy-store"); copyVcard = createContact("", "(999) 999-9999"); m_serverIface->call("createContact", copyVcard, "dummy-store"); // check if the cotact is listed in the correct order QDBusMessage result = m_serverIface->call("query", "", "", 0, false, QStringList()); QDBusObjectPath viewObjectPath = result.arguments()[0].value(); QDBusInterface *view = new QDBusInterface(m_serverIface->service(), viewObjectPath.path(), CPIM_ADDRESSBOOK_VIEW_IFACE_NAME); QDBusReply reply = view->call("contactsDetails", QStringList(), 0, 100); delete view; QCOMPARE(reply.value().count(), 6); QList contactsCreated = galera::VCardParser::vcardToContactSync(reply.value()); QCOMPARE(contactsCreated.count(), 6); // compare result order (different from creation) // Baz Quux, Fone Broke, Foo Bar, Spider Man, (999) 999-9999, 555-5555 QCOMPARE(contactsCreated[0].detail().label(), QStringLiteral("Baz Quux")); QCOMPARE(contactsCreated[1].detail().label(), QStringLiteral("Fone Broke")); QCOMPARE(contactsCreated[2].detail().label(), QStringLiteral("Foo Bar")); QCOMPARE(contactsCreated[3].detail().label(), QStringLiteral("Spider Man")); QCOMPARE(contactsCreated[4].detail().label(), QStringLiteral("(999) 999-9999")); QCOMPARE(contactsCreated[5].detail().label(), QStringLiteral("555-5555")); QContact c = contactsCreated[3]; QContactFavorite fav; fav.setFavorite(true); c.saveDetail(&fav); QStringList vcard = galera::VCardParser::contactToVcardSync(QList() << c); m_serverIface->call("updateContact", vcard, "dummy-store"); // check if the cotact still in the correct order result = m_serverIface->call("query", "", "", 0, false, QStringList()); viewObjectPath = result.arguments()[0].value(); view = new QDBusInterface(m_serverIface->service(), viewObjectPath.path(), CPIM_ADDRESSBOOK_VIEW_IFACE_NAME); reply = view->call("contactsDetails", QStringList(), 0, 100); delete view; QCOMPARE(reply.value().count(), 6); contactsCreated = galera::VCardParser::vcardToContactSync(reply.value()); QCOMPARE(contactsCreated.count(), 6); // Baz Quux, Fone Broke, Foo Bar, Spider Man, (999) 999-9999, 555-5555 QCOMPARE(contactsCreated[0].detail().label(), QStringLiteral("Baz Quux")); QCOMPARE(contactsCreated[1].detail().label(), QStringLiteral("Fone Broke")); QCOMPARE(contactsCreated[2].detail().label(), QStringLiteral("Foo Bar")); QCOMPARE(contactsCreated[3].detail().label(), QStringLiteral("Spider Man")); QCOMPARE(contactsCreated[4].detail().label(), QStringLiteral("(999) 999-9999")); QCOMPARE(contactsCreated[5].detail().label(), QStringLiteral("555-5555")); } }; QTEST_MAIN(ContactSortTest) #include "contact-sort-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/vcardparser-test.cpp0000644000015300001610000004206012632607666027247 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common/vcard-parser.h" using namespace QtContacts; using namespace galera; typedef QList QContactList; class VCardParseTest : public QObject { Q_OBJECT private: QStringList m_vcards; QList m_contacts; void compareContact(const QtContacts::QContact &contact, const QtContacts::QContact &other) { // name QCOMPARE(contact.detail(QtContacts::QContactDetail::TypeName), other.detail(QtContacts::QContactDetail::TypeName)); // phone - this is necessary because: // 1 ) the QContactDetail::FieldDetailUri can change based on the detail order // 2 ) the phone number can be returned in different order QList phones = contact.details(QtContacts::QContactDetail::TypePhoneNumber); QList otherPhones = other.details(QtContacts::QContactDetail::TypePhoneNumber); QCOMPARE(phones.size(), otherPhones.size()); for(int i=0; i < phones.size(); i++) { QtContacts::QContactDetail phone = phones[i]; bool found = false; for(int x=0; x < otherPhones.size(); x++) { QtContacts::QContactDetail otherPhone = otherPhones[x]; if (phone.value(QtContacts::QContactPhoneNumber::FieldNumber) == otherPhone.value(QtContacts::QContactPhoneNumber::FieldNumber)) { found = true; QList phoneTypes = phone.value(QtContacts::QContactPhoneNumber::FieldSubTypes).value< QList >(); QList otherPhoneTypes = otherPhone.value(QtContacts::QContactPhoneNumber::FieldSubTypes).value< QList >(); QCOMPARE(phoneTypes, otherPhoneTypes); QCOMPARE(phone.value(QtContacts::QContactPhoneNumber::FieldContext), otherPhone.value(QtContacts::QContactPhoneNumber::FieldContext)); break; } } QVERIFY2(found, "Phone number is not equal"); } // email same as phone number QList emails = contact.details(QtContacts::QContactDetail::TypeEmailAddress); QList otherEmails = other.details(QtContacts::QContactDetail::TypeEmailAddress); QCOMPARE(emails.size(), otherEmails.size()); for(int i=0; i < emails.size(); i++) { QtContacts::QContactDetail email = emails[i]; bool found = false; for(int x=0; x < otherEmails.size(); x++) { QtContacts::QContactDetail otherEmail = otherEmails[x]; if (email.value(QtContacts::QContactEmailAddress::FieldEmailAddress) == otherEmail.value(QtContacts::QContactEmailAddress::FieldEmailAddress)) { found = true; QCOMPARE(email.value(QtContacts::QContactEmailAddress::FieldContext), otherEmail.value(QtContacts::QContactEmailAddress::FieldContext)); break; } } QVERIFY2(found, "Email is not equal"); } } /* * Use this function to compare vcards because the order of the attributes in the returned vcard * can be different for each vcard. Example: * "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" or "TEL;TYPE=ISDN;PID=1.1:33331410\r\n" */ void compareVCards(const QString &vcard, const QString &other) { QStringList vcardLines = vcard.split("\n", QString::SkipEmptyParts); QStringList otherLines = other.split("\n", QString::SkipEmptyParts); QCOMPARE(vcardLines.size(), otherLines.size()); for(int i=0; i < vcardLines.size(); i++) { QString value = vcardLines[i].split(":").last(); QString otherValue = otherLines.first().split(":").last(); // compare values. After ":" QCOMPARE(value, otherValue); QString attribute = vcardLines[i].split(":").first(); QString attributeOther = otherLines.first().split(":").first(); // compare attributes. Before ":" QStringList attributeFields = attribute.split(";"); QStringList attributeOtherFields = attributeOther.split(";"); Q_FOREACH(const QString &attr, attributeFields) { attributeOtherFields.removeOne(attr); } QVERIFY2(attributeOtherFields.size() == 0, QString("Vcard attribute is not equal (%1) != (%2)").arg(vcardLines[i]).arg(otherLines.first()).toUtf8()); otherLines.removeFirst(); } } private Q_SLOTS: void init() { m_vcards << QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "N:Sauro;Dino;da Silva;;\r\n" "EMAIL:dino@familiadinosauro.com.br\r\n" "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" "TEL;PID=1.2;TYPE=CELL:8888888\r\n" "END:VCARD\r\n"); m_vcards << QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "N:Sauro;Baby;da Silva;;\r\n" "EMAIL:baby@familiadinosauro.com.br\r\n" "TEL;PID=1.1;TYPE=ISDN:1111111\r\n" "TEL;PID=1.2;TYPE=CELL:2222222\r\n" "END:VCARD\r\n"); QContact contactDino; QContactName name; name.setFirstName("Dino"); name.setMiddleName("da Silva"); name.setLastName("Sauro"); contactDino.saveDetail(&name); QContactEmailAddress email; email.setEmailAddress("dino@familiadinosauro.com.br"); contactDino.saveDetail(&email); QContactPhoneNumber phoneLandLine; phoneLandLine.setSubTypes(QList() << QContactPhoneNumber::SubTypeLandline); phoneLandLine.setNumber("33331410"); phoneLandLine.setDetailUri("1.1"); contactDino.saveDetail(&phoneLandLine); QContactPhoneNumber phoneMobile; phoneMobile.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); phoneMobile.setNumber("8888888"); phoneMobile.setDetailUri("1.2"); contactDino.saveDetail(&phoneMobile); QContact contactBaby; name.setFirstName("Baby"); name.setMiddleName("da Silva"); name.setLastName("Sauro"); contactBaby.saveDetail(&name); email.setEmailAddress("baby@familiadinosauro.com.br"); contactBaby.saveDetail(&email); phoneLandLine.setSubTypes(QList() << QContactPhoneNumber::SubTypeLandline); phoneLandLine.setNumber("1111111"); phoneLandLine.setDetailUri("1.1"); contactBaby.saveDetail(&phoneLandLine); phoneMobile.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); phoneMobile.setNumber("2222222"); phoneMobile.setDetailUri("1.2"); contactBaby.saveDetail(&phoneMobile); m_contacts << contactDino << contactBaby; } void cleanup() { m_vcards.clear(); m_contacts.clear(); } /* * Test parse from vcard to contact using the async function */ void testVCardToContactAsync() { VCardParser parser; qRegisterMetaType< QList >(); QSignalSpy vcardToContactSignal(&parser, SIGNAL(contactsParsed(QList))); parser.vcardToContact(m_vcards); QTRY_COMPARE(vcardToContactSignal.count(), 1); QList arguments = vcardToContactSignal.takeFirst(); QCOMPARE(arguments.size(), 1); QList contacts = qvariant_cast >(arguments.at(0)); QCOMPARE(contacts.size(), 2); compareContact(contacts[0], m_contacts[0]); compareContact(contacts[1], m_contacts[1]); } /* * Test parse from vcard to contact using the sync function */ void testVCardToContactSync() { QList contacts = VCardParser::vcardToContactSync(m_vcards); QCOMPARE(contacts.size(), 2); compareContact(contacts[0], m_contacts[0]); compareContact(contacts[1], m_contacts[1]); } /* * Test parse a single vcard to contact using the sync function */ void testSingleVCardToContactSync() { QContact contact = VCardParser::vcardToContact(m_vcards[0]); compareContact(contact, m_contacts[0]); } /* * Test parse a invalid vcard */ void testInvalidVCard() { QString vcard("BEGIN:VCARD\r\nEND::VCARD\r\n"); QContact contact = VCardParser::vcardToContact(vcard); QVERIFY(contact.isEmpty()); } /* * Test parse contacts to vcard using the async function */ void testContactToVCardAsync() { VCardParser parser; QSignalSpy contactToVCardSignal(&parser, SIGNAL(vcardParsed(QStringList))); parser.contactToVcard(m_contacts); // Check if the vcardParsed signal was fired QTRY_COMPARE(contactToVCardSignal.count(), 1); // Check if the signal was fired with two vcards QList arguments = contactToVCardSignal.takeFirst(); QCOMPARE(arguments.size(), 1); QStringList vcardsResults = qvariant_cast(arguments.at(0)); QCOMPARE(vcardsResults.size(), 2); // Check if the vcard in the signals was correct parsed compareVCards(vcardsResults[0], m_vcards[0]); compareVCards(vcardsResults[1], m_vcards[1]); } /* * Test parse contacts to vcard using the sync function */ void testContactToVCardSync() { QStringList vcards = VCardParser::contactToVcardSync(m_contacts); QCOMPARE(vcards.size(), 2); // Check if the returned vcards are correct compareVCards(vcards[0], m_vcards[0]); compareVCards(vcards[1], m_vcards[1]); } /* * Test parse a single contact to vcard using the sync function */ void testSingContactToVCardSync() { QString vcard = VCardParser::contactToVcard(m_contacts[0]); // Check if the returned vcard is correct compareVCards(vcard, m_vcards[0]); } /* * Test parse a vcard with sync target into a Contact */ void testVCardWithSyncTargetToContact() { QString vcard = QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "CLIENTPIDMAP;PID=1.ADDRESSBOOKID0:ADDRESSBOOKNAME0\r\n" "CLIENTPIDMAP;PID=2.ADDRESSBOOKID1:ADDRESSBOOKNAME1\r\n" "N:Sauro;Dino;da Silva;;\r\n" "EMAILPID=1.1;:dino@familiadinosauro.com.br\r\n" "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" "TEL;PID=1.2;TYPE=CELL:8888888\r\n" "END:VCARD\r\n"); QContact contact = VCardParser::vcardToContact(vcard); QList targets = contact.details(); QCOMPARE(targets.size(), 2); QContactSyncTarget target0; QContactSyncTarget target1; // put the target in order if (targets[0].detailUri().startsWith("1.")) { target0 = targets[0]; target1 = targets[1]; } else { target0 = targets[1]; target1 = targets[2]; } QCOMPARE(target0.detailUri(), QString("1.ADDRESSBOOKID0")); QCOMPARE(target0.syncTarget(), QString("ADDRESSBOOKNAME0")); QCOMPARE(target1.detailUri(), QString("2.ADDRESSBOOKID1")); QCOMPARE(target1.syncTarget(), QString("ADDRESSBOOKNAME1")); } /* * Test parse a Contact with sync target into a vcard */ void testContactWithSyncTargetToVCard() { QContact c = m_contacts[0]; QContactSyncTarget target; target.setDetailUri("1.ADDRESSBOOKID0"); target.setSyncTarget("ADDRESSBOOKNAME0"); c.saveDetail(&target); QContactSyncTarget target1; target1.setDetailUri("2.ADDRESSBOOKID1"); target1.setSyncTarget("ADDRESSBOOKNAME1"); c.saveDetail(&target1); QString vcard = VCardParser::contactToVcard(c); QVERIFY(vcard.contains("CLIENTPIDMAP;PID=1.ADDRESSBOOKID0:ADDRESSBOOKNAME0")); QVERIFY(vcard.contains("CLIENTPIDMAP;PID=2.ADDRESSBOOKID1:ADDRESSBOOKNAME1")); } /* * Test parse a vcard with timestamp */ void testVCardWithTimestamp() { QString vcard = QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "N:Sauro;Dino;da Silva;;\r\n" "EMAILPID=1.1;:dino@familiadinosauro.com.br\r\n" "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" "TEL;PID=1.2;TYPE=CELL:8888888\r\n" "X-CREATED-AT:2015-04-14T16:16:44Z\r\n" "REV:2015-04-14T21:56:56Z(2300)\r\n" "END:VCARD\r\n"); QContact contact = VCardParser::vcardToContact(vcard); QList timestamps = contact.details(); QCOMPARE(timestamps.size(), 1); QContactTimestamp timestamp = timestamps.at(0); QCOMPARE(timestamp.created(), QDateTime::fromString("2015-04-14T16:16:44Z", Qt::ISODate)); QCOMPARE(timestamp.lastModified(), QDateTime::fromString("2015-04-14T21:56:56Z(2300)", Qt::ISODate)); } /* * Test parse a vcard with extend details */ void testVCardWithExtendDetails() { QString vcard = QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "N:Sauro;Dino;da Silva;;\r\n" "EMAILPID=1.1;:dino@familiadinosauro.com.br\r\n" "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" "TEL;PID=1.2;TYPE=CELL:8888888\r\n" "X-AIM:foo@aim.com\r\n" "X-REMOTE-ID:MY_REMOTE_ID\r\n" "END:VCARD\r\n"); QContact contact = VCardParser::vcardToContact(vcard); QList xDetails = contact.details(); QCOMPARE(xDetails.size(), 1); QContactExtendedDetail xDetail = xDetails.at(0); QCOMPARE(xDetail.name(), QStringLiteral("X-REMOTE-ID")); QCOMPARE(xDetail.data().toString(), QStringLiteral("MY_REMOTE_ID")); } void testVCardFromGoogle() { QString vcard = QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "REV:2015-04-16T15:26:50Z\r\n" "X-GOOGLE-ETAG:\"RXc_eTVSLit7I2A9XRdUF04NRwc.\"\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "PHOTO;VALUE=URL,URL:https://www.google.com/m8/feeds/photos/media/renato.test\r\n" " e2%40gmail.com/1dd7d51a1518626a\r\n" "N:;REnato;;;\r\n" "EMAIL:renatox@gmail.com\r\n" "TEL:87042144\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n"); QContact contact = VCardParser::vcardToContact(vcard); QList xDetails = contact.details(); QCOMPARE(xDetails.size(), 2); } void testContactIdToUid() { // Create manager to allow us to creact contact id QContactManager manager("memory"); QContact c = m_contacts[0]; c.setId(QContactId::fromString(QStringLiteral("qtcontacts:memory::11"))); QString vcard = VCardParser::contactToVcard(c); QVERIFY(vcard.contains("UID:11")); } }; QTEST_MAIN(VCardParseTest) #include "vcardparser-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/dummy-backend.cpp0000644000015300001610000003076412632607666026506 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "dummy-backend.h" #include "scoped-loop.h" #include "lib/qindividual.h" #include "common/vcard-parser.h" #include #include DummyBackendProxy::DummyBackendProxy() : m_adaptor(0), m_backend(0), m_primaryPersonaStore(0), m_backendStore(0), m_aggregator(0), m_isReady(false), m_individualsChangedDetailedId(0) { } DummyBackendProxy::~DummyBackendProxy() { shutdown(); } void DummyBackendProxy::start(bool useDBus) { m_useDBus = useDBus; initEnviroment(); initFolks(); } void DummyBackendProxy::shutdown() { m_isReady = false; if (m_adaptor) { QDBusConnection connection = QDBusConnection::sessionBus(); connection.unregisterObject(DUMMY_OBJECT_PATH); connection.unregisterService(DUMMY_SERVICE_NAME); delete m_adaptor; m_adaptor = 0; Q_EMIT stopped(); } Q_FOREACH(galera::QIndividual *i, m_contacts.values()) { delete i; } m_contacts.clear(); if (m_aggregator) { g_signal_handler_disconnect(m_aggregator, m_individualsChangedDetailedId); g_object_unref(m_aggregator); m_aggregator = 0; } if (m_primaryPersonaStore) { g_object_unref(m_primaryPersonaStore); m_primaryPersonaStore = 0; } if (m_backend) { g_object_unref(m_backend); m_backend = 0; } if (m_backendStore) { g_object_unref(m_backendStore); m_backendStore = 0; } } QList DummyBackendProxy::contacts() const { QList contacts; Q_FOREACH(galera::QIndividual *i, m_contacts.values()) { contacts << i->contact(); } return contacts; } QList DummyBackendProxy::individuals() const { return m_contacts.values(); } FolksIndividualAggregator *DummyBackendProxy::aggregator() const { return m_aggregator; } QStringList DummyBackendProxy::listContacts() const { return galera::VCardParser::contactToVcardSync(contacts()); } void DummyBackendProxy::reset() { if (m_contacts.count()) { GeeMap *map = folks_persona_store_get_personas(m_primaryPersonaStore); GeeCollection *personas = gee_map_get_values(map); folks_dummy_persona_store_unregister_personas(FOLKS_DUMMY_PERSONA_STORE(m_primaryPersonaStore), (GeeSet*)personas); g_object_unref(personas); m_contacts.clear(); } // remove any extra collection/persona store GeeHashSet *extraStores = gee_hash_set_new(FOLKS_TYPE_PERSONA_STORE, (GBoxedCopyFunc) g_object_ref, g_object_unref, NULL, NULL, NULL, NULL, NULL, NULL); GeeMap *currentStores = folks_backend_get_persona_stores(FOLKS_BACKEND(m_backend)); GeeSet *keys = gee_map_get_keys(currentStores); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(keys)); while(gee_iterator_next(iter)) { const gchar *key = (const gchar*) gee_iterator_get(iter); if (strcmp(key, "dummy-store") != 0) { FolksPersonaStore *store = FOLKS_PERSONA_STORE(gee_map_get(currentStores, key)); gee_abstract_collection_add(GEE_ABSTRACT_COLLECTION(extraStores), store); g_object_unref(store); } } if (gee_collection_get_size(GEE_COLLECTION(extraStores)) > 0) { folks_dummy_backend_unregister_persona_stores(m_backend, GEE_SET(extraStores)); } g_object_unref(extraStores); g_object_unref(keys); g_object_unref(iter); } void DummyBackendProxy::initFolks() { m_aggregator = folks_individual_aggregator_dup(); m_individualsChangedDetailedId = g_signal_connect(m_aggregator, "individuals-changed-detailed", (GCallback) DummyBackendProxy::individualsChangedCb, this); folks_individual_aggregator_prepare(m_aggregator, (GAsyncReadyCallback) DummyBackendProxy::individualAggregatorPrepared, this); } bool DummyBackendProxy::isReady() const { return m_isReady; } QString DummyBackendProxy::createContact(const QtContacts::QContact &qcontact) { ScopedEventLoop loop(&m_eventLoop); GHashTable *details = galera::QIndividual::parseDetails(qcontact); Q_ASSERT(details); Q_ASSERT(m_aggregator); folks_individual_aggregator_add_persona_from_details(m_aggregator, NULL, //parent m_primaryPersonaStore, details, (GAsyncReadyCallback) DummyBackendProxy::individualAggregatorAddedPersona, this); loop.exec(); g_hash_table_destroy(details); return QString(); } void DummyBackendProxy::contactUpdated(const QString &contactId, const QString &errorMsg) { m_contactUpdated = true; } QString DummyBackendProxy::updateContact(const QString &contactId, const QtContacts::QContact &qcontact) { galera::QIndividual *i = m_contacts.value(contactId); Q_ASSERT(i); ScopedEventLoop loop(&m_eventLoop); m_contactUpdated = false; i->update(qcontact, this, SLOT(contactUpdated(QString,QString))); loop.exec(); return i->id(); } void DummyBackendProxy::checkError(GError *error) { if (error) { qWarning() << error->message; g_error_free(error); } Q_ASSERT(error == 0); } void DummyBackendProxy::mkpath(const QString &path) const { QDir dir; if (!dir.mkpath(path)) { qWarning() << "Fail to create path" << path; } } void DummyBackendProxy::initEnviroment() { Q_ASSERT(m_tmpDir.isValid()); QString tmpFullPath = QString("%1").arg(m_tmpDir.path()); qputenv("FOLKS_BACKENDS_ALLOWED", "dummy"); qputenv("FOLKS_PRIMARY_STORE", "dummy"); mkpath(tmpFullPath); qDebug() << "setting up in transient directory:" << tmpFullPath; // home qputenv("HOME", tmpFullPath.toUtf8().data()); // cache QString cacheDir = QString("%1/.cache/").arg(tmpFullPath); mkpath(cacheDir); qputenv("XDG_CACHE_HOME", cacheDir.toUtf8().data()); // config QString configDir = QString("%1/.config").arg(tmpFullPath); mkpath(configDir); qputenv("XDG_CONFIG_HOME", configDir.toUtf8().data()); // data QString dataDir = QString("%1/.local/share").arg(tmpFullPath); mkpath(dataDir); qputenv("XDG_DATA_HOME", dataDir.toUtf8().data()); mkpath(QString("%1/folks").arg(dataDir)); // runtime QString runtimeDir = QString("%1/run").arg(tmpFullPath); mkpath(runtimeDir); qputenv("XDG_RUNTIME_DIR", runtimeDir.toUtf8().data()); qputenv("XDG_DESKTOP_DIR", ""); qputenv("XDG_DOCUMENTS_DIR", ""); qputenv("XDG_DOWNLOAD_DIR", ""); qputenv("XDG_MUSIC_DIR", ""); qputenv("XDG_PICTURES_DIR", ""); qputenv("XDG_PUBLICSHARE_DIR", ""); qputenv("XDG_TEMPLATES_DIR", ""); qputenv("XDG_VIDEOS_DIR", ""); } bool DummyBackendProxy::registerObject() { QDBusConnection connection = QDBusConnection::sessionBus(); if (!connection.registerService(DUMMY_SERVICE_NAME)) { qWarning() << "Could not register service!" << DUMMY_SERVICE_NAME; return false; } m_adaptor = new DummyBackendAdaptor(connection, this); if (!connection.registerObject(DUMMY_OBJECT_PATH, this)) { qWarning() << "Could not register object!" << DUMMY_OBJECT_PATH; delete m_adaptor; m_adaptor = 0; } else { qDebug() << "Object registered:" << QString(DUMMY_OBJECT_PATH); } return (m_adaptor != 0); } void DummyBackendProxy::individualAggregatorPrepared(FolksIndividualAggregator *fia, GAsyncResult *res, DummyBackendProxy *self) { GError *error = 0; folks_individual_aggregator_prepare_finish(fia, res, &error); checkError(error); self->m_backendStore = folks_backend_store_dup(); self->m_backend = FOLKS_DUMMY_BACKEND(folks_backend_store_dup_backend_by_name(self->m_backendStore, "dummy")); if (!self->m_backend) { qWarning() << "fail to load dummy backend"; } self->m_primaryPersonaStore = folks_individual_aggregator_get_primary_store(fia); g_object_ref(self->m_primaryPersonaStore); if (self->m_useDBus) { self->registerObject(); } self->m_isReady = true; Q_EMIT self->ready(); } void DummyBackendProxy::individualAggregatorAddedPersona(FolksIndividualAggregator *fia, GAsyncResult *res, DummyBackendProxy *self) { GError *error = 0; folks_individual_aggregator_add_persona_from_details_finish(fia, res, &error); checkError(error); self->m_eventLoop->quit(); self->m_eventLoop = 0; } void DummyBackendProxy::individualsChangedCb(FolksIndividualAggregator *individualAggregator, GeeMultiMap *changes, DummyBackendProxy *self) { Q_UNUSED(individualAggregator); GeeIterator *iter; GeeSet *removed = gee_multi_map_get_keys(changes); GeeCollection *added = gee_multi_map_get_values(changes); QStringList addedIds; iter = gee_iterable_iterator(GEE_ITERABLE(added)); while(gee_iterator_next(iter)) { FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter)); if (individual) { galera::QIndividual *idv = new galera::QIndividual(individual, self->m_aggregator); self->m_contacts.insert(idv->id(), idv); addedIds << idv->id(); g_object_unref(individual); } } g_object_unref (iter); iter = gee_iterable_iterator(GEE_ITERABLE(removed)); while(gee_iterator_next(iter)) { FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter)); if (individual) { QString id = QString::fromUtf8(folks_individual_get_id(individual)); if (!addedIds.contains(id) && self->m_contacts.contains(id)) { delete self->m_contacts.take(id); } g_object_unref(individual); } } g_object_unref (iter); g_object_unref(added); g_object_unref(removed); } DummyBackendAdaptor::DummyBackendAdaptor(const QDBusConnection &connection, DummyBackendProxy *parent) : QDBusAbstractAdaptor(parent), m_connection(connection), m_proxy(parent) { if (m_proxy->isReady()) { Q_EMIT ready(); } connect(m_proxy, SIGNAL(ready()), this, SIGNAL(ready())); } DummyBackendAdaptor::~DummyBackendAdaptor() { } bool DummyBackendAdaptor::isReady() { return m_proxy->isReady(); } bool DummyBackendAdaptor::ping() { return true; } void DummyBackendAdaptor::quit() { QMetaObject::invokeMethod(m_proxy, "shutdown", Qt::QueuedConnection); } void DummyBackendAdaptor::reset() { m_proxy->reset(); } QStringList DummyBackendAdaptor::listContacts() { return m_proxy->listContacts(); } QString DummyBackendAdaptor::createContact(const QString &vcard) { QtContacts::QContact contact = galera::VCardParser::vcardToContact(vcard); return m_proxy->createContact(contact); } QString DummyBackendAdaptor::updateContact(const QString &contactId, const QString &vcard) { QtContacts::QContact contact = galera::VCardParser::vcardToContact(vcard); return m_proxy->updateContact(contactId, contact); } void DummyBackendAdaptor::enableAutoLink(bool flag) { galera::QIndividual::enableAutoLink(flag); } address-book-service-0.1.1+16.04.20151211.1/tests/unittest/clause-test.cpp0000644000015300001610000003337212632607666026215 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common/filter.h" using namespace QtContacts; using namespace galera; class ClauseParseTest : public QObject { Q_OBJECT private Q_SLOTS: void testParseClause() { // Create manager to allow us to creact contact id QContactManager manager("memory"); QContactId id5 = QContactId::fromString("qtcontacts:memory::5"); QContactId id2 = QContactId::fromString("qtcontacts:memory::2"); QContactGuid guid5; guid5.setGuid("5"); QContactGuid guid2; guid2.setGuid("2"); // guid is necessary because our server uses that for compare ids // check Filter source code for more details QContact contact5; contact5.setId(id5); contact5.appendDetail(guid5); QContact contact2; contact2.setId(id2); contact2.appendDetail(guid2); QContactIdFilter originalFilter = QContactIdFilter(); originalFilter.setIds(QList() << QContactId::fromString("qtcontacts:memory::1") << QContactId::fromString("qtcontacts:memory::2") << QContactId::fromString("qtcontacts:memory::3")); QString filterString = Filter(originalFilter).toString(); Filter filterFromString = Filter(filterString); QCOMPARE(filterFromString.test(contact5), false); QCOMPARE(filterFromString.test(contact2), true); QCOMPARE(filterFromString.test(contact5), QContactManagerEngine::testFilter(originalFilter, contact5)); QCOMPARE(filterFromString.test(contact2), QContactManagerEngine::testFilter(originalFilter, contact2)); } void testIntersectionWithContactId() { // Create manager to allow us to creact contact id QContactManager manager("memory"); QContactId id5 = QContactId::fromString("qtcontacts:memory::5"); QContactId id2 = QContactId::fromString("qtcontacts:memory::2"); QContactGuid guid5; guid5.setGuid("5"); QContactGuid guid2; guid2.setGuid("2"); // guid is necessary because our server uses that for compare ids // check Filter source code for more details QContact contact5; contact5.setId(id5); contact5.appendDetail(guid5); QContact contact2; contact2.setId(id2); contact2.appendDetail(guid2); QContactIntersectionFilter iFilter; QContactIdFilter originalFilter = QContactIdFilter(); originalFilter.setIds(QList() << QContactId::fromString("qtcontacts:memory::1") << QContactId::fromString("qtcontacts:memory::2") << QContactId::fromString("qtcontacts:memory::3")); iFilter.setFilters(QList() << originalFilter); QString filterString = Filter(iFilter).toString(); Filter filterFromString = Filter(filterString); QCOMPARE(filterFromString.test(contact5), false); QCOMPARE(filterFromString.test(contact2), true); QCOMPARE(filterFromString.test(contact5), QContactManagerEngine::testFilter(iFilter, contact5)); QCOMPARE(filterFromString.test(contact2), QContactManagerEngine::testFilter(iFilter, contact2)); } void testIntersecionListFilter() { // Create manager to allow us to creact contact id QContactManager manager("memory"); QContactId id5 = QContactId::fromString("qtcontacts:memory::5"); QContactId id2 = QContactId::fromString("qtcontacts:memory::2"); QContactGuid guid5; guid5.setGuid("5"); QContactGuid guid2; guid2.setGuid("2"); // guid is necessary because our server uses that for compare ids // check Filter source code for more details QContact contact5; contact5.setId(id5); contact5.appendDetail(guid5); QContact contact2; contact2.setId(id2); contact2.appendDetail(guid2); QContactIntersectionFilter iFilter; QContactIntersectionFilter emptyField; QContactIdFilter originalFilter = QContactIdFilter(); originalFilter.setIds(QList() << QContactId::fromString("qtcontacts:memory::1") << QContactId::fromString("qtcontacts:memory::2") << QContactId::fromString("qtcontacts:memory::3")); iFilter.setFilters(QList() << originalFilter << emptyField); QString filterString = Filter(iFilter).toString(); Filter filterFromString = Filter(filterString); QCOMPARE(filterFromString.test(contact5), QContactManagerEngine::testFilter(iFilter, contact5)); QCOMPARE(filterFromString.test(contact2), QContactManagerEngine::testFilter(iFilter, contact2)); QCOMPARE(filterFromString.test(contact5), false); QCOMPARE(filterFromString.test(contact2), false); } void testSimpleEmptyFilter() { Filter filter(""); QVERIFY(filter.isEmpty()); QVERIFY(filter.isValid()); } void testUnionEmptyFilter() { QContactUnionFilter uFilter; Filter filter(uFilter); QVERIFY(filter.isEmpty()); QVERIFY(filter.isValid()); QList filters; filters << QContactDetailFilter(); uFilter.setFilters(filters); Filter filter2(uFilter); QVERIFY(!filter2.isEmpty()); QVERIFY(filter2.isValid()); } void testIntersectEmptyFilter() { QContactIntersectionFilter iFilter; Filter filter(iFilter); QVERIFY(filter.isEmpty()); QVERIFY(filter.isValid()); QList filters; filters << QContactDetailFilter(); iFilter.setFilters(filters); Filter filter2(iFilter); QVERIFY(!filter2.isEmpty()); QVERIFY(filter2.isValid()); } void testPhoneNumberFilter_data() { QTest::addColumn("phoneNumber"); QTest::addColumn("query"); QTest::addColumn("match"); // if they are the same phone number QTest::addColumn("matchContains"); // if the phoneNumber contains query QTest::addColumn("matchStartsWith"); // if the phoneNumber starts with query QTest::addColumn("matchEndsWith"); // if the phoneNumber ends with the query QTest::addColumn("matchExactly"); // if the phoneNumber exacly match query QTest::newRow("string equal") << "12345678" << "12345678" << true << true << true << true << true; QTest::newRow("number with dash") << "1234-5678" << "12345678" << true << true << true << true << true;; QTest::newRow("number with country code") << "+55(81)87042155" << "(81)87042155" << true << true << false << true << false; QTest::newRow("number with and without country code") << "+55(81)87042155" << "(81)87042155" << true << true << false << true << false; QTest::newRow("number with and without area code") << "(81)87042155" << "87042155" << true << true << false << true << false; QTest::newRow("number with extension") << "12345678#123" << "12345678" << true << true << true << false << false; QTest::newRow("both numbers with extension") << "(123)12345678#1" << "12345678#1" << true << true << false << true << false; QTest::newRow("numbers with different extension") << "1234567#1" << "1234567#2" << false << false << false << false << false; QTest::newRow("short/emergency numbers") << "190" << "190" << true << true << true << true << true; QTest::newRow("different short/emergency numbers") << "911" << "11" << false << true << false << true << false; QTest::newRow("different numbers") << "12345678" << "1234567" << false << true << true << false << false; QTest::newRow("both non phone numbers") << "abcdefg" << "abcdefg" << false << false << false << false << false; QTest::newRow("different non phone numbers") << "abcdefg" << "bcdefg" << false << false << false << false << false; QTest::newRow("phone number and custom string") << "abc12345678" << "12345678" << true << true << true << true << false; QTest::newRow("Not match") << "+352 661 123456" << "+352 691 123456" << false << false << false << false << false; QTest::newRow("Phone number and small value") << "32634146" << "146" << false << true << false << true << false; QTest::newRow("Small phone numbers numbers") << "32634911" << "911" << false << true << false << true << false; QTest::newRow("Phone number and small number") << "32634911" << "+146" << false << false << false << false << false; } void testPhoneNumberFilter() { QFETCH(QString, phoneNumber); QFETCH(QString, query); QFETCH(bool, match); QFETCH(bool, matchContains); QFETCH(bool, matchStartsWith); QFETCH(bool, matchEndsWith); QFETCH(bool, matchExactly); QContact c; QContactPhoneNumber p; p.setNumber(phoneNumber); c.saveDetail(&p); // if they are the same phone number QContactDetailFilter f = QContactPhoneNumber::match(query); Filter myFilter(f); QCOMPARE(myFilter.test(c), match); // if the phoneNumber contains query f.setMatchFlags(QContactFilter::MatchPhoneNumber | QContactFilter::MatchContains); myFilter = Filter(f); QCOMPARE(myFilter.test(c), matchContains); // if the phoneNumber starts with query f.setMatchFlags(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith); myFilter = Filter(f); QCOMPARE(myFilter.test(c), matchStartsWith); // if the phoneNumber ends with the query f.setMatchFlags(QContactFilter::MatchPhoneNumber | QContactFilter::MatchEndsWith); myFilter = Filter(f); QCOMPARE(myFilter.test(c), matchEndsWith); // Exactly the same number f.setMatchFlags(QContactFilter::MatchPhoneNumber | QContactFilter::MatchExactly); myFilter = Filter(f); // Failing due the MatchFlag problem check: https://bugreports.qt.io/browse/QTBUG-47546 //QCOMPARE(myFilter.test(c), matchExactly); } void testExtractIds() { QContactIdFilter originalFilter; originalFilter.setIds(QList() << QContactId::fromString("qtcontacts:memory::1") << QContactId::fromString("qtcontacts:memory::2") << QContactId::fromString("qtcontacts:memory::3")); Filter f(originalFilter); QStringList ids = f.idsToFilter(); QCOMPARE(ids.size(), 3); Q_FOREACH(const QContactId &id, originalFilter.ids()) { ids.removeOne(id.toString().split(":").last()); } QCOMPARE(ids.size(), 0); } void testIncludeDeleted() { QContactChangeLogFilter removedFilter; removedFilter.setEventType(QContactChangeLogFilter::EventRemoved); removedFilter.setSince(QDateTime::currentDateTime()); Filter changeLogFilter(removedFilter); QVERIFY(changeLogFilter.includeRemoved()); QContactDetailFilter detFilter; detFilter.setDetailType(QContactFavorite::Type, QContactFavorite::FieldFavorite); detFilter.setValue(true); Filter favoriteFilter(detFilter); QVERIFY(!favoriteFilter.includeRemoved()); Filter combination(detFilter & removedFilter); QVERIFY(combination.includeRemoved()); } void testDeletedContact() { // create a contact with name QContact c; QContactName name; name.setFirstName("Foo"); name.setLastName("Bar"); c.saveDetail(&name); QContactDetailFilter favFilter; favFilter.setDetailType(QContactFavorite::Type, QContactFavorite::FieldFavorite); favFilter.setValue(true); // filter favorite contacts Filter myFilter(favFilter); QVERIFY(!myFilter.test(c)); // mark contact as favorite QContactFavorite fav; fav.setFavorite(true); c.saveDetail(&fav); // filter favorite contacts QVERIFY(myFilter.test(c)); // filter contacts removed and favorite QVERIFY(!myFilter.test(c, QDateTime::currentDateTime())); QContactChangeLogFilter removedFilter; removedFilter.setEventType(QContactChangeLogFilter::EventRemoved); removedFilter.setSince(QDateTime::currentDateTime().addDays(-1)); Filter removedAndFavoriteFilter(favFilter & removedFilter); // filter favorites and removed contacts QVERIFY(!removedAndFavoriteFilter.test(c)); QContactExtendedDetail removedDate; removedDate.setName("X-DELETED-AT"); removedDate.setData(QDateTime::currentDateTime()); c.saveDetail(&removedDate); // filter again with favorites and removed contacts QVERIFY(removedAndFavoriteFilter.test(c, QDateTime::currentDateTime())); } }; QTEST_MAIN(ClauseParseTest) #include "clause-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/dummy-backend-defs.h0000644000015300001610000000170512632607666027063 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __DUMMY_BACKEND_DEFS_H__ #define __DUMMY_BACKEND_DEFS_H__ #define DUMMY_SERVICE_NAME "com.canonical.test.folks" #define DUMMY_IFACE_NAME "com.canonical.test.folks.Dummy" #define DUMMY_OBJECT_PATH "/com/canonical/test/folks/Dummy" #endif address-book-service-0.1.1+16.04.20151211.1/tests/unittest/readonly-prop-test.cpp0000644000015300001610000000534512632607666027533 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include class ReadOnlyPropTest : public BaseClientTest { Q_OBJECT private: QString m_basicVcard; QString m_resultBasicVcard; private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); m_basicVcard = QStringLiteral("BEGIN:VCARD\n" "VERSION:3.0\n" "N:Tal;Fulano_;de;;\n" "EMAIL:fulano_@ubuntu.com\n" "TEL;PID=1.1;TYPE=ISDN:33331410\n" "TEL;PID=1.2;TYPE=CELL:8888888\n" "URL:www.canonical.com\n" "END:VCARD"); } void testCreateContactAndReturnReadOlyFileds() { // call create contact QDBusReply reply = m_serverIface->call("createContact", m_basicVcard, "dummy-store"); // check if the returned id is valid QString newContactId = reply.value(); QVERIFY(!newContactId.isEmpty()); // check if the cotact was created with URL as read-only field // URL is not a writable property on our dummy backend QDBusReply reply2 = m_dummyIface->call("listContacts"); QCOMPARE(reply2.value().count(), 1); QList contactsCreated = galera::VCardParser::vcardToContactSync(reply2.value()); QCOMPARE(contactsCreated.count(), 1); Q_FOREACH(QContactDetail det, contactsCreated[0].details()) { if (det.type() == QContactDetail::TypeUrl) { QVERIFY(det.accessConstraints().testFlag(QContactDetail::ReadOnly)); } else { QVERIFY(!det.accessConstraints().testFlag(QContactDetail::ReadOnly)); } } } }; QTEST_MAIN(ReadOnlyPropTest) #include "readonly-prop-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contact-avatar-test.cpp0000644000015300001610000002231312632607666027641 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "config.h" #include "common/vcard-parser.h" #include "base-eds-test.h" using namespace QtContacts; class ContactAvatarTest : public QObject, public BaseEDSTest { Q_OBJECT private: private Q_SLOTS: void initTestCase() { BaseEDSTest::initTestCaseImpl(); } void cleanupTestCase() { BaseEDSTest::cleanupTestCaseImpl(); } void init() { BaseEDSTest::initImpl(); } void cleanup() { QList contacts = m_manager->contactIds(); m_manager->removeContacts(contacts); BaseEDSTest::cleanupImpl(); } void testCreateAvatarWithRevision() { QContact contact = galera::VCardParser::vcardToContact(QString("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:123456\r\n" "X-AVATAR-REV:1234567890\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "PHOTO;VALUE=URL:file://%1/avatar.svg\r\n" "END:VCARD\r\n").arg(TEST_DATA_DIR)); // create a contact QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // qery contacts; QContactFilter filter; QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContactExtendedDetail avatarRev; Q_FOREACH(const QContactExtendedDetail &det, contacts[0].details()) { if (det.name() == "X-AVATAR-REV") { avatarRev = det; } } QCOMPARE(avatarRev.data().toString(), QStringLiteral("1234567890")); QContactAvatar avatar = contacts[0].detail(); QVERIFY(avatar.imageUrl().toString().startsWith(QStringLiteral("file:///tmp/contact-avatar-test-"))); } void testModifyAvatarLocally() { QContact contact = galera::VCardParser::vcardToContact(QString("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:123456\r\n" "X-AVATAR-REV:1234567890\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "PHOTO;VALUE=URL:file://%1/avatar.svg\r\n" "END:VCARD\r\n").arg(TEST_DATA_DIR)); // create a contact QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // qery contacts; QList contacts = m_manager->contacts(); QCOMPARE(contacts.size(), 1); // update a contact QContact newContact(contacts[0]); QContactAvatar avatar = newContact.detail(); QUrl oldAvatar = avatar.imageUrl(); avatar.setImageUrl(QUrl::fromLocalFile(QString("%1/new_avatar.svg").arg(TEST_DATA_DIR))); newContact.saveDetail(&avatar); QSignalSpy spyContactChanged(m_manager, SIGNAL(contactsChanged(QList))); result = m_manager->saveContact(&newContact); QCOMPARE(result, true); QTRY_COMPARE(spyContactChanged.count(), 1); // query the updated contact contacts = m_manager->contacts(); QCOMPARE(contacts.size(), 1); newContact = contacts[0]; // check if contact was updated QContactExtendedDetail avatarRev; Q_FOREACH(const QContactExtendedDetail &det, newContact.details()) { if (det.name() == "X-AVATAR-REV") { avatarRev = det; } } // Avatar revision must be reseted during a loca update QVERIFY(avatarRev.data().toString().isEmpty()); // Avatar should have a new value avatar = newContact.detail(); QVERIFY(!avatar.imageUrl().isEmpty()); QVERIFY(avatar.imageUrl() != oldAvatar); } void testModifyAvatarAndVersion() { QContact contact = galera::VCardParser::vcardToContact(QString("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:123456\r\n" "X-AVATAR-REV:1234567890\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "PHOTO;VALUE=URL:file://%1/avatar.svg\r\n" "END:VCARD\r\n").arg(TEST_DATA_DIR)); // create a contact QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // qery contacts; QList contacts = m_manager->contacts(); QCOMPARE(contacts.size(), 1); // update a contact QContact newContact(contacts[0]); QContactAvatar avatar = newContact.detail(); QUrl oldAvatar = avatar.imageUrl(); avatar.setImageUrl(QUrl::fromLocalFile(QString("%1/new_avatar.svg").arg(TEST_DATA_DIR))); newContact.saveDetail(&avatar); // udate avatar version QContactExtendedDetail avatarRev; Q_FOREACH(const QContactExtendedDetail &det, newContact.details()) { if (det.name() == "X-AVATAR-REV") { avatarRev = det; } } QString avatarRevValue(QDateTime::currentDateTime().toString(Qt::ISODate)); avatarRev.setData(avatarRevValue); newContact.saveDetail(&avatarRev); // save contact QSignalSpy spyContactChanged(m_manager, SIGNAL(contactsChanged(QList))); result = m_manager->saveContact(&newContact); QCOMPARE(result, true); QTRY_COMPARE(spyContactChanged.count(), 1); // query the updated contact contacts = m_manager->contacts(); QCOMPARE(contacts.size(), 1); newContact = contacts[0]; // check if contact was updated avatarRev = QContactExtendedDetail(); Q_FOREACH(const QContactExtendedDetail &det, newContact.details()) { if (det.name() == "X-AVATAR-REV") { avatarRev = det; } } // Avatar revision must be updated to the new value QCOMPARE(avatarRev.data().toString(), avatarRevValue); // Avatar should have a new value avatar = newContact.detail(); QVERIFY(!avatar.imageUrl().isEmpty()); QVERIFY(avatar.imageUrl() != oldAvatar); } }; QTEST_MAIN(ContactAvatarTest) #include "contact-avatar-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contactmap-test.cpp0000644000015300001610000001705012632607666027065 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "dummy-backend.h" #include "scoped-loop.h" #include "lib/contacts-map.h" #include "lib/qindividual.h" #include #include #include #include #include #include class ContactMapTest : public QObject { Q_OBJECT private: DummyBackendProxy *m_dummy; galera::ContactsMap m_map; QList m_individuals; int randomIndex() const { return qrand() % m_map.size(); } FolksIndividual *randomIndividual() const { return m_individuals[randomIndex()]; } void createContactWithSuffix(const QString &suffix) { QtContacts::QContact contact; QtContacts::QContactName name; name.setFirstName(QString("Fulano_%1").arg(suffix)); name.setMiddleName("de"); name.setLastName("Tal"); contact.saveDetail(&name); QtContacts::QContactEmailAddress email; email.setEmailAddress(QString("fulano_%1@ubuntu.com").arg(suffix)); contact.saveDetail(&email); QtContacts::QContactPhoneNumber phone; phone.setNumber(QString("33331410%1").arg(suffix)); contact.saveDetail(&phone); m_dummy->createContact(contact); } void createContactWithPhone(const QString &phoneNumber) { QtContacts::QContact contact; QtContacts::QContactName name; name.setFirstName("Foo"); name.setLastName("Bar"); contact.saveDetail(&name); QtContacts::QContactEmailAddress email; email.setEmailAddress("foo@ubuntu.com"); contact.saveDetail(&email); QtContacts::QContactPhoneNumber phone; phone.setNumber(phoneNumber); contact.saveDetail(&phone); m_dummy->createContact(contact); } QStringList allPhones() { QStringList phones; phones << "12345678" << "1234-5678" << "+55(81)87042155" << "(81)87042155" << "12345678#123" << "(123)12345678#1" << "1234567#1" << "190" << "911" << "abcdefg" << "abc12345678" << "+352 691 123456" << "32634146" << "32634911"; return phones; } private Q_SLOTS: void initTestCase() { m_dummy = new DummyBackendProxy(); m_dummy->start(); QTRY_VERIFY(m_dummy->isReady()); createContactWithSuffix("1"); createContactWithSuffix("2"); createContactWithSuffix("3"); Q_FOREACH(galera::QIndividual *i, m_dummy->individuals()) { m_map.insert(new galera::ContactEntry(new galera::QIndividual(i->individual(), m_dummy->aggregator()))); m_individuals << i->individual(); } } void cleanupTestCase() { m_dummy->shutdown(); delete m_dummy; m_map.clear(); } void testLookupByFolksIndividual() { QVERIFY(m_map.size() > 0); FolksIndividual *fIndividual = randomIndividual(); QVERIFY(m_map.contains(fIndividual)); galera::ContactEntry *entry = m_map.value(fIndividual); QVERIFY(entry->individual()->individual() == fIndividual); } void testLookupByFolksIndividualId() { FolksIndividual *fIndividual = randomIndividual(); QString id = QString::fromUtf8(folks_individual_get_id(fIndividual)); galera::ContactEntry *entry = m_map.value(id); QVERIFY(entry->individual()->individual() == fIndividual); } void testValues() { QList entries = m_map.values(); QCOMPARE(entries.size(), m_map.size()); QCOMPARE(m_individuals.size(), m_map.size()); Q_FOREACH(FolksIndividual *individual, m_individuals) { QVERIFY(m_map.contains(individual)); } } void testTakeIndividual() { FolksIndividual *individual = folks_individual_new(0); QVERIFY(m_map.take(individual) == 0); QVERIFY(!m_map.contains(individual)); g_object_unref(individual); individual = randomIndividual(); galera::ContactEntry *entry = m_map.take(individual); QVERIFY(entry->individual()->individual() == individual); //put it back m_map.insert(entry); } void testLookupByVcard() { FolksIndividual *individual = randomIndividual(); QString id = QString::fromUtf8(folks_individual_get_id(individual)); QString vcard = QString("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "UID:%1\r\n" "N:Gump;Forrest\r\n" "FN:Forrest Gump\r\n" "REV:2008-04-24T19:52:43Z\r\n").arg(id); galera::ContactEntry *entry = m_map.valueFromVCard(vcard); QVERIFY(entry); QVERIFY(entry->individual()->individual() == individual); } void testLookupByPhone_data() { QStringList phones = allPhones(); Q_FOREACH(const QString &phone, phones) { createContactWithPhone(phone); } Q_FOREACH(galera::QIndividual *i, m_dummy->individuals()) { m_map.insert(new galera::ContactEntry(new galera::QIndividual(i->individual(), m_dummy->aggregator()))); m_individuals << i->individual(); } QTest::addColumn("query"); QTest::addColumn("numberOfMatches"); QTest::newRow("string equal") << "12345678" << 3; QTest::newRow("number with dash") << "1234-5678" << 3; QTest::newRow("plain number") << "87042155" << 2; QTest::newRow("number with area code") << "(81)87042155" << 2; QTest::newRow("number with country code") << "+55(81)87042155" << 2; QTest::newRow("number with extension") << "12345678" << 3; QTest::newRow("both numbers with extension") << "12345678#1" << 1; QTest::newRow("numbers with different extension") << "1234567#2" << 0; QTest::newRow("short/emergency numbers") << "190" << 1; QTest::newRow("different short/emergency numbers") << "11" << 0; QTest::newRow("different numbers") << "1234567" << 0; QTest::newRow("both non phone numbers") << "abcdefg" << 0; QTest::newRow("different non phone numbers") << "bcdefg" << 0; QTest::newRow("phone number and custom string") << "bcdefg12345678" << 3; QTest::newRow("partial match") << "+352 691 123456" << 1; QTest::newRow("Phone number and small value") << "146" << 0; QTest::newRow("Small phone numbers") << "911" << 1; QTest::newRow("Phone number and small number") << "+146" << 0; } void testLookupByPhone() { QFETCH(QString, query); QFETCH(int, numberOfMatches); QList entries = m_map.valueByPhone(query); QCOMPARE(entries.size(), numberOfMatches); } }; QTEST_MAIN(ContactMapTest) #include "contactmap-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/fetch-hint-test.cpp0000644000015300001610000000473412632607666026772 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common/fetch-hint.h" using namespace QtContacts; using namespace galera; class FetchHintTest : public QObject { Q_OBJECT private Q_SLOTS: void testSimpleFields() { const QString strHint = QString("FIELDS:N,TEL"); FetchHint hint(strHint); QVERIFY(!hint.isEmpty()); QCOMPARE(hint.fields(), QStringList() << "N" << "TEL"); QContactFetchHint cHint; cHint.setDetailTypesHint(QList() << QContactDetail::TypeName << QContactDetail::TypePhoneNumber); QVERIFY(cHint.detailTypesHint() == hint.toContactFetchHint().detailTypesHint()); FetchHint hint2(cHint); QCOMPARE(hint2.toString(), strHint); } void testInvalidHint() { const QString strHint = QString("FIELDSS:N,TEL"); FetchHint hint(strHint); QVERIFY(hint.isEmpty()); } void testInvalidFieldName() { const QString strHint = QString("FIELDS:N,NONE,TEL,INVALID"); FetchHint hint(strHint); QVERIFY(!hint.isEmpty()); QCOMPARE(hint.fields(), QStringList() << "N" << "TEL"); } void testParseFieldNames() { QList fields = FetchHint::parseFieldNames(QStringList() << "ADR" << "BDAY" << "N" << "TEL"); QList expectedFields; expectedFields << QContactDetail::TypeAddress << QContactDetail::TypeBirthday << QContactDetail::TypeName << QContactDetail::TypePhoneNumber; QCOMPARE(fields, expectedFields); } }; QTEST_MAIN(FetchHintTest) #include "fetch-hint-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contact-link-test.cpp0000644000015300001610000000616612632607666027330 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "lib/qindividual.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include class ContactLinkTest : public BaseClientTest { Q_OBJECT private: QString m_vcard; private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); m_vcard = QStringLiteral("BEGIN:VCARD\n" "VERSION:3.0\n" "N:tal;fulano_;de;;\n" "EMAIL:email@ubuntu.com\n" "TEL;PID=1.1;TYPE=ISDN:33331410\n" "END:VCARD"); } void testCreateContactWithSameEmail() { // call create contact QDBusReply reply = m_serverIface->call("createContact", m_vcard, "dummy-store"); // check if the returned id is valid QString contactAId = reply.value(); QVERIFY(!contactAId.isEmpty()); QString vcardB = m_vcard.replace("fulano", "contact"); reply = m_serverIface->call("createContact", vcardB, "dummy-store"); // check if the returned id is valid QString contactBId = reply.value(); QVERIFY(!contactBId.isEmpty()); QVERIFY(contactAId != contactBId); } void testAppendOtherContactEmail() { // call create contact QDBusReply reply = m_serverIface->call("createContact", m_vcard, "dummy-store"); // check if the returned id is valid QString contactAId = reply.value(); QVERIFY(!contactAId.isEmpty()); // add a contact with diff email QString vcardB = m_vcard.replace("fulano", "contact").replace("email","email2"); reply = m_serverIface->call("createContact", vcardB, "dummy-store"); // check if the returned id is valid QString contactBId = reply.value(); QVERIFY(!contactBId.isEmpty()); // udate contactB with same email as contactA vcardB = m_vcard.replace("fulano", "contact"); reply = m_serverIface->call("createContact", vcardB, "dummy-store"); // check if the returned id is valid contactBId = reply.value(); QVERIFY(!contactBId.isEmpty()); // check if id still different QVERIFY(contactAId != contactBId); } }; QTEST_MAIN(ContactLinkTest) #include "contact-link-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/addressbook-test.cpp0000644000015300001610000003235112632607666027235 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include class AddressBookTest : public BaseClientTest { Q_OBJECT private: QString m_basicVcard; QString m_resultBasicVcard; QtContacts::QContact basicContactWithId(const QString &id) { QString newVcard = m_resultBasicVcard.arg(id); return galera::VCardParser::vcardToContact(newVcard); } void compareContact(const QtContacts::QContact &contact, const QtContacts::QContact &other) { // id QCOMPARE(contact.id(), other.id()); // name QCOMPARE(contact.detail(QtContacts::QContactDetail::TypeName), other.detail(QtContacts::QContactDetail::TypeName)); // phone - this is necessary because: // 1 ) the QContactDetail::FieldDetailUri can change based on the detail order // 2 ) the phone number can be returned in different order QList phones = contact.details(QtContacts::QContactDetail::TypePhoneNumber); QList otherPhones = other.details(QtContacts::QContactDetail::TypePhoneNumber); QCOMPARE(phones.size(), otherPhones.size()); for(int i=0; i < phones.size(); i++) { QtContacts::QContactDetail phone = phones[i]; bool found = false; for(int x=0; x < otherPhones.size(); x++) { QtContacts::QContactDetail otherPhone = otherPhones[x]; if (phone.value(QtContacts::QContactPhoneNumber::FieldNumber) == otherPhone.value(QtContacts::QContactPhoneNumber::FieldNumber)) { found = true; QList phoneTypes = phone.value(QtContacts::QContactPhoneNumber::FieldSubTypes).value< QList >(); QList otherPhoneTypes = otherPhone.value(QtContacts::QContactPhoneNumber::FieldSubTypes).value< QList >(); QCOMPARE(phoneTypes, otherPhoneTypes); QCOMPARE(phone.value(QtContacts::QContactPhoneNumber::FieldContext), otherPhone.value(QtContacts::QContactPhoneNumber::FieldContext)); break; } } QVERIFY2(found, "Phone number is not equal"); } // email same as phone number QList emails = contact.details(QtContacts::QContactDetail::TypeEmailAddress); QList otherEmails = other.details(QtContacts::QContactDetail::TypeEmailAddress); QCOMPARE(emails.size(), otherEmails.size()); for(int i=0; i < emails.size(); i++) { QtContacts::QContactDetail email = emails[i]; bool found = false; for(int x=0; x < otherEmails.size(); x++) { QtContacts::QContactDetail otherEmail = otherEmails[x]; if (email.value(QtContacts::QContactEmailAddress::FieldEmailAddress) == otherEmail.value(QtContacts::QContactEmailAddress::FieldEmailAddress)) { found = true; QCOMPARE(email.value(QtContacts::QContactEmailAddress::FieldContext), otherEmail.value(QtContacts::QContactEmailAddress::FieldContext)); break; } } QVERIFY2(found, "Email is not equal"); } } private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); m_basicVcard = QStringLiteral("BEGIN:VCARD\n" "VERSION:3.0\n" "N:Tal;Fulano_;de;;\n" "EMAIL:fulano_@ubuntu.com\n" "TEL;PID=1.1;TYPE=ISDN:33331410\n" "TEL;PID=1.2;TYPE=CELL:8888888\n" "END:VCARD"); m_resultBasicVcard = QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "UID:%1\r\n" "CLIENTPIDMAP:1;dummy:dummy-store:%2\r\n" "N;PID=1.1:Tal;Fulano_;de;;\r\n" "FN;PID=1.1:Fulano_ Tal\r\n" "X-QTPROJECT-FAVORITE;PID=1.1:false;0\r\n" "EMAIL;PID=1.1:fulano_@ubuntu.com\r\n" "TEL;PID=1.1;TYPE=ISDN:33331410\r\n" "TEL;PID=1.2;TYPE=CELL:8888888\r\n" "END:VCARD\r\n"); } void testSortFields() { QStringList defaultSortFields; defaultSortFields << "ADDR_COUNTRY" << "ADDR_LOCALITY" << "ADDR_POSTCODE" << "ADDR_POST_OFFICE_BOX" << "ADDR_REGION" << "ADDR_STREET" << "BIRTHDAY" << "EMAIL" << "FIRST_NAME" << "FULL_NAME" << "IM_PROTOCOL" << "IM_URI" << "LAST_NAME" << "MIDLE_NAME" << "NAME_PREFIX" << "NAME_SUFFIX" << "NICKNAME" << "ORG_DEPARTMENT" << "ORG_LOCATION" << "ORG_NAME" << "ORG_ROLE" << "ORG_TITLE" << "PHONE" << "PHOTO" << "TAG" << "URL"; QDBusReply reply = m_serverIface->call("sortFields"); QCOMPARE(reply.value(), defaultSortFields); } void testSource() { QDBusReply reply = m_serverIface->call("source"); QVERIFY(reply.isValid()); QCOMPARE(reply.value().id(), QStringLiteral("dummy-store")); } void testAvailableSources() { QDBusReply > reply = m_serverIface->call("availableSources"); galera::SourceList list = reply.value(); QCOMPARE(list.count(), 1); galera::Source src = list[0]; QCOMPARE(src.id(), QStringLiteral("dummy-store")); QCOMPARE(src.displayLabel(), QStringLiteral("Dummy personas")); QCOMPARE(src.isReadOnly(), false); } void testCreateContact() { // spy 'contactsAdded' signal QSignalSpy addedContactSpy(m_serverIface, SIGNAL(contactsAdded(QStringList))); // call create contact QDBusReply reply = m_serverIface->call("createContact", m_basicVcard, "dummy-store"); // check if the returned contact is valid QString vcard = reply.value(); QVERIFY(!vcard.isEmpty()); // check if the cotact was created with the correct fields QtContacts::QContact newContact = galera::VCardParser::vcardToContact(vcard); QDBusReply reply2 = m_dummyIface->call("listContacts"); QCOMPARE(reply2.value().count(), 1); QList contactsCreated = galera::VCardParser::vcardToContactSync(reply2.value()); QCOMPARE(contactsCreated.count(), 1); compareContact(contactsCreated[0], newContact); // check if the signal "contactAdded" was fired QTRY_COMPARE(addedContactSpy.count(), 1); QList args = addedContactSpy.takeFirst(); QCOMPARE(args.count(), 1); QStringList ids = args[0].toStringList(); QCOMPARE(ids[0], newContact.detail().guid()); } void testDuplicateContact() { // spy 'contactsAdded' signal QSignalSpy addedContactSpy(m_serverIface, SIGNAL(contactsAdded(QStringList))); // call create contact first QDBusReply reply = m_serverIface->call("createContact", m_basicVcard, "dummy-store"); // wait for folks to emit the signal QTRY_COMPARE(addedContactSpy.count(), 1); // user returned id to fill the new vcard QString newVcard = reply.value(); // try create a contact with the same id QDBusReply reply2 = m_serverIface->call("createContact", newVcard, "dummy-store"); // contactsAdded should be fired only once QTRY_COMPARE(addedContactSpy.count(), 1); // result should be null QVERIFY(reply2.value().isEmpty()); } void testCreateInvalidContact() { // spy 'contactsAdded' signal QSignalSpy addedContactSpy(m_serverIface, SIGNAL(contactsAdded(QStringList))); // call create contact with a invalid vcard string QDBusReply reply = m_serverIface->call("createContact", "INVALID VCARD", "dummy-store"); // wait for folks to emit the signal QTest::qWait(500); QVERIFY(reply.value().isEmpty()); QCOMPARE(addedContactSpy.count(), 0); } void testRemoveContact() { // create a basic contact QSignalSpy addedContactSpy(m_serverIface, SIGNAL(contactsAdded(QStringList))); QDBusReply replyAdd = m_serverIface->call("createContact", m_basicVcard, "dummy-store"); QString vcard = replyAdd.value(); // wait for added signal QTRY_COMPARE(addedContactSpy.count(), 1); // spy 'contactsRemoved' signal QSignalSpy removedContactSpy(m_serverIface, SIGNAL(contactsRemoved(QStringList))); // try remove the contact created QContact newContact = galera::VCardParser::vcardToContact(vcard); QString newContactId = newContact.detail().guid(); QDBusReply replyRemove = m_serverIface->call("removeContacts", QStringList() << newContactId); QCOMPARE(replyRemove.value(), 1); // check if the 'contactsRemoved' signal was fired with the correct args QTRY_COMPARE(removedContactSpy.count(), 1); QList args = removedContactSpy.takeFirst(); QCOMPARE(args.count(), 1); QStringList ids = args[0].toStringList(); QCOMPARE(ids[0], newContactId); // check if the contact was removed from the backend QDBusReply replyList = m_dummyIface->call("listContacts"); QCOMPARE(replyList.value().count(), 0); } void testUpdateContact() { // create a basic contact QSignalSpy addedContactSpy(m_serverIface, SIGNAL(contactsAdded(QStringList))); QDBusReply replyAdd = m_serverIface->call("createContact", m_basicVcard, "dummy-store"); // wait for added signal QTRY_COMPARE(addedContactSpy.count(), 1); QString vcard = replyAdd.value(); QContact newContact = galera::VCardParser::vcardToContact(vcard); QString newContactId = newContact.detail().guid(); // update the contact phone number vcard = vcard.replace("8888888", "0000000"); QtContacts::QContact contactUpdated = galera::VCardParser::vcardToContact(vcard); // spy 'contactsUpdated' signal QSignalSpy updateContactSpy(m_serverIface, SIGNAL(contactsUpdated(QStringList))); QDBusReply replyUpdate = m_serverIface->call("updateContacts", QStringList() << vcard); QStringList result = replyUpdate.value(); QCOMPARE(result.size(), 1); // check if contact returned by update function contains the new data QList contacts = galera::VCardParser::vcardToContactSync(result); QtContacts::QContact contactUpdatedResult = contacts[0]; compareContact(contactUpdatedResult, contactUpdated); // check if the 'contactsUpdated' signal was fired with the correct args QTRY_COMPARE(updateContactSpy.count(), 1); QList args = updateContactSpy.takeFirst(); QCOMPARE(args.count(), 1); QStringList ids = args[0].toStringList(); QCOMPARE(ids[0], newContactId); // check if the contact was updated into the backend QDBusReply replyList = m_dummyIface->call("listContacts"); result = replyList.value(); QCOMPARE(result.count(), 1); contacts = galera::VCardParser::vcardToContactSync(result); contactUpdatedResult = contacts[0]; compareContact(contactUpdatedResult, contactUpdated); } }; QTEST_MAIN(AddressBookTest) #include "addressbook-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/qcontacts-test.cpp0000644000015300001610000005276312632607666026745 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include #include "config.h" using namespace QtContacts; class QContactsTest : public BaseClientTest { Q_OBJECT private: QContactManager *m_manager; QContact testContact() { // create a contact QContact contact; QContactName name; name.setFirstName("Fulano"); name.setMiddleName("de"); name.setLastName("Tal"); contact.saveDetail(&name); QContactEmailAddress email; email.setEmailAddress("fulano@email.com"); contact.saveDetail(&email); return contact; } private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); } void init() { BaseClientTest::init(); m_manager = new QContactManager("galera"); } void cleanup() { delete m_manager; BaseClientTest::cleanup(); } void testAvailableManager() { QVERIFY(QContactManager::availableManagers().contains("galera")); } /* * Test create a new contact */ void testCreateContact() { // filter all contacts QContactFilter filter; // check result, must be empty QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 0); // create a contact QDateTime createdAt = QDateTime::currentDateTime(); QContact contact = testContact(); QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // query for new contacts contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContact createdContact = contacts[0]; // id QVERIFY(!createdContact.id().isNull()); // createdAt QContactTimestamp timestamp = contact.detail(); QVERIFY(createdAt.secsTo(timestamp.created()) < 60); QVERIFY(createdAt.secsTo(timestamp.lastModified()) < 60); // email QContactEmailAddress email = contact.detail(); QContactEmailAddress createdEmail = createdContact.detail(); QCOMPARE(createdEmail.emailAddress(), email.emailAddress()); // name QContactName name = contact.detail(); QContactName createdName = createdContact.detail(); QCOMPARE(createdName.firstName(), name.firstName()); QCOMPARE(createdName.middleName(), name.middleName()); QCOMPARE(createdName.lastName(), name.lastName()); QContactSyncTarget target = contact.detail(); QCOMPARE(target.syncTarget(), QStringLiteral("Dummy personas")); QCOMPARE(target.value(QContactSyncTarget::FieldSyncTarget + 1).toString(), QStringLiteral("dummy-store")); } /* * Test update a contact */ void testUpdateContact() { // filter all contacts QContactFilter filter; // create a contact QContact contact = testContact(); QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // modify contact QContactName name = contact.detail(); name.setMiddleName("da"); name.setLastName("Silva"); contact.saveDetail(&name); QSignalSpy spyContactChanged(m_manager, SIGNAL(contactsChanged(QList))); result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactChanged.count(), 1); // query for the contacts QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContact updatedContact = contacts[0]; // name QContactName updatedName = updatedContact.detail(); QCOMPARE(updatedName.firstName(), name.firstName()); QCOMPARE(updatedName.middleName(), name.middleName()); QCOMPARE(updatedName.lastName(), name.lastName()); } /* * Test query a contact source using the contact group */ void testQueryGroups() { QContactManager manager("galera"); // filter all contact groups/addressbook QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); filter.setValue(QContactType::TypeGroup); // check result QList contacts = manager.contacts(filter); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts[0].id().toString(), QStringLiteral("qtcontacts:galera::source@dummy-store")); QCOMPARE(contacts[0].type(), QContactType::TypeGroup); QContactDisplayLabel label = contacts[0].detail(QContactDisplayLabel::Type); QCOMPARE(label.label(), QStringLiteral("Dummy personas")); } /* * Test sort contacts by with symbols in the end */ void testQuerySortedWithSymbolsInTheEnd() { QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); // create a contact "" QContact contact; QContactName name = contact.detail(); name.setFirstName(""); name.setMiddleName(""); name.setLastName(""); contact.saveDetail(&name); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "Aa" contact = QContact(); name = contact.detail(); name.setFirstName("Aa"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "Ba" contact = QContact(); name = contact.detail(); name.setFirstName("Ba"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "Bb" contact = QContact(); name = contact.detail(); name.setFirstName("Bb"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "321" contact = QContact(); name = contact.detail(); name.setFirstName("321"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "*" contact = QContact(); name = contact.detail(); name.setFirstName("*"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "" with company "x" contact = QContact(); name = contact.detail(); name.setFirstName(""); name.setMiddleName(""); name.setLastName(""); contact.saveDetail(&name); QContactOrganization org; org.setName("x"); contact.saveDetail(&org); result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // sort contact by tag and display name QContactFilter filter; QContactSortOrder sortTag; sortTag.setDetailType(QContactDetail::TypeTag, QContactTag::FieldTag); sortTag.setDirection(Qt::AscendingOrder); sortTag.setCaseSensitivity(Qt::CaseInsensitive); sortTag.setBlankPolicy(QContactSortOrder::BlanksLast); QContactSortOrder sortDisplayName; sortDisplayName.setDetailType(QContactDetail::TypeDisplayLabel, QContactDisplayLabel::FieldLabel); sortDisplayName.setDirection(Qt::AscendingOrder); sortDisplayName.setCaseSensitivity(Qt::CaseInsensitive); sortDisplayName.setBlankPolicy(QContactSortOrder::BlanksLast); QList sortOrders; sortOrders << sortTag << sortDisplayName; // check result QList contacts = m_manager->contacts(filter, sortOrders); QCOMPARE(contacts.size(), 7); QCOMPARE(contacts[0].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("Aa")); QCOMPARE(contacts[1].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("Ba")); QCOMPARE(contacts[2].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("Bb")); QCOMPARE(contacts[3].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("x")); QCOMPARE(contacts[4].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("*")); QCOMPARE(contacts[5].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("321")); QCOMPARE(contacts[6].detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel).toString(), QStringLiteral("")); } void testContactDisplayName() { // create a contact "" QContact contact; QContactName name = contact.detail(); name.setFirstName(""); name.setMiddleName(""); name.setLastName(""); contact.saveDetail(&name); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "Aa" contact = QContact(); name = contact.detail(); name.setFirstName("Aa"); contact.saveDetail(&name); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // create contact "" with company "x" contact = QContact(); name = contact.detail(); name.setFirstName(""); name.setMiddleName(""); name.setLastName(""); contact.saveDetail(&name); QContactOrganization org; org.setName("x"); contact.saveDetail(&org); result = m_manager->saveContact(&contact); QCOMPARE(result, true); // check result QContactFilter filter; QList contacts = m_manager->contacts(filter); QStringList expectedContacts; expectedContacts << "" << "Aa" << "x"; QCOMPARE(contacts.size(), 3); Q_FOREACH(const QContact &c, contacts) { expectedContacts.removeAll(c.detail().label()); } QCOMPARE(expectedContacts.size(), 0); } void testContactUpdateDisplayName() { // create a contact "" QContact contact; QContactName name = contact.detail(); name.setFirstName("Fulano"); name.setMiddleName("de"); name.setLastName("Tal"); contact.saveDetail(&name); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); // check result QList ids; ids << contact.id(); QList contacts = m_manager->contacts(ids); QCOMPARE(contacts[0].details().size(), 1); QCOMPARE(contacts[0].detail().label(), QStringLiteral("Fulano Tal")); QCOMPARE(contacts[0].detail().tag(), QStringLiteral("FULANO TAL")); // Change contact name name = contact.detail(); name.setFirstName("xFulano"); contact.saveDetail(&name); m_manager->saveContact(&contact); contacts = m_manager->contacts(ids); QCOMPARE(contacts[0].details().size(), 1); QCOMPARE(contacts[0].detail().label(), QStringLiteral("xFulano Tal")); QCOMPARE(contacts[0].detail().tag(), QStringLiteral("XFULANO TAL")); } void testContactChangeOrder() { // create a contact "A de Tal" QContact contactA; QContactName name = contactA.detail(); name.setFirstName("A"); name.setMiddleName("de"); name.setLastName("Tal"); contactA.saveDetail(&name); bool result = m_manager->saveContact(&contactA); QCOMPARE(result, true); // create a contact "B de Tal" QContact contactB; name = contactB.detail(); name.setFirstName("B"); name.setMiddleName("de"); name.setLastName("Tal"); contactB.saveDetail(&name); result = m_manager->saveContact(&contactB); QCOMPARE(result, true); // create a contact "C de Tal" QContact contactC; name = contactC.detail(); name.setFirstName("C"); name.setMiddleName("de"); name.setLastName("Tal"); contactC.saveDetail(&name); result = m_manager->saveContact(&contactC); QCOMPARE(result, true); // create a contact "D de Tal" QContact contactD; name = contactD.detail(); name.setFirstName("D"); name.setMiddleName("de"); name.setLastName("Tal"); contactD.saveDetail(&name); result = m_manager->saveContact(&contactD); QCOMPARE(result, true); // check result QContactFilter filter; QList contacts = m_manager->contacts(filter); QCOMPARE(contacts[0].detail().label(), QStringLiteral("A Tal")); QCOMPARE(contacts[1].detail().label(), QStringLiteral("B Tal")); QCOMPARE(contacts[2].detail().label(), QStringLiteral("C Tal")); QCOMPARE(contacts[3].detail().label(), QStringLiteral("D Tal")); // Update contact B name contactB = contacts[1]; name = contactB.detail(); name.setFirstName("X"); contactB.saveDetail(&name); m_manager->saveContact(&contactB); // Fetch contacts again contacts = m_manager->contacts(filter); // check new order QCOMPARE(contacts[0].detail().label(), QStringLiteral("A Tal")); QCOMPARE(contacts[1].detail().label(), QStringLiteral("C Tal")); QCOMPARE(contacts[2].detail().label(), QStringLiteral("D Tal")); QCOMPARE(contacts[3].detail().label(), QStringLiteral("X Tal")); } /* * Test create a contact with two address * BUG: #1334402 */ void testCreateContactWithTwoAddress() { QContact contact = galera::VCardParser::vcardToContact("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "N:Gump;Forrest;Mr.\r\n" "FN:Forrest Gump\r\n" "ORG:Bubba Gump Shrimp Co.\r\n" "TITLE:Shrimp Man\r\n" "PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif\r\n" "TEL;TYPE=WORK,VOICE:(111) 555-12121\r\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\r\n" "ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America\r\n" "LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America\r\n" "ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America\r\n" "LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America\r\n" "EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\r\n" "REV:2008-04-24T19:52:43Z\r\n" "END:VCARD\r\n"); // create a contact QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // query for new contacts QContactFilter filter; QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContact createdContact = contacts[0]; // id QVERIFY(!createdContact.id().isNull()); // address QList address = createdContact.details(); QCOMPARE(address.size(), 2); QList originalAddress = contact.details(); Q_FOREACH(const QContactAddress addr, originalAddress) { address.removeAll(addr); } QCOMPARE(address.size(), 0); } /* * Test saving a contact with a multi line field * BUG: #240587 */ void testFieldWithNewLineChar() { // create a contact QContact contact = testContact(); QContactAddress addr; addr.setCountry("Line1\nLine2\r\nLine3"); contact.saveDetail(&addr); QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); addr = contact.detail(); QCOMPARE(addr.country(), QStringLiteral("Line1Line2Line3")); } void testUpdateAcontactWithoutChange() { // filter all contacts QContactFilter filter; // create a contact QContactPhoneNumber pn; QContact contact = testContact(); pn.setNumber("1234567"); pn.setContexts(QList() << 0); contact.saveDetail(&pn); pn = QContactPhoneNumber(); pn.setNumber("12345678"); pn.setContexts(QList() << 1); contact.saveDetail(&pn); QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // save contact again without any change QContact contact2 = testContact(); pn = QContactPhoneNumber(); pn.setNumber("12345678"); pn.setContexts(QList() << 1); contact2.saveDetail(&pn); pn = QContactPhoneNumber(); pn.setNumber("1234567"); pn.setContexts(QList() << 0); contact2.saveDetail(&pn); contact2.setId(contact.id()); QSignalSpy spyContactChanged(m_manager, SIGNAL(contactsChanged(QList))); result = m_manager->saveContact(&contact2); QCOMPARE(result, true); QTRY_COMPARE(spyContactChanged.count(), 1); // query for the contacts QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContact updatedContact = contacts[0]; // name QContactName name = contact.detail(); QContactName updatedName = updatedContact.detail(); QCOMPARE(updatedName.firstName(), name.firstName()); QCOMPARE(updatedName.middleName(), name.middleName()); QCOMPARE(updatedName.lastName(), name.lastName()); } void testAddressWithMultipleSubTypes() { // create a contact QContact contact = testContact(); QContactAddress addr; addr.setLocality("8777 West Six Mile Road"); addr.setContexts(QList() << QContactDetail::ContextWork); addr.setSubTypes(QList() << QContactAddress::SubTypeParcel << QContactAddress::SubTypeDomestic); contact.saveDetail(&addr); QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); } }; QTEST_MAIN(QContactsTest) #include "qcontacts-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/qcontacts-create-source-test.cpp0000644000015300001610000000455312632607666031476 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include #include "config.h" using namespace QtContacts; class QContactsCreateSourceTest : public BaseClientTest { Q_OBJECT private Q_SLOTS: void initTestCase() { BaseClientTest::initTestCase(); QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); } /* * Test create a contact source using the contact group */ void testCreateGroup() { QContactManager manager("galera"); // create a contact QContact contact; contact.setType(QContactType::TypeGroup); QContactDisplayLabel label; label.setLabel("test group"); contact.saveDetail(&label); bool result = manager.saveContact(&contact); QCOMPARE(result, true); // query for new contacts QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); filter.setValue(QContactType::TypeGroup); QList contacts = manager.contacts(filter); // will be two sources since we have the main source already created QCOMPARE(contacts.size(), 2); QContact createdContact = contacts[0]; // id QVERIFY(!createdContact.id().isNull()); // label QContactDisplayLabel createdlabel = createdContact.detail(); QCOMPARE(createdlabel.label(), label.label()); } }; QTEST_MAIN(QContactsCreateSourceTest) #include "qcontacts-create-source-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/qcontacts-async-request-test.cpp0000644000015300001610000000546512632607666031543 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/source.h" #include "common/dbus-service-defs.h" #include "common/vcard-parser.h" #include #include #include #include #include #include "config.h" using namespace QtContacts; class QContactsAsyncRequestTest : public BaseClientTest { Q_OBJECT private Q_SLOTS: /* * Test cancel a running request */ void testCancelRequest() { QContactManager manager("galera"); // create a contact QContact contact; contact.setType(QContactType::TypeGroup); QContactDisplayLabel label; label.setLabel("test group"); contact.saveDetail(&label); QContactSaveRequest req; req.setManager(&manager); req.setContact(contact); QSignalSpy spyContactAdded(&manager, SIGNAL(contactsAdded(QList))); // start request req.start(); QCOMPARE(req.state(), QContactAbstractRequest::ActiveState); // cancel running request req.cancel(); QCOMPARE(req.state(), QContactAbstractRequest::CanceledState); // check if the signal did not fire QCOMPARE(spyContactAdded.count(), 0); } /* * Test delete a running request */ void testDestroyRequest() { QContactManager manager("galera"); // create a contact QContact contact; contact.setType(QContactType::TypeGroup); QContactDisplayLabel label; label.setLabel("test group"); contact.saveDetail(&label); QContactSaveRequest *req = new QContactSaveRequest(); req->setManager(&manager); req->setContact(contact); QSignalSpy spyContactAdded(&manager, SIGNAL(contactsAdded(QList))); // start request req->start(); QCOMPARE(req->state(), QContactAbstractRequest::ActiveState); // delete req delete req; QTest::qWait(100); // check if the signal did not fire QCOMPARE(spyContactAdded.count(), 0); } }; QTEST_MAIN(QContactsAsyncRequestTest) #include "qcontacts-async-request-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/service-life-cycle-test.cpp0000644000015300001610000000335412632607666030410 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base-client-test.h" #include "common/dbus-service-defs.h" #include #include #include #include class ServiceLifeCycleTest : public BaseClientTest { Q_OBJECT private Q_SLOTS: void testServiceReady() { QTRY_COMPARE(m_serverIface->property("isReady").toBool(), true); QTRY_COMPARE(m_dummyIface->property("isReady").toBool(), true); } void testCallServiceFunction() { QDBusReply result = m_serverIface->call("ping"); QCOMPARE(result.value(), true); result = m_dummyIface->call("ping"); QCOMPARE(result.value(), true); } void testServiceShutdown() { m_dummyIface->call("quit"); // wait service quits QTest::qSleep(100); QDBusReply result = m_serverIface->call("ping"); QVERIFY(result.error().isValid()); result = m_dummyIface->call("ping"); QVERIFY(result.error().isValid()); } }; QTEST_MAIN(ServiceLifeCycleTest) #include "service-life-cycle-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/base-eds-test.h0000644000015300001610000000451612632607666026067 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "config.h" #include "common/dbus-service-defs.h" using namespace QtContacts; class BaseEDSTest { protected: QContactManager *m_manager; bool isReady() { QDBusMessage callIsReady = QDBusMessage::createMethodCall(CPIM_SERVICE_NAME, CPIM_ADDRESSBOOK_OBJECT_PATH, CPIM_ADDRESSBOOK_IFACE_NAME, "isReady"); QDBusReply reply = QDBusConnection::sessionBus().call(callIsReady); return reply.value(); } bool shutDownServer() { QDBusMessage callShutDown = QDBusMessage::createMethodCall(CPIM_SERVICE_NAME, CPIM_ADDRESSBOOK_OBJECT_PATH, CPIM_ADDRESSBOOK_IFACE_NAME, "shutDown"); QDBusConnection::sessionBus().call(callShutDown); } void initTestCaseImpl() { QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); qRegisterMetaType >("QList"); QTRY_VERIFY_WITH_TIMEOUT(isReady(), 60000); } void cleanupTestCaseImpl() { shutDownServer(); } void initImpl() { m_manager = new QContactManager("galera"); } void cleanupImpl() { delete m_manager; } }; address-book-service-0.1.1+16.04.20151211.1/tests/unittest/run-eds-test.sh0000755000015300001610000000361212632607666026143 0ustar pbuserpbgroup00000000000000#!/bin/sh echo ARG0=$0 # this script echo ARG1=$1 # full executable path of dbus-test-runner echo ARG2=$2 # full executable path of test app echo ARG3=$3 # test name echo ARG4=$4 # full executable path of evolution-addressbook-factory echo ARG5=$5 # bus service name of AddressBook factory echo ARG6=$6 # full exectuable path of evolution-source-registry echo ARG7=$7 # bus service name of evolution-source-registry echo ARG7=$8 # bus service name of evolution-source-registry # set up the tmpdir and tell the shell to purge it when we exit export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d $3-XXXXXXXXXX) || exit 1 echo "running test '$3' in ${TEST_TMP_DIR}" # set up the environment variables export QT_QPA_PLATFORM=minimal export FOLKS_BACKENDS_ALLOWED=eds export HOME=${TEST_TMP_DIR} export XDG_RUNTIME_DIR=${TEST_TMP_DIR} export XDG_CACHE_HOME=${TEST_TMP_DIR}/.cache export XDG_CONFIG_HOME=${TEST_TMP_DIR}/.config export XDG_DATA_HOME=${TEST_TMP_DIR}/.local/share export XDG_DESKTOP_DIR=${TEST_TMP_DIR} export XDG_DOCUMENTS_DIR=${TEST_TMP_DIR} export XDG_DOWNLOAD_DIR=${TEST_TMP_DIR} export XDG_MUSIC_DIR=${TEST_TMP_DIR} export XDG_PICTURES_DIR=${TEST_TMP_DIR} export XDG_PUBLICSHARE_DIR=${TEST_TMP_DIR} export XDG_TEMPLATES_DIR=${TEST_TMP_DIR} export XDG_VIDEOS_DIR=${TEST_TMP_DIR} export ADDRESS_BOOK_SERVICE_DEBUG=On export GIO_USE_VFS=local # needed to ensure GVFS shuts down cleanly after the test is over echo HOMEDIR=${HOME} rm -rf ${XDG_DATA_HOME} # run dbus-test-runner # run dbus-test-runner $1 --keep-env --max-wait=90 \ --task $2 --task-name $3 --wait-until-complete --wait-for=$5 \ --task $8 --task-name "ubuntu-pim" --wait-until-complete --wait-for=$5 -r \ --task $4 --task-name "evolution-addressbook" --wait-until-complete --wait-for=$7 -r \ --task $6 --task-name "source-registry" -r rv=$? # if the test passed, blow away the tmpdir if [ $rv -eq 0 ]; then rm -rf $TEST_TMP_DIR fi return $rv address-book-service-0.1.1+16.04.20151211.1/tests/unittest/CMakeLists.txt0000644000015300001610000001121712632607666026012 0ustar pbuserpbgroup00000000000000macro(declare_test TESTNAME RUN_SERVER) add_executable(${TESTNAME} ${ARGN} ${TESTNAME}.cpp ) if(TEST_XML_OUTPUT) set(TEST_ARGS -p -xunitxml -p -o -p test_${testname}.xml) else() set(TEST_ARGS "") endif() target_link_libraries(${TESTNAME} address-book-service-lib folks-dummy ${CONTACTS_SERVICE_LIB} ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} Qt5::Core Qt5::Contacts Qt5::Versit Qt5::Test Qt5::DBus ) if(${RUN_SERVER} STREQUAL "True") add_test(${TESTNAME} ${DBUS_RUNNER} --keep-env --task ${CMAKE_CURRENT_BINARY_DIR}/address-book-server-test --task ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} ${TEST_ARGS} --wait-for=com.canonical.pim) else() add_test(${TESTNAME} ${TESTNAME}) endif() set(TEST_ENVIRONMENT "QT_QPA_PLATFORM=minimal\;FOLKS_BACKEND_PATH=${folks-dummy-backend_BINARY_DIR}/dummy.so\;FOLKS_BACKENDS_ALLOWED=dummy\;ADDRESS_BOOK_SAFE_MODE=Off") set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT ${TEST_ENVIRONMENT} TIMEOUT ${CTEST_TESTING_TIMEOUT}) endmacro() macro(declare_eds_test TESTNAME) add_executable(${TESTNAME} ${TESTNAME}.cpp base-eds-test.h ) qt5_use_modules(${TESTNAME} Core Contacts Versit Test DBus) if(TEST_XML_OUTPUT) set(TEST_ARGS -p -xunitxml -p -o -p test_${testname}.xml) else() set(TEST_ARGS "") endif() target_link_libraries(${TESTNAME} address-book-service-lib ${CONTACTS_SERVICE_LIB} ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} ) add_test(${TESTNAME} ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-test.sh ${DBUS_RUNNER} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} ${TESTNAME} ${EVOLUTION_ADDRESSBOOK_FACTORY_BIN} ${EVOLUTION_ADDRESSBOOK_SERVICE_NAME} ${EVOLUTION_SOURCE_REGISTRY} ${EVOLUTION_SOURCE_SERVICE_NAME} ${address-book-service_BINARY_DIR}/address-book-service) endmacro() include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${folks-dummy-lib_BINARY_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_INCLUDE_DIRS} ${FOLKS_DUMMY_INCLUDE_DIRS} ) add_definitions(-DTEST_SUITE) if(NOT CTEST_TESTING_TIMEOUT) set(CTEST_TESTING_TIMEOUT 60) endif() declare_test(clause-test False) declare_test(sort-clause-test False) declare_test(fetch-hint-test False) declare_test(vcardparser-test False) set(DUMMY_BACKEND_SRC scoped-loop.h scoped-loop.cpp dummy-backend.cpp dummy-backend.h) declare_test(contactmap-test False ${DUMMY_BACKEND_SRC}) if(DBUS_RUNNER) set(BASE_CLIENT_TEST_SRC dummy-backend-defs.h base-client-test.h base-client-test.cpp) declare_test(addressbook-test True ${BASE_CLIENT_TEST_SRC}) declare_test(service-life-cycle-test True ${BASE_CLIENT_TEST_SRC}) declare_test(readonly-prop-test True ${BASE_CLIENT_TEST_SRC}) declare_test(contact-link-test True ${BASE_CLIENT_TEST_SRC}) declare_test(contact-sort-test True ${BASE_CLIENT_TEST_SRC}) declare_test(qcontacts-test True ${BASE_CLIENT_TEST_SRC}) declare_test(qcontacts-create-source-test True ${BASE_CLIENT_TEST_SRC}) declare_test(qcontacts-async-request-test True ${BASE_CLIENT_TEST_SRC}) declare_eds_test(contact-collection-test) declare_eds_test(contact-timestamp-test) declare_eds_test(contact-avatar-test) elseif() message(STATUS "DBus test runner not found. Some tests will be disabled") endif() # server code add_executable(address-book-server-test scoped-loop.h scoped-loop.cpp dummy-backend.h dummy-backend.cpp addressbook-server.cpp ) qt5_use_modules(address-book-server-test Core Contacts Versit DBus) target_link_libraries(address-book-server-test address-book-service-lib folks-dummy ${CONTACTS_SERVICE_LIB} ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} ) address-book-service-0.1.1+16.04.20151211.1/tests/unittest/dummy-backend.h0000644000015300001610000001117712632607666026150 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __DUMMY_BACKEND_TEST_H__ #define __DUMMY_BACKEND_TEST_H__ #include "dummy-backend-defs.h" #include "lib/qindividual.h" #include #include #include #include #include #include #include #include class DummyBackendAdaptor; class DummyBackendProxy: public QObject { Q_OBJECT public: DummyBackendProxy(); ~DummyBackendProxy(); void start(bool useDBus = false); bool isReady() const; QString createContact(const QtContacts::QContact &qcontact); QString updateContact(const QString &contactId, const QtContacts::QContact &qcontact); QList contacts() const; QList individuals() const; FolksIndividualAggregator *aggregator() const; public Q_SLOTS: void shutdown(); QStringList listContacts() const; void reset(); void contactUpdated(const QString &contactId, const QString &errorMsg); Q_SIGNALS: void ready(); void stopped(); private: QTemporaryDir m_tmpDir; DummyBackendAdaptor *m_adaptor; FolksDummyBackend *m_backend; FolksPersonaStore *m_primaryPersonaStore; FolksBackendStore *m_backendStore; QEventLoop *m_eventLoop; FolksIndividualAggregator *m_aggregator; bool m_isReady; int m_individualsChangedDetailedId; QHash m_contacts; bool m_contactUpdated; bool m_useDBus; bool registerObject(); void initFolks(); void initEnviroment(); void prepareAggregator(); void mkpath(const QString &path) const; static void checkError(GError *error); static void individualAggregatorPrepared(FolksIndividualAggregator *fia, GAsyncResult *res, DummyBackendProxy *self); static void individualAggregatorAddedPersona(FolksIndividualAggregator *fia, GAsyncResult *res, DummyBackendProxy *self); static void individualsChangedCb(FolksIndividualAggregator *individualAggregator, GeeMultiMap *changes, DummyBackendProxy *self); }; class DummyBackendAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", DUMMY_IFACE_NAME) Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") Q_PROPERTY(bool isReady READ isReady NOTIFY ready) public: DummyBackendAdaptor(const QDBusConnection &connection, DummyBackendProxy *parent); virtual ~DummyBackendAdaptor(); bool isReady(); public Q_SLOTS: bool ping(); void quit(); void reset(); QStringList listContacts(); QString createContact(const QString &vcard); QString updateContact(const QString &contactId, const QString &vcard); void enableAutoLink(bool flag); Q_SIGNALS: void ready(); void stopped(); private: QDBusConnection m_connection; DummyBackendProxy *m_proxy; }; #endif address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contact-collection-test.cpp0000644000015300001610000001464712632607673030527 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "base-eds-test.h" #include "config.h" using namespace QtContacts; class ContactCollectionTest : public QObject, public BaseEDSTest { Q_OBJECT private Q_SLOTS: void initTestCase() { BaseEDSTest::initTestCaseImpl(); } void cleanupTestCase() { BaseEDSTest::cleanupTestCaseImpl(); } void init() { BaseEDSTest::initImpl(); } void cleanup() { BaseEDSTest::cleanupImpl(); } /* * Test create a new collection */ void testCreateAddressBook() { QContact c; c.setType(QContactType::TypeGroup); QContactDisplayLabel label; QString uniqueName = QString("%1").arg(QUuid::createUuid().toString().remove("{").remove("}")); label.setLabel(uniqueName); c.saveDetail(&label); bool saveResult = m_manager->saveContact(&c); QVERIFY(saveResult); QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); filter.setValue(QContactType::TypeGroup); QList contacts = m_manager->contacts(filter); Q_FOREACH(const QContact &contact, contacts) { QVERIFY(c.id().toString().startsWith("qtcontacts:galera::source@")); if ((contact.detail().label() == uniqueName) && (contact.id() == c.id())) { return; } } QFAIL("New collection not found"); } /* * Test remove a collection */ void testRemoveAddressBook() { // create a source QContact c; c.setType(QContactType::TypeGroup); QContactDisplayLabel label; QString uniqueName = QString("%1").arg(QUuid::createUuid().toString().remove("{").remove("}")); label.setLabel(uniqueName); c.saveDetail(&label); bool saveResult = m_manager->saveContact(&c); QVERIFY(saveResult); // try to remove new source bool removeResult = m_manager->removeContact(c.id()); QVERIFY(removeResult); // check if the source was removed QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); filter.setValue(QContactType::TypeGroup); QList contacts = m_manager->contacts(filter); Q_FOREACH(const QContact &contact, contacts) { if (contact.id() == c.id()) { QFAIL("Collection not removed"); } } } /* * Test query for collections using the contact group type */ void testQueryAddressBook() { // filter all contact groups/addressbook QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); filter.setValue(QContactType::TypeGroup); // check result for the default source QList contacts = m_manager->contacts(filter); Q_FOREACH(const QContact &c, contacts) { QCOMPARE(c.type(), QContactType::TypeGroup); if (c.id().toString() == QStringLiteral("qtcontacts:galera::source@system-address-book")) { QContactDisplayLabel label = c.detail(QContactDisplayLabel::Type); QCOMPARE(label.label(), QStringLiteral("Personal")); return; } } QFAIL("Fail to query for collections"); } /* * Test edit collection */ void testEditAddressBook() { // create a source QContact c; c.setType(QContactType::TypeGroup); QContactDisplayLabel label; QString uniqueName = QString("%1").arg(QUuid::createUuid().toString().remove("{").remove("}")); label.setLabel(uniqueName); c.saveDetail(&label); bool saveResult = m_manager->saveContact(&c); QVERIFY(saveResult); QContactDetailFilter collectionFilter; collectionFilter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); collectionFilter.setValue(QContactType::TypeGroup); // current collection size int collectionSize = m_manager->contacts(collectionFilter).size(); label = c.detail(); QString newUniqueName = QString("NEW_%1").arg(uniqueName); label.setLabel(newUniqueName); c.saveDetail(&label); bool isPrimaryUpdated = false; Q_FOREACH(QContactExtendedDetail xDetail, c.details()) { if (xDetail.name() == "IS-PRIMARY") { xDetail.setData(true); c.saveDetail(&xDetail); isPrimaryUpdated = true; break; } } QVERIFY(isPrimaryUpdated); saveResult = m_manager->saveContact(&c); QVERIFY(saveResult); // check if the collections contains the new collection QList contacts = m_manager->contacts(collectionFilter); QCOMPARE(contacts.size() , collectionSize); Q_FOREACH(const QContact &contact, contacts) { QVERIFY(c.id().toString().startsWith("qtcontacts:galera::source@")); if (contact.detail().label() == newUniqueName) { bool isPrimary = false; Q_FOREACH(QContactExtendedDetail xDetail, c.details()) { if (xDetail.name() == "IS-PRIMARY") { isPrimary = xDetail.data().toBool(); } } QVERIFY(isPrimary); return; } } QFAIL("New collection not found"); } }; QTEST_MAIN(ContactCollectionTest) #include "contact-collection-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/addressbook-server.cpp0000644000015300001610000000225612632607666027565 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "lib/addressbook.h" #include "dummy-backend.h" #include int main(int argc, char** argv) { galera::AddressBook::init(); QCoreApplication app(argc, argv); // dummy DummyBackendProxy dummy; dummy.start(true); // addressbook galera::AddressBook book; book.connect(&dummy, SIGNAL(ready()), SLOT(start())); book.connect(&dummy, SIGNAL(stopped()), SLOT(shutdown())); app.connect(&book, SIGNAL(stopped()), SLOT(quit())); return app.exec(); } address-book-service-0.1.1+16.04.20151211.1/tests/unittest/scoped-loop.h0000644000015300001610000000174412632607666025653 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __SCOPED_LOOP_H__ #define __SCOPED_LOOP_H__ #include class ScopedEventLoop { public: ScopedEventLoop(QEventLoop **proxy); ~ScopedEventLoop(); void reset(QEventLoop **proxy); void exec(); private: QEventLoop m_eventLoop; QEventLoop **m_proxy; }; #endif address-book-service-0.1.1+16.04.20151211.1/tests/unittest/contact-timestamp-test.cpp0000644000015300001610000003125312632607666030371 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "config.h" #include "base-eds-test.h" #include "common/vcard-parser.h" using namespace QtContacts; class ContactTimeStampTest : public QObject, public BaseEDSTest { Q_OBJECT private Q_SLOTS: void initTestCase() { BaseEDSTest::initTestCaseImpl(); } void cleanupTestCase() { BaseEDSTest::cleanupTestCaseImpl(); } void init() { BaseEDSTest::initImpl(); } void cleanup() { BaseEDSTest::cleanupImpl(); } void testFilterContactByChangeLog() { QDateTime currentDate = QDateTime::currentDateTime(); // wait one sec to cause a create date later QTest::qWait(1000); QContact contact = galera::VCardParser::vcardToContact(QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:123456\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n")); // create a contact QSignalSpy spyContactAdded(m_manager, SIGNAL(contactsAdded(QList))); bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QTRY_COMPARE(spyContactAdded.count(), 1); // qery contacts; QContactFilter filter; QList contacts = m_manager->contacts(filter); QCOMPARE(contacts.size(), 1); QContactTimestamp timestamp = contacts[0].detail(); qDebug() << "CURRENT DATE:" << currentDate.toUTC() << "\n" << "CREATED DATE:" << timestamp.created() << "\n" << "MODIFIED DATE:" << timestamp.lastModified(); QVERIFY(timestamp.created().isValid()); QVERIFY(timestamp.lastModified().isValid()); QVERIFY(timestamp.created() >= currentDate); QVERIFY(timestamp.lastModified() >= currentDate); QContactChangeLogFilter changeLogFilter; changeLogFilter.setSince(currentDate); QList ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 1); QCOMPARE(ids[0], contacts[0].id()); // wait one sec to remove the contact QTest::qWait(1000); result = m_manager->removeContact(contact.id()); QVERIFY(result); // if the contact was deleted it should not appear on change log filter // with event type = EventAdded ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 0); } void testDeletedDateFilter() { QDateTime currentDate = QDateTime::currentDateTime(); // wait one sec to cause a create date later QTest::qWait(1000); QContact contact = galera::VCardParser::vcardToContact(QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:123456\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n")); // create a contact bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QContactChangeLogFilter changeLogFilter; changeLogFilter.setEventType(QContactChangeLogFilter::EventRemoved); changeLogFilter.setSince(currentDate); QContactDetailFilter detFilter; detFilter.setDetailType(QContactDetail::TypeEmailAddress, QContactEmailAddress::FieldEmailAddress); detFilter.setValue(QStringLiteral("fulano@gmail.com")); detFilter.setMatchFlags(QContactDetailFilter::MatchExactly); // no contact removed QList ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 0); // test intersection filter ids = m_manager->contactIds(changeLogFilter & detFilter); QCOMPARE(ids.size(), 0); // wait one more sec to remove the contact QTest::qWait(1000); result = m_manager->removeContact(contact.id()); QVERIFY(result); // test deleted filter ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 1); QCOMPARE(ids[0], contact.id()); // test intersection filter ids = m_manager->contactIds(changeLogFilter & detFilter); QCOMPARE(ids.size(), 1); QCOMPARE(ids[0], contact.id()); // test deleted filter with a future date changeLogFilter.setSince(currentDate.addDays(1)); ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 0); // test } void testPhoneNumberFilterWithDeleted() { QDateTime currentDate = QDateTime::currentDateTime(); // wait one sec to cause a create date later QTest::qWait(1000); QContact contact = galera::VCardParser::vcardToContact(QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:78910\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n")); // create a contact bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); // no contact removed QContactChangeLogFilter changeLogFilter; changeLogFilter.setEventType(QContactChangeLogFilter::EventRemoved); changeLogFilter.setSince(currentDate); QList ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 0); // Phone number filter QContactDetailFilter detFilter = QContactPhoneNumber::match("78910"); ids = m_manager->contactIds(detFilter); QCOMPARE(ids.size(), 1); // Intersection filter (phone + deleted) ids = m_manager->contactIds(detFilter & changeLogFilter); QCOMPARE(ids.size(), 0); // wait one more sec to remove the contact QTest::qWait(1000); result = m_manager->removeContact(contact.id()); QVERIFY(result); ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 1); QCOMPARE(ids[0], contact.id()); // Phone number filter (contact was removed) ids = m_manager->contactIds(detFilter); QCOMPARE(ids.size(), 0); // test intersection filter (phone number + removed) ids = m_manager->contactIds(changeLogFilter & detFilter); QCOMPARE(ids.size(), 1); QCOMPARE(ids[0], contact.id()); } void testIdFilterWithDeleted() { QDateTime currentDate = QDateTime::currentDateTime(); // wait one sec to cause a create date later QTest::qWait(1000); QContact contact = galera::VCardParser::vcardToContact(QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:78910\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n")); // create a contact bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); // no contact removed QContactChangeLogFilter changeLogFilter; changeLogFilter.setEventType(QContactChangeLogFilter::EventRemoved); changeLogFilter.setSince(currentDate); QList ids = m_manager->contactIds(changeLogFilter); QCOMPARE(ids.size(), 0); // query by id ids.clear(); ids.append(contact.id()); QList contacts = m_manager->contacts(ids); QCOMPARE(contacts.size(), 1); // wait one more sec to remove the contact QTest::qWait(1000); result = m_manager->removeContact(contact.id()); QVERIFY(result); // query by id (query by id return the contact deleted or not) ids.clear(); ids.append(contact.id()); contacts = m_manager->contacts(ids); QCOMPARE(contacts.size(), 1); } void testSyncTargetDeletedFilter() { QDateTime currentDate = QDateTime::currentDateTime(); // wait one sec to cause a create date later QTest::qWait(1000); QContact contact = galera::VCardParser::vcardToContact(QStringLiteral("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "X-REMOTE-ID:1dd7d51a1518626a\r\n" "N:;Fulano;;;\r\n" "EMAIL:fulano@gmail.com\r\n" "TEL:78910\r\n" "CLIENTPIDMAP:56183a5b-5da7-49fe-8cf6-9bfd3633bf6d\r\n" "END:VCARD\r\n")); // create a contact bool result = m_manager->saveContact(&contact); QCOMPARE(result, true); QString syncTargetId = contact.detail().value(QContactSyncTarget::FieldSyncTarget + 1).toString(); QVERIFY(!syncTargetId.isEmpty()); // wait one more sec to remove the contact QTest::qWait(1000); // remove contact result = m_manager->removeContact(contact.id()); QVERIFY(result); // sync target filter QContactDetailFilter fSyncTarget; fSyncTarget.setDetailType(QContactSyncTarget::Type, QContactSyncTarget::FieldSyncTarget + 1); fSyncTarget.setValue(syncTargetId); QContactChangeLogFilter fDeleted(QContactChangeLogFilter::EventRemoved); fDeleted.setSince(currentDate); QList ids = m_manager->contactIds(fDeleted & fSyncTarget); QCOMPARE(ids.size(), 1); QCOMPARE(ids.at(0), contact.id()); } }; QTEST_MAIN(ContactTimeStampTest) #include "contact-timestamp-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/unittest/sort-clause-test.cpp0000644000015300001610000001052212632607666027172 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common/sort-clause.h" using namespace QtContacts; using namespace galera; class SortClauseTest : public QObject { Q_OBJECT private Q_SLOTS: void testSingleClause() { const QString strClause = QString("FIRST_NAME ASC"); SortClause clause(strClause); QList cClauseList; QContactSortOrder cClause; cClause.setCaseSensitivity(Qt::CaseInsensitive); cClause.setDetailType(QContactDetail::TypeName, QContactName::FieldFirstName); cClauseList << cClause; QVERIFY(clause.toContactSortOrder() == cClauseList); SortClause clause2(cClauseList); QCOMPARE(clause2.toString(), strClause); } void testComplexClause() { const QString strClause = QString("FIRST_NAME ASC, ORG_DEPARTMENT, ADDR_STREET DESC, URL"); const QString strClauseFull = QString("FIRST_NAME ASC, ORG_DEPARTMENT ASC, ADDR_STREET DESC, URL ASC"); SortClause clause(strClause); QList cClauseList; QContactSortOrder sortFirstName; sortFirstName.setDetailType(QContactDetail::TypeName, QContactName::FieldFirstName); sortFirstName.setDirection(Qt::AscendingOrder); sortFirstName.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortFirstName; QContactSortOrder sortDepartment; sortDepartment.setDetailType(QContactDetail::TypeOrganization, QContactOrganization::FieldDepartment); sortDepartment.setDirection(Qt::AscendingOrder); sortDepartment.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortDepartment; QContactSortOrder sortStreet; sortStreet.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); sortStreet.setDirection(Qt::DescendingOrder); sortStreet.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortStreet; QContactSortOrder sortUrl; sortUrl.setDetailType(QContactDetail::TypeUrl, QContactUrl::FieldUrl); sortUrl.setDirection(Qt::AscendingOrder); sortUrl.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortUrl; QVERIFY(clause.toContactSortOrder() == cClauseList); SortClause clause2(cClauseList); QCOMPARE(clause2.toString(), strClauseFull); } // should ignore the first field void testInvalidFieldNameClause() { SortClause clause("FIRSTNAME ASC, URL"); QCOMPARE(clause.toString(), QString("URL ASC")); QList cClauseList; QContactSortOrder sortUrl; sortUrl.setDetailType(QContactDetail::TypeUrl, QContactUrl::FieldUrl); sortUrl.setDirection(Qt::AscendingOrder); sortUrl.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortUrl; QVERIFY(clause.toContactSortOrder() == cClauseList); } void testInvalidSintaxClause() { SortClause clause("FIRST_NAME ASC URL"); QCOMPARE(clause.toString(), QString("")); QCOMPARE(clause.toContactSortOrder().size(), 0); } void testTagSourtClause() { const QString strClause = QString("TAG ASC"); SortClause clause(strClause); QList cClauseList; QContactSortOrder sortTag; sortTag.setDetailType(QContactDetail::TypeTag, QContactTag::FieldTag); sortTag.setDirection(Qt::AscendingOrder); sortTag.setCaseSensitivity(Qt::CaseInsensitive); cClauseList << sortTag; QVERIFY(clause.toContactSortOrder() == cClauseList); } }; QTEST_MAIN(SortClauseTest) #include "sort-clause-test.moc" address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/0000755000015300001610000000000012632610222023401 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/tst-buteo-import.cpp0000644000015300001610000001166212632607666027373 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "ab-update.h" #include "config.h" #define ADDRESS_BOOK_BUS_NAME "com.canonical.pim" #define ADDRESS_BOOK_OBJ "/com/canonical/pim/AddressBook" #define ADDRESS_BOOK_IFACE "com.canonical.pim.AddressBook" using namespace QtContacts; class TstButeoImport : public QObject { Q_OBJECT QTemporaryFile *m_settingsFile; private: void createSource(const QString &sourceId, const QString &sourceName, const QString &provider, const QString &applicationId, quint32 accountId, bool readOnly, bool primary) { QDBusMessage createSource = QDBusMessage::createMethodCall(ADDRESS_BOOK_BUS_NAME, ADDRESS_BOOK_OBJ, ADDRESS_BOOK_IFACE, "createSource"); QList args; args << sourceId << sourceName << provider << applicationId << accountId << readOnly << primary; createSource.setArguments(args); QDBusReply reply = QDBusConnection::sessionBus().call(createSource); if (reply.error().isValid()) { qWarning() << "Fail to create source" << reply.error(); } else { qDebug() << "Source created" << reply.value(); } QVERIFY(reply.value()); } void resetAddressBook() { QDBusMessage reset = QDBusMessage::createMethodCall(ADDRESS_BOOK_BUS_NAME, ADDRESS_BOOK_OBJ, ADDRESS_BOOK_IFACE, "reset"); QDBusReply reply = QDBusConnection::sessionBus().call(reset); QVERIFY(reply.value()); } private Q_SLOTS: void init() { // we need `galera` manager to run this test QCoreApplication::setLibraryPaths(QStringList() << QT_PLUGINS_BINARY_DIR); QVERIFY(QContactManager::availableManagers().contains("galera")); m_settingsFile = new QTemporaryFile; QVERIFY(m_settingsFile->open()); qDebug() << "Using as temporary file:" << m_settingsFile->fileName(); //Make sure that we use a new settings every time that we run the test QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, m_settingsFile->fileName()); } void cleanup() { resetAddressBook(); delete m_settingsFile; } void tst_isOutDated() { ABUpdate updater; updater.skipNetworkTest(); updater.setSilenceMode(true); QCOMPARE(updater.needsUpdate().count(), 1); QVERIFY(!updater.isRunning()); } void tst_importAccount() { // populate sources createSource("source@1", "renato.teste2@gmail.com", "google", "", 0, false, true); createSource("source@2", "renato.teste3@gmail.com", "google", "", 0, false, false); createSource("source@3", "source-3", "google", "", 0, false, false); createSource("source@4", "source-4", "google", "", 0, false, false); ABUpdate updater; updater.skipNetworkTest(); updater.setSilenceMode(true); QSignalSpy updatedSignal(&updater, SIGNAL(updateDone())); QSignalSpy updateErrorSignal(&updater, SIGNAL(updateError(QString, ABUpdateModule::ImportError))); updater.startUpdate(); QVERIFY(updater.isRunning()); QTRY_COMPARE(updatedSignal.count(), 1); QTRY_COMPARE(updateErrorSignal.count(), 0); QCOMPARE(updater.isRunning(), false); // Check if old sources was deleted QContactManager manager("galera"); QContactDetailFilter sourceFilter; sourceFilter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); sourceFilter.setValue(QContactType::TypeGroup); QCOMPARE(manager.contacts(sourceFilter).size(), 2); } }; QTEST_MAIN(TstButeoImport) #include "tst-buteo-import.moc" address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/mock/0000755000015300001610000000000012632610222024332 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/mock/mock-buteo.py0000755000015300001610000000712512632607666027003 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 '''buteo syncfw mock template This creates the expected methods and properties of the main com.meego.msyncd object. You can specify D-BUS property values ''' # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation; either version 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Renato Araujo Oliveira Filho' __email__ = 'renato.filho@canonical.com' __copyright__ = '(c) 2015 Canonical Ltd.' __license__ = 'LGPL 3+' import dbus from gi.repository import GObject import dbus import dbus.service import dbus.mainloop.glib BUS_NAME = 'com.meego.msyncd' MAIN_OBJ = '/synchronizer' MAIN_IFACE = 'com.meego.msyncd' SYSTEM_BUS = False class ButeoSyncFw(dbus.service.Object): DBUS_NAME = None def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), object_path) self._mainloop = GObject.MainLoop() self._profiles = {} def _mock_profile_create(self, profileName): self.signalProfileChanged(profileName, 0, '') return False def _mock_sync_start(self, profileName): self.syncStatus(profileName, 1, '', 0) return False def _mock_sync_finished(self, profileName): self.syncStatus(profileName, 4, '', 100) return False @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='i', out_signature='s') def createSyncProfileForAccount(self, accountId): profileName = "profile-" + str(accountId) self._profiles[accountId] = profileName GObject.timeout_add(1000, self._mock_profile_create, profileName) GObject.timeout_add(2000, self._mock_sync_start, profileName) GObject.timeout_add(3000, self._mock_sync_finished, profileName) return profileName @dbus.service.method(dbus_interface=MAIN_IFACE, out_signature='as') def runningSyncs(self): return [] @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='ss', out_signature='as') def syncProfilesByKey(self, key, value): if key == "accountid" and (value in self._profiles): return [self._profiles[value]] else: return [] @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='s', out_signature='b') def removeProfile(self, profileId): if profileId in self._profiles: return True else: return False @dbus.service.method(dbus_interface=MAIN_IFACE, out_signature='as') def runningSyncs(self): return [] @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='s', out_signature='b') def startSync(self, profileId): return True @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='sisi') def syncStatus(self, profileId, status, message, statusDetails): print("SyncStatus called") @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='sis') def signalProfileChanged(self, profileId, status, changedProfile): print("profileChanged called") def _run(self): self._mainloop.run() if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) ButeoSyncFw.DBUS_NAME = dbus.service.BusName(BUS_NAME) buteo = ButeoSyncFw(MAIN_OBJ) buteo._run() address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/mock/mock-sync-monitor.py0000755000015300001610000000453612632607666030331 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 '''buteo syncfw mock template This creates the expected methods and properties of the main com.meego.msyncd object. You can specify D-BUS property values ''' # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation; either version 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Renato Araujo Oliveira Filho' __email__ = 'renato.filho@canonical.com' __copyright__ = '(c) 2015 Canonical Ltd.' __license__ = 'LGPL 3+' import dbus from gi.repository import GObject import dbus import dbus.service import dbus.mainloop.glib BUS_NAME = 'com.canonical.SyncMonitor' MAIN_OBJ = '/com/canonical/SyncMonitor' MAIN_IFACE = 'com.canonical.SyncMonitor' SYSTEM_BUS = False class SyncMonitor(dbus.service.Object): DBUS_NAME = None def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), object_path) self._mainloop = GObject.MainLoop() self._accountsById = {140: 'renato.teste2@gmail.com', 141: 'renato.teste3@gmail.com', 142: 'renato.teste4@gmail.com'} def _mock_sync_done(self, accountId): print("Emit sync done: %s" % (self._accountsById[accountId])) self.syncFinished(self._accountsById[accountId], 'contacts') return False @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='us') def syncAccount(self, accountId, serviceName): print("Will sync:", accountId) GObject.timeout_add(1000, self._mock_sync_done, accountId) @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='ss') def syncFinished(self, accountName, serviceName): print("syncFinished called") @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='sss') def syncError(self, accountName, serviceName, errorMessage): print("syncError called") def _run(self): self._mainloop.run() if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) SyncMonitor.DBUS_NAME = dbus.service.BusName(BUS_NAME) sync = SyncMonitor(MAIN_OBJ) sync._run() address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/mock/mock-galera.py0000755000015300001610000002237012632607666027117 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 '''buteo syncfw mock template This creates the expected methods and properties of the main com.meego.msyncd object. You can specify D-BUS property values ''' # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation; either version 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Renato Araujo Oliveira Filho' __email__ = 'renato.filho@canonical.com' __copyright__ = '(c) 2015 Canonical Ltd.' __license__ = 'LGPL 3+' import dbus from gi.repository import GObject import dbus import dbus.service import dbus.mainloop.glib BUS_NAME = 'com.canonical.pim' MAIN_OBJ = '/com/canonical/pim/AddressBook' MAIN_IFACE = 'com.canonical.pim.AddressBook' VIEW_OBJ = '/com/canonical/pim/AddressBookView' VIEW_IFACE = 'com.canonical.pim.AddressBookView' SYSTEM_BUS = False class AddressBookView(dbus.service.Object): DBUS_NAME = None def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), object_path) self._mainloop = GObject.MainLoop() self._sources = {} @dbus.service.method(dbus_interface=VIEW_IFACE, in_signature='asii', out_signature='as') def contactsDetails(self, fiels, startIndex, pageSize): return [] class AddressBook(dbus.service.Object): DBUS_NAME = None def __init__(self, object_path): dbus.service.Object.__init__(self, dbus.SessionBus(), object_path) self._mainloop = GObject.MainLoop() self._view = AddressBookView(VIEW_OBJ) self._isReady = True self._sources = {} @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='', out_signature='a(ssssubb)') def availableSources(self): return self._sources.values() @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='s', out_signature='b') def removeSource(self, sourceId): if sourceId in self._sources: del self._sources[sourceId] return True else: return False @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='as', out_signature='i') def removeContacts(self, contactIds): count = 0 for id in contactIds: if sourceId in self._sources: del self._sources[sourceId] count += 1 return count @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='ssibas', out_signature='o') def query(self, clause, sort, max_count, show_invisible, sources): return VIEW_OBJ @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, interface_name, prop_name): if interface_name == MAIN_IFACE: if property_name == 'isReady': return True return None @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='') def readyChanged(self): print("readyChanged called") @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='as') def contactsAdded(self, contacts): print("contactsAdded called") @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='as') def contactsRemoved(self, contacts): print("contactsRemoved called") @dbus.service.signal(dbus_interface=MAIN_IFACE, signature='as') def contactsUpdated(self, contacts): print("contactsUpdated called") @dbus.service.signal(dbus_interface=MAIN_IFACE) def safeModeChanged(self): print("safeModeChanged called") @dbus.service.signal(dbus_interface=MAIN_IFACE) def readyChanged(self): print("readyChanged called") #properties @dbus.service.method(dbus_interface='org.freedesktop.DBus.Introspectable', out_signature='s') def Introspect(self): return """ """ @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, interface_name, property_name): return self.GetAll(interface_name)[property_name] @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') def GetAll(self, interface_name): print("Get Property") return {'isReady': True} @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ssv') def Set(self, interface_name, property_name, new_value): # validate the property name and value, update internal state… if property_name == 'isReady': self._isReady = new_value self.readyChanged() self.PropertiesChanged(interface_name, { property_name: new_value }, []) @dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE, signature='sa{sv}as') def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties): pass #helper functions @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='ssssibb', out_signature='b') def createSource(self, sourceId, sourceName, provider, applicationId, accountId, readOnly, primary): self._sources[sourceId] = (sourceId, sourceName, provider, applicationId, accountId, readOnly, primary) return True @dbus.service.method(dbus_interface=MAIN_IFACE, in_signature='', out_signature='b') def reset(self): self._sources = {} return True def _run(self): self._mainloop.run() if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) AddressBook.DBUS_NAME = dbus.service.BusName(BUS_NAME) galera = AddressBook(MAIN_OBJ) galera._run() address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/tst-buteo-import-no-accounts.cpp0000644000015300001610000000377012632607666031623 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "ab-update.h" class TstButeoImportNoAccounts : public QObject { Q_OBJECT QTemporaryFile *m_settingsFile; private Q_SLOTS: void init() { m_settingsFile = new QTemporaryFile; QVERIFY(m_settingsFile->open()); qDebug() << "Using as temporary file:" << m_settingsFile->fileName(); //Make sure that we use a new settings every time that we run the test QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, m_settingsFile->fileName()); } void cleanup() { delete m_settingsFile; } void tst_isOutDated() { ABUpdate updater; updater.skipNetworkTest(); updater.setSilenceMode(true); QVERIFY(updater.needsUpdate().isEmpty()); } void tst_importAccount() { ABUpdate updater; updater.skipNetworkTest(); QSignalSpy updatedSignal(&updater, SIGNAL(updateDone())); qDebug() << "WILL START"; updater.startUpdate(); QTRY_COMPARE(updatedSignal.count(), 1); QCOMPARE(updater.isRunning(), false); QVERIFY(updater.needsUpdate().isEmpty()); } }; QTEST_MAIN(TstButeoImportNoAccounts) #include "tst-buteo-import-no-accounts.moc" address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/0000755000015300001610000000000012632610222025220 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/full-database/0000755000015300001610000000000012632610222027724 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/full-database/accounts.db0000644000015300001610000004600012632607666032074 0ustar pbuserpbgroup00000000000000SQLite format 3@ -   $"; renato.teste4@gmail.comgoogle" ; renato.teste3@gmail.comgoogle" ; renato.teste2@gmail.comgoogle  Accounts Services"  "02I;xrVbZ! D joyn-im")joyn-messaging!joyn "Icom.ubuntu.reminders_reminders trakt ubuntuone%testsync-ovi)google-carddav%yahoo-caldav'google-caldav'yahoo-carddav;shotwell-sharing-picasa?shotwell-sharing-facebook picasa)flickr-sharing/windows-live-mail#facebook-im;shotwell-sharing-flickr yahoo-im!yahoo-mail)yahoo-calendar%google-drive aim-im google-im %google-gmail wlm /twitter-microblog jabber-im'local-xmpp-im+google-calendar+google-contacts1facebook-microblog-flickr-microblog- facebook-sharing "02I;xrVbZ! D joyn-im")joyn-messaging!joyn "Icom.ubuntu.reminders_reminders trakt ubuntuone%testsync-ovi)google-carddav%yahoo-caldav'google-caldav'yahoo-carddav;shotwell-sharing-picasa?shotwell-sharing-facebook picasa)flickr-sharing/windows-live-mail#facebook-im;shotwell-sharing-flickr yahoo-im!yahoo-mail)yahoo-calendar%google-drive aim-im google-im %google-gmail wlm /twitter-microblog jabber-im'local-xmpp-im+google-calendar+google-contacts1facebook-microblog-flickr-microblog- facebook-sharing u_7  %DY1.>IsIE@enabledbtrue>enabledbtrue?enabledbtrue;enabledbtrue&?names'renato.teste3@gmail.com'enabledbtrue'CredentialsIdu248:enabledbtrue&?names'renato.teste2@gmail.com'enabledbtrue'CredentialsIdu247  ${{{`H`#" <`[2enabled@enabled?enabled>5enabled;enabled:UD3" nameenabled'CredentialsId nameenabled' CredentialsId zzP%pUtableAccountsAccountsCREATE TABLE Accounts (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,provider TEXT,enabled INTEGER)P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)!tableServicesServicesCREATE TABLE Services (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL UNIQUE,display TEXT NOT NULL,provider TEXT,type TEXT)/Cindexsqlite_autoindex_Services_1ServicesI#aindexidx_serviceServicesCREATE INDEX idx_service ON Services(name) mtableSettingsSettingsCREATE TABLE Settings (account INTEGER NOT NULL,service INTEGER,key TEXT NOT NULL,type TEXT NOT NULL,value BLOB)  O\m )!indexidx_signaturesSignatures CREATE UNIQUE INDEX idx_signatures ON Signatures (account, service, key)c#indexidx_settingSettingsCREATE UNIQUE INDEX idx_setting ON Settings (account, service, key)%/ triggertg_delete_accountAccountsCREATE TRIGGER tg_delete_account BEFORE DELETE ON Accounts FOR EACH ROW BEGIN DELETE FROM Settings WHERE account = OLD.id; END! !! tableSignaturesSignatures CREATE TABLE Signatures (account INTEGER NOT NULL,service INTEGER,key TEXT NOT NULL,signature TEXT NOT NULL,token TEXT NOT NULL)  5i5Y6wZv5??-shotwell-sharing-facebookFacebookfacebookshotwell-sharingpicasaPicasagooglesharing')flickr-sharingFlickrflickrsharing8//%windows-live-mailWindows Live Mailwindows-livemail##facebook-imFacebookfacebookIM9;-shotwell-sharing-flickrFlickrflickrshotwell-sharingyahoo-imYahoo!yahooIM$!#yahoo-mailYahoo! Mailyahoomail0)+yahoo-calendarYahoo! Calendaryahoocalendar, %#google-driveGoogleDrivegoogledocuments aim-imAIMaimIM! !google-imGoogleTalkgoogleIM! %google-gmailGMailgooglemail- 9%wlmWindows Live Messengerwindows-liveIM2/'twitter-microblogTwittertwittermicrobloggingjabber-imJabberjabberIM$'!local-xmpp-imSalutlocal-xmppIM2++google-calendarGoogle Calendargooglecalendar2++google-contactsGoogle Contactsgooglecontacts51'facebook-microblogFacebookfacebookmicroblogging/-'flickr-microblogFlickrflickrmicroblogging--facebook-sharingFacebookfacebooksharing c42"joyn-imJoynjoynIM,!)-joyn-messagingJoynjoynubuntu-messaging" -joynJoynubuntu-messagingjoyn_Iecom.ubuntu.reminders_remindersEvernotecom.ubuntu.reminders_evernote-account-pluginsharingtraktTrakt.tvtrakttrakt+!ubuntuoneUbuntu Oneubuntuoneubuntuone %testsync-oviOviSyncoviSync1)+google-carddavGoogle Contactsgooglecontacts-%)yahoo-caldavYahoo Calendaryahoocalendar0'+google-caldavGoogle Calendargooglecalendar.')yahoo-carddavYahoo Contactsyahoocontacts9;-shotwell-sharing-picasaPicasagoogleshotwell-sharingaddress-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/full-database/CMakeLists.txt0000644000015300001610000000055412632607666032512 0ustar pbuserpbgroup00000000000000add_custom_target(copy_accounts_full_db ALL) add_custom_command(TARGET copy_accounts_full_db PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db ${CMAKE_CURRENT_BINARY_DIR}/) if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) add_dependencies(copydbfiles copy_accounts_full_db) endif() address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/empty-database/0000755000015300001610000000000012632610222030120 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/empty-database/accounts.db0000644000015300001610000004600012632607666032270 0ustar pbuserpbgroup00000000000000SQLite format 3@ -   $  Accounts Services"  "02I;xrVbZ! D joyn-im")joyn-messaging!joyn "Icom.ubuntu.reminders_reminders trakt ubuntuone%testsync-ovi)google-carddav%yahoo-caldav'google-caldav'yahoo-carddav;shotwell-sharing-picasa?shotwell-sharing-facebook picasa)flickr-sharing/windows-live-mail#facebook-im;shotwell-sharing-flickr yahoo-im!yahoo-mail)yahoo-calendar%google-drive aim-im google-im %google-gmail wlm /twitter-microblog jabber-im'local-xmpp-im+google-calendar+google-contacts1facebook-microblog-flickr-microblog- facebook-sharing "02I;xrVbZ! D joyn-im")joyn-messaging!joyn "Icom.ubuntu.reminders_reminders trakt ubuntuone%testsync-ovi)google-carddav%yahoo-caldav'google-caldav'yahoo-carddav;shotwell-sharing-picasa?shotwell-sharing-facebook picasa)flickr-sharing/windows-live-mail#facebook-im;shotwell-sharing-flickr yahoo-im!yahoo-mail)yahoo-calendar%google-drive aim-im google-im %google-gmail wlm /twitter-microblog jabber-im'local-xmpp-im+google-calendar+google-contacts1facebook-microblog-flickr-microblog- facebook-sharing 777 %DY1.>IsIE1Y1 y{{{`H`#" <`[c>,&2% zzP%pUtableAccountsAccountsCREATE TABLE Accounts (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,provider TEXT,enabled INTEGER)P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)!tableServicesServicesCREATE TABLE Services (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL UNIQUE,display TEXT NOT NULL,provider TEXT,type TEXT)/Cindexsqlite_autoindex_Services_1ServicesI#aindexidx_serviceServicesCREATE INDEX idx_service ON Services(name) mtableSettingsSettingsCREATE TABLE Settings (account INTEGER NOT NULL,service INTEGER,key TEXT NOT NULL,type TEXT NOT NULL,value BLOB)  O\m )!indexidx_signaturesSignatures CREATE UNIQUE INDEX idx_signatures ON Signatures (account, service, key)c#indexidx_settingSettingsCREATE UNIQUE INDEX idx_setting ON Settings (account, service, key)%/ triggertg_delete_accountAccountsCREATE TRIGGER tg_delete_account BEFORE DELETE ON Accounts FOR EACH ROW BEGIN DELETE FROM Settings WHERE account = OLD.id; END! !! tableSignaturesSignatures CREATE TABLE Signatures (account INTEGER NOT NULL,service INTEGER,key TEXT NOT NULL,signature TEXT NOT NULL,token TEXT NOT NULL)  5i5Y6wZv5??-shotwell-sharing-facebookFacebookfacebookshotwell-sharingpicasaPicasagooglesharing')flickr-sharingFlickrflickrsharing8//%windows-live-mailWindows Live Mailwindows-livemail##facebook-imFacebookfacebookIM9;-shotwell-sharing-flickrFlickrflickrshotwell-sharingyahoo-imYahoo!yahooIM$!#yahoo-mailYahoo! Mailyahoomail0)+yahoo-calendarYahoo! Calendaryahoocalendar, %#google-driveGoogleDrivegoogledocuments aim-imAIMaimIM! !google-imGoogleTalkgoogleIM! %google-gmailGMailgooglemail- 9%wlmWindows Live Messengerwindows-liveIM2/'twitter-microblogTwittertwittermicrobloggingjabber-imJabberjabberIM$'!local-xmpp-imSalutlocal-xmppIM2++google-calendarGoogle Calendargooglecalendar2++google-contactsGoogle Contactsgooglecontacts51'facebook-microblogFacebookfacebookmicroblogging/-'flickr-microblogFlickrflickrmicroblogging--facebook-sharingFacebookfacebooksharing c42"joyn-imJoynjoynIM,!)-joyn-messagingJoynjoynubuntu-messaging" -joynJoynubuntu-messagingjoyn_Iecom.ubuntu.reminders_remindersEvernotecom.ubuntu.reminders_evernote-account-pluginsharingtraktTrakt.tvtrakttrakt+!ubuntuoneUbuntu Oneubuntuoneubuntuone %testsync-oviOviSyncoviSync1)+google-carddavGoogle Contactsgooglecontacts-%)yahoo-caldavYahoo Calendaryahoocalendar0'+google-caldavGoogle Calendargooglecalendar.')yahoo-carddavYahoo Contactsyahoocontacts9;-shotwell-sharing-picasaPicasagoogleshotwell-sharingaddress-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/empty-database/CMakeLists.txt0000644000015300001610000000055712632607666032711 0ustar pbuserpbgroup00000000000000add_custom_target(copy_accounts_empty_db ALL) add_custom_command(TARGET copy_accounts_empty_db PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db ${CMAKE_CURRENT_BINARY_DIR}/) if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) add_dependencies(copydbfiles copy_accounts_empty_db) endif() address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/accounts/CMakeLists.txt0000644000015300001610000000014112632607666027776 0ustar pbuserpbgroup00000000000000add_custom_target(copydbfiles) add_subdirectory(empty-database) add_subdirectory(full-database) address-book-service-0.1.1+16.04.20151211.1/tests/tst_tools/CMakeLists.txt0000644000015300001610000000327612632607666026173 0ustar pbuserpbgroup00000000000000find_program(DBUS_RUNNER_BIN dbus-test-runner) #tst-buteo-import add_executable(tst-buteo-import tst-buteo-import.cpp ) target_link_libraries(tst-buteo-import Qt5::Core Qt5::DBus Qt5::Test Qt5::Contacts address-book-updater-lib ) add_test(tst-buteo-import ${DBUS_RUNNER} --keep-env --task ${CMAKE_CURRENT_BINARY_DIR}/tst-buteo-import --wait-for=com.meego.msyncd --task ${CMAKE_CURRENT_SOURCE_DIR}/mock/mock-buteo.py --wait-for=com.canonical.pim -r --task-name=\"Buteo\" --task ${CMAKE_CURRENT_SOURCE_DIR}/mock/mock-galera.py -r --task-name=\"AddressBook Service\" --task ${CMAKE_CURRENT_SOURCE_DIR}/mock/mock-sync-monitor.py -r --task-name=\"Sync Monitor\" ) set_tests_properties(tst-buteo-import PROPERTIES ENVIRONMENT ACCOUNTS=${CMAKE_CURRENT_BINARY_DIR}/accounts/full-database ) #tst-buteo-import-no-accounts add_executable(tst-buteo-import-no-accounts tst-buteo-import-no-accounts.cpp ) target_link_libraries(tst-buteo-import-no-accounts Qt5::Core Qt5::DBus Qt5::Test Qt5::Contacts address-book-updater-lib ) add_test(tst-buteo-import-no-accounts ${DBUS_RUNNER} --keep-env --task ${CMAKE_CURRENT_BINARY_DIR}/tst-buteo-import-no-accounts --wait-for=com.meego.msyncd --task ${CMAKE_CURRENT_SOURCE_DIR}/mock/mock-buteo.py --wait-for=com.canonical.pim -r --task-name=\"Buteo\" --task ${CMAKE_CURRENT_SOURCE_DIR}/mock/mock-galera.py -r --task-name=\"AddressBook Service\" ) set_tests_properties(tst-buteo-import-no-accounts PROPERTIES ENVIRONMENT ACCOUNTS=${CMAKE_CURRENT_BINARY_DIR}/accounts/empty-database ) include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/updater ) add_subdirectory(accounts) address-book-service-0.1.1+16.04.20151211.1/tests/data/0000755000015300001610000000000012632610222022260 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/data/new_avatar.svg0000644000015300001610000024624512632607666025167 0ustar pbuserpbgroup00000000000000 image/svg+xml address-book-service-0.1.1+16.04.20151211.1/tests/data/backend-store-key-file-data.ini0000644000015300001610000000025512632607666030140 0ustar pbuserpbgroup00000000000000#export FOLKS_BACKEND_KEY_FILE_PATH [0] __alias=Renato Araujo msn=renato@msn.com [1] __alias=Rodrigo Almeida msn=kiko@msn.com [2] __alias=Raphael Almeida msn=rafa@msn.com address-book-service-0.1.1+16.04.20151211.1/tests/data/vcard.vcf0000644000015300001610000000010512632607666024075 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 N:User;UX; FN:UX User TEL:3333333 END:VCARD address-book-service-0.1.1+16.04.20151211.1/tests/data/avatar.svg0000644000015300001610000024624512632607666024316 0ustar pbuserpbgroup00000000000000 image/svg+xml address-book-service-0.1.1+16.04.20151211.1/tests/data/CMakeLists.txt0000644000015300001610000000035012632607666025040 0ustar pbuserpbgroup00000000000000project(test-data) set(FOLKS_BACKEND_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/backend-store-key-file-only.ini" PARENT_SCOPE) set(FOLKS_KEY_FILE_DATA_FILE "${CMAKE_CURRENT_SOURCE_DIR}/backend-store-key-file-data.ini" PARENT_SCOPE) address-book-service-0.1.1+16.04.20151211.1/tests/CMakeLists.txt0000644000015300001610000000045412632607666024134 0ustar pbuserpbgroup00000000000000find_program(EVOLUTION_ADDRESSBOOK_FACTORY_BIN evolution-addressbook-factory PATHS /usr/lib/evolution/) find_program(EVOLUTION_SOURCE_REGISTRY evolution-source-registry PATHS /usr/lib/evolution/) add_subdirectory(data) add_subdirectory(unittest) add_subdirectory(tst_tools) address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/0000755000015300001610000000000012632610222030143 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/__init__.py0000644000015300001610000000143212632607666032276 0ustar pbuserpbgroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical # Author: Omer Akram # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/tests/0000755000015300001610000000000012632610222031305 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/tests/__init__.py0000644000015300001610000000143212632607666033440 0ustar pbuserpbgroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical # Author: Omer Akram # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/tests/test_dummy_backend.pyaddress-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/tests/test_dummy_0000644000015300001610000000255212632607666033607 0ustar pbuserpbgroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical # Author: Omer Akram # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import testtools from address_book_service_testability import fixture_setup, helpers class DummyBackendTestCase(testtools.TestCase): """tests for the dummy backend of contacts service.""" def setUp(self): dummy_backend = fixture_setup.AddressBookServiceDummyBackend() self.useFixture(dummy_backend) super(DummyBackendTestCase, self).setUp() def test_dummy_backend_loads_vcard(self): """Makes sure the dummy backend is loading the vcard.""" contacts = str(helpers.query_contacts()) self.assertTrue('UX User' in contacts, True) address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/fixture_setup.py0000644000015300001610000000747112632607666033456 0ustar pbuserpbgroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Canonical # Author: Omer Akram # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os import subprocess import sysconfig import time from fixtures import EnvironmentVariable, Fixture def get_service_library_path(): """Return path of address-book-service binary directory.""" architecture = sysconfig.get_config_var('MULTIARCH') return os.path.join( '/usr/lib/', architecture, 'address-book-service/') class AddressBookServiceDummyBackend(Fixture): """Fixture to load test vcard for client applications Call the fixture without any paramter to load a default vcard :parameter vcard: call the fixture with a vcard to be used by test application. """ def __init__(self, vcard=None): self.contact_data = vcard def setUp(self): super(AddressBookServiceDummyBackend, self).setUp() self.useFixture(SetupEnvironmentVariables(self.contact_data)) self.useFixture(RestartService()) class SetupEnvironmentVariables(Fixture): def __init__(self, vcard): self.vcard = vcard def setUp(self): super(SetupEnvironmentVariables, self).setUp() self._setup_environment() def _setup_environment(self): self.useFixture(EnvironmentVariable( 'ALTERNATIVE_CPIM_SERVICE_NAME', 'com.canonical.test.pim')) self.useFixture(EnvironmentVariable( 'FOLKS_BACKEND_PATH', os.path.join(get_service_library_path(), 'dummy.so'))) self.useFixture(EnvironmentVariable('FOLKS_BACKENDS_ALLOWED', 'dummy')) self.useFixture(EnvironmentVariable('FOLKS_PRIMARY_STORE', 'dummy')) self.useFixture(EnvironmentVariable( 'ADDRESS_BOOK_SERVICE_DEMO_DATA', self._get_vcard_location())) def _get_vcard_location(self): if self.vcard: return self.vcard local_location = os.path.abspath('vcard.vcf') bin_location = '/usr/share/address-book-service/data/vcard.vcf' if os.path.exists(local_location): return local_location elif os.path.exists(bin_location): return bin_location else: raise RuntimeError('No VCARD found.') class RestartService(Fixture): def setUp(self): super(RestartService, self).setUp() self.addCleanup(self._kill_address_book_service) self._restart_address_book_service() def _kill_address_book_service(self): try: pid = subprocess.check_output( ['pidof', 'address-book-service']).strip() subprocess.call(['kill', '-3', pid]) except subprocess.CalledProcessError: # Service not running, so do nothing. pass def _restart_address_book_service(self): self._kill_address_book_service() path = os.path.join( get_service_library_path(), 'address-book-service') subprocess.Popen([path]) # FIXME: Wait for 5 seconds before proceeding so that the # service starts,doing this because the dbus interface is # not reliable enough it seems. --om26er 23-07-2014 time.sleep(5) address-book-service-0.1.1+16.04.20151211.1/tests/address_book_service_testability/helpers.py0000644000015300001610000000300512632607666032177 0ustar pbuserpbgroup00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright 2014 Canonical Ltd. # # This file is part of address-book-service tests. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see import dbus DBUS_IFACE_ADD_BOOK = 'com.canonical.pim.AddressBook' DBUS_IFACE_ADD_BOOKVIEW = 'com.canonical.pim.AddressBookView' bus = dbus.SessionBus() def query_contacts(fields='', query='', max_count=0, show_invisible=False, sources=[]): iface = _get_contacts_dbus_service_iface() view_path = iface.query(fields, query, max_count, show_invisible, []) view = bus.get_object( 'com.canonical.pim', view_path) view_iface = dbus.Interface( view, dbus_interface=DBUS_IFACE_ADD_BOOKVIEW) contacts = view_iface.contactsDetails([], 0, -1) view.close() return contacts def _get_contacts_dbus_service_iface(): proxy = bus.get_object( 'com.canonical.pim', '/com/canonical/pim/AddressBook') return dbus.Interface(proxy, 'com.canonical.pim.AddressBook') address-book-service-0.1.1+16.04.20151211.1/src/0000755000015300001610000000000012632610222020774 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/src/main.cpp0000644000015300001610000000653212632607666022454 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define ADDRESS_BOOK_SERVICE_DEMO_DATA "ADDRESS_BOOK_SERVICE_DEMO_DATA" #define ADDRESS_BOOK_SERVICE_DEBUG "ADDRESS_BOOK_SERVICE_DEBUG" #include "config.h" #include "addressbook.h" #include "common/vcard-parser.h" #include void contactServiceMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &message) { if (type != QtDebugMsg) { printf("[%s:%d]%s\n", qPrintable(QFileInfo(context.file).fileName()), context.line, qPrintable(message)); } } void onServiceReady(galera::AddressBook *book) { // disable debug message if variable not exported if (qEnvironmentVariableIsSet(ADDRESS_BOOK_SERVICE_DEMO_DATA)) { QFile demoFileData(qgetenv(ADDRESS_BOOK_SERVICE_DEMO_DATA)); qDebug() << "Load demo data from:" << demoFileData.fileName(); if (demoFileData.open(QFile::ReadOnly)) { QByteArray demoData = demoFileData.readAll(); QStringList vcards = galera::VCardParser::splitVcards(demoData); Q_FOREACH(const QString &vcard, vcards) { book->createContact(vcard, ""); } } } } int main(int argc, char** argv) { QCoreApplication::setOrganizationName(SETTINGS_ORG); QCoreApplication::setApplicationName(SETTINGS_APPLICATION); galera::AddressBook::init(); QCoreApplication app(argc, argv); // disable debug message if variable not exported if (qgetenv(ADDRESS_BOOK_SERVICE_DEBUG).isEmpty()) { qInstallMessageHandler(contactServiceMessageOutput); } // disable folks linking for now if (!qEnvironmentVariableIsSet("FOLKS_DISABLE_LINKING")) { qputenv("FOLKS_DISABLE_LINKING", "on"); } // Set primary store manually to avoid problems when removing stores // // Without manually set the primary store folks will try go guess that, and affter adding // a new store, folks will check if the eds store is marked as default, and if yes then folks // will update the primary store pointer with this new store. // But after removing a source if the source is used as the primary by folks, // folks will set the primary pointer to null. if (!qEnvironmentVariableIsSet("FOLKS_PRIMARY_STORE")) { qputenv("FOLKS_PRIMARY_STORE", "eds:system-address-book"); } galera::AddressBook book; QObject::connect(&book, &galera::AddressBook::readyChanged, [&book] () { onServiceReady(&book); }); app.connect(&book, SIGNAL(stopped()), SLOT(quit())); if (book.start()) { qDebug() << "Service started"; return app.exec(); } else { qDebug() << "Fail to start service"; } } address-book-service-0.1.1+16.04.20151211.1/src/CMakeLists.txt0000644000015300001610000000123212632607666023554 0ustar pbuserpbgroup00000000000000project(address-book-service) set(CONTACTS_SERVICE_BIN address-book-service) set(CONTACTS_SERVICE_BIN_SRC main.cpp ) add_executable(${CONTACTS_SERVICE_BIN} ${CONTACTS_SERVICE_BIN_SRC} ) target_link_libraries(${CONTACTS_SERVICE_BIN} address-book-service-lib ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} Qt5::Core Qt5::Contacts ) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${address-book-service-lib_SOURCE_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_INCLUDE_DIRS} ) install(TARGETS ${CONTACTS_SERVICE_BIN} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR}) address-book-service-0.1.1+16.04.20151211.1/README.test0000644000015300001610000000205612632607666022070 0ustar pbuserpbgroup00000000000000Running the address-book-service and qtcontacts in dummy mode. Running address-book-service =========================== # export ALTERNATIVE_CPIM_SERVICE_NAME="com.canonical.test.pim" # export FOLKS_BACKEND_PATH="/usr/lib/address-book-service/dummy.so" # export FOLKS_BACKENDS_ALLOWED="dummy" # export FOLKS_PRIMARY_STORE="dummy" # export ADDRESS_BOOK_SERVICE_DEMO_DATA= # /usr/libexec/address-book-service ALTERNATIVE_CPIM_SERVICE_NAME - Defines a new address book DBus service name FOLKS_BACKEND_PATH - Defines the path used by folks to find the backend FOLKS_BACKENDS_ALLOWED - Defines which backend folks is allowed to load FOLKS_PRIMARY_STORE - Defines which persona store should be used as primary store ADDRESS_BOOK_SERVICE_DEMO_DATA - VCard file which will be used to populate the server Running any address-book client =============================== # export ALTERNATIVE_CPIM_SERVICE_NAME="com.canonical.test.pim" ALTERNATIVE_CPIM_SERVICE_NAME - Defines a new address book DBus service name, should be the same as the server address-book-service-0.1.1+16.04.20151211.1/README0000644000015300001610000000002712632607666021106 0ustar pbuserpbgroup00000000000000canonical pim service. address-book-service-0.1.1+16.04.20151211.1/po/0000755000015300001610000000000012632610222020623 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/po/pl.po0000644000015300001610000000561012632607666021622 0ustar pbuserpbgroup00000000000000# Polish translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-13 11:56+0000\n" "Last-Translator: Bartosz Kosiorek \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Nie" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Aktualizacja zakończona" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/po/address-book-service.pot0000644000015300001610000000532012632607666025404 0ustar pbuserpbgroup00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Canonical Ltd. # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/po/gl.po0000644000015300001610000000776712632607666021630 0ustar pbuserpbgroup00000000000000# Galician translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-20 04:29+0000\n" "Last-Translator: Marcos Lans \n" "Language-Team: Galician \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "A conta de sincronización de contactos de %1 precisa anovación. Conéctese a " "internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" "A anovación da conta de sincronización de contactos de %1 xa está en proceso" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" "A anovación da conta de sincronización de contactos de %1 está en proceso" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Desexa reintentalo agora?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Actualización de contas" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Servizo de axenda" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Completouse a anovación da sincronización de contactos." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Xa se actualizou o aplicativo de contactos." #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Produciuse un fallo na autenticación." #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" "Produciuse un fallo conectando co servizo de sincronización de contactos." #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Produciuse un fallo ao conectar a internet." #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Produciuse un fallo creando o perfil de sincronización." #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Produciuse un fallo sincronizando os contactos." #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Produciuse un na actualización." #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Erro interno na sincronización." #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Non" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Non foi posíbel atopar a conta en liña." #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Para tentalo máis tarde, abra o aplicativo Contactos e prema no botón de " "sincronización." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "A actualización xa está en progreso." #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Actualización completada" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Requírese actualización" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Si" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "O aplicativo Contactos precisa anovarse. Até que se complete a anovación só " "poderá editar os contactos locais" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Completouse a actualización do aplicativo de contactos." address-book-service-0.1.1+16.04.20151211.1/po/nl.po0000644000015300001610000000737712632607666021634 0ustar pbuserpbgroup00000000000000# Dutch translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-24 07:16+0000\n" "Last-Translator: Hannie Dumoleyn \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "%1 contact sync-account moet worden opgewaardeerd. Gelieve verbinding te " "maken met het internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "%1 contact sync-account opwaardering is al begonnen" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "%1 contact sync-account opwaardering is begonnen" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Wilt u het nu opnieuw proberen?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Account bijwerken" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Adresboekdienst" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Contact sync opwaardering voltooid." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Contactenapp is al bijgewerkt!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Authenticatie mislukt!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Verbinden met contact sync-dienst mislukt!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Verbinden met het internet mislukt!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Aanmaken syncprofiel mislukt!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Contacten synchroniseren mislukt!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Bijwerken mislukt" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Interne fout tijdens de synchronisatie!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Nee" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Online-acount niet gevonden!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Om het later opnieuw te proberen, open de Contactenapp en druk op de sync-" "knop." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Bijwerken is al begonnen!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Bijwerken voltooid" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Bijwerken vereist" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Ja" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "U dient uw Contactenapp op te waarderen. Totdat het opwaarderen voltooid is, " "zullen alleen lokale contacten kunnen worden bewerkt." #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Het bijwerken van uw Contactenapp is voltooid." address-book-service-0.1.1+16.04.20151211.1/po/pt.po0000644000015300001610000000753512632607666021642 0ustar pbuserpbgroup00000000000000# Portuguese translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-19 14:51+0000\n" "Last-Translator: Ivo Xavier \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "Conta %1 de sincronização de contactos precisa de ser atualizada. Ligue-se à " "internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "Conta %1 de sincronização de contactos está a ser atualizada." #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Conta %1 de sincronização de contactos em atualização." #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Quer tentar novamente?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Atualização de conta" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Serviço de livro de endereços" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Atualização de sincronização de contactos completa." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "A app de contactos está atualizada!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Falha ao autenticar!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Falha ao ligar ao serviço de sincronização de contactos!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Falha ao ligar à internet!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Falha ao criar perfil de sincronização!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Falha ao sincronizar contactos!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Falha ao atualizar" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Erro interno durante a sincronização!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Não" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Sem conta online!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Para tentar novamente, abra os Contactos e clique no botão de sincronizar." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Atualização já está em progresso!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Atualização completa" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Atualização necessária" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Sim" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "A sua app de contactos precisa de ser atualizada. Apenas contactos locais " "serão editáveis até a atualização estar completa." #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "A sua app de contactos está atualizada." address-book-service-0.1.1+16.04.20151211.1/po/lv.po0000644000015300001610000000765512632607666021643 0ustar pbuserpbgroup00000000000000# Latvian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # # FIRST AUTHOR , 2015. # Rūdolfs Mazurs , 2015. msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-22 19:12+0000\n" "Last-Translator: Rūdolfs Mazurs \n" "Language-Team: Latvian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" "Language: lv\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "%1 kontaktu sinhronizēšanas kontu ir jāuzlabo. Lūdzu, savienojieties ar " "internetu." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "%1 kontaktu sinhronizēšanas konta uzlabošana jau notiek" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Notiek %1 kontaktu sinhronizēšanas konta uzlabošana" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Vai vēlaties mēģināt vēlreiz tagad?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Konta atjaunināšana" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Adrešu grāmatas serviss" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Kontaktu sinhronizēšanas uzlabošana ir pabeigta." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Kontaktu lietotne jau ir atjaunināta!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Neizdevās autentificēties!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Neizdevās savienoties ar kontaktu sinhronizēšanas servisu!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Neizdevās savienoties ar internetu!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Neizdevās izveidot sinhronizēt profilu!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Neizdevās sinhronizēt kontaktus!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Neizdevās atjaunināt" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Iekšēja kļūda sinhronizējot!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Nē" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Netika atrasts tiešsaistes konts!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Lai vēlāk mēģinātu atkal, atveriet Kontaktu lietotni un spiediet " "sinhronizēšanas pogu." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Jau notiek atjaunināšana!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Atjaunināšana ir pabeigta" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Nepieciešama atjaunināšana" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Jā" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Jūsu kontaktu lietotne ir jāuzlabo. Līdz uzlabošanai varēs rediģēt tikai " "lokālos kontaktus" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Kontaktu lietotne ir uzlabota." address-book-service-0.1.1+16.04.20151211.1/po/hu.po0000644000015300001610000001001312632607666021614 0ustar pbuserpbgroup00000000000000# Hungarian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-24 09:03+0000\n" "Last-Translator: Richard Somlói \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "%1 névjegy szinkronizáló szolgáltatást frissíteni kell. Kérjük csatlakozzon " "az internethez." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "%1 névjegy szinkronizáló szolgáltatás frissítése már folyamatban van" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "%1 névjegy szinkronizáló szolgáltatás frissítése folyamatban" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Szeretné újrapróbálni most?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Fiókfrissítés" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Névjegyek szolgáltatás" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "A névjegy szinkronizáló szolgáltatás frissítése sikeres." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "A Névjegyek alkalmazás frissítése már megtörtént!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Sikertelen hitelesítés!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Nem sikerült csatlakozni a névjegy szinkronizáló szolgáltatáshoz!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Nem sikerült csatlakozni az internethez!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Nem sikerült a szinkronizálóprofil létrehozása!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Nem sikerült a névjegy szinkronizálás!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Nem sikerült a frissítés" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Belső hiba történt a szinkronizálás közben!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Nem" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Online fiók megtalálva!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Próbálja később újra megnyitni a Névjegyek alkalmazást és kattintson a " "szinkronizálás gombra." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "A frissítés már folyamatban van!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "A frissítés készen van" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Frissítés szükséges" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Igen" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "A Névjegyek alkalmazás frissítésre szorul. Csak a helyi névjegyek " "szerkeszthetőek addig, amíg a frissítés kész nem lesz." #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "A Névjegyek alkalmazás frissítése sikeres." address-book-service-0.1.1+16.04.20151211.1/po/br.po0000644000015300001610000001004612632607666021611 0ustar pbuserpbgroup00000000000000# Breton translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-03 07:21+0000\n" "Last-Translator: Fohanno Thierry \n" "Language-Team: Breton \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "Ar gont da sinkronelaat darempredoù %1 he deus ezhomm da vezañ lakaet a-" "live. Kevreit ouzh internet, mar plij." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "Emeur o lakaat a-live ar gont da sinkronelaat darempredoù %1 dija" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Emeur o lakaat a-live ar gont da sinkronelaat darempredoù %1" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Ha fellout a ra deoc'h esaeañ adarre bremañ ?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Lakaat ar gont a-live" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Servij levr chomlec'hioù" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Echu ei lakaat a-live sinkroneladur an darempredoù" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "A-live emañ an arload Darempredoù dija ?" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "C'hwitet eo an dilesadur !" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" "N'eus ket bet gallet kevreañ ouzh ar servij da sinkronelaat an darempredoù !" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "N'eus ket bet gallet kevreañ ouzh internet !" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "N'eus ket bet gallet krouiñ ur profil sinkronelaat !" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "N'eus ket bet gallet sinkronelaat an darempredoù !" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "N'eus ket bet gallet lakaat a-live" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Fazi diabarzh e-pad ar sinkronelaat !" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Ket" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "N'eo ket bet kavet ar gont enlinenn !" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Evit esaeañ diwezhatoc'h, digeriñ an arload Darempredoù ha pouezañ war ar " "bouton sinkronelaat" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Emeur o lakaat a-live dija !" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Echu eo al lakaat a-live" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Ret eo lakaat a-live" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Ya" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Ret eo lakaat hoc'h arload Darempredoù a-live. N'eus nemet an darempredoù " "lec'hel a c'hallo bezañ aozet betek ma vo echu al lakaat a-live" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Echu eo lakaat hoc'h arload Darempredoù a-live" address-book-service-0.1.1+16.04.20151211.1/po/it.po0000644000015300001610000000765412632607666021635 0ustar pbuserpbgroup00000000000000# Italian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-24 13:22+0000\n" "Last-Translator: Claudio Arseni \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "L'account di sincronizzazione contatti %1 deve essere aggiornato. " "Connettersi a Internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" "Aggiornamento dell'account di sincronizzazione contatti %1 già in corso" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Aggiornamento dell'account di sincronizzazione contatti %1 in corso" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Riprovare ora?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Aggiornamento account" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Servizio di rubrica" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Aggiornamento della sincronizzazione contatti completato." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "App Contatti già aggiornata." #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Autenticazione non riuscita." #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Connessione al servizio di sincronizzazione contatti non riuscita." #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Connessione a Internet non riuscita." #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Creazione del profilo di sincronizzazione non riuscita." #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Sincronizzazione dei contatti non riuscita." #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Aggiornamento non riuscito" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Errore interno durante la sincronizzazione." #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "No" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Account online non trovato." #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Per riprovare in un secondo momento aprire l'app Contatti e premere il " "pulsante di sincronizzazione." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Aggiornamento già in corso." #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Aggiornamento completato" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Aggiornamento richiesto" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Sì" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "L'app Contatti deve essere aggiornata. Solo i contatti locali saranno " "modificabili fino al completamento dell'aggiornamento" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Aggiornamento dell'app Contatti completato." address-book-service-0.1.1+16.04.20151211.1/po/gd.po0000644000015300001610000000560212632607666021602 0ustar pbuserpbgroup00000000000000# Gaelic; Scottish translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-19 20:48+0000\n" "Last-Translator: GunChleoc \n" "Language-Team: Gaelic; Scottish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Chan eil" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/po/sr.po0000644000015300001610000001122212632607666021627 0ustar pbuserpbgroup00000000000000# Serbian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-21 21:35+0000\n" "Last-Translator: Bojan Bogdanović \n" "Language-Team: Serbian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "%1 налогу за синхронизовање контаката је потребна надоградња. Молимо да се " "повежете на интернет." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "Надоградња %1 налога за синхронизовање контаката је већ у току" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Надоградња %1 налога за синхронизовање контаката је у току" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Да ли желите да поново покушате сада?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Ажурирање налога" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Сервис именика" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Надоградња сонхронизације контаката је завршена." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Програм за контактвећ е је ажуриран!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Неуспешно аутентификовање!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Неуспешно повезивање на сервис синхронизације контаката!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Неупсешно повезивање на интернет!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Неуспешно креирање профила за синхронизацију!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Неуспешно синхронизовање контаката!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Неуспешно ажурирање" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Грешка на интернету током синхронизације!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Не" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Налог на мрежи није пронађен!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Да покушате касније отворите програм за контакте и притисните дугме за " "синхронизацију." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Ажурирање је већ у току!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Ажурирање је завршено" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Потребно ажурирање" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Да" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Ваш програм за контакте мора да се надогради. Само локални контакти могу " "бити мењани дос се надоградња не заврши" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Ажурирање Ваше апликације за контакте је завршено." address-book-service-0.1.1+16.04.20151211.1/po/uk.po0000644000015300001610000001163212632607666021627 0ustar pbuserpbgroup00000000000000# Ukrainian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-23 14:22+0000\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "Обліковий запис синхронізації контактів %1 потребує оновлення. Будь ласка, " "встановіть з’єднання з інтернетом." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" "Оновлення облікового запису синхронізації контактів %1 вже виконується" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" "Оновження облікового запису синхронізації контактів %1 вже виконується" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Хочете повторити спробу зараз?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Оновлення облікового запису" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Служба адресної книги" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Оновлення служби синхронізації контактів завершено." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Програму обслуговування записів контактів вже оновлено!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Не вдалося пройти розпізнавання!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Не вдалося встановити з’єднання із службою синхронізації контактів!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Не вдалося встановити з’єднання із інтернетом!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Не вдалося створити профіль синхронізації!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Не вдалося синхронізувати контакти!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Не вдалося оновити" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Внутрішня помилка під час синхронізації!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Ні" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Не знайдено облікового запису!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Щоб повторити спробу пізніше, відкрийте «Контакти» і натисніть кнопку " "синхронізації." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Оновлення вже виконується!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Оновлення зображення" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Потрібне оновлення" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Так" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Ваш засіб для роботи з контактами потребує оновлення. До завершення " "оновлення ви зможете редагувати лише локальні записи контактів" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Оновлення вашої програми обслуговування контактів завершено." address-book-service-0.1.1+16.04.20151211.1/po/fi.po0000644000015300001610000000713012632607666021604 0ustar pbuserpbgroup00000000000000# Finnish translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-23 16:11+0000\n" "Last-Translator: Jiri Grönroos \n" "Language-Team: Finnish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "\"%1\"-yhteystietojen synkronointitili tulee päivittää. Yhdistä laite " "internetiin." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "\"%1\"-yhteystietojen synkronointitilin päivitys on jo käynnissä" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "\"%1\"-yhteystietojen synkronointitilin päivitys on käynnissä" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Haluatko yrittää uudelleen nyt?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Tilin päivitys" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Osoitekirjapalvelu" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Yhteystietojen synkronoinnin päivitys on valmis." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Yhteystietosovellus on jo päivitetty!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Sisäinen virhe synkronoinnin aikana!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Ei" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Verkkotiliä ei löytynyt!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Yritä myöhemmin uudelleen avaamalla yhteystietosovellus ja painamalla " "synkronointipainiketta." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Päivitys on jo meneillään!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Päivitys valmistui" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Päivitys vaaditaan" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Kyllä" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Yhteystietosovellus tulee päivittää. Vain paikalliset yhteystiedot ovat " "muokattavissa, kunnes päivitys on tehty" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/po/ru.po0000644000015300001610000001010512632607666021630 0ustar pbuserpbgroup00000000000000# Russian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-26 06:09+0000\n" "Last-Translator: ☠Jay ZDLin☠ \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Попробовать ещё раз?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Обновление аккаунта" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Адресная книга" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Приложение Контакты уже обновлено!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Сбой аутентификации!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Не удалось подключиться к службе синхронизации контактов!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Не удалось подключиться к интернету!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Не удалось создать профиль синхронизации!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Не удалось синхронизировать контакты!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Не удалось обновить" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Внутренняя ошибка при синхронизации!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Нет" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Онлайн аккаунт не найден!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Для повторной попытки откройте Контакты и нажмите кнопку синхронизации." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Обновление уже запущено!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Обновление завершено" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Требуется обновление" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Да" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Приложению Контакты требуется обновление. До завершения обновления можно " "будет редактировать только локальные контакты" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Приложение Контакты обновлено" address-book-service-0.1.1+16.04.20151211.1/po/es.po0000644000015300001610000000764012632607666021623 0ustar pbuserpbgroup00000000000000# Spanish translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-27 13:55+0000\n" "Last-Translator: Víctor R. Ruiz \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "La sincronización de contactos de %1 necesita una actualización. Por favor, " "conéctese a Internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "La sincronización de contactos de %1 ya está en marcha" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "La sincronización de contactos de %1 está en marcha" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "¿Quiere intentarlo de nuevo ahora?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Actualización de cuenta" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Servicio de libreta de direcciones" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Actualización de sincronización de contactos completada." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Ya se ha actualizado la aplicación de cuentas." #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "¡Fallo al autentificar!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "¡Falló al conectar al servicio de sincronización de contactos!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "¡Falló al conectar a Internet!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "¡Falló al crear un perfil de sincronización!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "¡Falló al sincronizar contactos!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Fallo al actualizar" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "¡Error interno durante la sincronización!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "No" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "No se encontró la cuenta en línea." #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Para reintentarlo más tarde, abra la apli de Contactos y presione el botón " "de sincronización." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "¡Actualización ya en marcha!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Actualización completada" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Es necesario actualizar" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Sí" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Debe actualizar la apli de Contactos. Solo podrá editar los contactos " "locales hasta que actualice." #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "La actualización de la apli de Contactos ya está completa." address-book-service-0.1.1+16.04.20151211.1/po/CMakeLists.txt0000644000015300001610000000021012632607666023376 0ustar pbuserpbgroup00000000000000include(Translations) add_translations_directory("${GETTEXT_PACKAGE}") add_translations_catalog("${GETTEXT_PACKAGE}" ../updater ../lib) address-book-service-0.1.1+16.04.20151211.1/po/fr.po0000644000015300001610000001006712632607666021620 0ustar pbuserpbgroup00000000000000# French translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-12 18:39+0000\n" "Last-Translator: Anne \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "Le compte de synchronisation de contacts %1 a besoin d'être mis à niveau. " "Veuillez vous connecter via Internet." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" "Mise à niveau du compte de synchronisation de contacts %1 déjà en cours" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "Mise à niveau du compte de synchronisation de contacts %1 en cours" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Voulez-vous réessayer maintenant ?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Mise à jour du compte" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Service de synchronisation de carnet d'adresse" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Mise à niveau de la synchronisation de contacts terminée." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "L'application Contacts est déjà mise à jour !" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Échec lors de l'identification !" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Échec de la connexion au service de synchronisation de contacts !" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Échec de la connexion à Internet !" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Échec de la création d'un profil de synchronisation !" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Échec de la synchronisation de contacts !" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Échec de la mise à jour" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Erreur interne lors de la synchronisation !" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Non" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Compte en ligne non trouvé !" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Pour réessayer plus tard, ouvrez l'application Contacts et appuyez sur le " "bouton de synchronisation" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Mise à jour déjà en cours !" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Mise à jour terminée" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Mise à jour nécessaire" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Oui" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Votre application Contacts doit être mise à niveau. Seul les contacts locaux " "seront modifiables jusqu'à ce que la mise à niveau soit terminée" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "La mise à jour de votre application Contacts est terminée." address-book-service-0.1.1+16.04.20151211.1/po/de.po0000644000015300001610000000567312632607666021610 0ustar pbuserpbgroup00000000000000# German translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-11-22 20:04+0000\n" "Last-Translator: Phillip Sz \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Wollen Sie es nun noch einmal versuchen?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Nein" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Aktualisierung erforderlich" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Ja" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/po/be.po0000644000015300001610000001104412632607666021573 0ustar pbuserpbgroup00000000000000# Belarusian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-12-06 09:47+0000\n" "Last-Translator: XiveZ \n" "Language-Team: Belarusian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-07 05:36+0000\n" "X-Generator: Launchpad (build 17862)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" "%1 сінхранізацыя кантактаў уліковага запісу. Калі ласка, злучыцеся з " "інтэрнэтам." #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "%1 сінхранізацыя кантактаў уліковага запісу, ідзе абнаўленьне" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "%1 сінхранізацыя кантактаў уліковага запісу, прагрэс абнаўленьня" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" "%1.\n" "Паўтарыць зноўку?" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Абнаўленьне ўліковага запісу" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "Адрасная кніга" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "Сінхранізацыя кантактаў, абнаўленьне скончана." #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "Кантакты ўжо абноўлены!" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "Збой аўтэнтыфікацыі!" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "Не атрымалася далучыцца да службы сінхранізацыі кантактаў!" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "Не атрымалася далучыцца да інтэрнэту!" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "Не атрымалася стварыць профіль сінхранізацыі!" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "Не атрымалася сінхранізаваць кантакты!" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "Памылка пры абнаўленьні" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "Памылка пры сінхранізацыі!" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "Не" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "Анлайн акаўнт не знойдзены!" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" "Для паўторнай спробы адчыніце Кантакты і націсьніце кнопку сінхранізацыі." #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "Абнаўленьне ўжо запушчана!" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "Абнаўленьне выканана" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "Неабходнае абнаўленьне" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "Так" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" "Дадатку Кантакты патрабуецца абнаўленьне. Да завяршэння абнаўленьня можна " "будзе рэдагаваць толькі лакальныя кантакты" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "Дадатак Кантакты абноўлены" address-book-service-0.1.1+16.04.20151211.1/po/ast.po0000644000015300001610000000561012632607666021776 0ustar pbuserpbgroup00000000000000# Asturian translation for address-book-service # Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015 # This file is distributed under the same license as the address-book-service package. # FIRST AUTHOR , 2015. # msgid "" msgstr "" "Project-Id-Version: address-book-service\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2015-09-21 12:10-0300\n" "PO-Revision-Date: 2015-10-31 17:27+0000\n" "Last-Translator: enolp \n" "Language-Team: Asturian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2015-12-01 05:36+0000\n" "X-Generator: Launchpad (build 17850)\n" #: ../updater/ab-update.cpp:355 #, qt-format msgid "" "%1 contact sync account needs upgrade. Please connect with the internet." msgstr "" #: ../updater/ab-update.cpp:107 #, qt-format msgid "%1 contact sync account upgrade already in progress" msgstr "" #: ../updater/ab-update.cpp:344 #, qt-format msgid "%1 contact sync account upgrade in progress" msgstr "" #: ../updater/ab-update.cpp:288 #, qt-format msgid "" "%1.\n" "Do you want to retry now?" msgstr "" #: ../updater/ab-update.cpp:106 ../updater/ab-update.cpp:267 #: ../updater/ab-update.cpp:343 ../updater/ab-update.cpp:354 msgid "Account update" msgstr "Anovamientu de cuenta" #: ../lib/addressbook.cpp:983 msgid "Address book service" msgstr "" #: ../updater/ab-update.cpp:268 msgid "Contact sync upgrade complete." msgstr "" #: ../updater/ab-update.cpp:216 msgid "Contacts app is updated already!" msgstr "" #: ../updater/ab-update.cpp:224 msgid "Fail to authenticate!" msgstr "" #: ../updater/ab-update.cpp:220 msgid "Fail to connect to contact sync service!" msgstr "" #: ../updater/ab-update.cpp:218 msgid "Fail to connect to internet!" msgstr "" #: ../updater/ab-update.cpp:222 msgid "Fail to create sync profile!" msgstr "" #: ../updater/ab-update.cpp:233 msgid "Fail to sync contacts!" msgstr "" #: ../updater/ab-update.cpp:287 ../updater/ab-update.cpp:312 msgid "Fail to update" msgstr "" #: ../updater/ab-update.cpp:226 msgid "Internal error during the sync!" msgstr "" #: ../updater/ab-notify-message.cpp:93 msgid "No" msgstr "" #: ../updater/ab-update.cpp:228 msgid "Online account not found!" msgstr "" #: ../updater/ab-update.cpp:313 msgid "To retry later open Contacts app and press the sync button." msgstr "" #: ../updater/ab-update.cpp:230 msgid "Update already in progress!" msgstr "" #: ../lib/addressbook.cpp:1002 msgid "Update complete" msgstr "" #: ../lib/addressbook.cpp:995 msgid "Update required" msgstr "" #: ../updater/ab-notify-message.cpp:88 msgid "Yes" msgstr "" #: ../lib/addressbook.cpp:997 msgid "" "Your Contacts app needs to be upgraded. Only local contacts will be editable " "until upgrade is complete" msgstr "" #: ../lib/addressbook.cpp:1004 msgid "Your Contacts app upldate is complete." msgstr "" address-book-service-0.1.1+16.04.20151211.1/config.h.in0000644000015300001610000000213312632607666022251 0ustar pbuserpbgroup00000000000000#ifndef __GALERA_CONFIG_H__ #define __GALERA_CONFIG_H__ #define SETTINGS_APPLICATION "AddressBookService" #define SETTINGS_ORG "Canonical" #define SETTINGS_SAFE_MODE_KEY "safe-mode" #define SETTINGS_INVISIBLE_SOURCES "invisible-sources" #define ADDRESS_BOOK_SAFE_MODE "ADDRESS_BOOK_SAFE_MODE" #define ADDRESS_BOOK_SHOW_INVISIBLE_PROP "show-invisible" //updater #define SETTINGS_BUTEO_KEY "Buteo/migration_complete" #define QT_PLUGINS_BINARY_DIR "@CMAKE_BINARY_DIR@" #define TEST_DATA_DIR "@TEST_DATA_DIR@" #define EVOLUTION_ADDRESSBOOK_SERVICE_NAME "@EVOLUTION_ADDRESSBOOK_SERVICE_NAME@" #define EVOLUTION_API_3_17 @EVOLUTION_API_3_17@ #if EVOLUTION_API_3_17 #define E_BOOK_CLIENT_CONNECT_SYNC(SOURCE, CANCELLABLE, ERROR) \ e_book_client_connect_sync(SOURCE, -1, CANCELLABLE, ERROR) #else #define E_BOOK_CLIENT_CONNECT_SYNC(SOURCE, CANCELLABLE, ERROR) \ e_book_client_connect_sync(SOURCE, CANCELLABLE, ERROR) #endif #endif //__GALERA_CONFIG_H__ address-book-service-0.1.1+16.04.20151211.1/cmake_uninstall.cmake.in0000644000015300001610000000165412632607666025015 0ustar pbuserpbgroup00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") IF(EXISTS "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSE(EXISTS "$ENV{DESTDIR}${file}") MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") ENDIF(EXISTS "$ENV{DESTDIR}${file}") ENDFOREACH(file) address-book-service-0.1.1+16.04.20151211.1/COPYING0000644000015300001610000010451312632607666021266 0ustar pbuserpbgroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . address-book-service-0.1.1+16.04.20151211.1/eds-extension/0000755000015300001610000000000012632610222022772 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/eds-extension/e-source-ubuntu.c0000644000015300001610000002631512632607666026231 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2015 Canonical Ltd * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see . * * Authors: Renato Araujo Oliveira Filho * */ /** * SECTION: e-source-ubuntu * @short_description: #ESource extension for Ubuntu applications **/ #include "e-source-ubuntu.h" #include #include #include #include #define E_SOURCE_UBUNTU_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE_UBUNTU, ESourceUbuntuPrivate)) struct _ESourceUbuntuPrivate { GMutex property_lock; guint account_id; gchar *application_id; gboolean auto_remove; gchar *provider; AgAccount *account; }; enum { PROP_0, PROP_ACCOUNT_ID, PROP_APPLICATION_ID, PROP_AUTOREMOVE, PROP_ACCOUNT_PROVIDER }; G_DEFINE_TYPE ( ESourceUbuntu, e_source_ubuntu, E_TYPE_SOURCE_EXTENSION) static void source_ubuntu_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ACCOUNT_ID: e_source_ubuntu_set_account_id(E_SOURCE_UBUNTU (object), g_value_get_uint(value)); return; case PROP_APPLICATION_ID: e_source_ubuntu_set_application_id (E_SOURCE_UBUNTU (object), g_value_get_string (value)); return; case PROP_AUTOREMOVE: e_source_ubuntu_set_autoremove (E_SOURCE_UBUNTU (object), g_value_get_boolean (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_ubuntu_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ACCOUNT_ID: g_value_set_uint (value, e_source_ubuntu_get_account_id (E_SOURCE_UBUNTU (object))); return; case PROP_APPLICATION_ID: g_value_take_string (value, e_source_ubuntu_dup_application_id (E_SOURCE_UBUNTU (object))); return; case PROP_AUTOREMOVE: g_value_set_boolean (value, e_source_ubuntu_get_autoremove (E_SOURCE_UBUNTU (object))); return; case PROP_ACCOUNT_PROVIDER: g_value_take_string (value, e_source_ubuntu_dup_account_provider (E_SOURCE_UBUNTU (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_ubuntu_finalize (GObject *object) { ESourceUbuntuPrivate *priv; priv = E_SOURCE_UBUNTU_GET_PRIVATE (object); g_mutex_clear (&priv->property_lock); if (priv->account) { g_object_unref (priv->account); } g_free (priv->application_id); g_free (priv->provider); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_source_ubuntu_parent_class)->finalize (object); } static void e_source_ubuntu_class_init (ESourceUbuntuClass *klass) { GObjectClass *object_class; ESourceExtensionClass *extension_class; g_type_class_add_private (klass, sizeof (ESourceUbuntuPrivate)); object_class = G_OBJECT_CLASS (klass); object_class->set_property = source_ubuntu_set_property; object_class->get_property = source_ubuntu_get_property; object_class->finalize = source_ubuntu_finalize; extension_class = E_SOURCE_EXTENSION_CLASS (klass); extension_class->name = E_SOURCE_EXTENSION_UBUNTU; g_object_class_install_property ( object_class, PROP_ACCOUNT_ID, g_param_spec_uint ( "account-id", "Account ID", "Online Account ID", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_APPLICATION_ID, g_param_spec_string ( "application-id", "Application ID", "Ubuntu application ID", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_AUTOREMOVE, g_param_spec_boolean ( "auto-remove", "Autoremove source", "Autoremove source", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_ACCOUNT_PROVIDER, g_param_spec_string ( "account-provider", "Provider name", "Online Account Provider name", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void e_source_ubuntu_init (ESourceUbuntu *extension) { extension->priv = E_SOURCE_UBUNTU_GET_PRIVATE (extension); g_mutex_init (&extension->priv->property_lock); } /** * e_source_ubuntu_get_account_id: * @extension: an #ESourceUbuntu * * Returns the identifier uint of the Online Account associated * with the #ESource to which @extension belongs. * * Returns: the associated Online Account ID * **/ guint e_source_ubuntu_get_account_id (ESourceUbuntu *extension) { g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), 0); return extension->priv->account_id; } /** * e_source_ubuntu_set_account_id: * @extension: an #ESourceUbuntu * @account_id: (allow-none): the associated Online Account ID, or %NULL * * Sets the identifier guint of the Online Account associated * with the #ESource to which @extension belongs. * * The internal copy of @account_id is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. **/ void e_source_ubuntu_set_account_id (ESourceUbuntu *extension, guint account_id) { g_return_if_fail (E_IS_SOURCE_UBUNTU (extension)); g_mutex_lock (&extension->priv->property_lock); if (extension->priv->account_id == account_id) { g_mutex_unlock (&extension->priv->property_lock); return; } if (extension->priv->account) { g_object_unref (extension->priv->account); extension->priv->account = 0; } extension->priv->account_id = account_id; if (account_id != 0) { AgManager *manager = ag_manager_new (); extension->priv->account = ag_manager_get_account (manager, account_id); } g_mutex_unlock (&extension->priv->property_lock); g_object_notify (G_OBJECT (extension), "account-id"); } /** * e_source_ubuntu_get_application_id: * @extension: an #ESourceUbuntu * * Returns the id string of the application associated * with the #ESource to which @extension belongs. Can be %NULL or an empty * string for accounts not supporting this property. * * Returns: the associated application-id * **/ const gchar * e_source_ubuntu_get_application_id (ESourceUbuntu *extension) { g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), NULL); return extension->priv->application_id; } /** * e_source_ubuntu_dup_application_id: * @extension: an #ESourceUbuntu * * Thread-safe variation of e_source_ubuntu_get_application_id(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: a newly-allocated copy of #ESourceUbuntu:application-id * * Since: 3.8 **/ gchar * e_source_ubuntu_dup_application_id (ESourceUbuntu *extension) { const gchar *application_id; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), NULL); g_mutex_lock (&extension->priv->property_lock); application_id = e_source_ubuntu_get_application_id (extension); duplicate = g_strdup (application_id); g_mutex_unlock (&extension->priv->property_lock); return duplicate; } /** * e_source_ubuntu_set_application_id: * @extension: an #ESourceUbuntu * @application_id: (allow-none): the associated application ID, or %NULL * * Sets the id of the application associated * with the #ESource to which @extension belongs. * * The internal copy of @calendar_url is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * **/ void e_source_ubuntu_set_application_id (ESourceUbuntu *extension, const gchar *application_id) { g_return_if_fail (E_IS_SOURCE_UBUNTU (extension)); g_mutex_lock (&extension->priv->property_lock); if (g_strcmp0 (extension->priv->application_id, application_id) == 0) { g_mutex_unlock (&extension->priv->property_lock); return; } g_free (extension->priv->application_id); extension->priv->application_id = e_util_strdup_strip (application_id); g_mutex_unlock (&extension->priv->property_lock); g_object_notify (G_OBJECT (extension), "application-id"); } const gchar * e_source_ubuntu_get_account_provider (ESourceUbuntu *extension) { g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), NULL); if (extension->priv->account) { return ag_account_get_provider_name (extension->priv->account); } return NULL; } gchar * e_source_ubuntu_dup_account_provider (ESourceUbuntu *extension) { const gchar *provider; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), NULL); g_mutex_lock (&extension->priv->property_lock); provider = e_source_ubuntu_get_account_provider (extension); duplicate = g_strdup (provider); g_mutex_unlock (&extension->priv->property_lock); return duplicate; } gboolean e_source_ubuntu_get_autoremove(ESourceUbuntu *extension) { g_return_val_if_fail (E_IS_SOURCE_UBUNTU (extension), FALSE); return extension->priv->auto_remove; } void e_source_ubuntu_set_autoremove(ESourceUbuntu *extension, gboolean flag) { g_return_if_fail (E_IS_SOURCE_UBUNTU (extension)); g_mutex_lock (&extension->priv->property_lock); if (extension->priv->auto_remove == flag) { g_mutex_unlock (&extension->priv->property_lock); return; } extension->priv->auto_remove = flag; g_mutex_unlock (&extension->priv->property_lock); g_object_notify (G_OBJECT (extension), "auto-remove"); } address-book-service-0.1.1+16.04.20151211.1/eds-extension/module-ubuntu-sources.c0000644000015300001610000003242112632607673027446 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2015 Canonical Ltd * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see . * * Authors: Renato Araujo Oliveira Filho * */ #include "e-source-ubuntu.h" #include #include /* Standard GObject macros */ #define E_TYPE_UBUNTU_SOURCES \ (e_ubuntu_sources_get_type ()) #define E_UBUNTU_SOURCES(obj) \ (G_TYPE_CHECK_INSTANCE_CAST \ ((obj), E_TYPE_UBUNTU_SOURCES, EUbuntuSources)) typedef struct _EUbuntuSources EUbuntuSources; typedef struct _EUbuntuSourcesClass EUbuntuSourcesClass; struct _EUbuntuSources { EExtension parent; AgManager *ag_manager; /* AgAccountId -> ESource UID */ GHashTable *uoa_to_eds; }; struct _EUbuntuSourcesClass { EExtensionClass parent_class; }; /* Module Entry Points */ void e_module_load (GTypeModule *type_module); void e_module_unload (GTypeModule *type_module); /* Forward Declarations */ GType e_ubuntu_sources_get_type (void); G_DEFINE_TYPE ( EUbuntuSources, e_ubuntu_sources, E_TYPE_EXTENSION) static ESourceRegistryServer * ubuntu_sources_get_server (EUbuntuSources *extension) { EExtensible *extensible; extensible = e_extension_get_extensible (E_EXTENSION (extension)); return E_SOURCE_REGISTRY_SERVER (extensible); } static void ubuntu_sources_remove_collection (EUbuntuSources *extension, ESource *source) { GError *local_error = NULL; ESourceUbuntu *ubuntu_ext; g_debug("ubuntu_sources_remove_collection: %s", e_source_get_display_name(source)); ubuntu_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_UBUNTU); if (e_source_ubuntu_get_autoremove (ubuntu_ext)) { /* This removes the entire subtree rooted at source. * Deletes the corresponding on-disk key files too. */ e_source_remove_sync (source, NULL, &local_error); if (local_error != NULL) { g_warning ("%s: %s", G_STRFUNC, local_error->message); g_error_free (local_error); } } else { g_debug("Source not marked to auto-remove"); } } static void ubuntu_sources_config_source (EUbuntuSources *extension, ESource *source, AgAccount *ag_account) { ESourceExtension *source_extension; g_debug("CONFIGURE SOURCE: %s,%s", e_source_get_display_name(source), e_source_get_uid(source)); g_object_bind_property ( ag_account, "display-name", source, "display-name", G_BINDING_SYNC_CREATE); g_object_bind_property ( ag_account, "enabled", source, "enabled", G_BINDING_SYNC_CREATE); source_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_UBUNTU); g_object_bind_property ( ag_account, "id", source_extension, "account-id", G_BINDING_SYNC_CREATE); } static void ubuntu_sources_account_deleted_cb (AgManager *ag_manager, AgAccountId ag_account_id, EUbuntuSources *extension) { ESourceRegistryServer *server; GSList *eds_id_list; GSList *link; GQueue trash = G_QUEUE_INIT; server = ubuntu_sources_get_server (extension); eds_id_list = g_hash_table_lookup (extension->uoa_to_eds, GUINT_TO_POINTER (ag_account_id)); g_debug("Sources registered for account: %d", g_slist_length (eds_id_list)); for (link = eds_id_list; link != NULL; link = g_slist_next (link)) { const gchar *source_uid = link->data; ESource *source = e_source_registry_server_ref_source (server, source_uid); if (source != NULL) { g_debug ("Source selected to remove: %s", e_source_get_display_name (source)); g_queue_push_tail (&trash, source); } } // destroy source id list if (eds_id_list) { g_slist_free_full (eds_id_list, g_free); g_hash_table_remove (extension->uoa_to_eds, GUINT_TO_POINTER (ag_account_id)); } /* Empty the trash. */ while (!g_queue_is_empty (&trash)) { ESource *source = g_queue_pop_head (&trash); ubuntu_sources_remove_collection (extension, source); g_object_unref (source); } } static gboolean ubuntu_sources_register_source (EUbuntuSources *extension, ESource *source) { ESourceUbuntu *ubuntu_ext; AgAccountId ag_account_id; AgAccount *ag_account; g_debug("Register new source: %s/%s", e_source_get_display_name(source), e_source_get_uid(source)); if (!e_source_has_extension (source, E_SOURCE_EXTENSION_UBUNTU)) { return FALSE; } ubuntu_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_UBUNTU); ag_account_id = e_source_ubuntu_get_account_id (ubuntu_ext); ag_account = ag_manager_get_account (extension->ag_manager, ag_account_id); if (ag_account) { GSList *eds_id_list; GSList *match; const gchar *source_uid; eds_id_list = g_hash_table_lookup (extension->uoa_to_eds, GUINT_TO_POINTER (ag_account_id)); source_uid = e_source_get_uid (source); match = g_slist_find(eds_id_list, source_uid); if (match) { g_object_unref (ag_account); g_debug ("Source Already registered"); return FALSE; } eds_id_list = g_slist_append (eds_id_list, g_strdup (source_uid)); g_hash_table_insert (extension->uoa_to_eds, GUINT_TO_POINTER (ag_account_id), eds_id_list); ubuntu_sources_config_source (extension, source, ag_account); g_object_unref (ag_account); g_debug("Source %s, linked with account %d", source_uid, ag_account_id); return TRUE; } return FALSE; } static void ubuntu_sources_populate_accounts_table (EUbuntuSources *extension) { ESourceRegistryServer *server; GQueue trash = G_QUEUE_INIT; GList *list, *link; server = ubuntu_sources_get_server (extension); list = e_source_registry_server_list_sources (server, E_SOURCE_EXTENSION_UBUNTU); g_debug ("Found %d ubuntu accounts.", g_list_length(list)); for (link = list; link != NULL; link = g_list_next (link)) { ESource *source; source = E_SOURCE (link->data); /* If a matching AgAccountId was found, add it * to our accounts hash table. Otherwise remove * the ESource after we finish looping. */ if (!ubuntu_sources_register_source (extension, source)) { g_debug ("Account not found we will remove the source: %s", e_source_get_display_name (source)); g_queue_push_tail (&trash, source); } } /* Empty the trash. */ while (!g_queue_is_empty (&trash)) { ESource *source = g_queue_pop_head (&trash); ubuntu_sources_remove_collection (extension, source); } g_list_free_full (list, (GDestroyNotify) g_object_unref); g_debug("ubuntu_sources_populate_accounts_table:END"); } static void ubuntu_source_source_added_cb (ESourceRegistryServer *server, ESource *source, EUbuntuSources *extension) { ubuntu_sources_register_source(extension, source); } static void ubuntu_source_source_removed_cb (ESourceRegistryServer *server, ESource *source, EUbuntuSources *extension) { GHashTableIter iter; gpointer key, value; const gchar *source_uid = e_source_get_uid(source); g_debug("Source removed: %s", source_uid); GSList *source_link = 0; guint account_id = 0; g_hash_table_iter_init (&iter, extension->uoa_to_eds); while (g_hash_table_iter_next (&iter, &key, &value)) { GSList *sources = (GSList*) value; source_link = g_slist_find_custom (sources, source_uid, (GCompareFunc) g_strcmp0); if (source_link) { account_id = (gulong) key; break; } } if (account_id != 0) { GSList *eds_id_list = g_hash_table_lookup (extension->uoa_to_eds, GUINT_TO_POINTER (account_id)); eds_id_list = g_slist_remove_link (eds_id_list, source_link); g_free(source_link->data); g_hash_table_insert (extension->uoa_to_eds, GUINT_TO_POINTER (account_id), eds_id_list); g_debug ("Remove source :%s for account %lu", source_uid, (gulong) value); } } static void ubuntu_sources_bus_acquired_cb (EDBusServer *server, GDBusConnection *connection, EUbuntuSources *extension) { g_debug("loading ubuntu sources"); extension->ag_manager = ag_manager_new (); /* This populates a hash table of UOA ID -> ESource UID strings by * searching through available data sources for ones with a "Ubuntu * Source" extension. If such an extension is found, but * no corresponding AgAccount (presumably meaning the UOA account * was somehow deleted between E-D-S sessions) then the ESource in * which the extension was found gets deleted. */ ubuntu_sources_populate_accounts_table (extension); /* Listen for Online Account changes. */ g_signal_connect (extension->ag_manager, "account-deleted", G_CALLBACK (ubuntu_sources_account_deleted_cb), extension); ESourceRegistryServer *registry_server; registry_server = ubuntu_sources_get_server (extension); g_signal_connect (registry_server, "source_added", G_CALLBACK (ubuntu_source_source_added_cb), extension); g_signal_connect (registry_server, "source_removed", G_CALLBACK (ubuntu_source_source_removed_cb), extension); } static void ubuntu_sources_dispose (GObject *object) { EUbuntuSources *extension; extension = E_UBUNTU_SOURCES (object); if (extension->ag_manager != NULL) { g_signal_handlers_disconnect_matched ( extension->ag_manager, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); g_object_unref (extension->ag_manager); extension->ag_manager = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_ubuntu_sources_parent_class)->dispose (object); } static void ubuntu_sources_finalize (GObject *object) { EUbuntuSources *extension; extension = E_UBUNTU_SOURCES (object); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, extension->uoa_to_eds); while (g_hash_table_iter_next (&iter, &key, &value)) { g_slist_free_full ((GSList*) value, g_free); } g_hash_table_destroy (extension->uoa_to_eds); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_ubuntu_sources_parent_class)->finalize (object); } static void ubuntu_sources_constructed (GObject *object) { EExtension *extension; EExtensible *extensible; extension = E_EXTENSION (object); extensible = e_extension_get_extensible (extension); /* Wait for the registry service to acquire its well-known * bus name so we don't do anything destructive beforehand. * Run last so that all the sources get loaded first. */ g_signal_connect_after ( extensible, "bus-acquired", G_CALLBACK (ubuntu_sources_bus_acquired_cb), extension); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_ubuntu_sources_parent_class)->constructed (object); } static void e_ubuntu_sources_class_init (EUbuntuSourcesClass *klass) { GObjectClass *object_class; EExtensionClass *extension_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = ubuntu_sources_dispose; object_class->finalize = ubuntu_sources_finalize; object_class->constructed = ubuntu_sources_constructed; extension_class = E_EXTENSION_CLASS (klass); extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER; } static void e_ubuntu_sources_destroy_eds_id_slist (GSList *eds_id_list) { g_slist_free_full (eds_id_list, g_free); } static void e_ubuntu_sources_init (EUbuntuSources *extension) { extension->uoa_to_eds = g_hash_table_new_full ( (GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, (GDestroyNotify) NULL, (GDestroyNotify) NULL); } G_MODULE_EXPORT void e_module_load (GTypeModule *type_module) { g_type_ensure (E_TYPE_SOURCE_UBUNTU); g_type_ensure (E_TYPE_UBUNTU_SOURCES); } G_MODULE_EXPORT void e_module_unload (GTypeModule *type_module) { } address-book-service-0.1.1+16.04.20151211.1/eds-extension/e-source-ubuntu.h0000644000015300001610000000622512632607666026234 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2015 Canonical Ltd * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see . * * Authors: Renato Araujo Oliveira Filho * */ #ifndef E_SOURCE_UBUNTU_H #define E_SOURCE_UBUNTU_H #include /* Standard GObject macros */ #define E_TYPE_SOURCE_UBUNTU \ (e_source_ubuntu_get_type ()) #define E_SOURCE_UBUNTU(obj) \ (G_TYPE_CHECK_INSTANCE_CAST \ ((obj), E_TYPE_SOURCE_UBUNTU, ESourceUbuntu)) #define E_SOURCE_UBUNTU_CLASS(cls) \ (G_TYPE_CHECK_CLASS_CAST \ ((cls), E_TYPE_SOURCE_UBUNTU, ESourceUbuntuClass)) #define E_IS_SOURCE_UBUNTU(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE \ ((obj), E_TYPE_SOURCE_UBUNTU)) #define E_IS_SOURCE_UBUNTU_CLASS(cls) \ (G_TYPE_CHECK_CLASS_TYPE \ ((cls), E_TYPE_SOURCE_UBUNTU)) #define E_SOURCE_UBUNTU_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS \ ((obj), E_TYPE_SOURCE_UBUNTU, ESourceUbuntuClass)) /** * E_SOURCE_EXTENSION_UBUNTU: * * Pass this extension name to e_source_get_extension() to access * #ESourceUbuntu. This is also used as a group name in key files. * **/ #define E_SOURCE_EXTENSION_UBUNTU "Ubuntu" G_BEGIN_DECLS typedef struct _ESourceUbuntu ESourceUbuntu; typedef struct _ESourceUbuntuClass ESourceUbuntuClass; typedef struct _ESourceUbuntuPrivate ESourceUbuntuPrivate; /** * ESourceUbuntu: * * Contains only private data that should be read and manipulated using the * functions below. * **/ struct _ESourceUbuntu { ESourceExtension parent; ESourceUbuntuPrivate *priv; }; struct _ESourceUbuntuClass { ESourceExtensionClass parent_class; }; GType e_source_ubuntu_get_type (void) G_GNUC_CONST; guint e_source_ubuntu_get_account_id (ESourceUbuntu *extension); void e_source_ubuntu_set_account_id (ESourceUbuntu *extension, guint id); const gchar * e_source_ubuntu_get_application_id (ESourceUbuntu *extension); gchar * e_source_ubuntu_dup_application_id (ESourceUbuntu *extension); void e_source_ubuntu_set_application_id (ESourceUbuntu *extension, const gchar *application_id); gboolean e_source_ubuntu_get_autoremove (ESourceUbuntu *extension); void e_source_ubuntu_set_autoremove (ESourceUbuntu *extension, gboolean flag); const gchar * e_source_ubuntu_get_account_provider(ESourceUbuntu *extension); gchar * e_source_ubuntu_dup_account_provider(ESourceUbuntu *extension); G_END_DECLS #endif /* E_SOURCE_UBUNTU_H */ address-book-service-0.1.1+16.04.20151211.1/eds-extension/CMakeLists.txt0000644000015300001610000000246312632607666025561 0ustar pbuserpbgroup00000000000000project(ubuntu-sources) include_directories( ${CMAKE_BINARY_DIR} ${GLIB_INCLUDE_DIRS} ${GLIB_ACCOUNTS_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_EDS_INCLUDE_DIRS} ) execute_process( COMMAND pkg-config --variable=moduledir libebackend-1.2 OUTPUT_VARIABLE EDS_MODULES_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) # Ubuntu Source set(UBUNTU_SOURCE_LIB ubuntu-source-eds) set(UBUNTU_SOURCE_LIB_SRCS e-source-ubuntu.c e-source-ubuntu.h ) add_library(${UBUNTU_SOURCE_LIB} SHARED ${UBUNTU_SOURCE_LIB_SRCS} ) target_link_libraries(${UBUNTU_SOURCE_LIB} ${GLIB_LIBRARIES} ${GLIB_ACCOUNTS_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_EDS_LIBRARIES} ) install(TARGETS ${UBUNTU_SOURCE_LIB} LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} ) # Module Ubuntu Sources set(MODULE_UBUNTU_SOURCES module-ubuntu-sources) set(MODULE_UBUNTU_SOURCES_SRCS module-ubuntu-sources.c ) add_library(${MODULE_UBUNTU_SOURCES} MODULE ${MODULE_UBUNTU_SOURCES_SRCS} ) set_target_properties(${MODULE_UBUNTU_SOURCES} PROPERTIES PREFIX "" ) target_link_libraries(${MODULE_UBUNTU_SOURCES} ${GLIB_LIBRARIES} ${GLIB_ACCOUNTS_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_EDS_LIBRARIES} ${UBUNTU_SOURCE_LIB} ) install(TARGETS ${MODULE_UBUNTU_SOURCES} LIBRARY DESTINATION ${EDS_MODULES_DIR}/ ) address-book-service-0.1.1+16.04.20151211.1/lib/0000755000015300001610000000000012632610222020753 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/lib/addressbook-adaptor.cpp0000644000015300001610000001516512632607673025437 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "addressbook-adaptor.h" #include "addressbook.h" #include "view.h" namespace galera { AddressBookAdaptor::AddressBookAdaptor(const QDBusConnection &connection, AddressBook *parent) : QDBusAbstractAdaptor(parent), m_addressBook(parent), m_connection(connection) { setAutoRelaySignals(true); connect(m_addressBook, SIGNAL(readyChanged()), SIGNAL(readyChanged())); connect(m_addressBook, SIGNAL(safeModeChanged()), SIGNAL(safeModeChanged())); connect(m_addressBook, SIGNAL(sourcesChanged()), SIGNAL(sourcesChanged())); } AddressBookAdaptor::~AddressBookAdaptor() { // destructor } SourceList AddressBookAdaptor::availableSources(const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "availableSources", Qt::QueuedConnection, Q_ARG(const QDBusMessage&, message)); return SourceList(); } Source AddressBookAdaptor::source(const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "source", Qt::QueuedConnection, Q_ARG(const QDBusMessage&, message)); return Source(); } Source AddressBookAdaptor::createSource(const QString &sourceName, bool setAsPrimary, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "createSource", Qt::QueuedConnection, Q_ARG(const QString&, sourceName), Q_ARG(uint, 0), Q_ARG(bool, setAsPrimary), Q_ARG(const QDBusMessage&, message)); return Source(); } Source AddressBookAdaptor::createSourceForAccount(const QString &sourceName, uint accountId, bool setAsPrimary, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "createSource", Qt::QueuedConnection, Q_ARG(const QString&, sourceName), Q_ARG(uint, accountId), Q_ARG(bool, setAsPrimary), Q_ARG(const QDBusMessage&, message)); return Source(); } SourceList AddressBookAdaptor::updateSources(const SourceList &sources, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "updateSources", Qt::QueuedConnection, Q_ARG(const SourceList&, sources), Q_ARG(const QDBusMessage&, message)); return SourceList(); } bool AddressBookAdaptor::removeSource(const QString &sourceId, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "removeSource", Qt::QueuedConnection, Q_ARG(const QString&, sourceId), Q_ARG(const QDBusMessage&, message)); return false; } QString AddressBookAdaptor::createContact(const QString &contact, const QString &source, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "createContact", Qt::QueuedConnection, Q_ARG(const QString&, contact), Q_ARG(const QString&, source), Q_ARG(const QDBusMessage&, message)); return QString(); } QDBusObjectPath AddressBookAdaptor::query(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources) { View *v = m_addressBook->query(clause, sort, maxCount, showInvisible, sources); v->registerObject(m_connection); return QDBusObjectPath(v->dynamicObjectPath()); } int AddressBookAdaptor::removeContacts(const QStringList &contactIds, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "removeContacts", Qt::QueuedConnection, Q_ARG(const QStringList&, contactIds), Q_ARG(const QDBusMessage&, message)); return 0; } QStringList AddressBookAdaptor::sortFields() { return m_addressBook->sortFields(); } QString AddressBookAdaptor::linkContacts(const QStringList &contactsIds) { return m_addressBook->linkContacts(contactsIds); } bool AddressBookAdaptor::unlinkContacts(const QString &parentId, const QStringList &contactsIds) { return m_addressBook->unlinkContacts(parentId, contactsIds); } QStringList AddressBookAdaptor::updateContacts(const QStringList &contacts, const QDBusMessage &message) { message.setDelayedReply(true); QMetaObject::invokeMethod(m_addressBook, "updateContacts", Qt::QueuedConnection, Q_ARG(const QStringList&, contacts), Q_ARG(const QDBusMessage&, message)); return QStringList(); } bool AddressBookAdaptor::isReady() { return m_addressBook->isReady(); } bool AddressBookAdaptor::ping() { return true; } void AddressBookAdaptor::purgeContacts(const QString &since, const QString &sourceId, const QDBusMessage &message) { QDateTime sinceDate; if (since.isEmpty()) { sinceDate = QDateTime::fromTime_t(0); } else { sinceDate = QDateTime::fromString(since, Qt::ISODate); } m_addressBook->purgeContacts(sinceDate, sourceId, message); } void AddressBookAdaptor::shutDown() const { m_addressBook->shutdown(); } bool AddressBookAdaptor::safeMode() const { return m_addressBook->isSafeMode(); } void AddressBookAdaptor::setSafeMode(bool flag) { m_addressBook->setSafeMode(flag); } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/gee-utils.h0000644000015300001610000000734512632607666023055 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_GEE_UTILS_H__ #define __GALERA_GEE_UTILS_H__ #include #include #include #include static guint _folks_abstract_field_details_hash_data_func (gconstpointer v, gpointer self) { const FolksAbstractFieldDetails *constDetails = static_cast(v); return folks_abstract_field_details_hash_static (const_cast(constDetails)); } static int _folks_abstract_field_details_equal_data_func (gconstpointer a, gconstpointer b, gpointer self) { const FolksAbstractFieldDetails *constDetailsA = static_cast(a); const FolksAbstractFieldDetails *constDetailsB = static_cast(b); return folks_abstract_field_details_equal_static (const_cast(constDetailsA), const_cast(constDetailsB)); } #define SET_AFD_NEW() \ GEE_SET(gee_hash_set_new(FOLKS_TYPE_ABSTRACT_FIELD_DETAILS, \ (GBoxedCopyFunc) g_object_ref, g_object_unref, \ _folks_abstract_field_details_hash_data_func, \ NULL, \ NULL, \ _folks_abstract_field_details_equal_data_func, \ NULL, \ NULL)) #define SET_PERSONA_NEW() \ GEE_SET(gee_hash_set_new(FOLKS_TYPE_PERSONA, \ (GBoxedCopyFunc) g_object_ref, g_object_unref, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL)) #define GEE_MULTI_MAP_AFD_NEW(FOLKS_TYPE) \ GEE_MULTI_MAP(gee_hash_multi_map_new(G_TYPE_STRING,\ (GBoxedCopyFunc) g_strdup, g_free, \ FOLKS_TYPE, \ (GBoxedCopyFunc)g_object_ref, g_object_unref, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ _folks_abstract_field_details_hash_data_func, \ NULL, \ NULL, \ _folks_abstract_field_details_equal_data_func, \ NULL, \ NULL)) class GeeUtils { public: static GValue* gValueSliceNew(GType type); static void gValueSliceFree(GValue *value); static void personaDetailsInsert(GHashTable *details, FolksPersonaDetail key, gpointer value); static GValue* asvSetStrNew(QMultiMap providerUidMap); }; // class #endif address-book-service-0.1.1+16.04.20151211.1/lib/contacts-map.h0000644000015300001610000000534012632607666023541 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_CONTACTS_MAP_PRIV_H__ #define __GALERA_CONTACTS_MAP_PRIV_H__ #include "common/sort-clause.h" #include #include #include #include #include #include #include namespace galera { class QIndividual; class ContactEntry { public: ContactEntry(QIndividual *individual); ~ContactEntry(); QIndividual *individual() const; private: ContactEntry(); ContactEntry(const ContactEntry &other); QIndividual *m_individual; }; class ContactsMap { public: ContactsMap(); ~ContactsMap(); ContactEntry *valueFromVCard(const QString &vcard) const; bool contains(FolksIndividual *individual) const; bool contains(const QString &id) const; ContactEntry *value(FolksIndividual *individual) const; ContactEntry *value(const QString &id) const; QList valueByPhone(const QString &phone) const; QList values(const QStringList &ids) const; ContactEntry *take(FolksIndividual *individual); ContactEntry *take(const QString &id); void remove(const QString &id); void insert(ContactEntry *entry); void updatePosition(ContactEntry *entry); int size() const; void clear(); void lockForRead(); void unlock(); QList values() const; QList contacts() const; QStringList keys() const; void sertSort(const SortClause &clause); SortClause sort() const; static SortClause defaultSort(); private: QHash m_idToEntry; QMultiMap m_phoneToEntry; // sorted contacts QList m_contacts; SortClause m_sortClause; QReadWriteLock m_mutex; void removeData(ContactEntry *entry, bool del); void insertData(ContactEntry *entry); void insertData(const QList &numbers, ContactEntry *entry); QString minimalNumber(const QString &phone) const; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/qindividual.h0000644000015300001610000002354412632607666023467 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QINDIVIDUAL_H__ #define __GALERA_QINDIVIDUAL_H__ #include #include #include #include #include #include #include #include #include namespace galera { typedef GHashTable* (*ParseDetailsFunc)(GHashTable*, const QList &); class UpdateContactRequest; class QIndividual { public: QIndividual(FolksIndividual *individual, FolksIndividualAggregator *aggregator); ~QIndividual(); QString id() const; QtContacts::QContact &contact(); QtContacts::QContact copy(QList fields); bool update(const QString &vcard, QObject *object, const char *slot); bool update(const QtContacts::QContact &contact, QObject *object, const char *slot); void setIndividual(FolksIndividual *individual); FolksIndividual *individual() const; QList personas() const; void addListener(QObject *object, const char *slot); bool isValid() const; void flush(); bool markAsDeleted(); QDateTime deletedAt(); bool setVisible(bool visible); bool isVisible() const; static QtContacts::QContact copy(const QtContacts::QContact &c, QList fields); static GHashTable *parseDetails(const QtContacts::QContact &contact); static QString displayName(const QtContacts::QContact &contact); static void setExtendedDetails(FolksPersona *persona, const QList &xDetails, const QDateTime &createdAt = QDateTime()); // enable or disable auto-link static void enableAutoLink(bool flag); static bool autoLinkEnabled(); private: FolksIndividual *m_individual; FolksIndividualAggregator *m_aggregator; QtContacts::QContact *m_contact; UpdateContactRequest *m_currentUpdate; QList > m_listeners; QMap m_personas; QList m_notifyConnections; QString m_id; QMetaObject::Connection m_updateConnection; QMutex m_contactLock; QDateTime m_deletedAt; bool m_visible; static bool m_autoLink; static QStringList m_supportedExtendedDetails; QIndividual(); QIndividual(const QIndividual &); void notifyUpdate(); QMultiHash parseDetails(FolksAbstractFieldDetails *details) const; void markAsDirty(); void updateContact(QtContacts::QContact *contact) const; void updatePersonas(); void clearPersonas(); void clear(); FolksPersona *primaryPersona(); QtContacts::QContactDetail detailFromUri(QtContacts::QContactDetail::DetailType type, const QString &uri) const; void appendDetailsForPersona(QtContacts::QContact *contact, const QtContacts::QContactDetail &detail, bool readOnly) const; void appendDetailsForPersona(QtContacts::QContact *contact, QList details, const QString &preferredAction, const QtContacts::QContactDetail &preferred, bool readOnly) const; // QContact QtContacts::QContactDetail getUid() const; QList getSyncTargets() const; QtContacts::QContactDetail getTimeStamp (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaName (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaFullName (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaNickName (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaBirthday (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaPhoto (FolksPersona *persona, int index) const; QtContacts::QContactDetail getPersonaFavorite (FolksPersona *persona, int index) const; QList getPersonaExtendedDetails (FolksPersona *persona, int index) const; QList getPersonaRoles (FolksPersona *persona, QtContacts::QContactDetail *preferredRole, int index) const; QList getPersonaEmails (FolksPersona *persona, QtContacts::QContactDetail *preferredEmail, int index) const; QList getPersonaPhones (FolksPersona *persona, QtContacts::QContactDetail *preferredPhone, int index) const; QList getPersonaAddresses(FolksPersona *persona, QtContacts::QContactDetail *preferredAddress, int index) const; QList getPersonaIms (FolksPersona *persona, QtContacts::QContactDetail *preferredIm, int index) const; QList getPersonaUrls (FolksPersona *persona, QtContacts::QContactDetail *preferredUrl, int index) const; static void avatarCacheStoreDone(GObject *source, GAsyncResult *result, gpointer data); // create void createPersonaFromDetails(QList detail, ParseDetailsFunc parseFunc, void *data) const; static void createPersonaForDetailDone(GObject *detail, GAsyncResult *result, gpointer userdata); // translate details static GHashTable *parseFullNameDetails (GHashTable *details, const QList &cDetails, const QString &fallback); static GHashTable *parseNicknameDetails (GHashTable *details, const QList &cDetails); static GHashTable *parseNameDetails (GHashTable *details, const QList &cDetails); static GHashTable *parseGenderDetails (GHashTable *details, const QList &cDetails); static GHashTable *parseFavoriteDetails (GHashTable *details, const QList &cDetails); static GHashTable *parsePhotoDetails (GHashTable *details, const QList &cDetails); static GHashTable *parseBirthdayDetails (GHashTable *details, const QList &cDetails); static GHashTable *parseAddressDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parsePhoneNumbersDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parseOrganizationDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parseImDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parseNoteDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parseEmailDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); static GHashTable *parseUrlDetails (GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail); // property changed static void folksIndividualChanged (FolksIndividual *individual, GParamSpec *pspec, QIndividual *self); static QString qStringFromGChar (const gchar *str); }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/contacts-map.cpp0000644000015300001610000002041312632607666024072 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "contact-less-than.h" #include "contacts-map.h" #include "qindividual.h" #include #include #include #include #include #include #include using namespace QtContacts; namespace galera { //ContactInfo ContactEntry::ContactEntry(QIndividual *individual) : m_individual(individual) { Q_ASSERT(individual); } ContactEntry::~ContactEntry() { delete m_individual; } QIndividual *ContactEntry::individual() const { return m_individual; } //ContactMap ContactsMap::ContactsMap() : m_sortClause(defaultSort()) { } ContactsMap::~ContactsMap() { clear(); } ContactEntry *ContactsMap::value(const QString &id) const { return m_idToEntry.value(id, 0); } QList ContactsMap::valueByPhone(const QString &phone) const { if (phone.isEmpty()) { return values(); } return m_phoneToEntry.values(minimalNumber(phone)); } QList ContactsMap::values(const QStringList &ids) const { QList result; Q_FOREACH(const QString &id, ids) { ContactEntry *entry = m_idToEntry.value(id, 0); if (entry) { result << entry; } } return result; } ContactEntry *ContactsMap::take(FolksIndividual *individual) { QString contactId = QString::fromUtf8(folks_individual_get_id(individual)); return take(contactId); } ContactEntry *ContactsMap::take(const QString &id) { QWriteLocker locker(&m_mutex); ContactEntry *entry = m_idToEntry.take(id); removeData(entry, false); return entry; } void ContactsMap::remove(const QString &id) { QWriteLocker locker(&m_mutex); ContactEntry *entry = m_idToEntry.take(id); removeData(entry, true); } void ContactsMap::insert(ContactEntry *entry) { QWriteLocker locker(&m_mutex); insertData(entry); } void ContactsMap::updatePosition(ContactEntry *entry) { QWriteLocker locker(&m_mutex); if (!m_sortClause.isEmpty()) { int oldPos = m_contacts.indexOf(entry); ContactEntryLessThan lessThan(m_sortClause); QList::iterator it(std::upper_bound(m_contacts.begin(), m_contacts.end(), entry, lessThan)); if (it != m_contacts.end()) { int newPos = std::distance(m_contacts.begin(), it); if (oldPos != newPos) { m_contacts.move(oldPos, newPos); } } else if (oldPos != (m_contacts.size() - 1)) { m_contacts.move(oldPos, m_contacts.size() -1); } } // update phone number map Q_FOREACH(const QString &key, m_phoneToEntry.keys(entry)) { m_phoneToEntry.remove(key, entry); } insertData(entry->individual()->contact().details(), entry); } int ContactsMap::size() const { return m_idToEntry.size(); } void ContactsMap::clear() { QWriteLocker locker(&m_mutex); QList entries = m_idToEntry.values(); m_idToEntry.clear(); m_phoneToEntry.clear(); m_contacts.clear(); qDeleteAll(entries); } void ContactsMap::lockForRead() { m_mutex.lockForRead(); } void ContactsMap::unlock() { m_mutex.unlock(); } QList ContactsMap::values() const { return m_contacts; } QList ContactsMap::contacts() const { QList result; Q_FOREACH(ContactEntry *e, m_contacts) { result << e->individual()->contact(); } return result; } QStringList ContactsMap::keys() const { return m_idToEntry.keys(); } void ContactsMap::sertSort(const SortClause &clause) { if (clause.toContactSortOrder() != m_sortClause.toContactSortOrder()) { m_sortClause = clause; if (!m_sortClause.isEmpty()) { ContactEntryLessThan lessThan(m_sortClause); qSort(m_contacts.begin(), m_contacts.end(), lessThan); } } } SortClause ContactsMap::sort() const { return m_sortClause; } SortClause ContactsMap::defaultSort() { static SortClause clause(""); if (clause.isEmpty()) { // create a default sort, this sort is used by the most commom case QList cClauseList; QContactSortOrder cClauseTag; cClauseTag.setCaseSensitivity(Qt::CaseInsensitive); cClauseTag.setDetailType(QContactDetail::TypeTag, QContactTag::FieldTag); cClauseTag.setBlankPolicy(QContactSortOrder::BlanksLast); cClauseTag.setDirection(Qt::AscendingOrder); cClauseList << cClauseTag; QContactSortOrder cClauseName; cClauseName.setCaseSensitivity(Qt::CaseInsensitive); cClauseName.setDetailType(QContactDetail::TypeDisplayLabel, QContactDisplayLabel::FieldLabel); cClauseName.setBlankPolicy(QContactSortOrder::BlanksLast); cClauseName.setDirection(Qt::AscendingOrder); cClauseList << cClauseName; clause = SortClause(cClauseList); } return clause; } ContactEntry *ContactsMap::valueFromVCard(const QString &vcard) const { //GET UID int startIndex = vcard.indexOf("UID:"); if (startIndex) { startIndex += 4; // "UID:" int endIndex = vcard.indexOf("\r\n", startIndex); QString id = vcard.mid(startIndex, endIndex - startIndex); return m_idToEntry[id]; } return 0; } bool ContactsMap::contains(FolksIndividual *individual) const { QString contactId = QString::fromUtf8(folks_individual_get_id(individual)); return contains(contactId); } bool ContactsMap::contains(const QString &id) const { return m_idToEntry.contains(id); } ContactEntry *ContactsMap::value(FolksIndividual *individual) const { QString contactId = QString::fromUtf8(folks_individual_get_id(individual)); return m_idToEntry.value(contactId, 0); } void ContactsMap::removeData(ContactEntry *entry, bool del) { if (entry) { Q_FOREACH(const QString &key, m_phoneToEntry.keys(entry)) { m_phoneToEntry.remove(key, entry); } m_contacts.removeOne(entry); if (del) { delete entry; } } } void ContactsMap::insertData(ContactEntry *entry) { FolksIndividual *fIndividual = entry->individual()->individual(); if (fIndividual) { // fill id map m_idToEntry.insert(folks_individual_get_id(fIndividual), entry); // fill contact list if (!m_sortClause.isEmpty()) { ContactEntryLessThan lessThan(m_sortClause); QList::iterator it(std::upper_bound(m_contacts.begin(), m_contacts.end(), entry, lessThan)); m_contacts.insert(it, entry); } else { m_contacts.append(entry); } // fill phone map insertData(entry->individual()->contact().details(), entry); } } void ContactsMap::insertData(const QList &numbers, ContactEntry *entry) { Q_FOREACH(const QContactPhoneNumber &phone, numbers) { QString mNumber = minimalNumber(phone.number()); if (!mNumber.isEmpty()) { m_phoneToEntry.insert(mNumber, entry); } } } QString ContactsMap::minimalNumber(const QString &phone) const { static i18n::phonenumbers::PhoneNumberUtil *phonenumberUtil = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); std::string stdPreprocessedPhone(phone.toStdString()); phonenumberUtil->NormalizeDiallableCharsOnly(&stdPreprocessedPhone); if (stdPreprocessedPhone.length() <= 7) { return QString::fromStdString(stdPreprocessedPhone); } return QString::fromStdString(stdPreprocessedPhone).right(7); } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/view.h0000644000015300001610000000437512632607666022131 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_VIEW_H__ #define __GALERA_VIEW_H__ #include #include #include #include #include #include namespace galera { class ContactEntry; class ViewAdaptor; class ContactsMap; class FilterThread; class SortContact; class View : public QObject { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) public: View(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources, ContactsMap *allContacts, QObject *parent); ~View(); static QString objectPath(); QString dynamicObjectPath() const; QObject *adaptor() const; bool registerObject(QDBusConnection &connection); void unregisterObject(QDBusConnection &connection); // contacts bool appendContact(ContactEntry *entry); bool removeContact(ContactEntry *entry); // Adaptor QString contactDetails(const QStringList &fields, const QString &id); int count(); void sort(const QString &field); void close(); bool isOpen() const; public Q_SLOTS: QStringList contactsDetails(const QStringList &fields, int startIndex, int pageSize, const QDBusMessage &message); void onFilterDone(); private Q_SLOTS: void onVCardParsed(const QStringList &vcards); Q_SIGNALS: void closed(); void countChanged(int count=0); private: QStringList m_sources; FilterThread *m_filterThread; ViewAdaptor *m_adaptor; QEventLoop *m_waiting; void waitFilter(); }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/contacts-utils.cpp0000644000015300001610000000512712632607666024462 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "contacts-utils.h" #include #include #include using namespace QtVersit; namespace galera { QVersitProperty ContactsUtils::createProperty(int sourceIndex, int index, const QString &name, const QString &value) { QVersitProperty prop; QMultiHash param; prop.setName(name); prop.setValue(value); param.insert("PID", QString("%1.%1").arg(sourceIndex).arg(index)); prop.setParameters(param); return prop; } QList ContactsUtils::parsePersona(int index, FolksPersona *persona) { QList result; QVersitProperty prop; prop.setName("CLIENTPIDMAP"); prop.setValue(QString("%1;%2").arg(index) .arg(folks_persona_get_uid(persona))); result << prop; prop.clear(); result << createProperty(index, 1, "N", folks_persona_(persona)); return result; } QByteArray ContactsUtils::serializeIndividual(FolksIndividual *individual) { QVersitProperty prop; QVersitDocument vcard(QVersitDocument::VCard30Type); //UID prop.setName("UID"); prop.setValue(folks_individual_get_id(individual)); vcard.addProperty(prop); GeeIterator *iter; GeeSet *personas = folks_individual_get_personas(individual); int index = 1; iter = gee_iterable_iterator(GEE_ITERABLE(personas)); QList personaProps; while(gee_iterator_next(iter)) { personaProps = parsePersona(index++, FOLKS_PERSONA(gee_iterator_get(iter))); Q_FOREACH(QVersitProperty p, personaProps) { vcard.addProperty(p); } } QByteArray result; QVersitWriter writer(&result); writer.startWriting(vcard); writer.waitForFinished(); return result; } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/contacts-utils.h0000644000015300001610000000253212632607666024124 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_CONTACTS_UTILS_H__ #define __GALERA_CONTACTS_UTILS_H__ #include #include #include namespace galera { class ContactsUtils { public: static QByteArray serializeIndividual(FolksIndividual *individual); private: static QList parsePersona(int index, FolksPersona *persona); static QtVersit::QVersitProperty createProperty(int sourceIndex, int index, const QString &name, const QString &value); }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/view.cpp0000644000015300001610000002617612632607666022467 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "view.h" #include "view-adaptor.h" #include "contacts-map.h" #include "contact-less-than.h" #include "qindividual.h" #include "common/vcard-parser.h" #include "common/filter.h" #include "common/fetch-hint.h" #include "common/dbus-service-defs.h" #include #include #include #include using namespace QtContacts; using namespace QtVersit; namespace galera { class FilterThread: public QRunnable { public: FilterThread(QString filter, QString sort, int maxCount, bool showInvisible, ContactsMap *allContacts, QObject *parent) : m_parent(parent), m_filter(filter), m_sortClause(sort), m_maxCount(maxCount), m_allContacts(allContacts), m_showInvisible(showInvisible), m_canceled(false), m_running(false), m_done(false) { setAutoDelete(false); } QList result() const { if (isRunning()) { return QList(); } else { return m_contacts; } } bool appendContact(const QContact &contact, const QDateTime &deteletedAt) { if (checkContact(contact, deteletedAt)) { addSorted(&m_contacts, contact, m_sortClause); return true; } return false; } bool removeContact(const QContact &contact) { return m_contacts.removeAll(contact); } void chageSort(SortClause clause) { m_sortClause = clause; if (!clause.isEmpty()) { ContactLessThan lessThan(m_sortClause); qSort(m_contacts.begin(), m_contacts.end(), lessThan); } } void addSorted(QList *sorted, const QContact &toAdd, const SortClause& sortOrder) { if (!sortOrder.isEmpty()) { ContactLessThan lessThan(sortOrder); QList::iterator it(std::upper_bound(sorted->begin(), sorted->end(), toAdd, lessThan)); sorted->insert(it, toAdd); } else { // no sort order just add it to the end sorted->append(toAdd); } } void cancel() { m_canceledLock.lockForWrite(); m_canceled = true; m_canceledLock.unlock(); } bool isRunning() const { return m_running; } bool done() const { return m_done; } protected: void notifyFinished() { m_running = false; m_done = true; QMetaObject::invokeMethod(m_parent, "onFilterDone", Qt::QueuedConnection); } void run() { if (m_canceled || !m_allContacts) { notifyFinished(); return; } m_allContacts->lockForRead(); // only sort contacts if the contacts was stored in a different order into the contacts map bool needSort = (!m_sortClause.isEmpty() && (m_sortClause.toContactSortOrder() != m_allContacts->sort().toContactSortOrder())); // filter contacts if necessary if (m_filter.isValid() && m_filter.isEmpty()) { Q_FOREACH(ContactEntry *entry, m_allContacts->values()) { if ((m_showInvisible || entry->individual()->isVisible()) && !entry->individual()->deletedAt().isValid()) { QContact contact = entry->individual()->contact(); if (needSort) { addSorted(&m_contacts, contact, m_sortClause); } else { m_contacts.append(contact); } if ((m_maxCount > 0) && (m_maxCount >= m_contacts.size())) { break; } } } } else if (m_filter.isValid()) { // optmization QList preFilter; // check if is a query by id QStringList idsToFilter = m_filter.idsToFilter(); if (!idsToFilter.isEmpty()) { preFilter = m_allContacts->values(idsToFilter); } else { // check if is a phone number query QString phoneToFilter = m_filter.phoneNumberToFilter(); if (!phoneToFilter.isEmpty()) { preFilter = m_allContacts->valueByPhone(phoneToFilter); } else { qDebug() << "Filter not optimized" << m_filter.toContactFilter(); preFilter = m_allContacts->values(); } } Q_FOREACH(ContactEntry *entry, preFilter) { m_canceledLock.lockForRead(); if (m_canceled) { m_canceledLock.unlock(); m_allContacts->unlock(); notifyFinished(); return; } QContact contact = entry->individual()->contact(); QDateTime deletedAt = entry->individual()->deletedAt(); m_canceledLock.unlock(); if ((m_showInvisible || entry->individual()->isVisible()) && checkContact(contact, deletedAt)) { if (needSort) { addSorted(&m_contacts, contact, m_sortClause); } else { m_contacts.append(contact); } if ((m_maxCount > 0) && (m_contacts.size() >= m_maxCount)) { break; } } } } else { // invalid filter m_contacts.clear(); } m_allContacts->unlock(); notifyFinished(); } private: QObject *m_parent; Filter m_filter; SortClause m_sortClause; ContactsMap *m_allContacts; QList m_contacts; int m_maxCount; bool m_showInvisible; bool m_canceled; QReadWriteLock m_canceledLock; bool m_running; bool m_done; bool checkContact(const QContact &contact, const QDateTime &deletedAt) { return m_filter.test(contact, deletedAt); } }; View::View(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources, ContactsMap *allContacts, QObject *parent) : QObject(parent), m_sources(sources), m_filterThread(new FilterThread(clause, sort, maxCount, showInvisible, allContacts, this)), m_adaptor(0), m_waiting(0) { if (allContacts) { QThreadPool::globalInstance()->start(m_filterThread); } } View::~View() { close(); } void View::close() { if (m_adaptor) { Q_EMIT m_adaptor->contactsRemoved(0, m_filterThread->result().count()); Q_EMIT closed(); QDBusConnection conn = QDBusConnection::sessionBus(); unregisterObject(conn); m_adaptor->destroy(); m_adaptor = 0; } if (m_filterThread) { if (!m_filterThread->done()) { m_filterThread->cancel(); waitFilter(); } delete m_filterThread; m_filterThread = 0; } } bool View::isOpen() const { return (m_adaptor != 0); } QString View::contactDetails(const QStringList &fields, const QString &id) { Q_ASSERT(FALSE); return QString(); } QStringList View::contactsDetails(const QStringList &fields, int startIndex, int pageSize, const QDBusMessage &message) { if (!m_filterThread || !isOpen()) { return QStringList(); } waitFilter(); const QList &contacts = m_filterThread->result(); if (startIndex < 0) { startIndex = 0; } if ((pageSize < 0) || ((startIndex + pageSize) >= contacts.count())) { pageSize = contacts.count() - startIndex; } QList pageOfContacts; for(int i = startIndex, iMax = (startIndex + pageSize); i < iMax; i++) { pageOfContacts << QIndividual::copy(contacts.at(i), FetchHint::parseFieldNames(fields)); } VCardParser *parser = new VCardParser(this); parser->setProperty("DATA", QVariant::fromValue(message)); connect(parser, &VCardParser::vcardParsed, this, &View::onVCardParsed); parser->contactToVcard(pageOfContacts); return QStringList(); } void View::onVCardParsed(const QStringList &vcards) { QObject *sender = QObject::sender(); QDBusMessage reply = sender->property("DATA").value().createReply(vcards); QDBusConnection::sessionBus().send(reply); sender->deleteLater(); } void View::onFilterDone() { if (m_waiting) { m_waiting->quit(); m_waiting = 0; } } void View::waitFilter() { if (m_filterThread && !m_filterThread->done()) { QEventLoop loop; m_waiting = &loop; loop.exec(); } } int View::count() { if (!isOpen()) { return 0; } waitFilter(); return m_filterThread->result().count(); } void View::sort(const QString &field) { if (!isOpen()) { return; } m_filterThread->chageSort(SortClause(field)); } QString View::objectPath() { return CPIM_ADDRESSBOOK_VIEW_OBJECT_PATH; } QString View::dynamicObjectPath() const { return objectPath() + "/" + QString::number((long)this); } bool View::registerObject(QDBusConnection &connection) { if (!m_adaptor) { m_adaptor = new ViewAdaptor(connection, this); if (!connection.registerObject(dynamicObjectPath(), this)) { qWarning() << "Could not register object!" << objectPath(); delete m_adaptor; m_adaptor = 0; } else { connect(this, SIGNAL(countChanged(int)), m_adaptor, SIGNAL(countChanged(int))); } } return (m_adaptor != 0); } void View::unregisterObject(QDBusConnection &connection) { if (m_adaptor) { connection.unregisterObject(dynamicObjectPath()); } } bool View::appendContact(ContactEntry *entry) { if (!isOpen()) { return false; } if (m_filterThread->appendContact(entry->individual()->contact(), entry->individual()->deletedAt())) { Q_EMIT countChanged(m_filterThread->result().count()); return true; } return false; } bool View::removeContact(ContactEntry *entry) { if (!isOpen()) { return false; } if (m_filterThread->removeContact(entry->individual()->contact())) { Q_EMIT countChanged(m_filterThread->result().count()); return true; } return false; } QObject *View::adaptor() const { return m_adaptor; } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/gee-utils.cpp0000644000015300001610000000363512632607666023406 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gee-utils.h" #include GValue* GeeUtils::gValueSliceNew(GType type) { GValue *retval = g_slice_new0(GValue); g_value_init(retval, type); return retval; } void GeeUtils::gValueSliceFree(GValue *value) { g_value_unset(value); g_slice_free(GValue, value); } void GeeUtils::personaDetailsInsert(GHashTable *details, FolksPersonaDetail key, gpointer value) { g_hash_table_insert(details, (gpointer) folks_persona_store_detail_key(key), value); } GValue* GeeUtils::asvSetStrNew(QMultiMap providerUidMap) { GeeMultiMap *hashSet = GEE_MULTI_MAP_AFD_NEW(FOLKS_TYPE_IM_FIELD_DETAILS); GValue *retval = gValueSliceNew (G_TYPE_OBJECT); g_value_take_object (retval, hashSet); QList keys = providerUidMap.keys(); Q_FOREACH(const QString& key, keys) { QList values = providerUidMap.values(key); Q_FOREACH(const QString& value, values) { FolksImFieldDetails *imfd; imfd = folks_im_field_details_new (value.toUtf8().data(), NULL); gee_multi_map_set(hashSet, key.toUtf8().data(), imfd); g_object_unref(imfd); } } return retval; } address-book-service-0.1.1+16.04.20151211.1/lib/dirtycontact-notify.h0000644000015300001610000000354512632607666025172 0ustar pbuserpbgroup00000000000000/* * Copyright 2014 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_DIRTYCONTACT_NOTIFY_H__ #define __GALERA_DIRTYCONTACT_NOTIFY_H__ #include #include #include #include namespace galera { class AddressBookAdaptor; // this is a helper class uses a timer with a small timeout to notify the client about // any contact change notification. This class should be used instead of emit the signal directly // this will avoid notify about the contact update several times when updating different fields simultaneously // With that we can reduce the dbus traffic and skip some client calls to query about the new contact info. class DirtyContactsNotify : public QObject { Q_OBJECT public: DirtyContactsNotify(AddressBookAdaptor *adaptor, QObject *parent=0); void insertChangedContacts(QSet ids); void insertRemovedContacts(QSet ids); void insertAddedContacts(QSet ids); void flush(); void clear(); private Q_SLOTS: void emitSignals(); private: AddressBookAdaptor *m_adaptor; QTimer m_timer; QSet m_contactsChanged; QSet m_contactsAdded; QSet m_contactsRemoved; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/view-adaptor.h0000644000015300001610000000620412632607666023552 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_VIEW_ADAPTOR_H__ #define __GALERA_VIEW_ADAPTOR_H__ #include #include #include #include #include "common/dbus-service-defs.h" namespace galera { class View; class ViewAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", CPIM_ADDRESSBOOK_VIEW_IFACE_NAME) Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") Q_PROPERTY(int count READ count NOTIFY countChanged) public: ViewAdaptor(const QDBusConnection &connection, View *parent); virtual ~ViewAdaptor(); void destroy(); public Q_SLOTS: QString contactDetails(const QStringList &fields, const QString &id); QStringList contactsDetails(const QStringList &fields, int startIndex, int pageSize, const QDBusMessage &message); int count(); void sort(const QString &field); void close(); Q_SIGNALS: void contactsAdded(int pos, int lenght); void contactsRemoved(int pos, int lenght); void contactsUpdated(int pos, int lenght); void countChanged(int count); private: View *m_view; QDBusConnection m_connection; }; } // namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/view-adaptor.cpp0000644000015300001610000000355112632607666024107 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "view-adaptor.h" #include "view.h" namespace galera { ViewAdaptor::ViewAdaptor(const QDBusConnection &connection, View *parent) : QDBusAbstractAdaptor(parent), m_view(parent), m_connection(connection) { setAutoRelaySignals(true); } ViewAdaptor::~ViewAdaptor() { m_view = 0; } void ViewAdaptor::destroy() { m_view = 0; this->deleteLater(); } QString ViewAdaptor::contactDetails(const QStringList &fields, const QString &id) { if (m_view) { return m_view->contactDetails(fields, id); } else { return QString(); } } QStringList ViewAdaptor::contactsDetails(const QStringList &fields, int startIndex, int pageSize, const QDBusMessage &message) { if (m_view) { message.setDelayedReply(true); m_view->contactsDetails(fields, startIndex, pageSize, message); } return QStringList(); } int ViewAdaptor::count() { if (m_view) { return m_view->count(); } else { return 0; } } void ViewAdaptor::sort(const QString &field) { if (m_view) { return m_view->sort(field); } } void ViewAdaptor::close() { if (m_view) { return m_view->close(); } } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/qindividual.cpp0000644000015300001610000020372512632607666024023 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qindividual.h" #include "detail-context-parser.h" #include "gee-utils.h" #include "update-contact-request.h" #include "e-source-ubuntu.h" #include "common/vcard-parser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" using namespace QtVersit; using namespace QtContacts; #define X_CREATED_AT "X-CREATED-AT" #define X_REMOTE_ID "X-REMOTE-ID" #define X_GOOGLE_ETAG "X-GOOGLE-ETAG" #define X_GROUP_ID "X-GROUP-ID" #define X_DELETED_AT "X-DELETED-AT" #define X_AVATAR_REV "X-AVATAR-REV" namespace { static void gValueGeeSetAddStringFieldDetails(GValue *value, GType g_type, const char* v_string, const QtContacts::QContactDetail &detail, bool ispreferred) { GeeCollection *collection = (GeeCollection*) g_value_get_object(value); if(collection == NULL) { collection = GEE_COLLECTION(SET_AFD_NEW()); g_value_take_object(value, collection); } FolksAbstractFieldDetails *fieldDetails = NULL; if(FALSE) { } else if(g_type == FOLKS_TYPE_EMAIL_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_email_field_details_new(v_string, NULL)); } else if(g_type == FOLKS_TYPE_IM_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_im_field_details_new(v_string, NULL)); } else if(g_type == FOLKS_TYPE_NOTE_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_note_field_details_new(v_string, NULL, NULL)); } else if(g_type == FOLKS_TYPE_PHONE_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_phone_field_details_new(v_string, NULL)); } else if(g_type == FOLKS_TYPE_URL_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_url_field_details_new(v_string, NULL)); } else if(g_type == FOLKS_TYPE_WEB_SERVICE_FIELD_DETAILS) { fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS ( folks_web_service_field_details_new(v_string, NULL)); } if (fieldDetails == NULL) { qWarning() << "Invalid fieldDetails type" << g_type; } else { galera::DetailContextParser::parseContext(fieldDetails, detail, ispreferred); gee_collection_add(collection, fieldDetails); g_object_unref(fieldDetails); } } #define PERSONA_DETAILS_INSERT_STRING_FIELD_DETAILS(\ details, cDetails, key, value, q_type, g_type, member, prefDetail) \ { \ if(cDetails.size() > 0) { \ value = GeeUtils::gValueSliceNew(G_TYPE_OBJECT); \ Q_FOREACH(const q_type& detail, cDetails) { \ if(!detail.isEmpty()) { \ gValueGeeSetAddStringFieldDetails(value, (g_type), \ detail.member().toUtf8().data(), \ detail, \ detail == prefDetail); \ } \ } \ GeeUtils::personaDetailsInsert((details), (key), (value)); \ } \ } static QString unaccent(const QString &value) { QString s2 = value.normalized(QString::NormalizationForm_D); QString out; for (int i=0, j=s2.length(); i listener = m_listeners[i]; listener.second.invoke(listener.first, Q_ARG(QIndividual*, this)); } } QIndividual::~QIndividual() { if (m_currentUpdate) { m_currentUpdate->disconnect(m_updateConnection); // this will leave the update object to destroy itself // this is necessary because the individual can be destroyed during a update // Eg. If the individual get linked m_currentUpdate->deatach(); m_currentUpdate = 0; } clear(); } QString QIndividual::id() const { return m_id; } QtContacts::QContactDetail QIndividual::getUid() const { QContactGuid uid; const char* id = folks_individual_get_id(m_individual); Q_ASSERT(id); uid.setGuid(QString::fromUtf8(id)); return uid; } QList QIndividual::getSyncTargets() const { QList details; Q_FOREACH(const QString id, m_personas.keys()) { QContactSyncTarget target; FolksPersona *p = m_personas[id]; FolksPersonaStore *ps = folks_persona_get_store(p); QString displayName = folks_persona_store_get_display_name(ps); QString storeId = QString::fromUtf8(folks_persona_store_get_id(ps)); QString accountId("0"); if (EDSF_IS_PERSONA_STORE(ps)) { ESource *source = edsf_persona_store_get_source(EDSF_PERSONA_STORE(ps)); if (e_source_has_extension(source, E_SOURCE_EXTENSION_UBUNTU)) { ESourceUbuntu *ubuntu_ex = E_SOURCE_UBUNTU(e_source_get_extension(source, E_SOURCE_EXTENSION_UBUNTU)); if (ubuntu_ex) { accountId = QString::number(e_source_ubuntu_get_account_id(ubuntu_ex)); } } } target.setDetailUri(QString(id).replace(":",".")); target.setSyncTarget(displayName); target.setValue(QContactSyncTarget::FieldSyncTarget + 1, storeId); target.setValue(QContactSyncTarget::FieldSyncTarget + 2, accountId); details << target; } return details; } void QIndividual::appendDetailsForPersona(QtContacts::QContact *contact, const QtContacts::QContactDetail &detail, bool readOnly) const { if (!detail.isEmpty()) { QtContacts::QContactDetail cpy(detail); QtContacts::QContactDetail::AccessConstraints access; if (readOnly || detail.accessConstraints().testFlag(QContactDetail::ReadOnly)) { access |= QContactDetail::ReadOnly; } if (detail.accessConstraints().testFlag(QContactDetail::Irremovable)) { access |= QContactDetail::Irremovable; } QContactManagerEngine::setDetailAccessConstraints(&cpy, access); contact->appendDetail(cpy); } } void QIndividual::appendDetailsForPersona(QtContacts::QContact *contact, QList details, const QString &preferredAction, const QtContacts::QContactDetail &preferred, bool readOnly) const { Q_FOREACH(const QContactDetail &detail, details) { appendDetailsForPersona(contact, detail, readOnly); if (!preferred.isEmpty()) { contact->setPreferredDetail(preferredAction, preferred); } } } QContactDetail QIndividual::getTimeStamp(FolksPersona *persona, int index) const { if (!EDSF_IS_PERSONA(persona)) { return QContactDetail(); } QContactTimestamp timestamp; EContact *c = edsf_persona_get_contact(EDSF_PERSONA(persona)); const gchar *rev = static_cast(e_contact_get_const(c, E_CONTACT_REV)); if (rev) { QString time = QString::fromUtf8(rev); QDateTime rev = QDateTime::fromString(time, Qt::ISODate); // time is saved on UTC FORMAT rev.setTimeSpec(Qt::UTC); timestamp.setLastModified(rev); } EVCardAttribute *attr = e_vcard_get_attribute(E_VCARD(c), X_CREATED_AT); QDateTime createdAtDate; if (attr) { GString *createdAt = e_vcard_attribute_get_value_decoded(attr); createdAtDate = QDateTime::fromString(createdAt->str, Qt::ISODate); createdAtDate.setTimeSpec(Qt::UTC); g_string_free(createdAt, TRUE); } else { // use last modified data as created date if it does not exists on contact createdAtDate = timestamp.lastModified(); } timestamp.setCreated(createdAtDate); return timestamp; } QContactDetail QIndividual::getPersonaName(FolksPersona *persona, int index) const { if (!FOLKS_IS_NAME_DETAILS(persona)) { return QContactDetail(); } QContactName detail; FolksStructuredName *sn = folks_name_details_get_structured_name(FOLKS_NAME_DETAILS(persona)); if (sn) { const char *name = folks_structured_name_get_given_name(sn); if (name && strlen(name)) { detail.setFirstName(qStringFromGChar(name)); } name = folks_structured_name_get_additional_names(sn); if (name && strlen(name)) { detail.setMiddleName(qStringFromGChar(name)); } name = folks_structured_name_get_family_name(sn); if (name && strlen(name)) { detail.setLastName(qStringFromGChar(name)); } name = folks_structured_name_get_prefixes(sn); if (name && strlen(name)) { detail.setPrefix(qStringFromGChar(name)); } name = folks_structured_name_get_suffixes(sn); if (name && strlen(name)) { detail.setSuffix(qStringFromGChar(name)); } detail.setDetailUri(QString("%1.1").arg(index)); } return detail; } QtContacts::QContactDetail QIndividual::getPersonaFullName(FolksPersona *persona, int index) const { if (!FOLKS_IS_NAME_DETAILS(persona)) { return QContactDetail(); } QContactDisplayLabel detail; const gchar *fullName = folks_name_details_get_full_name(FOLKS_NAME_DETAILS(persona)); if (fullName) { detail.setLabel(qStringFromGChar(fullName)); detail.setDetailUri(QString("%1.1").arg(index)); } return detail; } QtContacts::QContactDetail QIndividual::getPersonaNickName(FolksPersona *persona, int index) const { if (!FOLKS_IS_NAME_DETAILS(persona)) { return QContactDetail(); } QContactNickname detail; const gchar* nickname = folks_name_details_get_nickname(FOLKS_NAME_DETAILS(persona)); if (nickname && strlen(nickname)) { detail.setNickname(qStringFromGChar(nickname)); detail.setDetailUri(QString("%1.1").arg(index)); } return detail; } QtContacts::QContactDetail QIndividual::getPersonaBirthday(FolksPersona *persona, int index) const { if (!FOLKS_IS_BIRTHDAY_DETAILS(persona)) { return QContactDetail(); } QContactBirthday detail; GDateTime* datetime = folks_birthday_details_get_birthday(FOLKS_BIRTHDAY_DETAILS(persona)); if (datetime) { QDate date(g_date_time_get_year(datetime), g_date_time_get_month(datetime), g_date_time_get_day_of_month(datetime)); QTime time(g_date_time_get_hour(datetime), g_date_time_get_minute(datetime), g_date_time_get_second(datetime)); detail.setDateTime(QDateTime(date, time)); detail.setDetailUri(QString("%1.1").arg(index)); } return detail; } void QIndividual::folksIndividualChanged(FolksIndividual *individual, GParamSpec *pspec, QIndividual *self) { Q_UNUSED(individual); Q_UNUSED(pspec); // skip update contact during a contact update, the update will be done after if (self->m_contactLock.tryLock()) { // invalidate contact self->markAsDirty(); self->notifyUpdate(); self->m_contactLock.unlock(); } } QString QIndividual::qStringFromGChar(const gchar *str) { return QString::fromUtf8(str).remove(QRegExp("[\r\n]")); } QtContacts::QContactDetail QIndividual::getPersonaPhoto(FolksPersona *persona, int index) const { QContactAvatar avatar; if (!FOLKS_IS_AVATAR_DETAILS(persona)) { return avatar; } GLoadableIcon *avatarIcon = folks_avatar_details_get_avatar(FOLKS_AVATAR_DETAILS(persona)); if (avatarIcon) { QString url; if (G_IS_FILE_ICON(avatarIcon)) { GFile *avatarFile = g_file_icon_get_file(G_FILE_ICON(avatarIcon)); gchar *uri = g_file_get_uri(avatarFile); if (uri) { url = QString::fromUtf8(uri); g_free(uri); } } else { FolksAvatarCache *cache = folks_avatar_cache_dup(); const char *contactId = folks_individual_get_id(m_individual); gchar *uri = folks_avatar_cache_build_uri_for_avatar(cache, contactId); url = QString::fromUtf8(uri); if (!QFile::exists(url)) { folks_avatar_cache_store_avatar(cache, contactId, avatarIcon, QIndividual::avatarCacheStoreDone, strdup(uri)); } g_free(uri); g_object_unref(cache); } // Avoid to set a empty url if (url.isEmpty()) { return avatar; } avatar.setImageUrl(QUrl(url)); avatar.setDetailUri(QString("%1.1").arg(index)); } return avatar; } void QIndividual::avatarCacheStoreDone(GObject *source, GAsyncResult *result, gpointer data) { GError *error = 0; gchar *uri = folks_avatar_cache_store_avatar_finish(FOLKS_AVATAR_CACHE(source), result, &error); if (error) { qWarning() << "Fail to store avatar" << error->message; g_error_free(error); } if (!g_str_equal(data, uri)) { qWarning() << "Avatar name changed from" << (gchar*)data << "to" << uri; } g_free(data); } QList QIndividual::getPersonaRoles(FolksPersona *persona, QtContacts::QContactDetail *preferredRole, int index) const { if (!FOLKS_IS_ROLE_DETAILS(persona)) { return QList(); } QList details; GeeSet *roles = folks_role_details_get_roles(FOLKS_ROLE_DETAILS(persona)); if (!roles) { return details; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(roles)); int fieldIndex = 1; while(gee_iterator_next(iter)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iter)); FolksRole *role = FOLKS_ROLE(folks_abstract_field_details_get_value(fd)); QContactOrganization org; QString field; field = qStringFromGChar(folks_role_get_organisation_name(role)); if (!field.isEmpty()) { org.setName(field); } field = qStringFromGChar(folks_role_get_title(role)); if (!field.isEmpty()) { org.setTitle(field); } field = qStringFromGChar(folks_role_get_role(role)); if (!field.isEmpty()) { org.setRole(field); } bool isPref = false; DetailContextParser::parseParameters(org, fd, &isPref); org.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredRole = org; } g_object_unref(fd); details << org; } g_object_unref(iter); return details; } QList QIndividual::getPersonaEmails(FolksPersona *persona, QtContacts::QContactDetail *preferredEmail, int index) const { if (!FOLKS_IS_EMAIL_DETAILS(persona)) { return QList(); } QList details; GeeSet *emails = folks_email_details_get_email_addresses(FOLKS_EMAIL_DETAILS(persona)); if (!emails) { return details; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(emails)); int fieldIndex = 1; while(gee_iterator_next(iter)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iter)); const gchar *email = (const gchar*) folks_abstract_field_details_get_value(fd); QContactEmailAddress addr; addr.setEmailAddress(qStringFromGChar(email)); bool isPref = false; DetailContextParser::parseParameters(addr, fd, &isPref); addr.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredEmail = addr; } g_object_unref(fd); details << addr; } g_object_unref(iter); return details; } QList QIndividual::getPersonaPhones(FolksPersona *persona, QtContacts::QContactDetail *preferredPhone, int index) const { if (!FOLKS_IS_PHONE_DETAILS(persona)) { return QList(); } QList details; GeeSet *phones = folks_phone_details_get_phone_numbers(FOLKS_PHONE_DETAILS(persona)); if (!phones) { return details; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(phones)); int fieldIndex = 1; while(gee_iterator_next(iter)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iter)); const gchar *phone = (const char*) folks_abstract_field_details_get_value(fd); QContactPhoneNumber number; number.setNumber(qStringFromGChar(phone)); bool isPref = false; DetailContextParser::parseParameters(number, fd, &isPref); number.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredPhone = number; } g_object_unref(fd); details << number; } g_object_unref(iter); return details; } QList QIndividual::getPersonaAddresses(FolksPersona *persona, QtContacts::QContactDetail *preferredAddress, int index) const { if (!FOLKS_IS_POSTAL_ADDRESS_DETAILS(persona)) { return QList(); } QList details; GeeSet *addresses = folks_postal_address_details_get_postal_addresses(FOLKS_POSTAL_ADDRESS_DETAILS(persona)); if (!addresses) { return details; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(addresses)); int fieldIndex = 1; while(gee_iterator_next(iter)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iter)); FolksPostalAddress *addr = FOLKS_POSTAL_ADDRESS(folks_abstract_field_details_get_value(fd)); QContactAddress address; const char *field = folks_postal_address_get_country(addr); if (field && strlen(field)) { address.setCountry(qStringFromGChar(field)); } field = folks_postal_address_get_locality(addr); if (field && strlen(field)) { address.setLocality(qStringFromGChar(field)); } field = folks_postal_address_get_po_box(addr); if (field && strlen(field)) { address.setPostOfficeBox(qStringFromGChar(field)); } field = folks_postal_address_get_postal_code(addr); if (field && strlen(field)) { address.setPostcode(qStringFromGChar(field)); } field = folks_postal_address_get_region(addr); if (field && strlen(field)) { address.setRegion(qStringFromGChar(field)); } field = folks_postal_address_get_street(addr); if (field && strlen(field)) { address.setStreet(qStringFromGChar(field)); } bool isPref = false; DetailContextParser::parseParameters(address, fd, &isPref); address.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredAddress = address; } g_object_unref(fd); details << address; } g_object_unref(iter); return details; } QList QIndividual::getPersonaIms(FolksPersona *persona, QtContacts::QContactDetail *preferredIm, int index) const { if (!FOLKS_IS_IM_DETAILS(persona)) { return QList(); } QList details; GeeMultiMap *ims = folks_im_details_get_im_addresses(FOLKS_IM_DETAILS(persona)); if (!ims) { return details; } GeeSet *keys = gee_multi_map_get_keys(ims); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(keys)); int fieldIndex = 1; while(gee_iterator_next(iter)) { const gchar *key = (const gchar*) gee_iterator_get(iter); GeeCollection *values = gee_multi_map_get(ims, key); GeeIterator *iterValues = gee_iterable_iterator(GEE_ITERABLE(values)); while(gee_iterator_next(iterValues)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iterValues)); const char *uri = (const char*) folks_abstract_field_details_get_value(fd); GeeCollection *parameters = folks_abstract_field_details_get_parameter_values(fd, "X-FOLKS-FIELD"); if (parameters) { continue; } QContactOnlineAccount account; account.setAccountUri(qStringFromGChar(uri)); int protocolId = DetailContextParser::accountProtocolFromString(qStringFromGChar(key)); account.setProtocol(static_cast(protocolId)); bool isPref = false; DetailContextParser::parseParameters(account, fd, &isPref); account.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredIm = account; } g_object_unref(fd); details << account; } g_object_unref(iterValues); } g_object_unref(iter); return details; } QList QIndividual::getPersonaUrls(FolksPersona *persona, QtContacts::QContactDetail *preferredUrl, int index) const { if (!FOLKS_IS_URL_DETAILS(persona)) { return QList(); } QList details; GeeSet *urls = folks_url_details_get_urls(FOLKS_URL_DETAILS(persona)); if (!urls) { return details; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(urls)); int fieldIndex = 1; while(gee_iterator_next(iter)) { FolksAbstractFieldDetails *fd = FOLKS_ABSTRACT_FIELD_DETAILS(gee_iterator_get(iter)); const char *url = (const char*) folks_abstract_field_details_get_value(fd); QContactUrl detail; detail.setUrl(qStringFromGChar(url)); bool isPref = false; DetailContextParser::parseParameters(detail, fd, &isPref); detail.setDetailUri(QString("%1.%2").arg(index).arg(fieldIndex++)); if (isPref) { *preferredUrl = detail; } g_object_unref(fd); details << detail; } g_object_unref(iter); return details; } QtContacts::QContactDetail QIndividual::getPersonaFavorite(FolksPersona *persona, int index) const { if (!FOLKS_IS_FAVOURITE_DETAILS(persona)) { return QtContacts::QContactDetail(); } QContactFavorite detail; detail.setFavorite(folks_favourite_details_get_is_favourite(FOLKS_FAVOURITE_DETAILS(persona))); detail.setDetailUri(QString("%1.%2").arg(index).arg(1)); return detail; } QList QIndividual::getPersonaExtendedDetails(FolksPersona *persona, int index) const { QList result; if (!EDSF_IS_PERSONA(persona)) { return result; } EContact *c = edsf_persona_get_contact(EDSF_PERSONA(persona)); Q_FOREACH(const QString &xDetName, m_supportedExtendedDetails) { EVCardAttribute *attr = e_vcard_get_attribute(E_VCARD(c), xDetName.toUtf8().constData()); if (attr) { GString *attrValue = e_vcard_attribute_get_value_decoded(attr); QContactExtendedDetail xDet; xDet.setName(xDetName); xDet.setData(QString::fromUtf8(attrValue->str)); xDet.setDetailUri(QString("%1.1").arg(index)); g_string_free(attrValue, true); result << xDet; } } return result; } QtContacts::QContact QIndividual::copy(QList fields) { return copy(contact(), fields); } QtContacts::QContact QIndividual::copy(const QContact &c, QList fields) { QList details; QContact result; result = c; if (!fields.isEmpty()) { // this will remove the contact details but will keep the other metadata like preferred fields result = c; result.clearDetails(); // mandatory details << c.detail(); Q_FOREACH(QContactDetail det, c.details()) { details << det; } // sync targets Q_FOREACH(QContactDetail det, c.details()) { details << det; } if (fields.contains(QContactDetail::TypeName)) { details << c.detail(); } if (fields.contains(QContactDetail::TypeDisplayLabel)) { details << c.detail(); } if (fields.contains(QContactDetail::TypeNickname)) { details << c.detail(); } if (fields.contains(QContactDetail::TypeBirthday)) { details << c.detail(); } if (fields.contains(QContactDetail::TypeAvatar)) { details << c.detail(); } if (fields.contains(QContactDetail::TypeOrganization)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypeEmailAddress)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypePhoneNumber)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypeAddress)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypeUrl)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypeTag)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } if (fields.contains(QContactDetail::TypeFavorite)) { Q_FOREACH(QContactDetail det, c.details()) { details << det; } } Q_FOREACH(QContactDetail det, details) { result.appendDetail(det); } } return result; } QtContacts::QContact &QIndividual::contact() { if (!m_contact && m_individual) { QMutexLocker locker(&m_contactLock); updatePersonas(); // avoid change on m_contact pointer until the contact is fully loaded QContact contact; updateContact(&contact); m_contact = new QContact(contact); } return *m_contact; } void QIndividual::updatePersonas() { Q_FOREACH(FolksPersona *p, m_personas.values()) { g_object_unref(p); } GeeSet *personas = folks_individual_get_personas(m_individual); if (!personas) { Q_ASSERT(false); return; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas)); while(gee_iterator_next(iter)) { FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter)); m_personas.insert(qStringFromGChar(folks_persona_get_iid(persona)), persona); } g_object_unref(iter); } void QIndividual::updateContact(QContact *contact) const { if (!m_individual) { return; } contact->appendDetail(getUid()); Q_FOREACH(QContactDetail detail, getSyncTargets()) { contact->appendDetail(detail); } int personaIndex = 1; Q_FOREACH(FolksPersona *persona, m_personas.values()) { Q_ASSERT(FOLKS_IS_PERSONA(persona)); int wsize = 0; gchar **wproperties = folks_persona_get_writeable_properties(persona, &wsize); //"gender", "local-ids", "avatar", "postal-addresses", "urls", "phone-numbers", "structured-name", //"anti-links", "im-addresses", "is-favourite", "birthday", "notes", "roles", "email-addresses", //"web-service-addresses", "groups", "full-name" QStringList wPropList; for(int i=0; i < wsize; i++) { wPropList << wproperties[i]; } // vcard only support one of these details by contact if (personaIndex == 1) { appendDetailsForPersona(contact, getTimeStamp(persona, personaIndex), true); appendDetailsForPersona(contact, getPersonaName(persona, personaIndex), !wPropList.contains("structured-name")); appendDetailsForPersona(contact, getPersonaFullName(persona, personaIndex), !wPropList.contains("full-name")); appendDetailsForPersona(contact, getPersonaNickName(persona, personaIndex), !wPropList.contains("structured-name")); appendDetailsForPersona(contact, getPersonaBirthday(persona, personaIndex), !wPropList.contains("birthday")); appendDetailsForPersona(contact, getPersonaPhoto(persona, personaIndex), !wPropList.contains("avatar")); appendDetailsForPersona(contact, getPersonaFavorite(persona, personaIndex), !wPropList.contains("is-favourite")); } QList details; QContactDetail prefDetail; details = getPersonaRoles(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactOrganization::Type], prefDetail, !wPropList.contains("roles")); details = getPersonaEmails(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactEmailAddress::Type], prefDetail, !wPropList.contains("email-addresses")); details = getPersonaPhones(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactPhoneNumber::Type], prefDetail, !wPropList.contains("phone-numbers")); details = getPersonaAddresses(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactAddress::Type], prefDetail, !wPropList.contains("postal-addresses")); details = getPersonaIms(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactOnlineAccount::Type], prefDetail, !wPropList.contains("im-addresses")); details = getPersonaUrls(persona, &prefDetail, personaIndex); appendDetailsForPersona(contact, details, VCardParser::PreferredActionNames[QContactUrl::Type], prefDetail, !wPropList.contains("urls")); details = getPersonaExtendedDetails (persona, personaIndex); appendDetailsForPersona(contact, details, QString(), QContactDetail(), false); personaIndex++; } // Display label is mandatory QContactDisplayLabel dLabel = contact->detail(); if (dLabel.label().isEmpty()) { dLabel.setLabel(displayName(*contact)); contact->saveDetail(&dLabel); } QString label = dLabel.label(); // WORKAROUND: add a extra tag to help on alphabetic list // On the Ubuntu Address Book, contacts which the name starts with // number or symbol should be moved to bottom of the list. Since the standard // string sort put symbols and numbers on the top, we use the tag to sort, // and keep empty tags for the especial case. QContactTag tag = contact->detail(); label = label.toUpper(); if (label.isEmpty() || !label.at(0).isLetter()) { tag.setTag(""); } else { tag.setTag(label); } contact->saveDetail(&tag); QContactExtendedDetail normalizedLabel; normalizedLabel.setName("X-NORMALIZED_FN"); normalizedLabel.setData(unaccent(dLabel.label())); contact->saveDetail(&normalizedLabel); } bool QIndividual::update(const QtContacts::QContact &newContact, QObject *object, const char *slot) { QContact &originalContact = contact(); if (newContact != originalContact) { m_currentUpdate = new UpdateContactRequest(newContact, this, object, slot); if (!m_contactLock.tryLock(5000)) { qWarning() << "Fail to lock contact to update"; m_currentUpdate->notifyError("Fail to update contact"); m_currentUpdate->deleteLater(); m_currentUpdate = 0; return false; } m_updateConnection = QObject::connect(m_currentUpdate, &UpdateContactRequest::done, [this] (const QString &errorMessage) { if (errorMessage.isEmpty()) { markAsDirty(); } m_currentUpdate->deleteLater(); m_currentUpdate = 0; m_contactLock.unlock(); }); m_currentUpdate->start(); return true; } else { qDebug() << "Contact is equal"; return false; } } bool QIndividual::update(const QString &vcard, QObject *object, const char *slot) { QContact contact = VCardParser::vcardToContact(vcard); return update(contact, object, slot); } FolksIndividual *QIndividual::individual() const { return m_individual; } QList QIndividual::personas() const { return m_personas.values(); } void QIndividual::clearPersonas() { Q_FOREACH(FolksPersona *p, m_personas.values()) { g_object_unref(p); } m_personas.clear(); } void QIndividual::clear() { clearPersonas(); if (m_individual) { // disconnect any previous handler Q_FOREACH(int handlerId, m_notifyConnections) { g_signal_handler_disconnect(m_individual, handlerId); } m_notifyConnections.clear(); g_object_unref(m_individual); m_individual = 0; } if (m_contact) { delete m_contact; m_contact = 0; } } void QIndividual::addListener(QObject *object, const char *slot) { int slotIndex = object->metaObject()->indexOfSlot(++slot); if (slotIndex == -1) { qWarning() << "Invalid slot:" << slot << "for object" << object; } else { m_listeners << qMakePair(object, object->metaObject()->method(slotIndex)); } } bool QIndividual::isValid() const { return (m_individual != 0); } void QIndividual::flush() { // flush the folks persona store folks_persona_store_flush(folks_individual_aggregator_get_primary_store(m_aggregator), 0, 0); // cause the contact info to be reload markAsDirty(); } bool QIndividual::markAsDeleted() { QString currentDate = QDateTime::currentDateTime().toString(Qt::ISODate); GeeSet *personas = folks_individual_get_personas(m_individual); if (!personas) { return false; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas)); while(gee_iterator_next(iter)) { FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter)); if (EDSF_IS_PERSONA(persona)) { FolksPersonaStore *store = folks_persona_get_store(persona); if (!EDSF_IS_PERSONA_STORE(store)) { continue; } GError *error = NULL; ESource *source = edsf_persona_store_get_source(EDSF_PERSONA_STORE(store)); EClient *client = E_BOOK_CLIENT_CONNECT_SYNC(source, NULL, &error); if (error) { qWarning() << "Fail to connect with EDS" << error->message; g_error_free(error); continue; } EContact *c = edsf_persona_get_contact(EDSF_PERSONA(persona)); EVCardAttribute *attr = e_vcard_get_attribute(E_VCARD(c), X_DELETED_AT); if (!attr) { attr = e_vcard_attribute_new("", X_DELETED_AT); e_vcard_add_attribute_with_value(E_VCARD(c), attr, currentDate.toUtf8().constData()); } else { e_vcard_attribute_add_value(attr, currentDate.toUtf8().constData()); } e_book_client_modify_contact_sync(E_BOOK_CLIENT(client), c, NULL, &error); if (error) { qWarning() << "Fail to update EDS contact:" << error->message; g_error_free(error); } else { m_deletedAt = QDateTime::currentDateTime(); notifyUpdate(); } g_object_unref(client); } m_personas.insert(qStringFromGChar(folks_persona_get_iid(persona)), persona); } g_object_unref(iter); return m_deletedAt.isValid(); } QDateTime QIndividual::deletedAt() { if (!m_deletedAt.isNull()) { return m_deletedAt; } // make the date invalid to avoid re-check // it will be null again if the QIndividual is marked as dirty m_deletedAt = QDateTime(QDate(), QTime(0, 0, 0)); GeeSet *personas = folks_individual_get_personas(m_individual); if (!personas) { return m_deletedAt; } GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas)); while(gee_iterator_next(iter)) { FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter)); if (EDSF_IS_PERSONA(persona)) { EContact *c = edsf_persona_get_contact(EDSF_PERSONA(persona)); EVCardAttribute *attr = e_vcard_get_attribute(E_VCARD(c), X_DELETED_AT); if (attr) { GString *value = e_vcard_attribute_get_value_decoded(attr); if (value) { m_deletedAt = QDateTime::fromString(value->str, Qt::ISODate); g_string_free(value, true); // Addressbook server does not support aggregation we can return // the first person value break; } } } } return m_deletedAt; } bool QIndividual::setVisible(bool visible) { m_visible = visible; } bool QIndividual::isVisible() const { return m_visible; } void QIndividual::setIndividual(FolksIndividual *individual) { static QList individualProperties; if (m_individual != individual) { clear(); if (individual) { QString newId = QString::fromUtf8(folks_individual_get_id(individual)); if (!m_id.isEmpty()) { // we can only update to individual with the same id Q_ASSERT(newId == m_id); } else { m_id = newId; } } m_individual = individual; if (m_individual) { g_object_ref(m_individual); if (individualProperties.isEmpty()) { individualProperties << "alias" << "avatar" << "birthday" << "calendar-event-id" << "call-interaction-count" << "client-types" << "email-addresses" << "full-name" << "gender" << "groups" << "id" << "im-addresses" << "im-interaction-count" << "is-favourite" << "is-user" << "last-call-interaction-datetime" << "last-im-interaction-datetime" << "local-ids" << "location" << "nickname" << "notes" << "personas" << "phone-numbers" << "postal-addresses" << "presence-message" << "presence-status" << "presence-type" << "roles" << "structured-name" << "trust-level" << "urls" << "web-service-addresses"; } Q_FOREACH(const QByteArray &property, individualProperties) { uint signalHandler = g_signal_connect(G_OBJECT(m_individual), QByteArray("notify::") + property, (GCallback) QIndividual::folksIndividualChanged, const_cast(this)); m_notifyConnections << signalHandler; } } } } GHashTable *QIndividual::parseAddressDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value = GeeUtils::gValueSliceNew(G_TYPE_OBJECT); GeeCollection *collection = GEE_COLLECTION(SET_AFD_NEW()); g_value_take_object(value, collection); Q_FOREACH(const QContactDetail& detail, cDetails) { if(!detail.isEmpty()) { QContactAddress address = static_cast(detail); FolksPostalAddress *postalAddress = folks_postal_address_new(address.postOfficeBox().toUtf8().data(), NULL, // extension address.street().toUtf8().data(), address.locality().toUtf8().data(), address.region().toUtf8().data(), address.postcode().toUtf8().data(), address.country().toUtf8().data(), NULL, // address format NULL); //UID if (!folks_postal_address_is_empty(postalAddress)) { FolksPostalAddressFieldDetails *pafd = folks_postal_address_field_details_new(postalAddress, NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(pafd), address, detail == prefDetail); gee_collection_add(collection, pafd); g_object_unref(pafd); } g_object_unref(postalAddress); } } GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES, value); return details; } GHashTable *QIndividual::parsePhotoDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactAvatar avatar = static_cast(detail); if(!avatar.isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_FILE_ICON); QUrl avatarUri = avatar.imageUrl(); if(!avatarUri.isEmpty()) { QString formattedUri = avatarUri.toString(QUrl::RemoveUserInfo); if(!formattedUri.isEmpty()) { GFile *avatarFile = g_file_new_for_uri(formattedUri.toUtf8().data()); GFileIcon *avatarFileIcon = G_FILE_ICON(g_file_icon_new(avatarFile)); g_value_take_object(value, avatarFileIcon); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_AVATAR, value); g_clear_object((GObject**) &avatarFile); } } else { g_object_unref(value); } } } return details; } GHashTable *QIndividual::parseBirthdayDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactBirthday birthday = static_cast(detail); if(!birthday.isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_DATE_TIME); GDateTime *dateTime = g_date_time_new_from_unix_utc(birthday.dateTime().toMSecsSinceEpoch() / 1000); g_value_set_boxed(value, dateTime); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_BIRTHDAY, value); g_date_time_unref(dateTime); } } return details; } GHashTable *QIndividual::parseEmailDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value; PERSONA_DETAILS_INSERT_STRING_FIELD_DETAILS(details, cDetails, FOLKS_PERSONA_DETAIL_EMAIL_ADDRESSES, value, QContactEmailAddress, FOLKS_TYPE_EMAIL_FIELD_DETAILS, emailAddress, prefDetail); return details; } GHashTable *QIndividual::parseFavoriteDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactFavorite favorite = static_cast(detail); if(!favorite.isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_BOOLEAN); g_value_set_boolean(value, favorite.isFavorite()); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_IS_FAVOURITE, value); } } return details; } GHashTable *QIndividual::parseGenderDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactGender gender = static_cast(detail); if(!gender.isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(FOLKS_TYPE_GENDER); FolksGender genderEnum = FOLKS_GENDER_UNSPECIFIED; if(gender.gender() == QContactGender::GenderMale) { genderEnum = FOLKS_GENDER_MALE; } else if(gender.gender() == QContactGender::GenderFemale) { genderEnum = FOLKS_GENDER_FEMALE; } g_value_set_enum(value, genderEnum); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_GENDER, value); } } return details; } GHashTable *QIndividual::parseNameDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactName name = static_cast(detail); if(!name.isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(FOLKS_TYPE_STRUCTURED_NAME); FolksStructuredName *sn = folks_structured_name_new(name.lastName().toUtf8().data(), name.firstName().toUtf8().data(), name.middleName().toUtf8().data(), name.prefix().toUtf8().data(), name.suffix().toUtf8().data()); g_value_take_object(value, sn); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_STRUCTURED_NAME, value); } } return details; } GHashTable *QIndividual::parseFullNameDetails(GHashTable *details, const QList &cDetails, const QString &fallback) { bool found = false; Q_FOREACH(const QContactDetail& detail, cDetails) { QContactDisplayLabel displayLabel = static_cast(detail); if(!displayLabel.label().isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_STRING); g_value_set_string(value, displayLabel.label().toUtf8().data()); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_FULL_NAME, value); found = true; } } // Full name is mandatory if (!found) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_STRING); g_value_set_string(value, fallback.toUtf8().data()); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_FULL_NAME, value); } return details; } GHashTable *QIndividual::parseNicknameDetails(GHashTable *details, const QList &cDetails) { if(cDetails.size() == 0) { return details; } Q_FOREACH(const QContactDetail& detail, cDetails) { QContactNickname nickname = static_cast(detail); if(!nickname.nickname().isEmpty()) { GValue *value = GeeUtils::gValueSliceNew(G_TYPE_STRING); g_value_set_string(value, nickname.nickname().toUtf8().data()); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_NICKNAME, value); // FIXME: check if those values should all be set to the same thing value = GeeUtils::gValueSliceNew(G_TYPE_STRING); g_value_set_string(value, nickname.nickname().toUtf8().data()); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_ALIAS, value); } } return details; } GHashTable *QIndividual::parseNoteDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value; PERSONA_DETAILS_INSERT_STRING_FIELD_DETAILS(details, cDetails, FOLKS_PERSONA_DETAIL_NOTES, value, QContactNote, FOLKS_TYPE_NOTE_FIELD_DETAILS, note, prefDetail); return details; } GHashTable *QIndividual::parseImDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { Q_UNUSED(prefDetail); if(cDetails.size() == 0) { return details; } QMultiMap providerUidMap; Q_FOREACH(const QContactDetail &detail, cDetails) { QContactOnlineAccount account = static_cast(detail); if (!account.isEmpty()) { providerUidMap.insert(DetailContextParser::accountProtocolName(account.protocol()), account.accountUri()); } } if(!providerUidMap.isEmpty()) { //TODO: add account type and subtype GValue *value = GeeUtils::asvSetStrNew(providerUidMap); GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_IM_ADDRESSES, value); } return details; } GHashTable *QIndividual::parseOrganizationDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value = GeeUtils::gValueSliceNew(G_TYPE_OBJECT); GeeCollection *collection = GEE_COLLECTION(SET_AFD_NEW()); g_value_take_object(value, collection); Q_FOREACH(const QContactDetail& detail, cDetails) { QContactOrganization org = static_cast(detail); if(!org.isEmpty()) { FolksRole *role = folks_role_new(org.title().toUtf8().data(), org.name().toUtf8().data(), NULL); folks_role_set_role(role, org.role().toUtf8().data()); FolksRoleFieldDetails *rfd = folks_role_field_details_new(role, NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(rfd), org, detail == prefDetail); gee_collection_add(collection, rfd); g_object_unref(rfd); g_object_unref(role); } } GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_ROLES, value); return details; } GHashTable *QIndividual::parsePhoneNumbersDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value = GeeUtils::gValueSliceNew(G_TYPE_OBJECT); Q_FOREACH(const QContactDetail &detail, cDetails) { QContactPhoneNumber phone = static_cast(detail); if(!phone.isEmpty()) { gValueGeeSetAddStringFieldDetails(value, FOLKS_TYPE_PHONE_FIELD_DETAILS, phone.number().toUtf8().data(), phone, detail == prefDetail); } } GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_PHONE_NUMBERS, value); return details; } GHashTable *QIndividual::parseUrlDetails(GHashTable *details, const QList &cDetails, const QtContacts::QContactDetail &prefDetail) { if(cDetails.size() == 0) { return details; } GValue *value; PERSONA_DETAILS_INSERT_STRING_FIELD_DETAILS(details, cDetails, FOLKS_PERSONA_DETAIL_URLS, value, QContactUrl, FOLKS_TYPE_URL_FIELD_DETAILS, url, prefDetail); return details; } GHashTable *QIndividual::parseDetails(const QtContacts::QContact &contact) { GHashTable *details = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) GeeUtils::gValueSliceFree); parsePhotoDetails(details, contact.details(QContactAvatar::Type)); parseBirthdayDetails(details, contact.details(QContactBirthday::Type)); parseFavoriteDetails(details, contact.details(QContactFavorite::Type)); parseGenderDetails(details, contact.details(QContactGender::Type)); parseNameDetails(details, contact.details(QContactName::Type)); parseFullNameDetails(details, contact.details(QContactDisplayLabel::Type), displayName(contact)); parseNicknameDetails(details, contact.details(QContactNickname::Type)); parseAddressDetails(details, contact.details(QContactAddress::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactAddress::Type])); parseEmailDetails(details, contact.details(QContactEmailAddress::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactEmailAddress::Type])); parseNoteDetails(details, contact.details(QContactNote::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactNote::Type])); parseImDetails(details, contact.details(QContactOnlineAccount::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactOnlineAccount::Type])); parseOrganizationDetails(details, contact.details(QContactOrganization::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactOrganization::Type])); parsePhoneNumbersDetails(details, contact.details(QContactPhoneNumber::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactPhoneNumber::Type])); parseUrlDetails(details, contact.details(QContactUrl::Type), contact.preferredDetail(VCardParser::PreferredActionNames[QContactUrl::Type])); QContactDisplayLabel label = contact.detail(); if (label.label().isEmpty()) { // display label is mandatory, usese the fallback in case of empty values label.setLabel(displayName(contact)); } return details; } QString QIndividual::displayName(const QContact &contact) { if (contact.isEmpty()) { return ""; } QString fallbackLabel; // format display name based on designer request // fallback priority list [Name, Company, PhoneNumber, Email, Alias] if (fallbackLabel.isEmpty()) { QContactName name = contact.detail(); if (!name.isEmpty()) { QString fullName = QString("%1 %2").arg(name.firstName()).arg(name.lastName()); fallbackLabel = fullName.trimmed(); } } if (fallbackLabel.isEmpty()) { QContactOrganization org = contact.detail(); fallbackLabel = org.name().trimmed(); } if (fallbackLabel.isEmpty()) { QContactPhoneNumber number = contact.detail(); fallbackLabel = number.number().trimmed(); } if (fallbackLabel.isEmpty()) { QContactEmailAddress email = contact.detail(); fallbackLabel = email.emailAddress().trimmed(); } if (fallbackLabel.isEmpty()) { QContactOnlineAccount account = contact.detail(); fallbackLabel = account.accountUri().trimmed(); } return fallbackLabel; } void QIndividual::setExtendedDetails(FolksPersona *persona, const QList &xDetails, const QDateTime &createdAtDate) { FolksPersonaStore *store = folks_persona_get_store(persona); if (EDSF_IS_PERSONA_STORE(store)) { GError *error = NULL; ESource *source = edsf_persona_store_get_source(EDSF_PERSONA_STORE(store)); EClient *client = E_BOOK_CLIENT_CONNECT_SYNC(source, NULL, &error); if (error) { qWarning() << "Fail to connect with EDS" << error->message; g_error_free(error); } else { EContact *c = edsf_persona_get_contact(EDSF_PERSONA(persona)); // create X-CREATED-AT if it does not exists EVCardAttribute *attr = e_vcard_get_attribute(E_VCARD(c), X_CREATED_AT); if (!attr) { QDateTime createdAt = createdAtDate.isValid() ? createdAtDate : QDateTime::currentDateTime(); attr = e_vcard_attribute_new("", X_CREATED_AT); e_vcard_add_attribute_with_value(E_VCARD(c), attr, createdAt.toUTC().toString(Qt::ISODate).toUtf8().constData()); } Q_FOREACH(const QContactDetail &d, xDetails) { QContactExtendedDetail xd = static_cast(d); // X_CREATED_AT should not be updated if (xd.name() == X_CREATED_AT) { continue; } if (m_supportedExtendedDetails.contains(xd.name())) { // Remove old attribute attr = e_vcard_get_attribute(E_VCARD(c), xd.name().toUtf8().constData()); if (attr) { e_vcard_remove_attribute(E_VCARD(c), attr); } attr = e_vcard_attribute_new("", xd.name().toUtf8().constData()); e_vcard_add_attribute_with_value(E_VCARD(c), attr, xd.data().toString().toUtf8().constData()); } else { qWarning() << "Extended detail not supported" << xd.name(); } } e_book_client_modify_contact_sync(E_BOOK_CLIENT(client), c, NULL, &error); if (error) { qWarning() << "Fail to update EDS contact:" << error->message; g_error_free(error); } } g_object_unref(client); } } void QIndividual::markAsDirty() { delete m_contact; m_contact = 0; m_deletedAt = QDateTime(); } void QIndividual::enableAutoLink(bool flag) { m_autoLink = flag; } bool QIndividual::autoLinkEnabled() { return m_autoLink; } FolksPersona* QIndividual::primaryPersona() { if (m_personas.size() > 0) { return m_personas.begin().value(); } else { return 0; } } QtContacts::QContactDetail QIndividual::detailFromUri(QtContacts::QContactDetail::DetailType type, const QString &uri) const { Q_FOREACH(QContactDetail detail, m_contact->details(type)) { if (detail.detailUri() == uri) { return detail; } } return QContactDetail(); } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/contact-less-than.cpp0000644000015300001610000000365412632607666025040 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). * Copyright (C) 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Based on QContactManagerEngine code by Digia */ #include "contact-less-than.h" #include "contacts-map.h" #include "qindividual.h" #include #include #include using namespace QtContacts; namespace galera { ContactLessThan::ContactLessThan(const galera::SortClause &sortClause) : m_sortClause(sortClause) { } bool ContactLessThan::operator()(const QtContacts::QContact &contactA, const QtContacts::QContact &contactB) { int r = QContactManagerEngine::compareContact(contactA, contactB, m_sortClause.toContactSortOrder()); return (r <= 0); } ContactEntryLessThan::ContactEntryLessThan(const SortClause &sortClause) : m_sortClause(sortClause) { } bool ContactEntryLessThan::operator()(ContactEntry *entryA, ContactEntry *entryB) { int r = QContactManagerEngine::compareContact(entryA->individual()->contact(), entryB->individual()->contact(), m_sortClause.toContactSortOrder()); return (r <= 0); } } // namespace address-book-service-0.1.1+16.04.20151211.1/lib/detail-context-parser.cpp0000644000015300001610000003430312632607666025722 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "detail-context-parser.h" #include "common/vcard-parser.h" #include #include using namespace QtContacts; namespace galera { void DetailContextParser::parseContext(FolksAbstractFieldDetails *fd, const QtContacts::QContactDetail &detail, bool isPreffered) { // clear the current values to prevent duplicate values folks_abstract_field_details_set_parameter(fd, "type", ""); Q_FOREACH(const QString ¶m, listContext(detail)) { folks_abstract_field_details_add_parameter(fd, "type", param.toUtf8().data()); } if (isPreffered) { folks_abstract_field_details_set_parameter(fd, VCardParser::PrefParamName.toLower().toUtf8().data(), "1"); } } QStringList DetailContextParser::parseContext(const QtContacts::QContactDetail &detail) { static QMap map; // populate the map once if (map.isEmpty()) { map[QContactDetail::ContextHome] = "home"; map[QContactDetail::ContextWork] = "work"; map[QContactDetail::ContextOther] = "other"; } QStringList strings; Q_FOREACH(int subType, detail.contexts()) { if (map.contains(subType)) { strings << map[subType]; } } return strings; } QStringList DetailContextParser::listContext(const QtContacts::QContactDetail &detail) { QStringList context = parseContext(detail); switch (detail.type()) { case QContactDetail::TypePhoneNumber: context << parsePhoneContext(detail); break; case QContactDetail::TypeAddress: context << parseAddressContext(detail); // Setting more than one context causes folks to freeze if (!context.isEmpty()) { return context.mid(0,1); } break; case QContactDetail::TypeOnlineAccount: context << parseOnlineAccountContext(detail); break; case QContactDetail::TypeUrl: case QContactDetail::TypeEmailAddress: case QContactDetail::TypeOrganization: default: break; } return context; } QStringList DetailContextParser::parsePhoneContext(const QtContacts::QContactDetail &detail) { static QMap map; // populate the map once if (map.isEmpty()) { map[QContactPhoneNumber::SubTypeLandline] = "landline"; map[QContactPhoneNumber::SubTypeMobile] = "cell"; map[QContactPhoneNumber::SubTypeFax] = "fax"; map[QContactPhoneNumber::SubTypePager] = "pager"; map[QContactPhoneNumber::SubTypeVoice] = "voice"; map[QContactPhoneNumber::SubTypeModem] = "modem"; map[QContactPhoneNumber::SubTypeVideo] = "video"; map[QContactPhoneNumber::SubTypeCar] = "car"; map[QContactPhoneNumber::SubTypeBulletinBoardSystem] = "bulletinboard"; map[QContactPhoneNumber::SubTypeMessagingCapable] = "messaging"; map[QContactPhoneNumber::SubTypeAssistant] = "assistant"; map[QContactPhoneNumber::SubTypeDtmfMenu] = "dtmfmenu"; } QStringList strings; Q_FOREACH(int subType, static_cast(&detail)->subTypes()) { if (map.contains(subType)) { strings << map[subType]; } } return strings; } QStringList DetailContextParser::parseAddressContext(const QtContacts::QContactDetail &detail) { static QMap map; // populate the map once if (map.isEmpty()) { map[QContactAddress::SubTypeParcel] = "parcel"; map[QContactAddress::SubTypePostal] = "postal"; map[QContactAddress::SubTypeDomestic] = "domestic"; map[QContactAddress::SubTypeInternational] = "international"; } QStringList strings; Q_FOREACH(int subType, static_cast(&detail)->subTypes()) { if (map.contains(subType)) { strings << map[subType]; } } return strings; } QStringList DetailContextParser::parseOnlineAccountContext(const QtContacts::QContactDetail &im) { static QMap map; // populate the map once if (map.isEmpty()) { map[QContactOnlineAccount::SubTypeSip] = "sip"; map[QContactOnlineAccount::SubTypeSipVoip] = "sipvoip"; map[QContactOnlineAccount::SubTypeImpp] = "impp"; map[QContactOnlineAccount::SubTypeVideoShare] = "videoshare"; } QSet strings; const QContactOnlineAccount *acc = static_cast(&im); Q_FOREACH(int subType, acc->subTypes()) { if (map.contains(subType)) { strings << map[subType]; } } // Make compatible with contact importer if (acc->protocol() == QContactOnlineAccount::ProtocolJabber) { strings << map[QContactOnlineAccount::SubTypeImpp]; } else if (acc->protocol() == QContactOnlineAccount::ProtocolJabber) { strings << map[QContactOnlineAccount::SubTypeSip]; } return strings.toList(); } QString DetailContextParser::accountProtocolName(int protocol) { static QMap map; // populate the map once if (map.isEmpty()) { map[QContactOnlineAccount::ProtocolAim] = "aim"; map[QContactOnlineAccount::ProtocolIcq] = "icq"; map[QContactOnlineAccount::ProtocolIrc] = "irc"; map[QContactOnlineAccount::ProtocolJabber] = "jabber"; map[QContactOnlineAccount::ProtocolMsn] = "msn"; map[QContactOnlineAccount::ProtocolQq] = "qq"; map[QContactOnlineAccount::ProtocolSkype] = "skype"; map[QContactOnlineAccount::ProtocolYahoo] = "yahoo"; map[QContactOnlineAccount::ProtocolUnknown] = "unknown"; } if (map.contains(protocol)) { return map[protocol]; } else { qWarning() << "invalid IM protocol" << protocol; } return map[QContactOnlineAccount::ProtocolUnknown]; } QStringList DetailContextParser::listParameters(FolksAbstractFieldDetails *details) { static QStringList whiteList; if (whiteList.isEmpty()) { whiteList << "x-evolution-ui-slot" << "x-google-label"; } GeeMultiMap *map = folks_abstract_field_details_get_parameters(details); GeeSet *keys = gee_multi_map_get_keys(map); GeeIterator *siter = gee_iterable_iterator(GEE_ITERABLE(keys)); QStringList params; while (gee_iterator_next (siter)) { char *parameter = (char*) gee_iterator_get(siter); if (QString::fromUtf8(parameter).toUpper() == VCardParser::PrefParamName) { params << "pref"; continue; } else if (QString::fromUtf8(parameter) != "type") { if (!whiteList.contains(QString::fromUtf8(parameter))) { qDebug() << "not suported field details" << parameter; // FIXME: check what to do with other parameters } continue; } GeeCollection *args = folks_abstract_field_details_get_parameter_values(details, parameter); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE (args)); while (gee_iterator_next (iter)) { char *type = (char*) gee_iterator_get(iter); QString context(QString::fromUtf8(type).toLower()); if (!params.contains(context)) { params << context; } g_free(type); } g_free(parameter); g_object_unref(iter); } g_object_unref(siter); return params; } void DetailContextParser::parseParameters(QtContacts::QContactDetail &detail, FolksAbstractFieldDetails *fd, bool *isPref) { QStringList params = listParameters(fd); if (isPref) { *isPref = params.contains(VCardParser::PrefParamName.toLower()); if (*isPref) { params.removeOne(VCardParser::PrefParamName.toLower()); } } QList context = contextsFromParameters(¶ms); if (!context.isEmpty()) { detail.setContexts(context); } switch (detail.type()) { case QContactDetail::TypePhoneNumber: parsePhoneParameters(detail, params); break; case QContactDetail::TypeAddress: parseAddressParameters(detail, params); break; case QContactDetail::TypeOnlineAccount: parseOnlineAccountParameters(detail, params); break; case QContactDetail::TypeUrl: case QContactDetail::TypeEmailAddress: case QContactDetail::TypeOrganization: default: break; } } QList DetailContextParser::contextsFromParameters(QStringList *parameters) { static QMap map; // populate the map once if (map.isEmpty()) { map["home"] = QContactDetail::ContextHome; map["work"] = QContactDetail::ContextWork; map["other"] = QContactDetail::ContextOther; } QList values; QStringList accepted; Q_FOREACH(const QString ¶m, *parameters) { if (map.contains(param.toLower())) { accepted << param; values << map[param.toLower()]; } } Q_FOREACH(const QString ¶m, accepted) { parameters->removeOne(param); } return values; } void DetailContextParser::parsePhoneParameters(QtContacts::QContactDetail &phone, const QStringList ¶ms) { // populate the map once static QMap mapTypes; if (mapTypes.isEmpty()) { mapTypes["landline"] = QContactPhoneNumber::SubTypeLandline; mapTypes["mobile"] = QContactPhoneNumber::SubTypeMobile; mapTypes["cell"] = QContactPhoneNumber::SubTypeMobile; mapTypes["fax"] = QContactPhoneNumber::SubTypeFax; mapTypes["pager"] = QContactPhoneNumber::SubTypePager; mapTypes["voice"] = QContactPhoneNumber::SubTypeVoice; mapTypes["modem"] = QContactPhoneNumber::SubTypeModem; mapTypes["video"] = QContactPhoneNumber::SubTypeVideo; mapTypes["car"] = QContactPhoneNumber::SubTypeCar; mapTypes["bulletinboard"] = QContactPhoneNumber::SubTypeBulletinBoardSystem; mapTypes["messaging"] = QContactPhoneNumber::SubTypeMessagingCapable; mapTypes["assistant"] = QContactPhoneNumber::SubTypeAssistant; mapTypes["dtmfmenu"] = QContactPhoneNumber::SubTypeDtmfMenu; } QList subTypes; Q_FOREACH(const QString ¶m, params) { if (mapTypes.contains(param.toLower())) { subTypes << mapTypes[param.toLower()]; } else if (!param.isEmpty()) { qWarning() << "Invalid phone parameter:" << param; } } if (!subTypes.isEmpty()) { static_cast(&phone)->setSubTypes(subTypes); } } void DetailContextParser::parseAddressParameters(QtContacts::QContactDetail &address, const QStringList ¶meters) { static QMap map; // populate the map once if (map.isEmpty()) { map["parcel"] = QContactAddress::SubTypeParcel; map["postal"] = QContactAddress::SubTypePostal; map["domestic"] = QContactAddress::SubTypeDomestic; map["international"] = QContactAddress::SubTypeInternational; } QList values; Q_FOREACH(const QString ¶m, parameters) { if (map.contains(param.toLower())) { values << map[param.toLower()]; } else { qWarning() << "invalid Address subtype" << param; } } if (!values.isEmpty()) { static_cast(&address)->setSubTypes(values); } } void DetailContextParser::parseOnlineAccountParameters(QtContacts::QContactDetail &im, const QStringList ¶meters) { static QMap map; // populate the map once if (map.isEmpty()) { map["sip"] = QContactOnlineAccount::SubTypeSip; map["sipvoip"] = QContactOnlineAccount::SubTypeSipVoip; map["impp"] = QContactOnlineAccount::SubTypeImpp; map["videoshare"] = QContactOnlineAccount::SubTypeVideoShare; } QSet values; Q_FOREACH(const QString ¶m, parameters) { if (map.contains(param.toLower())) { values << map[param.toLower()]; } else { qWarning() << "invalid IM subtype" << param; } } // Make compatible with contact importer QContactOnlineAccount *acc = static_cast(&im); if (acc->protocol() == QContactOnlineAccount::ProtocolJabber) { values << QContactOnlineAccount::SubTypeImpp; } else if (acc->protocol() == QContactOnlineAccount::ProtocolJabber) { values << QContactOnlineAccount::SubTypeSip; } if (!values.isEmpty()) { static_cast(&im)->setSubTypes(values.toList()); } } int DetailContextParser::accountProtocolFromString(const QString &protocol) { static QMap map; // populate the map once if (map.isEmpty()) { map["aim"] = QContactOnlineAccount::ProtocolAim; map["icq"] = QContactOnlineAccount::ProtocolIcq; map["irc"] = QContactOnlineAccount::ProtocolIrc; map["jabber"] = QContactOnlineAccount::ProtocolJabber; map["msn"] = QContactOnlineAccount::ProtocolMsn; map["qq"] = QContactOnlineAccount::ProtocolQq; map["skype"] = QContactOnlineAccount::ProtocolSkype; map["yahoo"] = QContactOnlineAccount::ProtocolYahoo; } if (map.contains(protocol.toLower())) { return map[protocol.toLower()]; } else { qWarning() << "invalid IM protocol" << protocol; } return QContactOnlineAccount::ProtocolUnknown; } } // namespace address-book-service-0.1.1+16.04.20151211.1/lib/addressbook-adaptor.h0000644000015300001610000001635712632607673025110 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_ADDRESSBOOK_ADAPTOR_H__ #define __GALERA_ADDRESSBOOK_ADAPTOR_H__ #include #include #include #include #include #include "common/source.h" #include "common/dbus-service-defs.h" namespace galera { class AddressBook; class AddressBookAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", CPIM_ADDRESSBOOK_IFACE_NAME) Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") Q_PROPERTY(bool isReady READ isReady NOTIFY readyChanged) Q_PROPERTY(bool safeMode READ safeMode WRITE setSafeMode NOTIFY safeModeChanged) public: AddressBookAdaptor(const QDBusConnection &connection, AddressBook *parent); virtual ~AddressBookAdaptor(); void setSafeMode(bool flag); public Q_SLOTS: SourceList availableSources(const QDBusMessage &message); Source source(const QDBusMessage &message); Source createSource(const QString &sourceName, bool setAsPrimary, const QDBusMessage &message); Source createSourceForAccount(const QString &sourceName, uint accountId, bool setAsPrimary, const QDBusMessage &message); SourceList updateSources(const SourceList &sources, const QDBusMessage &message); bool removeSource(const QString &sourceId, const QDBusMessage &message); QStringList sortFields(); QDBusObjectPath query(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources); int removeContacts(const QStringList &contactIds, const QDBusMessage &message); QString createContact(const QString &contact, const QString &source, const QDBusMessage &message); QStringList updateContacts(const QStringList &contacts, const QDBusMessage &message); QString linkContacts(const QStringList &contacts); bool unlinkContacts(const QString &parentId, const QStringList &contactsIds); bool isReady(); bool safeMode() const; bool ping(); void purgeContacts(const QString &since, const QString &sourceId, const QDBusMessage &message); void shutDown() const; Q_SIGNALS: void contactsAdded(const QStringList &ids); void contactsRemoved(const QStringList &ids); void contactsUpdated(const QStringList &ids); void asyncOperationResult(QMap errors); void readyChanged(); void reloaded(); void safeModeChanged(); void sourcesChanged(); private: AddressBook *m_addressBook; QDBusConnection m_connection; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/lib/update-contact-request.cpp0000644000015300001610000011377112632607666026114 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "update-contact-request.h" #include "qindividual.h" #include "detail-context-parser.h" #include "gee-utils.h" #include "common/vcard-parser.h" #include using namespace QtContacts; namespace galera { template<> bool sortDetails(const QContactDetail &d1, const QContactDetail &d2) { return d1.value(QtContacts::QContactPhoneNumber::FieldNumber).toString() < d2.value(QtContacts::QContactPhoneNumber::FieldNumber).toString(); } template<> bool sortDetails(const QContactDetail &d1, const QContactDetail &d2) { return d1.value(QtContacts::QContactEmailAddress::FieldEmailAddress).toString() < d2.value(QtContacts::QContactEmailAddress::FieldEmailAddress).toString(); } UpdateContactRequest::UpdateContactRequest(QtContacts::QContact newContact, QIndividual *parent, QObject *listener, const char *slot) : QObject(), m_parent(parent), m_object(listener), m_currentPersona(0), m_eventLoop(0), m_newContact(newContact), m_currentPersonaIndex(0) { int slotIndex = listener->metaObject()->indexOfSlot(++slot); if (slotIndex == -1) { qWarning() << "Invalid slot:" << slot << "for object" << listener; } else { m_slot = listener->metaObject()->method(slotIndex); } } UpdateContactRequest::~UpdateContactRequest() { // check if there is a operation running if (m_currentPersona != 0) { wait(); } } void UpdateContactRequest::invokeSlot(const QString &errorMessage) { Q_EMIT done(errorMessage); if (m_slot.isValid() && m_parent) { m_slot.invoke(m_object, Q_ARG(QString, m_parent->id()), Q_ARG(QString, errorMessage)); } else if (m_parent == 0) { // the object was detached we need to destroy it deleteLater(); } if (m_eventLoop) { m_eventLoop->quit(); } } void UpdateContactRequest::start() { m_currentDetailType = QContactDetail::TypeAddress; m_originalContact = m_parent->contact(); m_personas = m_parent->personas(); m_currentPersonaIndex = 0; updatePersona(); } void UpdateContactRequest::wait() { Q_ASSERT(m_eventLoop == 0); QEventLoop eventLoop; m_eventLoop = &eventLoop; eventLoop.exec(); m_eventLoop = 0; } void UpdateContactRequest::deatach() { m_parent = 0; } void UpdateContactRequest::notifyError(const QString &errorMessage) { invokeSlot(errorMessage); } bool UpdateContactRequest::isEqual(const QtContacts::QContactDetail &detailA, const QtContacts::QContactDetail &detailB) { if (detailA.type() != detailB.type()) { return false; } switch(detailA.type()) { case QContactDetail::TypeFavorite: return detailA.value(QContactFavorite::FieldFavorite).toBool() == detailB.value(QContactFavorite::FieldFavorite).toBool(); default: { // clear detail uri to compare only the field value QContactDetail copyA(detailA); QContactDetail copyB(detailB); copyA.setDetailUri(""); copyB.setDetailUri(""); return (copyA == copyB); } } } bool UpdateContactRequest::isEqual(QList listA, QList listB) { if (listA.size() != listB.size()) { return false; } if (listA.size() == 0) { return true; } const int detailType = listA.first().type(); switch(detailType) { case QContactDetail::TypePhoneNumber: qSort(listA.begin(), listA.end(), sortDetails); qSort(listB.begin(), listB.end(), sortDetails); break; case QContactDetail::TypeEmailAddress: qSort(listA.begin(), listA.end(), sortDetails); qSort(listB.begin(), listB.end(), sortDetails); break; default: qSort(listA.begin(), listA.end(), sortDetails<0>); qSort(listB.begin(), listB.end(), sortDetails<0>); break; } for(int i=0; i < listA.size(); i++) { if (!isEqual(listA[i], listB[i])) { return false; } } return true; } bool UpdateContactRequest::isEqual(QList listA, const QtContacts::QContactDetail &prefA, QList listB, const QtContacts::QContactDetail &prefB) { if (prefA != prefB) { return false; } return isEqual(listA, listB); } bool UpdateContactRequest::checkPersona(QtContacts::QContactDetail &det, int persona) { if (det.detailUri().isEmpty()) { return false; } QStringList ids = det.detailUri().split("."); Q_ASSERT(ids.count() == 2); return (ids[0].toInt() == persona); } QList UpdateContactRequest::detailsFromPersona(const QtContacts::QContact &contact, QtContacts::QContactDetail::DetailType type, int persona, bool includeEmptyPersona, QtContacts::QContactDetail *pref) { QList personaDetails; QContactDetail globalPref; if (pref && VCardParser::PreferredActionNames.contains(type)) { globalPref = contact.preferredDetail(VCardParser::PreferredActionNames[type]); } Q_FOREACH(QContactDetail det, contact.details(type)) { if ((includeEmptyPersona && det.detailUri().isEmpty()) || checkPersona(det, persona)) { if (!det.isEmpty()) { personaDetails << det; if (pref && det == globalPref) { *pref = det; } } } } return personaDetails; } QList UpdateContactRequest::originalDetailsFromPersona(QtContacts::QContactDetail::DetailType type, int persona, QtContacts::QContactDetail *pref) const { return detailsFromPersona(m_originalContact, type, persona, false, pref); } QList UpdateContactRequest::detailsFromPersona(QtContacts::QContactDetail::DetailType type, int persona, QtContacts::QContactDetail *pref) const { // only return new details for the first persona, this will avoid to create the details for all personas return detailsFromPersona(m_newContact, type, persona, (persona==1), pref); } void UpdateContactRequest::updateAddress() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeAddress, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeAddress, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_POSTAL_ADDRESS_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "Adderess diff"; GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactAddress addr = static_cast(newDetail); FolksPostalAddress *pa; QByteArray postOfficeBox = addr.postOfficeBox().toUtf8(); QByteArray street = addr.street().toUtf8(); QByteArray locality = addr.locality().toUtf8(); QByteArray region = addr.region().toUtf8(); QByteArray postcode = addr.postcode().toUtf8(); QByteArray country = addr.country().toUtf8(); pa = folks_postal_address_new(postOfficeBox.constData(), NULL, street.constData(), locality.constData(), region.constData(), postcode.constData(), country.constData(), NULL, NULL); FolksPostalAddressFieldDetails *field = folks_postal_address_field_details_new(pa, NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, newDetail == prefDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); g_object_unref(pa); } folks_postal_address_details_change_postal_addresses(FOLKS_POSTAL_ADDRESS_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateAvatar() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeAvatar, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeAvatar, m_currentPersonaIndex, 0); if (m_currentPersona && FOLKS_IS_AVATAR_DETAILS(m_currentPersona) && !isEqual(originalDetails, newDetails)) { qDebug() << "avatar diff:" << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n" << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail()); // update avatar version if necessary QContactExtendedDetail originalAvatarRev; QContactExtendedDetail newAvatarRev; Q_FOREACH(const QContactExtendedDetail &det, originalDetailsFromPersona(QContactDetail::TypeExtendedDetail, m_currentPersonaIndex, 0)) { if (det.name() == "X-AVATAR-REV") { originalAvatarRev = det; break; } } Q_FOREACH(const QContactExtendedDetail &det, detailsFromPersona(QContactDetail::TypeExtendedDetail, m_currentPersonaIndex, 0)) { if (det.name() == "X-AVATAR-REV") { newAvatarRev = det; break; } } // if the avatar changed and the rev still the same we need to reset it to force a sync if (originalAvatarRev.data() == newAvatarRev.data()) { newAvatarRev.setData(""); m_newContact.saveDetail(&newAvatarRev); } //Only supports one avatar QUrl avatarUri; QUrl oldAvatarUri; if (originalDetails.count()) { QContactAvatar avatar = static_cast(originalDetails[0]); oldAvatarUri = avatar.imageUrl(); } if (newDetails.count()) { QContactAvatar avatar = static_cast(newDetails[0]); avatarUri = avatar.imageUrl(); } if ((avatarUri != oldAvatarUri) && (avatarUri.isLocalFile() || avatarUri.isEmpty())){ GFileIcon *avatarFileIcon = NULL; if(!avatarUri.isEmpty()) { QString formattedUri = avatarUri.toString(QUrl::RemoveUserInfo); if(!formattedUri.isEmpty()) { QByteArray uriUtf8 = formattedUri.toUtf8(); GFile *avatarFile = g_file_new_for_uri(uriUtf8.constData()); avatarFileIcon = G_FILE_ICON(g_file_icon_new(avatarFile)); g_object_unref(avatarFile); } } folks_avatar_details_change_avatar(FOLKS_AVATAR_DETAILS(m_currentPersona), G_LOADABLE_ICON(avatarFileIcon), (GAsyncReadyCallback) updateDetailsDone, this); if (avatarFileIcon) { g_object_unref(avatarFileIcon); } return; } } updateDetailsDone(0, 0, this); } void UpdateContactRequest::updateBirthday() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeBirthday, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeBirthday, m_currentPersonaIndex, 0); if (m_currentPersona && FOLKS_IS_BIRTHDAY_DETAILS(m_currentPersona) && !isEqual(originalDetails, newDetails)) { qDebug() << "birthday diff"; //Only supports one birthday QDateTime dateTimeBirthday; if (newDetails.count()) { QContactBirthday birthday = static_cast(newDetails[0]); if (!birthday.isEmpty()) { dateTimeBirthday = birthday.dateTime(); } } GDateTime *dateTime = NULL; if (dateTimeBirthday.isValid()) { dateTime = g_date_time_new_from_unix_utc(dateTimeBirthday.toMSecsSinceEpoch() / 1000); } folks_birthday_details_change_birthday(FOLKS_BIRTHDAY_DETAILS(m_currentPersona), dateTime, (GAsyncReadyCallback) updateDetailsDone, this); if (dateTime) { g_date_time_unref(dateTime); } } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateFullName(const QString &fullName) { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeDisplayLabel, m_currentPersonaIndex, 0); if (m_currentPersona && FOLKS_IS_NAME_DETAILS(m_currentPersona) && originalDetails.size() > 0 && (originalDetails[0].value(QContactDisplayLabel::FieldLabel).toString() != fullName)) { qDebug() << "Full Name diff:" << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n" << "\n\t" << fullName; //Only supports one fullName QByteArray fullNameUtf8 = fullName.toUtf8(); folks_name_details_change_full_name(FOLKS_NAME_DETAILS(m_currentPersona), fullNameUtf8.constData(), (GAsyncReadyCallback) updateDetailsDone, this); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateEmail() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeEmailAddress, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeEmailAddress, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_EMAIL_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "email diff"; GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactEmailAddress email = static_cast(newDetail); FolksEmailFieldDetails *field; QByteArray emailAddress = email.emailAddress().toUtf8(); field = folks_email_field_details_new(emailAddress.constData(), NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, newDetail == prefDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); } folks_email_details_change_email_addresses(FOLKS_EMAIL_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateName() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeName, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeName, m_currentPersonaIndex, 0); if (m_currentPersona && FOLKS_IS_NAME_DETAILS(m_currentPersona) && !isEqual(originalDetails, newDetails)) { //Only supports one fullName FolksStructuredName *sn = 0; if (newDetails.count()) { QContactName name = static_cast(newDetails[0]); QByteArray lastName = name.lastName().toUtf8(); QByteArray firstName = name.firstName().toUtf8(); QByteArray middleName = name.middleName().toUtf8(); QByteArray prefix = name.prefix().toUtf8(); QByteArray suffix = name.suffix().toUtf8(); sn = folks_structured_name_new(lastName.constData(), firstName.constData(), middleName.constData(), prefix.constData(), suffix.constData()); } folks_name_details_change_structured_name(FOLKS_NAME_DETAILS(m_currentPersona), sn, (GAsyncReadyCallback) updateDetailsDone, this); if (sn) { g_object_unref(sn); } } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateNickname() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeNickname, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeNickname, m_currentPersonaIndex, 0); if (m_currentPersona && FOLKS_IS_NAME_DETAILS(m_currentPersona) && !isEqual(originalDetails, newDetails)) { qDebug() << "Nickname diff"; //Only supports one fullName QString nicknameValue; if (newDetails.count()) { QContactNickname nickname = static_cast(newDetails[0]); nicknameValue = nickname.nickname(); } QByteArray nicknameValueUtf8 = nicknameValue.toUtf8(); folks_name_details_change_nickname(FOLKS_NAME_DETAILS(m_currentPersona), nicknameValueUtf8.constData(), (GAsyncReadyCallback) updateDetailsDone, this); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateNote() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeNote, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeNote, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_EMAIL_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "notes diff"; GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactNote note = static_cast(newDetail); FolksNoteFieldDetails *field; QByteArray noteUtf8 = note.note().toUtf8(); field = folks_note_field_details_new(noteUtf8.constData(), 0, 0); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, newDetail == prefDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); } folks_note_details_change_notes(FOLKS_NOTE_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateOnlineAccount() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeOnlineAccount, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeOnlineAccount, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_IM_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "OnlineAccounts diff"; GeeMultiMap *imMap = GEE_MULTI_MAP_AFD_NEW(FOLKS_TYPE_IM_FIELD_DETAILS); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactOnlineAccount account = static_cast(newDetail); FolksImFieldDetails *field; if (account.protocol() != QContactOnlineAccount::ProtocolUnknown) { QByteArray accountUri = account.accountUri().toUtf8(); field = folks_im_field_details_new(accountUri.constData(), NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), account, account == prefDetail); QString protocolName(DetailContextParser::accountProtocolName(account.protocol())); QByteArray protocolNameUtf8 = protocolName.toUtf8(); gee_multi_map_set(imMap, protocolNameUtf8.constData(), field); g_object_unref(field); } } folks_im_details_change_im_addresses(FOLKS_IM_DETAILS(m_currentPersona), imMap, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(imMap); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateOrganization() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeOrganization, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeOrganization, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_ROLE_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "Organization diff"; GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactOrganization org = static_cast(newDetail); FolksRoleFieldDetails *field; FolksRole *roleValue; QByteArray title = org.title().isEmpty() ? "" : org.title().toUtf8(); QByteArray name = org.name().isEmpty() ? "" : org.name().toUtf8(); QByteArray roleName = org.role().isEmpty() ? "" : org.role().toUtf8(); roleValue = folks_role_new(title.constData(), name.constData(), ""); folks_role_set_role(roleValue, roleName.constData()); field = folks_role_field_details_new(roleValue, NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, newDetail == prefDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); g_object_unref(roleValue); } folks_role_details_change_roles(FOLKS_ROLE_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updatePhone() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypePhoneNumber, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypePhoneNumber, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_PHONE_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "Phone diff:" << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n" << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail()); GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactPhoneNumber phone = static_cast(newDetail); FolksPhoneFieldDetails *field; QByteArray phoneNumber = phone.number().toUtf8(); field = folks_phone_field_details_new(phoneNumber.constData(), NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, newDetail == prefDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); } folks_phone_details_change_phone_numbers(FOLKS_PHONE_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateUrl() { QContactDetail originalPref; QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeUrl, m_currentPersonaIndex, &originalPref); QContactDetail prefDetail; QList newDetails = detailsFromPersona(QContactDetail::TypeUrl, m_currentPersonaIndex, &prefDetail); if (m_currentPersona && FOLKS_IS_URL_DETAILS(m_currentPersona) && !isEqual(originalDetails, originalPref, newDetails, prefDetail)) { qDebug() << "Url diff"; GeeSet *newSet = SET_AFD_NEW(); Q_FOREACH(QContactDetail newDetail, newDetails) { QContactUrl url = static_cast(newDetail); FolksUrlFieldDetails *field; QByteArray urlValue = url.url().toUtf8(); field = folks_url_field_details_new(urlValue.constData(), NULL); DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(field), newDetail, prefDetail == newDetail); gee_collection_add(GEE_COLLECTION(newSet), field); g_object_unref(field); } folks_url_details_change_urls(FOLKS_URL_DETAILS(m_currentPersona), newSet, (GAsyncReadyCallback) updateDetailsDone, this); g_object_unref(newSet); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateFavorite() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeFavorite, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeFavorite, m_currentPersonaIndex, 0); // as default all contacts has favorte set to false if (newDetails.isEmpty()) { QContactFavorite fav; fav.setFavorite(false); newDetails << fav; } if (m_currentPersona && FOLKS_IS_FAVOURITE_DETAILS(m_currentPersona) && !isEqual(originalDetails, newDetails)) { qDebug() << "Favorite diff:" << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n" << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail()); //Only supports one fullName bool isFavorite = false; if (newDetails.count()) { QContactFavorite favorite = static_cast(newDetails[0]); isFavorite = favorite.isFavorite(); } folks_favourite_details_change_is_favourite(FOLKS_FAVOURITE_DETAILS(m_currentPersona), isFavorite, (GAsyncReadyCallback) updateDetailsDone, this); } else { updateDetailsDone(0, 0, this); } } void UpdateContactRequest::updateExtendedDetails() { QList originalDetails = originalDetailsFromPersona(QContactDetail::TypeExtendedDetail, m_currentPersonaIndex, 0); QList newDetails = detailsFromPersona(QContactDetail::TypeExtendedDetail, m_currentPersonaIndex, 0); if (m_currentPersona && !isEqual(originalDetails, newDetails)) { qDebug() << "Extended details diff"; QIndividual::setExtendedDetails(m_currentPersona, newDetails); } updateDetailsDone(0, 0, this); } void UpdateContactRequest::updatePersona() { if (m_personas.size() <= m_currentPersonaIndex) { m_currentPersona = 0; if (m_parent) { m_parent->flush(); } invokeSlot(); } else { m_currentPersona = m_personas[m_currentPersonaIndex]; g_object_ref(m_currentPersona); m_currentDetailType = QContactDetail::TypeUndefined; m_currentPersonaIndex++; updateDetailsDone(0, 0, this); } } QString UpdateContactRequest::callDetailChangeFinish(QtContacts::QContactDetail::DetailType detailType, FolksPersona *persona, GAsyncResult *result) { Q_ASSERT(persona); Q_ASSERT(result); GError *error = 0; switch(detailType) { case QContactDetail::TypeAddress: folks_postal_address_details_change_postal_addresses_finish(FOLKS_POSTAL_ADDRESS_DETAILS(persona), result, &error); break; case QContactDetail::TypeAvatar: folks_avatar_details_change_avatar_finish(FOLKS_AVATAR_DETAILS(persona), result, &error); break; case QContactDetail::TypeBirthday: folks_birthday_details_change_birthday_finish(FOLKS_BIRTHDAY_DETAILS(persona), result, &error); break; case QContactDetail::TypeDisplayLabel: folks_name_details_change_full_name_finish(FOLKS_NAME_DETAILS(persona), result, &error); break; case QContactDetail::TypeEmailAddress: folks_email_details_change_email_addresses_finish(FOLKS_EMAIL_DETAILS(persona), result, &error); break; case QContactDetail::TypeFavorite: folks_favourite_details_change_is_favourite_finish(FOLKS_FAVOURITE_DETAILS(persona), result, &error); break; case QContactDetail::TypeName: folks_name_details_change_structured_name_finish(FOLKS_NAME_DETAILS(persona), result, &error); break; case QContactDetail::TypeNickname: folks_name_details_change_nickname_finish(FOLKS_NAME_DETAILS(persona), result, &error); break; case QContactDetail::TypeNote: folks_note_details_change_notes_finish(FOLKS_NOTE_DETAILS(persona), result, &error); break; case QContactDetail::TypeOnlineAccount: folks_im_details_change_im_addresses_finish(FOLKS_IM_DETAILS(persona), result, &error); break; case QContactDetail::TypeOrganization: folks_role_details_change_roles_finish(FOLKS_ROLE_DETAILS(persona), result, &error); break; case QContactDetail::TypePhoneNumber: folks_phone_details_change_phone_numbers_finish(FOLKS_PHONE_DETAILS(persona), result, &error); break; case QContactDetail::TypeUrl: folks_url_details_change_urls_finish(FOLKS_URL_DETAILS(persona), result, &error); default: break; } QString errorMessage; if (error) { errorMessage = QString::fromUtf8(error->message); g_error_free(error); } return errorMessage; } void UpdateContactRequest::updateDetailsDone(GObject *detail, GAsyncResult *result, gpointer userdata) { UpdateContactRequest *self = static_cast(userdata); QString errorMessage; if (detail && result) { if (FOLKS_IS_PERSONA(detail)) { // This is a normal field update errorMessage = self->callDetailChangeFinish(static_cast(self->m_currentDetailType), FOLKS_PERSONA(detail), result); } if (!errorMessage.isEmpty()) { qWarning() << "Fail to update contact" << errorMessage; self->invokeSlot(errorMessage); return; } } self->m_currentDetailType += 1; switch(static_cast(self->m_currentDetailType)) { case QContactDetail::TypeAddress: self->updateAddress(); break; case QContactDetail::TypeAvatar: self->updateAvatar(); break; case QContactDetail::TypeBirthday: self->updateBirthday(); break; case QContactDetail::TypeDisplayLabel: self->updateFullName(QIndividual::displayName(self->m_newContact)); break; case QContactDetail::TypeEmailAddress: //WORKAROUND: Folks automatically add online accounts based on e-mail address // for example user@gmail.com will create a jabber account, and this causes some // confusions on the service during the update, because of that we first update // the online account and this will avoid problems with the automatic update ] // from folks self->updateOnlineAccount(); break; case QContactDetail::TypeExtendedDetail: self->updateExtendedDetails(); break; case QContactDetail::TypeFavorite: self->updateFavorite(); break; case QContactDetail::TypeName: self->updateName(); break; case QContactDetail::TypeNickname: self->updateNickname(); break; case QContactDetail::TypeNote: self->updateNote(); break; case QContactDetail::TypeOnlineAccount: //WORKAROUND: see TypeEmailAddress update clause self->updateEmail(); break; case QContactDetail::TypeOrganization: self->updateOrganization(); break; case QContactDetail::TypePhoneNumber: self->updatePhone(); break; case QContactDetail::TypeUrl: self->updateUrl(); break; case QContactDetail::TypeAnniversary: case QContactDetail::TypeFamily: case QContactDetail::TypeGender: case QContactDetail::TypeGeoLocation: case QContactDetail::TypeGlobalPresence: case QContactDetail::TypeHobby: case QContactDetail::TypeRingtone: case QContactDetail::TypeTag: case QContactDetail::TypeTimestamp: //TODO case QContactDetail::TypeGuid: case QContactDetail::TypeType: updateDetailsDone(0, 0, self); break; case QContactDetail::TypeVersion: g_object_unref(self->m_currentPersona); self->m_currentPersona = 0; self->updatePersona(); break; default: qWarning() << "Update not implemented for" << self->m_currentDetailType; updateDetailsDone(0, 0, self); break; } } } // namespace address-book-service-0.1.1+16.04.20151211.1/lib/addressbook.cpp0000644000015300001610000016300312632607673024002 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "addressbook.h" #include "addressbook-adaptor.h" #include "view.h" #include "contacts-map.h" #include "qindividual.h" #include "dirtycontact-notify.h" #include "e-source-ubuntu.h" #include "common/vcard-parser.h" #include #include #include #include #include #include // Ubuntu #include #include #include namespace C { #include } #define MESSAGING_MENU_SOURCE_ID "address-book-service" using namespace QtContacts; namespace { class CreateContactData { public: QDBusMessage m_message; QContact m_contact; galera::AddressBook *m_addressbook; }; class UpdateContactsData { public: QList m_contacts; QStringList m_request; int m_currentIndex; QStringList m_result; galera::AddressBook *m_addressbook; QDBusMessage m_message; }; class RemoveContactsData { public: QStringList m_request; galera::AddressBook *m_addressbook; QDBusMessage m_message; int m_sucessCount; bool m_softRemoval; }; class CreateSourceData { public: QString m_sourceId; QString m_sourceName; QString m_applicationId; QString m_providerName; uint m_accountId; bool m_setAsPrimary; galera::AddressBook *m_addressbook; QDBusMessage m_message; ESource *m_source; }; class UpdateSourceData { public: galera::SourceList m_toUpdate; galera::SourceList m_result; ESource *m_currentSource; ESourceRegistry *m_registry; galera::AddressBook *m_addressbook; QDBusMessage m_message; }; class RemoveSourceData { public: galera::AddressBook *m_addressbook; QDBusMessage m_message; }; ESource* create_esource_from_data(CreateSourceData &data, ESourceRegistry **registry) { GError *error = NULL; ESource *source = e_source_new_with_uid(data.m_sourceId.toUtf8().data(), NULL, &error); if (error) { qWarning() << "Fail to create source" << error->message; g_error_free(error); return 0; } e_source_set_parent(source, "contacts-stub"); e_source_set_display_name(source, data.m_sourceName.toUtf8().data()); if (data.m_accountId > 0) { ESourceUbuntu *ubuntu_ex = E_SOURCE_UBUNTU(e_source_get_extension(source, E_SOURCE_EXTENSION_UBUNTU)); e_source_ubuntu_set_account_id(ubuntu_ex, data.m_accountId); e_source_ubuntu_set_application_id(ubuntu_ex, data.m_applicationId.toUtf8()); e_source_ubuntu_set_autoremove(ubuntu_ex, TRUE); data.m_providerName = QString::fromUtf8(e_source_ubuntu_get_account_provider(ubuntu_ex)); } ESourceAddressBook *ext = E_SOURCE_ADDRESS_BOOK(e_source_get_extension(source, E_SOURCE_EXTENSION_ADDRESS_BOOK)); e_source_backend_set_backend_name(E_SOURCE_BACKEND(ext), "local"); *registry = e_source_registry_new_sync(NULL, &error); if (error) { qWarning() << "Fail to change default contact address book" << error->message; g_error_free(error); g_object_unref(source); return 0; } return source; } } namespace galera { int AddressBook::m_sigQuitFd[2] = {0, 0}; QSettings AddressBook::m_settings(SETTINGS_ORG, SETTINGS_APPLICATION); AddressBook::AddressBook(QObject *parent) : QObject(parent), m_individualAggregator(0), m_contacts(0), m_adaptor(0), m_notifyContactUpdate(0), m_edsIsLive(false), m_ready(false), m_isAboutToQuit(false), m_isAboutToReload(false), m_individualsChangedDetailedId(0), m_notifyIsQuiescentHandlerId(0), m_connection(QDBusConnection::sessionBus()), m_messagingMenu(0), m_messagingMenuMessage(0), m_sourceRegistryListener(0) { if (qEnvironmentVariableIsSet(ALTERNATIVE_CPIM_SERVICE_NAME)) { m_serviceName = qgetenv(ALTERNATIVE_CPIM_SERVICE_NAME); qDebug() << "Using alternative service name:" << m_serviceName; } else { m_serviceName = CPIM_SERVICE_NAME; } prepareUnixSignals(); connectWithEDS(); connect(this, SIGNAL(readyChanged()), SLOT(checkCompatibility())); connect(this, SIGNAL(safeModeChanged()), SLOT(onSafeModeChanged())); } AddressBook::~AddressBook() { if (m_sourceRegistryListener) { g_object_unref(m_sourceRegistryListener); m_sourceRegistryListener = 0; } if (m_messagingMenuMessage) { g_object_unref(m_messagingMenuMessage); m_messagingMenuMessage = 0; } if (m_messagingMenu) { g_object_unref(m_messagingMenu); m_messagingMenu = 0; } if (m_individualAggregator) { qWarning() << "Addressbook destructor called while running, you should call shutdown first"; shutdown(); while (m_adaptor) { QCoreApplication::processEvents(); } } if (m_notifyContactUpdate) { delete m_notifyContactUpdate; m_notifyContactUpdate = 0; } } QString AddressBook::objectPath() { return CPIM_ADDRESSBOOK_OBJECT_PATH; } bool AddressBook::registerObject(QDBusConnection &connection) { if (connection.interface()->isServiceRegistered(m_serviceName)) { qWarning() << "Galera pin service already registered"; return false; } else if (!connection.registerService(m_serviceName)) { qWarning() << "Could not register service!" << m_serviceName; return false; } if (!m_adaptor) { m_adaptor = new AddressBookAdaptor(connection, this); if (!connection.registerObject(galera::AddressBook::objectPath(), this)) { qWarning() << "Could not register object!" << objectPath(); delete m_adaptor; m_adaptor = 0; if (m_notifyContactUpdate) { delete m_notifyContactUpdate; m_notifyContactUpdate = 0; } } } if (m_adaptor) { m_notifyContactUpdate = new DirtyContactsNotify(m_adaptor); } return (m_adaptor != 0); } bool AddressBook::start(QDBusConnection connection) { if (registerObject(connection)) { m_connection = connection; prepareFolks(); return true; } return false; } bool AddressBook::start() { g_type_ensure (E_TYPE_SOURCE_UBUNTU); return start(QDBusConnection::sessionBus()); } void AddressBook::unprepareFolks() { // remove all contacts // flusing any pending notification m_notifyContactUpdate->flush(); setIsReady(false); Q_FOREACH(View* view, m_views) { view->close(); } m_views.clear(); if (m_contacts) { delete m_contacts; m_contacts = 0; } qDebug() << "Will destroy aggregator" << (void*) m_individualAggregator; if (m_individualAggregator) { g_signal_handler_disconnect(m_individualAggregator, m_individualsChangedDetailedId); g_signal_handler_disconnect(m_individualAggregator, m_notifyIsQuiescentHandlerId); m_individualsChangedDetailedId = m_notifyIsQuiescentHandlerId = 0; // make it sync qDebug() << "call unprepare"; folks_individual_aggregator_unprepare(m_individualAggregator, AddressBook::folksUnprepared, this); } } void AddressBook::checkCompatibility() { QByteArray envSafeMode = qgetenv(ADDRESS_BOOK_SAFE_MODE); if (!envSafeMode.isEmpty()) { return; } bool enableSafeMode = m_settings.value(SETTINGS_SAFE_MODE_KEY, true).toBool(); if (!enableSafeMode) { qDebug() << "Server marked as updated"; return; } GError *gError = NULL; ESourceRegistry *r = e_source_registry_new_sync(NULL, &gError); if (gError) { qWarning() << "Fail to check compatibility" << gError->message; g_error_free(gError); return; } enableSafeMode = false; GList *sources = e_source_registry_list_sources(r, E_SOURCE_EXTENSION_ADDRESS_BOOK); for(GList *l = sources; l != NULL; l = l->next) { ESource *s = E_SOURCE(l->data); if ((strcmp(e_source_get_uid(s), "system-address-book") != 0) && !e_source_has_extension(s, E_SOURCE_EXTENSION_UBUNTU)) { qDebug() << "Source does not contains UBUNTU extension" << QString::fromUtf8(e_source_get_display_name(s)); enableSafeMode = true; break; } } g_list_free_full(sources, g_object_unref); g_object_unref(r); if (enableSafeMode) { qWarning() << "Enabling safe mode"; setSafeMode(true); Q_EMIT safeModeChanged(); } else { qDebug() << "Safe mode not necessary"; } } void AddressBook::shutdown() { m_isAboutToQuit = true; unprepareFolks(); } void AddressBook::continueShutdown() { qDebug() << "Folks is not running anymore"; if (m_adaptor) { if (m_connection.interface() && m_connection.interface()->isValid()) { m_connection.unregisterObject(objectPath()); if (m_connection.interface()->isServiceRegistered(m_serviceName)) { m_connection.unregisterService(m_serviceName); } } delete m_adaptor; m_adaptor = 0; Q_EMIT stopped(); } } void AddressBook::setIsReady(bool isReady) { if (isReady != m_ready) { m_ready = isReady; if (m_adaptor) { Q_EMIT readyChanged(); } } } void AddressBook::prepareFolks() { qDebug() << "Initialize folks"; m_contacts = new ContactsMap; m_individualAggregator = folks_individual_aggregator_dup(); gboolean ready; g_object_get(G_OBJECT(m_individualAggregator), "is-quiescent", &ready, NULL); m_notifyIsQuiescentHandlerId = g_signal_connect(m_individualAggregator, "notify::is-quiescent", (GCallback) AddressBook::isQuiescentChanged, this); m_individualsChangedDetailedId = g_signal_connect(m_individualAggregator, "individuals-changed-detailed", (GCallback) AddressBook::individualsChangedCb, this); folks_individual_aggregator_prepare(m_individualAggregator, (GAsyncReadyCallback) AddressBook::prepareFolksDone, this); if (ready) { qDebug() << "Folks is already in quiescent mode"; setIsReady(ready); } } void AddressBook::unprepareEds() { FolksBackendStore *store = folks_backend_store_dup(); FolksBackend *edsBackend = folks_backend_store_dup_backend_by_name(store, "eds"); if (edsBackend && folks_backend_get_is_prepared(edsBackend)) { qDebug() << "WILL unprepare EDS"; folks_backend_unprepare(edsBackend, AddressBook::edsUnprepared, this); } else { qDebug() << "Eds not prepared will restart folks"; prepareFolks(); } } void AddressBook::connectWithEDS() { // we need to keep it update with the EDS dbus service name static const QString evolutionServiceName(EVOLUTION_ADDRESSBOOK_SERVICE_NAME); // Check if eds was disabled manually // If eds was disabled we should skip the check if (qEnvironmentVariableIsSet("FOLKS_BACKENDS_ALLOWED")) { QString allowedBackends = qgetenv("FOLKS_BACKENDS_ALLOWED"); if (!allowedBackends.contains("eds")) { m_edsIsLive = true; return; } } // connect with source registry to get notifications about source change GError *gError = NULL; if (m_sourceRegistryListener) { g_object_unref(m_sourceRegistryListener); m_sourceRegistryListener = 0; } m_sourceRegistryListener = e_source_registry_new_sync(NULL, &gError); if (gError) { qWarning() << "Fail to connect with source registry" << gError->message; g_error_free(gError); m_sourceRegistryListener = 0; } else { g_signal_connect(m_sourceRegistryListener, "source-added", G_CALLBACK(AddressBook::sourceEDSChanged), this); g_signal_connect(m_sourceRegistryListener, "source-changed", G_CALLBACK(AddressBook::sourceEDSChanged), this); g_signal_connect(m_sourceRegistryListener, "source-removed", G_CALLBACK(AddressBook::sourceEDSChanged), this); g_signal_connect(m_sourceRegistryListener, "source-enabled", G_CALLBACK(AddressBook::sourceEDSChanged), this); g_signal_connect(m_sourceRegistryListener, "source-disabled", G_CALLBACK(AddressBook::sourceEDSChanged), this); } // check if service is already registered // We will try register a EDS service if its fails this mean that the service is already registered m_edsIsLive = !QDBusConnection::sessionBus().registerService(evolutionServiceName); if (!m_edsIsLive) { // if we succeed we need to unregister it QDBusConnection::sessionBus().unregisterService(evolutionServiceName); } m_edsWatcher = new QDBusServiceWatcher(evolutionServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); connect(m_edsWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(onEdsServiceOwnerChanged(QString,QString,QString))); // WORKAROUND: Will ceck for EDS after the service get ready connect(this, SIGNAL(readyChanged()), SLOT(checkForEds())); } SourceList AddressBook::availableSources(const QDBusMessage &message) { getSource(message, false); return SourceList(); } Source AddressBook::source(const QDBusMessage &message) { getSource(message, true); return Source(); } Source AddressBook::createSource(const QString &sourceName, uint accountId, bool setAsPrimary, const QDBusMessage &message) { CreateSourceData *data = new CreateSourceData; data->m_addressbook = this; data->m_message = message; data->m_sourceName = sourceName; data->m_setAsPrimary = setAsPrimary; data->m_accountId = accountId; FolksPersonaStore *store = folks_individual_aggregator_get_primary_store(m_individualAggregator); QString personaStoreTypeId("dummy"); if (store) { personaStoreTypeId = QString::fromUtf8(folks_persona_store_get_type_id(store)); } if (personaStoreTypeId == "dummy") { FolksBackendStore *backendStore = folks_backend_store_dup(); FolksBackend *dummy = folks_backend_store_dup_backend_by_name(backendStore, "dummy"); GeeMap *stores = folks_backend_get_persona_stores(dummy); GeeSet *storesKeys = gee_map_get_keys(stores); GeeSet *storesIds = (GeeSet*) gee_hash_set_new(G_TYPE_STRING, (GBoxedCopyFunc) g_strdup, g_free, NULL, NULL, NULL, NULL, NULL, NULL); gee_collection_add_all(GEE_COLLECTION(storesIds), GEE_COLLECTION(storesKeys)); gee_collection_add(GEE_COLLECTION(storesIds), sourceName.toUtf8().constData()); folks_backend_set_persona_stores(dummy, storesIds); g_object_unref(storesIds); g_object_unref(backendStore); g_object_unref(dummy); Source src(sourceName, sourceName, QString(), QString(), data->m_accountId, false, false); QDBusMessage reply = message.createReply(QVariant::fromValue(src)); QDBusConnection::sessionBus().send(reply); } else if (personaStoreTypeId == "eds") { data->m_sourceId = QUuid::createUuid().toString().remove("{").remove("}"); ESourceRegistry *registry = NULL; ESource *source = create_esource_from_data(*data, ®istry); if (source) { data->m_source = source; e_source_registry_commit_source(registry, source, NULL, (GAsyncReadyCallback) AddressBook::createSourceDone, data); } else { delete data; QDBusMessage reply = message.createReply(QVariant::fromValue(Source())); QDBusConnection::sessionBus().send(reply); } } else { qWarning() << "Not supported, create sources on persona store with type id:" << personaStoreTypeId; delete data; QDBusMessage reply = message.createReply(QVariant::fromValue(Source())); QDBusConnection::sessionBus().send(reply); } return Source(); } SourceList AddressBook::updateSources(const SourceList &sources, const QDBusMessage &message) { FolksPersonaStore *store = folks_individual_aggregator_get_primary_store(m_individualAggregator); QString personaStoreTypeId("dummy"); if (store) { personaStoreTypeId = QString::fromUtf8(folks_persona_store_get_type_id(store)); } if (personaStoreTypeId == "eds") { UpdateSourceData *data = new UpdateSourceData; data->m_toUpdate = sources; data->m_registry = 0; data->m_message = message; data->m_addressbook = this; data->m_result = SourceList(); updateSourcesEDS(data); } else { qWarning() << "Not supported, update sources on persona store with type id:" << personaStoreTypeId; QDBusMessage reply = message.createReply(QVariant::fromValue(SourceList())); QDBusConnection::sessionBus().send(reply); } return SourceList(); } void AddressBook::updateSourcesEDS(void *data) { Source source; ESource *eSource = NULL; UpdateSourceData *uData = static_cast(data); if (uData->m_toUpdate.isEmpty()) { goto operation_done; } if (uData->m_registry == 0) { GError *gError = 0; uData->m_registry = e_source_registry_new_sync(NULL, &gError); if (gError) { qWarning() << "Fail to create source registry" << gError->message; g_error_free(gError); goto operation_done; } } source = uData->m_toUpdate.takeFirst(); eSource = e_source_registry_ref_source(uData->m_registry, source.id().toUtf8().data()); if (eSource) { // set as primary if necessary if (source.isPrimary()) { e_source_registry_set_default_address_book(uData->m_registry, eSource); } e_source_set_display_name(eSource, source.displayLabel().toUtf8().data()); if (source.accountId() > 0) { ESourceUbuntu *ubuntu_ex = E_SOURCE_UBUNTU(e_source_get_extension(eSource, E_SOURCE_EXTENSION_UBUNTU)); e_source_ubuntu_set_account_id(ubuntu_ex, source.accountId()); e_source_ubuntu_set_application_id(ubuntu_ex, source.applicationId().toUtf8().data()); } uData->m_currentSource = eSource; e_source_registry_commit_source(uData->m_registry, eSource, NULL, (GAsyncReadyCallback) AddressBook::updateSourceEDSDone, data); } else { // next source updateSourcesEDS(data); } return; operation_done: SourceList result(uData->m_result); QDBusMessage reply = uData->m_message.createReply(QVariant::fromValue(result)); QDBusConnection::sessionBus().send(reply); if (uData->m_registry) { g_object_unref (uData->m_registry); } delete uData; } void AddressBook::updateSourceEDSDone(GObject *registry, GAsyncResult *res, void *data) { UpdateSourceData *uData = static_cast(data); GError *error = 0; e_source_registry_commit_source_finish(E_SOURCE_REGISTRY(registry), res, &error); if (error) { qWarning() << "Failed to update source" << error->message; g_error_free(error); } else { uData->m_result.append(parseEDSSource(uData->m_registry, uData->m_currentSource)); } g_object_unref(uData->m_currentSource); uData->m_addressbook->updateSourcesEDS(data); } void AddressBook::sourceEDSChanged(ESourceRegistry *registry, ESource *source, AddressBook *self) { Q_EMIT self->sourcesChanged(); } void AddressBook::removeSource(const QString &sourceId, const QDBusMessage &message) { FolksBackendStore *bs = folks_backend_store_dup(); FolksBackend *backend = folks_backend_store_dup_backend_by_name(bs, "eds"); bool error = false; if (backend) { GeeMap *storesMap = folks_backend_get_persona_stores(backend); GeeCollection *stores = gee_map_get_values(storesMap); GeeIterator *i = gee_iterable_iterator(GEE_ITERABLE(stores)); RemoveSourceData *rData = 0; while (gee_iterator_next(i)) { FolksPersonaStore *ps = FOLKS_PERSONA_STORE(gee_iterator_get(i)); if (g_strcmp0(folks_persona_store_get_id(ps), sourceId.toUtf8().constData()) == 0) { ESource *src = edsf_persona_store_get_source(EDSF_PERSONA_STORE(ps)); if (src) { e_source_set_enabled(src, FALSE); rData = new RemoveSourceData; rData->m_addressbook = this; rData->m_message = message; e_source_write(src, NULL, AddressBook::removeSourceDone, rData); } g_object_unref(ps); break; } g_object_unref(ps); } g_object_unref(backend); g_object_unref(stores); if (!rData) { qWarning() << "Source not found to remove:" << sourceId; error = true; } } else { qWarning() << "Fail to create eds backend during the source removal:" << sourceId; error = true; } g_object_unref(bs); if (error) { QDBusMessage reply = message.createReply(false); QDBusConnection::sessionBus().send(reply); } } void AddressBook::removeSourceDone(GObject *source, GAsyncResult *res, void *data) { GError *error = 0; bool result = true; e_source_write_finish(E_SOURCE(source), res, &error); if (error) { qWarning() << "Fail to remove source" << error->message; g_error_free(error); result = false; } RemoveSourceData *rData = static_cast(data); QDBusMessage reply = rData->m_message.createReply(result); QDBusConnection::sessionBus().send(reply); delete rData; } void AddressBook::folksUnprepared(GObject *source, GAsyncResult *res, void *data) { AddressBook *self = static_cast(data); GError *error = NULL; folks_individual_aggregator_unprepare_finish(FOLKS_INDIVIDUAL_AGGREGATOR(source), res, &error); if (error) { qWarning() << "Fail to unprepare folks:" << error->message; g_error_free(error); } g_clear_object(&self->m_individualAggregator); qDebug() << "Folks unprepared" << (void*) self->m_individualAggregator; if (self->m_isAboutToQuit) { self->continueShutdown(); } else { self->unprepareEds(); } } void AddressBook::edsUnprepared(GObject *source, GAsyncResult *res, void *data) { GError *error = NULL; folks_backend_unprepare_finish(FOLKS_BACKEND(source), res, &error); if (error) { qWarning() << "Fail to unprepare eds:" << error->message; g_error_free(error); } qDebug() << "EDS unprepared"; folks_backend_prepare(FOLKS_BACKEND(source), AddressBook::edsPrepared, data); } void AddressBook::edsPrepared(GObject *source, GAsyncResult *res, void *data) { AddressBook *self = static_cast(data); GError *error = NULL; folks_backend_prepare_finish(FOLKS_BACKEND(source), res, &error); if (error) { qWarning() << "Fail to prepare eds:" << error->message; g_error_free(error); } // remove reference created by parent function g_object_unref(source); // will start folks again self->prepareFolks(); } void AddressBook::onSafeModeMessageActivated(MessagingMenuMessage *message, const char *actionId, GVariant *param, AddressBook *self) { if (self->m_messagingMenu) { if (self->m_messagingMenuMessage) { messaging_menu_app_remove_message(self->m_messagingMenu, self->m_messagingMenuMessage); g_object_unref(self->m_messagingMenuMessage); self->m_messagingMenuMessage = 0; } messaging_menu_app_unregister(self->m_messagingMenu); g_object_unref(self->m_messagingMenu); self->m_messagingMenu = 0; } url_dispatch_send("application:///address-book-app.desktop", NULL, NULL); } Source AddressBook::parseEDSSource(ESourceRegistry *registry, ESource *eSource) { if (eSource) { guint accountId; QString applicationId; QString providerName; // ubuntu extension info if (e_source_has_extension(eSource, E_SOURCE_EXTENSION_UBUNTU)) { ESourceUbuntu *ubuntu_ex = E_SOURCE_UBUNTU(e_source_get_extension(eSource, E_SOURCE_EXTENSION_UBUNTU)); accountId = e_source_ubuntu_get_account_id(ubuntu_ex); applicationId = QString::fromUtf8(e_source_ubuntu_get_application_id(ubuntu_ex)); providerName = QString::fromUtf8(e_source_ubuntu_get_account_provider(ubuntu_ex)); } // check primary ESource *defaultAddressBook = e_source_registry_ref_default_address_book(registry); bool isPrimary = e_source_equal(defaultAddressBook, eSource); g_object_unref (defaultAddressBook); return Source(QString::fromUtf8(e_source_get_uid(eSource)), QString::fromUtf8(e_source_get_display_name(eSource)), applicationId, providerName, accountId, !e_source_get_writable(eSource), isPrimary); } return Source(); } bool AddressBook::isSafeMode() { QByteArray envSafeMode = qgetenv(ADDRESS_BOOK_SAFE_MODE); if (!envSafeMode.isEmpty()) { return (envSafeMode.toLower() == "on" ? true : false); } else { return m_settings.value(SETTINGS_SAFE_MODE_KEY, false).toBool(); } } void AddressBook::setSafeMode(bool flag) { QByteArray envSafeMode = qgetenv(ADDRESS_BOOK_SAFE_MODE); if (!envSafeMode.isEmpty()) { return; } if (m_settings.value(SETTINGS_SAFE_MODE_KEY, false).toBool() != flag) { m_settings.setValue(SETTINGS_SAFE_MODE_KEY, flag); if (!flag) { // make all contacts visible Q_FOREACH(ContactEntry *entry, m_contacts->values()) { QIndividual *i = entry->individual(); if (!i->isVisible()) { i->setVisible(true); } } // clear invisible sources list m_settings.setValue(SETTINGS_INVISIBLE_SOURCES, QStringList()); } m_settings.sync(); // avoid send a ton of signals since the service will be reseted after the // 'safeModeChanged' signal m_notifyContactUpdate->clear(); Q_EMIT safeModeChanged(); } } void AddressBook::createSourceDone(GObject *source, GAsyncResult *res, void *data) { CreateSourceData *cData = static_cast(data); GError *error = 0; Source src; e_source_registry_commit_source_finish(E_SOURCE_REGISTRY(source), res, &error); if (error) { qWarning() << "Failed to create source" << error->message; g_error_free(error); } else { // set as primary if necessary if (cData->m_setAsPrimary) { e_source_registry_set_default_address_book(E_SOURCE_REGISTRY(source), cData->m_source); } src = Source(cData->m_sourceId, cData->m_sourceName, cData->m_applicationId, cData->m_providerName, cData->m_accountId, false, cData->m_setAsPrimary); // if in safe mode source will be invisible, we use that to avoid invalid states if (isSafeMode()) { qDebug() << "Source will be invisible until safe mode is gone" << cData->m_sourceId << e_source_get_uid(cData->m_source); QStringList iSources = cData->m_addressbook->m_settings.value(SETTINGS_INVISIBLE_SOURCES).toStringList(); iSources << e_source_get_uid(cData->m_source); cData->m_addressbook->m_settings.setValue(SETTINGS_INVISIBLE_SOURCES, iSources); cData->m_addressbook->m_settings.sync(); } } g_object_unref(source); QDBusMessage reply = cData->m_message.createReply(QVariant::fromValue(src)); QDBusConnection::sessionBus().send(reply); delete cData; } void AddressBook::getSource(const QDBusMessage &message, bool onlyTheDefault) { FolksBackendStore *backendStore = folks_backend_store_dup(); QDBusMessage *msg = new QDBusMessage(message); if (folks_backend_store_get_is_prepared(backendStore)) { if (onlyTheDefault) { availableSourcesDoneListDefaultSource(backendStore, 0, msg); } else { availableSourcesDoneListAllSources(backendStore, 0, msg); } } else { if (onlyTheDefault) { folks_backend_store_prepare(backendStore, (GAsyncReadyCallback) availableSourcesDoneListDefaultSource, msg); } else { folks_backend_store_prepare(backendStore, (GAsyncReadyCallback) availableSourcesDoneListAllSources, msg); } } g_object_unref(backendStore); } void AddressBook::availableSourcesDoneListAllSources(FolksBackendStore *backendStore, GAsyncResult *res, QDBusMessage *msg) { SourceList list = availableSourcesDoneImpl(backendStore, res); QDBusMessage reply = msg->createReply(QVariant::fromValue(list)); QDBusConnection::sessionBus().send(reply); delete msg; } void AddressBook::availableSourcesDoneListDefaultSource(FolksBackendStore *backendStore, GAsyncResult *res, QDBusMessage *msg) { Source defaultSource; SourceList list = availableSourcesDoneImpl(backendStore, res); if (list.count() > 0) { defaultSource = list.first(); } QDBusMessage reply = msg->createReply(QVariant::fromValue(defaultSource)); QDBusConnection::sessionBus().send(reply); delete msg; } SourceList AddressBook::availableSourcesDoneImpl(FolksBackendStore *backendStore, GAsyncResult *res) { if (res) { folks_backend_store_prepare_finish(backendStore, res); } static QStringList backendBlackList; // these backends are not fully supported yet if (backendBlackList.isEmpty()) { backendBlackList << "telepathy" << "bluez" << "ofono" << "key-file"; } GeeCollection *backends = folks_backend_store_list_backends(backendStore); SourceList result; GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(backends)); while(gee_iterator_next(iter)) { FolksBackend *backend = FOLKS_BACKEND(gee_iterator_get(iter)); QString backendName = QString::fromUtf8(folks_backend_get_name(backend)); if (backendBlackList.contains(backendName)) { continue; } GeeMap *stores = folks_backend_get_persona_stores(backend); GeeCollection *values = gee_map_get_values(stores); GeeIterator *backendIter = gee_iterable_iterator(GEE_ITERABLE(values)); while(gee_iterator_next(backendIter)) { FolksPersonaStore *store = FOLKS_PERSONA_STORE(gee_iterator_get(backendIter)); QString id = QString::fromUtf8(folks_persona_store_get_id(store)); QString displayName = folks_persona_store_get_display_name(store); bool canWrite = folks_persona_store_get_can_add_personas(store) && folks_persona_store_get_can_remove_personas(store); bool isPrimary = folks_persona_store_get_is_primary_store(store); uint accountId = 0; QString applicationId; QString providerName; // FIXME: Due a bug on Folks we can not rely on folks_persona_store_get_is_primary_store // see main.cpp:68 if (strcmp(folks_backend_get_name(backend), "eds") == 0) { GError *error = 0; ESourceRegistry *r = e_source_registry_new_sync(NULL, &error); if (error) { qWarning() << "Failt to check default source:" << error->message; g_error_free(error); } else { ESource *defaultSource = e_source_registry_ref_default_address_book(r); ESource *source = edsf_persona_store_get_source(EDSF_PERSONA_STORE(store)); displayName = QString::fromUtf8(e_source_get_display_name(source)); isPrimary = e_source_equal(defaultSource, source); g_object_unref(defaultSource); g_object_unref(r); if (e_source_has_extension(source, E_SOURCE_EXTENSION_UBUNTU)) { ESourceUbuntu *ubuntu_ex = E_SOURCE_UBUNTU(e_source_get_extension(source, E_SOURCE_EXTENSION_UBUNTU)); if (ubuntu_ex) { applicationId = QString::fromUtf8(e_source_ubuntu_get_application_id(ubuntu_ex)); providerName = QString::fromUtf8(e_source_ubuntu_get_account_provider(ubuntu_ex)); accountId = e_source_ubuntu_get_account_id(ubuntu_ex); } } else { qDebug() << "SOURCE DOES NOT HAVE UBUNTU EXTENSION:" << displayName; } } } // If running on safe mode only the system-address-book is writable if (isSafeMode() && (id != "system-address-book")) { qDebug() << "Running safe mode for source" << id << displayName; canWrite = false; } result.append(Source(id, displayName, applicationId, providerName, accountId, !canWrite, isPrimary)); g_object_unref(store); } g_object_unref(backendIter); g_object_unref(backend); g_object_unref(values); } g_object_unref(iter); return result; } QString AddressBook::createContact(const QString &contact, const QString &source, const QDBusMessage &message) { ContactEntry *entry = m_contacts->valueFromVCard(contact); if (entry) { qWarning() << "Contact exists"; } else { QContact qcontact = VCardParser::vcardToContact(contact); if (!qcontact.isEmpty()) { GHashTable *details = QIndividual::parseDetails(qcontact); Q_ASSERT(details); CreateContactData *data = new CreateContactData; data->m_message = message; data->m_addressbook = this; data->m_contact = qcontact; FolksPersonaStore *store = getFolksStore(source); folks_individual_aggregator_add_persona_from_details(m_individualAggregator, NULL, //parent store, details, (GAsyncReadyCallback) createContactDone, (void*) data); g_hash_table_destroy(details); g_object_unref(store); return ""; } } if (message.type() != QDBusMessage::InvalidMessage) { QDBusMessage reply = message.createReply(QString()); QDBusConnection::sessionBus().send(reply); } return ""; } FolksPersonaStore * AddressBook::getFolksStore(const QString &source) { QString sourceId(source); FolksPersonaStore *result = 0; // if source is empty we try use EDS default source if (source.isEmpty()) { GError *gError = NULL; ESourceRegistry *registry = e_source_registry_new_sync (NULL, &gError); if (gError) { qWarning() << "Fail to find EDS default souce"; } else { ESource *defaultAB = e_source_registry_ref_default_address_book(registry); if (defaultAB) { sourceId = QString::fromUtf8(e_source_get_uid(defaultAB)); } g_object_unref(registry); } } if (!sourceId.isEmpty()) { FolksBackendStore *backendStore = folks_backend_store_dup(); GeeCollection *backends = folks_backend_store_list_backends(backendStore); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(backends)); while((result == 0) && gee_iterator_next(iter)) { FolksBackend *backend = FOLKS_BACKEND(gee_iterator_get(iter)); GeeMap *stores = folks_backend_get_persona_stores(backend); GeeCollection *values = gee_map_get_values(stores); GeeIterator *storeIter = gee_iterable_iterator(GEE_ITERABLE(values)); while(gee_iterator_next(storeIter)) { FolksPersonaStore *store = FOLKS_PERSONA_STORE(gee_iterator_get(storeIter)); QString id = QString::fromUtf8(folks_persona_store_get_id(store)); if (id == sourceId) { result = store; break; } g_object_unref(store); } g_object_unref(storeIter); g_object_unref(backend); g_object_unref(values); } g_object_unref(iter); g_object_unref(backendStore); } if (!result) { result = folks_individual_aggregator_get_primary_store(m_individualAggregator); Q_ASSERT(result); g_object_ref(result); } return result; } QString AddressBook::linkContacts(const QStringList &contacts) { //TODO return ""; } View *AddressBook::query(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources) { View *view = new View(clause, sort, maxCount, showInvisible, sources, m_ready ? m_contacts : 0, this); m_views << view; connect(view, SIGNAL(closed()), this, SLOT(viewClosed())); return view; } void AddressBook::viewClosed() { m_views.remove(qobject_cast(QObject::sender())); } void AddressBook::individualChanged(QIndividual *individual) { if (individual->isVisible()) { m_notifyContactUpdate->insertChangedContacts(QSet() << individual->id()); } } void AddressBook::onEdsServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { if (newOwner.isEmpty()) { m_edsIsLive = false; m_isAboutToReload = true; qWarning() << "EDS died: restarting service" << m_individualsChangedDetailedId; unprepareFolks(); } else { m_edsIsLive = true; } } void AddressBook::onSafeModeChanged() { GIcon *icon = g_themed_icon_new("address-book-app"); if (m_messagingMenu == 0) { m_messagingMenu = messaging_menu_app_new("address-book-app.desktop"); messaging_menu_app_register(m_messagingMenu); messaging_menu_app_append_source(m_messagingMenu, MESSAGING_MENU_SOURCE_ID, icon, C::gettext("Address book service")); } if (m_messagingMenuMessage) { messaging_menu_app_remove_message(m_messagingMenu, m_messagingMenuMessage); g_object_unref (m_messagingMenuMessage); m_messagingMenuMessage = 0; } if (isSafeMode()) { m_messagingMenuMessage = messaging_menu_message_new("address-book-service-safe-mode", icon, C::gettext("Update required"), NULL, C::gettext("Only local contacts will be editable until the contact sync upgrade is complete."), QDateTime::currentMSecsSinceEpoch() * 1000); // the value is expected to be in microseconds } else { m_messagingMenuMessage = messaging_menu_message_new("address-book-service-safe-mode", icon, C::gettext("Update complete"), NULL, C::gettext("Your Contact sync upgrade is complete."), QDateTime::currentMSecsSinceEpoch() * 1000); // the value is expected to be in microseconds } g_signal_connect(m_messagingMenuMessage, "activate", G_CALLBACK(&AddressBook::onSafeModeMessageActivated), this); messaging_menu_app_append_message(m_messagingMenu, m_messagingMenuMessage, MESSAGING_MENU_SOURCE_ID, true); g_object_unref(icon); } int AddressBook::removeContacts(const QStringList &contactIds, const QDBusMessage &message) { RemoveContactsData *data = new RemoveContactsData; data->m_addressbook = this; data->m_message = message; data->m_request = contactIds; data->m_sucessCount = 0; data->m_softRemoval = true; removeContactDone(0, 0, data); return 0; } void AddressBook::removeContactDone(FolksIndividualAggregator *individualAggregator, GAsyncResult *result, void *data) { GError *error = 0; RemoveContactsData *removeData = static_cast(data); if (result) { folks_individual_aggregator_remove_individual_finish(individualAggregator, result, &error); if (error) { qWarning() << "Fail to remove contact:" << error->message; g_error_free(error); } else { removeData->m_sucessCount++; } } if (!removeData->m_request.isEmpty()) { QString contactId = removeData->m_request.takeFirst(); ContactEntry *entry = removeData->m_addressbook->m_contacts->value(contactId); if (entry) { if (removeData->m_softRemoval && entry->individual()->markAsDeleted()) { removeContactDone(individualAggregator, 0, data); // since this will not be removed we need to send a removal singal removeData->m_addressbook->m_notifyContactUpdate->insertRemovedContacts(QSet() << entry->individual()->id()); } else { folks_individual_aggregator_remove_individual(individualAggregator, entry->individual()->individual(), (GAsyncReadyCallback) removeContactDone, data); } } else { removeContactDone(individualAggregator, 0, data); } } else { QDBusMessage reply = removeData->m_message.createReply(removeData->m_sucessCount); QDBusConnection::sessionBus().send(reply); delete removeData; } } QStringList AddressBook::sortFields() { return SortClause::supportedFields(); } bool AddressBook::unlinkContacts(const QString &parent, const QStringList &contacts) { //TODO return false; } bool AddressBook::isReady() const { return m_ready && m_edsIsLive; } QStringList AddressBook::updateContacts(const QStringList &contacts, const QDBusMessage &message) { //TODO: support multiple update contacts calls Q_ASSERT(m_updateCommandPendingContacts.isEmpty()); if (!processUpdates()) { qWarning() << "Fail to process pending updates"; QDBusMessage reply = m_updateCommandReplyMessage.createReply(QStringList()); QDBusConnection::sessionBus().send(reply); return QStringList(); } m_updatedIds.clear(); m_updateCommandReplyMessage = message; m_updateCommandResult = contacts; m_updateCommandPendingContacts = contacts; updateContactsDone("", ""); return QStringList(); } void AddressBook::purgeContacts(const QDateTime &since, const QString &sourceId, const QDBusMessage &message) { RemoveContactsData *data = new RemoveContactsData; data->m_addressbook = this; data->m_message = message; data->m_sucessCount = 0; data->m_softRemoval = false; Q_FOREACH(const ContactEntry *entry, m_contacts->values()) { if (entry->individual()->deletedAt() > since) { QContactSyncTarget syncTarget = entry->individual()->contact().detail(); if (syncTarget.value(QContactSyncTarget::FieldSyncTarget + 1).toString() == sourceId) { data->m_request << entry->individual()->id(); } } } removeContactDone(0, 0, data); } void AddressBook::updateContactsDone(const QString &contactId, const QString &error) { int currentContactIndex = m_updateCommandResult.size() - m_updateCommandPendingContacts.size() - 1; if (!error.isEmpty()) { // update the result with the error if (currentContactIndex >= 0 && currentContactIndex < m_updateCommandResult.size()) { m_updateCommandResult[currentContactIndex] = error; } else { qWarning() << "Invalid contact changed index" << currentContactIndex << "Contact list size" << m_updateCommandResult.size(); } } else if (!contactId.isEmpty()){ // update the result with the new contact info ContactEntry *entry = m_contacts->value(contactId); Q_ASSERT(entry); m_updatedIds << contactId; QContact contact = entry->individual()->contact(); QString vcard = VCardParser::contactToVcard(contact); if (!vcard.isEmpty()) { m_updateCommandResult[currentContactIndex] = vcard; } else { m_updateCommandResult[currentContactIndex] = ""; } // update contact position on map m_contacts->updatePosition(entry); } if (!m_updateCommandPendingContacts.isEmpty()) { QString vCard = m_updateCommandPendingContacts.takeFirst(); QContact newContact = VCardParser::vcardToContact(vCard); ContactEntry *entry = m_contacts->value(newContact.detail().guid()); if (entry) { entry->individual()->update(newContact, this, SLOT(updateContactsDone(QString,QString))); } else { qWarning() << "Contact not found for update:" << vCard; updateContactsDone("", "Contact not found!"); } } else { QDBusMessage reply = m_updateCommandReplyMessage.createReply(m_updateCommandResult); QDBusConnection::sessionBus().send(reply); // notify about the changes m_notifyContactUpdate->insertChangedContacts(m_updatedIds.toSet()); // clear command data m_updatedIds.clear(); m_updateCommandResult.clear(); m_updateCommandReplyMessage = QDBusMessage(); m_updateLock.unlock(); } } QString AddressBook::removeContact(FolksIndividual *individual, bool *visible) { QString contactId = QString::fromUtf8(folks_individual_get_id(individual)); ContactEntry *ci = m_contacts->take(contactId); if (ci) { *visible = ci->individual()->isVisible(); delete ci; return contactId; } return QString(); } QString AddressBook::addContact(FolksIndividual *individual, bool visible) { QString id = QString::fromUtf8(folks_individual_get_id(individual)); ContactEntry *entry = m_contacts->value(id); if (entry) { entry->individual()->setIndividual(individual); entry->individual()->setVisible(visible); // update contact position on map m_contacts->updatePosition(entry); } else { QIndividual *i = new QIndividual(individual, m_individualAggregator); i->addListener(this, SLOT(individualChanged(QIndividual*))); i->setVisible(visible); m_contacts->insert(new ContactEntry(i)); //TODO: Notify view } return id; } void AddressBook::individualsChangedCb(FolksIndividualAggregator *individualAggregator, GeeMultiMap *changes, AddressBook *self) { Q_UNUSED(individualAggregator); QSet removedIds; QSet addedIds; QSet updatedIds; QStringList invisibleSources; if (isSafeMode()) { invisibleSources = self->m_settings.value(SETTINGS_INVISIBLE_SOURCES).toStringList(); } GeeSet *removed = gee_multi_map_get_keys(changes); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(removed)); while(gee_iterator_next(iter)) { FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter)); if (!individual) { continue; } bool visible = true; QString cId = self->removeContact(individual, &visible); if (visible && !cId.isEmpty()) { removedIds << cId; } g_object_unref(individual); } g_object_unref(iter); GeeCollection *added = gee_multi_map_get_values(changes); iter = gee_iterable_iterator(GEE_ITERABLE(added)); while(gee_iterator_next(iter)) { FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter)); if (!individual) { continue; } QString id = QString::fromUtf8(folks_individual_get_id(individual)); if (addedIds.contains(id)) { g_object_unref(individual); continue; } bool visible = true; if (!invisibleSources.isEmpty()) { GeeSet *personas = folks_individual_get_personas(individual); GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas)); if (gee_iterator_next(iter)) { FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter)); FolksPersonaStore *ps = folks_persona_get_store(persona); g_object_unref(persona); visible = !invisibleSources.contains(folks_persona_store_get_id(ps)); } g_object_unref(iter); } bool exists = self->m_contacts->contains(id); QString cId = self->addContact(individual, visible); if (visible && exists) { updatedIds << cId; } else if (visible) { addedIds << cId; } g_object_unref(individual); } g_object_unref(iter); g_object_unref(removed); g_object_unref(added); if (!removedIds.isEmpty()) { self->m_notifyContactUpdate->insertRemovedContacts(removedIds); } if (!addedIds.isEmpty()) { self->m_notifyContactUpdate->insertAddedContacts(addedIds); } if (!updatedIds.isEmpty()) { self->m_notifyContactUpdate->insertChangedContacts(updatedIds); } } void AddressBook::prepareFolksDone(GObject *source, GAsyncResult *res, AddressBook *self) { Q_UNUSED(source); Q_UNUSED(res); Q_UNUSED(self); } void AddressBook::createContactDone(FolksIndividualAggregator *individualAggregator, GAsyncResult *res, void *data) { CreateContactData *createData = static_cast(data); FolksPersona *persona; GError *error = NULL; QDBusMessage reply; persona = folks_individual_aggregator_add_persona_from_details_finish(individualAggregator, res, &error); if (error != NULL) { qWarning() << "Failed to create individual from contact:" << error->message; reply = createData->m_message.createErrorReply("Failed to create individual from contact", error->message); g_clear_error(&error); } else if (persona == NULL) { qWarning() << "Failed to create individual from contact: Persona already exists"; reply = createData->m_message.createErrorReply("Failed to create individual from contact", "Contact already exists"); } else { QIndividual::setExtendedDetails(persona, createData->m_contact.details(QContactExtendedDetail::Type), QDateTime::currentDateTime()); FolksIndividual *individual = folks_persona_get_individual(persona); ContactEntry *entry = createData->m_addressbook->m_contacts->value(QString::fromUtf8(folks_individual_get_id(individual))); if (entry) { // We will need to reload contact due the extended details entry->individual()->flush(); QString vcard = VCardParser::contactToVcard(entry->individual()->contact()); if (createData->m_message.type() != QDBusMessage::InvalidMessage) { reply = createData->m_message.createReply(vcard); } } else if (createData->m_message.type() != QDBusMessage::InvalidMessage) { reply = createData->m_message.createErrorReply("", "Failed to retrieve the new contact"); } } //TODO: use dbus connection if (createData->m_message.type() != QDBusMessage::InvalidMessage) { QDBusConnection::sessionBus().send(reply); } delete createData; } void AddressBook::isQuiescentChanged(GObject *source, GParamSpec *param, AddressBook *self) { Q_UNUSED(param); gboolean ready = false; g_object_get(source, "is-quiescent", &ready, NULL); if (self) { self->setIsReady(ready); } } void AddressBook::quitSignalHandler(int) { char a = 1; ::write(m_sigQuitFd[0], &a, sizeof(a)); } bool AddressBook::processUpdates() { int timeout = 10; while(!m_updateLock.tryLock(1000)) { if (timeout <= 0) { return false; } QCoreApplication::processEvents(); timeout--; } return true; } int AddressBook::init() { struct sigaction quit = { { 0 } }; Source::registerMetaType(); quit.sa_handler = AddressBook::quitSignalHandler; sigemptyset(&quit.sa_mask); quit.sa_flags |= SA_RESTART; if (sigaction(SIGQUIT, &quit, 0) > 0) return 1; return 0; } void AddressBook::prepareUnixSignals() { if (::socketpair(AF_UNIX, SOCK_STREAM, 0, m_sigQuitFd)) { qFatal("Couldn't create HUP socketpair"); } m_snQuit = new QSocketNotifier(m_sigQuitFd[1], QSocketNotifier::Read, this); connect(m_snQuit, SIGNAL(activated(int)), this, SLOT(handleSigQuit())); } void AddressBook::handleSigQuit() { m_snQuit->setEnabled(false); char tmp; ::read(m_sigQuitFd[1], &tmp, sizeof(tmp)); shutdown(); m_snQuit->setEnabled(true); } // WORKAROUND: For some strange reason sometimes EDS does not start with the service request // we will try to reload folks if this happen void AddressBook::checkForEds() { if (!m_ready) { return; } // Use maxRetry value to avoid infinite loop static const int maxRetry = 10; static int retryCount = 0; qDebug() << "Check for EDS attempt number " << retryCount; if (retryCount >= maxRetry) { // abort when reach the maxRetry qWarning() << QDateTime::currentDateTime().toString() << "Fail to start EDS the service will abort"; QTimer::singleShot(500, this, SLOT(shutdown())); return; } retryCount++; if (!m_edsIsLive) { // wait some ms to restart folks, this increase 1s for each retryCount m_isAboutToReload = true; QTimer::singleShot(1000 * retryCount, this, SLOT(unprepareFolks())); qWarning() << QDateTime::currentDateTime().toString() << "EDS did not start, trying to reload folks"; } else { retryCount = 0; } } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/contact-less-than.h0000644000015300001610000000260112632607666024474 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_CONTACT_LESS_THAN_H__ #define __GALERA_CONTACT_LESS_THAN_H__ #include "common/sort-clause.h" #include #include #include namespace galera { class ContactEntry; class ContactLessThan { public: ContactLessThan(const SortClause &sortClause); bool operator()(const QtContacts::QContact &contactA, const QtContacts::QContact &contactB); private: SortClause m_sortClause; }; class ContactEntryLessThan { public: ContactEntryLessThan(const SortClause &sortClause); bool operator()(ContactEntry *entryA, ContactEntry *entryB); private: SortClause m_sortClause; }; } // namespace #endif //__GALERA_CONTACT_LESS_THAN_H__ address-book-service-0.1.1+16.04.20151211.1/lib/detail-context-parser.h0000644000015300001610000000417712632607666025375 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_DETAIL_CONTEXT_PARSER_H__ #define __GALERA_DETAIL_CONTEXT_PARSER_H__ #include #include #include namespace galera { class DetailContextParser { public: static void parseContext(FolksAbstractFieldDetails *fd, const QtContacts::QContactDetail &detail, bool isPreffered); static QStringList parseContext(const QtContacts::QContactDetail &detail); static QStringList listContext(const QtContacts::QContactDetail &detail); static QStringList parsePhoneContext(const QtContacts::QContactDetail &detail); static QStringList parseAddressContext(const QtContacts::QContactDetail &detail); static QStringList parseOnlineAccountContext(const QtContacts::QContactDetail &im); static QString accountProtocolName(int protocol); static QStringList listParameters(FolksAbstractFieldDetails *details); static void parseParameters(QtContacts::QContactDetail &detail, FolksAbstractFieldDetails *fd, bool *isPref); static QList contextsFromParameters(QStringList *parameters); static void parsePhoneParameters(QtContacts::QContactDetail &phone, const QStringList ¶ms); static void parseAddressParameters(QtContacts::QContactDetail &address, const QStringList ¶meters); static int accountProtocolFromString(const QString &protocol); static void parseOnlineAccountParameters(QtContacts::QContactDetail &im, const QStringList ¶meters); }; } #endif address-book-service-0.1.1+16.04.20151211.1/lib/update-contact-request.h0000644000015300001610000001037212632607666025552 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_UPDATE_CONTACT_REQUEST_H__ #define __GALERA_UPDATE_CONTACT_REQUEST_H__ #include #include #include #include #include #include #include #include namespace galera { class QIndividual; template bool sortDetails(const QtContacts::QContactDetail &d1, const QtContacts::QContactDetail &d2) { return d1.value(0) < d2.value(0); } class UpdateContactRequest : public QObject { Q_OBJECT public: UpdateContactRequest(QtContacts::QContact newContact, QIndividual *parent, QObject *listener, const char *slot); ~UpdateContactRequest(); void start(); void wait(); void deatach(); void notifyError(const QString &errorMessage); Q_SIGNALS: void done(const QString &errorMessage); private: QIndividual *m_parent; QObject *m_object; FolksPersona *m_currentPersona; QEventLoop *m_eventLoop; QList m_personas; QtContacts::QContact m_originalContact; QtContacts::QContact m_newContact; int m_currentDetailType; QMetaMethod m_slot; int m_currentPersonaIndex; void invokeSlot(const QString &errorMessage = QString()); static bool isEqual(QList listA, const QtContacts::QContactDetail &prefA, QList listB, const QtContacts::QContactDetail &prefB); static bool isEqual(QList listA, QList listB); static bool isEqual(const QtContacts::QContactDetail &detailA, const QtContacts::QContactDetail &detailB); static bool checkPersona(QtContacts::QContactDetail &det, int persona); static QList detailsFromPersona(const QtContacts::QContact &contact, QtContacts::QContactDetail::DetailType type, int persona, bool includeEmptyPersona, QtContacts::QContactDetail *pref); QList originalDetailsFromPersona(QtContacts::QContactDetail::DetailType type, int persona, QtContacts::QContactDetail *pref) const; QList detailsFromPersona(QtContacts::QContactDetail::DetailType type, int persona, QtContacts::QContactDetail *pref) const; void updatePersona(); void updateAddress(); void updateAvatar(); void updateBirthday(); void updateFullName(const QString &fullName); void updateEmail(); void updateName(); void updateNickname(); void updateNote(); void updateOnlineAccount(); void updateOrganization(); void updatePhone(); void updateUrl(); void updateFavorite(); void updateExtendedDetails(); QString callDetailChangeFinish(QtContacts::QContactDetail::DetailType detailType, FolksPersona *persona, GAsyncResult *result); static void updateDetailsDone(GObject *detail, GAsyncResult *result, gpointer userdata); }; } #endif address-book-service-0.1.1+16.04.20151211.1/lib/CMakeLists.txt0000644000015300001610000000260512632607666023540 0ustar pbuserpbgroup00000000000000project(address-book-service-lib) set(CONTACTS_SERVICE_LIB address-book-service-lib) set(CONTACTS_SERVICE_LIB_SRC addressbook.cpp addressbook-adaptor.cpp contact-less-than.cpp contacts-map.cpp detail-context-parser.cpp dirtycontact-notify.cpp gee-utils.cpp qindividual.cpp update-contact-request.cpp view.cpp view-adaptor.cpp ) set(CONTACTS_SERVICE_LIB_HEADERS addressbook.h addressbook-adaptor.h contact-less-than.h contacts-map.h detail-context-parser.h dirtycontact-notify.h gee-utils.h qindividual.h update-contact-request.h view.h view-adaptor.h ) add_library(${CONTACTS_SERVICE_LIB} STATIC ${CONTACTS_SERVICE_LIB_SRC} ${CONTACTS_SERVICE_LIB_HEADERS} ) target_link_libraries(${CONTACTS_SERVICE_LIB} galera-common ubuntu-source-eds ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} ${FOLKS_EDS_LIBRARIES} ${LibPhoneNumber_LIBRARIES} ${MESSAGING_MENU_LIBRARIES} ${URL_DISPATCHER_LIBRARIES} Qt5::Core Qt5::Contacts Qt5::DBus Qt5::Versit ) include_directories( ${ubuntu-sources_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_INCLUDE_DIRS} ${FOLKS_EDS_INCLUDE_DIRS} ${LibPhoneNumber_INCLUDE_DIRS} ${MESSAGING_MENU_INCLUDE_DIRS} ${URL_DISPATCHER_INCLUDE_DIRS} ) address-book-service-0.1.1+16.04.20151211.1/lib/dirtycontact-notify.cpp0000644000015300001610000000727012632607673025522 0ustar pbuserpbgroup00000000000000/* * Copyright 2014 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //this timeout represents how long the server will wait for changes on the contact before notify the client #define NOTIFY_CONTACTS_TIMEOUT 500 #include "dirtycontact-notify.h" #include "addressbook-adaptor.h" namespace galera { DirtyContactsNotify::DirtyContactsNotify(AddressBookAdaptor *adaptor, QObject *parent) : QObject(parent), m_adaptor(adaptor) { m_timer.setInterval(NOTIFY_CONTACTS_TIMEOUT); m_timer.setSingleShot(true); connect(&m_timer, SIGNAL(timeout()), SLOT(emitSignals())); } void DirtyContactsNotify::insertAddedContacts(QSet ids) { if (!m_adaptor->isReady()) { return; } // if the contact was removed before ignore the removal signal, and send a update signal QSet addedIds = ids; Q_FOREACH(QString added, ids) { if (m_contactsRemoved.contains(added)) { m_contactsRemoved.remove(added); addedIds.remove(added); m_contactsChanged.insert(added); } } m_contactsAdded += addedIds; m_timer.start(); } void DirtyContactsNotify::flush() { m_timer.stop(); emitSignals(); } void DirtyContactsNotify::clear() { qWarning() << "Clear notify" << (m_contactsChanged.size() + m_contactsAdded.size() + m_contactsRemoved.size()); m_contactsChanged.clear(); m_contactsAdded.clear(); m_contactsRemoved.clear(); m_timer.stop(); } void DirtyContactsNotify::insertRemovedContacts(QSet ids) { if (!m_adaptor->isReady()) { return; } // if the contact was added before ignore the added and removed signal QSet removedIds = ids; Q_FOREACH(QString removed, ids) { if (m_contactsAdded.contains(removed)) { m_contactsAdded.remove(removed); removedIds.remove(removed); } } m_contactsRemoved += removedIds; m_timer.start(); } void DirtyContactsNotify::insertChangedContacts(QSet ids) { if (!m_adaptor->isReady()) { return; } m_contactsChanged += ids; m_timer.start(); } void DirtyContactsNotify::emitSignals() { qWarning() << "Emit singals:" << "\n\tChanged:" << m_contactsChanged.size() << "\n\tRemoved:" << m_contactsRemoved.size() << "\n\tAdded:" << m_contactsAdded.size(); if (!m_contactsChanged.isEmpty()) { // ignore the signal if the added signal was not fired yet m_contactsChanged.subtract(m_contactsAdded); // ignore the signal if the contact was removed m_contactsChanged.subtract(m_contactsRemoved); if (!m_contactsChanged.isEmpty()) { Q_EMIT m_adaptor->contactsUpdated(m_contactsChanged.toList()); m_contactsChanged.clear(); } } if (!m_contactsRemoved.isEmpty()) { Q_EMIT m_adaptor->contactsRemoved(m_contactsRemoved.toList()); m_contactsRemoved.clear(); } if (!m_contactsAdded.isEmpty()) { Q_EMIT m_adaptor->contactsAdded(m_contactsAdded.toList()); m_contactsAdded.clear(); } } } //namespace address-book-service-0.1.1+16.04.20151211.1/lib/addressbook.h0000644000015300001610000002014212632607673023443 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_ADDRESSBOOK_H__ #define __GALERA_ADDRESSBOOK_H__ #include "common/source.h" #include #include #include #include #include #include #include #include #include #include typedef struct _MessagingMenuMessage MessagingMenuMessage; typedef struct _MessagingMenuApp MessagingMenuApp; typedef struct _ESource ESource; typedef struct _ESourceRegistry ESourceRegistry; namespace galera { class View; class ContactsMap; class AddressBookAdaptor; class QIndividual; class DirtyContactsNotify; class AddressBook: public QObject { Q_OBJECT public: AddressBook(QObject *parent=0); virtual ~AddressBook(); static QString objectPath(); bool start(QDBusConnection connection); // Adaptor QString linkContacts(const QStringList &contacts); View *query(const QString &clause, const QString &sort, int maxCount, bool showInvisible, const QStringList &sources); QStringList sortFields(); bool unlinkContacts(const QString &parent, const QStringList &contacts); bool isReady() const; void setSafeMode(bool flag); static bool isSafeMode(); static int init(); Q_SIGNALS: void stopped(); void readyChanged(); void safeModeChanged(); void sourcesChanged(); public Q_SLOTS: bool start(); void shutdown(); SourceList availableSources(const QDBusMessage &message); Source source(const QDBusMessage &message); Source createSource(const QString &sourceName, uint accountId, bool setAsPrimary, const QDBusMessage &message); SourceList updateSources(const SourceList &sources, const QDBusMessage &message); void removeSource(const QString &sourceId, const QDBusMessage &message); QString createContact(const QString &contact, const QString &source, const QDBusMessage &message = QDBusMessage()); int removeContacts(const QStringList &contactIds, const QDBusMessage &message); QStringList updateContacts(const QStringList &contacts, const QDBusMessage &message); void purgeContacts(const QDateTime &since, const QString &sourceId, const QDBusMessage &message); void updateContactsDone(const QString &contactId, const QString &error); private Q_SLOTS: void viewClosed(); void individualChanged(QIndividual *individual); void onEdsServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); void onSafeModeChanged(); // Unix signal handlers. void handleSigQuit(); // WORKAROUND: Check if EDS was running when the service started void checkForEds(); void unprepareFolks(); // check compatibility and if the safe mode should be enabled void checkCompatibility(); private: FolksIndividualAggregator *m_individualAggregator; ContactsMap *m_contacts; QSet m_views; AddressBookAdaptor *m_adaptor; // timer to avoid send several updates at the same time DirtyContactsNotify *m_notifyContactUpdate; QDBusServiceWatcher *m_edsWatcher; MessagingMenuApp *m_messagingMenu; MessagingMenuMessage *m_messagingMenuMessage; ESourceRegistry *m_sourceRegistryListener; static QSettings m_settings; bool m_edsIsLive; bool m_ready; bool m_isAboutToQuit; bool m_isAboutToReload; gulong m_individualsChangedDetailedId; gulong m_notifyIsQuiescentHandlerId; QDBusConnection m_connection; // Update command QMutex m_updateLock; QDBusMessage m_updateCommandReplyMessage; QStringList m_updateCommandResult; QStringList m_updatedIds; QStringList m_updateCommandPendingContacts; // Unix signals static int m_sigQuitFd[2]; QSocketNotifier *m_snQuit; // dbus service name QString m_serviceName; // Disable copy contructor AddressBook(const AddressBook&); void getSource(const QDBusMessage &message, bool onlyTheDefault); void setupUnixSignals(); // Unix signal handlers. void prepareUnixSignals(); static void quitSignalHandler(int unused); bool processUpdates(); void prepareFolks(); void unprepareEds(); void connectWithEDS(); void continueShutdown(); void setIsReady(bool isReady); bool registerObject(QDBusConnection &connection); QString removeContact(FolksIndividual *individual, bool *visible); QString addContact(FolksIndividual *individual, bool visible); FolksPersonaStore *getFolksStore(const QString &source); static void availableSourcesDoneListAllSources(FolksBackendStore *backendStore, GAsyncResult *res, QDBusMessage *msg); static void availableSourcesDoneListDefaultSource(FolksBackendStore *backendStore, GAsyncResult *res, QDBusMessage *msg); static SourceList availableSourcesDoneImpl(FolksBackendStore *backendStore, GAsyncResult *res); static void individualsChangedCb(FolksIndividualAggregator *individualAggregator, GeeMultiMap *changes, AddressBook *self); static void isQuiescentChanged(GObject *source, GParamSpec *param, AddressBook *self); static void prepareFolksDone(GObject *source, GAsyncResult *res, AddressBook *self); static void createContactDone(FolksIndividualAggregator *individualAggregator, GAsyncResult *res, void *data); static void removeContactDone(FolksIndividualAggregator *individualAggregator, GAsyncResult *result, void *data); static void createSourceDone(GObject *source, GAsyncResult *res, void *data); static void removeSourceDone(GObject *source, GAsyncResult *res, void *data); static void folksUnprepared(GObject *source, GAsyncResult *res, void *data); static void edsUnprepared(GObject *source, GAsyncResult *res, void *data); static void edsPrepared(GObject *source, GAsyncResult *res, void *data); static void onSafeModeMessageActivated(MessagingMenuMessage *message, const char *actionId, GVariant *param, AddressBook *self); //EDS helper void updateSourcesEDS(void *data); static Source parseEDSSource(ESourceRegistry *registry, ESource *eSource); static void edsRemoveContact(FolksIndividual *individual); static void updateSourceEDSDone(GObject *source, GAsyncResult *res, void *data); static void sourceEDSChanged(ESourceRegistry *registry, ESource *source, AddressBook *self); friend class DirtyContactsNotify; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/3rd_party/0000755000015300001610000000000012632610222022114 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/0000755000015300001610000000000012632610222023232 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/0000755000015300001610000000000012632610222024365 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/0000755000015300001610000000000012632610222025133 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/folks-dummy.pc.in0000644000015300001610000000061412632607666030356 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ bindir=@bindir@ includedir=@includedir@ datarootdir=@datarootdir@ datadir=@datadir@ vapidir=@datadir@/vala/vapi Name: Folks dummy support library Description: Dummy support library for the Folks meta-contacts library Version: @VERSION@ Requires: folks glib-2.0 gobject-2.0 gee-0.8 Libs: -L${libdir} -lfolks-dummy Cflags: -I${includedir} address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/backend.mk0000644000015300001610000000002512632607666027072 0ustar pbuserpbgroup00000000000000BACKEND_NAME = dummy address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/dummy-persona-store.vala0000644000015300001610000011022712632607666031757 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Philip Withnall * Copyright (C) 2013 Canonical Ltd * Copyright (C) 2013 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Philip Withnall * Renato Araujo Oliveira Filho */ using Folks; using Gee; using GLib; /** * A persona store which allows {@link FolksDummy.Persona}s to be * programmatically created and manipulated, for the purposes of testing the * core of libfolks itself. This should not be used in user-visible * applications. * * There are two sides to this class’ interface: the methods and properties * declared by {@link Folks.PersonaStore}, which form the normal libfolks * persona store API; and the mock methods and properties (such as * {@link FolksDummy.PersonaStore.add_persona_from_details_mock}) which are * intended to be used by test driver code to simulate the behaviour of a real * backing store. Calls to these mock methods effect state changes in the store * which are visible in the normal libfolks API. The ``update_``, ``register_`` * and ``unregister_`` prefixes and the ``mock`` suffix are commonly used for * backing store methods. * * The main action performed with a dummy persona store is to change its set of * personas, adding and removing them dynamically to test client-side behaviour. * The client-side APIs ({@link Folks.PersonaStore.add_persona_from_details} and * {@link Folks.PersonaStore.remove_persona}) should //not// be used for this. * Instead, the mock APIs should be used: * {@link FolksDummy.PersonaStore.freeze_personas_changed}, * {@link FolksDummy.PersonaStore.register_personas}, * {@link FolksDummy.PersonaStore.unregister_personas} and * {@link FolksDummy.PersonaStore.thaw_personas_changed}. These can be used to * build up complex {@link Folks.PersonaStore.personas_changed} signal * emissions, which are only emitted after the final call to * {@link FolksDummy.PersonaStore.thaw_personas_changed}. * * The API in {@link FolksDummy} is unstable and may change wildly. It is * designed mostly for use by libfolks unit tests. * * @since UNRELEASED */ public class FolksDummy.PersonaStore : Folks.PersonaStore { private bool _is_prepared = false; private bool _prepare_pending = false; private bool _is_quiescent = false; private bool _quiescent_on_prepare = false; private int _contact_id = 0; /** * The type of persona store this is. * * See {@link Folks.PersonaStore.type_id}. * * @since UNRELEASED */ public override string type_id { get { return BACKEND_NAME; } } private MaybeBool _can_add_personas = MaybeBool.FALSE; /** * Whether this PersonaStore can add {@link Folks.Persona}s. * * See {@link Folks.PersonaStore.can_add_personas}. * * @since UNRELEASED */ public override MaybeBool can_add_personas { get { if (!this._is_prepared) { return MaybeBool.FALSE; } return this._can_add_personas; } } private MaybeBool _can_alias_personas = MaybeBool.FALSE; /** * Whether this PersonaStore can set the alias of {@link Folks.Persona}s. * * See {@link Folks.PersonaStore.can_alias_personas}. * * @since UNRELEASED */ public override MaybeBool can_alias_personas { get { if (!this._is_prepared) { return MaybeBool.FALSE; } return this._can_alias_personas; } } /** * Whether this PersonaStore can set the groups of {@link Folks.Persona}s. * * See {@link Folks.PersonaStore.can_group_personas}. * * @since UNRELEASED */ public override MaybeBool can_group_personas { get { return ("groups" in this._always_writeable_properties) ? MaybeBool.TRUE : MaybeBool.FALSE; } } private MaybeBool _can_remove_personas = MaybeBool.FALSE; /** * Whether this PersonaStore can remove {@link Folks.Persona}s. * * See {@link Folks.PersonaStore.can_remove_personas}. * * @since UNRELEASED */ public override MaybeBool can_remove_personas { get { if (!this._is_prepared) { return MaybeBool.FALSE; } return this._can_remove_personas; } } /** * Whether this PersonaStore has been prepared. * * See {@link Folks.PersonaStore.is_prepared}. * * @since UNRELEASED */ public override bool is_prepared { get { return this._is_prepared; } } private string[] _always_writeable_properties = {}; private static string[] _always_writeable_properties_empty = {}; /* oh Vala */ /** * {@inheritDoc} * * @since UNRELEASED */ public override string[] always_writeable_properties { get { if (!this._is_prepared) { return PersonaStore._always_writeable_properties_empty; } return this._always_writeable_properties; } } /* * Whether this PersonaStore has reached a quiescent state. * * See {@link Folks.PersonaStore.is_quiescent}. * * @since UNRELEASED */ public override bool is_quiescent { get { return this._is_quiescent; } } private HashMap _personas; private Map _personas_ro; /* Personas which have been registered but not yet emitted in a * personas-changed signal. */ private HashSet _pending_persona_registrations; /* Personas which have been unregistered but not yet emitted in a * personas-changed signal. */ private HashSet _pending_persona_unregistrations; /* Freeze counter for persona changes: personas-changed is only emitted when * this is 0. */ private uint _personas_changed_frozen = 0; /** * The {@link Persona}s exposed by this PersonaStore. * * See {@link Folks.PersonaStore.personas}. * * @since UNRELEASED */ public override Map personas { get { return this._personas_ro; } } /** * Create a new persona store. * * This store will have no personas to begin with; use * {@link FolksDummy.PersonaStore.register_personas} to add some, then call * {@link FolksDummy.PersonaStore.reach_quiescence} to signal the store * reaching quiescence. * * @param id The new store's ID. * @param display_name The new store's display name. * @param always_writeable_properties The set of always writeable properties. * * @since UNRELEASED */ public PersonaStore (string id, string display_name, string[] always_writeable_properties) { Object ( id: id, display_name: display_name); this._always_writeable_properties = always_writeable_properties; } construct { this._personas = new HashMap (); this._personas_ro = this._personas.read_only_view; this._pending_persona_registrations = new HashSet (); this._pending_persona_unregistrations = new HashSet (); } /** * Add a new {@link Persona} to the PersonaStore. * * Accepted keys for ``details`` are: * - PersonaStore.detail_key (PersonaDetail.AVATAR) * - PersonaStore.detail_key (PersonaDetail.BIRTHDAY) * - PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES) * - PersonaStore.detail_key (PersonaDetail.FULL_NAME) * - PersonaStore.detail_key (PersonaDetail.GENDER) * - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES) * - PersonaStore.detail_key (PersonaDetail.IS_FAVOURITE) * - PersonaStore.detail_key (PersonaDetail.PHONE_NUMBERS) * - PersonaStore.detail_key (PersonaDetail.POSTAL_ADDRESSES) * - PersonaStore.detail_key (PersonaDetail.ROLES) * - PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME) * - PersonaStore.detail_key (PersonaDetail.LOCAL_IDS) * - PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES) * - PersonaStore.detail_key (PersonaDetail.NOTES) * - PersonaStore.detail_key (PersonaDetail.URLS) * * See {@link Folks.PersonaStore.add_persona_from_details}. * * @param details key–value pairs giving the new persona’s details * @throws Folks.PersonaStoreError.STORE_OFFLINE if the store hasn’t been * prepared * @throws Folks.PersonaStoreError.CREATE_FAILED if creating the persona in * the dummy store failed * * @since UNRELEASED */ public override async Folks.Persona? add_persona_from_details ( HashTable details) throws PersonaStoreError { /* We have to have called prepare() beforehand. */ if (!this._is_prepared) { throw new PersonaStoreError.STORE_OFFLINE ( "Persona store has not yet been prepared."); } /* Allow overriding the class used. */ var contact_id = this._contact_id.to_string(); this._contact_id++; var uid = Folks.Persona.build_uid (BACKEND_NAME, this.id, contact_id); var iid = this.id + ":" + contact_id; var persona = Object.new (this._persona_type, "display-id", contact_id, "uid", uid, "iid", iid, "store", this, "is-user", false, null) as FolksDummy.Persona; assert (persona != null); persona.update_writeable_properties (this.always_writeable_properties); unowned Value? v; try { v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.FULL_NAME)); var p_name = persona as NameDetails; if (p_name != null && v != null) { string full_name = ((!) v).get_string () ?? ""; yield p_name.change_full_name (full_name); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME)); if (p_name != null && v != null) { var sname = (StructuredName) ((!) v).get_object (); if (sname != null) yield p_name.change_structured_name (sname); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.NICKNAME)); if (p_name != null && v != null) { string nickname = ((!) v).get_string () ?? ""; yield p_name.change_nickname (nickname); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES)); var p_email = persona as EmailDetails; if (p_email != null && v != null) { var email_addresses = (Set) ((!) v).get_object (); if (email_addresses != null) yield p_email.change_email_addresses (email_addresses); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.AVATAR)); var p_avatar = persona as AvatarDetails; if (p_avatar != null && v != null) { var avatar = (LoadableIcon?) ((!) v).get_object (); if (avatar != null) yield p_avatar.change_avatar (avatar); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)); var p_im = persona as ImDetails; if (p_im != null && v != null) { var im_addresses = (MultiMap) ((!) v).get_object (); if (im_addresses != null) yield p_im.change_im_addresses (im_addresses); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.PHONE_NUMBERS)); var p_phone = persona as PhoneDetails; if (p_phone != null && v != null) { var phone_numbers = (Set) ((!) v).get_object (); if (phone_numbers != null) yield p_phone.change_phone_numbers (phone_numbers); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.POSTAL_ADDRESSES)); var p_postal = persona as PostalAddressDetails; if (p_postal != null && v != null) { var postal_fds = (Set) ((!) v).get_object (); if (postal_fds != null) yield p_postal.change_postal_addresses (postal_fds); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.LOCAL_IDS)); var p_local = persona as LocalIdDetails; if (p_local != null && v != null) { var local_ids = (Set) ((!) v).get_object (); if (local_ids != null) yield p_local.change_local_ids (local_ids); } v = details.lookup ( Folks.PersonaStore.detail_key ( PersonaDetail.WEB_SERVICE_ADDRESSES)); var p_web = persona as WebServiceDetails; if (p_web != null && v != null) { var addrs = (HashMultiMap) ((!) v).get_object (); if (addrs != null) yield p_web.change_web_service_addresses (addrs); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.NOTES)); var p_note = persona as NoteDetails; if (p_note != null && v != null) { var notes = (Gee.HashSet) ((!) v).get_object (); if (notes != null) yield p_note.change_notes (notes); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.GENDER)); var p_gender = persona as GenderDetails; if (p_gender != null && v != null) { var gender = (Gender) ((!) v).get_enum (); yield p_gender.change_gender (gender); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.URLS)); var p_url = persona as UrlDetails; if (p_url != null && v != null) { var urls = (Set) ((!) v).get_object (); if (urls != null) yield p_url.change_urls (urls); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.BIRTHDAY)); var p_birthday = persona as BirthdayDetails; if (p_birthday != null && v != null) { var birthday = (DateTime?) ((!) v).get_boxed (); if (birthday != null) yield p_birthday.change_birthday (birthday); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.ROLES)); var p_role = persona as RoleDetails; if (p_role != null && v != null) { var roles = (Set) ((!) v).get_object (); if (roles != null) yield p_role.change_roles (roles); } v = details.lookup ( Folks.PersonaStore.detail_key (PersonaDetail.IS_FAVOURITE)); var p_favourite = persona as FavouriteDetails; if (p_favourite != null && v != null) { bool is_fav = ((!) v).get_boolean (); yield p_favourite.change_is_favourite (is_fav); } } catch (PropertyError e1) { throw new PersonaStoreError.CREATE_FAILED ( "Setting a property on the new persona failed: %s", e1.message); } /* Allow the caller to inject failures and delays into * add_persona_from_details() by providing a mock function. */ if (this.add_persona_from_details_mock != null) { var delay = this.add_persona_from_details_mock (persona); yield this._implement_mock_delay (delay); } /* No simulated failure: continue adding the persona. */ this._personas.set (persona.iid, persona); /* Notify of the new persona. */ var added_personas = new HashSet (); added_personas.add (persona); this._emit_personas_changed (added_personas, null); return persona; } /** * Remove a {@link Persona} from the PersonaStore. * * See {@link Folks.PersonaStore.remove_persona}. * * @param persona the persona that should be removed * @throws Folks.PersonaStoreError.STORE_OFFLINE if the store hasn’t been * prepared or has gone offline * @throws Folks.PersonaStoreError.PERMISSION_DENIED if the store denied * permission to delete the contact * @throws Folks.PersonaStoreError.READ_ONLY if the store is read only * @throws Folks.PersonaStoreError.REMOVE_FAILED if any other errors happened * in the store * * @since UNRELEASED */ public override async void remove_persona (Folks.Persona persona) throws PersonaStoreError requires (persona is FolksDummy.Persona) { /* We have to have called prepare() beforehand. */ if (!this._is_prepared) { throw new PersonaStoreError.STORE_OFFLINE ( "Persona store has not yet been prepared."); } /* Allow the caller to inject failures and delays. */ if (this.remove_persona_mock != null) { var delay = this.remove_persona_mock ((FolksDummy.Persona) persona); yield this._implement_mock_delay (delay); } Persona? _persona = this._personas.get (persona.iid); if (_persona != null) { this._personas.unset (persona.iid); /* Handle the case where a contact is removed while persona changes * are frozen. */ this._pending_persona_registrations.remove ((!) _persona); this._pending_persona_unregistrations.remove ((!) _persona); /* Notify of the removal. */ var removed_personas = new HashSet (); removed_personas.add ((!) persona); this._emit_personas_changed (null, removed_personas); } } /** * Prepare the PersonaStore for use. * * See {@link Folks.PersonaStore.prepare}. * * @throws Folks.PersonaStoreError.STORE_OFFLINE if the store is offline * @throws Folks.PersonaStoreError.PERMISSION_DENIED if permission was denied * to open the store * @throws Folks.PersonaStoreError.INVALID_ARGUMENT if any other error * occurred in the store * * @since UNRELEASED */ public override async void prepare () throws PersonaStoreError { Internal.profiling_start ("preparing Dummy.PersonaStore (ID: %s)", this.id); if (this._is_prepared == true || this._prepare_pending == true) { return; } try { this._prepare_pending = true; /* Allow the caller to inject failures and delays. */ if (this.prepare_mock != null) { var delay = this.prepare_mock (); yield this._implement_mock_delay (delay); } this._is_prepared = true; this.notify_property ("is-prepared"); /* If reach_quiescence() has been called already, signal * quiescence. */ if (this._quiescent_on_prepare == true) { this.reach_quiescence (); } } finally { this._prepare_pending = false; } Internal.profiling_end ("preparing Dummy.PersonaStore"); } /* * All the functions below here are to be used by testing code rather than by * libfolks clients. They form the interface which would normally be between * the PersonaStore and a web service or backing store of some kind. */ /** * Delay for the given number of milliseconds. * * This implements an asynchronous delay (which should be yielded on) until * the given number of milliseconds has elapsed. * * If ``delay`` is negative, this function returns immediately. If it is * zero, this function returns in an idle callback. * * @param delay number of milliseconds to delay for * * @since UNRELEASED */ private async void _implement_mock_delay (int delay) { if (delay < 0) { /* No delay. */ return; } else if (delay == 0) { /* Idle delay. */ Idle.add (() => { this._implement_mock_delay.callback (); return false; }); yield; } else { /* Timed delay. */ Timeout.add (delay, () => { this._implement_mock_delay.callback (); return false; }); yield; } } /** * Type of a mock function for * {@link Folks.PersonaStore.add_persona_from_details}. * * See {@link FolksDummy.PersonaStore.add_persona_from_details_mock}. * * @param persona the persona being added to the store, as constructed from * the details passed to {@link Folks.PersonaStore.add_persona_from_details}. * @throws PersonaStoreError to be thrown from * {@link Folks.PersonaStore.add_persona_from_details} * @return delay to apply to the add persona operation (negative delays * complete synchronously; zero delays complete in an idle callback; positive * delays complete after that many milliseconds) * * @since UNRELEASED */ public delegate int AddPersonaFromDetailsMock (Persona persona) throws PersonaStoreError; /** * Mock function for {@link Folks.PersonaStore.add_persona_from_details}. * * This function is called whenever this store's * {@link Folks.PersonaStore.add_persona_from_details} method is called. It * allows the caller to determine whether adding the given persona should * fail, by throwing an error from this mock function. If no error is thrown * from this function, adding the given persona will succeed. This is useful * for testing error handling of calls to * {@link Folks.PersonaStore.add_persona_from_details}. * * The value returned by this function gives a delay which is imposed for * completion of the {@link Folks.PersonaStore.add_persona_from_details} call. * Negative or zero delays * result in completion in an idle callback, and positive delays result in * completion after that many milliseconds. * * If this is ``null``, all calls to * {@link Folks.PersonaStore.add_persona_from_details} will succeed. * * This mock function may be changed at any time; changes will take effect for * the next call to {@link Folks.PersonaStore.add_persona_from_details}. * * @since UNRELEASED */ public unowned AddPersonaFromDetailsMock? add_persona_from_details_mock { get; set; default = null; } /** * Type of a mock function for {@link Folks.PersonaStore.remove_persona}. * * See {@link FolksDummy.PersonaStore.remove_persona_mock}. * * @param persona the persona being removed from the store * @throws PersonaStoreError to be thrown from * {@link Folks.PersonaStore.remove_persona} * @return delay to apply to the remove persona operation (negative and zero * delays complete in an idle callback; positive * delays complete after that many milliseconds) * * @since UNRELEASED */ public delegate int RemovePersonaMock (Persona persona) throws PersonaStoreError; /** * Mock function for {@link Folks.PersonaStore.remove_persona}. * * This function is called whenever this store's * {@link Folks.PersonaStore.remove_persona} method is called. It allows * the caller to determine whether removing the given persona should fail, by * throwing an error from this mock function. If no error is thrown from this * function, removing the given persona will succeed. This is useful for * testing error handling of calls to * {@link Folks.PersonaStore.remove_persona}. * * See {@link FolksDummy.PersonaStore.add_persona_from_details_mock}. * * This mock function may be changed at any time; changes will take effect for * the next call to {@link Folks.PersonaStore.remove_persona}. * * @since UNRELEASED */ public unowned RemovePersonaMock? remove_persona_mock { get; set; default = null; } /** * Type of a mock function for {@link Folks.PersonaStore.prepare}. * * See {@link FolksDummy.PersonaStore.prepare_mock}. * * @throws PersonaStoreError to be thrown from * {@link Folks.PersonaStore.prepare} * @return delay to apply to the prepare operation (negative and zero delays * complete in an idle callback; positive * delays complete after that many milliseconds) * * @since UNRELEASED */ public delegate int PrepareMock () throws PersonaStoreError; /** * Mock function for {@link Folks.PersonaStore.prepare}. * * This function is called whenever this store's * {@link Folks.PersonaStore.prepare} method is called on an unprepared store. * It allows the caller to determine whether preparing the store should fail, * by throwing an error from this mock function. If no error is thrown from * this function, preparing the store will succeed (and all future calls to * {@link Folks.PersonaStore.prepare} will return immediately without calling * this mock function). This is useful for testing error handling of calls to * {@link Folks.PersonaStore.prepare}. * * See {@link FolksDummy.PersonaStore.add_persona_from_details_mock}. * * This mock function may be changed at any time; changes will take effect for * the next call to {@link Folks.PersonaStore.prepare}. * * @since UNRELEASED */ public unowned PrepareMock? prepare_mock { get; set; default = null; } private Type _persona_type = typeof (FolksDummy.Persona); /** * Type of programmatically created personas. * * This is the type used to create new personas when * {@link Folks.PersonaStore.add_persona_from_details} is called. It must be a * subtype of {@link FolksDummy.Persona}. * * This may be modified at any time, with modifications taking effect for the * next call to {@link Folks.PersonaStore.add_persona_from_details} or * {@link FolksDummy.PersonaStore.register_personas}. * * @since UNRELEASED */ public Type persona_type { get { return this._persona_type; } set { assert (value.is_a (typeof (FolksDummy.Persona))); if (this._persona_type != value) { this._persona_type = value; this.notify_property ("persona-type"); } } } /** * Set capabilities of the persona store. * * This sets the capabilities of the store, as if they were changed on a * backing store somewhere. This is intended to be used for testing code which * depends on the values of {@link Folks.PersonaStore.can_add_personas}, * {@link Folks.PersonaStore.can_alias_personas} and * {@link Folks.PersonaStore.can_remove_personas}. * * @param can_add_personas whether the store can handle adding personas * @param can_alias_personas whether the store can handle and update * user-specified persona aliases * @param can_remove_personas whether the store can handle removing personas * * @since UNRELEASED */ public void update_capabilities (MaybeBool can_add_personas, MaybeBool can_alias_personas, MaybeBool can_remove_personas) { this.freeze_notify (); if (can_add_personas != this._can_add_personas) { this._can_add_personas = can_add_personas; this.notify_property ("can-add-personas"); } if (can_alias_personas != this._can_alias_personas) { this._can_alias_personas = can_alias_personas; this.notify_property ("can-alias-personas"); } if (can_remove_personas != this._can_remove_personas) { this._can_remove_personas = can_remove_personas; this.notify_property ("can-remove-personas"); } this.thaw_notify (); } /** * Freeze persona changes in the store. * * This freezes externally-visible changes to the set of personas in the store * until {@link FolksDummy.PersonaStore.thaw_personas_changed} is called, at * which point all pending changes are made visible in the * {@link Folks.PersonaStore.personas} property and by emitting * {@link Folks.PersonaStore.personas_changed}. * * Calls to {@link FolksDummy.PersonaStore.freeze_personas_changed} and * {@link FolksDummy.PersonaStore.thaw_personas_changed} must be well-nested. * Pending changes will only be committed after the final call to * {@link FolksDummy.PersonaStore.thaw_personas_changed}. * * @see PersonaStore.thaw_personas_changed * @since UNRELEASED */ public void freeze_personas_changed () { this._personas_changed_frozen++; } /** * Thaw persona changes in the store. * * This thaws externally-visible changes to the set of personas in the store. * If the number of calls to * {@link FolksDummy.PersonaStore.thaw_personas_changed} matches the number of * calls to {@link FolksDummy.PersonaStore.freeze_personas_changed}, all * pending changes are committed and made externally-visible. * * @see PersonaStore.freeze_personas_changed * @since UNRELEASED */ public void thaw_personas_changed () { assert (this._personas_changed_frozen > 0); this._personas_changed_frozen--; if (this._personas_changed_frozen == 0) { /* Emit the queued changes. */ this._emit_personas_changed (this._pending_persona_registrations, this._pending_persona_unregistrations); this._pending_persona_registrations.clear (); this._pending_persona_unregistrations.clear (); } } /** * Register new personas with the persona store. * * This registers a set of personas as if they had just appeared in the * backing store. If the persona store is not frozen (see * {@link FolksDummy.PersonaStore.freeze_personas_changed}) the changes are * made externally visible on the store immediately (e.g. in the * {@link Folks.PersonaStore.personas} property and through a * {@link Folks.PersonaStore.personas_changed} signal). If the store is * frozen, the changes will be pending until the store is next unfrozen. * * All elements in the @personas set be of type * {@link FolksDummy.PersonaStore.persona_type}. * * @param personas set of personas to register * * @since UNRELEASED */ public void register_personas (Set personas) { Set added_personas; var emit_notifications = (this._personas_changed_frozen == 0); /* If the persona store has persona changes frozen, queue up the * personas and emit a notification about them later. */ if (emit_notifications == false) added_personas = this._pending_persona_registrations; else added_personas = new HashSet (); foreach (var persona in personas) { assert (persona.get_type ().is_a (this._persona_type)); /* Handle the case where a persona is unregistered while the store is * frozen, then registered again before it's unfrozen. */ if (this._pending_persona_unregistrations.remove (persona)) this._personas.unset (persona.iid); if (this._personas.has_key (persona.iid)) continue; added_personas.add (persona); if (emit_notifications == true) this._personas.set (persona.iid, persona); } if (added_personas.size > 0 && emit_notifications == true) this._emit_personas_changed (added_personas, null); } /** * Unregister existing personas with the persona store. * * This unregisters a set of personas as if they had just disappeared from the * backing store. If the persona store is not frozen (see * {@link FolksDummy.PersonaStore.freeze_personas_changed}) the changes are * made externally visible on the store immediately (e.g. in the * {@link Folks.PersonaStore.personas} property and through a * {@link Folks.PersonaStore.personas_changed} signal). If the store is * frozen, the changes will be pending until the store is next unfrozen. * * @param personas set of personas to unregister * * @since UNRELEASED */ public void unregister_personas (Set personas) { Set removed_personas; var emit_notifications = (this._personas_changed_frozen == 0); /* If the persona store has persona changes frozen, queue up the * personas and emit a notification about them later. */ if (emit_notifications == false) removed_personas = this._pending_persona_unregistrations; else removed_personas = new HashSet (); foreach (var _persona in personas) { /* Handle the case where a persona is registered while the store is * frozen, then unregistered before it's unfrozen. */ this._pending_persona_registrations.remove (_persona); Persona? persona = this._personas.get (_persona.iid); if (persona == null) continue; removed_personas.add ((!) persona); } /* Modify this._personas afterwards, just in case * personas == this._personas. */ if (removed_personas.size > 0 && emit_notifications == true) { foreach (var _persona in removed_personas) this._personas.unset (_persona.iid); this._emit_personas_changed (null, removed_personas); } } /** * Reach quiescence on the store. * * If the {@link Folks.PersonaStore.prepare} method has already been called on * the store, this causes the store to signal that it has reached quiescence * immediately. If the store has not yet been prepared, this will set a flag * to ensure that quiescence is reached as soon as * {@link Folks.PersonaStore.prepare} is called. * * This must be called before the store will reach quiescence. * * @since UNRELEASED */ public void reach_quiescence () { /* Can't reach quiescence until prepare() has been called. */ if (this._is_prepared == false) { this._quiescent_on_prepare = true; return; } /* The initial query is complete, so signal that we've reached * quiescence (even if there was an error). */ if (this._is_quiescent == false) { this._is_quiescent = true; this.notify_property ("is-quiescent"); } } /** * Update the {@link Folks.PersonaStore.is_user_set_default} property. * * Backend method for use by test code to simulate a backing-store-driven * change in the {@link Folks.PersonaStore.is_user_set_default} property. * * @param is_user_set_default new value for the property * * @since UNRELEASED */ public void update_is_user_set_default (bool is_user_set_default) { /* Implemented as an ‘update_*()’ method to make it more explicit that * this is for test driver use only. */ this.is_user_set_default = is_user_set_default; } /** * Update the {@link Folks.PersonaStore.trust_level} property. * * Backend method for use by test code to simulate a backing-store-driven * change in the {@link Folks.PersonaStore.trust_level} property. * * @param trust_level new value for the property * * @since UNRELEASED */ public void update_trust_level (PersonaStoreTrust trust_level) { /* Implemented as an ‘update_*()’ method to make it more explicit that * this is for test driver use only. */ this.trust_level = trust_level; } } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/dummy-backend.vala0000644000015300001610000002722212632607666030547 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Philip Withnall * Copyright (C) 2013 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Philip Withnall */ using Gee; using GLib; using Folks; extern const string BACKEND_NAME; /** * A backend which allows {@link FolksDummy.PersonaStore}s and * {@link FolksDummy.Persona}s to be programmatically created and manipulated, * for the purposes of testing the core of libfolks itself. * * This backend is not meant to be enabled in production use. The methods on * {@link FolksDummy.Backend} (and other classes) for programmatically * manipulating the backend's state are considered internal to libfolks and are * not stable. * * This backend maintains two sets of persona stores: the set of all persona * stores, and the set of enabled persona stores (which must be a subset of the * former). {@link FolksDummy.Backend.register_persona_stores} adds persona * stores to the set of all stores. Optionally it also enables them, adding them * to the set of enabled stores. The set of persona stores advertised by the * backend as {@link Folks.Backend.persona_stores} is the set of enabled stores. * libfolks may internally enable or disable stores using * {@link Folks.Backend.enable_persona_store}, * {@link Folks.Backend.disable_persona_store} * and {@link Folks.Backend.set_persona_stores}. The ``register_`` and * ``unregister_`` prefixes are commonly used for backend methods. * * The API in {@link FolksDummy} is unstable and may change wildly. It is * designed mostly for use by libfolks unit tests. * * @since UNRELEASED */ public class FolksDummy.Backend : Folks.Backend { private bool _is_prepared = false; private bool _prepare_pending = false; /* used for unprepare() too */ private bool _is_quiescent = false; private HashMap _all_persona_stores; private HashMap _enabled_persona_stores; private Map _enabled_persona_stores_ro; private const string[] _always_writable_properties = { "avatar", "birthday", "email-addresses", "full-name", "gender", "im-addresses", "is-favourite", "nickname", "phone-numbers", "postal-addresses", "roles", "structured-name", "local-ids", "location", "web-service-addresses", "notes", "groups", null }; /** * {@inheritDoc} * * @since UNRELEASED */ public Backend () { Object (); } construct { this._all_persona_stores = new HashMap (); this._enabled_persona_stores = new HashMap (); this._enabled_persona_stores_ro = this._enabled_persona_stores.read_only_view; } /** * Whether this Backend has been prepared. * * See {@link Folks.Backend.is_prepared}. * * @since UNRELEASED */ public override bool is_prepared { get { return this._is_prepared; } } /** * Whether this Backend has reached a quiescent state. * * See {@link Folks.Backend.is_quiescent}. * * @since UNRELEASED */ public override bool is_quiescent { get { return this._is_quiescent; } } /** * {@inheritDoc} * * @since UNRELEASED */ public override string name { get { return BACKEND_NAME; } } /** * {@inheritDoc} * * @since UNRELEASED */ public override Map persona_stores { get { return this._enabled_persona_stores_ro; } } /** * {@inheritDoc} * * @since UNRELEASED */ public override void disable_persona_store (Folks.PersonaStore store) { this._disable_persona_store (store); } /** * {@inheritDoc} * * @since UNRELEASED */ public override void enable_persona_store (Folks.PersonaStore store) { this._enable_persona_store ((FolksDummy.PersonaStore) store); } /** * {@inheritDoc} * * @since UNRELEASED */ public override void set_persona_stores (Set? store_ids) { /* If the set is empty, load all unloaded stores then return. */ if (store_ids == null) { this.freeze_notify (); foreach (var store in this._all_persona_stores.values) { this._enable_persona_store (store); } this.thaw_notify (); return; } /* First handle adding any missing persona stores. */ this.freeze_notify (); foreach (var id in store_ids) { if (!this._enabled_persona_stores.has_key (id)) { var store = this._all_persona_stores.get (id); if (store == null) { /* Create a new persona store. */ store = new FolksDummy.PersonaStore (id, id, FolksDummy.Backend._always_writable_properties); store.persona_type = typeof (FolksDummy.FullPersona); this._all_persona_stores.set (store.id, store); } this._enable_persona_store (store); } } /* Keep persona stores to remove in a separate array so we don't * invalidate the list we are iterating over. */ PersonaStore[] stores_to_remove = {}; foreach (var store in this._enabled_persona_stores.values) { if (!store_ids.contains (store.id)) { stores_to_remove += store; } } foreach (var store in stores_to_remove) { this._disable_persona_store (store); } this.thaw_notify (); } private void _enable_persona_store (PersonaStore store) { if (this._enabled_persona_stores.has_key (store.id)) { return; } assert (this._all_persona_stores.has_key (store.id)); store.removed.connect (this._store_removed_cb); this._enabled_persona_stores.set (store.id, store); this.persona_store_added (store); this.notify_property ("persona-stores"); } private void _disable_persona_store (Folks.PersonaStore store) { if (!this._enabled_persona_stores.unset (store.id)) { return; } assert (this._all_persona_stores.has_key (store.id)); this.persona_store_removed (store); this.notify_property ("persona-stores"); store.removed.disconnect (this._store_removed_cb); } private void _store_removed_cb (Folks.PersonaStore store) { this._disable_persona_store (store); } /** * {@inheritDoc} * * @since UNRELEASED */ public override async void prepare () throws GLib.Error { Internal.profiling_start ("preparing Dummy.Backend"); if (this._is_prepared || this._prepare_pending) { return; } try { this._prepare_pending = true; this.freeze_notify (); this._is_prepared = true; this.notify_property ("is-prepared"); this._is_quiescent = true; this.notify_property ("is-quiescent"); } finally { this.thaw_notify (); this._prepare_pending = false; } Internal.profiling_end ("preparing Dummy.Backend"); } /** * {@inheritDoc} * * @since UNRELEASED */ public override async void unprepare () throws GLib.Error { if (!this._is_prepared || this._prepare_pending) { return; } try { this._prepare_pending = true; this.freeze_notify (); var enabled_stores = this._enabled_persona_stores.values.to_array (); foreach (var persona_store in enabled_stores) { this._disable_persona_store (persona_store); } this._is_quiescent = false; this.notify_property ("is-quiescent"); this._is_prepared = false; this.notify_property ("is-prepared"); } finally { this.thaw_notify (); this._prepare_pending = false; } } /* * All the functions below here are to be used by testing code rather than by * libfolks clients. They form the interface which would normally be between * the Backend and a web service or backing store of some kind. */ /** * Register and enable some {@link FolksDummy.PersonaStore}s. * * For each of the persona stores in ``stores``, register it with this * backend. If ``enable_stores`` is ``true``, added stores will also be * enabled, emitting {@link Folks.Backend.persona_store_added} for each * newly-enabled store. After all addition signals are emitted, a change * notification for {@link Folks.Backend.persona_stores} will be emitted (but * only if at least one addition signal is emitted). * * Persona stores are identified by their {@link Folks.PersonaStore.id}; if a * store in ``stores`` has the same ID as a store previously registered * through this method, the duplicate will be ignored (so * {@link Folks.Backend.persona_store_added} won't be emitted for that store). * * Persona stores must be instances of {@link FolksDummy.PersonaStore} or * subclasses of it, allowing for different persona store implementations to * be tested. * * @param stores set of persona stores to register * @param enable_stores whether to automatically enable the stores * @since UNRELEASED */ public void register_persona_stores (Set stores, bool enable_stores = true) { this.freeze_notify (); foreach (var store in stores) { assert (store is FolksDummy.PersonaStore); if (this._all_persona_stores.has_key (store.id)) { continue; } this._all_persona_stores.set (store.id, store); if (enable_stores == true) { this._enable_persona_store (store); } } this.thaw_notify (); } /** * Disable and unregister some {@link FolksDummy.PersonaStore}s. * * For each of the persona stores in ``stores``, disable it (if it was * enabled) and unregister it from the backend so that it cannot be re-enabled * using {@link Folks.Backend.enable_persona_store} or * {@link Folks.Backend.set_persona_stores}. * * {@link Folks.Backend.persona_store_removed} will be emitted for all persona * stores in ``stores`` which were previously enabled. After all removal * signals are emitted, a change notification for * {@link Folks.Backend.persona_stores} will be emitted (but only if at least * one removal signal is emitted). * * @since UNRELEASED */ public void unregister_persona_stores (Set stores) { this.freeze_notify (); foreach (var store in stores) { assert (store is FolksDummy.PersonaStore); if (!this._all_persona_stores.has_key (store.id)) { continue; } this._disable_persona_store (store); this._all_persona_stores.unset (store.id); } this.thaw_notify (); } } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/dummy-persona.vala0000644000015300001610000002524112632607666030626 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Philip Withnall * Copyright (C) 2013 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Philip Withnall */ using Folks; using Gee; using GLib; /** * A persona subclass representing a single contact. * * This mocks up a ‘thin’ persona which implements none of the available * property interfaces provided by libfolks, and is designed as a base class to * be subclassed by personas which will implement one or more of these * interfaces. For example, {@link FolksDummy.FullPersona} is one such subclass * which implements all available interfaces. * * There are two sides to this class’ interface: the normal methods required by * {@link Folks.Persona}, such as * {@link Folks.Persona.linkable_property_to_links}, * and the backend methods which should be called by test driver code to * simulate changes in the backing store providing this persona, such as * {@link FolksDummy.Persona.update_writeable_properties}. The ``update_``, * ``register_`` and ``unregister_`` prefixes are commonly used for backend * methods. * * All property changes for contact details of subclasses of * {@link FolksDummy.Persona} have a configurable delay before taking effect, * which can be controlled by {@link FolksDummy.Persona.property_change_delay}. * * The API in {@link FolksDummy} is unstable and may change wildly. It is * designed mostly for use by libfolks unit tests. * * @since UNRELEASED */ public class FolksDummy.Persona : Folks.Persona { private string[] _linkable_properties = new string[0]; /** * {@inheritDoc} * * @since UNRELEASED */ public override string[] linkable_properties { get { return this._linkable_properties; } } private string[] _writeable_properties = new string[0]; /** * {@inheritDoc} * * @since UNRELEASED */ public override string[] writeable_properties { get { return this._writeable_properties; } } /** * Create a new persona. * * Create a new persona for the {@link FolksDummy.PersonaStore} ``store``, * with the given construct-only properties. * * The persona’s {@link Folks.Persona.writeable_properties} are initialised to * the given ``store``’s * {@link Folks.PersonaStore.always_writeable_properties}. They may be updated * afterwards using {@link FolksDummy.Persona.update_writeable_properties}. * * @param store the store which will contain the persona * @param contact_id a unique free-form string identifier for the persona * @param is_user ``true`` if the persona represents the user, ``false`` * otherwise * @param linkable_properties an array of names of the properties which should * be used for linking this persona to others * * @since UNRELEASED */ public Persona (PersonaStore store, string contact_id, bool is_user = false, string[] linkable_properties = {}) { var uid = Folks.Persona.build_uid (BACKEND_NAME, store.id, contact_id); var iid = store.id + ":" + contact_id; Object (display_id: contact_id, uid: uid, iid: iid, store: store, is_user: is_user); this._linkable_properties = linkable_properties; this._writeable_properties = this.store.always_writeable_properties; } /** * {@inheritDoc} * * @since UNRELEASED */ public override void linkable_property_to_links (string prop_name, Folks.Persona.LinkablePropertyCallback callback) { if (prop_name == "im-addresses") { var persona = this as ImDetails; assert (persona != null); foreach (var protocol in persona.im_addresses.get_keys ()) { var im_fds = persona.im_addresses.get (protocol); foreach (var im_fd in im_fds) { callback (protocol + ":" + im_fd.value); } } } else if (prop_name == "local-ids") { var persona = this as LocalIdDetails; assert (persona != null); foreach (var id in persona.local_ids) { callback (id); } } else if (prop_name == "web-service-addresses") { var persona = this as WebServiceDetails; assert (persona != null); foreach (var web_service in persona.web_service_addresses.get_keys ()) { var web_service_addresses = persona.web_service_addresses.get (web_service); foreach (var ws_fd in web_service_addresses) { callback (web_service + ":" + ws_fd.value); } } } else if (prop_name == "email-addresses") { var persona = this as EmailDetails; assert (persona != null); foreach (var email in persona.email_addresses) { callback (email.value); } } else { /* Chain up */ base.linkable_property_to_links (prop_name, callback); } } /* * All the functions below here are to be used by testing code rather than by * libfolks clients. They form the interface which would normally be between * the Persona and a web service or backing store of some kind. */ /** * Update the persona’s set of writeable properties. * * Update the {@link Folks.Persona.writeable_properties} property to contain * the union of {@link Folks.PersonaStore.always_writeable_properties} from * the persona’s store, and the given ``writeable_properties``. * * This should be used to simulate a change in the backing store for the * persona which affects the writeability of one or more of its properties. * * @since UNRELEASED */ public void update_writeable_properties (string[] writeable_properties) { var new_writeable_properties = new HashSet (); foreach (var p in this.store.always_writeable_properties) new_writeable_properties.add (p); foreach (var p in writeable_properties) new_writeable_properties.add (p); /* Check for changes. */ var changed = false; if (this._writeable_properties.length != new_writeable_properties.size) { changed = true; } else { foreach (var p in this._writeable_properties) { if (new_writeable_properties.contains (p) == false) { changed = true; break; } } } if (changed == true) { this._writeable_properties = new_writeable_properties.to_array (); this.notify_property ("writeable-properties"); } } public void update_linkable_properties (string[] linkable_properties) { var new_linkable_properties = new HashSet (); new_linkable_properties.add_all_array(linkable_properties); /* Check for changes. */ var changed = false; if (this._linkable_properties.length != new_linkable_properties.size) { changed = true; } else { foreach (var p in this._linkable_properties) { if (new_linkable_properties.contains (p) == false) { changed = true; break; } } } if (changed == true) { this._linkable_properties = new_linkable_properties.to_array (); this.notify_property ("linkable-properties"); } } /** * Delay between property changes and notifications. * * This sets an optional delay between client code requesting a property * change (e.g. by calling {@link Folks.NameDetails.change_nickname}) and the * property change taking place and a {@link Object.notify} signal being * emitted for it. * * Delays are in milliseconds. Negative delays mean that property change * notifications happen synchronously in the change method. A delay of 0 * means that property change notifications happen in an idle callback * immediately after the change method. A positive delay means that property * change notifications happen that many milliseconds after the change method * is called. * * @since UNRELEASED */ protected int property_change_delay { get; set; } /** * Callback to effect a property change in a backing store. * * This is called by {@link FolksDummy.Persona.change_property} after the * {@link FolksDummy.Persona.property_change_delay} has expired. It must * effect the property change in the simulated backing store, for example by * calling an ‘update’ method such as * {@link FolksDummy.FullPersona.update_nickname}. * * @since UNRELEASED */ protected delegate void ChangePropertyCallback (); /** * Change a property in the simulated backing store. * * This triggers a property change in the simulated backing store, applying * the current {@link FolksDummy.Persona.property_change_delay} before calling * the given ``callback`` which should actually effect the property change. * * @param property_name name of the property being changed * @param callback callback to call once the change delay has passed * @since UNRELEASED */ protected async void change_property (string property_name, ChangePropertyCallback callback) { if (this.property_change_delay < 0) { /* No delay. */ callback (); } else if (this.property_change_delay == 0) { /* Idle delay. */ Idle.add (() => { callback (); this.change_property.callback (); return false; }); yield; } else { /* Timed delay. */ Timeout.add (this.property_change_delay, () => { callback (); this.change_property.callback (); return false; }); yield; } } } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/CMakeLists.txt0000644000015300001610000000124312632607666027715 0ustar pbuserpbgroup00000000000000project(folks-dummy-lib) vala_precompile(LIB_DUMMY_VALA_C SOURCES internal_0_9_2.vala dummy-backend.vala dummy-full-persona.vala dummy-persona-store.vala dummy-persona.vala GENERATE_VAPI folks-dummy GENERATE_HEADER folks-dummy PACKAGES posix folks gee-0.8 gio-2.0 gobject-2.0 ) add_definitions(-DBACKEND_NAME="dummy") add_definitions(-fPIC) include_directories( ${CMAKE_SOURCE_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_INCLUDE_DIRS} ) add_library(folks-dummy STATIC ${LIB_DUMMY_VALA_C} ) target_link_libraries(folks-dummy ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} ) address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/internal_0_9_2.vala0000644000015300001610000000623312632607666030532 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Raul Gutierrez Segales */ using GLib; using Gee; using Posix; namespace Folks.Internal { public static bool equal_sets (Set a, Set b) { if (a.size != b.size) return false; foreach (var a_elem in a) { if (!b.contains (a_elem)) return false; } return true; } #if ENABLE_PROFILING /* See: http://people.gnome.org/~federico/news-2006-03.html#timeline-tools */ private static void profiling_markv (string format, va_list args) { var formatted = format.vprintf (args); var str = "MARK: %s-%p: %s".printf (Environment.get_prgname (), Thread.self (), formatted); access (str, F_OK); } #endif /** * Emit a profiling point. * * This emits a profiling point with the given message (printf-style), which * can be picked up by profiling tools and timing information extracted. * * @param format printf-style message format * @param ... message arguments * @since 0.7.2 */ public static void profiling_point (string format, ...) { #if ENABLE_PROFILING var args = va_list (); Internal.profiling_markv (format, args); #endif } /** * Start a profiling block. * * This emits a profiling start point with the given message (printf-style), * which can be picked up by profiling tools and timing information extracted. * * This is typically used in a pair with {@link Internal.profiling_end} to * delimit blocks of processing which need timing. * * @param format printf-style message format * @param ... message arguments * @since 0.7.2 */ public static void profiling_start (string format, ...) { #if ENABLE_PROFILING var args = va_list (); Internal.profiling_markv ("START: " + format, args); #endif } /** * End a profiling block. * * This emits a profiling end point with the given message (printf-style), * which can be picked up by profiling tools and timing information extracted. * * This is typically used in a pair with {@link Internal.profiling_start} to * delimit blocks of processing which need timing. * * @param format printf-style message format * @param ... message arguments * @since 0.7.2 */ public static void profiling_end (string format, ...) { #if ENABLE_PROFILING var args = va_list (); Internal.profiling_markv ("END: " + format, args); #endif } } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/lib/dummy-full-persona.vala0000644000015300001610000007753412632607666031602 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Philip Withnall * Copyright (C) 2013 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Philip Withnall * Travis Reitter * Marco Barisione * Raul Gutierrez Segales */ using Folks; using Gee; using GLib; /** * A persona subclass representing a single ‘full’ contact. * * This mocks up a ‘full’ persona which implements all the available property * interfaces provided by libfolks. This is in contrast with * {@link FolksDummy.Persona}, which provides a base class implementing none of * libfolks’ interfaces. * * The full dummy persona can be used to simulate a persona from most libfolks * backends, if writing a custom {@link FolksDummy.Persona} subclass is not an * option. * * There are two sides to this class’ interface: the normal methods required by * the libfolks ‘details’ interfaces, such as * {@link Folks.GenderDetails.change_gender}, * and the backend methods which should be called by test driver code to * simulate changes in the backing store providing this persona, such as * {@link FullPersona.update_gender}. For example, test driver code should call * {@link FullPersona.update_nickname} to simulate the user editing a contact’s * nickname in an online address book which is being exposed to libfolks. The * ``update_``, ``register_`` and ``unregister_`` prefixes are commonly used for * backend methods. * * The API in {@link FolksDummy} is unstable and may change wildly. It is * designed mostly for use by libfolks unit tests. * * @since UNRELEASED */ public class FolksDummy.FullPersona : FolksDummy.Persona, AntiLinkable, AvatarDetails, BirthdayDetails, EmailDetails, FavouriteDetails, GenderDetails, GroupDetails, ImDetails, LocalIdDetails, NameDetails, NoteDetails, PhoneDetails, RoleDetails, UrlDetails, PostalAddressDetails, WebServiceDetails { private const string[] _default_linkable_properties = { "im-addresses", "email-addresses", "local-ids", "web-service-addresses" }; /** * Create a new ‘full’ persona. * * Create a new persona for the {@link FolksDummy.PersonaStore} ``store``, * with the given construct-only properties. * * @param store the store which will contain the persona * @param contact_id a unique free-form string identifier for the persona * @param is_user ``true`` if the persona represents the user, ``false`` * otherwise * @param linkable_properties an array of names of the properties which should * be used for linking this persona to others * * @since UNRELEASED */ public FullPersona (PersonaStore store, string contact_id, bool is_user = false, string[] linkable_properties = {}) { base (store, contact_id, is_user, linkable_properties); } construct { this._local_ids_ro = this._local_ids.read_only_view; this._postal_addresses_ro = this._postal_addresses.read_only_view; this._email_addresses_ro = this._email_addresses.read_only_view; this._phone_numbers_ro = this._phone_numbers.read_only_view; this._notes_ro = this._notes.read_only_view; this._urls_ro = this._urls.read_only_view; this._groups_ro = this._groups.read_only_view; this._roles_ro = this._roles.read_only_view; this._anti_links_ro = this._anti_links.read_only_view; this.update_linkable_properties(FullPersona._default_linkable_properties); } private HashMultiMap _web_service_addresses = new HashMultiMap ( null, null, AbstractFieldDetails.hash_static, AbstractFieldDetails.equal_static); /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public MultiMap web_service_addresses { get { return this._web_service_addresses; } set { this.change_web_service_addresses.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_web_service_addresses ( MultiMap web_service_addresses) throws PropertyError { yield this.change_property ("web-service-addresses", () => { this.update_web_service_addresses (web_service_addresses); }); } private HashSet _local_ids = new HashSet (); private Set _local_ids_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set local_ids { get { if (this._local_ids.contains (this.iid) == false) { this._local_ids.add (this.iid); } return this._local_ids_ro; } set { this.change_local_ids.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_local_ids (Set local_ids) throws PropertyError { yield this.change_property ("local-ids", () => { this.update_local_ids (local_ids); }); } private HashSet _postal_addresses = new HashSet (); private Set _postal_addresses_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set postal_addresses { get { return this._postal_addresses_ro; } set { this.change_postal_addresses.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_postal_addresses ( Set postal_addresses) throws PropertyError { yield this.change_property ("postal-addresses", () => { this.update_postal_addresses (postal_addresses); }); } private HashSet _phone_numbers = new HashSet (); private Set _phone_numbers_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set phone_numbers { get { return this._phone_numbers_ro; } set { this.change_phone_numbers.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_phone_numbers ( Set phone_numbers) throws PropertyError { yield this.change_property ("phone-numbers", () => { this.update_phone_numbers (phone_numbers); }); } private HashSet? _email_addresses = new HashSet ( AbstractFieldDetails.hash_static, AbstractFieldDetails.equal_static); private Set _email_addresses_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set email_addresses { get { return this._email_addresses_ro; } set { this.change_email_addresses.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_email_addresses ( Set email_addresses) throws PropertyError { yield this.change_property ("email-addresses", () => { this.update_email_addresses (email_addresses); }); } private HashSet _notes = new HashSet (); private Set _notes_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set notes { get { return this._notes_ro; } set { this.change_notes.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_notes (Set notes) throws PropertyError { yield this.change_property ("notes", () => { this.update_notes (notes); }); } private LoadableIcon? _avatar = null; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public LoadableIcon? avatar { get { return this._avatar; } set { this.change_avatar.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_avatar (LoadableIcon? avatar) throws PropertyError { yield this.change_property ("avatar", () => { this.update_avatar (avatar); }); } private StructuredName? _structured_name = null; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public StructuredName? structured_name { get { return this._structured_name; } set { this.change_structured_name.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_structured_name (StructuredName? structured_name) throws PropertyError { yield this.change_property ("structured-name", () => { this.update_structured_name (structured_name); }); } private string _full_name = ""; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public string full_name { get { return this._full_name; } set { this.change_full_name.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_full_name (string full_name) throws PropertyError { yield this.change_property ("full-name", () => { this.update_full_name (full_name); }); } private string _nickname = ""; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public string nickname { get { return this._nickname; } set { this.change_nickname.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_nickname (string nickname) throws PropertyError { yield this.change_property ("nickname", () => { this.update_nickname (nickname); }); } private Gender _gender = Gender.UNSPECIFIED; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Gender gender { get { return this._gender; } set { this.change_gender.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_gender (Gender gender) throws PropertyError { yield this.change_property ("gender", () => { this.update_gender (gender); }); } private HashSet _urls = new HashSet (); private Set _urls_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set urls { get { return this._urls_ro; } set { this.change_urls.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_urls (Set urls) throws PropertyError { yield this.change_property ("urls", () => { this.update_urls (urls); }); } private HashMultiMap _im_addresses = new HashMultiMap (null, null, AbstractFieldDetails.hash_static, AbstractFieldDetails.equal_static); /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public MultiMap im_addresses { get { return this._im_addresses; } set { this.change_im_addresses.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_im_addresses ( MultiMap im_addresses) throws PropertyError { yield this.change_property ("im-addresses", () => { this.update_im_addresses (im_addresses); }); } private HashSet _groups = new HashSet (); private Set _groups_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set groups { get { return this._groups_ro; } set { this.change_groups.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_group (string group, bool is_member) throws GLib.Error { /* Nothing to do? */ if ((is_member == true && this._groups.contains (group) == true) || (is_member == false && this._groups.contains (group) == false)) { return; } /* Replace the current set of groups with a modified one. */ var new_groups = new HashSet (); foreach (var category_name in this._groups) { new_groups.add (category_name); } if (is_member == false) { new_groups.remove (group); } else { new_groups.add (group); } yield this.change_groups (new_groups); } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_groups (Set groups) throws PropertyError { yield this.change_property ("groups", () => { this.update_groups (groups); }); } private string? _calendar_event_id = null; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public string? calendar_event_id { get { return this._calendar_event_id; } set { this.change_calendar_event_id.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_calendar_event_id (string? calendar_event_id) throws PropertyError { yield this.change_property ("calendar-event-id", () => { this.update_calendar_event_id (calendar_event_id); }); } private DateTime? _birthday = null; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public DateTime? birthday { get { return this._birthday; } set { this.change_birthday.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_birthday (DateTime? bday) throws PropertyError { yield this.change_property ("birthday", () => { this.update_birthday (bday); }); } private HashSet _roles = new HashSet (); private Set _roles_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set roles { get { return this._roles_ro; } set { this.change_roles.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_roles (Set roles) throws PropertyError { yield this.change_property ("roles", () => { this.update_roles (roles); }); } private bool _is_favourite = false; /** * Whether this contact is a user-defined favourite. * * @since UNRELEASED */ [CCode (notify = false)] public bool is_favourite { get { return this._is_favourite; } set { this.change_is_favourite.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_is_favourite (bool is_favourite) throws PropertyError { yield this.change_property ("is-favourite", () => { this.update_is_favourite (is_favourite); }); } private HashSet _anti_links = new HashSet (); private Set _anti_links_ro; /** * {@inheritDoc} * * @since UNRELEASED */ [CCode (notify = false)] public Set anti_links { get { return this._anti_links_ro; } set { this.change_anti_links.begin (value); } } /** * {@inheritDoc} * * @since UNRELEASED */ public async void change_anti_links (Set anti_links) throws PropertyError { yield this.change_property ("anti-links", () => { this.update_anti_links (anti_links); }); } /* * All the functions below here are to be used by testing code rather than by * libfolks clients. They form the interface which would normally be between * the Persona and a web service or backing store of some kind. */ private HashSet _dup_to_hash_set (Set input_set) { var output_set = new HashSet (); output_set.add_all (input_set); return output_set; } private HashMultiMap _dup_to_hash_multi_map ( MultiMap input_multi_map) { var output_multi_map = new HashMultiMap (); var iter = input_multi_map.map_iterator (); while (iter.next () == true) output_multi_map.set (iter.get_key (), iter.get_value ()); return output_multi_map; } /** * Update persona's gender. * * This simulates a backing-store-side update of the persona's * {@link Folks.GenderDetails.gender} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param gender persona's new gender * @since UNRELEASED */ public void update_gender (Gender gender) { if (this._gender != gender) { this._gender = gender; this.notify_property ("gender"); } } /** * Update persona's birthday calendar event ID. * * This simulates a backing-store-side update of the persona's * {@link Folks.BirthdayDetails.calendar_event_id} property. It is intended to * be used for testing code which consumes this property. If the property * value changes, this results in a property change notification on the * persona. * * @param calendar_event_id persona's new birthday calendar event ID * @since UNRELEASED */ public void update_calendar_event_id (string? calendar_event_id) { if (calendar_event_id != this._calendar_event_id) { this._calendar_event_id = calendar_event_id; this.notify_property ("calendar-event-id"); } } /** * Update persona's birthday. * * This simulates a backing-store-side update of the persona's * {@link Folks.BirthdayDetails.birthday} property. It is intended to be used * for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param birthday persona's new birthday * @since UNRELEASED */ public void update_birthday (DateTime? birthday) { if ((this._birthday == null) != (birthday == null) || (this._birthday != null && birthday != null && !((!) this._birthday).equal ((!) birthday))) { this._birthday = birthday; this.notify_property ("birthday"); } } /** * Update persona's roles. * * This simulates a backing-store-side update of the persona's * {@link Folks.RoleDetails.roles} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param roles persona's new roles * @since UNRELEASED */ public void update_roles (Set roles) { if (!Folks.Internal.equal_sets (roles, this._roles)) { this._roles = this._dup_to_hash_set (roles); this._roles_ro = this._roles.read_only_view; this.notify_property ("roles"); } } /** * Update persona's groups. * * This simulates a backing-store-side update of the persona's * {@link Folks.GroupDetails.groups} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param groups persona's new groups * @since UNRELEASED */ public void update_groups (Set groups) { if (!Folks.Internal.equal_sets (groups, this._groups)) { this._groups = this._dup_to_hash_set (groups); this._groups_ro = this._groups.read_only_view; this.notify_property ("groups"); } } /** * Update persona's web service addresses. * * This simulates a backing-store-side update of the persona's * {@link Folks.WebServiceDetails.web_service_addresses} property. It is * intended to be used for testing code which consumes this property. If the * property value changes, this results in a property change notification on * the persona. * * @param web_service_addresses persona's new web service addresses * @since UNRELEASED */ public void update_web_service_addresses ( MultiMap web_service_addresses) { if (!Utils.multi_map_str_afd_equal (web_service_addresses, this._web_service_addresses)) { this._web_service_addresses = this._dup_to_hash_multi_map ( web_service_addresses); this.notify_property ("web-service-addresses"); } } /** * Update persona's e-mail addresses. * * This simulates a backing-store-side update of the persona's * {@link Folks.EmailDetails.email_addresses} property. It is intended to be * used for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param email_addresses persona's new e-mail addresses * @since UNRELEASED */ public void update_email_addresses (Set email_addresses) { if (!Folks.Internal.equal_sets (email_addresses, this._email_addresses)) { this._email_addresses = this._dup_to_hash_set (email_addresses); this._email_addresses_ro = this._email_addresses.read_only_view; this.notify_property ("email-addresses"); } } /** * Update persona's notes. * * This simulates a backing-store-side update of the persona's * {@link Folks.NoteDetails.notes} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param notes persona's new notes * @since UNRELEASED */ public void update_notes (Set notes) { if (!Folks.Internal.equal_sets (notes, this._notes)) { this._notes = this._dup_to_hash_set (notes); this._notes_ro = this._notes.read_only_view; this.notify_property ("notes"); } } /** * Update persona's full name. * * This simulates a backing-store-side update of the persona's * {@link Folks.NameDetails.full_name} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param full_name persona's new full name * @since UNRELEASED */ public void update_full_name (string full_name) { if (this._full_name != full_name) { this._full_name = full_name; this.notify_property ("full-name"); } } /** * Update persona's nickname. * * This simulates a backing-store-side update of the persona's * {@link Folks.NameDetails.nickname} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param nickname persona's new nickname * @since UNRELEASED */ public void update_nickname (string nickname) { if (this._nickname != nickname) { this._nickname = nickname; this.notify_property ("nickname"); } } /** * Update persona's structured name. * * This simulates a backing-store-side update of the persona's * {@link Folks.NameDetails.structured_name} property. It is intended to be * used for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param structured_name persona's new structured name * @since UNRELEASED */ public void update_structured_name (StructuredName? structured_name) { if (structured_name != null && !((!) structured_name).is_empty ()) { this._structured_name = (!) structured_name; this.notify_property ("structured-name"); } else if (this._structured_name != null) { this._structured_name = null; this.notify_property ("structured-name"); } } /** * Update persona's avatar. * * This simulates a backing-store-side update of the persona's * {@link Folks.AvatarDetails.avatar} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param avatar persona's new avatar * @since UNRELEASED */ public void update_avatar (LoadableIcon? avatar) { if ((this._avatar == null) != (avatar == null) || (this._avatar != null && avatar != null && !((!) this._avatar).equal ((!) avatar))) { this._avatar = avatar; this.notify_property ("avatar"); } } /** * Update persona's URIs. * * This simulates a backing-store-side update of the persona's * {@link Folks.UrlDetails.urls} property. It is intended to be used for * testing code which consumes this property. If the property value changes, * this results in a property change notification on the persona. * * @param urls persona's new URIs * @since UNRELEASED */ public void update_urls (Set urls) { if (!Utils.set_afd_equal (urls, this._urls)) { this._urls = this._dup_to_hash_set (urls); this._urls_ro = this._urls.read_only_view; this.notify_property ("urls"); } } /** * Update persona's IM addresses. * * This simulates a backing-store-side update of the persona's * {@link Folks.ImDetails.im_addresses} property. It is intended to be used * for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param im_addresses persona's new IM addresses * @since UNRELEASED */ public void update_im_addresses ( MultiMap im_addresses) { if (!Utils.multi_map_str_afd_equal (im_addresses, this._im_addresses)) { this._im_addresses = this._dup_to_hash_multi_map ( im_addresses); this.notify_property ("im-addresses"); } } /** * Update persona's phone numbers. * * This simulates a backing-store-side update of the persona's * {@link Folks.PhoneDetails.phone_numbers} property. It is intended to be * used for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param phone_numbers persona's new phone numbers * @since UNRELEASED */ public void update_phone_numbers (Set phone_numbers) { if (!Folks.Internal.equal_sets (phone_numbers, this._phone_numbers)) { this._phone_numbers = this._dup_to_hash_set (phone_numbers); this._phone_numbers_ro = this._phone_numbers.read_only_view; this.notify_property ("phone-numbers"); } } /** * Update persona's postal addresses. * * This simulates a backing-store-side update of the persona's * {@link Folks.PostalAddressDetails.postal_addresses} property. It is * intended to be used for testing code which consumes this property. If the * property value changes, this results in a property change notification on * the persona. * * @param postal_addresses persona's new postal addresses * @since UNRELEASED */ public void update_postal_addresses ( Set postal_addresses) { if (!Folks.Internal.equal_sets ( postal_addresses, this._postal_addresses)) { this._postal_addresses = this._dup_to_hash_set ( postal_addresses); this._postal_addresses_ro = this._postal_addresses.read_only_view; this.notify_property ("postal-addresses"); } } /** * Update persona's local IDs. * * This simulates a backing-store-side update of the persona's * {@link Folks.LocalIdDetails.local_ids} property. It is intended to be used * for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param local_ids persona's new local IDs * @since UNRELEASED */ public void update_local_ids (Set local_ids) { if (!Folks.Internal.equal_sets (local_ids, this.local_ids)) { this._local_ids = this._dup_to_hash_set (local_ids); this._local_ids_ro = this._local_ids.read_only_view; this.notify_property ("local-ids"); } } /** * Update persona's status as a favourite. * * This simulates a backing-store-side update of the persona's * {@link Folks.FavouriteDetails.is_favourite} property. It is intended to be * used for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param is_favourite persona's new status as a favourite * @since UNRELEASED */ public void update_is_favourite (bool is_favourite) { if (is_favourite != this._is_favourite) { this._is_favourite = is_favourite; this.notify_property ("is-favourite"); } } /** * Update persona's anti-links. * * This simulates a backing-store-side update of the persona's * {@link Folks.AntiLinkable.anti_links} property. It is intended to be used * for testing code which consumes this property. If the property value * changes, this results in a property change notification on the persona. * * @param anti_links persona's new anti-links * @since UNRELEASED */ public void update_anti_links (Set anti_links) { if (!Folks.Internal.equal_sets (anti_links, this._anti_links)) { this._anti_links = this._dup_to_hash_set (anti_links); this._anti_links_ro = this._anti_links.read_only_view; this.notify_property ("anti-links"); } } } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/CMakeLists.txt0000644000015300001610000000027212632607666027150 0ustar pbuserpbgroup00000000000000# Remove any link flag to keep it compatible with folks, otherwise folks would not be able to load it set(CMAKE_MODULE_LINKER_FLAGS "") add_subdirectory(lib) add_subdirectory(backend) address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/backend/0000755000015300001610000000000012632610222025754 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/backend/dummy-backend-factory.vala0000644000015300001610000000651712632607666033041 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2009 Zeeshan Ali (Khattak) . * Copyright (C) 2009 Nokia Corporation. * Copyright (C) 2011, 2013 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: Zeeshan Ali (Khattak) * Travis Reitter * Marco Barisione * Raul Gutierrez Segales * * This file was originally part of Rygel. */ using Folks; using Gee; /** * The dummy backend module entry point. * * @backend_store a store to add the dummy backends to * @since UNRELEASED */ public void module_init (BackendStore backend_store) { string[] writable_properties = { Folks.PersonaStore.detail_key (PersonaDetail.AVATAR), Folks.PersonaStore.detail_key (PersonaDetail.BIRTHDAY), Folks.PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES), Folks.PersonaStore.detail_key (PersonaDetail.FULL_NAME), Folks.PersonaStore.detail_key (PersonaDetail.GENDER), Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES), Folks.PersonaStore.detail_key (PersonaDetail.IS_FAVOURITE), Folks.PersonaStore.detail_key (PersonaDetail.NICKNAME), Folks.PersonaStore.detail_key (PersonaDetail.PHONE_NUMBERS), Folks.PersonaStore.detail_key (PersonaDetail.POSTAL_ADDRESSES), Folks.PersonaStore.detail_key (PersonaDetail.ROLES), Folks.PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME), Folks.PersonaStore.detail_key (PersonaDetail.LOCAL_IDS), Folks.PersonaStore.detail_key (PersonaDetail.LOCATION), Folks.PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES), Folks.PersonaStore.detail_key (PersonaDetail.NOTES), // Keep URLS as read-only fields for testing purpose //Folks.PersonaStore.detail_key (PersonaDetail.URLS), Folks.PersonaStore.detail_key (PersonaDetail.GROUPS), null }; /* Create a new persona store. */ var dummy_persona_store = new FolksDummy.PersonaStore ("dummy-store", "Dummy personas", writable_properties); dummy_persona_store.persona_type = typeof (FolksDummy.FullPersona); /* Register it with the backend. */ var persona_stores = new HashSet (); persona_stores.add (dummy_persona_store); var backend = new FolksDummy.Backend (); backend.register_persona_stores (persona_stores); dummy_persona_store.reach_quiescence(); backend_store.add_backend (backend); } /** * The dummy backend module exit point. * * @param backend_store the store to remove the backends from * @since UNRELEASED */ public void module_finalize (BackendStore backend_store) { /* FIXME: No backend_store.remove_backend() API exists. */ } address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/dummy/backend/CMakeLists.txt0000644000015300001610000000135212632607666030537 0ustar pbuserpbgroup00000000000000project(folks-dummy-backend) vala_precompile(DUMMY_VALA_C SOURCES dummy-backend-factory.vala PACKAGES posix folks gee-0.8 gio-2.0 gobject-2.0 CUSTOM_VAPIS ${folks-dummy-lib_BINARY_DIR}/folks-dummy.vapi ) add_definitions(-DBACKEND_NAME="dummy") include_directories( ${CMAKE_SOURCE_DIR} ${folks-dummy-lib_BINARY_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${FOLKS_INCLUDE_DIRS} ) add_library(dummy MODULE ${DUMMY_VALA_C} ) set_target_properties(dummy PROPERTIES PREFIX "" ) target_link_libraries(dummy folks-dummy ${GLIB_LIBRARIES} ${GIO_LIBRARIES} ${FOLKS_LIBRARIES} ) install(TARGETS dummy LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/address-book-service/) address-book-service-0.1.1+16.04.20151211.1/3rd_party/folks/CMakeLists.txt0000644000015300001610000000021612632607666026013 0ustar pbuserpbgroup00000000000000list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/vala) find_package(Vala "0.17.6" REQUIRED) include(UseVala) add_subdirectory(dummy) address-book-service-0.1.1+16.04.20151211.1/3rd_party/CMakeLists.txt0000644000015300001610000000003012632607666024667 0ustar pbuserpbgroup00000000000000add_subdirectory(folks) address-book-service-0.1.1+16.04.20151211.1/dbus/0000755000015300001610000000000012632610222021142 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/dbus/com.canonical.pim.updater.service.in0000644000015300001610000000015112632607666030103 0ustar pbuserpbgroup00000000000000[D-BUS Service] Name=com.canonical.pim.updater Exec=@CMAKE_INSTALL_FULL_LIBEXECDIR@/address-book-updater address-book-service-0.1.1+16.04.20151211.1/dbus/CMakeLists.txt0000644000015300001610000000040512632607666023723 0ustar pbuserpbgroup00000000000000configure_file(com.canonical.pim.updater.service.in ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.pim.updater.service) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.pim.updater.service DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/services/) address-book-service-0.1.1+16.04.20151211.1/common/0000755000015300001610000000000012632610222021475 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/common/sort-clause.cpp0000644000015300001610000001653512632607666024476 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sort-clause.h" #include #include #include #include using namespace QtContacts; namespace galera { static QMap > clauseFieldMap; SortClause::SortClause(const QString &sort) { initialize(); Q_FOREACH(QString sortClause, sort.split(",")) { QContactSortOrder sOrder = fromString(sortClause); if (sOrder.isValid()) { m_sortOrders << sOrder; } } } SortClause::SortClause(QList sort) : m_sortOrders(sort) { initialize(); } SortClause::SortClause(const SortClause &other) : m_sortOrders(other.m_sortOrders) { initialize(); } bool SortClause::isEmpty() const { return m_sortOrders.isEmpty(); } QString SortClause::toString() const { QString result; Q_FOREACH(QContactSortOrder sortOrder, m_sortOrders) { result += toString(sortOrder) + ", "; } if (result.endsWith(", ")) { result = result.mid(0, result.size() - 2); } return result; } QtContacts::QContactSortOrder SortClause::fromString(const QString &clause) const { QStringList sort = clause.trimmed().split(" "); if ((sort.count() == 0) || (sort.count() > 2)) { qWarning() << "Invalid sort clause:" << clause; return QContactSortOrder(); } QString fieldName = sort[0].trimmed().toUpper(); QString orderName = (sort.count() == 2 ? sort[1].trimmed().toUpper() : "ASC"); QtContacts::QContactSortOrder sOrder; if (clauseFieldMap.contains(fieldName)) { QPair details = clauseFieldMap[fieldName]; sOrder.setDetailType(details.first, details.second); Qt::SortOrder order = (orderName == "DESC" ? Qt::DescendingOrder : Qt::AscendingOrder); sOrder.setDirection(order); sOrder.setCaseSensitivity(Qt::CaseInsensitive); return sOrder; } else if (!sort[0].isEmpty()) { qWarning() << "Invalid sort field:" << sort[0]; } return QContactSortOrder(); } QList SortClause::toContactSortOrder() const { return m_sortOrders; } QStringList SortClause::supportedFields() { initialize(); return clauseFieldMap.keys(); } void SortClause::initialize() { if (clauseFieldMap.isEmpty()) { clauseFieldMap["NAME_PREFIX"] = QPair(QContactDetail::TypeName, QContactName::FieldPrefix); clauseFieldMap["FIRST_NAME"] = QPair(QContactDetail::TypeName, QContactName::FieldFirstName); clauseFieldMap["MIDLE_NAME"] = QPair(QContactDetail::TypeName, QContactName::FieldMiddleName); clauseFieldMap["LAST_NAME"] = QPair(QContactDetail::TypeName, QContactName::FieldLastName); clauseFieldMap["NAME_SUFFIX"] = QPair(QContactDetail::TypeName, QContactName::FieldSuffix); clauseFieldMap["FULL_NAME"] = QPair(QContactDetail::TypeDisplayLabel, QContactDisplayLabel::FieldLabel); clauseFieldMap["NICKNAME"] = QPair(QContactDetail::TypeNickname, QContactNickname::FieldNickname); clauseFieldMap["BIRTHDAY"] = QPair(QContactDetail::TypeBirthday, QContactBirthday::FieldBirthday); clauseFieldMap["PHOTO"] = QPair(QContactDetail::TypeAvatar, QContactAvatar::FieldImageUrl); clauseFieldMap["ORG_ROLE"] = QPair(QContactDetail::TypeOrganization, QContactOrganization::FieldRole); clauseFieldMap["ORG_NAME"] = QPair(QContactDetail::TypeOrganization, QContactOrganization::FieldName); clauseFieldMap["ORG_DEPARTMENT"]= QPair(QContactDetail::TypeOrganization, QContactOrganization::FieldDepartment); clauseFieldMap["ORG_LOCATION"] = QPair(QContactDetail::TypeOrganization, QContactOrganization::FieldLocation); clauseFieldMap["ORG_TITLE"] = QPair(QContactDetail::TypeOrganization, QContactOrganization::FieldTitle); clauseFieldMap["EMAIL"] = QPair(QContactDetail::TypeEmailAddress, QContactEmailAddress::FieldEmailAddress); clauseFieldMap["PHONE"] = QPair(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); clauseFieldMap["ADDR_STREET"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldStreet); clauseFieldMap["ADDR_LOCALITY"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldLocality); clauseFieldMap["ADDR_REGION"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldRegion); clauseFieldMap["ADDR_COUNTRY"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldCountry); clauseFieldMap["ADDR_POSTCODE"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldPostcode); clauseFieldMap["ADDR_POST_OFFICE_BOX"] = QPair(QContactDetail::TypeAddress, QContactAddress::FieldPostOfficeBox); clauseFieldMap["IM_URI"] = QPair(QContactDetail::TypeOnlineAccount, QContactOnlineAccount::FieldAccountUri); clauseFieldMap["IM_PROTOCOL"] = QPair(QContactDetail::TypeOnlineAccount, QContactOnlineAccount::FieldProtocol); clauseFieldMap["TAG"] = QPair(QContactDetail::TypeTag, QContactTag::FieldTag); clauseFieldMap["URL"] = QPair(QContactDetail::TypeUrl, QContactUrl::FieldUrl); } } QString SortClause::toString(const QContactSortOrder &sort) const { QPair clausePair = qMakePair(sort.detailType(), sort.detailField()); Q_FOREACH(QString key, clauseFieldMap.keys()) { if (clauseFieldMap[key] == clausePair) { return key + (sort.direction() == Qt::AscendingOrder ? " ASC" : " DESC"); } } if (sort.isValid()) { qWarning() << "No sorting support for" << sort; } return ""; } } address-book-service-0.1.1+16.04.20151211.1/common/filter.cpp0000644000015300001610000003706612632607666023524 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QtContacts; namespace galera { Filter::Filter(const QString &filter) { m_filter = buildFilter(filter); } Filter::Filter(const QtContacts::QContactFilter &filter) { m_filter = parseFilter(filter); } QString Filter::toString() const { return toString(m_filter); } QtContacts::QContactFilter Filter::toContactFilter() const { return m_filter; } bool Filter::test(const QContact &contact, const QDateTime &deletedDate) const { if (deletedDate.isValid() && !includeRemoved()) { return false; } return testFilter(m_filter, contact, deletedDate); } bool Filter::testFilter(const QContactFilter& filter, const QContact &contact, const QDateTime &deletedDate) { switch(filter.type()) { case QContactFilter::IdFilter: return QContactManagerEngine::testFilter(filter, contact); case QContactFilter::ChangeLogFilter: { const QContactChangeLogFilter bf(filter); if (bf.eventType() == QContactChangeLogFilter::EventRemoved) { return (deletedDate >= bf.since()); } else if (QContactManagerEngine::testFilter(filter, contact)) { return true; } break; } case QContactFilter::ContactDetailFilter: { const QContactDetailFilter cdf(filter); if (cdf.detailType() == QContactDetail::TypeUndefined) return false; /* See if this contact has one of these details in it */ const QList& details = contact.details(cdf.detailType()); if (details.count() == 0) return false; /* can't match */ /* See if we need to check the values */ if (cdf.detailField() == -1) return true; /* just testing for the presence of a detail of the specified type */ if (cdf.matchFlags() & QContactFilter::MatchPhoneNumber) { /* Doing phone number filtering. We hand roll an implementation here, backends will obviously want to override this. */ QString input = cdf.value().toString(); /* Look at every detail in the set of details and compare */ for (int j = 0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QString& valueString = detail.value(cdf.detailField()).toString(); if (comparePhoneNumbers(input, valueString, cdf.matchFlags())) { return true; } } } else if (QContactManagerEngine::testFilter(cdf, contact)) { return true; } break; } case QContactFilter::IntersectionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QContactIntersectionFilter bf(filter); const QList& terms = bf.filters(); if (terms.isEmpty()) { break; } Q_FOREACH(const QContactFilter &f, terms) { if (!testFilter(f, contact, deletedDate)) { return false; } } return true; } break; case QContactFilter::UnionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QContactUnionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { for(int j = 0; j < terms.count(); j++) { if (testFilter(terms.at(j), contact, deletedDate)) { return true; } } return false; } // Fall through to end } break; default: if (QContactManagerEngine::testFilter(filter, contact)) { return true; } break; } return false; } bool Filter::comparePhoneNumbers(const QString &input, const QString &value, QContactFilter::MatchFlags flags) { static i18n::phonenumbers::PhoneNumberUtil *phonenumberUtil = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); std::string stdPreprocessedInput(input.toStdString()); std::string stdProcessedValue(value.toStdString()); phonenumberUtil->NormalizeDiallableCharsOnly(&stdPreprocessedInput); phonenumberUtil->NormalizeDiallableCharsOnly(&stdProcessedValue); QString preprocessedInput = QString::fromStdString(stdPreprocessedInput); QString preprocessedValue = QString::fromStdString(stdProcessedValue); // if one of they does not contain digits return false if (preprocessedInput.isEmpty() || preprocessedValue.isEmpty()) { return false; } bool mc = flags & QContactFilter::MatchContains; bool msw = flags & QContactFilter::MatchStartsWith; bool mew = flags & QContactFilter::MatchEndsWith; bool me = flags & QContactFilter::MatchExactly; if (!mc && !msw && !mew && !me && ((preprocessedInput.length() < 6) || (preprocessedValue.length() < 6))) { return preprocessedInput == preprocessedValue; } if (mc) { return preprocessedValue.contains(preprocessedInput); } else if (msw) { return preprocessedValue.startsWith(preprocessedInput); } else if (mew) { return preprocessedValue.endsWith(preprocessedInput); } else { i18n::phonenumbers::PhoneNumberUtil::MatchType match = phonenumberUtil->IsNumberMatchWithTwoStrings(input.toStdString(), value.toStdString()); if (me) { return match == i18n::phonenumbers::PhoneNumberUtil::EXACT_MATCH; } else { return match > i18n::phonenumbers::PhoneNumberUtil::NO_MATCH; } } return false; } bool Filter::checkIsValid(const QList filters) const { Q_FOREACH(const QContactFilter &f, filters) { switch (f.type()) { case QContactFilter::InvalidFilter: return false; case QContactFilter::IntersectionFilter: return checkIsValid(static_cast(f).filters()); case QContactFilter::UnionFilter: return checkIsValid(static_cast(f).filters()); default: return true; } } // list is empty return true; } bool Filter::isIdFilter(const QContactFilter &filter) const { if (filter.type() == QContactFilter::IdFilter) { return true; } // FIXME: We convert IdFilter to GUID filter check Filter implementation if (filter.type() == QContactFilter::UnionFilter) { QContactUnionFilter uFilter(filter); if ((uFilter.filters().size() == 1) && (uFilter.filters().at(0).type() == QContactFilter::ContactDetailFilter)) { QContactDetailFilter dFilter(uFilter.filters().at(0)); return ((dFilter.detailType() == QContactDetail::TypeGuid) && (dFilter.detailField() == QContactGuid::FieldGuid)); } } return false; } bool Filter::isValid() const { return checkIsValid(QList() << m_filter); } bool Filter::checkIsEmpty(const QList filters) const { Q_FOREACH(const QContactFilter &f, filters) { switch (f.type()) { case QContactFilter::DefaultFilter: return true; case QContactFilter::IntersectionFilter: return checkIsEmpty(static_cast(f).filters()); case QContactFilter::UnionFilter: return checkIsEmpty(static_cast(f).filters()); default: return false; } } // list is empty return true; } bool Filter::isEmpty() const { return checkIsEmpty(QList() << m_filter); } bool Filter::includeRemoved() const { // FIXME: Return deleted contacts for id filter // we need this to avoid problems with buteo, we should fix buteo to query for any contact // include deleted ones. if (isIdFilter(m_filter)) { return true; } return includeRemoved(m_filter); } QString Filter::phoneNumberToFilter() const { return phoneNumberToFilter(m_filter); } QStringList Filter::idsToFilter() const { return idsToFilter(m_filter); } QString Filter::phoneNumberToFilter(const QtContacts::QContactFilter &filter) { switch (filter.type()) { case QContactFilter::ContactDetailFilter: { const QContactDetailFilter cdf(filter); if (cdf.matchFlags() & QContactFilter::MatchPhoneNumber) { return cdf.value().toString(); } break; } case QContactFilter::UnionFilter: { // if the union contains only the phone filter we'are still able to optimize const QContactUnionFilter uf(filter); if (uf.filters().size() == 1) { return phoneNumberToFilter(uf.filters().first()); } break; } case QContactFilter::IntersectionFilter: { const QContactIntersectionFilter cif(filter); Q_FOREACH(const QContactFilter &f, cif.filters()) { QString phoneToFilter = phoneNumberToFilter(f); if (!phoneToFilter.isEmpty()) { return phoneToFilter; } } break; } default: break; } return QString(); } QStringList Filter::idsToFilter(const QtContacts::QContactFilter &filter) { QStringList result; switch (filter.type()) { case QContactFilter::ContactDetailFilter: { const QContactDetailFilter cdf(filter); if ((cdf.detailType() == QContactDetail::TypeGuid) && (cdf.detailField() == QContactGuid::FieldGuid) && cdf.matchFlags().testFlag(QContactFilter::MatchExactly)) { result << cdf.value().toString(); } break; } case QContactFilter::IdFilter: { const QContactIdFilter idf(filter); Q_FOREACH(const QContactId &id, idf.ids()) { result << id.toString(); } break; } case QContactFilter::UnionFilter: { const QContactUnionFilter uf(filter); Q_FOREACH(const QContactFilter &f, uf.filters()) { result.append(idsToFilter(f)); } break; } case QContactFilter::IntersectionFilter: { const QContactIntersectionFilter cif(filter); Q_FOREACH(const QContactFilter &f, cif.filters()) { result.append(idsToFilter(f)); } break; } default: break; } return result; } QString Filter::toString(const QtContacts::QContactFilter &filter) { QByteArray filterArray; QDataStream filterData(&filterArray, QIODevice::WriteOnly); filterData << filter; return QString::fromLatin1(filterArray.toBase64()); } QtContacts::QContactFilter Filter::buildFilter(const QString &filter) { QContactFilter filterObject; QByteArray filterArray = QByteArray::fromBase64(filter.toLatin1()); QDataStream filterData(&filterArray, QIODevice::ReadOnly); filterData >> filterObject; return filterObject; } bool Filter::includeRemoved(const QList filters) { Q_FOREACH(const QContactFilter &f, filters) { if (includeRemoved(f)) { return true; } } return false; } // we will include removed contacts if the filter contains ChangeLogFilter with type EventRemoved; bool Filter::includeRemoved(const QContactFilter &filter) { if (filter.type() == QContactFilter::ChangeLogFilter) { QContactChangeLogFilter fChangeLog(filter); return (fChangeLog.eventType() == QContactChangeLogFilter::EventRemoved); } else if (filter.type() == QContactFilter::UnionFilter) { return includeRemoved(QContactUnionFilter(filter).filters()); } else if (filter.type() == QContactFilter::IntersectionFilter) { return includeRemoved(QContactIntersectionFilter(filter).filters()); } else { return false; } } QtContacts::QContactFilter Filter::parseFilter(const QtContacts::QContactFilter &filter) { QContactFilter newFilter; switch (filter.type()) { case QContactFilter::IdFilter: newFilter = parseIdFilter(filter); break; case QContactFilter::UnionFilter: newFilter = parseUnionFilter(filter); break; case QContactFilter::IntersectionFilter: newFilter = parseIntersectionFilter(filter); break; default: return filter; } return newFilter; } QtContacts::QContactFilter Filter::parseUnionFilter(const QtContacts::QContactFilter &filter) { QContactUnionFilter newFilter; const QContactUnionFilter *unionFilter = static_cast(&filter); Q_FOREACH(const QContactFilter &f, unionFilter->filters()) { newFilter << parseFilter(f); } return newFilter; } QtContacts::QContactFilter Filter::parseIntersectionFilter(const QtContacts::QContactFilter &filter) { QContactIntersectionFilter newFilter; const QContactIntersectionFilter intersectFilter(filter); Q_FOREACH(const QContactFilter &f, intersectFilter.filters()) { newFilter << parseFilter(f); } return newFilter; } QtContacts::QContactFilter Filter::parseIdFilter(const QContactFilter &filter) { // ContactId to be serialized between process is necessary to instantiate the manager in both sides. // Since the dbus service does not instantiate the manager we translate it to QContactDetailFilter // using Guid values. This is possible because our server use the Guid to build the contactId. const QContactIdFilter *idFilter = static_cast(&filter); if (idFilter->ids().isEmpty()) { return filter; } QContactUnionFilter newFilter; Q_FOREACH(const QContactId &id, idFilter->ids()) { QContactDetailFilter detailFilter; detailFilter.setMatchFlags(QContactFilter::MatchExactly); detailFilter.setDetailType(QContactDetail::TypeGuid, QContactGuid::FieldGuid); detailFilter.setValue(id.toString().split(":").last()); newFilter << detailFilter; } return newFilter; } } address-book-service-0.1.1+16.04.20151211.1/common/source.cpp0000644000015300001610000001447412632607673023533 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "source.h" using namespace QtContacts; namespace galera { Source::Source() : m_accountId(0), m_isReadOnly(false), m_isPrimary(false) { } Source::Source(const Source &other) : m_id(other.id()), m_displayName(other.displayLabel()), m_applicationId(other.applicationId()), m_providerName(other.providerName()), m_isReadOnly(other.isReadOnly()), m_isPrimary(other.isPrimary()), m_accountId(other.accountId()) { } Source::Source(QString id, const QString &displayName, const QString &applicationId, const QString &providerName, uint accountId, bool isReadOnly, bool isPrimary) : m_id(id), m_displayName(displayName), m_applicationId(applicationId), m_providerName(providerName), m_accountId(accountId), m_isReadOnly(isReadOnly), m_isPrimary(isPrimary) { Q_ASSERT(displayName.isEmpty() == false); } bool Source::isValid() const { return !m_id.isEmpty(); } bool Source::isPrimary() const { return m_isPrimary; } uint Source::accountId() const { return m_accountId; } QString Source::applicationId() const { return m_applicationId; } QString Source::providerName() const { return m_providerName; } QString Source::id() const { return m_id; } QString Source::displayLabel() const { return m_displayName; } bool Source::isReadOnly() const { return m_isReadOnly; } void Source::registerMetaType() { qRegisterMetaType("Source"); qRegisterMetaType("SourceList"); qDBusRegisterMetaType(); qDBusRegisterMetaType(); } QDBusArgument &operator<<(QDBusArgument &argument, const Source &source) { argument.beginStructure(); argument << source.m_id; argument << source.m_displayName; argument << source.m_providerName; argument << source.m_applicationId; argument << source.m_accountId; argument << source.m_isReadOnly; argument << source.m_isPrimary; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, Source &source) { argument.beginStructure(); argument >> source.m_id; argument >> source.m_displayName; argument >> source.m_providerName; argument >> source.m_applicationId; argument >> source.m_accountId; argument >> source.m_isReadOnly; argument >> source.m_isPrimary; argument.endStructure(); return argument; } QDBusArgument &operator<<(QDBusArgument &argument, const SourceList &sources) { argument.beginArray(qMetaTypeId()); for(int i=0; i < sources.count(); ++i) { argument << sources[i]; } argument.endArray(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, SourceList &sources) { argument.beginArray(); sources.clear(); while(!argument.atEnd()) { Source source; argument >> source; sources << source; } argument.endArray(); return argument; } QtContacts::QContact Source::toContact(const QContactId &id) const { QContact contact; // contact group type contact.setType(QContactType::TypeGroup); // id contact.setId(id); // guid QContactGuid guid; guid.setGuid(m_id); contact.saveDetail(&guid); // display name QContactDisplayLabel displayLabel; displayLabel.setLabel(m_displayName); contact.saveDetail(&displayLabel); // read-only QContactExtendedDetail readOnly; readOnly.setName("READ-ONLY"); readOnly.setData(m_isReadOnly); contact.saveDetail(&readOnly); // Primary QContactExtendedDetail primary; primary.setName("IS-PRIMARY"); primary.setData(m_isPrimary); contact.saveDetail(&primary); // Account Id QContactExtendedDetail accountId; accountId.setName("ACCOUNT-ID"); accountId.setData(m_accountId); contact.saveDetail(&accountId); // Application Id QContactExtendedDetail applicationId; applicationId.setName("APPLICATION-ID"); applicationId.setData(m_applicationId); contact.saveDetail(&applicationId); // Provider QContactExtendedDetail provider; provider.setName("PROVIDER"); provider.setData(m_providerName); contact.saveDetail(&provider); return contact; } Source Source::fromQContact(const QtContacts::QContact &contact) { if (contact.type() != QContactType::TypeGroup) { qWarning() << "Invalid contact type"; return Source(); } QContactGuid guid = contact.detail(); QContactDisplayLabel displayLabel = contact.detail(); bool isReadOnly = false; bool isPrimary = false; uint accountId = 0; QString applicationId; QString providerName; Q_FOREACH(const QContactExtendedDetail &xDetail, contact.details()) { if (xDetail.name() == "READ-ONLY") { isReadOnly = xDetail.data().toBool(); } if (xDetail.name() == "IS-PRIMARY") { isPrimary = xDetail.data().toBool(); } if (xDetail.name() == "ACCOUNT-ID") { accountId = xDetail.data().toUInt(); } if (xDetail.name() == "APPLICATION-ID") { applicationId = xDetail.data().toString(); } if (xDetail.name() == "PROVIDER") { providerName = xDetail.data().toString(); } } return Source(guid.guid(), displayLabel.label(), applicationId, providerName, accountId, isReadOnly, isPrimary); } } // namespace Galera address-book-service-0.1.1+16.04.20151211.1/common/vcard-parser.cpp0000644000015300001610000004211612632607666024620 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "vcard-parser.h" #include #include #include #include #include #include #include #include #include #include using namespace QtVersit; using namespace QtContacts; namespace { class ContactExporterDetailHandler : public QVersitContactExporterDetailHandlerV2 { public: virtual void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact); Q_UNUSED(document); Q_UNUSED(processedFields); Q_UNUSED(toBeRemoved); // Export custom property PIDMAP if (detail.type() == QContactDetail::TypeSyncTarget) { QContactSyncTarget syncTarget = static_cast(detail); QVersitProperty prop; prop.setValueType(QVersitProperty::CompoundType); prop.setName(galera::VCardParser::PidMapFieldName); QStringList values; values << syncTarget.syncTarget() << syncTarget.value(QContactSyncTarget::FieldSyncTarget + 1).toString() << syncTarget.value(QContactSyncTarget::FieldSyncTarget + 2).toString(); prop.setValue(values); *toBeAdded << prop; } if (detail.type() == QContactDetail::TypeExtendedDetail) { QVersitProperty prop; prop.setName(detail.value(QContactExtendedDetail::FieldName).toString()); prop.setValue(detail.value(QContactExtendedDetail::FieldData).toString()); *toBeAdded << prop; } if (toBeAdded->size() == 0) { return; } // export detailUir as PID field if (!detail.detailUri().isEmpty()) { QVersitProperty &prop = toBeAdded->last(); QMultiHash params = prop.parameters(); params.insert(galera::VCardParser::PidFieldName, detail.detailUri()); prop.setParameters(params); } // export read-only info if (detail.accessConstraints().testFlag(QContactDetail::ReadOnly)) { QVersitProperty &prop = toBeAdded->last(); QMultiHash params = prop.parameters(); params.insert(galera::VCardParser::ReadOnlyFieldName, "YES"); prop.setParameters(params); } // export Irremovable info if (detail.accessConstraints().testFlag(QContactDetail::Irremovable)) { QVersitProperty &prop = toBeAdded->last(); QMultiHash params = prop.parameters(); params.insert(galera::VCardParser::IrremovableFieldName, "YES"); prop.setParameters(params); } switch (detail.type()) { case QContactDetail::TypeAvatar: { QContactAvatar avatar = static_cast(detail); QVersitProperty &prop = toBeAdded->last(); prop.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); prop.setValue(avatar.imageUrl().toString(QUrl::RemoveUserInfo)); break; } case QContactDetail::TypePhoneNumber: { QString prefName = galera::VCardParser::PreferredActionNames[QContactDetail::TypePhoneNumber]; QContactDetail prefPhone = contact.preferredDetail(prefName); if (prefPhone == detail) { QVersitProperty &prop = toBeAdded->last(); QMultiHash params = prop.parameters(); params.insert(galera::VCardParser::PrefParamName, "1"); prop.setParameters(params); } break; } default: break; } } virtual void contactProcessed(const QContact& contact, QVersitDocument* document) { if (!contact.id().isNull() && contact.details().isEmpty()) { // translate contact id to uid vcard QVersitProperty prop; prop.setName("UID"); prop.setValue(contact.id().toString().split("::").last()); document->addProperty(prop); } Q_UNUSED(contact); document->removeProperties("X-QTPROJECT-EXTENDED-DETAIL"); } }; class ContactImporterPropertyHandler : public QVersitContactImporterPropertyHandlerV2 { public: virtual void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool *alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document); Q_UNUSED(contact); if (!*alreadyProcessed && (property.name() == galera::VCardParser::PidMapFieldName)) { QContactSyncTarget target; QStringList values = property.value().split(QStringLiteral(";")); target.setSyncTarget(values.value(0)); if (values.size() > 1) { target.setValue(QContactSyncTarget::FieldSyncTarget + 1, values.value(1)); } if (values.size() > 2) { target.setValue(QContactSyncTarget::FieldSyncTarget + 2, values.value(2)); } *updatedDetails << target; *alreadyProcessed = true; } if (!*alreadyProcessed && (property.name().startsWith("X-"))) { QContactExtendedDetail xDet; xDet.setName(property.name()); xDet.setData(property.value()); *updatedDetails << xDet; *alreadyProcessed = true; } if (!*alreadyProcessed) { return; } QString pid = property.parameters().value(galera::VCardParser::PidFieldName); if (!pid.isEmpty()) { QContactDetail &det = updatedDetails->last(); det.setDetailUri(pid); } bool ro = (property.parameters().value(galera::VCardParser::ReadOnlyFieldName, "NO") == "YES"); bool irremovable = (property.parameters().value(galera::VCardParser::IrremovableFieldName, "NO") == "YES"); if (ro && irremovable) { QContactDetail &det = updatedDetails->last(); QContactManagerEngine::setDetailAccessConstraints(&det, QContactDetail::ReadOnly | QContactDetail::Irremovable); } else if (ro) { QContactDetail &det = updatedDetails->last(); QContactManagerEngine::setDetailAccessConstraints(&det, QContactDetail::ReadOnly); } else if (irremovable) { QContactDetail &det = updatedDetails->last(); QContactManagerEngine::setDetailAccessConstraints(&det, QContactDetail::Irremovable); } if (updatedDetails->size() == 0) { return; } // Remove empty phone and address subtypes QContactDetail &det = updatedDetails->last(); switch (det.type()) { case QContactDetail::TypePhoneNumber: { QContactPhoneNumber phone = static_cast(det); if (phone.subTypes().isEmpty()) { det.setValue(QContactPhoneNumber::FieldSubTypes, QVariant()); } if (property.parameters().contains(galera::VCardParser::PrefParamName)) { m_prefferedPhone = phone; } break; } case QContactDetail::TypeAvatar: { QString value = property.parameters().value("VALUE"); if (value == "URL") { det.setValue(QContactAvatar::FieldImageUrl, QUrl(property.value())); } break; } default: break; } } virtual void documentProcessed(const QVersitDocument& document, QContact* contact) { if (!m_prefferedPhone.isEmpty()) { contact->setPreferredDetail(galera::VCardParser::PreferredActionNames[QContactDetail::TypePhoneNumber], m_prefferedPhone); m_prefferedPhone = QContactDetail(); } if (contact->id().isNull() && !contact->detail().isEmpty()) { QContactId id = QContactId::fromString( QString("qtcontacts:galera::%1").arg(contact->detail().guid())); contact->setId(id); } //update contact timestamp with X-CREATED-AT QContactTimestamp timestamp = contact->detail(); QDateTime createdAt = timestamp.lastModified(); Q_FOREACH(const QVersitProperty &prop, document.properties()) { if (prop.name() == "X-CREATED-AT") { createdAt = QDateTime::fromString(prop.value(), Qt::ISODate).toUTC(); break; } } timestamp.setCreated(createdAt); contact->saveDetail(×tamp); } private: QContactDetail m_prefferedPhone; }; } namespace galera { const QString VCardParser::PidMapFieldName = QStringLiteral("CLIENTPIDMAP"); const QString VCardParser::PidFieldName = QStringLiteral("PID"); const QString VCardParser::PrefParamName = QStringLiteral("PREF"); const QString VCardParser::IrremovableFieldName = QStringLiteral("IRREMOVABLE"); const QString VCardParser::ReadOnlyFieldName = QStringLiteral("READ-ONLY"); const QString VCardParser::TagFieldName = QStringLiteral("TAG"); static QMap prefferedActions() { QMap values; values.insert(QContactDetail::TypeAddress, QStringLiteral("ADR")); values.insert(QContactDetail::TypeEmailAddress, QStringLiteral("EMAIL")); values.insert(QContactDetail::TypeNote, QStringLiteral("NOTE")); values.insert(QContactDetail::TypeOnlineAccount, QStringLiteral("IMPP")); values.insert(QContactDetail::TypeOrganization, QStringLiteral("ORG")); values.insert(QContactDetail::TypePhoneNumber, QStringLiteral("TEL")); values.insert(QContactDetail::TypeUrl, QStringLiteral("URL")); return values; } const QMap VCardParser::PreferredActionNames = prefferedActions(); VCardParser::VCardParser(QObject *parent) : QObject(parent), m_versitWriter(0), m_versitReader(0) { m_exporterHandler = new ContactExporterDetailHandler; m_importerHandler = new ContactImporterPropertyHandler; } VCardParser::~VCardParser() { waitForFinished(); delete m_exporterHandler; delete m_importerHandler; } QList VCardParser::vcardToContactSync(const QStringList &vcardList) { VCardParser parser; parser.vcardToContact(vcardList); parser.waitForFinished(); return parser.contactsResult(); } QtContacts::QContact VCardParser::vcardToContact(const QString &vcard) { return vcardToContactSync(QStringList() << vcard).value(0, QContact()); } void VCardParser::vcardToContact(const QStringList &vcardList) { if (m_versitReader) { qWarning() << "Import operation in progress."; return; } m_vcardsResult.clear(); m_contactsResult.clear(); QString vcards = vcardList.join("\r\n"); m_versitReader = new QVersitReader(vcards.toUtf8()); connect(m_versitReader, SIGNAL(resultsAvailable()), SLOT(onReaderResultsAvailable())); connect(m_versitReader, SIGNAL(stateChanged(QVersitReader::State)), SLOT(onReaderStateChanged(QVersitReader::State))); m_versitReader->startReading(); } void VCardParser::cancel() { if (m_versitReader) { m_versitReader->disconnect(this); m_versitReader->cancel(); m_versitReader->deleteLater(); m_versitReader = 0; } if (m_versitWriter) { m_versitWriter->disconnect(this); m_versitWriter->cancel(); m_versitWriter->deleteLater(); m_versitWriter = 0; } Q_EMIT canceled(); } void VCardParser::waitForFinished() { if (m_versitReader) { m_versitReader->waitForFinished(); } if (m_versitWriter) { m_versitWriter->waitForFinished(); } // wait state changed events to arrive QCoreApplication::sendPostedEvents(this); } QStringList VCardParser::vcardResult() const { return m_vcardsResult; } QList VCardParser::contactsResult() const { return m_contactsResult; } void VCardParser::onReaderResultsAvailable() { //NOTHING FOR NOW } QStringList VCardParser::splitVcards(const QByteArray &vcardList) { QStringList result; int start = 0; while(start < vcardList.size()) { int pos = vcardList.indexOf("BEGIN:VCARD", start + 1); if (pos == -1) { pos = vcardList.length(); } QByteArray vcard = vcardList.mid(start, (pos - start)); result << vcard; start = pos; } return result; } void VCardParser::onReaderStateChanged(QVersitReader::State state) { if (m_versitReader && (state == QVersitReader::FinishedState)) { QList documents = m_versitReader->results(); QVersitContactImporter contactImporter; contactImporter.setPropertyHandler(m_importerHandler); if (!contactImporter.importDocuments(documents)) { qWarning() << "Fail to import contacts"; return; } m_contactsResult = contactImporter.contacts(); Q_EMIT contactsParsed(contactImporter.contacts()); delete m_versitReader; m_versitReader = 0; } } void VCardParser::contactToVcard(QList contacts) { if (m_versitWriter) { qWarning() << "Export operation in progress."; return; } m_vcardsResult.clear(); m_contactsResult.clear(); QVersitContactExporter exporter; exporter.setDetailHandler(m_exporterHandler); if (!exporter.exportContacts(contacts, QVersitDocument::VCard30Type)) { qWarning() << "Fail to export contacts" << exporter.errors(); return; } m_versitWriter = new QVersitWriter(&m_vcardData); connect(m_versitWriter, SIGNAL(stateChanged(QVersitWriter::State)), SLOT(onWriterStateChanged(QVersitWriter::State))); m_versitWriter->startWriting(exporter.documents()); } void VCardParser::onWriterStateChanged(QVersitWriter::State state) { if (state == QVersitWriter::FinishedState) { QStringList vcards = VCardParser::splitVcards(m_vcardData); m_vcardsResult = vcards; Q_EMIT vcardParsed(vcards); delete m_versitWriter; m_versitWriter = 0; } } QStringList VCardParser::contactToVcardSync(QList contacts) { VCardParser parser; parser.contactToVcard(contacts); parser.waitForFinished(); return parser.vcardResult(); } QString VCardParser::contactToVcard(const QContact &contact) { return contactToVcardSync(QList() << contact).value(0, QString()); } } //namespace address-book-service-0.1.1+16.04.20151211.1/common/fetch-hint.h0000644000015300001610000000312412632607666023721 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_FETCH_HINT_H__ #define __GALERA_FETCH_HINT_H__ #include #include #include namespace galera { class FetchHint { public: FetchHint(const QtContacts::QContactFetchHint &hint); FetchHint(const QString &hint); FetchHint(const FetchHint &other); FetchHint(); bool isEmpty() const; QString toString() const; QStringList fields() const; QtContacts::QContactFetchHint toContactFetchHint() const; static QMap contactFieldNames(); static QList parseFieldNames(const QStringList &fieldNames); private: QtContacts::QContactFetchHint m_hint; QString m_strHint; QStringList m_fields; void update(); static QtContacts::QContactFetchHint buildFilter(const QString &originalHint); }; } #endif address-book-service-0.1.1+16.04.20151211.1/common/source.h0000644000015300001610000000426612632607673023176 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_SOURCE_H__ #define __GALERA_SOURCE_H__ #include #include #include namespace galera { class Source { public: Source(); Source(const Source &other); Source(QString id, const QString &displayName, const QString &applicationId, const QString &providerName, uint accountId, bool isReadOnly, bool isPrimary); friend QDBusArgument &operator<<(QDBusArgument &argument, const Source &source); friend const QDBusArgument &operator>>(const QDBusArgument &argument, Source &source); static void registerMetaType(); QString id() const; QString displayLabel() const; bool isReadOnly() const; bool isValid() const; bool isPrimary() const; uint accountId() const; QString applicationId() const; QString providerName() const; QtContacts::QContact toContact(const QtContacts::QContactId &id) const; static Source fromQContact(const QtContacts::QContact &contact); private: QString m_id; QString m_displayName; QString m_applicationId; QString m_providerName; uint m_accountId; bool m_isReadOnly; bool m_isPrimary; }; typedef QList SourceList; QDBusArgument &operator<<(QDBusArgument &argument, const SourceList &sources); const QDBusArgument &operator>>(const QDBusArgument &argument, SourceList &sources); } // namespace galera Q_DECLARE_METATYPE(galera::Source) Q_DECLARE_METATYPE(galera::SourceList) #endif address-book-service-0.1.1+16.04.20151211.1/common/fetch-hint.cpp0000644000015300001610000001035612632607666024261 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fetch-hint.h" #include #include using namespace QtContacts; namespace galera { FetchHint::FetchHint() { //empty } FetchHint::FetchHint(const QtContacts::QContactFetchHint &hint) : m_hint(hint) { update(); } bool FetchHint::isEmpty() const { return m_strHint.isEmpty(); } FetchHint::FetchHint(const QString &hint) : m_hint(buildFilter(hint)) { update(); } FetchHint::FetchHint(const FetchHint &other) : m_hint(other.m_hint) { update(); } QString FetchHint::toString() const { return m_strHint; } QStringList FetchHint::fields() const { return m_fields; } void FetchHint::update() { m_strHint.clear(); m_fields.clear(); QMap map = contactFieldNames(); Q_FOREACH(QContactDetail::DetailType type, m_hint.detailTypesHint()) { QString fieldName = map.key(type, ""); if (!fieldName.isEmpty()) { m_fields << fieldName; } } if (!m_fields.isEmpty()) { m_strHint = QString("FIELDS:") + m_fields.join(","); } } QtContacts::QContactFetchHint FetchHint::toContactFetchHint() const { return m_hint; } QMap FetchHint::contactFieldNames() { static QMap map; if (map.isEmpty()) { map.insert("ADR", QContactAddress::Type); map.insert("BDAY", QContactBirthday::Type); map.insert("EMAIL", QContactEmailAddress::Type); map.insert("FN", QContactDisplayLabel::Type); map.insert("GENDER", QContactGender::Type); map.insert("N", QContactName::Type); map.insert("NICKNAME", QContactNickname::Type); map.insert("NOTE", QContactNote::Type); map.insert("ORG", QContactOrganization::Type); map.insert("PHOTO", QContactAvatar::Type); map.insert("TEL", QContactPhoneNumber::Type); map.insert("URL", QContactUrl::Type); map.insert("TAG", QContactTag::Type); map.insert("X-QTPROJECT-FAVORITE", QContactFavorite::Type); } return map; } QList FetchHint::parseFieldNames(const QStringList &fieldNames) { QList result; const QMap map = contactFieldNames(); Q_FOREACH(QString fieldName, fieldNames) { if (map.contains(fieldName)) { result << map[fieldName]; } } return result; } // Parse string // Format: :VALUE0,VALUE1;:VALUE0,VALUE1 QtContacts::QContactFetchHint FetchHint::buildFilter(const QString &originalHint) { QContactFetchHint result; if (!originalHint.isEmpty()) { QString hint = QString(originalHint).replace(" ",""); QStringList groups = hint.split(";"); Q_FOREACH(QString group, groups) { QStringList values = group.split(":"); if (values.count() == 2) { if (values[0] == "FIELDS") { QList fields; QMap map = contactFieldNames(); Q_FOREACH(QString field, values[1].split(",")) { if (map.contains(field)) { fields << map[field]; } } result.setDetailTypesHint(fields); } } else { qWarning() << "invalid fech hint: " << values; } } } return result; } } address-book-service-0.1.1+16.04.20151211.1/common/dbus-service-defs.h0000644000015300001610000000264612632607666025212 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __DBUS_SERVICE_DEFS_H__ #define __DBUS_SERVICE_DEFS_H__ #define ALTERNATIVE_CPIM_SERVICE_NAME "CANONICAL_PIN_SERVICE_NAME" #define CPIM_SERVICE_NAME "com.canonical.pim" #define CPIM_ADDRESSBOOK_OBJECT_PATH "/com/canonical/pim/AddressBook" #define CPIM_ADDRESSBOOK_IFACE_NAME "com.canonical.pim.AddressBook" #define CPIM_ADDRESSBOOK_VIEW_OBJECT_PATH "/com/canonical/pim/AddressBookView" #define CPIM_ADDRESSBOOK_VIEW_IFACE_NAME "com.canonical.pim.AddressBookView" //Updater #define CPIM_UPDATE_SERVICE_NAME "com.canonical.pim.updater" #define CPIM_UPDATE_OBJECT_PATH "/com/canonical/pim/Updater" #define CPIM_UPDATE_OBJECT_IFACE_NAME "com.canonical.pim.Updater" #endif address-book-service-0.1.1+16.04.20151211.1/common/filter.h0000644000015300001610000000565712632607666023172 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_FILTER_H__ #define __GALERA_FILTER_H__ #include #include #include namespace galera { class Filter { public: Filter(const QtContacts::QContactFilter &filter); Filter(const QString &filter); Filter(const Filter &other); QString toString() const; QtContacts::QContactFilter toContactFilter() const; bool test(const QtContacts::QContact &contact, const QDateTime &deletedDate = QDateTime()) const; bool isValid() const; bool isEmpty() const; bool includeRemoved() const; // optimization by index QString phoneNumberToFilter() const; QStringList idsToFilter() const; private: QtContacts::QContactFilter m_filter; Filter(); bool checkIsEmpty(const QList filters) const; bool checkIsValid(const QList filters) const; bool isIdFilter(const QtContacts::QContactFilter &filter) const; static QString phoneNumberToFilter(const QtContacts::QContactFilter &filter); static QStringList idsToFilter(const QtContacts::QContactFilter &filter); static QString toString(const QtContacts::QContactFilter &filter); static QtContacts::QContactFilter buildFilter(const QString &filter); static bool includeRemoved(const QList filters); static bool includeRemoved(const QtContacts::QContactFilter &filter); static QString detailFilterToString(const QtContacts::QContactFilter &filter); static QString unionFilterToString(const QtContacts::QContactFilter &filter); static QtContacts::QContactFilter parseFilter(const QtContacts::QContactFilter &filter); static QtContacts::QContactFilter parseIdFilter(const QtContacts::QContactFilter &filter); static QtContacts::QContactFilter parseUnionFilter(const QtContacts::QContactFilter &filter); static QtContacts::QContactFilter parseIntersectionFilter(const QtContacts::QContactFilter &filter); static bool testFilter(const QtContacts::QContactFilter& filter, const QtContacts::QContact &contact, const QDateTime &deletedDate); static bool comparePhoneNumbers(const QString &phoneNumberA, const QString &phoneNumberB, QtContacts::QContactFilter::MatchFlags flags); }; } #endif address-book-service-0.1.1+16.04.20151211.1/common/CMakeLists.txt0000644000015300001610000000075412632607666024265 0ustar pbuserpbgroup00000000000000set(GALERA_COMMON_LIB galera-common) set(GALERA_COMMON_LIB_SRC filter.cpp fetch-hint.cpp sort-clause.cpp source.cpp vcard-parser.cpp ) set(GALERA_COMMON_LIB_HEADERS filter.h fetch-hint.h sort-clause.h source.h vcard-parser.h dbus-service-defs.h ) add_library(${GALERA_COMMON_LIB} STATIC ${GALERA_COMMON_LIB_SRC} ${GALERA_COMMON_LIB_HEADERS} ) target_link_libraries(${GALERA_COMMON_LIB} Qt5::Core Qt5::Versit Qt5::Contacts ) address-book-service-0.1.1+16.04.20151211.1/common/vcard-parser.h0000644000015300001610000000567612632607666024277 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_VCARD_PARSER_H__ #define __GALERA_VCARD_PARSER_H__ #include #include #include #include #include #include #include #include #include namespace galera { using namespace QtVersit; class VCardParser : public QObject { Q_OBJECT public: VCardParser(QObject *parent=0); ~VCardParser(); void contactToVcard(QList contacts); void vcardToContact(const QStringList &vcardList); void cancel(); void waitForFinished(); QStringList vcardResult() const; QList contactsResult() const; static const QString PidMapFieldName; static const QString PidFieldName; static const QString PrefParamName; static const QString IrremovableFieldName; static const QString ReadOnlyFieldName; static const QString TagFieldName; static const QMap PreferredActionNames; static QtContacts::QContact vcardToContact(const QString &vcard); static QList vcardToContactSync(const QStringList &vcardList); static QString contactToVcard(const QtContacts::QContact &contact); static QStringList contactToVcardSync(QList contacts); static QStringList splitVcards(const QByteArray &vcardList); Q_SIGNALS: void vcardParsed(const QStringList &vcards); void contactsParsed(QList contacts); void finished(); void canceled(); private Q_SLOTS: void onWriterStateChanged(QVersitWriter::State state); void onReaderStateChanged(QVersitReader::State state); void onReaderResultsAvailable(); private: QtVersit::QVersitWriter *m_versitWriter; QtVersit::QVersitReader *m_versitReader; QtVersit::QVersitContactExporterDetailHandlerV2 *m_exporterHandler; QtVersit::QVersitContactImporterPropertyHandlerV2 *m_importerHandler; QByteArray m_vcardData; QStringList m_vcardsResult; QList m_contactsResult; }; } Q_DECLARE_METATYPE(QList) #endif address-book-service-0.1.1+16.04.20151211.1/common/sort-clause.h0000644000015300001610000000270412632607666024134 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_SORT_CLAUSE_H__ #define __GALERA_SORT_CLAUSE_H__ #include #include #include namespace galera { class ContactEntry; class SortClause { public: SortClause(const QString &sort); SortClause(QList sort); SortClause(const SortClause &other); bool isEmpty() const; QString toString() const; QList toContactSortOrder() const; static QStringList supportedFields(); private: QList m_sortOrders; QtContacts::QContactSortOrder fromString(const QString &clause) const; QString toString(const QtContacts::QContactSortOrder &sort) const; static void initialize(); }; } #endif address-book-service-0.1.1+16.04.20151211.1/upstart/0000755000015300001610000000000012632610222021707 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/upstart/address-book-service.conf.in0000644000015300001610000000052012632607666027215 0ustar pbuserpbgroup00000000000000description "address-book-service" author "Bill Filler " start on started dbus and xsession SESSION=ubuntu-touch stop on runlevel [06] respawn pre-start script echo "START `date`" end script post-stop script echo "STOP `date`" end script exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/address-book-service address-book-service-0.1.1+16.04.20151211.1/upstart/CMakeLists.txt0000644000015300001610000000062512632607666024474 0ustar pbuserpbgroup00000000000000configure_file(address-book-service.conf.in ${CMAKE_CURRENT_BINARY_DIR}/address-book-service.conf) configure_file(address-book-updater.conf.in ${CMAKE_CURRENT_BINARY_DIR}/address-book-updater.conf) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/address-book-service.conf ${CMAKE_CURRENT_BINARY_DIR}/address-book-updater.conf DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions/) address-book-service-0.1.1+16.04.20151211.1/upstart/address-book-updater.conf.in0000644000015300001610000000042012632607666027220 0ustar pbuserpbgroup00000000000000description "address-book-update" author "Renato Araujo Oliveira Filho " start on started address-book-service and started msyncd and started sync-monitor stop on runlevel [06] exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/address-book-updater --sync address-book-service-0.1.1+16.04.20151211.1/CMakeLists.txt0000644000015300001610000000751512632607666022777 0ustar pbuserpbgroup00000000000000project(address-book-service C CXX) cmake_minimum_required(VERSION 2.8.9) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) include(FindPkgConfig) # Standard install paths include(GNUInstallDirs) find_package(Qt5Core REQUIRED) find_package(Qt5DBus REQUIRED) find_package(Qt5Versit REQUIRED) find_package(Qt5Contacts REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5Xml REQUIRED) find_package(Qt5Test REQUIRED) find_package(LibPhoneNumber REQUIRED) find_program(DBUS_RUNNER dbus-test-runner) find_program(INTLTOOL_MERGE_EXECUTABLE intltool-merge) find_program(INTLTOOL_EXTRACT_EXECUTABLE intltool-extract) add_definitions(-DQT_NO_KEYWORDS) pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) pkg_check_modules(EDATASERVER REQUIRED libedataserver-1.2>=3.8) pkg_check_modules(FOLKS REQUIRED folks>=0.9.0) pkg_check_modules(FOLKS_EDS REQUIRED folks-eds) pkg_check_modules(GLIB_ACCOUNTS REQUIRED libaccounts-glib) pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu) pkg_check_modules(URL_DISPATCHER REQUIRED url-dispatcher-1) pkg_check_modules(AccountsQt5 REQUIRED accounts-qt5) pkg_check_modules(LIBNOTIFY REQUIRED libnotify) if(EDATASERVER_VERSION VERSION_LESS "3.16") set(EVOLUTION_API_3_17 "0") set(EVOLUTION_ADDRESSBOOK_SERVICE_NAME "org.gnome.evolution.dataserver.AddressBook6") set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources3") else() set(EVOLUTION_API_3_17 "1") set(EVOLUTION_ADDRESSBOOK_SERVICE_NAME "org.gnome.evolution.dataserver.AddressBook9") set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources4") endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) add_definitions(-std=c++11) # I18n set(PACKAGE ${CMAKE_PROJECT_NAME}) set(GETTEXT_PACKAGE "address-book-service") add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" -DGETTEXT_LOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") # Coverage tools OPTION(ENABLE_COVERAGE "Build with coverage analysis support" OFF) if(ENABLE_COVERAGE) message(STATUS "Using coverage flags") find_program(COVERAGE_COMMAND gcov) if(NOT COVERAGE_COMMAND) message(FATAL_ERROR "gcov command not found") endif() SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) endif() # Address Sanitizer OPTION(ENABLE_ADDRSANITIZER "Build with address sanitizer support" OFF) if(ENABLE_ADDRSANITIZER) message(STATUS "Using address sanitizer flags") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") enable_testing() add_custom_target(check) add_custom_command(TARGET check COMMAND ctest -V WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_subdirectory(3rd_party) add_subdirectory(common) add_subdirectory(eds-extension) add_subdirectory(lib) add_subdirectory(src) add_subdirectory(contacts) add_subdirectory(updater) add_subdirectory(upstart) add_subdirectory(dbus) add_subdirectory(po) #add_subdirectory(eds-test) if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc") # Some tests fail when running on PPPC check bug #1294229 message(STATUS "Tests disable for ppc") else() add_subdirectory(tests) endif() set(TEST_DATA_DIR "${CMAKE_SOURCE_DIR}/tests/data") configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) address-book-service-0.1.1+16.04.20151211.1/examples/0000755000015300001610000000000012632610222022023 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/examples/contacts.py0000644000015300001610000000664112632607666024244 0ustar pbuserpbgroup00000000000000#!/usr/bin/env python3 # -*- encoding: utf-8 -*- # # Copyright 2013 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; version 3. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # import dbus import argparse VCARD_JOE = """ BEGIN:VCARD VERSION:3.0 N:Gump;Forrest FN:Forrest Gump TEL;TYPE=WORK,VOICE;PID=1.1:(111) 555-1212 TEL;TYPE=HOME,VOICE;PID=1.2:(404) 555-1212 EMAIL;TYPE=PREF,INTERNET;PID=1.1:forrestgump@example.com END:VCARD """ class Contacts(object): def __init__(self): self.bus = None self.addr = None self.addr_iface = None def connect(self): self.bus = dbus.SessionBus() self.addr = self.bus.get_object('com.canonical.pim', '/com/canonical/pim/AddressBook') self.addr_iface = dbus.Interface(self.addr, dbus_interface='com.canonical.pim.AddressBook') def query(self, fields = '', query = '', sources = []): view_path = self.addr_iface.query(fields, query, []) view = self.bus.get_object('com.canonical.pim', view_path) view_iface = dbus.Interface(view, dbus_interface='com.canonical.pim.AddressBookView') contacts = view_iface.contactsDetails([], 0, -1) view.close() return contacts def update(self, vcard): return self.addr_iface.updateContacts([vcard]) def create(self, vcard): return self.addr_iface.createContact(vcard, "") def delete(self, ids): return self.addr_iface.removeContacts(ids) service = Contacts() service.connect() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('command', choices=['query','create','update', 'delete','load']) parser.add_argument('filename', action='store', nargs='?') args = parser.parse_args() if args.command == 'query': contacts = service.query() if contacts: for contact in contacts: print (contact) else: print ("No contacts found") if args.command == 'update': vcard = VCARD_JOE contactId = service.create(vcard) vcard = vcard.replace("VERSION:3.0", "VERSION:3.0\nUID:%s" % (contactId)) vcard = vcard.replace("N:Gump;Forrest", "N:Hanks;Tom") vcard = vcard.replace("FN:Forrest Gump", "FN:Tom Hanks") print (service.update(vcard)) if args.command == 'create': print ("New UID:", service.create(VCARD_JOE)) if args.command == 'delete': vcard = VCARD_JOE contactId = service.create(vcard) print ("Deleted contact: %d" % service.delete([contactId])) if args.command == 'load': if args.filename: f = open(args.filename, 'r') vcard = f.read() print ("New UID:", service.create(vcard)) else: print ("You must supply a path to a VCARD") address-book-service-0.1.1+16.04.20151211.1/contacts/0000755000015300001610000000000012632610222022023 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/contacts/qcontact-backend.h0000644000015300001610000001335112632607666025422 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALER_QCONTACT_BACKEND_H__ #define __GALER_QCONTACT_BACKEND_H__ #include #include #include #include #include #include namespace galera { class GaleraContactsService; class GaleraEngineFactory : public QtContacts::QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "galera.json") public: QtContacts::QContactManagerEngine* engine(const QMap ¶meters, QtContacts::QContactManager::Error*); QString managerName() const; QtContacts::QContactEngineId* createContactEngineId(const QMap ¶meters, const QString &engineIdString) const; }; class GaleraManagerEngine : public QtContacts::QContactManagerEngine { Q_OBJECT public: static GaleraManagerEngine *createEngine(const QMap ¶meters); ~GaleraManagerEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; /*! \reimp */ int managerVersion() const; /* Filtering */ virtual QList contactIds(const QtContacts::QContactFilter &filter, const QList &sortOrders, QtContacts::QContactManager::Error *error) const; virtual QList contacts(const QtContacts::QContactFilter &filter, const QList& sortOrders, const QtContacts::QContactFetchHint &fetchHint, QtContacts::QContactManager::Error *error) const; virtual QList contacts(const QList &contactIds, const QtContacts::QContactFetchHint& fetchHint, QMap *errorMap, QtContacts::QContactManager::Error *error) const; virtual QtContacts::QContact contact(const QtContacts::QContactId &contactId, const QtContacts::QContactFetchHint &fetchHint, QtContacts::QContactManager::Error *error) const; virtual bool saveContact(QtContacts::QContact *contact, QtContacts::QContactManager::Error *error); virtual bool removeContact(const QtContacts::QContactId &contactId, QtContacts::QContactManager::Error *error); virtual bool saveRelationship(QtContacts::QContactRelationship *relationship, QtContacts::QContactManager::Error *error); virtual bool removeRelationship(const QtContacts::QContactRelationship &relationship, QtContacts::QContactManager::Error *error); virtual bool saveContacts(QList *contacts, QMap *errorMap, QtContacts::QContactManager::Error *error); virtual bool saveContacts(QList *contacts, const QList &typeMask, QMap *errorMap, QtContacts::QContactManager::Error *error); virtual bool removeContacts(const QList &contactIds, QMap *errorMap, QtContacts::QContactManager::Error *error); /* "Self" contact id (MyCard) */ virtual bool setSelfContactId(const QtContacts::QContactId &contactId, QtContacts::QContactManager::Error *error); virtual QtContacts::QContactId selfContactId(QtContacts::QContactManager::Error *error) const; /* Relationships between contacts */ virtual QList relationships(const QString &relationshipType, const QtContacts::QContact& participant, QtContacts::QContactRelationship::Role role, QtContacts::QContactManager::Error *error) const; virtual bool saveRelationships(QList *relationships, QMap* errorMap, QtContacts::QContactManager::Error *error); virtual bool removeRelationships(const QList &relationships, QMap *errorMap, QtContacts::QContactManager::Error *error); /* Validation for saving */ virtual bool validateContact(const QtContacts::QContact &contact, QtContacts::QContactManager::Error *error) const; /* Asynchronous Request Support */ virtual void requestDestroyed(QtContacts::QContactAbstractRequest *req); virtual bool startRequest(QtContacts::QContactAbstractRequest *req); virtual bool cancelRequest(QtContacts::QContactAbstractRequest *req); virtual bool waitForRequestFinished(QtContacts::QContactAbstractRequest *req, int msecs); /* Capabilities reporting */ virtual bool isRelationshipTypeSupported(const QString &relationshipType, QtContacts::QContactType::TypeValues contactType) const; virtual bool isFilterSupported(const QtContacts::QContactFilter &filter) const; virtual QList supportedDataTypes() const; private: GaleraManagerEngine(); QList contactIds(const QList &contacts) const; GaleraContactsService *m_service; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontact-engineid.h0000644000015300001610000000335612632607666025621 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACT_ENGINEID_H__ #define __GALERA_QCONTACT_ENGINEID_H__ #include namespace galera { class GaleraEngineId : public QtContacts::QContactEngineId { public: GaleraEngineId(); ~GaleraEngineId(); GaleraEngineId(const QString &contactId, const QString &managerUri); GaleraEngineId(const GaleraEngineId &other); GaleraEngineId(const QMap ¶meters, const QString &engineIdString); bool isEqualTo(const QtContacts::QContactEngineId *other) const; bool isLessThan(const QtContacts::QContactEngineId *other) const; QString managerUri() const; QContactEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const; #endif #ifndef QT_NO_DATASTREAM friend QDataStream& operator<<(QDataStream& out, const GaleraEngineId& filter); friend QDataStream& operator>>(QDataStream& in, GaleraEngineId& filter); #endif uint hash() const; private: QString m_contactId; QString m_managerUri; }; } //namespace #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactfetchrequest-data.cpp0000644000015300001610000001046412632607666027724 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontactfetchrequest-data.h" #include "common/vcard-parser.h" #include #include using namespace QtContacts; namespace galera { QContactFetchRequestData::QContactFetchRequestData(QContactAbstractRequest *request, QDBusInterface *view, const FetchHint &hint) : QContactRequestData(request), m_runningParser(0), m_view(0), m_offset(0), m_hint(hint) { if (view) { updateView(view); } } QContactFetchRequestData::~QContactFetchRequestData() { delete m_runningParser; m_runningParser = 0; } int QContactFetchRequestData::offset() const { return m_offset; } QDBusInterface* QContactFetchRequestData::view() const { return m_view.data(); } void QContactFetchRequestData::setVCardParser(VCardParser *parser) { m_runningParser = parser; } void QContactFetchRequestData::clearVCardParser() { m_runningParser = 0; } void QContactFetchRequestData::updateView(QDBusInterface* view) { m_view = QSharedPointer(view, QContactFetchRequestData::deleteView); } QStringList QContactFetchRequestData::fields() const { return m_hint.fields(); } void QContactFetchRequestData::updateOffset(int offset) { m_offset += offset; } QList QContactFetchRequestData::result() const { return m_result; } void QContactFetchRequestData::update(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { if (error != QtContacts::QContactManager::NoError) { m_result.clear(); } QContactRequestData::update(state, error, errorMap); } void QContactFetchRequestData::update(QList result, QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { m_result = result; m_allResults += result; QContactRequestData::update(state, error, errorMap); } void QContactFetchRequestData::cancel() { if (m_runningParser) { m_runningParser->cancel(); } QContactRequestData::cancel(); } void QContactFetchRequestData::notifyError(QContactFetchRequest *request, QContactManager::Error error) { QContactManagerEngine::updateContactFetchRequest(request, QList(), error, QContactAbstractRequest::FinishedState); } void QContactFetchRequestData::updateRequest(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { QList result; // send all results only in the finished state, this will avoid a contact update in every updateContactFetchRequest switch(state) { case QContactAbstractRequest::FinishedState: result = m_allResults; break; default: result = m_result; } QContactManagerEngine::updateContactFetchRequest(static_cast(m_request.data()), result, error, state); } void QContactFetchRequestData::deleteView(QDBusInterface *view) { if (view) { view->asyncCall("close"); delete view; } } } //namespace address-book-service-0.1.1+16.04.20151211.1/contacts/galera.json0000644000015300001610000000003512632607666024171 0ustar pbuserpbgroup00000000000000{ "Keys": [ "galera" ] } address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactrequest-data.cpp0000644000015300001610000000620612632607666026711 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontactrequest-data.h" #include #include using namespace QtContacts; namespace galera { QContactRequestData::QContactRequestData(QContactAbstractRequest *request, QDBusPendingCallWatcher *watcher) : m_request(request), m_eventLoop(0) { updateWatcher(watcher); } QContactRequestData::~QContactRequestData() { Q_ASSERT(m_eventLoop == 0); m_request.clear(); } QContactAbstractRequest* QContactRequestData::request() const { return m_request.data(); } bool QContactRequestData::isLive() const { return !m_request.isNull() && (m_request->state() == QContactAbstractRequest::ActiveState); } void QContactRequestData::cancel() { m_watcher.clear(); if (!m_request.isNull()) { update(QContactAbstractRequest::CanceledState, QContactManager::NoError); m_request.clear(); } // quit event loop if waiting if (m_eventLoop) { m_eventLoop->quit(); } } void QContactRequestData::wait() { if (m_eventLoop) { qWarning() << "Recursive wait call"; Q_ASSERT(false); } QMutexLocker locker(&m_waiting); if (isLive()) { QEventLoop eventLoop; m_eventLoop = &eventLoop; eventLoop.exec(); m_eventLoop = 0; } } void QContactRequestData::releaseRequest() { m_request.clear(); } void QContactRequestData::finish(QContactManager::Error error) { update(QContactAbstractRequest::FinishedState, error, m_errorMap); } void QContactRequestData::deleteLater() { // skip delete if still running if (m_waiting.tryLock()) { m_waiting.unlock(); delete this; } } void QContactRequestData::updateWatcher(QDBusPendingCallWatcher *watcher) { m_watcher.clear(); if (watcher) { m_watcher = QSharedPointer(watcher, QContactRequestData::deleteWatcher); } } void QContactRequestData::update(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { if (!isLive()) { return; } updateRequest(state, error, errorMap); if (m_eventLoop && (state != QContactAbstractRequest::ActiveState)) { m_eventLoop->quit(); } } void QContactRequestData::deleteWatcher(QDBusPendingCallWatcher *watcher) { if (watcher) { delete watcher; } } } //namespace address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactsaverequest-data.h0000644000015300001610000000552612632607673027237 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACTSAVEREQUESTDATA_DATA_H__ #define __GALERA_QCONTACTSAVEREQUESTDATA_DATA_H__ #include "qcontactrequest-data.h" #include #include #include #include namespace galera { class GaleraEngineId; class QContactSaveRequestData : public QContactRequestData { public: QContactSaveRequestData(QtContacts::QContactSaveRequest *request); void prepareToUpdate(); void prepareToCreate(); bool hasNext() const; QString nextContact(QString *syncTargetName); QtContacts::QContact currentContact() const; QStringList allPendingContacts() const; void updateCurrentContactId(GaleraEngineId *engineId); void updateCurrentContact(const QtContacts::QContact &contact); void updatePendingContacts(QStringList vcards); bool hasNextGroup() const; Source nextGroup(); Source currentGroup() const; SourceList allPendingGroups() const; void updateCurrentGroup(const Source &group, const QString &managerUri); void updatePendingGroups(const SourceList &groups, const QString &managerUri); void notifyUpdateError(QtContacts::QContactManager::Error error); void notifyError(QtContacts::QContactManager::Error error); static void notifyError(QtContacts::QContactSaveRequest *request, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NotSupportedError); protected: virtual void updateRequest(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error, QMap errorMap); private: QMap m_contactsToUpdate; QMap m_contactsToCreate; QMap m_pendingContacts; QMap m_pendingContactsSyncTarget; QMap::Iterator m_currentContact; QMap m_pendingGroups; QMap::Iterator m_currentGroup; void prepareContacts(QMap contacts); }; } Q_DECLARE_METATYPE(galera::QContactSaveRequestData*) #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactfetchrequest-data.h0000644000015300001610000000577012632607666027375 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACTFETCHREQUEST_DATA_H__ #define __GALERA_QCONTACTFETCHREQUEST_DATA_H__ #include "qcontactrequest-data.h" #include #include #include #include #include #include #include namespace galera { class VCardParser; class QContactFetchRequestData : public QContactRequestData { public: QContactFetchRequestData(QtContacts::QContactAbstractRequest *request, QDBusInterface *view, const FetchHint &hint = FetchHint()); ~QContactFetchRequestData(); QStringList fields() const; void updateOffset(int offset); int offset() const; void updateView(QDBusInterface *view); QDBusInterface* view() const; void setVCardParser(VCardParser *parser); void clearVCardParser(); QList result() const; void update(QList result, QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NoError, QMap errorMap = QMap()); void cancel(); static void notifyError(QtContacts::QContactFetchRequest *request, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NotSupportedError); protected: QList m_result; QList m_allResults; virtual void updateRequest(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error, QMap errorMap); virtual void update(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NoError, QMap errorMap = QMap()); private: VCardParser *m_runningParser; QSharedPointer m_view; int m_offset; FetchHint m_hint; static void deleteView(QDBusInterface *view); }; } #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactremoverequest-data.cpp0000644000015300001610000000546712632607666030137 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontactremoverequest-data.h" #include #include using namespace QtContacts; namespace galera { QContactRemoveRequestData::QContactRemoveRequestData(QContactRemoveRequest *request) : QContactRequestData(request) { static QString sourcePrefix("source@"); Q_FOREACH(QContactId contactId, request->contactIds()) { QString id = contactId.toString().split(":").last(); // WORKAROUND: Today there is no QtContacts API for contacts sources/address-book // the only way to remove a source is using the same functionas contact, because // of that we need to parse the contact id to check if it is a contact ID or a // source id, this works for EDS backend but could fail for others backends. if (id.startsWith(sourcePrefix)) { m_sourcesIds << id.mid(sourcePrefix.size()); } else { m_contactsIds << id; } } } QStringList QContactRemoveRequestData::contactIds() const { return m_contactsIds; } QStringList QContactRemoveRequestData::sourcesIds() const { return m_sourcesIds; } void QContactRemoveRequestData::notifyError(QContactRemoveRequest *request, QContactManager::Error error) { //TODO: fill erro map with the failed contact QContactManagerEngine::updateContactRemoveRequest(request, error, QMap(), QContactAbstractRequest::FinishedState); } void QContactRemoveRequestData::updateRequest(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { QContactManagerEngine::updateContactRemoveRequest(static_cast(m_request.data()), error, errorMap, state); } } //namespace address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactremoverequest-data.h0000644000015300001610000000320012632607666027563 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACTREMOVEREQUEST_DATA_H__ #define __GALERA_QCONTACTREMOVEREQUEST_DATA_H__ #include "qcontactrequest-data.h" #include #include namespace galera { class QContactRemoveRequestData : public QContactRequestData { public: QContactRemoveRequestData(QtContacts::QContactRemoveRequest *request); QStringList contactIds() const; QStringList sourcesIds() const; static void notifyError(QtContacts::QContactRemoveRequest *request, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NotSupportedError); protected: virtual void updateRequest(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error, QMap errorMap); private: QStringList m_contactsIds; QStringList m_sourcesIds; }; } #endif address-book-service-0.1.1+16.04.20151211.1/contacts/contacts-service.h0000644000015300001610000001140312632607673025467 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __CONTACTS_SERVICE_H__ #define __CONTACTS_SERVICE_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include class QDBusInterface; using namespace QtContacts; // necessary for signal signatures namespace galera { class QContactRequestData; class QContactSaveRequestData; class QContactFetchRequestData; class QContactRemoveRequestData; class GaleraContactsService : public QObject { Q_OBJECT public: GaleraContactsService(const QString &managerUri); GaleraContactsService(const GaleraContactsService &other); ~GaleraContactsService(); QList engines() const; void appendEngine(QtContacts::QContactManagerEngine *engine); void removeEngine(QtContacts::QContactManagerEngine *engine); QList relationships() const; void addRequest(QtContacts::QContactAbstractRequest *request); void cancelRequest(QtContacts::QContactAbstractRequest *request); void waitRequest(QtContacts::QContactAbstractRequest *request); void releaseRequest(QtContacts::QContactAbstractRequest *request); void setShowInvisibleContacts(bool show); Q_SIGNALS: void contactsAdded(QList ids); void contactsRemoved(QList ids); void contactsUpdated(QList ids); void serviceChanged(); private Q_SLOTS: void onContactsAdded(const QStringList &ids); void onContactsRemoved(const QStringList &ids); void onContactsUpdated(const QStringList &ids); void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); void onServiceReady(); void onVCardsParsed(QList contacts); void onVCardParseCanceled(); private: QString m_managerUri; // for faster lookup. QDBusServiceWatcher *m_serviceWatcher; bool m_serviceIsReady; int m_pageSize; bool m_showInvisibleContacts; QSharedPointer m_iface; QString m_serviceName; QList m_runningRequests; Q_INVOKABLE void initialize(); Q_INVOKABLE void deinitialize(); bool isOnline() const; void fetchContacts(QtContacts::QContactFetchRequest *request); void fetchContactsContinue(QContactFetchRequestData *data, QDBusPendingCallWatcher *call); void fetchContactsGroupsContinue(QContactFetchRequestData *request, QDBusPendingCallWatcher *call); void fetchContactsById(QtContacts::QContactFetchByIdRequest *request); void fetchContactsPage(QContactFetchRequestData *data); void fetchContactsDone(QContactFetchRequestData *data, QDBusPendingCallWatcher *call); void saveContact(QtContacts::QContactSaveRequest *request); void createGroupsStart(QContactSaveRequestData *data); void createContactsStart(QContactSaveRequestData *data); void updateGroups(QContactSaveRequestData *data); void updateGroupsDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call); void updateContacts(QContactSaveRequestData *data); void updateContactDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call); void createContactsDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call); void createGroupDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call); void removeContact(QtContacts::QContactRemoveRequest *request); void removeContactContinue(QContactRemoveRequestData *data, QDBusPendingCallWatcher *call); void removeContactDone(QContactRemoveRequestData *data, QDBusPendingCallWatcher *call); void destroyRequest(QContactRequestData *request); QList parseIds(const QStringList &ids) const; }; } #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactfetchbyidrequest-data.cpp0000644000015300001610000000473212632607666030575 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontactfetchbyidrequest-data.h" #include #include using namespace QtContacts; namespace galera { QContactFetchByIdRequestData::QContactFetchByIdRequestData(QContactFetchByIdRequest *request, QDBusInterface *view) : QContactFetchRequestData(request, view) { } void QContactFetchByIdRequestData::notifyError(QContactFetchByIdRequest *request, QContactManager::Error error) { QContactManagerEngine::updateContactFetchByIdRequest(request, QList(), error, QMap(), QContactAbstractRequest::FinishedState); } void QContactFetchByIdRequestData::updateRequest(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { QList result; // send all results only in the finished state, this will avoid a contact update in every updateContactFetchRequest switch(state) { case QContactAbstractRequest::FinishedState: result = m_allResults; break; default: result = m_result; } QContactManagerEngine::updateContactFetchByIdRequest(static_cast(m_request.data()), result, error, errorMap, state); } } //namespace address-book-service-0.1.1+16.04.20151211.1/contacts/contacts-service.cpp0000644000015300001610000010041712632607673026026 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "contacts-service.h" #include "qcontact-engineid.h" #include "qcontactrequest-data.h" #include "qcontactfetchrequest-data.h" #include "qcontactfetchbyidrequest-data.h" #include "qcontactremoverequest-data.h" #include "qcontactsaverequest-data.h" #include "common/vcard-parser.h" #include "common/filter.h" #include "common/fetch-hint.h" #include "common/sort-clause.h" #include "common/dbus-service-defs.h" #include "common/source.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ALTERNATIVE_CPIM_SERVICE_PAGE_SIZE "CANONICAL_PIM_SERVICE_PAGE_SIZE" #define FETCH_PAGE_SIZE 25 using namespace QtVersit; using namespace QtContacts; namespace galera { GaleraContactsService::GaleraContactsService(const QString &managerUri) : m_managerUri(managerUri), m_serviceIsReady(false), m_iface(0) { Source::registerMetaType(); if (qEnvironmentVariableIsSet(ALTERNATIVE_CPIM_SERVICE_NAME)) { m_serviceName = qgetenv(ALTERNATIVE_CPIM_SERVICE_NAME); } else { m_serviceName = CPIM_SERVICE_NAME; } if (qEnvironmentVariableIsSet(ALTERNATIVE_CPIM_SERVICE_PAGE_SIZE)) { m_pageSize = qgetenv(ALTERNATIVE_CPIM_SERVICE_PAGE_SIZE).toInt(); } else { m_pageSize = FETCH_PAGE_SIZE; } m_serviceWatcher = new QDBusServiceWatcher(m_serviceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); connect(m_serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(serviceOwnerChanged(QString,QString,QString))); initialize(); } GaleraContactsService::GaleraContactsService(const GaleraContactsService &other) : m_managerUri(other.m_managerUri), m_iface(other.m_iface) { } GaleraContactsService::~GaleraContactsService() { delete m_serviceWatcher; Q_FOREACH(QContactRequestData *r, m_runningRequests) { r->cancel(); r->wait(); } m_runningRequests.clear(); } void GaleraContactsService::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); if (name == m_serviceName) { if (!newOwner.isEmpty()) { // service appear qDebug() << "Service appeared"; initialize(); } else if (!m_iface.isNull()) { // lost service qDebug() << "Service disappeared"; deinitialize(); } } } void GaleraContactsService::onServiceReady() { bool isReady = m_iface.data()->property("isReady").toBool(); if (isReady != m_serviceIsReady) { m_serviceIsReady = isReady; Q_EMIT serviceChanged(); } } void GaleraContactsService::initialize() { if (m_iface.isNull()) { m_iface = QSharedPointer(new QDBusInterface(m_serviceName, CPIM_ADDRESSBOOK_OBJECT_PATH, CPIM_ADDRESSBOOK_IFACE_NAME)); if (!m_iface->lastError().isValid()) { m_serviceIsReady = m_iface.data()->property("isReady").toBool(); connect(m_iface.data(), SIGNAL(readyChanged()), this, SLOT(onServiceReady()), Qt::UniqueConnection); connect(m_iface.data(), SIGNAL(safeModeChanged()), this, SIGNAL(serviceChanged())); connect(m_iface.data(), SIGNAL(contactsAdded(QStringList)), this, SLOT(onContactsAdded(QStringList))); connect(m_iface.data(), SIGNAL(contactsRemoved(QStringList)), this, SLOT(onContactsRemoved(QStringList))); connect(m_iface.data(), SIGNAL(contactsUpdated(QStringList)), this, SLOT(onContactsUpdated(QStringList))); if (m_serviceIsReady) { Q_EMIT serviceChanged(); } } else { qWarning() << "Fail to connect with service:" << m_iface->lastError(); m_iface.clear(); } } } void GaleraContactsService::deinitialize() { // wait until all request finish while (m_runningRequests.size()) { QCoreApplication::processEvents(); } // this will make the service re-initialize m_iface->call("ping"); if (m_iface->lastError().isValid()) { qWarning() << m_iface->lastError(); m_iface.clear(); m_serviceIsReady = false; } else { m_serviceIsReady = m_iface.data()->property("isReady").toBool(); } Q_EMIT serviceChanged(); } bool GaleraContactsService::isOnline() const { return !m_iface.isNull() && m_serviceIsReady; } void GaleraContactsService::fetchContactsById(QtContacts::QContactFetchByIdRequest *request) { if (!isOnline()) { qWarning() << "Server is not online"; QContactFetchByIdRequestData::notifyError(request); return; } QContactIdFilter filter; filter.setIds(request->contactIds()); QString filterStr = Filter(filter).toString(); QDBusMessage result = m_iface->call("query", filterStr, "", request->fetchHint().maxCountHint(), m_showInvisibleContacts, QStringList()); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << result.errorName() << result.errorMessage(); QContactFetchByIdRequestData::notifyError(request); return; } QDBusObjectPath viewObjectPath = result.arguments()[0].value(); QDBusInterface *view = new QDBusInterface(m_serviceName, viewObjectPath.path(), CPIM_ADDRESSBOOK_VIEW_IFACE_NAME); QContactFetchByIdRequestData *data = new QContactFetchByIdRequestData(request, view); m_runningRequests << data; fetchContactsPage(data); } void GaleraContactsService::fetchContacts(QtContacts::QContactFetchRequest *request) { if (!isOnline()) { qWarning() << "Server is not online"; QContactFetchRequestData::notifyError(request); return; } // Only return the sources names if the filter is set as contact group type if (request->filter().type() == QContactFilter::ContactDetailFilter) { QContactDetailFilter dFilter = static_cast(request->filter()); if ((dFilter.detailType() == QContactDetail::TypeType) && (dFilter.detailField() == QContactType::FieldType) && (dFilter.value() == QContactType::TypeGroup)) { QDBusPendingCall pcall = m_iface->asyncCall("availableSources"); if (pcall.isError()) { qWarning() << pcall.error().name() << pcall.error().message(); QContactFetchRequestData::notifyError(request); return; } QContactFetchRequestData *data = new QContactFetchRequestData(request, 0); m_runningRequests << data; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->fetchContactsGroupsContinue(data, call); }); return; } } QString sortStr = SortClause(request->sorting()).toString(); QString filterStr = Filter(request->filter()).toString(); FetchHint fetchHint = FetchHint(request->fetchHint()).toString(); QDBusPendingCall pcall = m_iface->asyncCall("query", filterStr, sortStr, request->fetchHint().maxCountHint(), m_showInvisibleContacts, QStringList()); if (pcall.isError()) { qWarning() << pcall.error().name() << pcall.error().message(); QContactFetchRequestData::notifyError(request); return; } QContactFetchRequestData *data = new QContactFetchRequestData(request, 0, fetchHint); m_runningRequests << data; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->fetchContactsContinue(data, call); }); } void GaleraContactsService::fetchContactsContinue(QContactFetchRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); destroyRequest(data); } else { QDBusObjectPath viewObjectPath = reply.value(); QDBusInterface *view = new QDBusInterface(m_serviceName, viewObjectPath.path(), CPIM_ADDRESSBOOK_VIEW_IFACE_NAME); data->updateView(view); fetchContactsPage(data); } } void GaleraContactsService::fetchContactsPage(QContactFetchRequestData *data) { if (!isOnline() || !data->isLive()) { destroyRequest(data); return; } // Load contacs async QDBusPendingCall pcall = data->view()->asyncCall("contactsDetails", data->fields(), data->offset(), m_pageSize); if (pcall.isError()) { qWarning() << pcall.error().name() << pcall.error().message(); data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->fetchContactsDone(data, call); }); } void GaleraContactsService::fetchContactsDone(QContactFetchRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); data->update(QList(), QContactAbstractRequest::FinishedState, QContactManager::UnspecifiedError); destroyRequest(data); } else { const QStringList vcards = reply.value(); if (vcards.size()) { VCardParser *parser = new VCardParser; parser->setProperty("DATA", QVariant::fromValue(data)); data->setVCardParser(parser); connect(parser, SIGNAL(contactsParsed(QList)), SLOT(onVCardsParsed(QList))); connect(parser, SIGNAL(canceled()), SLOT(onVCardParseCanceled())); parser->vcardToContact(vcards); } else { data->update(QList(), QContactAbstractRequest::FinishedState); destroyRequest(data); } } } void GaleraContactsService::onVCardParseCanceled() { QObject *sender = QObject::sender(); disconnect(sender); QContactFetchRequestData *data = static_cast(sender->property("DATA").value()); data->clearVCardParser(); if (!data->isLive()) { sender->deleteLater(); destroyRequest(data); return; } sender->deleteLater(); } void GaleraContactsService::onVCardsParsed(QList contacts) { QObject *sender = QObject::sender(); disconnect(sender); QContactFetchRequestData *data = static_cast(sender->property("DATA").value()); data->clearVCardParser(); if (!data->isLive()) { sender->deleteLater(); destroyRequest(data); return; } QList::iterator contact; for (contact = contacts.begin(); contact != contacts.end(); ++contact) { if (!contact->isEmpty()) { QContactGuid detailId = contact->detail(); GaleraEngineId *engineId = new GaleraEngineId(detailId.guid(), m_managerUri); QContactId newId = QContactId(engineId); contact->setId(newId); } } if (contacts.size() == m_pageSize) { data->update(contacts, QContactAbstractRequest::ActiveState); data->updateOffset(m_pageSize); data->updateWatcher(0); fetchContactsPage(data); } else { data->update(contacts, QContactAbstractRequest::FinishedState); destroyRequest(data); } sender->deleteLater(); } void GaleraContactsService::fetchContactsGroupsContinue(QContactFetchRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } QList contacts; QContactManager::Error opError = QContactManager::NoError; QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); opError = QContactManager::UnspecifiedError; } else { Q_FOREACH(const Source &source, reply.value()) { galera::GaleraEngineId *engineId = new galera::GaleraEngineId(QString("source@%1").arg(source.id()), m_managerUri); QContactId id = QContactId(engineId); QContact c = source.toContact(id); if (source.isPrimary()) { contacts.prepend(c); } else { contacts << c; } } } data->update(contacts, QContactAbstractRequest::FinishedState, opError); destroyRequest(data); } /* Saving contacts * * Due the limitation on QtPim API we do not have a native way to create * 'address-books'/'sources'/'collections', to WORKAROUND it we use contacts * with type == 'QContactType::TypeGroup' as 'collections' * * FIXME: the new QtPim API already support collections for contacts. We should * rewrite this before update to the new QtPim. * * The steps are: * - Create each group individually due the limitation of the server API * - Create each contact individually due the limitation of the sever API * - Update all groups that already have IDs * - Update all contacts that already have IDs * * If the request was canceled between any of these steps, the data object is destroyed and a finish signal is fired. */ /* This function can receive a mix of contacts and groups, the contacts without id will be created * on server and contacts that already have id will be updated: */ void GaleraContactsService::saveContact(QtContacts::QContactSaveRequest *request) { QContactSaveRequestData *data = new QContactSaveRequestData(request); m_runningRequests << data; // first create the new groups data->prepareToCreate(); createGroupsStart(data); } /* Will create async the first group on the list, if the list is not empty, * otherwise it will call 'createContactsStart' to create the contacts in the list * * Due the Server limitation we will call this function for each contact * sequentially. */ void GaleraContactsService::createGroupsStart(QContactSaveRequestData *data) { if (!data->isLive()) { data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } if(!data->hasNextGroup()) { // If there is no more groups to create go to create contacts createContactsStart(data); return; } Source sourceToCreate = data->nextGroup(); QDBusPendingCall pcall = m_iface->asyncCall("createSourceForAccount", sourceToCreate.displayLabel(), sourceToCreate.accountId(), sourceToCreate.isPrimary()); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->createGroupDone(data, call); }); } /* 'createSourceForAccount' will call this function when done, * we need to check for errors and update the contact Id with the new Id, and * call 'createGroupsStart' to continue with the next group. */ void GaleraContactsService::createGroupDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); data->notifyUpdateError(QContactManager::UnspecifiedError); } else { data->updateCurrentGroup(reply.value(), m_managerUri); } // go to next group createGroupsStart(data); } /* After handle all contacts with type = 'QContactType::TypeGroup', we need to * create the real contacts. * * Due the Server limitation we will call this function for each contact * sequentially. */ void GaleraContactsService::createContactsStart(QContactSaveRequestData *data) { if (!data->isLive()) { data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } if(!data->hasNext()) { // If there is no more contacts to create go to update groups data->prepareToUpdate(); updateGroups(data); return; } QString syncSource; QString contact = data->nextContact(&syncSource); QDBusPendingCall pcall = m_iface->asyncCall("createContact", contact, syncSource); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->createContactsDone(data, call); }); } /* 'createContact' will call this function when done, * we need to check for errors and update the contact Id with the new Id, and * call 'createContactsStart' to continue with the next contact. */ void GaleraContactsService::createContactsDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); data->notifyUpdateError(QContactManager::UnspecifiedError); } else { const QString vcard = reply.value(); if (!vcard.isEmpty()) { QContact contact = VCardParser::vcardToContact(vcard); QContactGuid detailId = contact.detail(); GaleraEngineId *engineId = new GaleraEngineId(detailId.guid(), m_managerUri); QContactId newId = QContactId(engineId); contact.setId(newId); data->updateCurrentContact(contact); } else { data->notifyUpdateError(QContactManager::UnspecifiedError); } } // go to next contact createContactsStart(data); } /* * Our server support update a list of groups, because of that we can handle all * pending to update groups in one single call. */ void GaleraContactsService::updateGroups(QContactSaveRequestData *data) { if (!data->isLive()) { data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } SourceList pendingGroups = data->allPendingGroups(); if (pendingGroups.isEmpty()) { // If there is no groups to update we can proceed to 'updateContacts' updateContacts(data); return; } QDBusPendingCall pcall = m_iface->asyncCall("updateSources", QVariant::fromValue(pendingGroups)); if (pcall.isError()) { qWarning() << "Error" << pcall.error().name() << pcall.error().message(); data->finish(QtContacts::QContactManager::UnspecifiedError); destroyRequest(data); } else { QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->updateGroupsDone(data, call); }); } } /* * Callback used to process server reply from 'updateSources', we need to check * for errors and update the result with the new groups info. */ void GaleraContactsService::updateGroupsDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } QDBusPendingReply reply = *call; QContactManager::Error opError = QContactManager::NoError; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); opError = QContactManager::UnspecifiedError; } else { const SourceList sources = reply.value(); data->updatePendingGroups(sources, m_managerUri); } // proceed to next step 'updateContacts' updateContacts(data); } /* * Last step * We will update all pending contacts */ void GaleraContactsService::updateContacts(QContactSaveRequestData *data) { if (!data->isLive()) { destroyRequest(data); return; } QStringList pendingContacts = data->allPendingContacts(); if (pendingContacts.isEmpty()) { // Last step, notify query finish and destroy query data data->finish(QContactManager::NoError); destroyRequest(data); return; } QDBusPendingCall pcall = m_iface->asyncCall("updateContacts", pendingContacts); if (pcall.isError()) { qWarning() << "Error" << pcall.error().name() << pcall.error().message(); data->finish(QtContacts::QContactManager::UnspecifiedError); destroyRequest(data); } else { QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->updateContactDone(data, call); }); } } /* * Callback used to process server reply from 'updateContacts', we need to check * for errors and update the result with the new contact info. */ void GaleraContactsService::updateContactDone(QContactSaveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } QDBusPendingReply reply = *call; QContactManager::Error opError = QContactManager::NoError; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); opError = QContactManager::UnspecifiedError; } else { const QStringList vcards = reply.value(); data->updatePendingContacts(vcards); } data->finish(opError); // Last step of 'saveContact', we can destroy the request data now. destroyRequest(data); } void GaleraContactsService::removeContact(QContactRemoveRequest *request) { if (!isOnline()) { qWarning() << "Server is not online"; QContactRemoveRequestData::notifyError(request); return; } QContactRemoveRequestData *data = new QContactRemoveRequestData(request); m_runningRequests << data; if (data->contactIds().isEmpty()) { removeContactContinue(data, 0); } else { QDBusPendingCall pcall = m_iface->asyncCall("removeContacts", data->contactIds()); if (pcall.isError()) { qWarning() << "Error" << pcall.error().name() << pcall.error().message(); data->finish(QContactManager::UnspecifiedError); destroyRequest(data); } else { QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->removeContactContinue(data, call); }); } } } void GaleraContactsService::removeContactContinue(QContactRemoveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } if (call) { QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } } if (data->sourcesIds().isEmpty()) { removeContactDone(data, 0); } else { if (data->sourcesIds().size() > 1) { qWarning() << "Remove multiple sources not supported."; } QDBusPendingCall pcall = m_iface->asyncCall("removeSource", data->sourcesIds().first()); if (pcall.isError()) { qWarning() << "Error" << pcall.error().name() << pcall.error().message(); data->finish(QContactManager::UnspecifiedError); destroyRequest(data); } else { QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, 0); data->updateWatcher(watcher); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *call) { this->removeContactDone(data, call); }); } } } void GaleraContactsService::removeContactDone(QContactRemoveRequestData *data, QDBusPendingCallWatcher *call) { if (!data->isLive()) { destroyRequest(data); return; } if (call) { QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << reply.error().name() << reply.error().message(); data->finish(QContactManager::UnspecifiedError); destroyRequest(data); return; } } data->finish(); destroyRequest(data); } void GaleraContactsService::cancelRequest(QtContacts::QContactAbstractRequest *request) { Q_FOREACH(QContactRequestData *rData, m_runningRequests) { if (rData->request() == request) { rData->cancel(); destroyRequest(rData); return; } } } void GaleraContactsService::waitRequest(QtContacts::QContactAbstractRequest *request) { QContactRequestData *data = 0; Q_FOREACH(QContactRequestData *rData, m_runningRequests) { if (rData->request() == request) { data = rData; break; } } if (data) { data->wait(); // this is the only case where we still need to delete data even if the data is not in the running list anymore, // because we can not delete it while waiting (the pointer still be used by wait function) m_runningRequests.removeOne(data); // the data could be removed from m_runningRequests while waiting, but we still need to destroy it data->deleteLater(); } } void GaleraContactsService::releaseRequest(QContactAbstractRequest *request) { Q_FOREACH(QContactRequestData *rData, m_runningRequests) { if (rData->request() == request) { m_runningRequests.removeOne(rData); rData->releaseRequest(); rData->cancel(); rData->deleteLater(); return; } } } void GaleraContactsService::setShowInvisibleContacts(bool show) { m_showInvisibleContacts = show; } void GaleraContactsService::addRequest(QtContacts::QContactAbstractRequest *request) { if (!isOnline()) { qWarning() << "Server is not online"; QContactManagerEngine::updateRequestState(request, QContactAbstractRequest::FinishedState); return; } Q_ASSERT(request->state() == QContactAbstractRequest::ActiveState); switch (request->type()) { case QContactAbstractRequest::ContactFetchRequest: fetchContacts(static_cast(request)); break; case QContactAbstractRequest::ContactFetchByIdRequest: fetchContactsById(static_cast(request)); break; case QContactAbstractRequest::ContactIdFetchRequest: qDebug() << "Not implemented: ContactIdFetchRequest"; break; case QContactAbstractRequest::ContactSaveRequest: saveContact(static_cast(request)); break; case QContactAbstractRequest::ContactRemoveRequest: removeContact(static_cast(request)); break; case QContactAbstractRequest::RelationshipFetchRequest: qDebug() << "Not implemented: RelationshipFetchRequest"; break; case QContactAbstractRequest::RelationshipRemoveRequest: qDebug() << "Not implemented: RelationshipRemoveRequest"; break; case QContactAbstractRequest::RelationshipSaveRequest: qDebug() << "Not implemented: RelationshipSaveRequest"; break; break; default: // unknown request type. break; } } void GaleraContactsService::destroyRequest(QContactRequestData *request) { // only destroy the resquest data if it still on the list // otherwise it was already destroyed if (m_runningRequests.removeOne(request)) { request->deleteLater(); } } QList GaleraContactsService::parseIds(const QStringList &ids) const { QList contactIds; Q_FOREACH(QString id, ids) { GaleraEngineId *engineId = new GaleraEngineId(id, m_managerUri); contactIds << QContactId(engineId); } return contactIds; } void GaleraContactsService::onContactsAdded(const QStringList &ids) { Q_EMIT contactsAdded(parseIds(ids)); } void GaleraContactsService::onContactsRemoved(const QStringList &ids) { Q_EMIT contactsRemoved(parseIds(ids)); } void GaleraContactsService::onContactsUpdated(const QStringList &ids) { Q_EMIT contactsUpdated(parseIds(ids)); } } //namespace address-book-service-0.1.1+16.04.20151211.1/contacts/qcontact-backend.cpp0000644000015300001610000003004212632607666025751 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontact-backend.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "contacts-service.h" #include "qcontact-engineid.h" #include "config.h" using namespace QtContacts; namespace galera { QtContacts::QContactManagerEngine* GaleraEngineFactory::engine(const QMap ¶meters, QtContacts::QContactManager::Error *error) { Q_UNUSED(error); GaleraManagerEngine *engine = GaleraManagerEngine::createEngine(parameters); return engine; } QtContacts::QContactEngineId* GaleraEngineFactory::createContactEngineId(const QMap ¶meters, const QString &engineIdString) const { return new GaleraEngineId(parameters, engineIdString); } QString GaleraEngineFactory::managerName() const { return QString::fromLatin1("galera"); } GaleraManagerEngine* GaleraManagerEngine::createEngine(const QMap ¶meters) { GaleraManagerEngine *engine = new GaleraManagerEngine(); engine->m_service->setShowInvisibleContacts(parameters.value(ADDRESS_BOOK_SHOW_INVISIBLE_PROP, "false").toLower() == "true"); return engine; } /*! * Constructs a new in-memory backend which shares the given \a data with * other shared memory engines. */ GaleraManagerEngine::GaleraManagerEngine() : m_service(new GaleraContactsService(managerUri())) { connect(m_service, SIGNAL(contactsAdded(QList)), this, SIGNAL(contactsAdded(QList))); connect(m_service, SIGNAL(contactsRemoved(QList)), this, SIGNAL(contactsRemoved(QList))); connect(m_service, SIGNAL(contactsUpdated(QList)), this, SIGNAL(contactsChanged(QList))); connect(m_service, SIGNAL(serviceChanged()), this, SIGNAL(dataChanged()), Qt::QueuedConnection); } /*! Frees any memory used by this engine */ GaleraManagerEngine::~GaleraManagerEngine() { delete m_service; } /* URI reporting */ QString GaleraManagerEngine::managerName() const { return "galera"; } QMap GaleraManagerEngine::managerParameters() const { QMap parameters; return parameters; } int GaleraManagerEngine::managerVersion() const { return 1; } /* Filtering */ QList GaleraManagerEngine::contactIds(const QtContacts::QContactFilter &filter, const QList &sortOrders, QtContacts::QContactManager::Error *error) const { QContactFetchHint hint; hint.setDetailTypesHint(QList() << QContactDetail::TypeGuid); QList clist = contacts(filter, sortOrders, hint, error); /* Extract the ids */ QList ids; Q_FOREACH(const QContact &c, clist) ids.append(c.id()); return ids; } QList GaleraManagerEngine::contacts(const QtContacts::QContactFilter &filter, const QList& sortOrders, const QContactFetchHint &fetchHint, QtContacts::QContactManager::Error *error) const { QContactFetchRequest request; request.setFilter(filter); request.setSorting(sortOrders); request.setFetchHint(fetchHint); const_cast(this)->startRequest(&request); const_cast(this)->waitForRequestFinished(&request, -1); if (error) { *error = request.error(); } return request.contacts(); } QList GaleraManagerEngine::contacts(const QList &contactIds, const QContactFetchHint &fetchHint, QMap *errorMap, QContactManager::Error *error) const { QContactFetchByIdRequest request; request.setIds(contactIds); request.setFetchHint(fetchHint); const_cast(this)->startRequest(&request); const_cast(this)->waitForRequestFinished(&request, -1); if (errorMap) { *errorMap = request.errorMap(); } if (error) { *error = request.error(); } return request.contacts(); } QContact GaleraManagerEngine::contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const { QContactFetchByIdRequest request; request.setIds(QList() << contactId); request.setFetchHint(fetchHint); const_cast(this)->startRequest(&request); const_cast(this)->waitForRequestFinished(&request, -1); if (error) { *error = request.error(); } return request.contacts().value(0, QContact()); } bool GaleraManagerEngine::saveContact(QtContacts::QContact *contact, QtContacts::QContactManager::Error *error) { QContactSaveRequest request; request.setContact(*contact); startRequest(&request); waitForRequestFinished(&request, -1); *error = QContactManager::NoError; // FIXME: GaleraContactsService::updateContactDone doesn't return contacts if (contact->id().isNull()) { *contact = request.contacts()[0]; } return true; } bool GaleraManagerEngine::removeContact(const QtContacts::QContactId &contactId, QtContacts::QContactManager::Error *error) { QContactRemoveRequest request; request.setContactId(contactId); startRequest(&request); waitForRequestFinished(&request, -1); *error = QContactManager::NoError; return true; } bool GaleraManagerEngine::saveRelationship(QtContacts::QContactRelationship *relationship, QtContacts::QContactManager::Error *error) { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } bool GaleraManagerEngine::removeRelationship(const QtContacts::QContactRelationship &relationship, QtContacts::QContactManager::Error *error) { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } bool GaleraManagerEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error) { return saveContacts(contacts, QList(), errorMap, error); } bool GaleraManagerEngine::saveContacts(QList *contacts, const QList &typeMask, QMap *errorMap, QContactManager::Error *error) { Q_ASSERT(contacts != 0); QContactSaveRequest request; request.setContacts(*contacts); request.setTypeMask(typeMask); startRequest(&request); waitForRequestFinished(&request, -1); *contacts = request.contacts(); if (error) { *error = request.error(); } if (errorMap) { *errorMap = request.errorMap(); } return (request.error() == QContactManager::NoError); } bool GaleraManagerEngine::removeContacts(const QList &contactIds, QMap *errorMap, QtContacts::QContactManager::Error *error) { QContactRemoveRequest request; request.setContactIds(contactIds); startRequest(&request); waitForRequestFinished(&request, -1); *error = QContactManager::NoError; return true; } /* "Self" contact id (MyCard) */ bool GaleraManagerEngine::setSelfContactId(const QtContacts::QContactId &contactId, QtContacts::QContactManager::Error *error) { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } QtContacts::QContactId GaleraManagerEngine::selfContactId(QtContacts::QContactManager::Error *error) const { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return QContactId(); } /* Relationships between contacts */ QList GaleraManagerEngine::relationships(const QString &relationshipType, const QContact& participant, QContactRelationship::Role role, QtContacts::QContactManager::Error *error) const { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return QList(); } bool GaleraManagerEngine::saveRelationships(QList *relationships, QMap* errorMap, QtContacts::QContactManager::Error *error) { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } bool GaleraManagerEngine::removeRelationships(const QList &relationships, QMap *errorMap, QtContacts::QContactManager::Error *error) { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } /* Validation for saving */ bool GaleraManagerEngine::validateContact(const QtContacts::QContact &contact, QtContacts::QContactManager::Error *error) const { qWarning() << "Function not implemented" << Q_FUNC_INFO; *error = QContactManager::NoError; return true; } /* Asynchronous Request Support */ void GaleraManagerEngine::requestDestroyed(QtContacts::QContactAbstractRequest *req) { m_service->releaseRequest(req); } bool GaleraManagerEngine::startRequest(QtContacts::QContactAbstractRequest *req) { if (!req) { return false; } QPointer checkDeletion(req); updateRequestState(req, QContactAbstractRequest::ActiveState); if (!checkDeletion.isNull()) { m_service->addRequest(req); } return true; } bool GaleraManagerEngine::cancelRequest(QtContacts::QContactAbstractRequest *req) { if (req) { m_service->cancelRequest(req); return true; } else { return false; } } bool GaleraManagerEngine::waitForRequestFinished(QtContacts::QContactAbstractRequest *req, int msecs) { Q_UNUSED(msecs); m_service->waitRequest(req); return true; } /* Capabilities reporting */ bool GaleraManagerEngine::isRelationshipTypeSupported(const QString &relationshipType, QtContacts::QContactType::TypeValues contactType) const { qDebug() << Q_FUNC_INFO; return true; } bool GaleraManagerEngine::isFilterSupported(const QtContacts::QContactFilter &filter) const { qDebug() << Q_FUNC_INFO; return true; } QList GaleraManagerEngine::supportedDataTypes() const { QList st; st.append(QVariant::String); st.append(QVariant::Date); st.append(QVariant::DateTime); st.append(QVariant::Time); st.append(QVariant::Bool); st.append(QVariant::Char); st.append(QVariant::Int); st.append(QVariant::UInt); st.append(QVariant::LongLong); st.append(QVariant::ULongLong); st.append(QVariant::Double); return st; } } // namespace address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactfetchbyidrequest-data.h0000644000015300001610000000311012632607666030227 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACTFETCHREQUESTBYID_DATA_H__ #define __GALERA_QCONTACTFETCHREQUESTBYID_DATA_H__ #include "qcontactfetchrequest-data.h" #include #include namespace galera { class QContactFetchByIdRequestData : public QContactFetchRequestData { public: QContactFetchByIdRequestData(QtContacts::QContactFetchByIdRequest *request, QDBusInterface *view); static void notifyError(QtContacts::QContactFetchByIdRequest *request, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NotSupportedError); protected: virtual void updateRequest(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error, QMap errorMap); }; } #endif address-book-service-0.1.1+16.04.20151211.1/contacts/qcontact-engineid.cpp0000644000015300001610000000621712632607666026153 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontact-engineid.h" #include using namespace QtContacts; namespace galera { GaleraEngineId::GaleraEngineId() : m_contactId("") { } GaleraEngineId::GaleraEngineId(const QString &contactId, const QString &managerUri) : m_contactId(contactId), m_managerUri(managerUri) { } GaleraEngineId::~GaleraEngineId() { } GaleraEngineId::GaleraEngineId(const GaleraEngineId &other) : m_contactId(other.m_contactId), m_managerUri(other.m_managerUri) { } GaleraEngineId::GaleraEngineId(const QMap ¶meters, const QString &engineIdString) { m_contactId = engineIdString; m_managerUri = QContactManager::buildUri("galera", parameters); } bool GaleraEngineId::isEqualTo(const QtContacts::QContactEngineId *other) const { Q_ASSERT(other); if (!other) { qWarning() << "GaleraEngineId::isEqualTo, other is null"; return false; } if (m_contactId != static_cast(other)->m_contactId) return false; return true; } bool GaleraEngineId::isLessThan(const QtContacts::QContactEngineId *other) const { Q_ASSERT(other); if (!other) { qWarning() << "GaleraEngineId::isLessThan, other is null"; return false; } const GaleraEngineId *otherPtr = static_cast(other); if (m_managerUri < otherPtr->m_managerUri) return true; if (m_contactId < otherPtr->m_contactId) return true; return false; } QString GaleraEngineId::managerUri() const { return m_managerUri; } QString GaleraEngineId::toString() const { return m_contactId; } QtContacts::QContactEngineId* GaleraEngineId::clone() const { return new GaleraEngineId(m_contactId, m_managerUri); } #ifndef QT_NO_DEBUG_STREAM QDebug& GaleraEngineId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "EngineId(" << m_managerUri << "," << m_contactId << ")"; return dbg.maybeSpace(); } #endif uint GaleraEngineId::hash() const { return qHash(m_contactId); } #ifndef QT_NO_DATASTREAM QDataStream& operator<<(QDataStream& out, const GaleraEngineId& engineId) { out << engineId.m_managerUri << engineId.m_contactId; return out; } QDataStream& operator>>(QDataStream& in, GaleraEngineId& engineId) { QString managerUri; QString contactId; in >> managerUri; in >> contactId; engineId.m_contactId = contactId; engineId.m_managerUri = managerUri; //= GaleraEngineId(contactId, managerUri); return in; } #endif } address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactrequest-data.h0000644000015300001610000000527212632607666026360 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __GALERA_QCONTACTREQUEST_DATA_H__ #define __GALERA_QCONTACTREQUEST_DATA_H__ #include #include #include #include #include #include #include #include #include namespace galera { class QContactRequestData { public: QContactRequestData(QtContacts::QContactAbstractRequest *request, QDBusPendingCallWatcher *watcher = 0); QtContacts::QContactAbstractRequest* request() const; void updateWatcher(QDBusPendingCallWatcher *watcher); bool isLive() const; virtual void cancel(); void wait(); void releaseRequest(); void finish(QtContacts::QContactManager::Error error = QtContacts::QContactManager::NoError); void deleteLater(); virtual void update(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error = QtContacts::QContactManager::NoError, QMap errorMap = QMap()); protected: QPointer m_request; QMap m_errorMap; virtual ~QContactRequestData(); virtual void updateRequest(QtContacts::QContactAbstractRequest::State state, QtContacts::QContactManager::Error error, QMap errorMap) = 0; private: QSharedPointer m_watcher; QEventLoop *m_eventLoop; QMutex m_waiting; void init(QtContacts::QContactAbstractRequest *request, QDBusInterface *view, QDBusPendingCallWatcher *watcher); static void deleteRequest(QtContacts::QContactAbstractRequest *obj); static void deleteWatcher(QDBusPendingCallWatcher *watcher); }; } Q_DECLARE_METATYPE(galera::QContactRequestData*) #endif address-book-service-0.1.1+16.04.20151211.1/contacts/CMakeLists.txt0000644000015300001610000000221512632607666024605 0ustar pbuserpbgroup00000000000000project(qtcontacts_galera) set(QCONTACTS_BACKEND qtcontacts_galera) set(QCONTACTS_BACKEND_SRCS qcontact-backend.cpp qcontact-engineid.cpp qcontactfetchrequest-data.cpp qcontactfetchbyidrequest-data.cpp qcontactremoverequest-data.cpp qcontactrequest-data.cpp qcontactsaverequest-data.cpp contacts-service.cpp ) set(QCONTACTS_BACKEND_HDRS qcontact-backend.h qcontact-engineid.h qcontactfetchrequest-data.h qcontactfetchbyidrequest-data.h qcontactremoverequest-data.h qcontactrequest-data.h qcontactsaverequest-data.h contacts-service.h ) add_library(${QCONTACTS_BACKEND} SHARED ${QCONTACTS_BACKEND_SRCS} ${QCONTACTS_BACKEND_HDRS} ) include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR} ) target_link_libraries(${QCONTACTS_BACKEND} galera-common Qt5::Core Qt5::Contacts Qt5::DBus Qt5::Versit ) add_definitions(-std=gnu++11) execute_process( COMMAND qmake -query QT_INSTALL_PLUGINS OUTPUT_VARIABLE QT_INSTALL_PLUGINS OUTPUT_STRIP_TRAILING_WHITESPACE ) install(TARGETS ${QCONTACTS_BACKEND} LIBRARY DESTINATION ${QT_INSTALL_PLUGINS}/contacts) address-book-service-0.1.1+16.04.20151211.1/contacts/qcontactsaverequest-data.cpp0000644000015300001610000001567012632607673027573 0ustar pbuserpbgroup00000000000000/* * Copyright 2013 Canonical Ltd. * * This file is part of contact-service-app. * * contact-service-app is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qcontactsaverequest-data.h" #include "common/vcard-parser.h" #include "qcontact-engineid.h" #include #include #include #include using namespace QtContacts; using namespace QtVersit; namespace galera { QContactSaveRequestData::QContactSaveRequestData(QContactSaveRequest *request) : QContactRequestData(request) { int index = 0; Q_FOREACH(const QContact &contact, request->contacts()) { if (contact.id().isNull()) { m_contactsToCreate.insert(index, contact); } else { m_contactsToUpdate.insert(index, contact); } index++; } } void QContactSaveRequestData::prepareContacts(QMap contacts) { Q_FOREACH(int index, contacts.keys()) { QContact contact = contacts[index]; if (contact.type() == QContactType::TypeGroup) { m_pendingGroups.insert(index, Source::fromQContact(contact)); } else { m_pendingContacts.insert(index, VCardParser::contactToVcard(contact)); m_pendingContactsSyncTarget.insert(index, contact.detail().syncTarget()); } } } void QContactSaveRequestData::prepareToUpdate() { Q_ASSERT(m_pendingContacts.count() == 0); prepareContacts(m_contactsToUpdate); } void QContactSaveRequestData::prepareToCreate() { Q_ASSERT(m_pendingContacts.count() == 0); prepareContacts(m_contactsToCreate); } bool QContactSaveRequestData::hasNext() const { return (m_pendingContacts.count() > 0); } bool QContactSaveRequestData::hasNextGroup() const { return (m_pendingGroups.count() > 0); } QString QContactSaveRequestData::nextContact(QString *syncTargetName) { Q_ASSERT(m_pendingContacts.count() > 0); m_currentContact = m_pendingContacts.begin(); if (syncTargetName) { *syncTargetName = m_pendingContactsSyncTarget.begin().value(); } return m_currentContact.value(); } Source QContactSaveRequestData::nextGroup() { Q_ASSERT(m_pendingGroups.count() > 0); m_currentGroup = m_pendingGroups.begin(); return *m_currentGroup; } void QContactSaveRequestData::updateCurrentContactId(GaleraEngineId *engineId) { QContactId contactId(engineId); QContact &contact = m_contactsToCreate[m_currentContact.key()]; contact.setId(contactId); m_pendingContacts.remove(m_currentContact.key()); m_pendingContactsSyncTarget.remove(m_currentContact.key()); } void QContactSaveRequestData::updateCurrentContact(const QContact &contact) { m_contactsToCreate[m_currentContact.key()] = contact; m_pendingContacts.remove(m_currentContact.key()); m_pendingContactsSyncTarget.remove(m_currentContact.key()); } void QContactSaveRequestData::updateCurrentGroup(const Source &group, const QString &managerUri) { galera::GaleraEngineId *engineId = new galera::GaleraEngineId(QString("source@%1").arg(group.id()), managerUri); QContactId id = QContactId(engineId); m_contactsToCreate[m_currentGroup.key()] = group.toContact(id); m_pendingGroups.remove(m_currentGroup.key()); } void QContactSaveRequestData::updatePendingGroups(const SourceList &groups, const QString &managerUri) { if (groups.size() != m_pendingGroups.size()) { qWarning() << "Fail to update groups"; return; } int index = 0; Q_FOREACH(int key, m_pendingGroups.keys()) { const Source &group = groups.at(index); galera::GaleraEngineId *engineId = new galera::GaleraEngineId(QString("source@%1").arg(group.id()), managerUri); QContactId id = QContactId(engineId); m_contactsToUpdate.insert(key, group.toContact(id)); index++; } } void QContactSaveRequestData::updatePendingContacts(QStringList vcards) { if (!vcards.isEmpty()) { QList contacts = VCardParser::vcardToContactSync(vcards); if (contacts.count() != m_pendingContacts.count()) { qWarning() << "Fail to parse vcards to contacts"; } // update the contacts on update list QList indexes = m_contactsToUpdate.keys(); Q_FOREACH(const QContact &contact, contacts) { m_contactsToUpdate.insert(indexes.takeFirst(), contact); } } } void QContactSaveRequestData::notifyUpdateError(QContactManager::Error error) { m_contactsToUpdate.remove(m_currentContact.key()); m_errorMap.insert(m_currentContact.key(), error); m_pendingContacts.remove(m_currentContact.key()); m_pendingContactsSyncTarget.remove(m_currentContact.key()); } QContact QContactSaveRequestData::currentContact() const { return qobject_cast(request())->contacts().at(m_currentContact.key()); } QStringList QContactSaveRequestData::allPendingContacts() const { return m_pendingContacts.values(); } SourceList QContactSaveRequestData::allPendingGroups() const { return m_pendingGroups.values(); } void QContactSaveRequestData::notifyError(QContactSaveRequest *request, QContactManager::Error error) { QContactManagerEngine::updateContactSaveRequest(request, QList(), error, QMap(), QContactAbstractRequest::FinishedState); } void QContactSaveRequestData::updateRequest(QContactAbstractRequest::State state, QContactManager::Error error, QMap errorMap) { // join back contacts to update and create QMap allContacts = m_contactsToUpdate; Q_FOREACH(int key, m_contactsToCreate.keys()) { allContacts.insert(key, m_contactsToCreate[key]); } QContactManagerEngine::updateContactSaveRequest(static_cast(m_request.data()), allContacts.values(), error, errorMap, state); } } //namespace address-book-service-0.1.1+16.04.20151211.1/updater/0000755000015300001610000000000012632610222021651 5ustar pbuserpbgroup00000000000000address-book-service-0.1.1+16.04.20151211.1/updater/ab-update.cpp0000644000015300001610000002617312632607666024252 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ab-update.h" #include "ab-update-module.h" #include "ab-update-buteo-import.h" #include "ab-notify-message.h" #include "ab-i18n.h" #include #include #include #include #include #define TRANSFER_ICON "/usr/share/icons/suru/status/scalable/transfer-progress.svg" #define TRANSFER_ICON_PAUSED "/usr/share/icons/suru/status/scalable/transfer-paused.svg" #define TRANSFER_ICON_ERROR "/usr/share/icons/suru/status/scalable/transfer-error.svg" ABUpdate::ABUpdate(QObject *parent) : QObject(parent), m_netManager(new QNetworkConfigurationManager), m_needsUpdate(false), m_isRunning(false), m_activeModule(-1), m_skipNetworkTest(false), m_silenceMode(false), m_lockFile(QDir::tempPath() + "/address-book-updater.lock") { // load update modules (this can be a plugin system in the future) m_updateModules << new ButeoImport; m_lockFile.setStaleLockTime(0); } ABUpdate::~ABUpdate() { qDeleteAll(m_updateModules); } QList ABUpdate::needsUpdate() const { QList result; Q_FOREACH(ABUpdateModule *module, m_updateModules) { bool mNeedsUpdate = module->needsUpdate(); qDebug() << "Check if module needs update" << module->name() << ":" << mNeedsUpdate; if (mNeedsUpdate) { result << module; } else { module->markAsUpdate(); } } return result; } bool ABUpdate::isRunning() { if (m_lock.tryLock()) { m_lock.unlock(); return false; } return true; } void ABUpdate::skipNetworkTest() { m_skipNetworkTest = true; } void ABUpdate::setSilenceMode(bool flag) { m_silenceMode = flag; } void ABUpdate::startUpdate() { qDebug() << "Start update..."; if (!m_lockFile.tryLock()) { qWarning() << "Lock file is locked. Removing it..."; m_lockFile.removeStaleLockFile(); } if (isRunning()) { qWarning() << "Update already running."; if (!m_silenceMode) { ABNotifyMessage *msg = new ABNotifyMessage(true, this); //if force is set and the app is waiting for internet continue anyway msg->show(_("Account update"), QString(_("%1 contact sync account upgrade already in progress")).arg("Google"), TRANSFER_ICON); } return; } // check if any module needs a upgrade qDebug() << "check modules to update"; QList modulesToUpdate = needsUpdate(); if (modulesToUpdate.isEmpty()) { qDebug() << "No module to update."; notifyDone(); return; } if (m_waitingForInternet) { m_waitingForInternet = false; m_netManager->disconnect(this); } m_lock.lock(); m_modulesToUpdate = modulesToUpdate; qDebug() << "Modules to update" << m_modulesToUpdate.size(); if (isOnline(false)) { notifyStart(); } else { qWarning() << "No internet abort"; notifyNoInternet(); updateNextModule(); } } void ABUpdate::startUpdateWhenConnected() { qDebug() << "Start update when connected..."; if (isRunning()) { qWarning() << "Update already running."; return; } if (needsUpdate().isEmpty()) { qDebug() << "No modules to update"; notifyDone(); return; } if (isOnline(true)) { startUpdate(); } else { notifyNoInternet(); waitForInternet(); } } void ABUpdate::startUpdate(ABUpdateModule *module) { qDebug() << "Start update for" << module->name(); m_activeModule = m_updateModules.indexOf(module); if (!module->canUpdate()) { qWarning() << "Module can not be updated" << module->name(); onModuleUpdateError("", ABUpdateModule::InernalError); } else { if (!module->prepareToUpdate()) { qWarning() << "Fail to prepare to update:" << module->name(); updateNextModule(); } else { connect(module, SIGNAL(updated()), SLOT(onModuleUpdated())); connect(module, SIGNAL(updateError(QString, ABUpdateModule::ImportError)), SLOT(onModuleUpdateError(QString, ABUpdateModule::ImportError))); module->update(); } } } bool ABUpdate::isOnline(bool checkConnectionType) const { if (m_skipNetworkTest) { return true; } if (!m_netManager->isOnline()) { return false; } else if (checkConnectionType) { QNetworkConfiguration::BearerType bearerType = m_netManager->defaultConfiguration().bearerType(); return ((bearerType == QNetworkConfiguration::BearerWLAN) || (bearerType == QNetworkConfiguration::BearerEthernet)); } else { return true; } } void ABUpdate::waitForInternet() { qDebug() << "Not internet connection wait before start upgrade"; m_waitingForInternet = true; connect(m_netManager.data(), SIGNAL(onlineStateChanged(bool)), SLOT(onOnlineStateChanged())); connect(m_netManager.data(), SIGNAL(updateCompleted()), SLOT(onOnlineStateChanged())); connect(m_netManager.data(), SIGNAL(configurationAdded(QNetworkConfiguration)), SLOT(onOnlineStateChanged())); connect(m_netManager.data(), SIGNAL(configurationChanged(QNetworkConfiguration)), SLOT(onOnlineStateChanged())); connect(m_netManager.data(), SIGNAL(configurationRemoved(QNetworkConfiguration)), SLOT(onOnlineStateChanged())); } QString ABUpdate::errorMessage(ABUpdateModule::ImportError error) const { switch (error) { case ABUpdateModule::ApplicationAreadyUpdated: return _("Contacts app is updated already!"); case ABUpdateModule::ConnectionError: return _("Fail to connect to internet!"); case ABUpdateModule::FailToConnectWithButeo: return _("Fail to connect to contact sync service!"); case ABUpdateModule::FailToCreateButeoProfiles: return _("Fail to create sync profile!"); case ABUpdateModule::FailToAuthenticate: return _("Fail to authenticate!"); case ABUpdateModule::InernalError: return _("Internal error during the sync!"); case ABUpdateModule::OnlineAccountNotFound: return _("Online account not found!"); case ABUpdateModule::SyncAlreadyRunning: return _("Contact sync update already in progress!!"); case ABUpdateModule::SyncError: default: return _("Fail to sync contacts!"); } } void ABUpdate::updateNextModule() { qDebug() << "Update next module" << m_modulesToUpdate.size(); if (m_modulesToUpdate.isEmpty()) { notifyDone(); } else { startUpdate(m_modulesToUpdate.takeFirst()); } } void ABUpdate::cancelUpdate() { } void ABUpdate::onModuleUpdated() { ABUpdateModule *module = m_updateModules.at(m_activeModule); module->disconnect(this); if (!module->commit()) { qDebug() << "Fail to commit final changes for module" << module->name(); onModuleUpdateError("", ABUpdateModule::InernalError); return; } qDebug() << "Update complete for:" << module->name(); if (m_silenceMode) { updateNextModule(); } else { ABNotifyMessage *msg = new ABNotifyMessage(true, this); msg->show(_("Account update"), _("Contact sync update complete."), TRANSFER_ICON); connect(msg, SIGNAL(messageClosed()), SLOT(updateNextModule())); } } void ABUpdate::onModuleUpdateError(const QString &accountName, ABUpdateModule::ImportError error) { ABUpdateModule *module = m_updateModules.at(m_activeModule); module->disconnect(this); qWarning() << "Fail to update module" << module->name() << accountName << module->lastError(); module->rollback(); if (m_silenceMode) { updateNextModule(); } else { ABNotifyMessage *msg = new ABNotifyMessage(true, this); msg->setProperty("MODULE", QVariant::fromValue(module)); msg->askYesOrNo(_("Fail to update"), QString(_("%1.\nDo you want to retry now?")).arg(errorMessage(error)), TRANSFER_ICON_ERROR); connect(msg, SIGNAL(questionAccepted()), this, SLOT(onModuleUpdateRetry())); connect(msg, SIGNAL(questionRejected()), SLOT(onModuleUpdateNoRetry())); connect(msg, SIGNAL(messageClosed()), SLOT(onModuleUpdateNoRetry())); } } void ABUpdate::onModuleUpdateRetry() { QObject *sender = QObject::sender(); disconnect(sender); ABUpdateModule *module = qobject_cast(sender->property("MODULE").value()); m_modulesToUpdate << module; updateNextModule(); } void ABUpdate::onModuleUpdateNoRetry() { QObject *sender = QObject::sender(); disconnect(sender); ABNotifyMessage *msg = new ABNotifyMessage(true, this); msg->show(_("Fail to update"), _("To retry later open Contacts app and press the sync button."), TRANSFER_ICON); connect(msg, SIGNAL(messageClosed()), SLOT(updateNextModule())); } void ABUpdate::onOnlineStateChanged() { if (isOnline(true)) { qDebug() << "Network is online resume upddate process."; m_waitingForInternet = false; m_netManager->disconnect(this); QTimer::singleShot(5000, this, SLOT(continueUpdateWithInternet())); } } void ABUpdate::continueUpdateWithInternet() { if (isOnline(true)) { startUpdate(); } else { waitForInternet(); } } void ABUpdate::notifyStart() { if (m_silenceMode) { updateNextModule(); } else { ABNotifyMessage *msg = new ABNotifyMessage(true, this); msg->show(_("Account update"), QString(_("%1 contact sync account upgrade in progress")).arg("Google"), TRANSFER_ICON); connect(msg, SIGNAL(messageClosed()), SLOT(updateNextModule())); } } void ABUpdate::notifyNoInternet() { if (!m_silenceMode) { ABNotifyMessage *msg = new ABNotifyMessage(true, this); msg->show(_("Account update"), QString(_("%1 contact sync account needs upgrade. Please connect with the internet.")).arg("Google"), TRANSFER_ICON_PAUSED); } } void ABUpdate::notifyDone() { m_activeModule = -1; m_lock.unlock(); qDebug() << "Send update done"; Q_EMIT updateDone(); } address-book-service-0.1.1+16.04.20151211.1/updater/ab-update.h0000644000015300001610000000451212632607666023710 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "ab-update-module.h" #include #include #include #include #include class ABUpdate : public QObject { Q_OBJECT public: ABUpdate(QObject *parent = 0); ~ABUpdate() override; QList needsUpdate() const; bool isRunning(); // test void skipNetworkTest(); void setSilenceMode(bool flag); public Q_SLOTS: void startUpdate(); void startUpdateWhenConnected(); void cancelUpdate(); Q_SIGNALS: void onNeedsUpdateChanged(); void onIsRunningChanged(); void updateDone(); void updateError(const QString &accountName, ABUpdateModule::ImportError); private Q_SLOTS: void onModuleUpdated(); void onModuleUpdateError(const QString &accountName, ABUpdateModule::ImportError error); void onOnlineStateChanged(); void continueUpdateWithInternet(); void updateNextModule(); void onModuleUpdateRetry(); void onModuleUpdateNoRetry(); private: QScopedPointer m_netManager; QList m_updateModules; QList m_modulesToUpdate; QMutex m_lock; QLockFile m_lockFile; bool m_needsUpdate; bool m_isRunning; bool m_waitingForInternet; int m_activeModule; bool m_skipNetworkTest; bool m_silenceMode; void notifyStart(); void notifyNoInternet(); void notifyDone(); void startUpdate(ABUpdateModule *module); bool isOnline(bool checkConnectionType) const; void waitForInternet(); QString errorMessage(ABUpdateModule::ImportError error) const; }; address-book-service-0.1.1+16.04.20151211.1/updater/main.cpp0000644000015300001610000000474412632607666023334 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "ab-update-adaptor.h" #include "ab-update.h" #include "dbus-service-defs.h" #include "config.h" namespace C { #include } int main(int argc, char **argv) { QCoreApplication::setOrganizationName(SETTINGS_ORG); QCoreApplication::setApplicationName("AddressBookUpdate"); setlocale(LC_ALL, ""); C::bindtextdomain(GETTEXT_PACKAGE, GETTEXT_LOCALEDIR); C::bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); QCoreApplication app(argc, argv); // AddressBook updater QScopedPointer abUpdate(new ABUpdate); QScopedPointer abUpdateAdaptor(new ABUpdateAdaptor(abUpdate.data())); // connect to D-Bus and register as an object: QDBusConnection connection = QDBusConnection::sessionBus(); bool ret = connection.registerService(CPIM_UPDATE_SERVICE_NAME); if (!ret) { qWarning() << "Fail to register service:" << CPIM_UPDATE_SERVICE_NAME; return -1; } ret = connection.registerObject(CPIM_UPDATE_OBJECT_PATH, abUpdate.data()); if (!ret) { qWarning() << "Fail to register object:" << CPIM_UPDATE_SERVICE_NAME; return -2; } if (app.arguments().contains("--sync")) { QTimer::singleShot(0, abUpdate.data(), SLOT(startUpdateWhenConnected())); } QObject::connect(abUpdate.data(), SIGNAL(updateDone()), &app, SLOT(quit())); qDebug() << "Updater started" << QDateTime::currentDateTime(); app.exec(); qDebug() << "Updater finished" << QDateTime::currentDateTime(); connection.unregisterObject(CPIM_UPDATE_OBJECT_PATH); connection.unregisterService(CPIM_UPDATE_SERVICE_NAME); } address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-adaptor.cpp0000644000015300001610000000231112632607666025666 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ab-update-adaptor.h" ABUpdateAdaptor::ABUpdateAdaptor(ABUpdate *parent) : QDBusAbstractAdaptor(parent), m_abUpdate(parent) { setAutoRelaySignals(true); } ABUpdateAdaptor::~ABUpdateAdaptor() { } bool ABUpdateAdaptor::needsUpdate() const { return !m_abUpdate->needsUpdate().isEmpty(); } bool ABUpdateAdaptor::isRunning() const { return m_abUpdate->isRunning(); } void ABUpdateAdaptor::startUpdate() { m_abUpdate->startUpdate(); } void ABUpdateAdaptor::cancelUpdate() { m_abUpdate->cancelUpdate(); } address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-adaptor.h0000644000015300001610000000400012632607666025330 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "ab-update.h" #include "dbus-service-defs.h" #include "config.h" class ABUpdateAdaptor : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", CPIM_UPDATE_OBJECT_IFACE_NAME) Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") Q_PROPERTY(bool needsUpdate READ needsUpdate NOTIFY onNeedsUpdateChanged) Q_PROPERTY(bool isRunning READ isRunning NOTIFY onIsRunningChanged) public: ABUpdateAdaptor(ABUpdate *parent = 0); ~ABUpdateAdaptor(); bool needsUpdate() const; bool isRunning() const; public Q_SLOTS: void startUpdate(); void cancelUpdate(); Q_SIGNALS: void onNeedsUpdateChanged(); void onIsRunningChanged(); void updateDone(); void updateError(const QString &errorMessage); private: ABUpdate *m_abUpdate; bool m_needsUpdate; bool m_isRunning; }; address-book-service-0.1.1+16.04.20151211.1/updater/ab-notify-message.h0000644000015300001610000000420212632607666025354 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include class ABNotifyMessage : public QObject { Q_OBJECT public: ABNotifyMessage(bool singleMessage, QObject *parent = 0); ~ABNotifyMessage(); void show(const QString &title, const QString &msg, const QString &iconName); void askYesOrNo(const QString &title, const QString &msg, const QString &iconName); void askQuestion(const QString &title, const QString &iconName, const QString &question, const QMap &actions); int closedReason() const; Q_SIGNALS: void questionAccepted(); void questionRejected(); void questionReplied(const QString &action); void messageClosed(); private: NotifyNotification *m_notification; bool m_singleMessage; static int m_instanceCount; static void onQuestionAccepted(NotifyNotification *notification, char *action, ABNotifyMessage *self); static void onQuestionRejected(NotifyNotification *notification, char *action, ABNotifyMessage *self); static void onNotificationClosed(NotifyNotification *notification, ABNotifyMessage *self); static void onQuestionReplied(NotifyNotification *notification, char *action, ABNotifyMessage *self); }; address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-module.cpp0000644000015300001610000000147212632607666025530 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ab-update-module.h" ABUpdateModule::ABUpdateModule(QObject *parent) : QObject(parent) { } ABUpdateModule::~ABUpdateModule() { } address-book-service-0.1.1+16.04.20151211.1/updater/ab-notify-message.cpp0000644000015300001610000001476712632607666025730 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ab-notify-message.h" #include "ab-i18n.h" #include #include int ABNotifyMessage::m_instanceCount = 0; ABNotifyMessage::ABNotifyMessage(bool singleMessage, QObject *parent) : QObject(parent), m_notification(0), m_singleMessage(singleMessage) { if (m_instanceCount == 0) { m_instanceCount++; notify_init(QCoreApplication::instance()->applicationName().toUtf8()); } } ABNotifyMessage::~ABNotifyMessage() { if (m_notification) { g_object_unref(m_notification); m_notification = 0; } } void ABNotifyMessage::show(const QString &title, const QString &msg, const QString &iconName) { m_notification = notify_notification_new(title.toUtf8().data(), msg.toUtf8().data(), iconName.isEmpty() ? (const char*) 0 : iconName.toUtf8().constData()); GError *error = 0; notify_notification_set_timeout(m_notification, NOTIFY_EXPIRES_DEFAULT); notify_notification_show(m_notification, &error); if (error) { qWarning() << "Fail to launch notification" << error->message; g_error_free(error); } g_signal_connect_after(m_notification, "closed", (GCallback) ABNotifyMessage::onNotificationClosed, this); } void ABNotifyMessage::askYesOrNo(const QString &title, const QString &msg, const QString &iconName) { m_notification = notify_notification_new(title.toUtf8().data(), msg.toUtf8().data(), iconName.isEmpty() ? (const char*) 0 : iconName.toUtf8().constData()); notify_notification_set_hint_string(m_notification, "x-canonical-snap-decisions", "true"); notify_notification_set_hint_string(m_notification, "x-canonical-private-button-tint", "true"); notify_notification_set_hint_string(m_notification, "x-canonical-non-shaped-icon", "true"); notify_notification_add_action(m_notification, "action_accept", _("Yes"), (NotifyActionCallback) ABNotifyMessage::onQuestionAccepted, this, NULL); notify_notification_add_action(m_notification, "action_reject", _("No"), (NotifyActionCallback) ABNotifyMessage::onQuestionRejected, this, NULL); notify_notification_show(m_notification, 0); g_signal_connect_after(m_notification, "closed", (GCallback) ABNotifyMessage::onNotificationClosed, this); } void ABNotifyMessage::askQuestion(const QString &title, const QString &iconName, const QString &question, const QMap &actions) { m_notification = notify_notification_new(title.toUtf8().data(), question.toUtf8().data(), iconName.isEmpty() ? (const char*) 0 : iconName.toUtf8().constData()); notify_notification_set_hint_string(m_notification, "x-canonical-snap-decisions", "true"); notify_notification_set_hint_string(m_notification, "x-canonical-private-button-tint", "true"); notify_notification_set_hint_int32(m_notification, "x-canonical-snap-decisions-timeout", -1); Q_FOREACH(const QString &act, actions.keys()) { notify_notification_add_action(m_notification, act.toUtf8().data(), actions.value(act).toUtf8().data(), (NotifyActionCallback) ABNotifyMessage::onQuestionReplied, this, NULL); } notify_notification_show(m_notification, 0); g_signal_connect_after(m_notification, "closed", (GCallback) ABNotifyMessage::onNotificationClosed, this); } int ABNotifyMessage::closedReason() const { if (m_notification) { return notify_notification_get_closed_reason(m_notification); } else { return 0; } } void ABNotifyMessage::onQuestionAccepted(NotifyNotification *notification, char *action, ABNotifyMessage *self) { Q_UNUSED(notification) Q_UNUSED(action) Q_EMIT self->questionAccepted(); } void ABNotifyMessage::onQuestionRejected(NotifyNotification *notification, char *action, ABNotifyMessage *self) { Q_UNUSED(notification) Q_UNUSED(action) Q_EMIT self->questionRejected(); } void ABNotifyMessage::onNotificationClosed(NotifyNotification *notification, ABNotifyMessage *self) { Q_UNUSED(notification) Q_EMIT self->messageClosed(); if (self->m_singleMessage) { self->deleteLater(); } } void ABNotifyMessage::onQuestionReplied(NotifyNotification *notification, char *action, ABNotifyMessage *self) { Q_UNUSED(notification) Q_EMIT self->questionReplied(QString::fromUtf8(action)); if (self->m_singleMessage) { self->deleteLater(); } } address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-buteo-import.cpp0000644000015300001610000007641312632607666026700 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ab-update-buteo-import.h" #include "ab-notify-message.h" #include "ab-i18n.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #define UOA_CONTACTS_SERVICE_TYPE "contacts" #define BUTEO_UOA_SERVICE_NAME "google-buteo-contacts" #define SYNCEVO_UOA_SERVICE_NAME "google-carddav" #define BUTEO_DBUS_SERVICE_NAME "com.meego.msyncd" #define BUTEO_DBUS_OBJECT_PATH "/synchronizer" #define BUTEO_DBUS_INTERFACE "com.meego.msyncd" #define SYNCMONITOR_DBUS_SERVICE_NAME "com.canonical.SyncMonitor" #define SYNCMONITOR_DBUS_OBJECT_PATH "/com/canonical/SyncMonitor" #define SYNCMONITOR_DBUS_INTERFACE "com.canonical.SyncMonitor" #define TRANSFER_ICON "/usr/share/icons/suru/status/scalable/transfer-progress.svg" using namespace QtContacts; AccountInfo::AccountInfo(quint32 _accountId, const QString &_accountName, bool _syncEnabled, const QString &_oldSourceId, const QString &_newSourceId, bool _emptySource) : accountId(_accountId), accountName(_accountName), syncEnabled(_syncEnabled), oldSourceId(_oldSourceId), newSourceId(_newSourceId), emptySource(_emptySource), removeAfterUpdate(true) { } AccountInfo::AccountInfo(const AccountInfo &other) : accountId(other.accountId), accountName(other.accountName), syncEnabled(other.syncEnabled), oldSourceId(other.oldSourceId), newSourceId(other.newSourceId), emptySource(other.emptySource), syncProfile(other.syncProfile), removeAfterUpdate(other.removeAfterUpdate) { } void AccountInfo::enableSync(const QString &syncService, bool enable) { Accounts::Manager mgr; QScopedPointer account(mgr.account(accountId)); Accounts::Service service = mgr.service(syncService); if (!service.isValid()) { qWarning() << "Fail to enable" << syncService << "for account" << accountId << accountName; } else { account->selectService(service); if (account->enabled() != enable) { account->setEnabled(enable); account->syncAndBlock(); syncEnabled = enable; } } } ButeoImport::ButeoImport(QObject *parent) : ABUpdateModule(parent), m_lastError(ABUpdateModule::NoError) { } ButeoImport::~ButeoImport() { } QString ButeoImport::name() const { return QStringLiteral("Buteo"); } QStringList ButeoImport::runningSyncs() const { if (m_buteoInterface.isNull()) { return QStringList(); } QDBusReply result = m_buteoInterface->call("runningSyncs"); if (result.error().isValid()) { qWarning() << "Fail to retrieve running syncs" << result.error(); return QStringList(); } return result.value(); } QString ButeoImport::profileName(const QString &xml) const { QDomDocument doc; QString errorMsg; int errorLine; int errorColumn; if (doc.setContent(xml, &errorMsg, &errorLine, &errorColumn)) { QDomNodeList profileElements = doc.elementsByTagName("profile"); if (!profileElements.isEmpty()) { QDomElement e = profileElements.item(0).toElement(); return e.attribute("name"); } else { qWarning() << "Invalid profile" << xml; } } else { qWarning() << "Fail to parse profile:" << xml; qWarning() << "Error:" << errorMsg << errorLine << errorColumn; } return QString(); } QString ButeoImport::profileName(quint32 accountId) const { if (m_buteoInterface.isNull()) { return QString(); } QDBusReply reply = m_buteoInterface->call("syncProfilesByKey", "accountid", QString::number(accountId)); if (!reply.value().isEmpty()) { return this->profileName(reply.value().first()); } return QString(); } bool ButeoImport::startSync(const QString &profile) const { if (m_buteoInterface.isNull()) { qWarning() << "Buteo interface is not valid"; return false; } QDBusReply result = m_buteoInterface->call("startSync", profile); if (result.error().isValid()) { qWarning() << "Fail to start sync for profile" << profile << result.error().message(); return false; } return result.value(); } bool ButeoImport::matchFavorites() { QMap parameters; parameters.insert(ADDRESS_BOOK_SHOW_INVISIBLE_PROP, "true"); QScopedPointer manager(new QContactManager("galera", parameters)); // load old favorites QContactDetailFilter folksFavorite; folksFavorite.setDetailType(QContactDetail::TypeFavorite, QContactFavorite::FieldFavorite); folksFavorite.setValue(true); QContactFetchHint hint; hint.setDetailTypesHint(QList() << QContactDetail::TypeName << QContactDetail::TypeEmailAddress << QContactDetail::TypePhoneNumber); QList favorites = manager->contacts(folksFavorite, QList(), hint); qDebug() << "number of folks favorites" << favorites.size(); // try to match with new contacts QList toUpdate; Q_FOREACH(const QContact &f, favorites) { QContactIntersectionFilter iFilter; // No favorite QContactDetailFilter noFavorite; noFavorite.setDetailType(QContactDetail::TypeFavorite, QContactFavorite::FieldFavorite); noFavorite.setValue(false); iFilter.append(noFavorite); // By name QContactDetailFilter firstNameFilter; firstNameFilter.setDetailType(QContactDetail::TypeName, QContactName::FieldFirstName); firstNameFilter.setValue(f.detail().firstName()); iFilter.append(firstNameFilter); QContactDetailFilter lastNameFilter; lastNameFilter.setDetailType(QContactDetail::TypeName, QContactName::FieldLastName); lastNameFilter.setValue(f.detail().lastName()); iFilter.append(lastNameFilter); // By Email Q_FOREACH(const QContactEmailAddress &e, f.details()) { QContactDetailFilter emailFilter; emailFilter.setDetailType(QContactDetail::TypeEmailAddress, QContactEmailAddress::FieldEmailAddress); emailFilter.setValue(e.emailAddress()); iFilter.append(emailFilter); } // By Phone Q_FOREACH(const QContactPhoneNumber &p, f.details()) { QContactDetailFilter phoneFilter; phoneFilter.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); phoneFilter.setValue(p.number()); iFilter.append(phoneFilter); } QList contacts = manager->contacts(iFilter); if (contacts.isEmpty()) { qWarning() << "Favorite contact not found" << f; } else { qDebug() << "Number of contacts that match with old favorite" << contacts.size(); } Q_FOREACH(QContact c, contacts) { QContactFavorite fav = c.detail(); fav.setFavorite(true); c.saveDetail(&fav); toUpdate << c; } } if (!toUpdate.isEmpty()) { if (!manager->saveContacts(&toUpdate)) { qWarning() << "Fail to save favorite contacts"; return false; } } return true; } bool ButeoImport::checkOldAccounts() { // check if the user has account with contacts and disabled sync m_disabledAccounts.clear(); m_lastError = ABUpdateModule::NoError; for(int i=0; i < m_accounts.size(); i++) { const AccountInfo &acc = m_accounts.at(i); if (!acc.syncEnabled && !acc.emptySource) { qDebug() << "Account needs to be enabled:" << acc.accountId << acc.accountName; m_disabledAccounts << i; } } askAboutDisabledAccounts(); } void ButeoImport::askAboutDisabledAccounts() { if (m_disabledAccounts.isEmpty()) { syncOldContacts(); return; } const AccountInfo &acc = m_accounts.at(m_disabledAccounts.first()); QMap updateOptions; updateOptions.insert("enable", _("Enable Sync")); updateOptions.insert("disable", _("Keep Disabled")); qDebug() << "Ask question if keep account" << acc.accountName; ABNotifyMessage *msg = new ABNotifyMessage(true, this); connect(msg, SIGNAL(questionReplied(QString)), this, SLOT(onEnableAccountsReplied(QString))); msg->askQuestion(_("Contact Sync Upgrade"), TRANSFER_ICON, QString(_("Google account %1 currently has contact sync disabled.\n" "We need to enable it to proceed with the contact sync upgrade.\n" "If you keep it disabled, your contacts will be saved but you won't be able to sync them anymore with this account.") ).arg(acc.accountName), updateOptions); } void ButeoImport::onEnableAccountsReplied(const QString &reply) { AccountInfo &acc = m_accounts[m_disabledAccounts.takeFirst()]; qDebug() << "Account" << acc.accountId << acc.accountName << reply; if (reply == "enable") { acc.enableSync(SYNCEVO_UOA_SERVICE_NAME); } else if (reply == "disable") { acc.removeAfterUpdate = false; } askAboutDisabledAccounts(); } bool ButeoImport::syncOldContacts() { //call syncevolution if (m_syncMonitorInterface.isNull()) { m_syncMonitorInterface.reset(new QDBusInterface(SYNCMONITOR_DBUS_SERVICE_NAME, SYNCMONITOR_DBUS_OBJECT_PATH, SYNCMONITOR_DBUS_INTERFACE)); if (!m_buteoInterface->isValid()) { m_buteoInterface.reset(); qWarning() << "Fail to connect with sync-monitor"; return false; } connect(m_syncMonitorInterface.data(), SIGNAL(syncFinished(QString, QString)), SLOT(onOldContactsSyncFinished(QString,QString)), Qt::UniqueConnection); connect(m_syncMonitorInterface.data(), SIGNAL(syncError(QString, QString, QString)), SLOT(onOldContactsSyncError(QString,QString,QString)), Qt::UniqueConnection); } m_syncEvolutionQueue.clear(); for(int i=0; i < m_accounts.size(); i++) { const AccountInfo &accInfo = m_accounts[i]; // if the account is disabled or the new source was already created we do not need to sync if (accInfo.syncEnabled && accInfo.newSourceId.isEmpty() && !accInfo.oldSourceId.isEmpty()) { qDebug() << "SyncEvolution: Prepare to sync" << accInfo.accountId << accInfo.accountName; m_syncEvolutionQueue << i; } else { qDebug() << "SyncEvolution: Skip sync for disabled account" << accInfo.accountId << accInfo.accountName; } } syncOldContactsContinue(); } void ButeoImport::syncOldContactsContinue() { if (m_syncEvolutionQueue.isEmpty()) { continueUpdate(); return; } const AccountInfo &accInfo = m_accounts[m_syncEvolutionQueue.first()]; QDBusReply result = m_syncMonitorInterface->call("syncAccount", accInfo.accountId, "contacts"); if (result.error().isValid()) { qWarning() << "SyncEvolution: Fail to start account sync" << accInfo.accountId << accInfo.accountName << result.error(); onError("", ButeoImport::InitialSyncError, true); } else { qDebug() << "SyncEvolution: Syncing" << accInfo.accountId << accInfo.accountName; } } ABUpdateModule::ImportError ButeoImport::parseError(int errorCode) const { switch (errorCode) { case 9: //SYNC_AUTHENTICATION_FAILURE: return ABUpdateModule::FailToAuthenticate; case 11: //SYNC_CONNECTION_ERROR: return ABUpdateModule::ConnectionError; case 14: //SYNC_PLUGIN_ERROR: case 15: //SYNC_PLUGIN_TIMEOUT: return ABUpdateModule::FailToConnectWithButeo; default: return ABUpdateModule::SyncError; } } void ButeoImport::sourceInfo(Accounts::Account *account, QString &oldSourceId, QString &newSourceId, bool &isEmpty) { QString accountName = account->displayName(); QScopedPointer manager(new QContactManager("galera")); QContactDetailFilter sourceFilter; sourceFilter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); sourceFilter.setValue(QContactType::TypeGroup); Q_FOREACH(const QContact &c, manager->contacts(sourceFilter)) { if (c.detail().label() == accountName) { bool newSource = false; Q_FOREACH(const QContactExtendedDetail &xDet, c.details()) { if (xDet.name() == "ACCOUNT-ID") { if (xDet.data().toUInt() == account->id()) { newSource = true; break; } } } if (newSource) { newSourceId = c.id().toString(); } else { oldSourceId = c.id().toString(); } } if (!newSourceId.isEmpty() && !oldSourceId.isEmpty()) { // both sources found break; } } if (!oldSourceId.isEmpty()) { QMap parameters; parameters.insert(ADDRESS_BOOK_SHOW_INVISIBLE_PROP, "true"); QScopedPointer manager(new QContactManager("galera", parameters)); QContactDetailFilter sourceFilter; sourceFilter.setDetailType(QContactSyncTarget::Type, QContactSyncTarget::FieldSyncTarget + 1); sourceFilter.setValue(oldSourceId.replace("qtcontacts:galera::source@", "")); QContactFetchHint fetchHint; fetchHint.setMaxCountHint(1); QList contacts = manager->contacts(sourceFilter, QList(), fetchHint); isEmpty = contacts.isEmpty(); } else { isEmpty = true; } } bool ButeoImport::needsUpdate() { // check settings QSettings settings; if (settings.value(SETTINGS_BUTEO_KEY, false).toBool()) { qDebug() << "Buteo has already been updated."; return false; } Accounts::Manager mgr; Accounts::AccountIdList accounts = mgr.accountList(UOA_CONTACTS_SERVICE_TYPE); if (accounts.isEmpty()) { // update settings key QSettings settings; settings.setValue(SETTINGS_BUTEO_KEY, true); settings.sync(); return false; } return true; } bool ButeoImport::prepareToUpdate() { // populate accounts map; Accounts::Manager mgr; Accounts::AccountIdList accounts = mgr.accountList(UOA_CONTACTS_SERVICE_TYPE); m_accounts.clear(); qDebug() << "Loading account information"; Q_FOREACH(Accounts::AccountId accountId, accounts) { QScopedPointer acc(mgr.account(accountId)); if (acc->providerName() != "google") { qDebug() << "Skip non google account" << acc->displayName(); continue; } QString oldSourceId; QString newSourceId; bool isEmpty; sourceInfo(acc.data(), oldSourceId, newSourceId, isEmpty); Accounts::Service service = mgr.service(SYNCEVO_UOA_SERVICE_NAME); acc->selectService(service); AccountInfo accInfo(accountId, acc->displayName(), acc->isEnabled(), oldSourceId, newSourceId, isEmpty); accInfo.syncProfile = profileName(accountId); qDebug() << "\tAccount:" << accountId << acc->displayName() << "\n\t\tEnabled" << acc->isEnabled() << "\n\t\tOld source:" << oldSourceId << "isEmpty" << isEmpty << "\n\t\tNew source:" << newSourceId << "\n\t\tSync profile:" << accInfo.syncProfile; m_accounts << accInfo; } return true; } bool ButeoImport::update() { if (!m_importLock.tryLock()) { qWarning() << "Fail to lock import mutex"; onError("", ButeoImport::InernalError, false); return false; } if (m_accounts.isEmpty()) { qDebug() << "No accounts to update"; // if there is not account to update just commit the update m_importLock.unlock(); Q_EMIT updated(); return true; } return checkOldAccounts(); } bool ButeoImport::continueUpdate() { m_failToSyncProfiles.clear(); m_buteoQueue.clear(); // enable buteo sync service if necessary Q_FOREACH(AccountInfo info, m_accounts) { if (info.syncEnabled) { info.enableSync(BUTEO_UOA_SERVICE_NAME); } } for(int i=0; i < m_accounts.size(); i++) { AccountInfo &accInfo = m_accounts[i]; if (accInfo.syncProfile.isEmpty()) { qDebug() << "BUTEO: Will create buteo profile for" << accInfo.accountId << "account"; accInfo.syncProfile = createProfileForAccount(accInfo.accountId); if (accInfo.syncProfile.isEmpty()) { // fail to create profiles qWarning() << "Fail to create profiles"; onError("", ButeoImport::FailToCreateButeoProfiles, true); return false; } if (accInfo.syncEnabled) { qDebug() << "BUTEO: Will start sync" << accInfo.accountId << accInfo.accountName; m_buteoQueue.insert(i, accInfo.syncProfile); } else { qDebug() << "BUTEO: account sync disabled" << accInfo.accountId << accInfo.accountName; } } else if (accInfo.syncEnabled) { qDebug() << "BUTEO: Will start sync" << accInfo.accountId << accInfo.accountName; m_buteoQueue.insert(i, accInfo.syncProfile); if (!startSync(accInfo.syncProfile)) { qWarning() << "Fail to start sync" << accInfo.syncProfile; m_buteoQueue.remove(i); } } else { qDebug() << "BUTEO: Sync disabled for account" << accInfo.accountId << accInfo.accountName; } } if (m_buteoQueue.isEmpty()) { qDebug() << "No account to update"; QTimer::singleShot(0, this, SIGNAL(updated())); } return true; } bool ButeoImport::prepareButeo() { if (!m_buteoInterface.isNull()) { return true; } m_buteoInterface.reset(new QDBusInterface(BUTEO_DBUS_SERVICE_NAME, BUTEO_DBUS_OBJECT_PATH, BUTEO_DBUS_INTERFACE)); if (!m_buteoInterface->isValid()) { m_buteoInterface.reset(); qWarning() << "Fail to connect with syncfw"; return false; } connect(m_buteoInterface.data(), SIGNAL(signalProfileChanged(QString, int, QString)), SLOT(onProfileChanged(QString, int, QString))); connect(m_buteoInterface.data(), SIGNAL(syncStatus(QString,int,QString,int)), SLOT(onSyncStatusChanged(QString,int,QString,int))); return true; } QString ButeoImport::createProfileForAccount(quint32 id) { if (m_buteoInterface.isNull()) { qWarning() << "Buteo interface is not valid"; return QString(); } QDBusReply result = m_buteoInterface->call("createSyncProfileForAccount", id); if (result.error().isValid()) { qWarning() << "Fail to create profile for account" << id << result.error(); } else if (!result.value().isEmpty()) { qDebug() << "Profile created" << result.value() << id; return result.value(); } else { qWarning() << "Fail to create profile for account" << id; } return QString(); } bool ButeoImport::removeProfile(const QString &profileId) { if (m_buteoInterface.isNull()) { qWarning() << "Buteo interface is not valid"; return false; } // check for account quint32 accountId = 0; QString newSourceId; for (int i=0; i < m_accounts.size(); i++) { AccountInfo acc = m_accounts[i]; if (acc.syncProfile == profileId) { accountId = acc.accountId; newSourceId = acc.newSourceId; break; } } if (accountId != 0) { qWarning() << "Fail to find account related with profile" << profileId; return false; } // remove source if (!newSourceId.isEmpty()) { QScopedPointer manager(new QContactManager("galera")); if (!manager->removeContact(QContactId::fromString(newSourceId))) { qWarning() << "Fail to remove contact source:" << newSourceId; return false; } else { qDebug() << "Source removed" << newSourceId; } } else { qDebug() << "No source was created for account" << accountId; } // remove profile return buteoRemoveProfile(profileId); } bool ButeoImport::buteoRemoveProfile(const QString &profileId) const { QDBusReply result = m_buteoInterface->call("removeProfile", profileId); if (result.error().isValid()) { qWarning() << "Fail to remove profile" << profileId << result.error(); return false; } return true; } bool ButeoImport::removeSources(const QStringList &sources) { if (sources.isEmpty()) { return true; } bool result = true; QScopedPointer manager(new QContactManager("galera")); Q_FOREACH(const QString &source, sources) { QString sourceId(source); // append source id prefix if necessary if (!sourceId.startsWith("qtcontacts:galera::source@")) { sourceId = QString("qtcontacts:galera::source@%2").arg(sourceId); } if (!manager->removeContact(QContactId::fromString(sourceId))) { qWarning() << "Fail to remove source" << sourceId; result = false; } else { qDebug() << "source removed" << sourceId; } } return result; } bool ButeoImport::restoreService() { QDBusMessage setSafeMode = QDBusMessage::createMethodCall("com.canonical.pim", "/com/canonical/pim/AddressBook", "org.freedesktop.DBus.Properties", "Set"); QList args; args << "com.canonical.pim.AddressBook" << "safeMode" << QVariant::fromValue(QDBusVariant(false)); setSafeMode.setArguments(args); QDBusReply reply = QDBusConnection::sessionBus().call(setSafeMode); if (reply.error().isValid()) { qWarning() << "Fail to disable safe-mode" << reply.error().message(); return false; } else { qDebug() << "Server safe mode disabled"; return true; } } bool ButeoImport::commit() { Q_ASSERT(m_buteoQueue.isEmpty()); // update new favorites matchFavorites(); QStringList sourceToRemove; for(int i=0; i < m_accounts.size(); i++) { AccountInfo &accInfo = m_accounts[i]; if (accInfo.removeAfterUpdate) { qDebug() << "Will remove source for old account" << accInfo.accountId << accInfo.accountName; sourceToRemove << accInfo.oldSourceId; } // disable old syncevolution service if (accInfo.syncEnabled) { qDebug() << "SyncEvo: Disable old sync" << accInfo.accountId << accInfo.accountName; accInfo.enableSync(SYNCEVO_UOA_SERVICE_NAME, false); } } removeSources(sourceToRemove); m_accounts.clear(); // all accounts synced m_importLock.unlock(); // update settings key QSettings settings; settings.setValue(SETTINGS_BUTEO_KEY, true); settings.sync(); // disable address-book-service safe-mode restoreService(); // WORKAROUND: wait 4 secs to fire update done, this is necessary because the contacts will be set as favorite // just after the signal be fired and the changes can not have the same timestamp that the creation QTimer::singleShot(4000, this, SIGNAL(updated())); return true; } bool ButeoImport::rollback() { // remove all profiles and new sources Q_FOREACH(const QString &profile, m_failToSyncProfiles) { removeProfile(profile); } m_failToSyncProfiles.clear(); return true; } bool ButeoImport::markAsUpdate() { return restoreService(); } void ButeoImport::onError(const QString &accountName, int errorCode, bool unlock) { if (unlock) { m_importLock.unlock(); } m_lastError = ABUpdateModule::ImportError(errorCode); Q_EMIT updateError(accountName, m_lastError); } void ButeoImport::onOldContactsSyncFinished(const QString &accountName, const QString &serviceName) { if (m_syncEvolutionQueue.isEmpty()) { return; } AccountInfo info = m_accounts.at(m_syncEvolutionQueue.first()); if (info.accountName == accountName && serviceName == "contacts") { m_syncEvolutionQueue.takeFirst(); syncOldContactsContinue(); } else { qDebug() << "Sync finished ignored:" << accountName << serviceName; } } void ButeoImport::onOldContactsSyncError(const QString &accountName, const QString &serviceName, const QString &error) { if (m_syncEvolutionQueue.isEmpty()) { return; } AccountInfo info = m_accounts.at(m_syncEvolutionQueue.first()); if (info.accountName == accountName && serviceName == "contacts") { m_syncEvolutionQueue.takeFirst(); qWarning() << "SyncEvolution: Fail to sync account " << accountName << serviceName << error; onError("", ButeoImport::InitialSyncError, true); } else { qDebug() << "Sync Error ignored:" << accountName << serviceName << error; } } bool ButeoImport::requireInternetConnection() { return true; } bool ButeoImport::canUpdate() { if (!prepareButeo()) { qWarning() << "Fail to connect with contact sync service. We can not continue the upgrade"; return false; } // check for any running sync QStringList syncs = runningSyncs(); if (!syncs.isEmpty()) { qWarning() << "Sync running" << syncs << "no way to check for outdated information"; return false; } return true; } ButeoImport::ImportError ButeoImport::lastError() const { return m_lastError; } void ButeoImport::onProfileChanged(const QString &profileName, int changeType, const QString &profileAsXml) { Q_UNUSED(profileAsXml); /* * 0 (ADDITION): Profile was added. * 1 (MODIFICATION): Profile was modified. * 2 (DELETION): Profile was deleted. */ switch(changeType) { case 0: // profile created sync should start soon qDebug() << "Signal profile created received" << profileName; break; case 1: break; case 2: { int index = m_buteoQueue.key(profileName, -1); if (index != -1) { AccountInfo &accInfo = m_accounts[index]; accInfo.syncProfile = ""; m_buteoQueue.remove(index); qDebug() << "Profile removed" << accInfo.accountId << profileName; } if (m_buteoQueue.isEmpty()) { // all accounts removed m_importLock.unlock(); } break; } } } void ButeoImport::onSyncStatusChanged(const QString &profileName, int status, const QString &message, int moreDetails) { Q_UNUSED(message); Q_UNUSED(moreDetails); int index = m_buteoQueue.key(profileName, -1); if (index == -1) { qDebug() << "Profile not found" << profileName << m_buteoQueue.values(); return; } AccountInfo &accInfo = m_accounts[index]; qDebug() << "SyncStatus" << "\n\tProfile:" << profileName << "\n\tAccount:" << accInfo.accountId << "\n\tStatus:" << status << "\n\tMessage:" << message << "\n\tDetails:" << moreDetails; /* * 0 (QUEUED): Sync request has been queued or was already in the * queue when sync start was requested. * 1 (STARTED): Sync session has been started. * 2 (PROGRESS): Sync session is progressing. * 3 (ERROR): Sync session has encountered an error and has been stopped, * or the session could not be started at all. * 4 (DONE): Sync session was successfully completed. * 5 (ABORTED): Sync session was aborted. */ switch(status) { case 0: case 1: case 2: return; case 3: if (!accInfo.syncEnabled) { // error because the account is not enabled } else { qWarning() << "Sync error for account:" << accInfo.accountId << "and profile" << profileName; m_failToSyncProfiles << profileName; m_lastError = parseError(moreDetails); } break; case 4: qDebug() << "Sync finished for account:" << accInfo.accountId << "and profile" << profileName; break; case 5: qWarning() << "Sync aborted for account:" << accInfo.accountId << "and profile" << profileName; m_failToSyncProfiles << profileName; break; } m_buteoQueue.remove(index); qDebug() << "Accounts to sync" << m_buteoQueue; if (m_buteoQueue.isEmpty()) { qDebug() << "All accounts have finished the sync, number of accounts that fail to sync:" << m_failToSyncProfiles; if (m_failToSyncProfiles.isEmpty()) { Q_EMIT updated(); } else { QMetaObject::invokeMethod(this, "onError", Qt::QueuedConnection, Q_ARG(QString, ""), Q_ARG(int, m_lastError), Q_ARG(bool, true)); } } } address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-buteo-import.h0000644000015300001610000000705412632607666026340 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "ab-update-module.h" class AccountInfo { public: AccountInfo(quint32 _accountId, const QString &_accountName, bool _syncEnabled, const QString &_oldSourceId, const QString &_newSourceId, bool _emptySource); AccountInfo(const AccountInfo &other); void enableSync(const QString &syncService, bool enable=true); quint32 accountId; QString accountName; bool syncEnabled; QString oldSourceId; QString newSourceId; QString syncProfile; bool emptySource; bool removeAfterUpdate; }; class ButeoImport : public ABUpdateModule { Q_OBJECT public: ButeoImport(QObject *parent = 0); ~ButeoImport(); QString name() const override; bool needsUpdate() override; bool prepareToUpdate() override; bool update() override; bool requireInternetConnection() override; bool canUpdate() override; bool commit() override; bool rollback() override; bool markAsUpdate() override; ImportError lastError() const override; private Q_SLOTS: void onProfileChanged(const QString &profileName, int changeType, const QString &profileAsXml); void onSyncStatusChanged(const QString &aProfileName, int aStatus, const QString &aMessage, int aMoreDetails); void onError(const QString &accountName, int errorCode, bool unlock); void onEnableAccountsReplied(const QString &reply); void onOldContactsSyncFinished(const QString &accountName, const QString &serviceName); void onOldContactsSyncError(const QString &accountName, const QString &serviceName, const QString &error); private: QList m_accounts; QList m_disabledAccounts; QList m_syncEvolutionQueue; QMap m_buteoQueue; QStringList m_failToSyncProfiles; QScopedPointer m_buteoInterface; QScopedPointer m_syncMonitorInterface; QMutex m_importLock; ImportError m_lastError; QString createProfileForAccount(quint32 id); bool prepareButeo(); bool createAccounts(QList ids); bool removeProfile(const QString &profileId); bool buteoRemoveProfile(const QString &profileId) const; bool removeSources(const QStringList &sources); bool restoreService(); QStringList runningSyncs() const; QString profileName(const QString &xml) const; QString profileName(quint32 accountId) const; bool startSync(const QString &profile) const; bool matchFavorites(); bool checkOldAccounts(); bool syncOldContacts(); void syncOldContactsContinue(); bool continueUpdate(); ImportError parseError(int errorCode) const; void askAboutDisabledAccounts(); static void sourceInfo(Accounts::Account *account, QString &oldSourceId, QString &newSourceId, bool &isEmpty); }; address-book-service-0.1.1+16.04.20151211.1/updater/ab-update-module.h0000644000015300001610000000326412632607666025176 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include class ABUpdateModule : public QObject { Q_OBJECT Q_ENUMS(ImportError) public: enum ImportError { ApplicationAreadyUpdated = 0, ConnectionError, FailToConnectWithButeo, FailToCreateButeoProfiles, FailToAuthenticate, InernalError, InitialSyncError, OnlineAccountNotFound, SyncAlreadyRunning, SyncError, NoError }; ABUpdateModule(QObject *parent = 0); ~ABUpdateModule() override; virtual QString name() const = 0; virtual bool needsUpdate() = 0; virtual bool prepareToUpdate() = 0; virtual bool update() = 0; virtual bool canUpdate() = 0; virtual bool requireInternetConnection() = 0; virtual bool commit() = 0; virtual bool rollback() = 0; virtual bool markAsUpdate() = 0; virtual ImportError lastError() const = 0; Q_SIGNALS: void updated(); void updateError(const QString &accountName, ABUpdateModule::ImportError errorCode); }; address-book-service-0.1.1+16.04.20151211.1/updater/CMakeLists.txt0000644000015300001610000000203212632607666024430 0ustar pbuserpbgroup00000000000000set(ADDRESS_BOOK_UPDATE_LIB address-book-updater-lib) set(ADDRESS_BOOK_UPDATE_BIN address-book-updater) include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/common ${AccountsQt5_INCLUDE_DIRS} ${LIBNOTIFY_INCLUDE_DIRS} ) set(ADDRESS_BOOK_UPDATE_LIB_SRCS ab-i18n.h ab-notify-message.h ab-notify-message.cpp ab-update.h ab-update.cpp ab-update-adaptor.h ab-update-adaptor.cpp ab-update-buteo-import.h ab-update-buteo-import.cpp ab-update-module.h ab-update-module.cpp ) add_library(${ADDRESS_BOOK_UPDATE_LIB} STATIC ${ADDRESS_BOOK_UPDATE_LIB_SRCS} ) target_link_libraries(${ADDRESS_BOOK_UPDATE_LIB} Qt5::Core Qt5::DBus Qt5::Contacts Qt5::Xml Qt5::Network ${AccountsQt5_LIBRARIES} ${LIBNOTIFY_LIBRARIES} ) add_executable(${ADDRESS_BOOK_UPDATE_BIN} main.cpp ) target_link_libraries(${ADDRESS_BOOK_UPDATE_BIN} ${ADDRESS_BOOK_UPDATE_LIB} ) install(TARGETS ${ADDRESS_BOOK_UPDATE_BIN} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR} ) address-book-service-0.1.1+16.04.20151211.1/updater/ab-i18n.h0000644000015300001610000000144712632607666023211 0ustar pbuserpbgroup00000000000000/* * Copyright 2015 Canonical Ltd. * * This file is part of address-book-service. * * sync-monitor is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * contact-service-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "config.h" extern "C" { #include #define _(String) dgettext (GETTEXT_PACKAGE, String) }