diff options
Diffstat (limited to 'addressbook/backend/ebook/e-card-compare.c')
-rw-r--r-- | addressbook/backend/ebook/e-card-compare.c | 528 |
1 files changed, 0 insertions, 528 deletions
diff --git a/addressbook/backend/ebook/e-card-compare.c b/addressbook/backend/ebook/e-card-compare.c deleted file mode 100644 index 19352e4755..0000000000 --- a/addressbook/backend/ebook/e-card-compare.c +++ /dev/null @@ -1,528 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* - * e-card-compare.c - * - * Copyright (C) 2001 Ximian, Inc. - * - * Developed by Jon Trowbridge <trow@ximian.com> - */ - -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA. - */ - -#include <config.h> -#include <ctype.h> -#include <gal/unicode/gunicode.h> -#include "e-book-util.h" -#include "e-card-compare.h" - -/* This is an "optimistic" combiner: the best of the two outcomes is - selected. */ -static ECardMatchType -combine_comparisons (ECardMatchType prev, - ECardMatchType new_info) -{ - if (new_info == E_CARD_MATCH_NOT_APPLICABLE) - return prev; - return (ECardMatchType) MAX ((gint) prev, (gint) new_info); -} - - -/*** Name comparisons ***/ - -/* This *so* doesn't belong here... at least not implemented in a - sucky way like this. But by getting it in here now, I can fix it - up w/o adding a new feature when we are in feature freeze. :-) */ - -/* This is very Anglocentric. */ -static gchar *name_synonyms[][2] = { - { "jon", "john" }, /* Ah, the hacker's perogative */ - { "joseph", "joe" }, - { "robert", "bob" }, - { "richard", "dick" }, - { "william", "bill" }, - { "anthony", "tony" }, - { "michael", "mike" }, - { "eric", "erik" }, - { "elizabeth", "liz" }, - { "jeff", "geoff" }, - { "jeff", "geoffrey" }, - { "jim", "james" }, - { "abigal", "abby" }, - { "amanda", "amy" }, - { "amanda", "manda" }, - { "jennifer", "jenny" }, - { "rebecca", "becca" }, - { "rebecca", "becky" }, - { "anderson", "andersen" }, - /* We could go on and on... */ - { NULL, NULL } -}; - -static gboolean -name_fragment_match (const gchar *a, const gchar *b) -{ - gint i, len_a, len_b; - - /* This will cause "Chris" and "Christopher" to match. */ - len_a = g_utf8_strlen (a, -1); - len_b = g_utf8_strlen (b, -1); - if (!g_utf8_strncasecmp (a, b, MIN (len_a, len_b))) - return TRUE; - - /* Check for nicknames. Yes, the linear search blows. */ - for (i=0; name_synonyms[i][0]; ++i) { - - if (!g_utf8_strcasecmp (name_synonyms[i][0], a) - && !g_utf8_strcasecmp (name_synonyms[i][1], b)) - return TRUE; - - if (!g_utf8_strcasecmp (name_synonyms[i][0], b) - && !g_utf8_strcasecmp (name_synonyms[i][1], a)) - return TRUE; - } - - return FALSE; -} - -ECardMatchType -e_card_compare_name (ECard *card1, ECard *card2) -{ - ECardName *a, *b; - gint matches=0, possible=0; - gboolean given_match = FALSE, additional_match = FALSE, family_match = FALSE; - - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - a = card1->name; - b = card2->name; - - if (a == NULL || b == NULL) - return E_CARD_MATCH_NOT_APPLICABLE; - - if (a->given && b->given) { - ++possible; - if (name_fragment_match (a->given, b->given)) { - ++matches; - given_match = TRUE; - } - } - - if (a->additional && b->additional) { - ++possible; - if (name_fragment_match (a->additional, b->additional)) { - ++matches; - additional_match = TRUE; - } - } - - if (a->family && b->family) { - ++possible; - if (name_fragment_match (a->family, b->family)) { - ++matches; - family_match = TRUE; - } - } - - /* Now look at the # of matches and try to intelligently map - an E_CARD_MATCH_* type to it. Special consideration is given - to family-name matches. */ - - if (possible == 0) - return E_CARD_MATCH_NOT_APPLICABLE; - - if (possible == 1) - return family_match ? E_CARD_MATCH_VAGUE : E_CARD_MATCH_NONE; - - if (possible == matches) - return family_match ? E_CARD_MATCH_EXACT : E_CARD_MATCH_PARTIAL; - - if (possible == matches+1) - return family_match ? E_CARD_MATCH_PARTIAL : E_CARD_MATCH_VAGUE; - - return E_CARD_MATCH_NONE; -} - - -/*** Nickname Comparisons ***/ - -ECardMatchType -e_card_compare_nickname (ECard *card1, ECard *card2) -{ - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - return E_CARD_MATCH_NOT_APPLICABLE; -} - - - -/*** E-mail Comparisons ***/ - -static gboolean -match_email_username (const gchar *addr1, const gchar *addr2) -{ - gint c1, c2; - if (addr1 == NULL || addr2 == NULL) - return FALSE; - - while (*addr1 && *addr2 && *addr1 != '@' && *addr2 != '@') { - c1 = isupper (*addr1) ? tolower (*addr1) : *addr1; - c2 = isupper (*addr2) ? tolower (*addr2) : *addr2; - if (c1 != c2) - return FALSE; - ++addr1; - ++addr2; - } - - return *addr1 == *addr2; -} - -static gboolean -match_email_hostname (const gchar *addr1, const gchar *addr2) -{ - gint c1, c2; - gboolean seen_at1, seen_at2; - if (addr1 == NULL || addr2 == NULL) - return FALSE; - - /* Walk to the end of each string. */ - seen_at1 = FALSE; - if (*addr1) { - while (*addr1) { - if (*addr1 == '@') - seen_at1 = TRUE; - ++addr1; - } - --addr1; - } - - seen_at2 = FALSE; - if (*addr2) { - while (*addr2) { - if (*addr2 == '@') - seen_at2 = TRUE; - ++addr2; - } - --addr2; - } - - if (!seen_at1 && !seen_at2) - return TRUE; - if (!seen_at1 || !seen_at2) - return FALSE; - - while (*addr1 != '@' && *addr2 != '@') { - c1 = isupper (*addr1) ? tolower (*addr1) : *addr1; - c2 = isupper (*addr2) ? tolower (*addr2) : *addr2; - if (c1 != c2) - return FALSE; - --addr1; - --addr2; - } - - /* This will match bob@foo.ximian.com and bob@ximian.com */ - return *addr1 == '.' || *addr2 == '.'; -} - -static ECardMatchType -compare_email_addresses (const gchar *addr1, const gchar *addr2) -{ - if (addr1 == NULL || addr2 == NULL) - return E_CARD_MATCH_NOT_APPLICABLE; - - if (match_email_username (addr1, addr2)) - return match_email_hostname (addr1, addr2) ? E_CARD_MATCH_EXACT : E_CARD_MATCH_PARTIAL; - - return E_CARD_MATCH_NONE; -} - -ECardMatchType -e_card_compare_email (ECard *card1, ECard *card2) -{ - EIterator *i1, *i2; - ECardMatchType match = E_CARD_MATCH_NOT_APPLICABLE; - - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - if (card1->email == NULL || card2->email == NULL) - return E_CARD_MATCH_NOT_APPLICABLE; - - i1 = e_list_get_iterator (card1->email); - i2 = e_list_get_iterator (card2->email); - - /* Do pairwise-comparisons on all of the e-mail addresses. If - we find an exact match, there is no reason to keep - checking. */ - e_iterator_reset (i1); - while (e_iterator_is_valid (i1) && match != E_CARD_MATCH_EXACT) { - const gchar *addr1 = (const gchar *) e_iterator_get (i1); - - e_iterator_reset (i2); - while (e_iterator_is_valid (i2) && match != E_CARD_MATCH_EXACT) { - const gchar *addr2 = (const gchar *) e_iterator_get (i2); - - match = combine_comparisons (match, compare_email_addresses (addr1, addr2)); - - e_iterator_next (i2); - } - - e_iterator_next (i1); - } - - gtk_object_unref (GTK_OBJECT (i1)); - gtk_object_unref (GTK_OBJECT (i2)); - - return match; -} - -ECardMatchType -e_card_compare_address (ECard *card1, ECard *card2) -{ - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - /* Unimplemented */ - - return E_CARD_MATCH_NOT_APPLICABLE; -} - -ECardMatchType -e_card_compare_telephone (ECard *card1, ECard *card2) -{ - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - /* Unimplemented */ - - return E_CARD_MATCH_NOT_APPLICABLE; -} - -ECardMatchType -e_card_compare (ECard *card1, ECard *card2) -{ - ECardMatchType result; - - g_return_val_if_fail (card1 && E_IS_CARD (card1), E_CARD_MATCH_NOT_APPLICABLE); - g_return_val_if_fail (card2 && E_IS_CARD (card2), E_CARD_MATCH_NOT_APPLICABLE); - - result = E_CARD_MATCH_NONE; - result = combine_comparisons (result, e_card_compare_name (card1, card2)); - result = combine_comparisons (result, e_card_compare_nickname (card1, card2)); - result = combine_comparisons (result, e_card_compare_email (card1, card2)); - result = combine_comparisons (result, e_card_compare_address (card1, card2)); - result = combine_comparisons (result, e_card_compare_telephone (card1, card2)); - - return result; -} - -typedef struct _MatchSearchInfo MatchSearchInfo; -struct _MatchSearchInfo { - ECard *card; - GList *avoid; - ECardMatchQueryCallback cb; - gpointer closure; -}; - -static void -match_search_info_free (MatchSearchInfo *info) -{ - if (info) { - gtk_object_unref (GTK_OBJECT (info->card)); - - /* This should already have been deallocated, but just in case... */ - if (info->avoid) { - g_list_foreach (info->avoid, (GFunc) gtk_object_unref, NULL); - g_list_free (info->avoid); - info->avoid = NULL; - } - - g_free (info); - } -} - -static void -simple_query_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure) -{ - MatchSearchInfo *info = (MatchSearchInfo *) closure; - ECardMatchType best_match = E_CARD_MATCH_NONE; - ECard *best_card = NULL; - GList *remaining_cards = NULL; - const GList *i; - - if (status != E_BOOK_SIMPLE_QUERY_STATUS_SUCCESS) { - info->cb (info->card, NULL, E_CARD_MATCH_NONE, info->closure); - match_search_info_free (info); - return; - } - - /* remove the cards we're to avoid from the list, if they're present */ - for (i = cards; i != NULL; i = g_list_next (i)) { - ECard *this_card = E_CARD (i->data); - GList *iterator; - gboolean avoid = FALSE; - for (iterator = info->avoid; iterator; iterator = iterator->next) { - if (!strcmp (e_card_get_id (iterator->data), e_card_get_id (this_card))) { - avoid = TRUE; - break; - } - } - if (!avoid) - remaining_cards = g_list_prepend (remaining_cards, this_card); - } - - remaining_cards = g_list_reverse (remaining_cards); - - for (i = remaining_cards; i != NULL; i = g_list_next (i)) { - ECard *this_card = E_CARD (i->data); - ECardMatchType this_match = e_card_compare (info->card, this_card); - if ((gint)this_match > (gint)best_match) { - best_match = this_match; - best_card = this_card; - } - } - - g_list_free (remaining_cards); - - info->cb (info->card, best_card, best_match, info->closure); - match_search_info_free (info); -} - -#define MAX_QUERY_PARTS 10 -static void -use_common_book_cb (EBook *book, gpointer closure) -{ - MatchSearchInfo *info = (MatchSearchInfo *) closure; - ECard *card = info->card; - gchar *query_parts[MAX_QUERY_PARTS]; - gint p=0; - gchar *query, *qj; - int i; - - if (book == NULL) { - info->cb (info->card, NULL, E_CARD_MATCH_NONE, info->closure); - match_search_info_free (info); - return; - } - - if (card->nickname) - query_parts[p++] = g_strdup_printf ("(beginswith \"nickname\" \"%s\")", card->nickname); - - - if (card->name->given && strlen (card->name->given) > 1) - query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", card->name->given); - - if (card->name->additional && strlen (card->name->additional) > 1) - query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", card->name->additional); - - if (card->name->family && strlen (card->name->family) > 1) - query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", card->name->family); - - - if (card->email) { - EIterator *iter = e_list_get_iterator (card->email); - while (e_iterator_is_valid (iter) && p < MAX_QUERY_PARTS) { - gchar *addr = g_strdup (e_iterator_get (iter)); - if (addr) { - gchar *s = addr; - while (*s) { - if (*s == '@') { - *s = '\0'; - break; - } - ++s; - } - query_parts[p++] = g_strdup_printf ("(beginswith \"email\" \"%s\")", addr); - g_free (addr); - } - e_iterator_next (iter); - } - } - - - - /* Build up our full query from the parts. */ - query_parts[p] = NULL; - qj = g_strjoinv (" ", query_parts); - for(i = 0; query_parts[i] != NULL; i++) - g_free(query_parts[i]); - if (p > 0) { - query = g_strdup_printf ("(or %s)", qj); - g_free (qj); - } else { - query = qj; - } - - e_book_simple_query (book, query, simple_query_cb, info); - - g_free (query); -} - -void -e_card_locate_match (ECard *card, ECardMatchQueryCallback cb, gpointer closure) -{ - MatchSearchInfo *info; - - g_return_if_fail (card && E_IS_CARD (card)); - g_return_if_fail (cb != NULL); - - info = g_new (MatchSearchInfo, 1); - info->card = card; - gtk_object_ref (GTK_OBJECT (card)); - info->cb = cb; - info->closure = closure; - info->avoid = NULL; - - e_book_use_local_address_book (use_common_book_cb, info); -} - -/** - * e_card_locate_match_full: - * @book: The book to look in. If this is NULL, use the main local - * addressbook. - * @card: The card to compare to. - * @avoid: A list of cards to not match. These will not show up in the search. - * @cb: The function to call. - * @closure: The closure to add to the call. - * - * Look for the best match and return it using the ECardMatchQueryCallback. - **/ -void -e_card_locate_match_full (EBook *book, ECard *card, GList *avoid, ECardMatchQueryCallback cb, gpointer closure) -{ - MatchSearchInfo *info; - - g_return_if_fail (card && E_IS_CARD (card)); - g_return_if_fail (cb != NULL); - - info = g_new (MatchSearchInfo, 1); - info->card = card; - gtk_object_ref (GTK_OBJECT (card)); - info->cb = cb; - info->closure = closure; - info->avoid = g_list_copy (avoid); - g_list_foreach (info->avoid, (GFunc) gtk_object_ref, NULL); - - if (book) - use_common_book_cb (book, info); - else - e_book_use_local_address_book (use_common_book_cb, info); -} - |