[evolution-data-server/openismus-phonenumber-work-master: 4/4] sqlitedb: Store E.164 param in vcards



commit 8d01414fdb7fd0c1140c16b0c2d678d8486de2f1
Author: Mathias Hasselmann <mathias openismus com>
Date:   Mon Dec 10 15:24:57 2012 +0100

    sqlitedb: Store E.164 param in vcards
    
    The E.164 normalized phone number is of interest for handset related
    applications. With this change a X-EVOLUTION-E164 attribute is added
    to each TEL attribute if the contact summary contains a E.164 formatted
    variant of the phone number. This shall avoid overhead and inconsistency
    that would occur if clients would use their own mechanism to compute that
    already stored information.

 addressbook/libebook/e-vcard.h                     |    1 +
 .../libedata-book/e-book-backend-sqlitedb.c        |   86 +++++-
 tests/libebook/client/Makefile.am                  |    4 +
 tests/libebook/client/client-test-utils.c          |   23 ++
 tests/libebook/client/client-test-utils.h          |    1 +
 .../client/test-client-change-country-code.c       |  317 ++++++++++++++++++++
 6 files changed, 428 insertions(+), 4 deletions(-)
---
diff --git a/addressbook/libebook/e-vcard.h b/addressbook/libebook/e-vcard.h
index 5f4156c..c783a94 100644
--- a/addressbook/libebook/e-vcard.h
+++ b/addressbook/libebook/e-vcard.h
@@ -81,6 +81,7 @@ G_BEGIN_DECLS
 #define EVC_X_DEST_EMAIL_NUM		"X-EVOLUTION-DEST-EMAIL-NUM"
 #define EVC_X_DEST_HTML_MAIL		"X-EVOLUTION-DEST-HTML-MAIL"
 #define EVC_X_DEST_SOURCE_UID		"X-EVOLUTION-DEST-SOURCE-UID"
+#define EVC_X_E164			"X-EVOLUTION-E164"
 #define EVC_X_FILE_AS			"X-EVOLUTION-FILE-AS"
 #define EVC_X_GADUGADU			"X-GADUGADU"
 #define EVC_X_GROUPWISE			"X-GROUPWISE"
diff --git a/addressbook/libedata-book/e-book-backend-sqlitedb.c b/addressbook/libedata-book/e-book-backend-sqlitedb.c
index b07a32c..e6b4764 100644
--- a/addressbook/libedata-book/e-book-backend-sqlitedb.c
+++ b/addressbook/libedata-book/e-book-backend-sqlitedb.c
@@ -1548,6 +1548,77 @@ mprintf_phone (const gchar *normal,
 	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;
+	}
+
+	return NULL;
+}
+
+static gboolean
+update_e164_params (EVCard *vcard,
+                    const gchar *country_code)
+{
+	const GList *attr_list = e_vcard_get_attributes (vcard);
+	gboolean modified = FALSE;
+
+	if (!e_phone_number_is_supported ())
+		return FALSE;
+
+	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;
+
+		/* Skip all attributes but phone numbers. */
+		if (strcmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
+			continue;
+
+		/* Compute normalized phone number. */
+		param = find_param (attr, EVC_X_E164);
+		formatted_number = e_vcard_attribute_get_value (attr);
+
+		if (formatted_number)
+			normalized_number = convert_phone (formatted_number, country_code);
+
+		/* 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;
+				}
+			}
+
+			g_free (normalized_number);
+		} else if (param) {
+			e_vcard_attribute_remove_param (attr, EVC_X_E164);
+			modified = TRUE;
+		}
+	}
+
+	return modified;
+}
+
 /* Add Contact (free the result with g_free() ) */
 static gchar *
 insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
@@ -1617,7 +1688,13 @@ insert_stmt_from_contact (EBookBackendSqliteDB *ebsdb,
 			g_warn_if_reached ();
 	}
 
-	vcard_str = store_vcard ? e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30) : NULL;
+	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;
+	}
+
 	str = sqlite3_mprintf (", %Q, %Q)", vcard_str, NULL);
 
 	g_string_append (string, str);
