[evolution-data-server/openismus-work] Brute force backport of EBookBackendSqliteDB + phone number work.
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/openismus-work] Brute force backport of EBookBackendSqliteDB + phone number work.
- Date: Thu, 21 Feb 2013 13:45:43 +0000 (UTC)
commit c48698688cad87cfe1459a3a3cde75dcb556122c
Author: Tristan Van Berkom <tristanvb openismus com>
Date: Thu Feb 21 21:58:29 2013 +0900
Brute force backport of EBookBackendSqliteDB + phone number work.
This is basically overwriting our openismus-work phone-number related
work (plus the EBookBackendSqliteDB) from master, ensuring that the
phone number details work the same way in master and in openismus-work.
As for the direct port of EBookBackendSqliteDB, additional work was done
there, it's more stable, mutexes work properly and I trust it more
(plus it conforms to the same API, and has phone number related work
from Mathias).
addressbook/libebook-contacts/Makefile.am | 9 +
.../libebook-contacts/e-phone-number-private.cpp | 311 +++
.../libebook-contacts/e-phone-number-private.h | 85 +
addressbook/libebook-contacts/e-phone-number.c | 389 ++++
.../libebook-contacts/e-phone-number.h | 173 +-
addressbook/libebook-contacts/libebook-contacts.h | 1 +
.../libedata-book/e-book-backend-sqlitedb.c | 2421 +++++++++++---------
libedataserver/Makefile.am | 7 -
libedataserver/e-phone-utils.cpp | 317 ---
libedataserver/libedataserver.h | 1 -
m4/evo_phonenumber.m4 | 80 +-
tests/libebook/Makefile.am | 3 +
tests/libebook/client/Makefile.am | 2 +-
tests/libebook/client/test-client-custom-summary.c | 2 +
tests/libebook/client/test-client-e164-param.c | 3 +
.../libebook/client/test-client-get-contact-uids.c | 3 +
tests/libebook/client/test-client-get-contact.c | 3 +
tests/libebook/client/test-client-get-view.c | 3 +
.../libebook/client/test-client-view-operations.c | 52 +-
tests/libebook/test-ebook-phone-number.c | 437 ++++
tests/libedataserver/Makefile.am | 4 -
tests/libedataserver/e-phone-utils-test.c | 273 ---
22 files changed, 2762 insertions(+), 1817 deletions(-)
---
diff --git a/addressbook/libebook-contacts/Makefile.am b/addressbook/libebook-contacts/Makefile.am
index 876e50d..8140543 100644
--- a/addressbook/libebook-contacts/Makefile.am
+++ b/addressbook/libebook-contacts/Makefile.am
@@ -35,6 +35,8 @@ libebook_contacts_1_2_la_CPPFLAGS = \
libebook_contacts_1_2_la_SOURCES = \
$(ENUM_GENERATED) \
$(MARSHAL_GENERATED) \
+ e-phone-number.c \
+ e-phone-number-private.h \
e-book-contacts-types.c \
e-address-western.c \
e-name-western.c \
@@ -56,6 +58,12 @@ libebook_contacts_1_2_la_LDFLAGS = \
$(CODE_COVERAGE_LDFLAGS) \
$(NULL)
+if ENABLE_PHONENUMBER
+libebook_contacts_1_2_la_SOURCES += e-phone-number-private.cpp
+libebook_contacts_1_2_la_CPPFLAGS += $(PHONENUMBER_INCLUDES)
+libebook_contacts_1_2_la_LIBADD += $(PHONENUMBER_LIBS)
+endif # ENABLE_PHONENUMBER
+
libebookcontactsincludedir = $(privincludedir)/libebook-contacts
libebookcontactsinclude_HEADERS = \
@@ -67,6 +75,7 @@ libebookcontactsinclude_HEADERS = \
e-name-western.h \
e-contact.h \
e-vcard.h \
+ e-phone-number.h \
e-source-backend-summary-setup.h
diff --git a/addressbook/libebook-contacts/e-phone-number-private.cpp
b/addressbook/libebook-contacts/e-phone-number-private.cpp
new file mode 100644
index 0000000..86c4e52
--- /dev/null
+++ b/addressbook/libebook-contacts/e-phone-number-private.cpp
@@ -0,0 +1,311 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ENABLE_PHONENUMBER
+#error Phone number support must be enabled for this file
+#endif /* ENABLE_PHONENUMBER */
+
+#include "e-phone-number-private.h"
+
+/* C++ standard library */
+#include <string>
+
+/* system headers */
+#include <langinfo.h>
+#include <locale.h>
+
+/* libphonenumber */
+#include <phonenumbers/logger.h>
+#include <phonenumbers/phonenumberutil.h>
+
+using i18n::phonenumbers::PhoneNumber;
+using i18n::phonenumbers::PhoneNumberUtil;
+
+struct _EPhoneNumber {
+ PhoneNumber priv;
+};
+
+static PhoneNumberUtil *
+e_phone_number_util_get_instance (void)
+{
+ static PhoneNumberUtil *instance = NULL;
+
+ if (g_once_init_enter (&instance)) {
+ /* FIXME: Ideally PhoneNumberUtil would not be a singleton,
+ * so that we could safely tweak it's attributes without
+ * influencing other users of the library. */
+ PhoneNumberUtil *new_instance = PhoneNumberUtil::GetInstance ();
+
+ /* Disable all logging: libphonenumber is pretty verbose. */
+ new_instance->SetLogger (new i18n::phonenumbers::NullLogger);
+ g_once_init_leave (&instance, new_instance);
+ }
+
+ return instance;
+}
+
+static EPhoneNumberError
+e_phone_number_error_code (PhoneNumberUtil::ErrorType error)
+{
+ switch (error) {
+ case PhoneNumberUtil::NO_PARSING_ERROR:
+ g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+ case PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR:
+ return E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE;
+ case PhoneNumberUtil::NOT_A_NUMBER:
+ return E_PHONE_NUMBER_ERROR_NOT_A_NUMBER;
+ case PhoneNumberUtil::TOO_SHORT_AFTER_IDD:
+ return E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD;
+ case PhoneNumberUtil::TOO_SHORT_NSN:
+ return E_PHONE_NUMBER_ERROR_TOO_SHORT;
+ case PhoneNumberUtil::TOO_LONG_NSN:
+ return E_PHONE_NUMBER_ERROR_TOO_LONG;
+ }
+
+ /* Please file a bug that we can add a proper error code. */
+ g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+}
+
+static std::string
+e_phone_number_make_region_code (const gchar *region_code)
+{
+ /* Get two-letter country code from current locale's address facet if supported */
+#if HAVE__NL_ADDRESS_COUNTRY_AB2
+ if (region_code == NULL || region_code[0] == '\0')
+ region_code = nl_langinfo (_NL_ADDRESS_COUNTRY_AB2);
+#endif /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
+
+ /* Extract two-letter country code from current locale id if needed */
+ if (region_code == NULL || region_code[0] == '\0') {
+ /* From outside this is a C library, so we better consult the
+ * C infrastructure instead of std::locale, which might divert. */
+ std::string current_region = setlocale (LC_ADDRESS, NULL);
+ const std::string::size_type uscore = current_region.find ('_');
+
+ if (uscore != std::string::npos) {
+ const std::string::size_type n = std::min (uscore + 3, current_region.length ());
+
+ if (n == current_region.length() || not ::isalpha(current_region.at(n)))
+ current_region = current_region.substr (uscore + 1, 2);
+ }
+
+ if (current_region.length() != 2)
+ return "US";
+
+ return current_region;
+ }
+
+ return region_code;
+}
+
+gint
+_e_phone_number_cxx_get_country_code_for_region (const gchar *region_code)
+{
+ return e_phone_number_util_get_instance ()->GetCountryCodeForRegion (
+ e_phone_number_make_region_code (region_code));
+}
+
+gchar *
+_e_phone_number_cxx_get_default_region ()
+{
+ return g_strdup (e_phone_number_make_region_code (NULL).c_str ());
+}
+
+EPhoneNumber *
+_e_phone_number_cxx_from_string (const gchar *phone_number,
+ const gchar *region_code,
+ GError **error)
+{
+ g_return_val_if_fail (NULL != phone_number, NULL);
+
+ const std::string valid_region = e_phone_number_make_region_code (region_code);
+ std::auto_ptr<EPhoneNumber> parsed_number(new EPhoneNumber);
+
+ const PhoneNumberUtil::ErrorType err =
+#ifdef PHONENUMBER_RAW_INPUT_NEEDED
+ e_phone_number_util_get_instance ()->ParseAndKeepRawInput (
+ phone_number, valid_region, &parsed_number->priv);
+#else /* PHONENUMBER_RAW_INPUT_NEEDED */
+ e_phone_number_util_get_instance ()->Parse (
+ phone_number, valid_region, &parsed_number->priv);
+#endif /* PHONENUMBER_RAW_INPUT_NEEDED */
+
+ if (err != PhoneNumberUtil::NO_PARSING_ERROR) {
+ _e_phone_number_set_error (error, e_phone_number_error_code (err));
+ return NULL;
+ }
+
+ return parsed_number.release ();
+}
+
+gchar *
+_e_phone_number_cxx_to_string (const EPhoneNumber *phone_number,
+ EPhoneNumberFormat format)
+{
+ g_return_val_if_fail (NULL != phone_number, NULL);
+
+ std::string formatted_number;
+
+ e_phone_number_util_get_instance ()->Format
+ (phone_number->priv,
+ static_cast<PhoneNumberUtil::PhoneNumberFormat> (format),
+ &formatted_number);
+
+ if (!formatted_number.empty ())
+ return g_strdup (formatted_number.c_str ());
+
+ return NULL;
+}
+
+static EPhoneNumberCountrySource
+e_phone_number_get_country_source (const EPhoneNumber *phone_number)
+{
+ g_return_val_if_fail (
+ phone_number->priv.has_country_code_source (),
+ E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT);
+
+ switch (phone_number->priv.country_code_source ()) {
+ case PhoneNumber::FROM_NUMBER_WITH_PLUS_SIGN:
+ return E_PHONE_NUMBER_COUNTRY_FROM_FQTN;
+
+ case PhoneNumber::FROM_NUMBER_WITH_IDD:
+ return E_PHONE_NUMBER_COUNTRY_FROM_IDD;
+
+ /* FROM_NUMBER_WITHOUT_PLUS_SIGN only is used internally
+ * by libphonenumber to properly(???) reconstruct raw input
+ * from PhoneNumberUtil::ParseAndKeepRawInput(). Let's not
+ * bother our users with that barely understandable and
+ * almost undocumented implementation detail.
+ */
+ case PhoneNumber::FROM_NUMBER_WITHOUT_PLUS_SIGN:
+ case PhoneNumber::FROM_DEFAULT_COUNTRY:
+ break;
+ }
+
+ return E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT;
+}
+
+gint
+_e_phone_number_cxx_get_country_code (const EPhoneNumber *phone_number,
+ EPhoneNumberCountrySource *source)
+{
+ g_return_val_if_fail (NULL != phone_number, 0);
+
+ if (phone_number->priv.has_country_code ()) {
+ if (source)
+ *source = e_phone_number_get_country_source (phone_number);
+
+ return phone_number->priv.country_code ();
+ }
+
+ return 0;
+}
+
+gchar *
+_e_phone_number_cxx_get_national_number (const EPhoneNumber *phone_number)
+{
+ g_return_val_if_fail (NULL != phone_number, NULL);
+
+ std::string national_number;
+
+ e_phone_number_util_get_instance ()->GetNationalSignificantNumber (
+ phone_number->priv, &national_number);
+
+ if (!national_number.empty ())
+ return g_strdup (national_number.c_str ());
+
+ return NULL;
+}
+
+static EPhoneNumberMatch
+e_phone_number_match (PhoneNumberUtil::MatchType match_type)
+{
+ switch (match_type) {
+ case PhoneNumberUtil::NO_MATCH:
+ case PhoneNumberUtil::INVALID_NUMBER:
+ return E_PHONE_NUMBER_MATCH_NONE;
+ case PhoneNumberUtil::SHORT_NSN_MATCH:
+ return E_PHONE_NUMBER_MATCH_SHORT;
+ case PhoneNumberUtil::NSN_MATCH:
+ return E_PHONE_NUMBER_MATCH_NATIONAL;
+ case PhoneNumberUtil::EXACT_MATCH:
+ return E_PHONE_NUMBER_MATCH_EXACT;
+ }
+
+ g_return_val_if_reached (E_PHONE_NUMBER_MATCH_NONE);
+}
+
+EPhoneNumberMatch
+_e_phone_number_cxx_compare (const EPhoneNumber *first_number,
+ const EPhoneNumber *second_number)
+{
+ g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+ g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+ const PhoneNumberUtil::MatchType match_type =
+ e_phone_number_util_get_instance ()->
+ IsNumberMatch (first_number->priv, second_number->priv);
+
+ g_warn_if_fail (match_type != PhoneNumberUtil::INVALID_NUMBER);
+ return e_phone_number_match (match_type);
+}
+
+EPhoneNumberMatch
+_e_phone_number_cxx_compare_strings (const gchar *first_number,
+ const gchar *second_number,
+ GError **error)
+{
+ EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+ g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+ g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+ const PhoneNumberUtil::MatchType match_type =
+ e_phone_number_util_get_instance ()->
+ IsNumberMatchWithTwoStrings (first_number, second_number);
+
+ if (match_type == PhoneNumberUtil::INVALID_NUMBER) {
+ _e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+ } else {
+ result = e_phone_number_match (match_type);
+ }
+
+ return result;
+}
+
+EPhoneNumber *
+_e_phone_number_cxx_copy (const EPhoneNumber *phone_number)
+{
+ if (phone_number)
+ return new EPhoneNumber (*phone_number);
+
+ return NULL;
+}
+
+void
+_e_phone_number_cxx_free (EPhoneNumber *phone_number)
+{
+ delete phone_number;
+}
diff --git a/addressbook/libebook-contacts/e-phone-number-private.h
b/addressbook/libebook-contacts/e-phone-number-private.h
new file mode 100644
index 0000000..ec9c6fe
--- /dev/null
+++ b/addressbook/libebook-contacts/e-phone-number-private.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+/* NOTE: Keeping API documentation in this header file because gtkdoc-mkdb
+ * explicitly only scans .h and .c files, but ignores .cpp files. */
+
+/**
+ * SECTION: e-phone-utils
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Phone number support
+ *
+ * This modules provides utility functions for parsing and formatting
+ * phone numbers. Under the hood it uses Google's libphonenumber.
+ **/
+
+#if !defined (__LIBEBOOK_CONTACTS_H_INSIDE__) && !defined (LIBEBOOK_CONTACTS_COMPILATION)
+#error "Only <libebook-contacts/libebook-contacts.h> should be included directly."
+#endif
+
+#ifndef E_PHONE_NUMBER_PRIVATE_H
+#define E_PHONE_NUMBER_PRIVATE_H
+
+#include "e-phone-number.h"
+
+G_BEGIN_DECLS
+
+#if __GNUC__ >= 4
+#define E_PHONE_NUMBER_LOCAL __attribute__ ((visibility ("hidden")))
+#else
+#define E_PHONE_NUMBER_LOCAL
+#endif
+
+/* defined and used in e-phone-number.c, but also used by e-phone-number-private.cpp */
+
+E_PHONE_NUMBER_LOCAL void _e_phone_number_set_error (GError **error,
+ EPhoneNumberError code);
+
+#ifdef ENABLE_PHONENUMBER
+
+/* defined in e-phone-number-private.cpp, and used by by e-phone-number.c */
+
+E_PHONE_NUMBER_LOCAL gint _e_phone_number_cxx_get_country_code_for_region
+ (const gchar *region_code);
+E_PHONE_NUMBER_LOCAL gchar * _e_phone_number_cxx_get_default_region (void);
+
+E_PHONE_NUMBER_LOCAL EPhoneNumber * _e_phone_number_cxx_from_string (const gchar *phone_number,
+ const gchar *region_code,
+ GError **error);
+E_PHONE_NUMBER_LOCAL gchar * _e_phone_number_cxx_to_string (const EPhoneNumber
*phone_number,
+ EPhoneNumberFormat format);
+E_PHONE_NUMBER_LOCAL gint _e_phone_number_cxx_get_country_code (const EPhoneNumber
*phone_number,
+ EPhoneNumberCountrySource
*source);
+E_PHONE_NUMBER_LOCAL gchar * _e_phone_number_cxx_get_national_number (const EPhoneNumber
*phone_number);
+
+E_PHONE_NUMBER_LOCAL EPhoneNumberMatch _e_phone_number_cxx_compare (const EPhoneNumber
*first_number,
+ const EPhoneNumber
*second_number);
+E_PHONE_NUMBER_LOCAL EPhoneNumberMatch _e_phone_number_cxx_compare_strings (const gchar *first_number,
+ const gchar *second_number,
+ GError **error);
+E_PHONE_NUMBER_LOCAL EPhoneNumber * _e_phone_number_cxx_copy (const EPhoneNumber
*phone_number);
+E_PHONE_NUMBER_LOCAL void _e_phone_number_cxx_free (EPhoneNumber *phone_number);
+
+#endif /* ENABLE_PHONENUMBER */
+
+G_END_DECLS
+
+#endif /* E_PHONE_NUMBER_PRIVATE_H */
diff --git a/addressbook/libebook-contacts/e-phone-number.c b/addressbook/libebook-contacts/e-phone-number.c
new file mode 100644
index 0000000..95beef4
--- /dev/null
+++ b/addressbook/libebook-contacts/e-phone-number.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-phone-number.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-phone-number-private.h"
+
+G_DEFINE_BOXED_TYPE (
+ EPhoneNumber, e_phone_number,
+ e_phone_number_copy, e_phone_number_free)
+
+G_DEFINE_QUARK (e-phone-number-error-quark, e_phone_number_error)
+
+static const gchar *
+e_phone_number_error_to_string (EPhoneNumberError code)
+{
+ switch (code) {
+ case E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED:
+ return _("The library was built without phone number support.");
+ case E_PHONE_NUMBER_ERROR_UNKNOWN:
+ return _("The phone number parser reported an yet unkown error code.");
+ case E_PHONE_NUMBER_ERROR_NOT_A_NUMBER:
+ return _("Not a phone number");
+ case E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE:
+ return _("Invalid country calling code");
+ case E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD:
+ return _("Remaining text after the country calling code is to short for a phone number");
+ case E_PHONE_NUMBER_ERROR_TOO_SHORT:
+ return _("Text is too short for a phone number");
+ case E_PHONE_NUMBER_ERROR_TOO_LONG:
+ return _("Text is too long for a phone number");
+ }
+
+ return _("Unknown error");
+}
+
+void
+_e_phone_number_set_error (GError **error,
+ EPhoneNumberError code)
+{
+ const gchar *message = e_phone_number_error_to_string (code);
+ g_set_error_literal (error, E_PHONE_NUMBER_ERROR, code, message);
+}
+
+/**
+ * e_phone_number_is_supported:
+ *
+ * Checks if phone number support is available. It is recommended to call this
+ * function before using any of the phone-utils functions to ensure that the
+ * required functionality is available, and to pick alternative mechnisms if
+ * needed.
+ *
+ * Returns: %TRUE if phone number support is available.
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_phone_number_is_supported (void)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return TRUE;
+
+#else /* ENABLE_PHONENUMBER */
+
+ return FALSE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_get_country_code_for_region:
+ * @region_code: (allow-none): a two-letter country code, or %NULL
+ *
+ * Retrieves the preferred country calling code for @region_code,
+ * e.g. 358 for "fi".
+ *
+ * If %NULL is passed for @region_code the default region as returned by
+ * e_phone_number_get_default_region() is used.
+ *
+ * Returns: a valid country calling code, or zero if an unknown region
+ * code was passed.
+ *
+ * Since: 3.8
+ */
+gint
+e_phone_number_get_country_code_for_region (const gchar *region_code)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_get_country_code_for_region (region_code);
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return 0;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_get_default_region:
+ *
+ * Retrieves the current two-letter country code that's used by default for
+ * parsing phone numbers in e_phone_number_from_string(). It can be useful
+ * to store this number before parsing a bigger number of phone numbers.
+ *
+ * The result of this functions depends on the current setup of the
+ * %LC_ADDRESS category: If that category provides a reasonable value
+ * for %_NL_ADDRESS_COUNTRY_AB2 this value is returned. Otherwise the
+ * locale name configured for %LC_ADDRESS is parsed.
+ *
+ * Returns: (transfer full): a newly allocated string containing the
+ * current locale's two-letter code for phone number parsing.
+ *
+ * Since: 3.8
+ */
+gchar *
+e_phone_number_get_default_region (void)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_get_default_region ();
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_from_string:
+ * @phone_number: the phone number to parse
+ * @region_code: (allow-none): a two-letter country code, or %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Parses the string passed in @phone_number. Note that no validation is
+ * performed whether the recognized phone number is valid for a particular
+ * region.
+ *
+ * The two-letter country code passed in @region_code only is used if the
+ * @phone_number is not written in international format. The application's
+ * default region as returned by e_phone_number_get_default_region() is used
+ * if @region_code is %NULL.
+ *
+ * If the number is guaranteed to start with a '+' followed by the country
+ * calling code, then "ZZ" can be passed for @region_code.
+ *
+ * Returns: (transfer full): a new EPhoneNumber instance on success,
+ * or %NULL on error. Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *
+e_phone_number_from_string (const gchar *phone_number,
+ const gchar *region_code,
+ GError **error)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_from_string (phone_number, region_code, error);
+
+#else /* ENABLE_PHONENUMBER */
+
+ _e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+ return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_to_string:
+ * @phone_number: the phone number to format
+ * @format: the phone number format to apply
+ *
+ * Describes the @phone_number according to the rules applying to @format.
+ *
+ * Returns: (transfer full): A formatted string for @phone_number.
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_phone_number_to_string (const EPhoneNumber *phone_number,
+ EPhoneNumberFormat format)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_to_string (phone_number, format);
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_get_country_code:
+ * @phone_number: the phone number to query
+ * @source: an optional location for storing the phone number's origin, or %NULL
+ *
+ * Queries the @phone_number's country calling code and optionally stores the country
+ * calling code's origin in @source. For instance when parsing "+1-617-5423789" this
+ * function would return one and assing E_PHONE_NUMBER_COUNTRY_FROM_FQTN to @source.
+ *
+ * Returns: A valid country calling code, or zero if no code is known.
+ *
+ * Since: 3.8
+ **/
+gint
+e_phone_number_get_country_code (const EPhoneNumber *phone_number,
+ EPhoneNumberCountrySource *source)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_get_country_code (phone_number, source);
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return 0;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_get_national_number:
+ * @phone_number: the phone number to query
+ *
+ * Queries the national portion of @phone_number without any call-out
+ * prefixes. For instance when parsing "+1-617-5423789" this function would
+ * return the string "6175423789".
+ *
+ * Returns: (transfer full): The national portion of @phone_number.
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_phone_number_get_national_number (const EPhoneNumber *phone_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_get_national_number (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_compare:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumberMatch
+e_phone_number_compare (const EPhoneNumber *first_number,
+ const EPhoneNumber *second_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_compare (first_number, second_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+ /* NOTE: This calls for a dedicated return value, but I sense broken
+ * client code that only checks for E_PHONE_NUMBER_MATCH_NONE and then
+ * treats the "not-implemented" return value as a match */
+ g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+ return E_PHONE_NUMBER_MATCH_NONE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_compare_strings:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumberMatch
+e_phone_number_compare_strings (const gchar *first_number,
+ const gchar *second_number,
+ GError **error)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_compare_strings (first_number, second_number, error);
+
+#else /* ENABLE_PHONENUMBER */
+
+ _e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+ return E_PHONE_NUMBER_MATCH_NONE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_copy:
+ * @phone_number: the EPhoneNumber to copy
+ *
+ * Makes a copy of @phone_number.
+ *
+ * Returns: (transfer full): A newly allocated EPhoneNumber instance.
+ * Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *
+e_phone_number_copy (const EPhoneNumber *phone_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_copy (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+ /* Without phonenumber support there are no instances.
+ * Any non-NULL value is a programming error in this setup. */
+ g_warn_if_fail (phone_number == NULL);
+ return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_free:
+ * @phone_number: the EPhoneNumber to free
+ *
+ * Released the memory occupied by @phone_number.
+ *
+ * Since: 3.8
+ **/
+void
+e_phone_number_free (EPhoneNumber *phone_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ _e_phone_number_cxx_free (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+ /* Without phonenumber support there are no instances.
+ * Any non-NULL value is a programming error in this setup. */
+ g_warn_if_fail (phone_number == NULL);
+
+#endif /* ENABLE_PHONENUMBER */
+}
diff --git a/libedataserver/e-phone-utils.h b/addressbook/libebook-contacts/e-phone-number.h
similarity index 62%
rename from libedataserver/e-phone-utils.h
rename to addressbook/libebook-contacts/e-phone-number.h
index e887c4f..3c39a40 100644
--- a/libedataserver/e-phone-utils.h
+++ b/addressbook/libebook-contacts/e-phone-number.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * Copyright Copyright (C) 2012 Intel Corporation
+ * Copyright (C) 2012,2013 Intel Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU Lesser General Public
@@ -19,11 +19,8 @@
* Author: Mathias Hasselmann <mathias openismus com>
*/
-/* NOTE: Keeping API documentation in this header file because gtkdoc-mkdb
- * explicitly only scans .h and .c files, but ignores .cpp files. */
-
/**
- * SECTION: e-phone-utils
+ * SECTION: e-phone-number
* @include: libedataserver/libedataserver.h
* @short_description: Phone number support
*
@@ -31,12 +28,12 @@
* phone numbers. Under the hood it uses Google's libphonenumber.
**/
-#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
-#error "Only <libedataserver/libedataserver.h> should be included directly."
+#if !defined (__LIBEBOOK_CONTACTS_H_INSIDE__) && !defined (LIBEBOOK_CONTACTS_COMPILATION)
+#error "Only <libebook-contacts/libebook-contacts.h> should be included directly."
#endif
-#ifndef E_PHONE_UTILS_H
-#define E_PHONE_UTILS_H
+#ifndef E_PHONE_NUMBER_H
+#define E_PHONE_NUMBER_H
#include <glib-object.h>
@@ -51,11 +48,13 @@ G_BEGIN_DECLS
* @E_PHONE_NUMBER_FORMAT_INTERNATIONAL: a formatted phone number always
* starting with the country calling code: "+49 30 55667788".
* @E_PHONE_NUMBER_FORMAT_NATIONAL: a formatted phone number in national
- * scope, that is without country code: "(030) 55667788".
+ * scope, that is without country calling code: "(030) 55667788".
* @E_PHONE_NUMBER_FORMAT_RFC3966: a tel: URL according to RFC 3966:
* "tel:+49-30-55667788".
*
* The supported formatting rules for phone numbers.
+ *
+ * Since: 3.8
**/
typedef enum {
E_PHONE_NUMBER_FORMAT_E164,
@@ -68,18 +67,19 @@ typedef enum {
* EPhoneNumberMatch:
* @E_PHONE_NUMBER_MATCH_NONE: The phone numbers did not match.
* @E_PHONE_NUMBER_MATCH_EXACT: The phone numbers matched exactly.
- * @E_PHONE_NUMBER_MATCH_NATIONAL: There was no country code for at least
- * one of the numbers, but the national parts matched.
- * @E_PHONE_NUMBER_MATCH_SHORT: There was no country code for at least
- * one of the numbers, but one number might be part (suffix) of the other.
+ * @E_PHONE_NUMBER_MATCH_NATIONAL: There was no country calling code
+ * for at least one of the numbers, but the national parts matched.
+ * @E_PHONE_NUMBER_MATCH_SHORT: There was no country calling code for
+ * at least one of the numbers, but one number might be part (suffix)
+ * of the other.
*
* The quality of a phone number match.
* Let's consider the phone number "+1-221-5423789", then comparing with
* "+1.221.542.3789" we have get E_PHONE_NUMBER_MATCH_EXACT because country
* code, region code and local number are matching. Comparing with "2215423789"
- * will result in E_PHONE_NUMBER_MATCH_NATIONAL because the country code is
- * missing, but the national portion is matching. Finally comparing with
+ * will result in E_PHONE_NUMBER_MATCH_NATIONAL because the country calling code
+ * is missing, but the national portion is matching. Finally comparing with
* "5423789" gives E_PHONE_NUMBER_MATCH_SHORT. For more detail have a look at
* the following table:
*
@@ -125,7 +125,9 @@ typedef enum {
* </tr>
* </tbody>
* </informaltable>
- */
+ *
+ * Since: 3.8
+ **/
typedef enum {
E_PHONE_NUMBER_MATCH_NONE,
E_PHONE_NUMBER_MATCH_EXACT,
@@ -140,14 +142,16 @@ typedef enum {
* @E_PHONE_NUMBER_ERROR_UNKNOWN: the phone number parser reported an yet
* unkown error code.
* @E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE: the supplied phone number has an
- * invalid country code.
+ * invalid country calling code.
* @E_PHONE_NUMBER_ERROR_NOT_A_NUMBER: the supplied text is not a phone number.
* @E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD: the remaining text after the
- * country code is to short for a phone number.
+ * country calling code is to short for a phone number.
* @E_PHONE_NUMBER_ERROR_TOO_SHORT: the text is too short for a phone number.
* @E_PHONE_NUMBER_ERROR_TOO_LONG: the text is too long for a phone number.
*
* Numeric description of a phone number related error.
+ *
+ * Since: 3.8
**/
typedef enum {
E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED,
@@ -156,123 +160,68 @@ typedef enum {
E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE,
E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD,
E_PHONE_NUMBER_ERROR_TOO_SHORT,
- E_PHONE_NUMBER_ERROR_TOO_LONG,
+ E_PHONE_NUMBER_ERROR_TOO_LONG
} EPhoneNumberError;
/**
+ * EPhoneNumberCountrySource:
+ * @E_PHONE_NUMBER_COUNTRY_FROM_FQTN: the EPhoneNumber was build from a
+ * fully qualified telephone number that contained a valid country
+ * calling code
+ * @E_PHONE_NUMBER_COUNTRY_FROM_IDD: the parsed phone number started
+ * with the current locale's international call prefix, followed by a
+ * valid country calling code
+ * @E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT: the parsed phone didn't start
+ * with a (recognizable) country calling code, the code was chosen by
+ * checking the current locale settings
+ *
+ * The origin of a parsed EPhoneNumber's country calling code.
+ *
+ * Since: 3.8
+ **/
+typedef enum {
+ E_PHONE_NUMBER_COUNTRY_FROM_FQTN = 1,
+ E_PHONE_NUMBER_COUNTRY_FROM_IDD = 5,
+ E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT = 20
+} EPhoneNumberCountrySource;
+
+/**
* EPhoneNumber:
* This opaque type describes a parsed phone number. It can be copied using
* e_phone_number_copy(). To release it call e_phone_number_free().
- */
+ *
+ * Since: 3.8
+ **/
typedef struct _EPhoneNumber EPhoneNumber;
-GType e_phone_number_get_type (void) G_GNUC_CONST;
-GQuark e_phone_number_error_quark (void) G_GNUC_CONST;
+GType e_phone_number_get_type (void);
+GQuark e_phone_number_error_quark (void);
-/**
- * e_phone_number_is_supported:
- *
- * Checks if phone number support is available. It is recommended to call this
- * function before using any of the phone-utils functions to ensure that the
- * required functionality is available, and to pick alternative mechnisms if
- * needed.
- *
- * Returns: %TRUE if phone number support is available.
- */
gboolean e_phone_number_is_supported (void) G_GNUC_CONST;
+gint e_phone_number_get_country_code_for_region
+ (const gchar *region_code);
+gchar * e_phone_number_get_default_region
+ (void);
-/**
- * e_phone_number_from_string:
- * @phone_number: the phone number to parse
- * @country_code: (allow-none): a 2-letter country code, or %NULL
- * @error: (out): a #GError to set an error, if any
- *
- * Parses the string passed in @phone_number. Note that no validation is
- * performed whether the recognized phone number is valid for a particular
- * region.
- *
- * The 2-letter country code passed in @country_code only is used if the
- * @phone_number is not written in international format. The applications's
- * currently locale is consulted if %NULL gets passed for @country_code.
- * If the number is guaranteed to start with a '+' followed by the country
- * calling code, then "ZZ" can be passed here.
- *
- * Returns: (transfer full): a new EPhoneNumber instance on success,
- * or %NULL on error. Call e_phone_number_free() to release this instance.
- *
- * Since: 3.8
- **/
EPhoneNumber * e_phone_number_from_string (const gchar *phone_number,
- const gchar *country_code,
+ const gchar *region_code,
GError **error);
-
-/**
- * e_phone_number_to_string:
- * @phone_number: the phone number to format
- * @format: the phone number format to apply
- *
- * Describes the @phone_number according to the rules applying to @format.
- *
- * Returns: (transfer full): A formatted string for @phone_number.
- *
- * Since: 3.8
- **/
gchar * e_phone_number_to_string (const EPhoneNumber *phone_number,
EPhoneNumberFormat format);
+gint e_phone_number_get_country_code (const EPhoneNumber *phone_number,
+ EPhoneNumberCountrySource *source);
+gchar * e_phone_number_get_national_number
+ (const EPhoneNumber *phone_number);
-/**
- * e_phone_number_compare:
- * @first_number: the first EPhoneNumber to compare
- * @second_number: the second EPhoneNumber to compare
- *
- * Compares two phone numbers.
- *
- * Returns: The quality of matching for the two phone numbers.
- *
- * Since: 3.8
- */
EPhoneNumberMatch e_phone_number_compare (const EPhoneNumber *first_number,
const EPhoneNumber *second_number);
-
-/**
- * e_phone_number_compare_strings:
- * @first_number: the first EPhoneNumber to compare
- * @second_number: the second EPhoneNumber to compare
- * @error: (out): a #GError to set an error, if any
- *
- * Compares two phone numbers.
- *
- * Returns: The quality of matching for the two phone numbers.
- *
- * Since: 3.8
- */
EPhoneNumberMatch e_phone_number_compare_strings (const gchar *first_number,
const gchar *second_number,
GError **error);
-/**
- * e_phone_number_copy:
- * @phone_number: the EPhoneNumber to copy
- *
- * Makes a copy of @phone_number.
- *
- * Returns: (transfer full): A newly allocated EPhoneNumber instance.
- * Call e_phone_number_free() to release this instance.
- *
- * Since: 3.8
- **/
EPhoneNumber * e_phone_number_copy (const EPhoneNumber *phone_number);
-
-/**
- * e_phone_number_free:
- * @phone_number: the EPhoneNumber to free
- *
- * Released the memory occupied by @phone_number.
- *
- * Since: 3.8
- **/
void e_phone_number_free (EPhoneNumber *phone_number);
G_END_DECLS
-#endif /* E_BOOK_BACKEND_FILE_PHONE_UTILS_H */
+#endif /* E_PHONE_NUMBER_H */
diff --git a/addressbook/libebook-contacts/libebook-contacts.h
b/addressbook/libebook-contacts/libebook-contacts.h
index 92f95f3..1d02bf1 100644
--- a/addressbook/libebook-contacts/libebook-contacts.h
+++ b/addressbook/libebook-contacts/libebook-contacts.h
@@ -31,6 +31,7 @@
#include <libebook-contacts/e-vcard.h>
#include <libebook-contacts/e-book-contacts-enumtypes.h>
#include <libebook-contacts/e-source-backend-summary-setup.h>
+#include <libebook-contacts/e-phone-number.h>
#undef __LIBEBOOK_CONTACTS_H_INSIDE__
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.c
b/addressbook/libedata-book/e-book-backend-sqlitedb.c
index 2577262..6e0c74c 100644
--- a/addressbook/libedata-book/e-book-backend-sqlitedb.c
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.c
@@ -19,11 +19,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "e-book-backend-sqlitedb.h"
+
+#include <locale.h>
#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
#include <errno.h>
-#include <langinfo.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
@@ -32,7 +32,6 @@
#include <libebackend/libebackend.h>
#include "e-book-backend-sexp.h"
-#include "e-book-backend-sqlitedb.h"
#define E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -40,18 +39,33 @@
#define d(x)
-#define DB_FILENAME "contacts.db"
-#define FOLDER_VERSION 4
+#if d(1)+0
+# define LOCK_MUTEX(mutex) \
+ G_STMT_START { \
+ g_message ("%s: DB Locking ", G_STRFUNC); \
+ g_mutex_lock (mutex); \
+ g_message ("%s: DB Locked ", G_STRFUNC); \
+ } G_STMT_END
+
+
+# define UNLOCK_MUTEX(mutex) \
+ G_STMT_START { \
+ g_message ("%s: Unlocking ", G_STRFUNC); \
+ g_mutex_unlock (mutex); \
+ g_message ("%s: DB Unlocked ", G_STRFUNC); \
+ } G_STMT_END
+#else
+# define LOCK_MUTEX(mutex) g_mutex_lock (mutex)
+# define UNLOCK_MUTEX(mutex) g_mutex_unlock (mutex)
+#endif
-#define READER_LOCK(ebsdb) g_static_rw_lock_reader_lock (&ebsdb->priv->rwlock)
-#define READER_UNLOCK(ebsdb) g_static_rw_lock_reader_unlock (&ebsdb->priv->rwlock)
-#define WRITER_LOCK(ebssdb) g_static_rw_lock_writer_lock (&ebsdb->priv->rwlock)
-#define WRITER_UNLOCK(ebssdb) g_static_rw_lock_writer_unlock (&ebsdb->priv->rwlock)
+#define DB_FILENAME "contacts.db"
+#define FOLDER_VERSION 5
typedef enum {
INDEX_PREFIX = (1 << 0),
INDEX_SUFFIX = (1 << 1),
- INDEX_PHONE = (1 << 2),
+ INDEX_PHONE = (1 << 2)
} IndexFlags;
typedef struct {
@@ -66,25 +80,23 @@ struct _EBookBackendSqliteDBPrivate {
gchar *path;
gchar *hash_key;
- gboolean store_vcard;
- GStaticRWLock rwlock;
+ GMutex lock;
+ GMutex updates_lock; /* This is for deprecated e_book_backend_sqlitedb_lock_updates() */
- GMutex *in_transaction_lock;
+ gboolean store_vcard;
guint32 in_transaction;
SummaryField *summary_fields;
gint n_summary_fields;
guint have_attr_list : 1;
IndexFlags attr_list_indexes;
-
- gchar *country_code;
};
G_DEFINE_TYPE (EBookBackendSqliteDB, e_book_backend_sqlitedb, G_TYPE_OBJECT)
static GHashTable *db_connections = NULL;
-static GStaticMutex dbcon_lock = G_STATIC_MUTEX_INIT;
+static GMutex dbcon_lock;
static EContactField default_summary_fields[] = {
E_CONTACT_UID,
@@ -94,21 +106,18 @@ static EContactField default_summary_fields[] = {
E_CONTACT_FULL_NAME,
E_CONTACT_GIVEN_NAME,
E_CONTACT_FAMILY_NAME,
- E_CONTACT_EMAIL_1,
- E_CONTACT_EMAIL_2,
- E_CONTACT_EMAIL_3,
- E_CONTACT_EMAIL_4,
+ E_CONTACT_EMAIL,
E_CONTACT_IS_LIST,
E_CONTACT_LIST_SHOW_ADDRESSES,
E_CONTACT_WANTS_HTML
};
-/* Create indexes on full_name and email_1 as autocompletion queries would mainly
- * rely on this. Assuming that the frequency of matching on these would be higher than
- * on the other fields like email_2, surname etc. email_1 should be the primary email */
+/* Create indexes on full_name and email fields as autocompletion queries would mainly
+ * rely on this.
+ */
static EContactField default_indexed_fields[] = {
E_CONTACT_FULL_NAME,
- E_CONTACT_EMAIL_1
+ E_CONTACT_EMAIL
};
static EBookIndexType default_index_types[] = {
@@ -116,17 +125,18 @@ static EBookIndexType default_index_types[] = {
E_BOOK_INDEX_PREFIX
};
-static gboolean append_summary_field (GArray *array,
- EContactField field,
- gboolean *have_attr_list,
- GError **error);
+static SummaryField * append_summary_field (GArray *array,
+ EContactField field,
+ gboolean *have_attr_list,
+ GError **error);
-static gboolean validate_county_code (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- GError **error);
+static gboolean upgrade_contacts_table (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error);
static const gchar *
-summary_dbname_from_field (EBookBackendSqliteDB *ebsdb, EContactField field)
+summary_dbname_from_field (EBookBackendSqliteDB *ebsdb,
+ EContactField field)
{
gint i;
@@ -134,11 +144,13 @@ summary_dbname_from_field (EBookBackendSqliteDB *ebsdb, EContactField field)
if (ebsdb->priv->summary_fields[i].field == field)
return ebsdb->priv->summary_fields[i].dbname;
}
+
return NULL;
}
static gint
-summary_index_from_field_name (EBookBackendSqliteDB *ebsdb, const gchar *field_name)
+summary_index_from_field_name (EBookBackendSqliteDB *ebsdb,
+ const gchar *field_name)
{
gint i;
EContactField field;
@@ -153,24 +165,14 @@ summary_index_from_field_name (EBookBackendSqliteDB *ebsdb, const gchar *field_n
return -1;
}
-
typedef struct {
EBookBackendSqliteDB *ebsdb;
GSList *list;
} StoreVCardData;
-GQuark
-e_book_backend_sqlitedb_error_quark (void)
-{
- static GQuark quark = 0;
-
- if (G_UNLIKELY (quark == 0)) {
- const gchar *string = "e-book-backend-sqlitedb-error-quark";
- quark = g_quark_from_static_string (string);
- }
-
- return quark;
-}
+G_DEFINE_QUARK (
+ e-book-backend-sqlitedb-error-quark,
+ e_book_backend_sqlitedb_error)
static void
e_book_backend_sqlitedb_dispose (GObject *object)
@@ -179,7 +181,7 @@ e_book_backend_sqlitedb_dispose (GObject *object)
priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (object);
- g_static_mutex_lock (&dbcon_lock);
+ g_mutex_lock (&dbcon_lock);
if (db_connections != NULL) {
if (priv->hash_key != NULL) {
g_hash_table_remove (db_connections, priv->hash_key);
@@ -193,7 +195,7 @@ e_book_backend_sqlitedb_dispose (GObject *object)
priv->hash_key = NULL;
}
}
- g_static_mutex_unlock (&dbcon_lock);
+ g_mutex_unlock (&dbcon_lock);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->dispose (object);
@@ -206,15 +208,13 @@ e_book_backend_sqlitedb_finalize (GObject *object)
priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (object);
- g_static_rw_lock_free (&priv->rwlock);
-
sqlite3_close (priv->db);
g_free (priv->path);
g_free (priv->summary_fields);
- g_free (priv->country_code);
- g_mutex_free (priv->in_transaction_lock);
+ g_mutex_clear (&priv->lock);
+ g_mutex_clear (&priv->updates_lock);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_book_backend_sqlitedb_parent_class)->finalize (object);
@@ -238,10 +238,10 @@ e_book_backend_sqlitedb_init (EBookBackendSqliteDB *ebsdb)
ebsdb->priv = E_BOOK_BACKEND_SQLITEDB_GET_PRIVATE (ebsdb);
ebsdb->priv->store_vcard = TRUE;
- g_static_rw_lock_init (&ebsdb->priv->rwlock);
ebsdb->priv->in_transaction = 0;
- ebsdb->priv->in_transaction_lock = g_mutex_new ();
+ g_mutex_init (&ebsdb->priv->lock);
+ g_mutex_init (&ebsdb->priv->updates_lock);
}
static gint
@@ -278,18 +278,18 @@ get_bool_cb (gpointer ref,
* @data:
* @error:
*
- * Callers should hold the rw lock depending on read or write operation
+ * Callers should hold the rw lock depending on read or write operation
* Returns:
**/
static gboolean
book_backend_sql_exec_real (sqlite3 *db,
- const gchar *stmt,
- gint (*callback)(gpointer ,gint,gchar **,gchar **),
- gpointer data,
- GError **error)
+ const gchar *stmt,
+ gint (*callback)(gpointer ,gint,gchar **,gchar **),
+ gpointer data,
+ GError **error)
{
gchar *errmsg = NULL;
- gint ret = -1;
+ gint ret = -1;
ret = sqlite3_exec (db, stmt, callback, data, &errmsg);
while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
@@ -302,13 +302,12 @@ book_backend_sql_exec_real (sqlite3 *db,
}
if (ret != SQLITE_OK) {
- d(g_print ("Error in SQL EXEC statement: %s [%s] RETURN CODE: %d.\n", stmt, errmsg, ret));
-
- g_set_error (
+ d (g_printerr ("Error in SQL EXEC statement: %s [%s].\n", stmt, errmsg));
+ g_set_error_literal (
error, E_BOOK_SDB_ERROR,
ret == SQLITE_CONSTRAINT ?
E_BOOK_SDB_ERROR_CONSTRAINT : E_BOOK_SDB_ERROR_OTHER,
- "%s", errmsg);
+ errmsg);
sqlite3_free (errmsg);
errmsg = NULL;
return FALSE;
@@ -324,37 +323,57 @@ book_backend_sql_exec_real (sqlite3 *db,
static gint
print_debug_cb (gpointer ref,
- gint col,
- gchar **cols,
- gchar **name)
+ gint n_cols,
+ gchar **cols,
+ gchar **name)
{
gint i;
- g_print (" DEBUG BEGIN: %d results\n", col);
+ g_printerr (" DEBUG BEGIN:\n");
- for (i = 0; i < col; i++)
- g_print (" NAME: '%s' COL: %s\n", name[i], cols[i]);
+ for (i = 0; i < n_cols; i++)
+ g_printerr (" NAME: '%s' VALUE: %s\n", name[i], cols[i]);
- g_print (" DEBUG END\n");
+ g_printerr (" DEBUG END\n");
return 0;
}
+static gint G_GNUC_CONST
+booksql_debug (void)
+{
+ static gint booksql_debug = -1;
+
+ if (booksql_debug == -1) {
+ const gchar *const tmp = g_getenv ("BOOKSQL_DEBUG");
+ booksql_debug = (tmp != NULL ? MAX (0, atoi (tmp)) : 0);
+ }
+
+ return booksql_debug;
+}
+
static void
book_backend_sql_debug (sqlite3 *db,
- const gchar *stmt,
- gint (*callback)(gpointer ,gint,gchar **,gchar **),
- gpointer data,
- GError **error)
+ const gchar *stmt,
+ gint (*callback)(gpointer ,gint,gchar **,gchar **),
+ gpointer data,
+ GError **error)
{
- gchar *debug;
GError *local_error = NULL;
- debug = g_strconcat ("EXPLAIN QUERY PLAN ", stmt, NULL);
- g_print ("DEBUG STATEMENT: %s\n", stmt);
- book_backend_sql_exec_real (db, debug, print_debug_cb, NULL, &local_error);
- g_print ("DEBUG STATEMENT END: %s%s\n", local_error ? "Error: " : "", local_error ?
local_error->message : "Success");
- g_free (debug);
+ g_printerr ("DEBUG STATEMENT: %s\n", stmt);
+
+ if (booksql_debug () > 1) {
+ gchar *debug = g_strconcat ("EXPLAIN QUERY PLAN ", stmt, NULL);
+ book_backend_sql_exec_real (db, debug, print_debug_cb, NULL, &local_error);
+ g_free (debug);
+ }
+
+ if (local_error) {
+ g_printerr ("DEBUG STATEMENT END: Error: %s\n", local_error->message);
+ } else if (booksql_debug () > 1) {
+ g_printerr ("DEBUG STATEMENT END: Success\n");
+ }
g_clear_error (&local_error);
}
@@ -366,210 +385,233 @@ book_backend_sql_exec (sqlite3 *db,
gpointer data,
GError **error)
{
- static gint booksql_debug = -1;
-
- if (booksql_debug == -1) {
- booksql_debug = g_getenv ("BOOKSQL_DEBUG") != NULL ? 1 : 0;
- }
-
- if (booksql_debug)
+ if (booksql_debug ())
book_backend_sql_debug (db, stmt, callback, data, error);
return book_backend_sql_exec_real (db, stmt, callback, data, error);
}
-/* the first caller holds the writer lock too */
+/* This function must always be called with the priv->lock held */
static gboolean
book_backend_sqlitedb_start_transaction (EBookBackendSqliteDB *ebsdb,
GError **error)
{
- gboolean res = TRUE;
+ gboolean success = TRUE;
g_return_val_if_fail (ebsdb != NULL, FALSE);
g_return_val_if_fail (ebsdb->priv != NULL, FALSE);
g_return_val_if_fail (ebsdb->priv->db != NULL, FALSE);
- g_mutex_lock (ebsdb->priv->in_transaction_lock);
-
ebsdb->priv->in_transaction++;
- if (ebsdb->priv->in_transaction == 0) {
- g_mutex_unlock (ebsdb->priv->in_transaction_lock);
-
- g_return_val_if_fail (ebsdb->priv->in_transaction != 0, FALSE);
- return FALSE;
- }
+ g_return_val_if_fail (ebsdb->priv->in_transaction > 0, FALSE);
if (ebsdb->priv->in_transaction == 1) {
- WRITER_LOCK (ebsdb);
- res = book_backend_sql_exec (ebsdb->priv->db, "BEGIN", NULL, NULL, error);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, "BEGIN", NULL, NULL, error);
}
- g_mutex_unlock (ebsdb->priv->in_transaction_lock);
-
- return res;
+ return success;
}
-/* the last caller releases the writer lock too */
+/* This function must always be called with the priv->lock held */
static gboolean
-book_backend_sqlitedb_end_transaction (EBookBackendSqliteDB *ebsdb,
- gboolean do_commit,
- GError **error)
+book_backend_sqlitedb_commit_transaction (EBookBackendSqliteDB *ebsdb,
+ GError **error)
{
- gboolean res = TRUE;
+ gboolean success = TRUE;
g_return_val_if_fail (ebsdb != NULL, FALSE);
g_return_val_if_fail (ebsdb->priv != NULL, FALSE);
g_return_val_if_fail (ebsdb->priv->db != NULL, FALSE);
- g_mutex_lock (ebsdb->priv->in_transaction_lock);
- if (ebsdb->priv->in_transaction == 0) {
- g_mutex_unlock (ebsdb->priv->in_transaction_lock);
+ g_return_val_if_fail (ebsdb->priv->in_transaction > 0, FALSE);
- g_return_val_if_fail (ebsdb->priv->in_transaction > 0, FALSE);
- return FALSE;
+ ebsdb->priv->in_transaction--;
+
+ if (ebsdb->priv->in_transaction == 0) {
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, "COMMIT", NULL, NULL, error);
}
+ return success;
+}
+
+/* This function must always be called with the priv->lock held */
+static gboolean
+book_backend_sqlitedb_rollback_transaction (EBookBackendSqliteDB *ebsdb,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (ebsdb != NULL, FALSE);
+ g_return_val_if_fail (ebsdb->priv != NULL, FALSE);
+ g_return_val_if_fail (ebsdb->priv->db != NULL, FALSE);
+
+ g_return_val_if_fail (ebsdb->priv->in_transaction > 0, FALSE);
+
ebsdb->priv->in_transaction--;
if (ebsdb->priv->in_transaction == 0) {
- res = book_backend_sql_exec (ebsdb->priv->db, do_commit ? "COMMIT" : "ROLLBACK", NULL, NULL,
error);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, "ROLLBACK", NULL, NULL, error);
- WRITER_UNLOCK (ebsdb);
}
-
- g_mutex_unlock (ebsdb->priv->in_transaction_lock);
-
- return res;
+ return success;
}
static gint
collect_versions_cb (gpointer ref,
- gint col,
- gchar **cols,
- gchar **name)
+ gint col,
+ gchar **cols,
+ gchar **name)
{
gint *ret = ref;
- /* Just collect the first result, all folders should always have the same DB version */
+ /* Just collect the first result, all folders
+ * should always have the same DB version. */
*ret = cols [0] ? strtoul (cols [0], NULL, 10) : 0;
return 0;
}
-static void
+static gboolean
create_folders_table (EBookBackendSqliteDB *ebsdb,
+ gint *previous_schema,
GError **error)
{
- GError *err = NULL;
- gint version = 0;
+ gboolean success;
+ gint version = 0;
- /* sync_data points to syncronization data, it could be last_modified time
- * or a sequence number or some text depending on the backend.
+ /* sync_data points to syncronization data, it could be last_modified
+ * time or a sequence number or some text depending on the backend.
*
- * partial_content says whether the contents are partially downloaded for
- * auto-completion or if it has the complete content.
+ * partial_content says whether the contents are partially downloaded
+ * for auto-completion or if it has the complete content.
*
- * Have not included a bdata here since the keys table should suffice any
- * additional need that arises.
+ * Have not included a bdata here since the keys table should suffice
+ * any additional need that arises.
*/
const gchar *stmt =
"CREATE TABLE IF NOT EXISTS folders"
"( folder_id TEXT PRIMARY KEY,"
" folder_name TEXT,"
" sync_data TEXT,"
- " is_populated INTEGER,"
- " partial_content INTEGER,"
+ " is_populated INTEGER DEFAULT 0,"
+ " partial_content INTEGER DEFAULT 0,"
" version INTEGER,"
" revision TEXT,"
- " multivalues TEXT,"
- " reverse_multivalues INTEGER,"
- " countrycode VARCHAR(2) )";
+ " multivalues TEXT )";
if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
- return;
+ return FALSE;
- if (!err)
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL , &err);
+ if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
+ goto rollback;
- /* Create a child table to store key/value pairs for a folder */
- if (!err) {
- stmt = "CREATE TABLE IF NOT EXISTS keys"
- "( key TEXT PRIMARY KEY, value TEXT,"
- " folder_id TEXT REFERENCES folders)";
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- }
+ /* Create a child table to store key/value pairs for a folder. */
+ stmt = "CREATE TABLE IF NOT EXISTS keys"
+ "( key TEXT PRIMARY KEY, value TEXT,"
+ " folder_id TEXT REFERENCES folders)";
+ if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
+ goto rollback;
- if (!err) {
- stmt = "CREATE INDEX IF NOT EXISTS keysindex ON keys(folder_id)";
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- }
+ stmt = "CREATE INDEX IF NOT EXISTS keysindex ON keys(folder_id)";
+ if (!book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error))
+ goto rollback;
- /* Fetch the version, it should be the same for all folders (hence the LIMIT) */
- if (!err) {
- stmt = "SELECT version FROM folders LIMIT 1";
- book_backend_sql_exec (ebsdb->priv->db, stmt, collect_versions_cb, &version, &err);
- }
+ /* Fetch the version, it should be the
+ * same for all folders (hence the LIMIT). */
+ stmt = "SELECT version FROM folders LIMIT 1";
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, collect_versions_cb, &version, error);
+
+ if (!success)
+ goto rollback;
/* Upgrade DB to version 2, add revision column
*
- * (version = 0 indicates that it did not exist and we just created the table)
+ * (version = 0 indicates that it did not exist and we just
+ * created the table)
*/
- if (!err && version >= 1 && version < 2) {
+ if (version >= 1 && version < 2) {
stmt = "ALTER TABLE folders ADD COLUMN revision TEXT";
- book_backend_sql_exec (
- ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+
+ if (!success)
+ goto rollback;
}
/* Upgrade DB to version 3, add multivalues introspection columns
*/
- if (!err && version >= 1 && version < 3) {
+ if (version >= 1 && version < 3) {
stmt = "ALTER TABLE folders ADD COLUMN multivalues TEXT";
- book_backend_sql_exec (
- ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
- stmt = "ALTER TABLE folders ADD COLUMN reverse_multivalues INTEGER";
- book_backend_sql_exec (
- ebsdb->priv->db, stmt, NULL, NULL, &err);
+ if (!success)
+ goto rollback;
}
- /* Upgrade DB to version 4, add country-code column
+ /* Upgrade DB to version 4: Nothing to do. The country-code column it
+ * added got redundant already.
*/
- if (!err && version >= 1 && version < 4) {
- stmt = "ALTER TABLE folders ADD COLUMN countrycode VARCHAR(2)";
- book_backend_sql_exec (
- ebsdb->priv->db, stmt, NULL, NULL, &err);
+
+ /* Upgrade DB to version 5: Drop the reverse_multivalues column, but
+ * wait with converting phone summary values to new format until
+ * create_contacts_table() as we need introspection details for doing
+ * that.
+ */
+ if (version >= 3 && version < FOLDER_VERSION) {
+ stmt = "UPDATE folders SET "
+ "multivalues = REPLACE(RTRIM(REPLACE("
+ "multivalues || ':', ':', "
+ "CASE reverse_multivalues "
+ "WHEN 0 THEN ';prefix ' "
+ "ELSE ';prefix;suffix ' "
+ "END)), ' ', ':'), "
+ "reverse_multivalues = NULL";
+
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+
+ if (!success)
+ goto rollback;
}
- if (!err && version >= 1 && version < FOLDER_VERSION) {
+ /* Finish the eventual upgrade by storing the current schema version.
+ */
+ if (version >= 1 && version < FOLDER_VERSION) {
gchar *version_update_stmt =
sqlite3_mprintf ("UPDATE folders SET version = %d", FOLDER_VERSION);
- book_backend_sql_exec (
- ebsdb->priv->db, version_update_stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, version_update_stmt, NULL, NULL, error);
sqlite3_free (version_update_stmt);
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ if (!success)
+ goto rollback;
- if (err) {
- g_warning ("Error creating folders table: %s", err->message);
- g_propagate_error (error, err);
- }
+ *previous_schema = version;
+ return book_backend_sqlitedb_commit_transaction (ebsdb, error);
- return;
-}
+rollback:
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
+ *previous_schema = 0;
+ return FALSE;
+}
static gchar *
-format_multivalues (EBookBackendSqliteDB *ebsdb,
- IndexFlags *computed_multivalues)
+format_multivalues (EBookBackendSqliteDB *ebsdb)
{
gint i;
GString *string;
gboolean first = TRUE;
- IndexFlags computed = 0;
string = g_string_new (NULL);
@@ -583,60 +625,60 @@ format_multivalues (EBookBackendSqliteDB *ebsdb,
g_string_append (string, ebsdb->priv->summary_fields[i].dbname);
if ((ebsdb->priv->summary_fields[i].index & INDEX_PREFIX) != 0)
- computed |= INDEX_PREFIX;
+ g_string_append (string, ";prefix");
if ((ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0)
- computed |= INDEX_SUFFIX;
+ g_string_append (string, ";suffix");
if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0)
- computed |= INDEX_PHONE;
+ g_string_append (string, ";phone");
}
}
- if (computed_multivalues)
- *computed_multivalues = computed;
-
return g_string_free (string, FALSE);
}
-static void
+static gboolean
add_folder_into_db (EBookBackendSqliteDB *ebsdb,
const gchar *folderid,
const gchar *folder_name,
GError **error)
{
gchar *stmt;
- GError *err = NULL;
- IndexFlags computed = 0;
+ gboolean success;
gchar *multivalues;
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error))
+ return FALSE;
- multivalues = format_multivalues (ebsdb, &computed);
+ multivalues = format_multivalues (ebsdb);
stmt = sqlite3_mprintf (
- "INSERT OR IGNORE INTO folders VALUES "
- "( %Q, %Q, %Q, %d, %d, %d, %Q, %Q, %d, %Q ) ",
- folderid, folder_name, NULL, 0, 0, FOLDER_VERSION,
- NULL, multivalues, computed, NULL);
- book_backend_sql_exec (
+ "INSERT OR IGNORE INTO "
+ "folders ( folder_id, folder_name, version, multivalues ) "
+ "VALUES ( %Q, %Q, %d, %Q ) ",
+ folderid, folder_name, FOLDER_VERSION, multivalues);
+ success = book_backend_sql_exec (
ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
g_free (multivalues);
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ if (!success)
+ goto rollback;
+
+ return book_backend_sqlitedb_commit_transaction (ebsdb, error);
- if (err)
- g_propagate_error (error, err);
+rollback:
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- return;
+ return FALSE;
}
static gint
collect_columns_cb (gpointer ref,
- gint col,
- gchar **cols,
- gchar **name)
+ gint col,
+ gchar **cols,
+ gchar **name)
{
- GList **columns = (GList **)ref;
+ GList **columns = (GList **) ref;
gint i;
for (i = 0; i < col; i++) {
@@ -660,17 +702,15 @@ collect_columns_cb (gpointer ref,
static gboolean
introspect_summary (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- GError **error)
+ const gchar *folderid,
+ GError **error)
{
gboolean success;
gchar *stmt;
GList *summary_columns = NULL, *l;
GArray *summary_fields = NULL;
gchar *multivalues = NULL;
- IndexFlags computed_multivalues = 0;
- gchar **split;
- gint i;
+ gint i, j;
stmt = sqlite3_mprintf ("PRAGMA table_info (%Q);", folderid);
success = book_backend_sql_exec (
@@ -700,9 +740,9 @@ introspect_summary (EBookBackendSqliteDB *ebsdb,
}
/* First check exception fields */
- if (strcmp (col, "uid") == 0)
+ if (g_ascii_strcasecmp (col, "uid") == 0)
field = E_CONTACT_UID;
- else if (strcmp (col, "is_list") == 0)
+ else if (g_ascii_strcasecmp (col, "is_list") == 0)
field = E_CONTACT_IS_LIST;
else
field = e_contact_field_id (col);
@@ -747,61 +787,46 @@ introspect_summary (EBookBackendSqliteDB *ebsdb,
if (!success)
goto introspect_summary_finish;
- stmt = sqlite3_mprintf (
- "SELECT reverse_multivalues FROM folders WHERE folder_id = %Q", folderid);
- success = book_backend_sql_exec (
- ebsdb->priv->db, stmt, get_bool_cb, &computed_multivalues, error);
- sqlite3_free (stmt);
-
- if (!success)
- goto introspect_summary_finish;
-
if (multivalues) {
- split = g_strsplit (multivalues, ":", 0);
+ gchar **fields = g_strsplit (multivalues, ":", 0);
- for (i = 0; split[i] != NULL; i++) {
+ for (i = 0; fields[i] != NULL; i++) {
EContactField field;
+ SummaryField *iter;
+ gchar **params;
+
+ params = g_strsplit (fields[i], ";", 0);
+ field = e_contact_field_id (params[0]);
+ iter = append_summary_field (summary_fields, field, NULL, NULL);
+
+ if (iter) {
+ for (j = 1; params[j]; ++j) {
+ if (strcmp (params[j], "prefix") == 0) {
+ iter->index |= INDEX_PREFIX;
+ } else if (strcmp (params[j], "suffix") == 0) {
+ iter->index |= INDEX_SUFFIX;
+ } else if (strcmp (params[j], "phone") == 0) {
+ iter->index |= INDEX_PHONE;
+ }
+ }
+ }
- field = e_contact_field_id (split[i]);
- append_summary_field (summary_fields, field, NULL, NULL);
+ g_strfreev (params);
}
- g_strfreev (split);
- }
- /* With at least one reverse index the multi-value table has a reverse multivalue
- * column. Therefore it's safe to enable reverse lookups for every multivalue field.
- */
- computed_multivalues &= (INDEX_SUFFIX | INDEX_PHONE);
-
- if (computed_multivalues) {
- for (i = 0; i < summary_fields->len; i++) {
- SummaryField *iter = &g_array_index (summary_fields, SummaryField, i);
-
- if (iter->type == E_TYPE_CONTACT_ATTR_LIST)
- iter->index |= computed_multivalues;
- }
+ g_strfreev (fields);
}
- /* Retreive the country code used for guessing phone numbers */
- stmt = sqlite3_mprintf (
- "SELECT countrycode FROM folders WHERE folder_id = %Q", folderid);
- success = book_backend_sql_exec (
- ebsdb->priv->db, stmt, get_string_cb, &ebsdb->priv->country_code, error);
- sqlite3_free (stmt);
-
- if (!success)
- goto introspect_summary_finish;
-
introspect_summary_finish:
- g_list_free_full (summary_columns, (GDestroyNotify)g_free);
+ g_list_free_full (summary_columns, (GDestroyNotify) g_free);
g_free (multivalues);
/* Apply the introspected summary fields */
if (success) {
g_free (ebsdb->priv->summary_fields);
ebsdb->priv->n_summary_fields = summary_fields->len;
- ebsdb->priv->summary_fields = (SummaryField *)g_array_free (summary_fields, FALSE);
+ ebsdb->priv->summary_fields = (SummaryField *) g_array_free (summary_fields, FALSE);
} else if (summary_fields) {
g_array_free (summary_fields, TRUE);
}
@@ -809,21 +834,21 @@ introspect_summary (EBookBackendSqliteDB *ebsdb,
return success;
}
-
/* The column names match the fields used in book-backend-sexp */
static gboolean
create_contacts_table (EBookBackendSqliteDB *ebsdb,
const gchar *folderid,
+ gint previous_schema,
GError **error)
{
gint i;
- gboolean ret;
+ gboolean success;
gchar *stmt, *tmp;
- GError *err = NULL;
GString *string;
/* Construct the create statement from the summary fields table */
- string = g_string_new ("CREATE TABLE IF NOT EXISTS %Q ( uid TEXT PRIMARY KEY, ");
+ string = g_string_new (
+ "CREATE TABLE IF NOT EXISTS %Q ( uid TEXT PRIMARY KEY, ");
for (i = 1; i < ebsdb->priv->n_summary_fields; i++) {
if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
@@ -853,34 +878,40 @@ create_contacts_table (EBookBackendSqliteDB *ebsdb,
stmt = sqlite3_mprintf (string->str, folderid);
g_string_free (string, TRUE);
- WRITER_LOCK (ebsdb);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL , &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL , error);
+
sqlite3_free (stmt);
/* Create indexes on the summary fields configured for indexing */
- for (i = 0; !err && i < ebsdb->priv->n_summary_fields; i++) {
+ for (i = 0; success && i < ebsdb->priv->n_summary_fields; i++) {
if ((ebsdb->priv->summary_fields[i].index & INDEX_PREFIX) != 0 &&
ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
/* Derive index name from field & folder */
- tmp = g_strdup_printf ("INDEX_%s_%s",
- summary_dbname_from_field (ebsdb,
ebsdb->priv->summary_fields[i].field),
- folderid);
- stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)", tmp, folderid,
+ tmp = g_strdup_printf (
+ "INDEX_%s_%s",
+ summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field),
+ folderid);
+ stmt = sqlite3_mprintf (
+ "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)", tmp, folderid,
summary_dbname_from_field (ebsdb,
ebsdb->priv->summary_fields[i].field));
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
g_free (tmp);
}
- if ((ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0 &&
+ if (success &&
+ (ebsdb->priv->summary_fields[i].index & INDEX_SUFFIX) != 0 &&
ebsdb->priv->summary_fields[i].type != E_TYPE_CONTACT_ATTR_LIST) {
/* Derive index name from field & folder */
- tmp = g_strdup_printf ("RINDEX_%s_%s",
- summary_dbname_from_field (ebsdb,
ebsdb->priv->summary_fields[i].field),
- folderid);
- stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_reverse)", tmp,
folderid,
+ tmp = g_strdup_printf (
+ "RINDEX_%s_%s",
+ summary_dbname_from_field (ebsdb, ebsdb->priv->summary_fields[i].field),
+ folderid);
+ stmt = sqlite3_mprintf (
+ "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_reverse)", tmp, folderid,
summary_dbname_from_field (ebsdb,
ebsdb->priv->summary_fields[i].field));
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
g_free (tmp);
}
@@ -893,16 +924,16 @@ create_contacts_table (EBookBackendSqliteDB *ebsdb,
folderid);
stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (%s_phone)", tmp,
folderid,
summary_dbname_from_field (ebsdb,
ebsdb->priv->summary_fields[i].field));
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
g_free (tmp);
}
}
/* Construct the create statement from the attribute list summary table */
- if (!err && ebsdb->priv->have_attr_list) {
+ if (success && ebsdb->priv->have_attr_list) {
string = g_string_new ("CREATE TABLE IF NOT EXISTS %Q ( uid TEXT NOT NULL REFERENCES %Q(uid),
"
- "field TEXT, value TEXT");
+ "field TEXT, value TEXT");
if ((ebsdb->priv->attr_list_indexes & INDEX_SUFFIX) != 0)
g_string_append (string, ", value_reverse TEXT");
@@ -915,186 +946,316 @@ create_contacts_table (EBookBackendSqliteDB *ebsdb,
stmt = sqlite3_mprintf (string->str, tmp, folderid);
g_string_free (string, TRUE);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
/* Give the UID an index in this table, always */
stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS LISTINDEX ON %Q (uid)", tmp);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
/* Create indexes if specified */
- if (!err && (ebsdb->priv->attr_list_indexes & INDEX_PREFIX) != 0) {
+ if (success && (ebsdb->priv->attr_list_indexes & INDEX_PREFIX) != 0) {
stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS VALINDEX ON %Q (value)", tmp);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
}
- if (!err && (ebsdb->priv->attr_list_indexes & INDEX_SUFFIX) != 0) {
+ if (success && (ebsdb->priv->attr_list_indexes & INDEX_SUFFIX) != 0) {
stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS RVALINDEX ON %Q (value_reverse)",
tmp);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
}
- if (!err && (ebsdb->priv->attr_list_indexes & INDEX_PHONE) != 0) {
+ if (success && (ebsdb->priv->attr_list_indexes & INDEX_PHONE) != 0) {
stmt = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS PVALINDEX ON %Q (value_phone)",
tmp);
- ret = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
sqlite3_free (stmt);
}
+
g_free (tmp);
+
}
- WRITER_UNLOCK (ebsdb);
+ if (success)
+ success = introspect_summary (ebsdb, folderid, error);
+ if (success && previous_schema == 4)
+ success = upgrade_contacts_table (ebsdb, folderid, error);
- if (!err)
- ret = introspect_summary (ebsdb, folderid, &err);
+ return success;
+}
- if (!err)
- ret = validate_county_code (ebsdb, folderid, &err);
+typedef struct {
+ sqlite3 *db;
+ const gchar *collation;
+ const gchar *table;
+} CollationInfo;
- if (err)
- g_propagate_error (error, err);
+static gint
+create_phone_indexes_for_columns (gpointer data,
+ gint n_cols,
+ gchar **cols,
+ gchar **name)
+{
+ const gchar *column_name = cols[1];
+ CollationInfo *info = data;
- return ret;
+ if (g_str_has_suffix (column_name, "_phone")) {
+ gchar *index_name, *stmt;
+ GError *error = NULL;
+
+ index_name = g_strdup_printf (
+ "PINDEX_%s_ON_%s_WITH_%s", column_name, info->table, info->collation);
+ stmt = sqlite3_mprintf (
+ "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s COLLATE %Q)",
+ index_name, info->table, column_name, info->collation);
+
+ if (!book_backend_sql_exec (info->db, stmt, NULL, NULL, &error)) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+
+ sqlite3_free (stmt);
+ g_free (index_name);
+ }
+
+ return 0;
}
-static void
-eqphone_func (sqlite3_context *ctxt,
- int argc,
- sqlite3_value **argv,
- EPhoneNumberMatch required_match)
+static gint
+create_phone_indexes_for_tables (gpointer data,
+ gint n_cols,
+ gchar **cols,
+ gchar **name)
{
- EPhoneNumberMatch actual_match;
- GError *error;
+ CollationInfo *info = data;
+ GError *error = NULL;
+ gchar *tmp, *stmt;
- if (argc != 2) {
- sqlite3_result_error (ctxt, "Function requires exactly two arguments", -1);
- return;
+ info->table = cols[0];
+ stmt = sqlite3_mprintf ("PRAGMA table_info(%Q)", info->table);
+
+ if (!book_backend_sql_exec (
+ info->db, stmt, create_phone_indexes_for_columns, info, &error)) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_clear_error (&error);
}
- actual_match = e_phone_number_compare_strings (
- (const char *) sqlite3_value_text (argv[0]),
- (const char *) sqlite3_value_text (argv[1]),
- &error);
+ sqlite3_free (stmt);
- if (error) {
- sqlite3_result_error (ctxt, error->message, -1);
- g_error_free (error);
- return;
+ info->table = tmp = g_strconcat (info->table, "_lists", NULL);
+ stmt = sqlite3_mprintf ("PRAGMA table_info(%Q)", info->table);
+
+ if (!book_backend_sql_exec (
+ info->db, stmt, create_phone_indexes_for_columns, info, &error)) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_clear_error (&error);
}
- sqlite3_result_int
- (ctxt, actual_match >= E_PHONE_NUMBER_MATCH_EXACT
- && actual_match <= required_match);
+
+ sqlite3_free (stmt);
+ g_free (tmp);
+
+ return 0;
}
-static void
-eqphone_national_func (sqlite3_context *ctxt,
- int argc,
- sqlite3_value **argv)
+static GString *
+ixphone_str (gint country_code,
+ const gchar *const national_str,
+ gint national_len)
+{
+ GString *const str = g_string_sized_new (6 + national_len);
+ g_string_append_printf (str, "+%d|", country_code);
+ g_string_append_len (str, national_str, national_len);
+ return str;
+}
+
+static gint
+e_strcmp2n (const gchar *str1,
+ size_t len1,
+ const gchar *str2,
+ size_t len2)
+{
+ const gint cmp = memcmp (str1, str2, MIN (len1, len2));
+
+ return (cmp != 0 ? cmp :
+ len1 == len2 ? 0 :
+ len1 < len2 ? -1 : 1);
+}
+
+static gint
+ixphone_compare_for_country (gpointer data,
+ gint len1,
+ gconstpointer arg1,
+ gint len2,
+ gconstpointer arg2)
{
- eqphone_func (ctxt, argc, argv, E_PHONE_NUMBER_MATCH_NATIONAL);
+ const gchar *const str1 = arg1;
+ const gchar *const str2 = arg2;
+ const gchar *const sep1 = memchr (str1, '|', len1);
+ const gchar *const sep2 = memchr (str2, '|', len2);
+ const gint country_code = GPOINTER_TO_INT (data);
+
+ g_return_val_if_fail (sep1 != NULL, 0);
+ g_return_val_if_fail (sep2 != NULL, 0);
+
+ if ((str1 == sep1) == (str2 == sep2))
+ return e_strcmp2n (str1, len1, str2, len2);
+
+ if (str1 == sep1) {
+ GString *const tmp = ixphone_str (country_code, str1, len1);
+ const gint cmp = e_strcmp2n (tmp->str, tmp->len, str2, len2);
+ g_string_free (tmp, TRUE);
+ return cmp;
+ } else {
+ GString *const tmp = ixphone_str (country_code, str2, len2);
+ const gint cmp = e_strcmp2n (str1, len1, tmp->str, tmp->len);
+ g_string_free (tmp, TRUE);
+ return cmp;
+ }
+}
+
+static gint
+ixphone_compare_national (gpointer data,
+ gint len1,
+ gconstpointer arg1,
+ gint len2,
+ gconstpointer arg2)
+{
+ const gchar *const str1 = arg1;
+ const gchar *const str2 = arg2;
+ const gchar *sep1 = memchr (str1, '|', len1);
+ const gchar *sep2 = memchr (str2, '|', len2);
+
+ gint cmp;
+
+ g_return_val_if_fail (sep1 != NULL, 0);
+ g_return_val_if_fail (sep2 != NULL, 0);
+
+ cmp = e_strcmp2n (sep1 + 1, len1 - (sep1 + 1 - str1),
+ sep2 + 1, len2 - (sep2 + 1 - str2));
+
+ if (booksql_debug ()) {
+ gchar *const tmp1 = g_strndup (str1, len1);
+ gchar *const tmp2 = g_strndup (str2, len2);
+
+ g_printerr
+ (" DEBUG %s('%s', '%s') = %d\n",
+ G_STRFUNC, tmp1, tmp2, cmp);
+
+ g_free (tmp2);
+ g_free (tmp1);
+ }
+
+ return cmp;
}
static void
-eqphone_short_func (sqlite3_context *ctxt,
- int argc,
- sqlite3_value **argv)
+create_collation (gpointer data,
+ sqlite3 *db,
+ gint encoding,
+ const gchar *name)
{
- eqphone_func (ctxt, argc, argv, E_PHONE_NUMBER_MATCH_SHORT);
+ gint ret = SQLITE_DONE;
+ gint country_code;
+
+ g_warn_if_fail (encoding == SQLITE_UTF8);
+
+ if (1 == sscanf (name, "ixphone_%d", &country_code)) {
+ ret = sqlite3_create_collation (
+ db, name, SQLITE_UTF8, GINT_TO_POINTER (country_code),
+ ixphone_compare_for_country);
+ } else if (strcmp (name, "ixphone_nn") == 0) {
+ ret = sqlite3_create_collation (
+ db, name, SQLITE_UTF8, NULL,
+ ixphone_compare_national);
+ }
+
+ if (ret == SQLITE_OK) {
+ CollationInfo info = { db, name };
+ GError *error = NULL;
+
+ if (!book_backend_sql_exec (
+ db, "SELECT folder_id FROM folders",
+ create_phone_indexes_for_tables, &info, &error)) {
+ g_warning ("%s(%s): %s", G_STRFUNC, name, error->message);
+ g_error_free (error);
+ }
+ } else if (ret != SQLITE_DONE) {
+ g_warning ("%s(%s): %s", G_STRFUNC, name, sqlite3_errmsg (db));
+ }
}
static gboolean
book_backend_sqlitedb_load (EBookBackendSqliteDB *ebsdb,
const gchar *filename,
+ gint *previous_schema,
GError **error)
{
- EBookBackendSqliteDBPrivate *priv;
gint ret;
- priv = ebsdb->priv;
-
e_sqlite3_vfs_init ();
- ret = sqlite3_open (filename, &priv->db);
- if (ret) {
- if (!priv->db) {
+ ret = sqlite3_open (filename, &ebsdb->priv->db);
+
+ if (ret == SQLITE_OK)
+ ret = sqlite3_collation_needed (ebsdb->priv->db, ebsdb, create_collation);
+
+ if (ret != SQLITE_OK) {
+ if (!ebsdb->priv->db) {
g_set_error (
error, E_BOOK_SDB_ERROR,
E_BOOK_SDB_ERROR_OTHER,
_("Insufficient memory"));
} else {
const gchar *errmsg;
- errmsg = sqlite3_errmsg (priv->db);
- d (g_print ("Can't open database %s: %s\n", path, errmsg));
- g_set_error (
- error, E_BOOK_SDB_ERROR,
- E_BOOK_SDB_ERROR_OTHER, "%s", errmsg);
- sqlite3_close (priv->db);
+ errmsg = sqlite3_errmsg (ebsdb->priv->db);
+ d (g_printerr ("Can't open database %s: %s\n", path, errmsg));
+ g_set_error_literal (
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER, errmsg);
+ sqlite3_close (ebsdb->priv->db);
}
return FALSE;
}
- ret = sqlite3_create_function (priv->db, "eqphone_national",
- 2, SQLITE_UTF8, ebsdb,
- eqphone_national_func,
- NULL, NULL);
-
- if (ret) {
- g_set_error (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
- "%s", sqlite3_errmsg (priv->db));
- sqlite3_close (priv->db);
- return FALSE;
- }
-
- ret = sqlite3_create_function (priv->db, "eqphone_short",
- 2, SQLITE_UTF8, ebsdb,
- eqphone_short_func,
- NULL, NULL);
-
- if (ret) {
- g_set_error (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
- "%s", sqlite3_errmsg (priv->db));
- sqlite3_close (priv->db);
- return FALSE;
- }
-
- WRITER_LOCK (ebsdb);
-
- book_backend_sql_exec (priv->db, "ATTACH DATABASE ':memory:' AS mem", NULL, NULL, NULL);
- book_backend_sql_exec (priv->db, "PRAGMA foreign_keys = ON", NULL, NULL, NULL);
- book_backend_sql_exec (priv->db, "PRAGMA case_sensitive_like = ON", NULL, NULL, NULL);
- book_backend_sql_exec (priv->db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL);
-
- WRITER_UNLOCK (ebsdb);
-
- create_folders_table (ebsdb, error);
+ book_backend_sql_exec (
+ ebsdb->priv->db,
+ "ATTACH DATABASE ':memory:' AS mem",
+ NULL, NULL, NULL);
+ book_backend_sql_exec (
+ ebsdb->priv->db,
+ "PRAGMA foreign_keys = ON",
+ NULL, NULL, NULL);
+ book_backend_sql_exec (
+ ebsdb->priv->db,
+ "PRAGMA case_sensitive_like = ON",
+ NULL, NULL, NULL);
- return TRUE;
+ return create_folders_table (ebsdb, previous_schema, error);
}
static EBookBackendSqliteDB *
e_book_backend_sqlitedb_new_internal (const gchar *path,
- const gchar *emailid,
- const gchar *folderid,
- const gchar *folder_name,
- gboolean store_vcard,
- SummaryField *fields,
- gint n_fields,
- gboolean have_attr_list,
- IndexFlags attr_list_indexes,
- GError **error)
+ const gchar *emailid,
+ const gchar *folderid,
+ const gchar *folder_name,
+ gboolean store_vcard,
+ SummaryField *fields,
+ gint n_fields,
+ gboolean have_attr_list,
+ IndexFlags attr_list_indexes,
+ GError **error)
{
EBookBackendSqliteDB *ebsdb;
gchar *hash_key, *filename;
- GError *err = NULL;
+ gint previous_schema = 0;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (emailid != NULL, NULL);
g_return_val_if_fail (folderid != NULL, NULL);
g_return_val_if_fail (folder_name != NULL, NULL);
- g_static_mutex_lock (&dbcon_lock);
+ g_mutex_lock (&dbcon_lock);
hash_key = g_strdup_printf ("%s %s", emailid, path);
if (db_connections != NULL) {
@@ -1102,7 +1263,6 @@ e_book_backend_sqlitedb_new_internal (const gchar *path,
if (ebsdb) {
g_object_ref (ebsdb);
- g_static_mutex_unlock (&dbcon_lock);
g_free (hash_key);
goto exit;
}
@@ -1116,7 +1276,7 @@ e_book_backend_sqlitedb_new_internal (const gchar *path,
ebsdb->priv->attr_list_indexes = attr_list_indexes;
ebsdb->priv->store_vcard = store_vcard;
if (g_mkdir_with_parents (path, 0777) < 0) {
- g_static_mutex_unlock (&dbcon_lock);
+ g_mutex_unlock (&dbcon_lock);
g_set_error (
error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
"Can not make parent directory: errno %d", errno);
@@ -1124,9 +1284,8 @@ e_book_backend_sqlitedb_new_internal (const gchar *path,
}
filename = g_build_filename (path, DB_FILENAME, NULL);
- if (!book_backend_sqlitedb_load (ebsdb, filename, &err)) {
- g_static_mutex_unlock (&dbcon_lock);
- g_propagate_error (error, err);
+ if (!book_backend_sqlitedb_load (ebsdb, filename, &previous_schema, error)) {
+ g_mutex_unlock (&dbcon_lock);
g_object_unref (ebsdb);
g_free (filename);
return NULL;
@@ -1134,29 +1293,43 @@ e_book_backend_sqlitedb_new_internal (const gchar *path,
g_free (filename);
if (db_connections == NULL)
- db_connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ db_connections = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) NULL);
g_hash_table_insert (db_connections, hash_key, ebsdb);
ebsdb->priv->hash_key = g_strdup (hash_key);
- g_static_mutex_unlock (&dbcon_lock);
+ exit:
+ /* While the global dbcon_lock is held, hold the ebsdb specific lock and
+ * prolong the locking on that instance until the folders are all set up
+ */
+ LOCK_MUTEX (&ebsdb->priv->lock);
+ g_mutex_unlock (&dbcon_lock);
+
+ if (!add_folder_into_db (ebsdb, folderid, folder_name, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ g_object_unref (ebsdb);
+ return NULL;
+ }
-exit:
- if (!err)
- add_folder_into_db (ebsdb, folderid, folder_name, &err);
- if (!err)
- create_contacts_table (ebsdb, folderid, &err);
+ if (!create_contacts_table (ebsdb, folderid, previous_schema, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ g_object_unref (ebsdb);
+ return NULL;
+ }
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return ebsdb;
}
-static gboolean
-append_summary_field (GArray *array,
- EContactField field,
- gboolean *have_attr_list,
- GError **error)
+static SummaryField *
+append_summary_field (GArray *array,
+ EContactField field,
+ gboolean *have_attr_list,
+ GError **error)
{
const gchar *dbname = NULL;
GType type = G_TYPE_INVALID;
@@ -1164,16 +1337,17 @@ append_summary_field (GArray *array,
SummaryField new_field = { 0, };
if (field < 1 || field >= E_CONTACT_FIELD_LAST) {
- g_set_error (error, E_BOOK_SDB_ERROR,
- E_BOOK_SDB_ERROR_OTHER, _("Invalid contact field '%d' specified in summary"),
field);
- return FALSE;
+ g_set_error (
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+ _("Invalid contact field '%d' specified in summary"), field);
+ return NULL;
}
/* Avoid including the same field twice in the summary */
for (i = 0; i < array->len; i++) {
SummaryField *iter = &g_array_index (array, SummaryField, i);
if (field == iter->field)
- return TRUE;
+ return iter;
}
/* Resolve some exceptions, we store these
@@ -1187,9 +1361,6 @@ append_summary_field (GArray *array,
case E_CONTACT_IS_LIST:
dbname = "is_list";
break;
- case E_CONTACT_REV:
- dbname = "rev";
- break;
default:
dbname = e_contact_field_name (field);
break;
@@ -1200,30 +1371,32 @@ append_summary_field (GArray *array,
if (type != G_TYPE_STRING &&
type != G_TYPE_BOOLEAN &&
type != E_TYPE_CONTACT_ATTR_LIST) {
- g_set_error (error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
- _("Contact field '%s' of type '%s' specified in summary, "
- "but only boolean, string and string list field types are supported"),
- e_contact_pretty_name (field), g_type_name (type));
- return FALSE;
+ g_set_error (
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+ _("Contact field '%s' of type '%s' specified in summary, "
+ "but only boolean, string and string list field types are supported"),
+ e_contact_pretty_name (field), g_type_name (type));
+ return NULL;
}
if (type == E_TYPE_CONTACT_ATTR_LIST && have_attr_list)
*have_attr_list = TRUE;
- new_field.field = field;
+ new_field.field = field;
new_field.dbname = dbname;
new_field.type = type;
+ new_field.index = INDEX_PREFIX;
g_array_append_val (array, new_field);
- return TRUE;
+ return &g_array_index (array, SummaryField, array->len - 1);
}
static void
summary_fields_add_indexes (GArray *array,
- EContactField *indexes,
- EBookIndexType *index_types,
- gint n_indexes,
- IndexFlags *attr_list_indexes)
+ EContactField *indexes,
+ EBookIndexType *index_types,
+ gint n_indexes,
+ IndexFlags *attr_list_indexes)
{
gint i, j;
@@ -1260,7 +1433,6 @@ summary_fields_add_indexes (GArray *array,
}
}
-
/**
* e_book_backend_sqlitedb_new_full:
* @path: location where the db would be created
@@ -1268,21 +1440,19 @@ summary_fields_add_indexes (GArray *array,
* @folderid: folder id of the address-book
* @folder_name: name of the address-book
* @store_vcard: True if the vcard should be stored inside db, if FALSE only the summary fields would be
stored inside db.
- * @fields: An array of #EContactFields to be used for the summary
- * @n_fields: The number of summary fields in @fields
- * @indexed_fields: An array of #EContactFields to be indexed in the SQLite DB, this must be a subset of
@fields
- * @index_types: An array of #EBookIndexTypes indicating the type of indexes to create for @indexed_fields
- * @n_indexed_fields: The number of fields in @indexed_fields
+ * @setup: an #ESourceBackendSummarySetup describing how the summary should be setup
* @error: A location to store any error that may have occurred
*
* Like e_book_backend_sqlitedb_new(), but allows configuration of which contact fields
- * will be stored for quick reference in the summary.
+ * will be stored for quick reference in the summary. The configuration indicated by
+ * @setup will only be taken into account when initially creating the underlying table,
+ * further configurations will be ignored.
*
* The fields %E_CONTACT_UID and %E_CONTACT_REV are not optional,
* they will be stored in the summary regardless of this function's parameters
*
- * <note><para>Only #EContactFields with the type #G_TYPE_STRING or #G_TYPE_BOOLEAN
- * are currently supported.</para></note>
+ * <note><para>Only #EContactFields with the type #G_TYPE_STRING, #G_TYPE_BOOLEAN or
+ * #E_TYPE_CONTACT_ATTR_LIST are currently supported.</para></note>
*
* Returns: (transfer full): The newly created #EBookBackendSqliteDB
*
@@ -1290,34 +1460,33 @@ summary_fields_add_indexes (GArray *array,
**/
EBookBackendSqliteDB *
e_book_backend_sqlitedb_new_full (const gchar *path,
- const gchar *emailid,
- const gchar *folderid,
- const gchar *folder_name,
- gboolean store_vcard,
- ESourceBackendSummarySetup *setup,
- GError **error)
+ const gchar *emailid,
+ const gchar *folderid,
+ const gchar *folder_name,
+ gboolean store_vcard,
+ ESourceBackendSummarySetup *setup,
+ GError **error)
{
EBookBackendSqliteDB *ebsdb = NULL;
EContactField *fields;
EContactField *indexed_fields;
- EBookIndexType *index_types;
+ EBookIndexType *index_types = NULL;
gboolean have_attr_list = FALSE;
IndexFlags attr_list_indexes = 0;
gboolean had_error = FALSE;
GArray *summary_fields;
- gint n_fields, n_indexed_fields, i;
+ gint n_fields = 0, n_indexed_fields = 0, i;
fields = e_source_backend_summary_setup_get_summary_fields (setup, &n_fields);
indexed_fields = e_source_backend_summary_setup_get_indexed_fields (setup, &index_types,
&n_indexed_fields);
/* No specified summary fields indicates the default summary configuration should be used */
if (n_fields <= 0) {
-
ebsdb = e_book_backend_sqlitedb_new (path, emailid, folderid, folder_name, store_vcard,
error);
g_free (fields);
g_free (index_types);
g_free (indexed_fields);
-
+
return ebsdb;
}
@@ -1343,16 +1512,18 @@ e_book_backend_sqlitedb_new_full (const gchar *path,
}
/* Add the 'indexed' flag to the SummaryField structs */
- summary_fields_add_indexes (summary_fields, indexed_fields, index_types, n_indexed_fields,
- &attr_list_indexes);
-
- ebsdb = e_book_backend_sqlitedb_new_internal (path, emailid, folderid, folder_name,
- store_vcard,
- (SummaryField *)summary_fields->data,
- summary_fields->len,
- have_attr_list,
- attr_list_indexes,
- error);
+ summary_fields_add_indexes (
+ summary_fields, indexed_fields, index_types, n_indexed_fields,
+ &attr_list_indexes);
+
+ ebsdb = e_book_backend_sqlitedb_new_internal (
+ path, emailid, folderid, folder_name,
+ store_vcard,
+ (SummaryField *) summary_fields->data,
+ summary_fields->len,
+ have_attr_list,
+ attr_list_indexes,
+ error);
g_free (fields);
g_free (index_types);
@@ -1362,8 +1533,6 @@ e_book_backend_sqlitedb_new_full (const gchar *path,
return ebsdb;
}
-
-
/**
* e_book_backend_sqlitedb_new
* @path: location where the db would be created
@@ -1394,25 +1563,28 @@ e_book_backend_sqlitedb_new (const gchar *path,
IndexFlags attr_list_indexes = 0;
gint i;
- /* Create the default summery structs */
+ /* Create the default summary structs */
summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++)
append_summary_field (summary_fields, default_summary_fields[i], &have_attr_list, NULL);
/* Add the default index flags */
- summary_fields_add_indexes (summary_fields,
- default_indexed_fields,
- default_index_types,
- G_N_ELEMENTS (default_indexed_fields),
- &attr_list_indexes);
-
- ebsdb = e_book_backend_sqlitedb_new_internal (path, emailid, folderid, folder_name,
- store_vcard,
- (SummaryField *)summary_fields->data,
- summary_fields->len,
- have_attr_list,
- attr_list_indexes,
- error);
+ summary_fields_add_indexes (
+ summary_fields,
+ default_indexed_fields,
+ default_index_types,
+ G_N_ELEMENTS (default_indexed_fields),
+ &attr_list_indexes);
+
+ ebsdb = e_book_backend_sqlitedb_new_internal (
+ path, emailid, folderid, folder_name,
+ store_vcard,
+ (SummaryField *) summary_fields->data,
+ summary_fields->len,
+ have_attr_list,
+ attr_list_indexes,
+ error);
+
g_array_free (summary_fields, FALSE);
return ebsdb;
@@ -1422,9 +1594,17 @@ gboolean
e_book_backend_sqlitedb_lock_updates (EBookBackendSqliteDB *ebsdb,
GError **error)
{
+ gboolean success;
+
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
- return book_backend_sqlitedb_start_transaction (ebsdb, error);
+ LOCK_MUTEX (&ebsdb->priv->updates_lock);
+
+ LOCK_MUTEX (&ebsdb->priv->lock);
+ success = book_backend_sqlitedb_start_transaction (ebsdb, error);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+ return success;
}
gboolean
@@ -1432,9 +1612,19 @@ e_book_backend_sqlitedb_unlock_updates (EBookBackendSqliteDB *ebsdb,
gboolean do_commit,
GError **error)
{
+ gboolean success;
+
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
- return book_backend_sqlitedb_end_transaction (ebsdb, do_commit, error);
+ LOCK_MUTEX (&ebsdb->priv->lock);
+ success = do_commit ?
+ book_backend_sqlitedb_commit_transaction (ebsdb, error) :
+ book_backend_sqlitedb_rollback_transaction (ebsdb, error);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+ UNLOCK_MUTEX (&ebsdb->priv->updates_lock);
+
+ return success;
}
static gchar *
@@ -1447,114 +1637,94 @@ mprintf_suffix (const gchar *normal)
return stmt;
}
-static gchar *
-convert_phone (const gchar *normal,
- const gchar *country_code)
+static EPhoneNumber *
+phone_number_from_string (const gchar *normal,
+ const gchar *default_region)
{
EPhoneNumber *number = NULL;
- gchar *phone_number = NULL;
GError *error = NULL;
- if (normal)
- number = e_phone_number_from_string (normal, country_code, &error);
+ if (normal && e_phone_number_is_supported ())
+ number = e_phone_number_from_string (normal, default_region, &error);
if (error) {
- g_error ("%s:%d (%s): %s", __FILE__, __LINE__, __func__, error->message);
- g_clear_error (&error);
- }
+ /* Only barf on unusual errors */
+ if (error->domain != E_PHONE_NUMBER_ERROR
+ || error->code != E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE) {
+ g_warning ("%s: '%s': %s", G_STRLOC, normal, error->message);
+ }
- if (number) {
- phone_number = e_phone_number_to_string (number, E_PHONE_NUMBER_FORMAT_E164);
- e_phone_number_free (number);
+ g_clear_error (&error);
}
- return phone_number;
+ return number;
}
static gchar *
-mprintf_phone (const gchar *normal,
- const gchar *country_code)
+convert_phone_national (const gchar *normal,
+ const gchar *default_region)
{
- gchar *phone = convert_phone (normal, country_code);
- gchar *stmt = NULL;
+ EPhoneNumber *number = phone_number_from_string (normal, default_region);
+ gchar *indexed_phone_number = NULL;
+ gchar *national_number = NULL;
- if (phone) {
- stmt = sqlite3_mprintf ("%Q", phone);
- g_free (phone);
+ if (number) {
+ national_number = e_phone_number_get_national_number (number);
+ e_phone_number_free (number);
}
- return stmt;
-}
-
-static EVCardAttributeParam *
-find_param (EVCardAttribute *attr,
- const gchar *name)
-{
- GList *l;
-
- for (l = e_vcard_attribute_get_params (attr); l; l = l->next) {
- EVCardAttributeParam *const param = l->data;
-
- if (strcmp (e_vcard_attribute_param_get_name (param), name) == 0)
- return param;
+ if (national_number) {
+ indexed_phone_number = g_strconcat ("|", national_number, NULL);
+ g_free (national_number);
}
- return NULL;
+ return indexed_phone_number;
}
-static gboolean
-update_e164_params (EVCard *vcard,
- const gchar *country_code)
+static gchar *
+convert_phone (const gchar *normal,
+ const gchar *default_region)
{
- const GList *attr_list = e_vcard_get_attributes (vcard);
- gboolean modified = FALSE;
+ EPhoneNumber *number = phone_number_from_string (normal, default_region);
+ gchar *indexed_phone_number = NULL;
+ gchar *national_number = NULL;
+ gint country_code = 0;
- if (!e_phone_number_is_supported ())
- return FALSE;
+ if (number) {
+ EPhoneNumberCountrySource source;
- for (; attr_list; attr_list = attr_list->next) {
- EVCardAttribute *const attr = attr_list->data;
- char *normalized_number = NULL;
- char *formatted_number = NULL;
- EVCardAttributeParam *param = NULL;
+ national_number = e_phone_number_get_national_number (number);
+ country_code = e_phone_number_get_country_code (number, &source);
+ e_phone_number_free (number);
- /* Skip all attributes but phone numbers. */
- if (strcmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
- continue;
+ if (source == E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT)
+ country_code = 0;
+ }
- /* Compute normalized phone number. */
- param = find_param (attr, EVC_X_E164);
- formatted_number = e_vcard_attribute_get_value (attr);
+ if (national_number) {
+ indexed_phone_number = country_code
+ ? g_strdup_printf ("+%d|%s", country_code, national_number)
+ : g_strconcat ("|", national_number, NULL);
- if (formatted_number)
- normalized_number = convert_phone (formatted_number, country_code);
+ g_free (national_number);
+ }
- /* Update the phone number attribute. */
- if (normalized_number) {
- if (param == NULL) {
- param = e_vcard_attribute_param_new (EVC_X_E164);
- e_vcard_attribute_add_param_with_value (attr, param, normalized_number);
- modified = TRUE;
- } else {
- GList *values = e_vcard_attribute_param_get_values (param);
-
- if (values == NULL
- || g_strcmp0 (values->data, normalized_number)
- || values->next) {
- e_vcard_attribute_param_remove_values (param);
- e_vcard_attribute_param_add_value (param, normalized_number);
- modified = TRUE;
- }
- }
+ return indexed_phone_number;
+}
- g_free (normalized_number);
- } else if (param) {
- e_vcard_attribute_remove_param (attr, EVC_X_E164);
- modified = TRUE;
- }
+static gchar *
+mprintf_phone (const gchar *normal,
+ const gchar *default_region)
+{
+ gchar *phone = convert_phone (normal, default_region);
+ gchar *stmt = NULL;
+
+ if (phone) {
+ stmt = sqlite3_mprintf ("%Q", phone);
+ g_free (phone);
}
- return modified;
+ return stmt;
}
/* Add Contact (free the result with g_free() ) */
@@ -1563,19 +1733,19 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
EContact *contact,
const gchar *folderid,
gboolean store_vcard,
- gboolean replace_existing)
+ gboolean replace_existing,
+ const gchar *default_region)
{
GString *string;
- gchar *str, *vcard_str;
- gint i;
+ gchar *str, *vcard_str;
+ gint i;
- str = sqlite3_mprintf ("INSERT or %s INTO %Q VALUES (",
- replace_existing ? "REPLACE" : "FAIL", folderid);
+ str = sqlite3_mprintf ("INSERT or %s INTO %Q VALUES (",
+ replace_existing ? "REPLACE" : "FAIL", folderid);
string = g_string_new (str);
sqlite3_free (str);
for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
-
if (ebsdb->priv->summary_fields[i].type == G_TYPE_STRING) {
gchar *val;
gchar *normal;
@@ -1585,7 +1755,7 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
val = e_contact_get (contact, ebsdb->priv->summary_fields[i].field);
- /* Special exception, never normalize the UID & REV string */
+ /* Special exception, never normalize the UID or REV string */
if (ebsdb->priv->summary_fields[i].field != E_CONTACT_UID &&
ebsdb->priv->summary_fields[i].field != E_CONTACT_REV)
normal = e_util_utf8_normalize (val);
@@ -1604,15 +1774,14 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
}
if ((ebsdb->priv->summary_fields[i].index & INDEX_PHONE) != 0) {
- str = mprintf_phone (normal, ebsdb->priv->country_code);
+ str = mprintf_phone (normal, default_region);
g_string_append (string, ", ");
- g_string_append (string, str);
+ g_string_append (string, str ? str : "NULL");
sqlite3_free (str);
}
g_free (normal);
g_free (val);
-
} else if (ebsdb->priv->summary_fields[i].type == G_TYPE_BOOLEAN) {
gboolean val;
@@ -1626,13 +1795,7 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
g_warn_if_reached ();
}
- if (store_vcard) {
- update_e164_params (E_VCARD (contact), ebsdb->priv->country_code);
- vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
- } else {
- vcard_str = NULL;
- }
-
+ vcard_str = store_vcard ? e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30) : NULL;
str = sqlite3_mprintf (", %Q, %Q)", vcard_str, NULL);
g_string_append (string, str);
@@ -1713,6 +1876,7 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
EContact *contact,
const gchar *folderid,
gboolean replace_existing,
+ const gchar *default_region,
GError **error)
{
EBookBackendSqliteDBPrivate *priv;
@@ -1726,7 +1890,7 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
update_e164_attribute_params (E_VCARD (contact));
/* Update main summary table */
- stmt = insert_stmt_from_contact (ebsdb, contact, folderid, priv->store_vcard, replace_existing);
+ stmt = insert_stmt_from_contact (ebsdb, contact, folderid, priv->store_vcard, replace_existing,
default_region);
success = book_backend_sql_exec (priv->db, stmt, NULL, NULL, error);
g_free (stmt);
@@ -1750,15 +1914,18 @@ insert_contact (EBookBackendSqliteDB *ebsdb,
values = e_contact_get (contact, priv->summary_fields[i].field);
for (l = values; success && l != NULL; l = l->next) {
- gchar *value = (gchar *)l->data;
+ gchar *value = (gchar *) l->data;
gchar *normal = e_util_utf8_normalize (value);
gchar *stmt_suffix = NULL;
gchar *stmt_phone = NULL;
- if ((priv->attr_list_indexes & INDEX_SUFFIX) != 0)
+ if ((priv->attr_list_indexes & INDEX_SUFFIX) != 0
+ && (priv->summary_fields[i].index & INDEX_SUFFIX) != 0)
stmt_suffix = mprintf_suffix (normal);
- if ((priv->attr_list_indexes & INDEX_PHONE) != 0)
- stmt_phone = mprintf_phone (normal, ebsdb->priv->country_code);
+
+ if ((priv->attr_list_indexes & INDEX_PHONE) != 0
+ && (priv->summary_fields[i].index & INDEX_PHONE) != 0)
+ stmt_phone = mprintf_phone (normal, default_region);
stmt = sqlite3_mprintf ("INSERT INTO %Q (uid, field, value%s%s) "
"VALUES (%Q, %Q, %Q%s%s%s%s)",
@@ -1852,26 +2019,42 @@ e_book_backend_sqlitedb_new_contacts (EBookBackendSqliteDB *ebsdb,
GError **error)
{
GSList *l;
- GError *err = NULL;
+ gboolean success = TRUE;
+ gchar *default_region = NULL;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
g_return_val_if_fail (contacts != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
+
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
+ }
+
+ if (e_phone_number_is_supported ())
+ default_region = e_phone_number_get_default_region ();
- for (l = contacts; !err && l != NULL; l = g_slist_next (l)) {
+ for (l = contacts; success && l != NULL; l = g_slist_next (l)) {
EContact *contact = (EContact *) l->data;
- insert_contact (ebsdb, contact, folderid, replace_existing, &err);
+ success = insert_contact (
+ ebsdb, contact, folderid, replace_existing,
+ default_region, error);
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ g_free (default_region);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return err == NULL;
+ return success;
}
/**
@@ -1980,7 +2163,8 @@ generate_uid_list_for_stmt (GSList *uids)
}
static gchar *
-generate_delete_stmt (const gchar *table, GSList *uids)
+generate_delete_stmt (const gchar *table,
+ GSList *uids)
{
GString *str = g_string_new (NULL);
gchar *tmp;
@@ -1994,7 +2178,7 @@ generate_delete_stmt (const gchar *table, GSList *uids)
g_free (tmp);
g_string_append_c (str, ')');
- return g_string_free (str, FALSE);
+ return g_string_free (str, FALSE);
}
/**
@@ -2010,38 +2194,46 @@ e_book_backend_sqlitedb_remove_contacts (EBookBackendSqliteDB *ebsdb,
GSList *uids,
GError **error)
{
- GError *err = NULL;
+ gboolean success = TRUE;
gchar *stmt;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
g_return_val_if_fail (uids != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- /* Delete the auxillary contact infos first */
- if (!err && ebsdb->priv->have_attr_list) {
- gchar *lists_folder = g_strdup_printf ("%s_lists", folderid);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
+ }
- stmt = generate_delete_stmt (lists_folder, uids);
- g_free (lists_folder);
+ /* Delete the auxillary contact infos first */
+ if (success && ebsdb->priv->have_attr_list) {
+ gchar *lists_folder = g_strdup_printf ("%s_lists", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- g_free (stmt);
- }
+ stmt = generate_delete_stmt (lists_folder, uids);
+ g_free (lists_folder);
- stmt = generate_delete_stmt (folderid, uids);
- if (!err)
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
+ g_free (stmt);
+ }
- g_free (stmt);
+ if (success) {
+ stmt = generate_delete_stmt (folderid, uids);
+ success = book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, error);
+ g_free (stmt);
+ }
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
struct _contact_info {
@@ -2077,9 +2269,9 @@ e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
gboolean *partial_content,
GError **error)
{
- GError *err = NULL;
- gchar *stmt;
struct _contact_info cinfo;
+ gboolean success;
+ gchar *stmt;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
@@ -2088,20 +2280,23 @@ e_book_backend_sqlitedb_has_contact (EBookBackendSqliteDB *ebsdb,
cinfo.exists = FALSE;
cinfo.partial_content = FALSE;
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT partial_content FROM %Q WHERE uid = %Q", folderid, uid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, contact_found_cb , &cinfo, &err);
+ stmt = sqlite3_mprintf (
+ "SELECT partial_content FROM %Q WHERE uid = %Q",
+ folderid, uid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, contact_found_cb , &cinfo, error);
sqlite3_free (stmt);
- if (!err)
+ if (success)
*partial_content = cinfo.partial_content;
- else
- g_propagate_error (error, err);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return cinfo.exists;
+ /* FIXME Returning FALSE can mean either "contact not found" or
+ * "error occurred". Add a boolean (out) "exists" parameter. */
+ return success && cinfo.exists;
}
static gint
@@ -2133,7 +2328,6 @@ e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
gboolean *with_all_required_fields,
GError **error)
{
- GError *err = NULL;
EContact *contact = NULL;
gchar *vcard;
@@ -2143,12 +2337,12 @@ e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
vcard = e_book_backend_sqlitedb_get_vcard_string (
ebsdb, folderid, uid,
- fields_of_interest, with_all_required_fields, &err);
- if (!err && vcard) {
+ fields_of_interest, with_all_required_fields, error);
+
+ if (vcard != NULL) {
contact = e_contact_new_from_vcard_with_uid (vcard, uid);
g_free (vcard);
- } else
- g_propagate_error (error, err);
+ }
return contact;
}
@@ -2156,30 +2350,23 @@ e_book_backend_sqlitedb_get_contact (EBookBackendSqliteDB *ebsdb,
static gboolean
uid_rev_fields (GHashTable *fields_of_interest)
{
- gboolean uid_rev = TRUE;
GHashTableIter iter;
gpointer key, value;
- if (!fields_of_interest)
+ if (!fields_of_interest || g_hash_table_size (fields_of_interest) > 2)
return FALSE;
g_hash_table_iter_init (&iter, fields_of_interest);
while (g_hash_table_iter_next (&iter, &key, &value)) {
- const gchar *field_name = key;
- EContactField field = e_contact_field_id (field_name);
- gboolean found = FALSE;
+ const gchar *field_name = key;
+ EContactField field = e_contact_field_id (field_name);
- if (field == E_CONTACT_UID ||
- field == E_CONTACT_REV)
- found = TRUE;
-
- if (!found) {
- uid_rev = FALSE;
- break;
- }
+ if (field != E_CONTACT_UID &&
+ field != E_CONTACT_REV)
+ return FALSE;
}
- return uid_rev;
+ return TRUE;
}
/**
@@ -2247,6 +2434,8 @@ e_book_backend_sqlitedb_check_summary_fields (EBookBackendSqliteDB *ebsdb,
if (!fields_of_interest)
return FALSE;
+ LOCK_MUTEX (&ebsdb->priv->lock);
+
g_hash_table_iter_init (&iter, fields_of_interest);
while (g_hash_table_iter_next (&iter, &key, &value)) {
const gchar *field_name = key;
@@ -2258,24 +2447,26 @@ e_book_backend_sqlitedb_check_summary_fields (EBookBackendSqliteDB *ebsdb,
}
}
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
return summary_fields;
}
/* free return value with g_free */
static gchar *
summary_select_stmt (GHashTable *fields_of_interest,
- gboolean distinct)
+ gboolean distinct)
{
- GString *string;
+ GString *string;
if (distinct)
- string = g_string_new ("SELECT DISTINCT summary.uid AS uid");
+ string = g_string_new ("SELECT DISTINCT summary.uid");
else
- string = g_string_new ("SELECT summary.uid AS uid");
+ string = g_string_new ("SELECT summary.uid");
/* Add the E_CONTACT_REV field if they are both requested */
if (g_hash_table_size (fields_of_interest) == 2)
- g_string_append (string, ", summary.rev AS rev");
+ g_string_append (string, ", Rev");
return g_string_free (string, FALSE);
}
@@ -2299,13 +2490,12 @@ store_data_to_vcard (gpointer ref,
continue;
/* Only UID & REV can be used to create contacts from the summary columns */
- if (!strcmp (name[i], "uid")) {
+ if (!g_ascii_strcasecmp (name[i], "uid")) {
e_contact_set (contact, E_CONTACT_UID, cols[i]);
-
search_data->uid = g_strdup (cols[i]);
- } else if (!strcmp (name[i], "rev")) {
+ } else if (!g_ascii_strcasecmp (name[i], "Rev")) {
e_contact_set (contact, E_CONTACT_REV, cols[i]);
- } else if (!strcmp (name[i], "bdata"))
+ } else if (!g_ascii_strcasecmp (name[i], "bdata"))
search_data->bdata = g_strdup (cols[i]);
}
@@ -2355,7 +2545,7 @@ e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (folderid != NULL, NULL);
g_return_val_if_fail (uid != NULL, NULL);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
/* Try constructing contacts from only UID/REV first if that's requested */
if (uid_rev_fields (fields_of_interest)) {
@@ -2364,18 +2554,17 @@ e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
select_portion = summary_select_stmt (fields_of_interest, FALSE);
- stmt = sqlite3_mprintf ("%s FROM %Q AS summary WHERE uid = %Q",
- select_portion, folderid, uid);
- book_backend_sql_exec (ebsdb->priv->db, stmt,
- store_data_to_vcard,
- &vcards, error);
+ stmt = sqlite3_mprintf (
+ "%s FROM %Q AS summary WHERE summary.uid = %Q",
+ select_portion, folderid, uid);
+ book_backend_sql_exec (ebsdb->priv->db, stmt, store_data_to_vcard, &vcards, error);
sqlite3_free (stmt);
g_free (select_portion);
if (vcards) {
EbSdbSearchData *s_data = (EbSdbSearchData *) vcards->data;
- vcard_str = s_data->vcard;
+ vcard_str = s_data->vcard;
s_data->vcard = NULL;
e_book_backend_sqlitedb_search_data_free (s_data);
@@ -2385,9 +2574,7 @@ e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
}
local_with_all_required_fields = TRUE;
-
} else if (ebsdb->priv->store_vcard) {
-
stmt = sqlite3_mprintf (
"SELECT vcard FROM %Q WHERE uid = %Q", folderid, uid);
book_backend_sql_exec (
@@ -2397,13 +2584,13 @@ e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
local_with_all_required_fields = TRUE;
} else {
- g_set_error (error, E_BOOK_SDB_ERROR,
- E_BOOK_SDB_ERROR_OTHER,
- _("Full search_contacts are not stored in cache. vcards cannot be returned."));
+ g_set_error (
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+ _("Full search_contacts are not stored in cache. vcards cannot be returned."));
}
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
if (with_all_required_fields)
*with_all_required_fields = local_with_all_required_fields;
@@ -2416,7 +2603,6 @@ e_book_backend_sqlitedb_get_vcard_string (EBookBackendSqliteDB *ebsdb,
return vcard_str;
}
-
enum {
CHECK_IS_SUMMARY = (1 << 0),
CHECK_IS_LIST_ATTR = (1 << 1),
@@ -2424,9 +2610,9 @@ enum {
static ESExpResult *
func_check_subset (ESExp *f,
- gint argc,
- struct _ESExpTerm **argv,
- gpointer data)
+ gint argc,
+ struct _ESExpTerm **argv,
+ gpointer data)
{
ESExpResult *r, *r1;
gboolean one_non_summary_query = FALSE;
@@ -2456,10 +2642,45 @@ func_check_subset (ESExp *f,
if (one_non_summary_query)
result = 0;
- r = e_sexp_result_new (f, ESEXP_RES_INT);
- r->value.number = result;
+ r = e_sexp_result_new (f, ESEXP_RES_INT);
+ r->value.number = result;
+
+ return r;
+}
+
+static gint
+func_check_field_test (EBookBackendSqliteDB *ebsdb,
+ const gchar *query_name,
+ const gchar *query_value)
+{
+ gint i;
+ gint ret_val = 0;
+
+ if (ebsdb) {
+ for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
+ if (!g_ascii_strcasecmp (e_contact_field_name (ebsdb->priv->summary_fields[i].field),
query_name)) {
+ ret_val |= CHECK_IS_SUMMARY;
+
+ if (ebsdb->priv->summary_fields[i].type == E_TYPE_CONTACT_ATTR_LIST)
+ ret_val |= CHECK_IS_LIST_ATTR;
+
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++) {
+ if (!g_ascii_strcasecmp (e_contact_field_name (default_summary_fields[i]),
query_name)) {
+ ret_val |= CHECK_IS_SUMMARY;
+
+ if (e_contact_field_type (default_summary_fields[i]) ==
E_TYPE_CONTACT_ATTR_LIST)
+ ret_val |= CHECK_IS_LIST_ATTR;
- return r;
+ break;
+ }
+ }
+ }
+
+ return ret_val;
}
static ESExpResult *
@@ -2477,7 +2698,6 @@ func_check (struct _ESExp *f,
&& argv[1]->type == ESEXP_RES_STRING) {
const gchar *query_name = argv[0]->value.string;
const gchar *query_value = argv[1]->value.string;
- gint i;
/* Special case, when testing the special symbolic 'any field' we can
* consider it a summary query (it's similar to a 'no query'). */
@@ -2487,29 +2707,14 @@ func_check (struct _ESExp *f,
goto check_finish;
}
- if (ebsdb) {
- for (i = 0; i < ebsdb->priv->n_summary_fields; i++) {
- if (!strcmp (e_contact_field_name (ebsdb->priv->summary_fields[i].field),
query_name)) {
- ret_val |= CHECK_IS_SUMMARY;
-
- if (ebsdb->priv->summary_fields[i].type == E_TYPE_CONTACT_ATTR_LIST)
- ret_val |= CHECK_IS_LIST_ATTR;
-
- break;
- }
- }
- } else {
- for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++) {
- if (!strcmp (e_contact_field_name (default_summary_fields[i]), query_name)) {
- ret_val |= CHECK_IS_SUMMARY;
-
- if (e_contact_field_type (default_summary_fields[i]) ==
E_TYPE_CONTACT_ATTR_LIST)
- ret_val |= CHECK_IS_LIST_ATTR;
-
- break;
- }
- }
- }
+ ret_val |= func_check_field_test (ebsdb, query_name, query_value);
+ } else if (argc == 3
+ && argv[0]->type == ESEXP_RES_STRING
+ && argv[1]->type == ESEXP_RES_STRING
+ && argv[2]->type == ESEXP_RES_STRING) {
+ const gchar *query_name = argv[0]->value.string;
+ const gchar *query_value = argv[1]->value.string;
+ ret_val |= func_check_field_test (ebsdb, query_name, query_value);
}
check_finish:
@@ -2521,18 +2726,17 @@ func_check (struct _ESExp *f,
static ESExpResult *
func_check_phone (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
- EBookBackendSqliteDB *ebsdb = data;
ESExpResult *const r = func_check (f, argc, argv, data);
if (r && r->value.number) {
GError *error = NULL;
const gchar *const query_value = argv[1]->value.string;
EPhoneNumber *const number = e_phone_number_from_string (
- query_value, ebsdb->priv->country_code, &error);
+ query_value, NULL, &error);
if (number == NULL) {
if (error) {
@@ -2570,21 +2774,10 @@ static const struct {
{ "eqphone_short", func_check_phone, 0 }
};
-/**
- * e_book_backend_sqlitedb_check_summary_query:
- * @ebsdb: an #EBookBackendSqliteDB
- * @query: the query to check
- * @with_list_attrs: Return location to store whether the query touches upon list attributes
- *
- * Checks whether @query contains only checks for the summary fields
- * configured in @ebsdb
- *
- * Since: 3.8
- **/
-gboolean
-e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
- const gchar *query,
- gboolean *with_list_attrs)
+static gboolean
+e_book_backend_sqlitedb_check_summary_query_locked (EBookBackendSqliteDB *ebsdb,
+ const gchar *query,
+ gboolean *with_list_attrs)
{
ESExp *sexp;
ESExpResult *r;
@@ -2599,8 +2792,9 @@ e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
if (check_symbols[i].type == 1) {
- e_sexp_add_ifunction (sexp, 0, check_symbols[i].name,
- (ESExpIFunc *) check_symbols[i].func, ebsdb);
+ e_sexp_add_ifunction (
+ sexp, 0, check_symbols[i].name,
+ (ESExpIFunc *) check_symbols[i].func, ebsdb);
} else {
e_sexp_add_function (
sexp, 0, check_symbols[i].name,
@@ -2629,6 +2823,32 @@ e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
return retval;
}
+/**
+ * e_book_backend_sqlitedb_check_summary_query:
+ * @ebsdb: an #EBookBackendSqliteDB
+ * @query: the query to check
+ * @with_list_attrs: Return location to store whether the query touches upon list attributes
+ *
+ * Checks whether @query contains only checks for the summary fields
+ * configured in @ebsdb
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
+ const gchar *query,
+ gboolean *with_list_attrs)
+{
+ gboolean is_summary;
+
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
+
+ LOCK_MUTEX (&ebsdb->priv->lock);
+ is_summary = e_book_backend_sqlitedb_check_summary_query_locked (ebsdb, query, with_list_attrs);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+ return is_summary;
+}
/**
* e_book_backend_sqlitedb_is_summary_query:
@@ -2642,7 +2862,7 @@ e_book_backend_sqlitedb_check_summary_query (EBookBackendSqliteDB *ebsdb,
gboolean
e_book_backend_sqlitedb_is_summary_query (const gchar *query)
{
- return e_book_backend_sqlitedb_check_summary_query (NULL, query, NULL);
+ return e_book_backend_sqlitedb_check_summary_query_locked (NULL, query, NULL);
}
static ESExpResult *
@@ -2664,7 +2884,7 @@ func_and (ESExp *f,
continue;
}
if (r1->value.string && *r1->value.string)
- g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc -
1)) ? " AND ":"");
+ g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc -
1)) ? " AND ":"");
e_sexp_result_free (f, r1);
}
g_string_append (string, " )");
@@ -2699,7 +2919,7 @@ func_or (ESExp *f,
continue;
}
if (r1->value.string && *r1->value.string)
- g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc -
1)) ? " OR ":"");
+ g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc -
1)) ? " OR ":"");
e_sexp_result_free (f, r1);
}
g_string_append (string, " )");
@@ -2729,7 +2949,7 @@ typedef enum {
CONVERT_NOTHING = 0,
CONVERT_NORMALIZE = (1 << 0),
CONVERT_REVERSE = (1 << 1),
- CONVERT_PHONE = (1 << 2),
+ CONVERT_PHONE = (1 << 2)
} ConvertFlags;
static gchar *
@@ -2758,9 +2978,9 @@ extract_digits (const gchar *normal)
static gchar *
convert_string_value (EBookBackendSqliteDB *ebsdb,
- const gchar *value,
- ConvertFlags flags,
- MatchType match)
+ const gchar *value,
+ ConvertFlags flags,
+ MatchType match)
{
GString *str;
size_t len;
@@ -2789,7 +3009,6 @@ convert_string_value (EBookBackendSqliteDB *ebsdb,
switch (match) {
case MATCH_CONTAINS:
case MATCH_ENDS_WITH:
- case MATCH_NATIONAL_PHONE_NUMBER:
case MATCH_SHORT_PHONE_NUMBER:
g_string_append_c (str, '%');
break;
@@ -2797,6 +3016,7 @@ convert_string_value (EBookBackendSqliteDB *ebsdb,
case MATCH_BEGINS_WITH:
case MATCH_IS:
case MATCH_PHONE_NUMBER:
+ case MATCH_NATIONAL_PHONE_NUMBER:
break;
}
@@ -2804,7 +3024,12 @@ convert_string_value (EBookBackendSqliteDB *ebsdb,
computed = g_utf8_strreverse (normal, -1);
ptr = computed;
} else if (flags & CONVERT_PHONE) {
- computed = convert_phone (normal, ebsdb->priv->country_code);
+ if (match == MATCH_NATIONAL_PHONE_NUMBER) {
+ computed = convert_phone_national (normal, NULL);
+ } else {
+ computed = convert_phone (normal, NULL);
+ }
+
ptr = computed;
} else {
ptr = normal;
@@ -2901,56 +3126,61 @@ field_name_and_query_term (EBookBackendSqliteDB *ebsdb,
field_name = g_strdup ("multi.value_reverse");
list_attr = TRUE;
} else
- field_name = g_strconcat ("summary.",
- ebsdb->priv->summary_fields[summary_index].dbname,
- "_reverse", NULL);
+ field_name = g_strconcat (
+ "summary.",
+ ebsdb->priv->summary_fields[summary_index].dbname,
+ "_reverse", NULL);
if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
- value = convert_string_value (ebsdb, query_term_input, CONVERT_REVERSE,
- (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH
: MATCH_IS);
+ value = convert_string_value (
+ ebsdb, query_term_input, CONVERT_REVERSE,
+ (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
else
- value = convert_string_value (ebsdb, query_term_input, CONVERT_REVERSE |
CONVERT_NORMALIZE,
- (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH
: MATCH_IS);
+ value = convert_string_value (
+ ebsdb, query_term_input, CONVERT_REVERSE | CONVERT_NORMALIZE,
+ (match == MATCH_ENDS_WITH) ? MATCH_BEGINS_WITH : MATCH_IS);
} else if (phone_search) {
/* Special case for E.164 matching:
* o Normalize the string
* o Check the E.164 column instead
*/
+ const gint country_code = e_phone_number_get_country_code_for_region (NULL);
+
if (ebsdb->priv->summary_fields[summary_index].type == E_TYPE_CONTACT_ATTR_LIST) {
field_name = g_strdup ("multi.value_phone");
list_attr = TRUE;
- } else
- field_name = g_strconcat ("summary.",
- ebsdb->priv->summary_fields[summary_index].dbname,
- "_phone", NULL);
+ } else {
+ field_name = g_strdup_printf ("summary.%s_phone",
+ ebsdb->priv->summary_fields[summary_index].dbname);
+ }
if (match == MATCH_PHONE_NUMBER) {
- value = convert_string_value (ebsdb, query_term_input,
- CONVERT_NORMALIZE | CONVERT_PHONE, match);
- } else {
- gchar *digits = extract_digits (query_term_input);
+ extra = g_strdup_printf (" COLLATE ixphone_%d", country_code);
- value = convert_string_value (ebsdb, digits, CONVERT_NOTHING,
MATCH_ENDS_WITH);
+ value = convert_string_value (
+ ebsdb, query_term_input,
+ CONVERT_NORMALIZE | CONVERT_PHONE, match);
+ } else {
+ gchar *const digits = extract_digits (query_term_input);
+ extra = g_strdup (" COLLATE ixphone_nn");
if (match == MATCH_NATIONAL_PHONE_NUMBER) {
- extra = sqlite3_mprintf (" AND eqphone_national(%q, %Q)", field_name,
digits);
- } else if (match == MATCH_SHORT_PHONE_NUMBER) {
- extra = sqlite3_mprintf (" AND eqphone_short(%q, %Q)", field_name,
digits);
+ value = convert_string_value (ebsdb, digits, CONVERT_PHONE,
MATCH_NATIONAL_PHONE_NUMBER);
} else {
- g_assert_not_reached ();
+ value = convert_string_value (ebsdb, digits, CONVERT_NOTHING,
MATCH_ENDS_WITH);
}
g_free (digits);
}
} else {
-
if (ebsdb->priv->summary_fields[summary_index].type == E_TYPE_CONTACT_ATTR_LIST) {
field_name = g_strdup ("multi.value");
list_attr = TRUE;
} else
- field_name = g_strconcat ("summary.",
- ebsdb->priv->summary_fields[summary_index].dbname,
NULL);
+ field_name = g_strconcat (
+ "summary.",
+ ebsdb->priv->summary_fields[summary_index].dbname, NULL);
if (ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_UID ||
ebsdb->priv->summary_fields[summary_index].field == E_CONTACT_REV)
@@ -2982,12 +3212,12 @@ field_oper (MatchType match)
switch (match) {
case MATCH_IS:
case MATCH_PHONE_NUMBER:
+ case MATCH_NATIONAL_PHONE_NUMBER:
return "=";
case MATCH_CONTAINS:
case MATCH_BEGINS_WITH:
case MATCH_ENDS_WITH:
- case MATCH_NATIONAL_PHONE_NUMBER:
case MATCH_SHORT_PHONE_NUMBER:
break;
}
@@ -3002,7 +3232,7 @@ convert_match_exp (struct _ESExp *f,
gpointer data,
MatchType match)
{
- BuildQueryData *qdata = (BuildQueryData *)data;
+ BuildQueryData *qdata = (BuildQueryData *) data;
EBookBackendSqliteDB *ebsdb = qdata->ebsdb;
ESExpResult *r;
gchar *str = NULL;
@@ -3018,49 +3248,51 @@ convert_match_exp (struct _ESExp *f,
const gchar *const oper = field_oper (match);
gchar *field_name, *query_term, *extra_term;
- if (!strcmp (field, "full_name")) {
+ if (!g_ascii_strcasecmp (field, "full_name")) {
GString *names = g_string_new (NULL);
- field_name = field_name_and_query_term (ebsdb, qdata->folderid, "full_name",
- argv[1]->value.string,
- match, NULL, &query_term, NULL);
- g_string_append_printf (names, "(%s IS NOT NULL AND %s %s %s)",
- field_name, field_name, oper, query_term);
+ field_name = field_name_and_query_term (
+ ebsdb, qdata->folderid, "full_name",
+ argv[1]->value.string,
+ match, NULL, &query_term, NULL);
+ g_string_append_printf (
+ names, "(%s IS NOT NULL AND %s %s %s)",
+ field_name, field_name, oper, query_term);
g_free (field_name);
g_free (query_term);
if (summary_dbname_from_field (ebsdb, E_CONTACT_FAMILY_NAME)) {
-
- field_name = field_name_and_query_term (ebsdb, qdata->folderid,
"family_name",
- argv[1]->value.string,
- match, NULL, &query_term,
NULL);
- g_string_append_printf
- (names, " OR (%s IS NOT NULL AND %s %s %s)",
- field_name, field_name, oper, query_term);
+ field_name = field_name_and_query_term (
+ ebsdb, qdata->folderid, "family_name",
+ argv[1]->value.string,
+ match, NULL, &query_term, NULL);
+ g_string_append_printf (
+ names, " OR (%s IS NOT NULL AND %s %s %s)",
+ field_name, field_name, oper, query_term);
g_free (field_name);
g_free (query_term);
}
if (summary_dbname_from_field (ebsdb, E_CONTACT_GIVEN_NAME)) {
-
- field_name = field_name_and_query_term (ebsdb, qdata->folderid,
"given_name",
- argv[1]->value.string,
- match, NULL, &query_term,
NULL);
- g_string_append_printf
- (names, " OR (%s IS NOT NULL AND %s %s %s)",
- field_name, field_name, oper, query_term);
+ field_name = field_name_and_query_term (
+ ebsdb, qdata->folderid, "given_name",
+ argv[1]->value.string,
+ match, NULL, &query_term, NULL);
+ g_string_append_printf (
+ names, " OR (%s IS NOT NULL AND %s %s %s)",
+ field_name, field_name, oper, query_term);
g_free (field_name);
g_free (query_term);
}
if (summary_dbname_from_field (ebsdb, E_CONTACT_NICKNAME)) {
-
- field_name = field_name_and_query_term (ebsdb, qdata->folderid,
"nickname",
- argv[1]->value.string,
- match, NULL, &query_term,
NULL);
- g_string_append_printf
- (names, " OR (%s IS NOT NULL AND %s %s %s)",
- field_name, field_name, oper, query_term);
+ field_name = field_name_and_query_term (
+ ebsdb, qdata->folderid, "nickname",
+ argv[1]->value.string,
+ match, NULL, &query_term, NULL);
+ g_string_append_printf (
+ names, " OR (%s IS NOT NULL AND %s %s %s)",
+ field_name, field_name, oper, query_term);
g_free (field_name);
g_free (query_term);
}
@@ -3069,12 +3301,19 @@ convert_match_exp (struct _ESExp *f,
g_string_free (names, FALSE);
} else {
+ const gchar *const query_locale =
+ argc > 2 && argv[2]->type == ESEXP_RES_STRING ?
+ argv[2]->value.string : NULL;
+ const gchar *const saved_locale = query_locale ?
+ setlocale (LC_ADDRESS, query_locale) : NULL;
+
gboolean is_list = FALSE;
/* This should ideally be the only valid case from all the above special
casing, but oh well... */
- field_name = field_name_and_query_term (ebsdb, qdata->folderid, field,
- argv[1]->value.string,
- match, &is_list, &query_term,
&extra_term);
+ field_name = field_name_and_query_term (
+ ebsdb, qdata->folderid, field,
+ argv[1]->value.string,
+ match, &is_list, &query_term, &extra_term);
/* User functions like eqphone_national() cannot utilize indexes. Therefore we
* should reduce the result set first before applying any user functions. This
@@ -3084,17 +3323,22 @@ convert_match_exp (struct _ESExp *f,
gchar *tmp;
tmp = sqlite3_mprintf ("summary.uid = multi.uid AND multi.field =
%Q", field);
- str = g_strdup_printf ("(%s AND %s %s %s%s)",
- tmp, field_name, oper, query_term,
- extra_term ? extra_term : "");
+ str = g_strdup_printf (
+ "(%s AND (%s %s %s%s))",
+ tmp, field_name, oper, query_term,
+ extra_term ? extra_term : "");
sqlite3_free (tmp);
} else
- str = g_strdup_printf ("(%s IS NOT NULL AND %s %s %s%s)",
- field_name, field_name, oper, query_term,
- extra_term ? extra_term : "");
+ str = g_strdup_printf (
+ "(%s IS NOT NULL AND (%s %s %s%s))",
+ field_name, field_name, oper, query_term,
+ extra_term ? extra_term : "");
g_free (field_name);
g_free (query_term);
+
+ if (saved_locale)
+ setlocale (LC_ADDRESS, saved_locale);
}
}
}
@@ -3143,27 +3387,27 @@ func_endswith (struct _ESExp *f,
static ESExpResult *
func_eqphone (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
return convert_match_exp (f, argc, argv, data, MATCH_PHONE_NUMBER);
}
static ESExpResult *
func_eqphone_national (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
return convert_match_exp (f, argc, argv, data, MATCH_NATIONAL_PHONE_NUMBER);
}
static ESExpResult *
func_eqphone_short (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
return convert_match_exp (f, argc, argv, data, MATCH_SHORT_PHONE_NUMBER);
}
@@ -3188,8 +3432,8 @@ static struct {
static gchar *
sexp_to_sql_query (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- const gchar *query)
+ const gchar *folderid,
+ const gchar *query)
{
BuildQueryData data = { ebsdb, folderid };
ESExp *sexp;
@@ -3201,8 +3445,9 @@ sexp_to_sql_query (EBookBackendSqliteDB *ebsdb,
for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
if (symbols[i].immediate)
- e_sexp_add_ifunction (sexp, 0, symbols[i].name,
- (ESExpIFunc *) symbols[i].func, &data);
+ e_sexp_add_ifunction (
+ sexp, 0, symbols[i].name,
+ (ESExpIFunc *) symbols[i].func, &data);
else
e_sexp_add_function (
sexp, 0, symbols[i].name,
@@ -3270,50 +3515,50 @@ addto_slist_cb (gpointer ref,
static GSList *
book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
- const gchar *sql,
- const gchar *folderid,
- /* const */ GHashTable *fields_of_interest,
- gboolean *with_all_required_fields,
- gboolean query_with_list_attrs,
- GError **error)
+ const gchar *sql,
+ const gchar *folderid,
+ GHashTable *fields_of_interest,
+ gboolean *with_all_required_fields,
+ gboolean query_with_list_attrs,
+ GError **error)
{
- GError *err = NULL;
GSList *vcard_data = NULL;
gchar *stmt;
gboolean local_with_all_required_fields = FALSE;
-
- READER_LOCK (ebsdb);
+ gboolean success = TRUE;
/* Try constructing contacts from only UID/REV first if that's requested */
if (uid_rev_fields (fields_of_interest)) {
gchar *select_portion;
- select_portion = summary_select_stmt (fields_of_interest,
- query_with_list_attrs);
+ select_portion = summary_select_stmt (
+ fields_of_interest, query_with_list_attrs);
if (sql && sql[0]) {
if (query_with_list_attrs) {
gchar *list_table = g_strconcat (folderid, "_lists", NULL);
- stmt = sqlite3_mprintf ("%s FROM %Q AS summary, %Q AS multi WHERE %s",
- select_portion, folderid, list_table, sql);
+ stmt = sqlite3_mprintf (
+ "%s FROM %Q AS summary, %Q AS multi WHERE %s",
+ select_portion, folderid, list_table, sql);
g_free (list_table);
} else {
- stmt = sqlite3_mprintf ("%s FROM %Q AS summary WHERE %s",
- select_portion, folderid, sql);
+ stmt = sqlite3_mprintf (
+ "%s FROM %Q AS summary WHERE %s",
+ select_portion, folderid, sql);
}
- book_backend_sql_exec (
- ebsdb->priv->db, stmt,
- store_data_to_vcard, &vcard_data, &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt,
+ store_data_to_vcard, &vcard_data, error);
sqlite3_free (stmt);
} else {
stmt = sqlite3_mprintf ("%s FROM %Q AS summary", select_portion, folderid);
- book_backend_sql_exec (
+ success = book_backend_sql_exec (
ebsdb->priv->db, stmt,
- store_data_to_vcard, &vcard_data, &err);
+ store_data_to_vcard, &vcard_data, error);
sqlite3_free (stmt);
}
@@ -3327,40 +3572,46 @@ book_backend_sqlitedb_search_query (EBookBackendSqliteDB *ebsdb,
if (query_with_list_attrs) {
gchar *list_table = g_strconcat (folderid, "_lists", NULL);
- stmt = sqlite3_mprintf ("SELECT DISTINCT summary.uid, vcard, bdata "
- "FROM %Q AS summary, %Q AS multi WHERE %s",
- folderid, list_table, sql);
+ stmt = sqlite3_mprintf (
+ "SELECT DISTINCT summary.uid, vcard, bdata "
+ "FROM %Q AS summary, %Q AS multi WHERE %s",
+ folderid, list_table, sql);
g_free (list_table);
} else {
- stmt = sqlite3_mprintf ("SELECT uid, vcard, bdata FROM %Q as summary WHERE
%s", folderid, sql);
+ stmt = sqlite3_mprintf (
+ "SELECT uid, vcard, bdata FROM %Q as summary WHERE %s", folderid,
sql);
}
- book_backend_sql_exec (ebsdb->priv->db, stmt, addto_vcard_list_cb , &vcard_data,
&err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt,
+ addto_vcard_list_cb , &vcard_data, error);
+
sqlite3_free (stmt);
} else {
- stmt = sqlite3_mprintf ("SELECT uid, vcard, bdata FROM %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, addto_vcard_list_cb , &vcard_data,
&err);
+ stmt = sqlite3_mprintf (
+ "SELECT uid, vcard, bdata FROM %Q", folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt,
+ addto_vcard_list_cb , &vcard_data, error);
sqlite3_free (stmt);
}
+
local_with_all_required_fields = TRUE;
} else {
- g_set_error (error, E_BOOK_SDB_ERROR,
- E_BOOK_SDB_ERROR_OTHER,
- _("Full search_contacts are not stored in cache. vcards cannot be returned."));
+ g_set_error (
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+ _("Full search_contacts are not stored in cache. vcards cannot be returned."));
}
- READER_UNLOCK (ebsdb);
-
- if (vcard_data)
- vcard_data = g_slist_reverse (vcard_data);
-
- if (err)
- g_propagate_error (error, err);
+ if (!success) {
+ g_warn_if_fail (vcard_data == NULL);
+ return NULL;
+ }
if (with_all_required_fields)
- * with_all_required_fields = local_with_all_required_fields;
+ *with_all_required_fields = local_with_all_required_fields;
- return vcard_data;
+ return g_slist_reverse (vcard_data);
}
static GSList *
@@ -3370,39 +3621,39 @@ book_backend_sqlitedb_search_full (EBookBackendSqliteDB *ebsdb,
gboolean return_uids,
GError **error)
{
- GError *err = NULL;
GSList *r_list = NULL, *all = NULL, *l;
EBookBackendSExp *bsexp = NULL;
+ gboolean success;
gchar *stmt;
- READER_LOCK (ebsdb);
-
stmt = sqlite3_mprintf ("SELECT uid, vcard, bdata FROM %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, addto_vcard_list_cb , &all, &err);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, addto_vcard_list_cb , &all, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ if (!success) {
+ g_warn_if_fail (all == NULL);
+ return NULL;
+ }
- if (!err) {
- bsexp = e_book_backend_sexp_new (sexp);
+ bsexp = e_book_backend_sexp_new (sexp);
- for (l = all; l != NULL; l = g_slist_next (l)) {
- EbSdbSearchData *s_data = (EbSdbSearchData *) l->data;
+ for (l = all; l != NULL; l = g_slist_next (l)) {
+ EbSdbSearchData *s_data = (EbSdbSearchData *) l->data;
- if (e_book_backend_sexp_match_vcard (bsexp, s_data->vcard)) {
- if (!return_uids)
- r_list = g_slist_prepend (r_list, s_data);
- else {
- r_list = g_slist_prepend (r_list, g_strdup (s_data->uid));
- e_book_backend_sqlitedb_search_data_free (s_data);
- }
- } else
+ if (e_book_backend_sexp_match_vcard (bsexp, s_data->vcard)) {
+ if (!return_uids)
+ r_list = g_slist_prepend (r_list, s_data);
+ else {
+ r_list = g_slist_prepend (r_list, g_strdup (s_data->uid));
e_book_backend_sqlitedb_search_data_free (s_data);
- }
-
- g_object_unref (bsexp);
+ }
+ } else
+ e_book_backend_sqlitedb_search_data_free (s_data);
}
+ g_object_unref (bsexp);
+
g_slist_free (all);
return r_list;
@@ -3412,25 +3663,30 @@ book_backend_sqlitedb_search_full (EBookBackendSqliteDB *ebsdb,
* e_book_backend_sqlitedb_search
* @ebsdb:
* @folderid:
- * @sexp: search expression; use NULL or an empty string to get all stored contacts.
- * @fields_of_interest: a #GHashTable containing the names of fields to return, or NULL for all.
- * At the moment if this is non-null, the vcard will be populated with summary fields, else it would return
the
- * whole vcard if its stored in the db. [not implemented fully]
- * @searched: (allow none) (out): Whether @ebsdb was capable of searching for the provided query @sexp.
- * @with_all_required_fields: (allow none) (out): Whether all the required fields are present in the
returned vcards.
+ * @sexp: search expression; use NULL or an empty string to get all stored
+ * contacts.
+ * @fields_of_interest: a #GHashTable containing the names of fields to return,
+ * or NULL for all. At the moment if this is non-null, the vcard will be
+ * populated with summary fields, else it would return the whole vcard if
+ * its stored in the db. [not implemented fully]
+ * @searched: (allow none) (out): Whether @ebsdb was capable of searching
+ * for the provided query @sexp.
+ * @with_all_required_fields: (allow none) (out): Whether all the required
+ * fields are present in the returned vcards.
* @error:
*
- * Searching with summary fields is always supported. Search expressions containing
- * any other field is supported only if backend chooses to store the vcard inside the db.
+ * Searching with summary fields is always supported. Search expressions
+ * containing any other field is supported only if backend chooses to store
+ * the vcard inside the db.
*
- * Summary fields - uid, rev, nickname, given_name, family_name, file_as email_1, email_2, email_3, email_4,
is_list,
- * list_show_addresses, wants_html
+ * Summary fields - uid, rev, nickname, given_name, family_name, file_as
+ * email_1, email_2, email_3, email_4, is_list, list_show_addresses, wants_html
*
* If @ebsdb was incapable of returning vcards with results that satisfy
- * @fields_of_interest, then @with_all_required_fields will be updated to @FALSE
- * and only uid fields will be present in the returned vcards. This can be useful
- * when a summary query succeeds and the returned list can be used to iterate
- * and fetch for full required data from another persistance.
+ * @fields_of_interest, then @with_all_required_fields will be updated to
+ * @FALSE and only uid fields will be present in the returned vcards. This
+ * can be useful when a summary query succeeds and the returned list can be
+ * used to iterate and fetch for full required data from another persistance.
*
* Returns: List of EbSdbSearchData.
*
@@ -3440,7 +3696,7 @@ GSList *
e_book_backend_sqlitedb_search (EBookBackendSqliteDB *ebsdb,
const gchar *folderid,
const gchar *sexp,
- /* const */ GHashTable *fields_of_interest,
+ GHashTable *fields_of_interest,
gboolean *searched,
gboolean *with_all_required_fields,
GError **error)
@@ -3456,8 +3712,10 @@ e_book_backend_sqlitedb_search (EBookBackendSqliteDB *ebsdb,
if (sexp && !*sexp)
sexp = NULL;
- if (!sexp || e_book_backend_sqlitedb_check_summary_query (ebsdb, sexp,
- &query_with_list_attrs)) {
+ LOCK_MUTEX (&ebsdb->priv->lock);
+
+ if (!sexp || e_book_backend_sqlitedb_check_summary_query_locked (ebsdb, sexp,
+ &query_with_list_attrs)) {
gchar *sql_query;
sql_query = sexp ? sexp_to_sql_query (ebsdb, folderid, sexp) : NULL;
@@ -3471,16 +3729,21 @@ e_book_backend_sqlitedb_search (EBookBackendSqliteDB *ebsdb,
local_searched = TRUE;
} else if (ebsdb->priv->store_vcard) {
- search_contacts = book_backend_sqlitedb_search_full (ebsdb, sexp, folderid, FALSE, error);
+ search_contacts = book_backend_sqlitedb_search_full (
+ ebsdb, sexp, folderid, FALSE, error);
+
local_searched = TRUE;
local_with_all_required_fields = TRUE;
+
} else {
g_set_error (
error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
_("Full search_contacts are not stored in cache. "
- "Hence only summary query is supported."));
+ "Hence only summary query is supported."));
}
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
if (searched)
*searched = local_searched;
if (with_all_required_fields)
@@ -3513,23 +3776,26 @@ e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
if (sexp && !*sexp)
sexp = NULL;
- if (!sexp || e_book_backend_sqlitedb_check_summary_query (ebsdb, sexp, &query_with_list_attrs)) {
+ LOCK_MUTEX (&ebsdb->priv->lock);
+
+ if (!sexp || e_book_backend_sqlitedb_check_summary_query_locked (ebsdb, sexp,
&query_with_list_attrs)) {
gchar *stmt;
gchar *sql_query = sexp ? sexp_to_sql_query (ebsdb, folderid, sexp) : NULL;
- READER_LOCK (ebsdb);
-
if (sql_query && sql_query[0]) {
if (query_with_list_attrs) {
gchar *list_table = g_strconcat (folderid, "_lists", NULL);
- stmt = sqlite3_mprintf ("SELECT DISTINCT summary.uid FROM %Q AS summary, %Q
AS multi WHERE %s",
- folderid, list_table, sql_query);
+ stmt = sqlite3_mprintf (
+ "SELECT DISTINCT summary.uid FROM %Q AS summary, %Q AS multi WHERE
%s",
+ folderid, list_table, sql_query);
g_free (list_table);
} else
- stmt = sqlite3_mprintf ("SELECT summary.uid FROM %Q AS summary WHERE %s",
folderid, sql_query);
+ stmt = sqlite3_mprintf (
+ "SELECT summary.uid FROM %Q AS summary WHERE %s",
+ folderid, sql_query);
book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
sqlite3_free (stmt);
@@ -3540,22 +3806,25 @@ e_book_backend_sqlitedb_search_uids (EBookBackendSqliteDB *ebsdb,
sqlite3_free (stmt);
}
- READER_UNLOCK (ebsdb);
-
local_searched = TRUE;
g_free (sql_query);
+
} else if (ebsdb->priv->store_vcard) {
- uids = book_backend_sqlitedb_search_full (ebsdb, sexp, folderid, TRUE, error);
+ uids = book_backend_sqlitedb_search_full (
+ ebsdb, sexp, folderid, TRUE, error);
local_searched = TRUE;
+
} else {
g_set_error (
error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
_("Full vcards are not stored in cache. "
- "Hence only summary query is supported."));
+ "Hence only summary query is supported."));
}
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
if (searched)
*searched = local_searched;
@@ -3599,13 +3868,15 @@ e_book_backend_sqlitedb_get_uids_and_rev (EBookBackendSqliteDB *ebsdb,
uids_and_rev = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
stmt = sqlite3_mprintf ("SELECT uid,rev FROM %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_uids_and_rev_cb, uids_and_rev, error);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt,
+ get_uids_and_rev_cb, uids_and_rev, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return uids_and_rev;
}
@@ -3628,13 +3899,16 @@ e_book_backend_sqlitedb_get_is_populated (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT is_populated FROM folders WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
+ stmt = sqlite3_mprintf (
+ "SELECT is_populated FROM folders WHERE folder_id = %Q",
+ folderid);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return ret;
@@ -3654,34 +3928,42 @@ e_book_backend_sqlitedb_set_is_populated (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf (
- "UPDATE folders SET is_populated = %d WHERE folder_id = %Q",
- populated, folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "UPDATE folders SET is_populated = %d "
+ "WHERE folder_id = %Q", populated, folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
/**
* e_book_backend_sqlitedb_get_revision:
* @ebsdb: An #EBookBackendSqliteDB
* @folderid: folder id of the address-book
- * @revision_out: (out) (transfer full): The location to return the current revision
+ * @revision_out: (out) (transfer full): The location to return the current
+ * revision
* @error: A location to store any error that may have occurred
*
* Fetches the current revision for the address-book indicated by @folderid.
@@ -3695,29 +3977,28 @@ e_book_backend_sqlitedb_set_is_populated (EBookBackendSqliteDB *ebsdb,
*/
gboolean
e_book_backend_sqlitedb_get_revision (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- gchar **revision_out,
- GError **error)
+ const gchar *folderid,
+ gchar **revision_out,
+ GError **error)
{
gchar *stmt;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid && folderid[0], FALSE);
g_return_val_if_fail (revision_out != NULL && *revision_out == NULL, FALSE);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT revision FROM folders WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb, &revision_out, &err);
+ stmt = sqlite3_mprintf (
+ "SELECT revision FROM folders WHERE folder_id = %Q", folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_string_cb, &revision_out, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- if (err)
- g_propagate_error (error, err);
-
- return !err;
+ return success;
}
/**
@@ -3735,35 +4016,41 @@ e_book_backend_sqlitedb_get_revision (EBookBackendSqliteDB *ebsdb,
*/
gboolean
e_book_backend_sqlitedb_set_revision (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- const gchar *revision,
- GError **error)
+ const gchar *folderid,
+ const gchar *revision,
+ GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid && folderid[0], FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf ("UPDATE folders SET revision = %Q WHERE folder_id = %Q",
- revision, folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "UPDATE folders SET revision = %Q "
+ "WHERE folder_id = %Q", revision, folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
- if (err)
- g_propagate_error (error, err);
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- return !err;
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return success;
}
-
/**
* e_book_backend_sqlitedb_get_has_partial_content
* @ebsdb:
@@ -3787,13 +4074,16 @@ e_book_backend_sqlitedb_get_has_partial_content (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT partial_content FROM folders WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
+ stmt = sqlite3_mprintf (
+ "SELECT partial_content FROM folders "
+ "WHERE folder_id = %Q", folderid);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_bool_cb , &ret, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return ret;
}
@@ -3812,27 +4102,34 @@ e_book_backend_sqlitedb_set_has_partial_content (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf (
- "UPDATE folders SET partial_content = %d WHERE folder_id = %Q",
- partial_content, folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "UPDATE folders SET partial_content = %d "
+ "WHERE folder_id = %Q", partial_content, folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
/**
@@ -3849,18 +4146,26 @@ e_book_backend_sqlitedb_get_contact_bdata (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt, *ret = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
g_return_val_if_fail (folderid != NULL, NULL);
g_return_val_if_fail (uid != NULL, NULL);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT bdata FROM %Q WHERE uid = %Q", folderid, uid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ stmt = sqlite3_mprintf (
+ "SELECT bdata FROM %Q WHERE uid = %Q", folderid, uid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_string_cb , &ret, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+
+ if (!success) {
+ g_warn_if_fail (ret == NULL);
+ return NULL;
+ }
return ret;
}
@@ -3880,29 +4185,36 @@ e_book_backend_sqlitedb_set_contact_bdata (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf (
- "UPDATE %Q SET bdata = %Q WHERE uid = %Q",
- folderid, value, uid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "UPDATE %Q SET bdata = %Q WHERE uid = %Q",
+ folderid, value, uid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
/**
@@ -3922,13 +4234,16 @@ e_book_backend_sqlitedb_get_sync_data (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
g_return_val_if_fail (folderid != NULL, NULL);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- stmt = sqlite3_mprintf ("SELECT sync_data FROM folders WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ stmt = sqlite3_mprintf (
+ "SELECT sync_data FROM folders WHERE folder_id = %Q",
+ folderid);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_string_cb , &ret, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return ret;
}
@@ -3947,28 +4262,35 @@ e_book_backend_sqlitedb_set_sync_data (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
g_return_val_if_fail (sync_data != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf (
- "UPDATE folders SET sync_data = %Q WHERE folder_id = %Q",
- sync_data, folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "UPDATE folders SET sync_data = %Q "
+ "WHERE folder_id = %Q", sync_data, folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
/**
@@ -3990,15 +4312,16 @@ e_book_backend_sqlitedb_get_key_value (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (folderid != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
stmt = sqlite3_mprintf (
"SELECT value FROM keys WHERE folder_id = %Q AND key = %Q",
folderid, key);
- book_backend_sql_exec (ebsdb->priv->db, stmt, get_string_cb , &ret, error);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt, get_string_cb , &ret, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return ret;
}
@@ -4018,29 +4341,36 @@ e_book_backend_sqlitedb_set_key_value (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt = NULL;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- if (!err) {
- stmt = sqlite3_mprintf (
- "INSERT or REPLACE INTO keys (key, value, folder_id) "
- "values (%Q, %Q, %Q)", key, value, folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ stmt = sqlite3_mprintf (
+ "INSERT or REPLACE INTO keys (key, value, folder_id) "
+ "values (%Q, %Q, %Q)", key, value, folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (success)
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ else
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return success;
}
/**
@@ -4061,15 +4391,16 @@ e_book_backend_sqlitedb_get_partially_cached_ids (EBookBackendSqliteDB *ebsdb,
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), NULL);
g_return_val_if_fail (folderid != NULL, NULL);
- READER_LOCK (ebsdb);
+ LOCK_MUTEX (&ebsdb->priv->lock);
stmt = sqlite3_mprintf (
"SELECT uid FROM %Q WHERE partial_content = 1",
folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
+ book_backend_sql_exec (
+ ebsdb->priv->db, stmt, addto_slist_cb, &uids, error);
sqlite3_free (stmt);
- READER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
return uids;
}
@@ -4087,40 +4418,59 @@ e_book_backend_sqlitedb_delete_addressbook (EBookBackendSqliteDB *ebsdb,
GError **error)
{
gchar *stmt;
- GError *err = NULL;
+ gboolean success;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
g_return_val_if_fail (folderid != NULL, FALSE);
- book_backend_sqlitedb_start_transaction (ebsdb, &err);
+ LOCK_MUTEX (&ebsdb->priv->lock);
- /* delete the contacts table */
- if (!err) {
- stmt = sqlite3_mprintf ("DROP TABLE %Q ", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
+ if (!book_backend_sqlitedb_start_transaction (ebsdb, error)) {
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
+ return FALSE;
}
+ /* delete the contacts table */
+ stmt = sqlite3_mprintf ("DROP TABLE %Q ", folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (!success)
+ goto rollback;
+
/* delete the key/value pairs corresponding to this table */
- if (!err) {
- stmt = sqlite3_mprintf ("DELETE FROM keys WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
- }
+ stmt = sqlite3_mprintf (
+ "DELETE FROM keys WHERE folder_id = %Q", folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (!success)
+ goto rollback;
/* delete the folder from the folders table */
- if (!err) {
- stmt = sqlite3_mprintf ("DELETE FROM folders WHERE folder_id = %Q", folderid);
- book_backend_sql_exec (ebsdb->priv->db, stmt, NULL, NULL, &err);
- sqlite3_free (stmt);
- }
+ stmt = sqlite3_mprintf (
+ "DELETE FROM folders WHERE folder_id = %Q", folderid);
+ success = book_backend_sql_exec (
+ ebsdb->priv->db, stmt, NULL, NULL, error);
+ sqlite3_free (stmt);
+
+ if (!success)
+ goto rollback;
+
+ success = book_backend_sqlitedb_commit_transaction (ebsdb, error);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- book_backend_sqlitedb_end_transaction (ebsdb, !err, err ? NULL : &err);
+ return success;
+
+rollback:
+ /* The GError is already set. */
+ book_backend_sqlitedb_rollback_transaction (ebsdb, NULL);
- if (err)
- g_propagate_error (error, err);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- return !err;
+ return FALSE;
}
/**
@@ -4152,27 +4502,25 @@ gboolean
e_book_backend_sqlitedb_remove (EBookBackendSqliteDB *ebsdb,
GError **error)
{
- EBookBackendSqliteDBPrivate *priv;
gchar *filename;
gint ret;
g_return_val_if_fail (E_IS_BOOK_BACKEND_SQLITEDB (ebsdb), FALSE);
- priv = ebsdb->priv;
+ LOCK_MUTEX (&ebsdb->priv->lock);
- WRITER_LOCK (ebsdb);
+ sqlite3_close (ebsdb->priv->db);
- sqlite3_close (priv->db);
- filename = g_build_filename (priv->path, DB_FILENAME, NULL);
+ filename = g_build_filename (ebsdb->priv->path, DB_FILENAME, NULL);
ret = g_unlink (filename);
+ g_free (filename);
- WRITER_UNLOCK (ebsdb);
+ UNLOCK_MUTEX (&ebsdb->priv->lock);
- g_free (filename);
if (ret == -1) {
g_set_error (
- error, E_BOOK_SDB_ERROR,
- E_BOOK_SDB_ERROR_OTHER, "Unable to remove the db file: errno %d", errno);
+ error, E_BOOK_SDB_ERROR, E_BOOK_SDB_ERROR_OTHER,
+ _("Unable to remove the db file: errno %d"), errno);
return FALSE;
}
@@ -4186,44 +4534,27 @@ destroy_search_data (gpointer data)
}
static gboolean
-validate_county_code (EBookBackendSqliteDB *ebsdb,
- const gchar *folderid,
- GError **error)
+upgrade_contacts_table (EBookBackendSqliteDB *ebsdb,
+ const gchar *folderid,
+ GError **error)
{
gchar *stmt;
gboolean success = FALSE;
GSList *vcard_data = NULL;
GSList *l;
+ gchar *default_region = NULL;
-#if HAVE__NL_ADDRESS_COUNTRY_AB2
- const gchar *country_code = nl_langinfo (_NL_ADDRESS_COUNTRY_AB2);
-#else /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
-#error Cannot resolve default 2-letter country code. Find a replacement for _NL_ADDRESS_COUNTRY_AB2 or
implement code to parse the locale name.
-#endif /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
-
- if (country_code == NULL || strlen (country_code) != 2) {
- g_warning ("Cannot derive 2-letter country code from current locale.");
- country_code = "ZZ";
- }
-
- /* Nothing has changed. We can stop here. */
- if (g_strcmp0 (country_code, ebsdb->priv->country_code) == 0)
- return TRUE;
-
- g_free (ebsdb->priv->country_code);
- ebsdb->priv->country_code = g_strdup (country_code);
-
- /* Filter out NULL vcards to avoid warnings when upgrading from old Berkeley DB format */
- stmt = sqlite3_mprintf ("SELECT uid, vcard, NULL FROM %Q "
- "WHERE NOT vcard IS NULL", folderid);
+ stmt = sqlite3_mprintf ("SELECT uid, vcard, NULL FROM %Q", folderid);
success = book_backend_sql_exec (
ebsdb->priv->db, stmt, addto_vcard_list_cb, &vcard_data, error);
sqlite3_free (stmt);
- if (vcard_data) {
- g_print ("The country code has changed to \"%s\". "
- "Must rebuild %s parameters and indexes for stored vCards.\n",
- ebsdb->priv->country_code, EVC_X_E164);
+ if (vcard_data == NULL)
+ return TRUE;
+
+ if (e_phone_number_is_supported ()) {
+ g_message ("The phone number indexes' format has changed. Rebuilding them.");
+ default_region = e_phone_number_get_default_region ();
}
for (l = vcard_data; success && l; l = l->next) {
@@ -4233,23 +4564,13 @@ validate_county_code (EBookBackendSqliteDB *ebsdb,
if (contact == NULL)
continue;
- if (update_e164_params (E_VCARD (contact), ebsdb->priv->country_code))
- success = insert_contact (ebsdb, contact, folderid, TRUE, error);
+ success = insert_contact (ebsdb, contact, folderid, TRUE, default_region, error);
g_object_unref (contact);
}
g_slist_free_full (vcard_data, destroy_search_data);
-
- if (success) {
- stmt = sqlite3_mprintf (
- "UPDATE folders SET countrycode = %Q WHERE folder_id = %Q",
- country_code, folderid);
- success = book_backend_sql_exec (
- ebsdb->priv->db, stmt, get_string_cb, &ebsdb->priv->country_code, error);
- sqlite3_free (stmt);
- }
+ g_free (default_region);
return success;
}
-
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 58fe8b4..c0a2582 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -57,7 +57,6 @@ libedataserver_1_2_la_SOURCES = \
e-list-iterator.c \
e-memory.c \
e-operation-pool.c \
- e-phone-utils.cpp \
e-proxy.c \
e-sexp.c \
e-source.c \
@@ -114,11 +113,6 @@ libedataserver_1_2_la_LDFLAGS = \
$(CODE_COVERAGE_LDFLAGS) \
$(NULL)
-if ENABLE_PHONENUMBER
-libedataserver_1_2_la_CPPFLAGS += $(PHONENUMBER_INCLUDES)
-libedataserver_1_2_la_LIBADD += $(PHONENUMBER_LIBS)
-endif ENABLE_PHONENUMBER
-
libedataserverincludedir = $(privincludedir)/libedataserver
libedataserverinclude_HEADERS = \
@@ -134,7 +128,6 @@ libedataserverinclude_HEADERS = \
e-list-iterator.h \
e-memory.h \
e-operation-pool.h \
- e-phone-utils.h \
e-proxy.h \
e-sexp.h \
e-source.h \
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index b2ebe2a..84797fb 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -34,7 +34,6 @@
#include <libedataserver/e-list.h>
#include <libedataserver/e-memory.h>
#include <libedataserver/e-operation-pool.h>
-#include <libedataserver/e-phone-utils.h>
#include <libedataserver/e-proxy.h>
#include <libedataserver/e-sexp.h>
#include <libedataserver/e-source-address-book.h>
diff --git a/m4/evo_phonenumber.m4 b/m4/evo_phonenumber.m4
index 395a3bc..8aa2db0 100644
--- a/m4/evo_phonenumber.m4
+++ b/m4/evo_phonenumber.m4
@@ -1,15 +1,21 @@
-dnl EVO_PHONENUMBER_SUPPORT([default])
-dnl Check for Google's libphonenumber. Adds a --with-phonenumber option
-dnl to explicitly enable and disable phonenumber support, but also for
-dnl pointing to libphonenumber's install prefix.
-AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
- AC_MSG_CHECKING([whether to enable phonenumber support])
+dnl EVO_PHONENUMBER_ARGS([default])
+dnl
+dnl Checks configure script options for requesting libphonenumber support.
+dnl Adds a --with-phonenumber option to explicitly enable and disable
+dnl phonenumber support, but also for pointing to libphonenumber's install
+dnl prefix.
+dnl
+dnl Must be called before any other macro that might use the C++ compiler.
+AC_DEFUN([EVO_PHONENUMBER_ARGS],[
+ AC_BEFORE([$0], [AC_COMPILE_IFELSE])
+ AC_BEFORE([$0], [AC_LINK_IFELSE])
+ AC_BEFORE([$0], [AC_PROG_CXX])
+ AC_BEFORE([$0], [AC_RUN_IFELSE])
+ AC_BEFORE([$0], [LT_INIT])
evo_phonenumber_prefix=
- msg_phonenumber=no
- PHONENUMBER_INCLUDES=
- PHONENUMBER_LIBS=
+ AC_MSG_CHECKING([whether to enable phonenumber support])
AC_ARG_WITH([phonenumber],
[AS_HELP_STRING([--with-phonenumber@<:@=PREFIX@:>@],
@@ -19,6 +25,25 @@ AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
AC_MSG_RESULT([$with_phonenumber])
+ AS_VAR_IF([with_phonenumber],[no],,[evo_with_cxx=yes])
+])
+
+dnl EVO_PHONENUMBER_SUPPORT
+dnl
+dnl Check for Google's libphonenumber. Adds a --with-phonenumber option
+dnl to explicitly enable and disable phonenumber support, but also for
+dnl pointing to libphonenumber's install prefix.
+dnl
+dnl You most probably want to place a call to EVO_PHONENUMBER_ARGS near
+dnl to the top of your configure script.
+AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
+ AC_REQUIRE([EVO_PHONENUMBER_ARGS])
+
+ msg_phonenumber=no
+
+ PHONENUMBER_INCLUDES=
+ PHONENUMBER_LIBS=
+
AS_VAR_IF([with_phonenumber], [no],, [
AC_LANG_PUSH(C++)
@@ -45,15 +70,46 @@ AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
AC_MSG_ERROR([libphonenumber cannot be used. Use --with-phonenumber to
specify the library prefix.])])
])
- CXXFLAGS="$evo_cxxflags_saved"
- LDFLAGS="$evo_ldflags_saved"
- LIBS="$evo_libs_saved"
AS_VAR_IF([evo_phonenumber_prefix],,
[msg_phonenumber=$with_phonenumber],
[msg_phonenumber=$evo_phonenumber_prefix])
AC_MSG_RESULT([$with_phonenumber])
+
+ AS_VAR_IF(
+ [with_phonenumber],[yes],
+ [AC_MSG_CHECKING([whether ParseAndKeepRawInput() is needed])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <phonenumbers/phonenumberutil.h>]],
+ [[namespace pn = i18n::phonenumbers;i18n::phonenumbers;
+
+ pn::PhoneNumber n;
+
+ if (pn::PhoneNumberUtil::GetInstance ()->
+ Parse("049(800)46663", "DE", &) ==
pn::PhoneNumberUtil::NO_PARSING_ERROR
+ && n.has_country_code_source ()
+ && n.country_code_source () == 49)
+ return EXIT_SUCCESS;
+
+ return EXIT_FAILURE;]]
+ )],
+
+ [AC_MSG_RESULT([no])],
+ [AC_MSG_RESULT([yes])
+
+ AC_DEFINE_UNQUOTED(
+ [PHONENUMBER_RAW_INPUT_NEEDED], 1,
+ [Whether Parse() or ParseAndKeepRawInput() must be used to get the
country-code source])
+ ])
+ ])
+
+
+ CXXFLAGS="$evo_cxxflags_saved"
+ LDFLAGS="$evo_ldflags_saved"
+ LIBS="$evo_libs_saved"
+
AC_LANG_POP(C++)
])
diff --git a/tests/libebook/Makefile.am b/tests/libebook/Makefile.am
index 2749324..3e5b640 100644
--- a/tests/libebook/Makefile.am
+++ b/tests/libebook/Makefile.am
@@ -92,6 +92,7 @@ endif
noinst_PROGRAMS = \
$(DEPRECATED_TESTS) \
+ test-ebook-phone-number \
test-categories \
test-date \
test-photo \
@@ -109,6 +110,8 @@ EXTRA_DIST = \
$(srcdir)/data/vcards/simple-2.vcf \
$(NULL)
+test_ebook_phone_number_LDADD=$(TEST_LIBS)
+test_ebook_phone_number_CPPFLAGS=$(TEST_CPPFLAGS)
test_categories_LDADD=$(TEST_LIBS)
test_categories_CPPFLAGS=$(TEST_CPPFLAGS)
test_date_LDADD=$(TEST_LIBS)
diff --git a/tests/libebook/client/Makefile.am b/tests/libebook/client/Makefile.am
index 072e7dd..33ad444 100644
--- a/tests/libebook/client/Makefile.am
+++ b/tests/libebook/client/Makefile.am
@@ -58,7 +58,6 @@ TESTS = \
test-client-remove-contact-by-uid \
test-client-remove-contacts \
test-client-photo-is-uri \
- test-client-upgrade-addressbook \
test-client-async \
$(NULL)
@@ -68,6 +67,7 @@ TESTS = \
# not been ported to use ETestServerFixture yet.
noinst_PROGRAMS = \
$(TESTS) \
+ test-client-upgrade-addressbook \
test-client-change-country-code \
test-client-examine \
test-client \
diff --git a/tests/libebook/client/test-client-custom-summary.c
b/tests/libebook/client/test-client-custom-summary.c
index d9f6f10..afa8806 100644
--- a/tests/libebook/client/test-client-custom-summary.c
+++ b/tests/libebook/client/test-client-custom-summary.c
@@ -21,6 +21,7 @@
*/
#include <stdlib.h>
+#include <locale.h>
#include <libebook/libebook.h>
#include "client-test-utils.h"
@@ -251,6 +252,7 @@ main (gint argc,
#endif
g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "en_US.UTF-8");
/* Test all queries in 8 different combinations specified by the 'suites'
*/
diff --git a/tests/libebook/client/test-client-e164-param.c b/tests/libebook/client/test-client-e164-param.c
index d415d1b..9b0f936 100644
--- a/tests/libebook/client/test-client-e164-param.c
+++ b/tests/libebook/client/test-client-e164-param.c
@@ -21,6 +21,7 @@
#include <config.h>
#include <stdlib.h>
+#include <locale.h>
#include <libebook/libebook.h>
#include "client-test-utils.h"
@@ -140,6 +141,8 @@ main (gint argc,
#endif
g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "en_US.UTF-8");
+
#ifdef ENABLE_PHONENUMBER
g_test_add (
diff --git a/tests/libebook/client/test-client-get-contact-uids.c
b/tests/libebook/client/test-client-get-contact-uids.c
index 9527333..908fa28 100644
--- a/tests/libebook/client/test-client-get-contact-uids.c
+++ b/tests/libebook/client/test-client-get-contact-uids.c
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include <stdlib.h>
+#include <locale.h>
#include <libebook/libebook.h>
#include "client-test-utils.h"
@@ -100,6 +101,8 @@ main (gint argc,
#endif
g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "en_US.UTF-8");
+
g_test_add (
"/EBookClient/GetContactUids/Sync", ETestServerFixture, &book_closure,
e_test_server_utils_setup, test_get_contact_uids_sync, e_test_server_utils_teardown);
diff --git a/tests/libebook/client/test-client-get-contact.c b/tests/libebook/client/test-client-get-contact.c
index 524db39..0fec2df 100644
--- a/tests/libebook/client/test-client-get-contact.c
+++ b/tests/libebook/client/test-client-get-contact.c
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include <stdlib.h>
+#include <locale.h>
#include <libebook/libebook.h>
#include "client-test-utils.h"
@@ -74,6 +75,8 @@ main (gint argc,
#endif
g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "en_US.UTF-8");
+
g_test_add (
"/EBookClient/GetContact/Sync", ETestServerFixture, &book_closure,
e_test_server_utils_setup, test_get_contact_sync, e_test_server_utils_teardown);
diff --git a/tests/libebook/client/test-client-get-view.c b/tests/libebook/client/test-client-get-view.c
index 1e0b8fb..1ddc68f 100644
--- a/tests/libebook/client/test-client-get-view.c
+++ b/tests/libebook/client/test-client-get-view.c
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include <stdlib.h>
+#include <locale.h>
#include <libebook/libebook.h>
#include "client-test-utils.h"
@@ -148,6 +149,8 @@ main (gint argc,
#endif
g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "en_US.UTF-8");
+
g_test_add (
"/EBookClient/GetBookClientView/Sync", ETestServerFixture, &book_closure,
e_test_server_utils_setup, test_get_view_sync, e_test_server_utils_teardown);
diff --git a/tests/libebook/client/test-client-view-operations.c
b/tests/libebook/client/test-client-view-operations.c
index 7cba28c..b713c83 100644
--- a/tests/libebook/client/test-client-view-operations.c
+++ b/tests/libebook/client/test-client-view-operations.c
@@ -8,10 +8,9 @@
#include "client-test-utils.h"
#include "e-test-server-utils.h"
-static ETestServerClosure book_closure = { E_TEST_SERVER_ADDRESS_BOOK, NULL, 0 };
static ETestServerClosure direct_book_closure = { E_TEST_SERVER_DIRECT_ADDRESS_BOOK, NULL, 0 };
-#define N_THREADS 20
+#define N_THREADS 5
#define N_CONTACTS 5
typedef struct {
@@ -80,14 +79,18 @@ complete (EBookClientView *view,
g_mutex_unlock (&data->complete_mutex);
}
-static void
-view_ready (GObject *source_object,
- GAsyncResult *result,
- ThreadData *data)
+static gboolean
+start_view (ThreadData *data)
{
+ EBookQuery *query;
+ gchar *sexp;
GError *error = NULL;
- if (!e_book_client_get_view_finish (E_BOOK_CLIENT (source_object), result, &(data->view), &error))
+ query = e_book_query_any_field_contains ("");
+ sexp = e_book_query_to_string (query);
+
+ if (!e_book_client_get_view_sync (data->client, sexp,
+ &(data->view), NULL, &error))
g_error ("Error getting view: %s", error->message);
g_signal_connect (data->view, "objects-added", G_CALLBACK (objects_added), data);
@@ -102,18 +105,6 @@ view_ready (GObject *source_object,
e_book_client_view_start (data->view, &error);
if (error)
g_error ("start view: %s", error->message);
-}
-
-static gboolean
-start_view (ThreadData *data)
-{
- EBookQuery *query;
- gchar *sexp;
-
- query = e_book_query_any_field_contains ("");
- sexp = e_book_query_to_string (query);
-
- e_book_client_get_view (data->client, sexp, NULL, (GAsyncReadyCallback)view_ready, data);
e_book_query_unref (query);
g_free (sexp);
@@ -146,20 +137,6 @@ finish_thread_test (ThreadData *data)
g_slice_free (ThreadData, data);
}
-static void
-client_ready (GObject *source_object,
- GAsyncResult *res,
- ThreadData *data)
-{
- GError *error = NULL;
-
- if (!e_client_open_finish (E_CLIENT (source_object), res, &error))
- g_error ("Error opening client: %s",
- error->message);
-
- start_thread_test (data);
-}
-
static gpointer
test_view_thread (ThreadData *data)
{
@@ -181,15 +158,12 @@ test_view_thread (ThreadData *data)
if (!source)
g_error ("Unable to fetch source uid '%s' from the registry", data->book_uid);
- if (data->closure->type == E_TEST_SERVER_DIRECT_ADDRESS_BOOK)
- data->client = (EBookClient *)e_book_client_connect_direct_sync (registry, source, NULL,
&error);
- else
- data->client = e_book_client_new (source, &error);
+ data->client = (EBookClient *)e_book_client_connect_direct_sync (registry, source, NULL, &error);
if (!data->client)
g_error ("Unable to create EBookClient for uid '%s': %s", data->book_uid, error->message);
- e_client_open (E_CLIENT (data->client), TRUE, NULL, (GAsyncReadyCallback)client_ready, data);
+ start_thread_test (data);
g_main_loop_run (data->loop);
@@ -278,8 +252,6 @@ main (gint argc,
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "en_US.UTF-8");
- g_test_add ("/EBookClient/ConcurrentViews", ETestServerFixture, &book_closure,
- e_test_server_utils_setup, test_concurrent_views, e_test_server_utils_teardown);
g_test_add ("/EBookClient/DirectAccess/ConcurrentViews", ETestServerFixture, &direct_book_closure,
e_test_server_utils_setup, test_concurrent_views, e_test_server_utils_teardown);
diff --git a/tests/libebook/test-ebook-phone-number.c b/tests/libebook/test-ebook-phone-number.c
new file mode 100644
index 0000000..2b58b19
--- /dev/null
+++ b/tests/libebook/test-ebook-phone-number.c
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libebook/libebook.h>
+#include <locale.h>
+
+static const char *match_candidates[] = {
+ "not-a-number",
+ "+1-617-4663489", "617-4663489", "4663489",
+ "+1.408.845.5246", "4088455246", "8455246",
+ "+1-857-4663489"
+};
+
+static const EPhoneNumberMatch expected_matches[] = {
+ /* not a number */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* +1-617-4663489 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* 617-4663489 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* 4663489 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, really? I'd expect a full match here. */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+
+ /* +1.408.845.5246 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* 4088455246 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* 8455246 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, really? I'd expect a full match here. */
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* +1-857-4663489 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT
+};
+
+typedef struct {
+ gchar *phone_number;
+ gchar *region_code;
+ EPhoneNumberCountrySource country_source;
+ gint country_code;
+ gchar *national_number;
+ gchar *formatted_numbers[4];
+} ParseAndFormatData;
+
+static ParseAndFormatData *
+parse_and_format_data_new (const gchar *phone_number,
+ const gchar *region_code,
+ EPhoneNumberCountrySource country_source,
+ gint country_code,
+ const gchar *national_number,
+ const gchar *formatted_e164,
+ const gchar *formatted_intl,
+ const gchar *formatted_natl,
+ const gchar *formatted_uri)
+{
+ ParseAndFormatData *test_data = g_slice_new0 (ParseAndFormatData);
+
+ test_data->phone_number = g_strdup (phone_number);
+ test_data->region_code = g_strdup (region_code);
+ test_data->country_source = country_source;
+ test_data->country_code = country_code;
+ test_data->national_number = g_strdup (national_number);
+ test_data->formatted_numbers[0] = g_strdup (formatted_e164);
+ test_data->formatted_numbers[1] = g_strdup (formatted_intl);
+ test_data->formatted_numbers[2] = g_strdup (formatted_natl);
+ test_data->formatted_numbers[3] = g_strdup (formatted_uri);
+
+ return test_data;
+}
+
+static void
+parse_and_format_data_free (gpointer data)
+{
+ ParseAndFormatData *const test_data = data;
+
+ g_free (test_data->phone_number);
+ g_free (test_data->region_code);
+ g_free (test_data->national_number);
+ g_free (test_data->formatted_numbers[0]);
+ g_free (test_data->formatted_numbers[1]);
+ g_free (test_data->formatted_numbers[2]);
+ g_free (test_data->formatted_numbers[3]);
+
+ g_slice_free (ParseAndFormatData, test_data);
+}
+
+static void
+test_parse_and_format (gconstpointer data)
+{
+ const ParseAndFormatData *const test_data = data;
+ GError *error = NULL;
+ EPhoneNumber *parsed;
+
+ parsed = e_phone_number_from_string (
+ test_data->phone_number, test_data->region_code, &error);
+
+#ifdef ENABLE_PHONENUMBER
+
+ {
+ EPhoneNumberCountrySource source;
+ gchar *national;
+ gint i;
+
+ g_assert_cmpint (
+ e_phone_number_get_country_code (parsed, &source), ==,
+ test_data->country_code);
+ g_assert_cmpuint (source, ==, test_data->country_source);
+
+ national = e_phone_number_get_national_number (parsed);
+ g_assert_cmpstr (national, ==, test_data->national_number);
+ g_free (national);
+
+ g_assert (parsed != NULL);
+ g_assert (error == NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (test_data->formatted_numbers); ++i) {
+ gchar *formatted = e_phone_number_to_string (parsed, i);
+ g_assert (formatted != NULL);
+ g_assert_cmpstr (formatted, ==, test_data->formatted_numbers[i]);
+ g_free (formatted);
+ }
+
+ e_phone_number_free (parsed);
+ }
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_assert (parsed == NULL);
+ g_assert (error != NULL);
+ g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+ g_assert (error->message != NULL);
+
+#endif /* ENABLE_PHONENUMBER */
+
+ g_clear_error (&error);
+}
+
+static void
+test_parse_bad_number (void)
+{
+ GError *error = NULL;
+ EPhoneNumber *parsed;
+
+ parsed = e_phone_number_from_string ("+1-NOT-A-NUMBER", "US", &error);
+
+ g_assert (parsed == NULL);
+ g_assert (error != NULL);
+ g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+#ifdef ENABLE_PHONENUMBER
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+#else /* ENABLE_PHONENUMBER */
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+#endif /* ENABLE_PHONENUMBER */
+ g_assert (error->message != NULL);
+
+ g_clear_error (&error);
+}
+
+static void
+test_parse_auto_region (void)
+{
+ GError *error = NULL;
+ EPhoneNumber *parsed;
+
+ parsed = e_phone_number_from_string ("212-5423789", NULL, &error);
+
+#ifdef ENABLE_PHONENUMBER
+
+ {
+ EPhoneNumberCountrySource source;
+ gchar *national;
+ gchar *formatted;
+
+ g_assert (parsed != NULL);
+ g_assert (error == NULL);
+
+ g_assert_cmpint (e_phone_number_get_country_code (parsed, &source), ==, 1);
+ g_assert_cmpuint (source, ==, E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT);
+
+ national = e_phone_number_get_national_number (parsed);
+ g_assert_cmpstr (national, ==, "2125423789");
+ g_free (national);
+
+ formatted = e_phone_number_to_string (parsed, E_PHONE_NUMBER_FORMAT_E164);
+ g_assert_cmpstr (formatted, ==, "+12125423789");
+ g_free (formatted);
+
+ e_phone_number_free (parsed);
+ }
+
+#else /* ENABLE_PHONENUMBER */
+
+ g_assert (parsed == NULL);
+ g_assert (error != NULL);
+ g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+ g_assert (error->message != NULL);
+ g_clear_error (&error);
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+static void
+test_compare_numbers (gconstpointer data)
+{
+ const size_t n = GPOINTER_TO_UINT (data);
+ const size_t i = n % G_N_ELEMENTS (match_candidates);
+ const size_t j = n / G_N_ELEMENTS (match_candidates);
+
+#ifdef ENABLE_PHONENUMBER
+ const gboolean error_expected = !(i && j) ;
+#else /* ENABLE_PHONENUMBER */
+ const gboolean error_expected = TRUE;
+#endif /* ENABLE_PHONENUMBER */
+
+ EPhoneNumberMatch actual_match;
+ GError *error = NULL;
+
+ actual_match = e_phone_number_compare_strings (match_candidates[i],
+ match_candidates[j],
+ &error);
+
+#ifdef ENABLE_PHONENUMBER
+ g_assert_cmpuint (actual_match, ==, expected_matches[n]);
+#else /* ENABLE_PHONENUMBER */
+ g_assert_cmpuint (actual_match, ==, E_PHONE_NUMBER_MATCH_NONE);
+#endif /* ENABLE_PHONENUMBER */
+
+ if (!error_expected) {
+ g_assert (error == NULL);
+ } else {
+ g_assert (error != NULL);
+ g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+#ifdef ENABLE_PHONENUMBER
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+#else /* ENABLE_PHONENUMBER */
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+#endif /* ENABLE_PHONENUMBER */
+ g_assert (error->message != NULL);
+
+ g_clear_error (&error);
+ }
+}
+
+static void
+test_supported (void)
+{
+#ifdef ENABLE_PHONENUMBER
+ g_assert (e_phone_number_is_supported ());
+#else /* ENABLE_PHONENUMBER */
+ g_assert (!e_phone_number_is_supported ());
+#endif /* ENABLE_PHONENUMBER */
+}
+
+static void
+test_country_code_for_region (void)
+{
+ g_assert_cmpstr (setlocale (LC_ADDRESS, NULL), ==, "en_US.UTF-8");
+ g_assert_cmpint (e_phone_number_get_country_code_for_region ("CH"), ==, 41);
+ g_assert_cmpint (e_phone_number_get_country_code_for_region (NULL), ==, 1);
+ g_assert_cmpint (e_phone_number_get_country_code_for_region ("C"), ==, 0);
+ g_assert_cmpint (e_phone_number_get_country_code_for_region (""), ==, 1);
+}
+
+static void
+test_default_region (void)
+{
+ gchar *country;
+
+ g_assert_cmpstr (setlocale (LC_ADDRESS, NULL), ==, "en_US.UTF-8");
+
+ country = e_phone_number_get_default_region ();
+ g_assert_cmpstr (country, ==, "US");
+ g_free (country);
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ size_t i, j;
+
+ setlocale (LC_ALL, "en_US.UTF-8");
+
+ g_type_init ();
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func
+ ("/ebook-phone-number/supported",
+ test_supported);
+
+ g_test_add_data_func_full (
+ "/ebook-phone-number/parse-and-format/i164", parse_and_format_data_new (
+ "+493011223344", NULL,
+ E_PHONE_NUMBER_COUNTRY_FROM_FQTN, 49, "3011223344",
+ "+493011223344", "+49 30 11223344", "030 11223344", "tel:+49-30-11223344"),
+ test_parse_and_format, parse_and_format_data_free);
+ g_test_add_data_func_full (
+ "/ebook-phone-number/parse-and-format/national", parse_and_format_data_new (
+ "(030) 22334-455", "DE",
+ E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT, 49, "3022334455",
+ "+493022334455", "+49 30 22334455", "030 22334455", "tel:+49-30-22334455"),
+ test_parse_and_format, parse_and_format_data_free);
+ g_test_add_data_func_full (
+ "/ebook-phone-number/parse-and-format/national2", parse_and_format_data_new (
+ "0049 (30) 22334-455", "DE",
+ E_PHONE_NUMBER_COUNTRY_FROM_IDD, 49, "3022334455",
+ "+493022334455", "+49 30 22334455", "030 22334455", "tel:+49-30-22334455"),
+ test_parse_and_format, parse_and_format_data_free);
+ g_test_add_data_func_full (
+ "/ebook-phone-number/parse-and-format/international", parse_and_format_data_new (
+ "+1 212 33445566", NULL,
+ E_PHONE_NUMBER_COUNTRY_FROM_FQTN, 1, "21233445566",
+ "+121233445566", "+1 21233445566", "21233445566", "tel:+1-21233445566"),
+ test_parse_and_format, parse_and_format_data_free);
+ g_test_add_data_func_full (
+ "/ebook-phone-number/parse-and-format/rfc3966", parse_and_format_data_new (
+ "tel:+358-71-44556677", NULL,
+ E_PHONE_NUMBER_COUNTRY_FROM_FQTN, 358, "7144556677",
+ "+3587144556677", "+358 71 44556677", "071 44556677", "tel:+358-71-44556677"),
+ test_parse_and_format, parse_and_format_data_free);
+
+ g_test_add_func (
+ "/ebook-phone-number/parse-and-format/bad-number",
+ test_parse_bad_number);
+
+ g_test_add_func (
+ "/ebook-phone-number/parse-and-format/auto-region",
+ test_parse_auto_region);
+
+ g_assert_cmpint (G_N_ELEMENTS (match_candidates) * G_N_ELEMENTS (match_candidates),
+ ==, G_N_ELEMENTS (expected_matches));
+
+ for (i = 0; i < G_N_ELEMENTS (match_candidates); ++i) {
+ for (j = 0; j < G_N_ELEMENTS (match_candidates); ++j) {
+ const size_t n = j + i * G_N_ELEMENTS (match_candidates);
+ char *path = g_strdup_printf ("/ebook-phone-number/compare/%s/%s",
+ match_candidates[i], match_candidates[j]);
+
+ g_test_add_data_func (path, GUINT_TO_POINTER (n), test_compare_numbers);
+ g_free (path);
+ }
+ }
+
+ g_test_add_func (
+ "/ebook-phone-number/country-code/for-region",
+ test_country_code_for_region);
+ g_test_add_func (
+ "/ebook-phone-number/country-code/default-region",
+ test_default_region);
+
+ return g_test_run ();
+}
diff --git a/tests/libedataserver/Makefile.am b/tests/libedataserver/Makefile.am
index 8f17b02..09d9613 100644
--- a/tests/libedataserver/Makefile.am
+++ b/tests/libedataserver/Makefile.am
@@ -1,5 +1,4 @@
TESTS = \
- e-phone-utils-test \
$(NULL)
noinst_PROGRAMS = \
@@ -18,9 +17,6 @@ TEST_LDADD = \
$(E_DATA_SERVER_LIBS) \
$(GIO_UNIX_LIBS)
-e_phone_utils_test_CPPFLAGS = $(TEST_CPPFLAGS)
-e_phone_utils_test_LDADD = $(TEST_LDADD)
-
e_source_test_CPPFLAGS = $(TEST_CPPFLAGS)
e_source_test_LDADD = $(TEST_LDADD)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]