summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Polrola <mateuszx.potrola@intel.com>2014-12-12 12:50:09 +0000
committerMateusz Polrola <mateusz.polrola@gmail.com>2015-01-15 08:39:09 +0100
commitb52e2d3a0030d6467f372ee246b6848c49412281 (patch)
tree116324b6c25c633d76f6cb2788ccb4537736ba47
parenta02c510d95457ec04d82773284a2e4e6714536ec (diff)
downloadevolution-data-server-intel-work-3-12.tar.gz
EBookSqlite: Added new phone lookup operator.intel-work-3-12
New phone lookup operator uses custom phone matching function, that does not relies on currently set locale for given address book.
-rw-r--r--addressbook/libebook-contacts/e-book-query.c13
-rw-r--r--addressbook/libebook-contacts/e-book-query.h1
-rw-r--r--addressbook/libebook-contacts/e-phone-number-private.cpp13
-rw-r--r--addressbook/libebook-contacts/e-phone-number-private.h2
-rw-r--r--addressbook/libebook-contacts/e-phone-number.c318
-rw-r--r--addressbook/libebook-contacts/e-phone-number.h6
-rw-r--r--addressbook/libedata-book/e-book-backend-sexp.c33
-rw-r--r--addressbook/libedata-book/e-book-sqlite.c143
8 files changed, 526 insertions, 3 deletions
diff --git a/addressbook/libebook-contacts/e-book-query.c b/addressbook/libebook-contacts/e-book-query.c
index 518fb31d0..2baf04a3e 100644
--- a/addressbook/libebook-contacts/e-book-query.c
+++ b/addressbook/libebook-contacts/e-book-query.c
@@ -652,6 +652,15 @@ func_eqphone_short (struct _ESExp *f,
}
static ESExpResult *
+func_eqphone_is (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ return func_field_test (E_BOOK_QUERY_PHONE_NUMBER_IS, f, argc, argv, data);
+}
+
+static ESExpResult *
func_regex_normal (struct _ESExp *f,
gint argc,
struct _ESExpResult **argv,
@@ -777,6 +786,7 @@ static const struct {
{ "eqphone", func_eqphone, 0 },
{ "eqphone_national", func_eqphone_national, 0 },
{ "eqphone_short", func_eqphone_short, 0 },
+ { "eqphone_is", func_eqphone_is, 0 },
{ "regex_translit", func_regex_translit, 0 },
{ "regex_normal", func_regex_normal, 0 },
{ "regex_raw", func_regex_raw, 0 },
@@ -860,6 +870,8 @@ field_test_name (EBookQueryTest field_test)
return "eqphone_national";
case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER:
return "eqphone_short";
+ case E_BOOK_QUERY_PHONE_NUMBER_IS:
+ return "eqphone_is";
case E_BOOK_QUERY_REGEX_NORMAL:
return "regex_normal";
case E_BOOK_QUERY_REGEX_RAW:
@@ -888,6 +900,7 @@ is_phone_test (EBookQueryTest field_test)
case E_BOOK_QUERY_EQUALS_PHONE_NUMBER:
case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER:
case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER:
+ case E_BOOK_QUERY_PHONE_NUMBER_IS:
return TRUE;
case E_BOOK_QUERY_IS:
diff --git a/addressbook/libebook-contacts/e-book-query.h b/addressbook/libebook-contacts/e-book-query.h
index 1c74f4ad8..592854100 100644
--- a/addressbook/libebook-contacts/e-book-query.h
+++ b/addressbook/libebook-contacts/e-book-query.h
@@ -77,6 +77,7 @@ typedef enum {
E_BOOK_QUERY_EQUALS_PHONE_NUMBER,
E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER,
E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER,
+ E_BOOK_QUERY_PHONE_NUMBER_IS,
E_BOOK_QUERY_REGEX_NORMAL,
E_BOOK_QUERY_REGEX_RAW,
diff --git a/addressbook/libebook-contacts/e-phone-number-private.cpp b/addressbook/libebook-contacts/e-phone-number-private.cpp
index f41e75b9f..ff359ae87 100644
--- a/addressbook/libebook-contacts/e-phone-number-private.cpp
+++ b/addressbook/libebook-contacts/e-phone-number-private.cpp
@@ -139,6 +139,19 @@ _e_phone_number_cxx_get_default_region ()
return g_strdup (_e_phone_number_cxx_make_region_code (NULL).c_str ());
}
+gchar *
+_e_phone_number_cxx_normalize (const gchar *phone_number)
+{
+ std::string number = phone_number;
+ std::size_t pos = number.find_first_of('+');
+ if (pos != std::string::npos) {
+ number.replace (pos, 1, "00");
+ }
+
+ e_phone_number_util_get_instance ()->NormalizeDigitsOnly (&number);
+ return g_strdup (number.c_str());
+}
+
static bool
_e_phone_number_cxx_parse (const std::string &phone_number,
const std::string &region,
diff --git a/addressbook/libebook-contacts/e-phone-number-private.h b/addressbook/libebook-contacts/e-phone-number-private.h
index ac5b70171..700d8b8d5 100644
--- a/addressbook/libebook-contacts/e-phone-number-private.h
+++ b/addressbook/libebook-contacts/e-phone-number-private.h
@@ -54,6 +54,8 @@ 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 gchar * _e_phone_number_cxx_normalize (const gchar *phone_number);
+
E_PHONE_NUMBER_LOCAL EPhoneNumber * _e_phone_number_cxx_from_string (const gchar *phone_number,
const gchar *region_code,
GError **error);
diff --git a/addressbook/libebook-contacts/e-phone-number.c b/addressbook/libebook-contacts/e-phone-number.c
index c167d86c3..427deeca3 100644
--- a/addressbook/libebook-contacts/e-phone-number.c
+++ b/addressbook/libebook-contacts/e-phone-number.c
@@ -157,6 +157,33 @@ e_phone_number_get_default_region (GError **error)
}
/**
+ * e_phone_number_normalize:
+ * @phone_number: phone number string
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Normalizes provided phone number string.
+ *
+ * Returns: (transfer full): a newly allocated string containing the
+ * normalized phone number.
+ *
+ * Since: 3.12
+ */
+gchar *
+e_phone_number_normalize (const char *phone_number, GError **error)
+{
+#ifdef ENABLE_PHONENUMBER
+
+ return _e_phone_number_cxx_normalize (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+ _e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+ 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
@@ -422,3 +449,294 @@ e_phone_number_free (EPhoneNumber *phone_number)
#endif /* ENABLE_PHONENUMBER */
}
+
+/**
+ * match_exit_code:
+ * @phone: phone number string
+ * @max: number of characters to be checked (indexed from 0 or -1 if whole string should be checked)
+ * @up_to (out): number of matched characters (indexed from 0)
+ *
+ * Checks if given phone string begins with valid exit code.
+ * Tries to match exit codes from longest to shortest ones.
+ *
+ * @Returns: TRUE in case that string begins with valid exit code, false otherwise.
+ *
+ * Since: 3.12
+ **/
+static gboolean
+match_exit_code(const char* phone, int max, int* up_to)
+{
+ if (max == 4 || max == -1)
+ {
+ *up_to = 4;
+ if (strncmp (phone, "00414", 5) == 0) return TRUE;
+ if (strncmp (phone, "00468", 5) == 0) return TRUE;
+ if (strncmp (phone, "00456", 5) == 0) return TRUE;
+ if (strncmp (phone, "00444", 5) == 0) return TRUE;
+ }
+
+ if (max == 3 || max == -1)
+ {
+ *up_to = 3;
+ if (strncmp (phone, "0011", 4) == 0) return TRUE;
+ if (strncmp (phone, "0014", 4) == 0) return TRUE;
+ if (strncmp (phone, "0015", 4) == 0) return TRUE;
+ if (strncmp (phone, "0021", 4) == 0) return TRUE;
+ if (strncmp (phone, "0023", 4) == 0) return TRUE;
+ if (strncmp (phone, "0031", 4) == 0) return TRUE;
+ if (strncmp (phone, "1230", 4) == 0) return TRUE;
+ if (strncmp (phone, "1200", 4) == 0) return TRUE;
+ if (strncmp (phone, "1220", 4) == 0) return TRUE;
+ if (strncmp (phone, "1810", 4) == 0) return TRUE;
+ if (strncmp (phone, "1690", 4) == 0) return TRUE;
+ if (strncmp (phone, "1710", 4) == 0) return TRUE;
+ }
+
+ if (max == 2 || max == -1)
+ {
+ *up_to = 2;
+ if (strncmp (phone, "011", 3) == 0) return TRUE;
+ if (strncmp (phone, "001", 3) == 0) return TRUE;
+ if (strncmp (phone, "006", 3) == 0) return TRUE;
+ if (strncmp (phone, "007", 3) == 0) return TRUE;
+ if (strncmp (phone, "008", 3) == 0) return TRUE;
+ if (strncmp (phone, "119", 3) == 0) return TRUE;
+ if (strncmp (phone, "005", 3) == 0) return TRUE;
+ if (strncmp (phone, "007", 3) == 0) return TRUE;
+ if (strncmp (phone, "009", 3) == 0) return TRUE;
+ if (strncmp (phone, "990", 3) == 0) return TRUE;
+ if (strncmp (phone, "994", 3) == 0) return TRUE;
+ if (strncmp (phone, "999", 3) == 0) return TRUE;
+ if (strncmp (phone, "013", 3) == 0) return TRUE;
+ if (strncmp (phone, "014", 3) == 0) return TRUE;
+ if (strncmp (phone, "018", 3) == 0) return TRUE;
+ if (strncmp (phone, "010", 3) == 0) return TRUE;
+ if (strncmp (phone, "009", 3) == 0) return TRUE;
+ if (strncmp (phone, "002", 3) == 0) return TRUE;
+ }
+
+ if (max == 1 || max == -1)
+ {
+ *up_to = 1;
+ if (strncmp (phone, "00", 2) == 0) return TRUE;
+ if (strncmp (phone, "99", 2) == 0) return TRUE;
+ }
+
+ if (max == 0 || max == -1)
+ {
+ *up_to = 0;
+ if (strncmp (phone, "+", 1) == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * match_exit_code_reverse:
+ * @phone: phone number string
+ * @max: number of characters to be checked (indexed from 0 or -1 if whole string should be checked)
+ * @up_to (out): number of matched characters (indexed from 0)
+ *
+ * Checks if given phone string begins with valid exit code.
+ * Tries to match exit codes from shortest to longest ones.
+ *
+ * @Returns: TRUE in case that string begins with valid exit code, false otherwise.
+ *
+ * Since: 3.12
+ **/
+static gboolean
+match_exit_code_reverse(const char* phone, int max, int* up_to)
+{
+ if (max == 0 || max == -1)
+ {
+ *up_to = 0;
+ if (strncmp (phone, "+", 1) == 0) return TRUE;
+ }
+
+ if (max == 1 || max == -1)
+ {
+ *up_to = 1;
+ if (strncmp (phone, "00", 2) == 0) return TRUE;
+ if (strncmp (phone, "99", 2) == 0) return TRUE;
+ }
+
+ if (max == 2 || max == -1)
+ {
+ *up_to = 2;
+ if (strncmp (phone, "011", 3) == 0) return TRUE;
+ if (strncmp (phone, "001", 3) == 0) return TRUE;
+ if (strncmp (phone, "006", 3) == 0) return TRUE;
+ if (strncmp (phone, "007", 3) == 0) return TRUE;
+ if (strncmp (phone, "008", 3) == 0) return TRUE;
+ if (strncmp (phone, "119", 3) == 0) return TRUE;
+ if (strncmp (phone, "005", 3) == 0) return TRUE;
+ if (strncmp (phone, "007", 3) == 0) return TRUE;
+ if (strncmp (phone, "009", 3) == 0) return TRUE;
+ if (strncmp (phone, "990", 3) == 0) return TRUE;
+ if (strncmp (phone, "994", 3) == 0) return TRUE;
+ if (strncmp (phone, "999", 3) == 0) return TRUE;
+ if (strncmp (phone, "013", 3) == 0) return TRUE;
+ if (strncmp (phone, "014", 3) == 0) return TRUE;
+ if (strncmp (phone, "018", 3) == 0) return TRUE;
+ if (strncmp (phone, "010", 3) == 0) return TRUE;
+ if (strncmp (phone, "009", 3) == 0) return TRUE;
+ if (strncmp (phone, "002", 3) == 0) return TRUE;
+ }
+
+ if (max == 3 || max == -1)
+ {
+ *up_to = 3;
+ if (strncmp (phone, "0011", 4) == 0) return TRUE;
+ if (strncmp (phone, "0014", 4) == 0) return TRUE;
+ if (strncmp (phone, "0015", 4) == 0) return TRUE;
+ if (strncmp (phone, "0021", 4) == 0) return TRUE;
+ if (strncmp (phone, "0023", 4) == 0) return TRUE;
+ if (strncmp (phone, "0031", 4) == 0) return TRUE;
+ if (strncmp (phone, "1230", 4) == 0) return TRUE;
+ if (strncmp (phone, "1200", 4) == 0) return TRUE;
+ if (strncmp (phone, "1220", 4) == 0) return TRUE;
+ if (strncmp (phone, "1810", 4) == 0) return TRUE;
+ if (strncmp (phone, "1690", 4) == 0) return TRUE;
+ if (strncmp (phone, "1710", 4) == 0) return TRUE;
+ }
+
+ if (max == 4 || max == -1)
+ {
+ *up_to = 4;
+ if (strncmp (phone, "00414", 5) == 0) return TRUE;
+ if (strncmp (phone, "00468", 5) == 0) return TRUE;
+ if (strncmp (phone, "00456", 5) == 0) return TRUE;
+ if (strncmp (phone, "00444", 5) == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * is_exit_code_and_cc:
+ * @phone: phone number string
+ * @up_to: number of characters to be checked (indexed from 0)
+ *
+ * Checks if given phone string begins with valid exit code and country code.
+ * After sucessfully matching exit code, any further 1-3 digits
+ * will be recognized as valid country code.
+ *
+ * @Returns: TRUE in case that string begins with valid exit code
+ * and country code, false otherwise.
+ *
+ * Since: 3.12
+ **/
+static gboolean
+is_exit_code_and_cc(const char* phone, int up_to)
+{
+ int matched_len = 0;
+ if (match_exit_code(phone, -1, &matched_len))
+ {
+ if (up_to - matched_len -1 >= 0 &&
+ up_to - matched_len - 1 < 3)
+ return TRUE;
+ }
+ if (match_exit_code_reverse(phone, -1, &matched_len))
+ {
+ if (up_to - matched_len -1 >= 0 &&
+ up_to - matched_len - 1 < 3)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * is_trunk:
+ * @phone: phone number string
+ * @up_to: number of characters to be checked (indexed from 0)
+ *
+ * Checks if given phone string begins with trunk code.
+ *
+ * @Returns: TRUE in case that string begins with trunk code.
+ *
+ * Since: 3.12
+ **/
+static gboolean
+is_trunk(const char* phone, int up_to)
+{
+ if (up_to > 1) return FALSE;
+ if (phone[0] == '0' && up_to == 0) return TRUE;
+ if (phone[0] == '0' && phone[1] == '1' && up_to == 1) return TRUE; //Mexico trunk code
+ if (phone[0] == '0' && phone[1] == '6' && up_to == 1) return TRUE; //Hungary trunk code
+ if (phone[0] == '8' && phone[1] == '0' && up_to == 1) return TRUE; //Belarus trunk code
+
+
+ if (phone[0] == '1' && up_to == 0) return TRUE; //Jamaica trunk code
+ if (phone[0] == '8' && up_to == 0) return TRUE; //Kazakhstan,Lituana trunk code
+
+
+ return FALSE;
+}
+
+/**
+ * e_phone_number_equal:
+ * @phone1: first phone number string
+ * @phone2: second phone number string
+ *
+ * Compares two phone number strings.
+ *
+ * @Returns: TRUE in case that phone number matches. false otherwise.
+ *
+ * Since: 3.12
+ **/
+gboolean
+e_phone_number_equal (const gchar *phone1,
+ const gchar *phone2)
+{
+ int a = 0;
+ int b = 0;
+ int matched = 0;
+ if (!phone1 || !phone2) {
+ return FALSE;
+ }
+
+ a = strlen (phone1) - 1;
+ b = strlen (phone2) - 1;
+
+ /*
+ * Check (in reverse order) number of matching digits
+ */
+ while (a >= 0 && b >= 0) {
+ if (phone1[a] != phone2[b]) {
+ break;
+ }
+ a--;
+ b--;
+ matched++;
+ }
+
+ //if both phone numbers matched completly, they are equal
+ if (a < 0 && b < 0) {
+ return TRUE;
+ }
+
+ //if one of numbers matched completly,
+ //and total number of matched digits is greater than 7,
+ //assume also that numbers are equal
+ if (matched >= 7 && (a < 0 || b < 0)) {
+ return TRUE;
+ }
+
+ /*
+ * Check if first phone number begins with trunk code
+ * and second phone number begins exit code + country code
+ */
+ if (is_trunk(phone1, a) && is_exit_code_and_cc(phone2, b)) {
+ return TRUE;
+ }
+
+ /*
+ * Check opposite condition
+ */
+ if (is_trunk(phone2, b) && is_exit_code_and_cc(phone1, a)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/addressbook/libebook-contacts/e-phone-number.h b/addressbook/libebook-contacts/e-phone-number.h
index 5fef30773..c493c959a 100644
--- a/addressbook/libebook-contacts/e-phone-number.h
+++ b/addressbook/libebook-contacts/e-phone-number.h
@@ -206,6 +206,12 @@ GType e_phone_number_get_type (void);
GQuark e_phone_number_error_quark (void);
gboolean e_phone_number_is_supported (void) G_GNUC_CONST;
+gchar * e_phone_number_normalize (const gchar *phone_number,
+ GError **error);
+
+gboolean e_phone_number_equal (const gchar *first_number,
+ const gchar *second_number);
+
gint e_phone_number_get_country_code_for_region
(const gchar *region_code,
GError **error);
diff --git a/addressbook/libedata-book/e-book-backend-sexp.c b/addressbook/libedata-book/e-book-backend-sexp.c
index 6b3f9bd99..c1dfb0fdc 100644
--- a/addressbook/libedata-book/e-book-backend-sexp.c
+++ b/addressbook/libedata-book/e-book-backend-sexp.c
@@ -860,6 +860,38 @@ func_eqphone_short (struct _ESExp *f,
}
static gboolean
+eqphone_is_helper (const gchar *ps1,
+ const gchar *ps2,
+ const gchar *region)
+{
+ gchar *p1, *p2;
+ gboolean res = FALSE;
+ GError *error = NULL;
+
+ p1 = e_phone_number_normalize(ps1, &error);
+ p2 = e_phone_number_normalize(ps2, &error);
+
+ res = e_phone_number_equal (p1, p2);
+
+ g_free (p1);
+ g_free (p2);
+
+ return res;
+}
+
+static ESExpResult *
+func_eqphone_is (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, eqphone_is_helper);
+}
+
+
+static gboolean
regex_helper (const gchar *ps1,
const gchar *ps2,
const gchar *region,
@@ -1255,6 +1287,7 @@ static struct {
{ "eqphone", func_eqphone, 0 },
{ "eqphone_national", func_eqphone_national, 0 },
{ "eqphone_short", func_eqphone_short, 0 },
+ { "eqphone_is", func_eqphone_is, 0 },
{ "regex_normal", func_regex_normal, 0 },
{ "regex_translit", func_regex_normal, 0 },
{ "regex_raw", func_regex_raw, 0 },
diff --git a/addressbook/libedata-book/e-book-sqlite.c b/addressbook/libedata-book/e-book-sqlite.c
index 89ef58468..8d435bff9 100644
--- a/addressbook/libedata-book/e-book-sqlite.c
+++ b/addressbook/libedata-book/e-book-sqlite.c
@@ -277,6 +277,7 @@ ebsql_init_debug (void)
#define EBSQL_FUNC_EQPHONE_EXACT "eqphone_exact"
#define EBSQL_FUNC_EQPHONE_NATIONAL "eqphone_national"
#define EBSQL_FUNC_EQPHONE_SHORT "eqphone_short"
+#define EBSQL_FUNC_EQPHONE_IS "eqphone_is"
/* Fallback collations are generated as with a prefix and an EContactField name */
#define EBSQL_COLLATE_PREFIX "ebsql_"
@@ -290,6 +291,8 @@ ebsql_init_debug (void)
#define EBSQL_SUFFIX_PHONE "phone"
#define EBSQL_SUFFIX_COUNTRY "country"
#define EBSQL_SUFFIX_TRANSLIT "translit"
+#define EBSQL_SUFFIX_REVERSE_PHONE "phone_reverse"
+#define EBSQL_SUFFIX_NORMALIZED_PHONE "phone_normalized"
/* Track EBookIndexType's in a bit mask */
#define INDEX_FLAG(type) (1 << E_BOOK_INDEX_##type)
@@ -687,6 +690,14 @@ summary_field_list_columns (SummaryField *field,
info = column_info_new (field, folderid, EBSQL_SUFFIX_PHONE, "TEXT", NULL, "PINDEX");
columns = g_slist_prepend (columns, info);
+ /* One indexed column for storing reversed phone number */
+ info = column_info_new (field, folderid, EBSQL_SUFFIX_REVERSE_PHONE, "TEXT", NULL, "RINDEX");
+ columns = g_slist_prepend (columns, info);
+
+ /* One string column for storing normalized phone number */
+ info = column_info_new (field, folderid, EBSQL_SUFFIX_NORMALIZED_PHONE, "TEXT", NULL, NULL);
+ columns = g_slist_prepend (columns, info);
+
/* One integer column for storing the country code */
info = column_info_new (field, folderid, EBSQL_SUFFIX_COUNTRY, "INTEGER", "DEFAULT 0", NULL);
columns = g_slist_prepend (columns, info);
@@ -1502,6 +1513,32 @@ ebsql_eqphone_short (sqlite3_context *context,
ebsql_eqphone (context, argc, argv, E_PHONE_NUMBER_MATCH_SHORT);
}
+/* Alternative locale invariant phone number match function: EBSQL_FUNC_EQPHONE_IS */
+static void
+ebsql_eqphone_is (sqlite3_context *context,
+ gint argc,
+ sqlite3_value **argv)
+{
+ const gchar *phone1 = NULL;
+ const gchar *phone2 = NULL;
+ gboolean res = FALSE;
+
+ phone1 = (const gchar *) sqlite3_value_text (argv[0]);
+ phone2 = (const gchar *) sqlite3_value_text (argv[1]);
+
+ if (!phone1 || !phone2) {
+ sqlite3_result_error (context, "No phone number provided", -1);
+ return;
+ }
+
+ res = e_phone_number_equal (phone1, phone2);
+
+ if (res)
+ sqlite3_result_int (context, 1);
+ else
+ sqlite3_result_int (context, 0);
+}
+
/* Implementation of EBSQL_FUNC_FETCH_VCARD (fallback for shallow addressbooks) */
static void
ebsql_fetch_vcard (sqlite3_context *context,
@@ -1543,6 +1580,7 @@ static EbSqlCustomFuncTab ebsql_custom_functions[] = {
{ EBSQL_FUNC_EQPHONE_EXACT, ebsql_eqphone_exact, 2 }, /* eqphone_exact (search_input, column_data) */
{ EBSQL_FUNC_EQPHONE_NATIONAL, ebsql_eqphone_national, 2 }, /* eqphone_national (search_input, column_data) */
{ EBSQL_FUNC_EQPHONE_SHORT, ebsql_eqphone_short, 2 }, /* eqphone_national (search_input, column_data) */
+ { EBSQL_FUNC_EQPHONE_IS, ebsql_eqphone_is, 2 }, /* eqphone_is (search_input, column_data) */
};
/******************************************************
@@ -2162,6 +2200,14 @@ ebsql_introspect_summary (EBookSqlite *ebsql,
computed = INDEX_FLAG (PHONE);
freeme = g_strndup (col, p - col);
col = freeme;
+ } else if ((p = strstr (col, "_" EBSQL_SUFFIX_REVERSE_PHONE)) != NULL) {
+ computed = INDEX_FLAG (PHONE);
+ freeme = g_strndup (col, p - col);
+ col = freeme;
+ } else if ((p = strstr (col, "_" EBSQL_SUFFIX_NORMALIZED_PHONE)) != NULL) {
+ computed = INDEX_FLAG (PHONE);
+ freeme = g_strndup (col, p - col);
+ col = freeme;
} else if ((p = strstr (col, "_" EBSQL_SUFFIX_SORT_KEY)) != NULL) {
computed = INDEX_FLAG (SORT_KEY);
freeme = g_strndup (col, p - col);
@@ -3035,6 +3081,27 @@ remove_leading_zeros (gchar *number)
return trimmed;
}
+static gchar *
+normalize_phone (const gchar *normal)
+{
+ GError *error = NULL;
+ gchar *normalized = normal ? e_phone_number_normalize (normal, &error) : NULL;
+ return normalized;
+}
+
+static gchar *
+reverse_phone (const gchar *normal)
+{
+ gchar *normalized = normalize_phone (normal);
+ gchar *reverse = normalized ? g_utf8_strreverse (normalized, -1) : NULL;
+ gchar *reverse_cut = reverse ? g_strndup (reverse, 5) : NULL;
+
+ g_free (normalized);
+ g_free (reverse);
+
+ return reverse_cut;
+}
+
typedef struct {
gint country_code;
gchar *national;
@@ -3303,6 +3370,8 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql,
if ((field->index & INDEX_FLAG (PHONE)) != 0) {
g_string_append (string, ", value_" EBSQL_SUFFIX_PHONE);
g_string_append (string, ", value_" EBSQL_SUFFIX_COUNTRY);
+ g_string_append (string, ", value_" EBSQL_SUFFIX_REVERSE_PHONE);
+ g_string_append (string, ", value_" EBSQL_SUFFIX_NORMALIZED_PHONE);
}
if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
@@ -3316,6 +3385,8 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql,
if ((field->index & INDEX_FLAG (PHONE)) != 0) {
g_string_append (string, ", :value_" EBSQL_SUFFIX_PHONE);
g_string_append (string, ", :value_" EBSQL_SUFFIX_COUNTRY);
+ g_string_append (string, ", :value_" EBSQL_SUFFIX_REVERSE_PHONE);
+ g_string_append (string, ", :value_" EBSQL_SUFFIX_NORMALIZED_PHONE);
}
if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
@@ -3370,8 +3441,17 @@ ebsql_run_multi_insert_one (EBookSqlite *ebsql,
/* :value_country */
if (ret == SQLITE_OK)
- sqlite3_bind_int (stmt, param_idx++, country_code);
+ ret = sqlite3_bind_int (stmt, param_idx++, country_code);
+
+ /* :value reverse phone */
+ str = reverse_phone (normal);
+ if (ret == SQLITE_OK)
+ ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+ /* :value normalized phone */
+ str = normalize_phone (normal);
+ if (ret == SQLITE_OK)
+ sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
}
if (ret == SQLITE_OK && (field->index & INDEX_FLAG (TRANSLIT)) != 0) {
@@ -3482,6 +3562,15 @@ ebsql_prepare_insert (EBookSqlite *ebsql,
g_string_append (string, ", ");
g_string_append (string, field->dbname);
g_string_append (string, "_" EBSQL_SUFFIX_COUNTRY);
+
+ g_string_append (string, ", ");
+ g_string_append (string, field->dbname);
+ g_string_append (string, "_" EBSQL_SUFFIX_REVERSE_PHONE);
+
+ g_string_append (string, ", ");
+ g_string_append (string, field->dbname);
+ g_string_append (string, "_" EBSQL_SUFFIX_NORMALIZED_PHONE);
+
}
if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) {
@@ -3523,6 +3612,8 @@ ebsql_prepare_insert (EBookSqlite *ebsql,
if ((field->index & INDEX_FLAG (PHONE)) != 0) {
g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_PHONE, field->dbname);
g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_COUNTRY, field->dbname);
+ g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_REVERSE_PHONE, field->dbname);
+ g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_NORMALIZED_PHONE, field->dbname);
}
if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
@@ -3679,7 +3770,15 @@ ebsql_run_insert (EBookSqlite *ebsql,
ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
if (ret == SQLITE_OK)
- sqlite3_bind_int (stmt, param_idx++, country_code);
+ ret = sqlite3_bind_int (stmt, param_idx++, country_code);
+
+ str = reverse_phone (normal);
+ if (ret == SQLITE_OK)
+ ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+
+ str = normalize_phone (normal);
+ if (ret == SQLITE_OK)
+ sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
}
if (ret == SQLITE_OK &&
@@ -3870,6 +3969,7 @@ enum {
(query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER ? "eqphone" : \
(query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER ? "eqphone-national" : \
(query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER ? "eqphone-short" : \
+ (query) == E_BOOK_QUERY_PHONE_NUMBER_IS ? "eqphone-is" : \
(query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-normal" : \
(query) == E_BOOK_QUERY_REGEX_RAW ? "regex-raw" : \
(query) == E_BOOK_QUERY_REGEX_TRANSLIT ? "regex-translit" : \
@@ -3886,7 +3986,8 @@ enum {
#define IS_QUERY_PHONE(query) \
((query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER || \
(query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER || \
- (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER)
+ (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER || \
+ (query) == E_BOOK_QUERY_PHONE_NUMBER_IS)
typedef struct {
guint query; /* EBookQueryTest (extended) */
@@ -4232,6 +4333,7 @@ static const struct {
{ "eqphone", FALSE, E_BOOK_QUERY_EQUALS_PHONE_NUMBER },
{ "eqphone_national", FALSE, E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER },
{ "eqphone_short", FALSE, E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER },
+ { "eqphone_is", FALSE, E_BOOK_QUERY_PHONE_NUMBER_IS },
{ "regex_normal", FALSE, E_BOOK_QUERY_REGEX_NORMAL },
{ "regex_raw", FALSE, E_BOOK_QUERY_REGEX_RAW },
{ "regex_translit", FALSE, E_BOOK_QUERY_REGEX_TRANSLIT },
@@ -4653,6 +4755,7 @@ query_preflight_check (PreflightContext *context,
case E_BOOK_QUERY_EQUALS_PHONE_NUMBER:
case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER:
case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER:
+ case E_BOOK_QUERY_PHONE_NUMBER_IS:
/* Phone number queries are supported so long as they are in the summary,
* libphonenumber is available, and the phone number string is a valid one
@@ -5163,6 +5266,39 @@ field_test_query_eqphone_short (EBookSqlite *ebsql,
}
static void
+field_test_query_eqphone_is (EBookSqlite *ebsql,
+ GString *string,
+ QueryFieldTest *test)
+{
+ SummaryField *field = test->field;
+ QueryPhoneTest *phone_test = (QueryPhoneTest *) test;
+
+ gchar * normalized = normalize_phone (phone_test->value);
+ gchar * reversed = reverse_phone (phone_test->value);
+
+ if ((field->index & INDEX_FLAG (PHONE)) != 0) {
+ /* Only a compound expression if there is a country code */
+ g_string_append_c (string, '(');
+
+ /* Generate: phone = %Q */
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_REVERSE_PHONE);
+ ebsql_string_append_printf (string, " = %Q", reversed);
+
+ g_string_append (string, " AND ");
+ g_string_append (string, EBSQL_FUNC_EQPHONE_IS " (");
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_NORMALIZED_PHONE);
+ ebsql_string_append_printf (string, ", %Q))", normalized);
+ } else {
+ g_string_append (string, EBSQL_FUNC_EQPHONE_IS " (");
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_NORMALIZED_PHONE);
+ ebsql_string_append_printf (string, ", %Q)", normalized);
+ }
+
+ g_free (normalized);
+ g_free (reversed);
+}
+
+static void
field_test_query_regex_normal (EBookSqlite *ebsql,
GString *string,
QueryFieldTest *test)
@@ -5327,6 +5463,7 @@ static const GenerateFieldTest field_test_func_table[] = {
field_test_query_eqphone, /* E_BOOK_QUERY_EQUALS_PHONE_NUMBER */
field_test_query_eqphone_national, /* E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER */
field_test_query_eqphone_short, /* E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER */
+ field_test_query_eqphone_is, /* E_BOOK_QUERY_EQUALS_PHONE_NUMBER_IS */
field_test_query_regex_normal, /* E_BOOK_QUERY_REGEX_NORMAL */
NULL /* Requires fallback */, /* E_BOOK_QUERY_REGEX_RAW */
field_test_query_regex_translit, /* E_BOOK_QUERY_REGEX_TRANSLIT */