@@ -4222,8 +4299,8 @@ validate_county_code (EBookBackendSqliteDB  *ebsdb,
 
 	if (vcard_data) {
 		g_print ("The country code has changed to \"%s\". "
-		         "Must rebuild phone number indexes for stored vCards.\n",
-		         ebsdb->priv->country_code);
+		         "Must rebuild %s parameters and indexes for stored vCards.\n",
+		         ebsdb->priv->country_code, EVC_X_E164);
 	}
 
 	for (l = vcard_data; success && l; l = l->next) {
@@ -4233,7 +4310,8 @@ validate_county_code (EBookBackendSqliteDB  *ebsdb,
 		if (contact == NULL)
 			continue;
 
-		success = insert_contact (ebsdb, contact, folderid, error);
+		if (update_e164_params (E_VCARD (contact), ebsdb->priv->country_code))
+			success = insert_contact (ebsdb, contact, folderid, error);
 
 		g_object_unref (contact);
 	}
diff --git a/tests/libebook/client/Makefile.am b/tests/libebook/client/Makefile.am
index 9c582c7..348c762 100644
--- a/tests/libebook/client/Makefile.am
+++ b/tests/libebook/client/Makefile.am
@@ -13,6 +13,7 @@ libclient_test_utils_la_CPPFLAGS =				\
 	-I$(top_srcdir)/tests/test-server-utils     		\
 	-I$(top_builddir)/tests/test-server-utils   		\
 	-DSRCDIR=\""$(abs_srcdir)"\"				\
+	-DBUILDDIR=\""$(abs_topbuilddir)"\"			\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)				\
 	$(CAMEL_CFLAGS)						\
 	$(NULL)
@@ -42,6 +43,7 @@ TESTS =								\
 	test-client-remove-contact-by-uid			\
 	test-client-remove-contacts				\
 	test-client-photo-is-uri				\
+	test-client-change-country-code				\
 	$(NULL)
 
 # The noinst tests are functional tests, not unit tests.
@@ -112,6 +114,8 @@ test_client_remove_contacts_LDADD=$(TEST_LIBS)
 test_client_remove_contacts_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_photo_is_uri_LDADD=$(TEST_LIBS)
 test_client_photo_is_uri_CPPFLAGS=$(TEST_CPPFLAGS)
+test_client_change_country_code_LDADD=$(TEST_LIBS)
+test_client_change_country_code_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_stress_factory__fifo_LDADD=$(TEST_LIBS)
 test_client_stress_factory__fifo_CPPFLAGS=$(TEST_CPPFLAGS)
 test_client_stress_factory__serial_LDADD=$(TEST_LIBS)
diff --git a/tests/libebook/client/client-test-utils.c b/tests/libebook/client/client-test-utils.c
index f9bda66..23e3257 100644
--- a/tests/libebook/client/client-test-utils.c
+++ b/tests/libebook/client/client-test-utils.c
@@ -22,6 +22,7 @@
  *          Tristan Van Berkom <tristanvb openismus com>
  */
 
+#include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -96,6 +97,12 @@ main_initialize (void)
 	if (initialized)
 		return;
 
+	/* hasselmm: The locale doesn't only affect obvious settings like
+	 * program messages, or date formats. It also changes more subtile
+	 * aspects like the sorting order. Therefore (IMHO) it's generally
+	 * a good idea to use a fixed locale for regression tests. */
+	setlocale (LC_ALL, "en_US.UTF-8");
+
 	g_type_init ();
 	e_gdbus_templates_init_main_thread ();
 
@@ -224,6 +231,22 @@ stop_main_loop (gint stop_result)
 	g_main_loop_quit (loop);
 }
 
+static gboolean
+sleep_in_main_loop_cb (gpointer data)
+{
+	g_main_loop_quit (data);
+	return FALSE;
+}
+
+void
+sleep_in_main_loop (guint msec)
+{
+	GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+	g_timeout_add (msec, sleep_in_main_loop_cb, loop);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+}
+
 /* returns value used in stop_main_loop() */
 gint
 get_main_loop_stop_result (void)
diff --git a/tests/libebook/client/client-test-utils.h b/tests/libebook/client/client-test-utils.h
index 2537735..510f341 100644
--- a/tests/libebook/client/client-test-utils.h
+++ b/tests/libebook/client/client-test-utils.h
@@ -35,6 +35,7 @@ void main_initialize (void);
 void start_main_loop (GThreadFunc func, gpointer data);
 void start_in_thread_with_main_loop (GThreadFunc func, gpointer data);
 void start_in_idle_with_main_loop (GThreadFunc func, gpointer data);
+void sleep_in_main_loop (guint msec);
 void stop_main_loop (gint stop_result);
 gint get_main_loop_stop_result (void);
 
diff --git a/tests/libebook/client/test-client-change-country-code.c b/tests/libebook/client/test-client-change-country-code.c
new file mode 100644
index 0000000..786079d
--- /dev/null
+++ b/tests/libebook/client/test-client-change-country-code.c
@@ -0,0 +1,317 @@
+/* -*- 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>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <string.h>
+
+#include <libebook/libebook.h>
+
+#include "client-test-utils.h"
+#include "e-test-server-utils.h"
+
+
+static void
+setup_custom_book (ESource            *scratch,
+                   ETestServerClosure *closure)
+{
+	ESourceBackendSummarySetup *setup;
+
+	g_type_ensure (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP);
+	setup = e_source_get_extension (scratch, E_SOURCE_EXTENSION_BACKEND_SUMMARY_SETUP);
+	e_source_backend_summary_setup_set_summary_fields (setup,
+							   E_CONTACT_TEL,
+							   0);
+	e_source_backend_summary_setup_set_indexed_fields (setup,
+							   E_CONTACT_TEL, E_BOOK_INDEX_PHONE,
+							   0);
+}
+
+static gboolean
+query_service_pid (const gchar *name,
+                   GPid        *pid,
+                   GError     **error)
+{
+	GDBusConnection *connection;
+	GVariant *rv;
+
+	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+	if (connection == NULL)
+		return FALSE;
+
+	rv = g_dbus_connection_call_sync (connection,
+	                                  "org.freedesktop.DBus",
+	                                  "/org/freedesktop/DBus",
+	                                  "org.freedesktop.DBus",
+	                                  "GetConnectionUnixProcessID",
+	                                  g_variant_new ("(s)", name),
+	                                  NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+	                                  NULL, error);
+
+	g_object_unref (connection);
+
+	if (rv == NULL)
+		return FALSE;
+
+	g_variant_get (rv, "(u)", pid);
+	g_variant_unref (rv);
+
+	return TRUE;
+}
+
+static gboolean
+spawn_addressbook_factory (GPid *pid, GError **error)
+{
+	gint i;
+	GPid child_pid = 0;
+	GPid service_pid = 0;
+	gboolean success;
+
+	gchar *argv[] = {
+		g_build_filename (BUILDDIR, "services/evolution-addressbook-factory/evolution-addressbook-factory", NULL),
+		NULL
+	};
+
+	success = g_spawn_async (
+		NULL, argv, NULL,
+		g_test_verbose () ? 0 : G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+		NULL, NULL, &child_pid, error);
+
+	g_free (argv[0]);
+
+	/* Wait a bit for the addressbook factory starting up so that it can claim its D-Bus name */
+	if (success) {
+		for (i = 0; service_pid == 0 && i < 10; ++i) {
+			if (error && *error)
+				g_clear_error (error);
+
+			sleep_in_main_loop (150);
+			success = query_service_pid (ADDRESS_BOOK_DBUS_SERVICE_NAME, &service_pid, error);
+		}
+	}
+
+	if (success) {
+		g_assert_cmpuint (service_pid, ==, child_pid);
+
+		if (pid)
+			*pid = child_pid;
+	}
+
+	return success;
+}
+
+static void
+test_client_change_country_code (void)
+{
+	ETestServerFixture fixture;
+	ETestServerClosure data;
+
+	GError *error = NULL;
+	EContact *contact;
+	gchar *contact_uid;
+	EVCardAttribute *attr;
+	GList *e164_values;
+	gboolean success;
+	GPid factory_pid = 0;
+	GSList *fetched_uids;
+
+	/* Initialize e-test-server-utils structures */
+	memset (&fixture, 0, sizeof fixture);
+
+	memset (&data, 0, sizeof data);
+	data.type = E_TEST_SERVER_ADDRESS_BOOK;
+	data.customize = setup_custom_book;
+	data.keep_work_directory = TRUE;
+
+	/**************************************
+	 * Create contacts within U.S. locale *
+	 **************************************/
+
+	/* Set with U.S. locale for addresses */
+	g_setenv ("LC_ADDRESS", "en_US.UTF-8", TRUE);
+	setlocale (LC_ADDRESS, "");
+	g_assert_cmpstr (nl_langinfo (_NL_ADDRESS_COUNTRY_AB2), ==, "US");
+
+	/* Launch addressbook factory on fake-dbus */
+	success = spawn_addressbook_factory (&factory_pid, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert_cmpuint (factory_pid, !=, 0);
+	g_assert (success);
+
+	/* Create the test addressbook */
+	e_test_server_utils_setup (&fixture, &data);
+	g_assert (fixture.service.book_client != NULL);
+
+	success = e_client_open_sync
+		(E_CLIENT (fixture.service.book_client), FALSE, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	/* Add test contact */
+	contact = e_contact_new_from_vcard ("BEGIN:VCARD\nTEL:221.542.3789\nEND:VCARD");
+	success = e_book_client_add_contact_sync
+		(fixture.service.book_client, contact, &contact_uid, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+	g_object_unref (contact);
+
+	/**************************************
+	 * Verify contacts within U.S. locale *
+	 **************************************/
+
+	/* Fetch the contact by UID and check EVC_TEL attribute and its EVC_X_E164 parameter */
+	success = e_book_client_get_contact_sync
+		(fixture.service.book_client, contact_uid, &contact, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	attr = e_vcard_get_attribute (E_VCARD (contact), EVC_TEL);
+
+	g_assert (attr != NULL);
+	g_assert_cmpstr (e_vcard_attribute_get_value (attr), ==, "221.542.3789");
+
+	e164_values = e_vcard_attribute_get_param (attr, EVC_X_E164);
+
+	g_assert (e164_values != NULL);
+	g_assert_cmpstr (e164_values->data, ==, "+12215423789");
+
+	/* Now resolve the contact via its phone number, assuming indexes are used */
+	success = e_book_client_get_contacts_uids_sync (
+		fixture.service.book_client, "(eqphone \"phone\" \"+1/221/5423789\")",
+		&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 1);
+	g_assert_cmpstr (fetched_uids->data, ==, contact_uid);
+	g_slist_free_full (fetched_uids, g_free);
+
+	success = e_book_client_get_contacts_uids_sync (
+		fixture.service.book_client, "(eqphone \"phone\" \"+49 (221) 5423789\")",
+		&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 0);
+	g_slist_free_full (fetched_uids, g_free);
+
+	/***************************
+	 * Switch to German locale *
+	 ***************************/
+
+	/* Shutdown address book factory on fake D-Bus */
+	e_test_server_utils_teardown (&fixture, &data);
+	g_assert (fixture.service.book_client == NULL);
+	data.keep_work_directory = g_test_verbose ();
+
+	success = (kill (factory_pid, SIGTERM) == 0);
+	g_assert (success);
+	factory_pid = 0;
+
+	/* Wait a bit to let GDBus notice what happened... */
+	sleep_in_main_loop (1500);
+
+	/* Switch to German locale */
+	g_setenv ("LC_ADDRESS", "de_DE.UTF-8", TRUE);
+	setlocale (LC_ADDRESS, "");
+
+	g_assert_cmpstr (nl_langinfo (_NL_ADDRESS_COUNTRY_AB2), ==, "DE");
+
+	/* Respawn the addressbook factory */
+	success = spawn_addressbook_factory (&factory_pid, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert_cmpuint (factory_pid, !=, 0);
+	g_assert (success);
+
+	/* Reopen the book */
+	e_test_server_utils_setup (&fixture, &data);
+	g_assert (fixture.service.book_client != NULL);
+
+	success = e_client_open_sync
+		(E_CLIENT (fixture.service.book_client), FALSE, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	/****************************************
+	 * Verify contacts within German locale *
+	 ****************************************/
+
+	/* Fetch the contact by UID and check EVC_TEL attribute and its EVC_X_E164 parameter */
+	success = e_book_client_get_contact_sync
+		(fixture.service.book_client, contact_uid, &contact, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	attr = e_vcard_get_attribute (E_VCARD (contact), EVC_TEL);
+
+	g_assert (attr != NULL);
+	g_assert_cmpstr (e_vcard_attribute_get_value (attr), ==, "221.542.3789");
+
+	e164_values = e_vcard_attribute_get_param (attr, EVC_X_E164);
+
+	g_assert (e164_values != NULL);
+	g_assert_cmpstr (e164_values->data, ==, "+492215423789");
+
+	/* Now resolve the contact via its phone number, assuming indexes are used */
+	success = e_book_client_get_contacts_uids_sync (
+		fixture.service.book_client, "(eqphone \"phone\" \"+1/221/5423789\")",
+		&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 0);
+	g_slist_free_full (fetched_uids, g_free);
+
+	success = e_book_client_get_contacts_uids_sync (
+		fixture.service.book_client, "(eqphone \"phone\" \"+49 (221) 5423789\")",
+		&fetched_uids, NULL, &error);
+	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
+	g_assert (success);
+
+	g_assert_cmpuint (g_slist_length (fetched_uids), ==, 1);
+	g_assert_cmpstr (fetched_uids->data, ==, contact_uid);
+	g_slist_free_full (fetched_uids, g_free);
+
+	/***********
+	 * Cleanup *
+	 ***********/
+
+	e_test_server_utils_teardown (&fixture, &data);
+	g_assert (fixture.service.book_client == NULL);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+	g_test_init (&argc, &argv, NULL);
+
+	main_initialize ();
+	sleep_in_main_loop (500);
+
+	g_test_add_func ("/client/e164/change-country-code", test_client_change_country_code);
+
+	return e_test_server_utils_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]