No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Jon Trowbridge <trow@ximian.com>
18 * Chris Toshok <toshok@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <ctype.h>
29 #include <string.h>
30
31 #include <libedataserverui/libedataserverui.h>
32
33 #include "addressbook/util/eab-book-util.h"
34 #include "eab-contact-compare.h"
35
36 /* This is an "optimistic" combiner: the best of the two outcomes is
37 * selected. */
38 static EABContactMatchType
39 combine_comparisons (EABContactMatchType prev,
40 EABContactMatchType new_info)
41 {
42 if (new_info == EAB_CONTACT_MATCH_NOT_APPLICABLE)
43 return prev;
44 return (EABContactMatchType) MAX ((gint) prev, (gint) new_info);
45 }
46
47 /*** Name comparisons ***/
48
49 /* This *so* doesn't belong here... at least not implemented in a
50 * sucky way like this. But it can be fixed later. */
51
52 /* This is very Anglocentric. */
53 static const gchar *name_synonyms[][2] = {
54 { "jon", "john" }, /* Ah, the hacker's perogative */
55 { "joseph", "joe" },
56 { "robert", "bob" },
57 { "gene", "jean" },
58 { "jesse", "jessie" },
59 { "ian", "iain" },
60 { "richard", "dick" },
61 { "william", "bill" },
62 { "william", "will" },
63 { "anthony", "tony" },
64 { "michael", "mike" },
65 { "eric", "erik" },
66 { "elizabeth", "liz" },
67 { "jeff", "geoff" },
68 { "jeff", "geoffrey" },
69 { "tom", "thomas" },
70 { "dave", "david" },
71 { "jim", "james" },
72 { "abigal", "abby" },
73 { "amanda", "amy" },
74 { "amanda", "manda" },
75 { "jennifer", "jenny" },
76 { "christopher", "chris" },
77 { "rebecca", "becca" },
78 { "rebecca", "becky" },
79 { "anderson", "andersen" },
80 { "johnson", "johnsen" },
81 /* We could go on and on... */
82 /* We should add soundex here. */
83 { NULL, NULL }
84 };
85
86 static gboolean
87 name_fragment_match_with_synonyms (const gchar *a,
88 const gchar *b,
89 gboolean strict)
90 {
91 gint i;
92
93 if (!(a && b && *a && *b))
94 return FALSE;
95
96 if (!e_utf8_casefold_collate (a, b))
97 return TRUE;
98
99 /* Check for nicknames. Yes, the linear search blows. */
100 for (i = 0; name_synonyms[i][0]; ++i) {
101
102 if (!e_utf8_casefold_collate (name_synonyms[i][0], a)
103 && !e_utf8_casefold_collate (name_synonyms[i][1], b))
104 return TRUE;
105
106 if (!e_utf8_casefold_collate (name_synonyms[i][0], b)
107 && !e_utf8_casefold_collate (name_synonyms[i][1], a))
108 return TRUE;
109 }
110
111 return FALSE;
112 }
113
114 EABContactMatchType
115 eab_contact_compare_name_to_string (EContact *contact,
116 const gchar *str)
117 {
118 return eab_contact_compare_name_to_string_full (contact, str, FALSE, NULL, NULL, NULL);
119 }
120
121 EABContactMatchType
122 eab_contact_compare_name_to_string_full (EContact *contact,
123 const gchar *str,
124 gboolean allow_partial_matches,
125 gint *matched_parts_out,
126 EABContactMatchPart *first_matched_part_out,
127 gint *matched_character_count_out)
128 {
129 gchar **namev, **givenv = NULL, **addv = NULL, **familyv = NULL;
130
131 gint matched_parts = EAB_CONTACT_MATCH_PART_NONE;
132 EABContactMatchPart first_matched_part = EAB_CONTACT_MATCH_PART_NONE;
133 EABContactMatchPart this_part_match = EAB_CONTACT_MATCH_PART_NOT_APPLICABLE;
134 EABContactMatchType match_type;
135 EContactName *contact_name;
136
137 gint match_count = 0, matched_character_count = 0, fragment_count;
138 gint i, j;
139 gchar *str_cpy, *s;
140
141 g_return_val_if_fail (E_IS_CONTACT (contact), EAB_CONTACT_MATCH_NOT_APPLICABLE);
142
143 if (!e_contact_get_const (contact, E_CONTACT_FULL_NAME))
144 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
145 if (str == NULL)
146 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
147
148 str_cpy = s = g_strdup (str);
149 while (*s) {
150 if (*s == ',' || *s == '"')
151 *s = ' ';
152 ++s;
153 }
154 namev = g_strsplit (str_cpy, " ", 0);
155 g_free (str_cpy);
156
157 contact_name = e_contact_get (contact, E_CONTACT_NAME);
158
159 if (contact_name->given)
160 givenv = g_strsplit (contact_name->given, " ", 0);
161 if (contact_name->additional)
162 addv = g_strsplit (contact_name->additional, " ", 0);
163 if (contact_name->family)
164 familyv = g_strsplit (contact_name->family, " ", 0);
165
166 e_contact_name_free (contact_name);
167
168 fragment_count = 0;
169 for (i = 0; givenv && givenv[i]; ++i)
170 ++fragment_count;
171 for (i = 0; addv && addv[i]; ++i)
172 ++fragment_count;
173 for (i = 0; familyv && familyv[i]; ++i)
174 ++fragment_count;
175
176 for (i = 0; namev[i] && this_part_match != EAB_CONTACT_MATCH_PART_NONE; ++i) {
177
178 if (*namev[i]) {
179
180 this_part_match = EAB_CONTACT_MATCH_PART_NONE;
181
182 /* When we are allowing partials, we are strict about the matches we allow.
183 * Does this make sense? Not really, but it does the right thing for the purposes
184 * of completion. */
185
186 if (givenv && this_part_match == EAB_CONTACT_MATCH_PART_NONE) {
187 for (j = 0; givenv[j]; ++j) {
188 if (name_fragment_match_with_synonyms (givenv[j], namev[i], allow_partial_matches)) {
189
190 this_part_match = EAB_CONTACT_MATCH_PART_GIVEN_NAME;
191
192 /* We remove a piece of a name once it has been matched against, so
193 * that "john john" won't match "john doe". */
194 g_free (givenv[j]);
195 givenv[j] = g_strdup ("");
196 break;
197 }
198 }
199 }
200
201 if (addv && this_part_match == EAB_CONTACT_MATCH_PART_NONE) {
202 for (j = 0; addv[j]; ++j) {
203 if (name_fragment_match_with_synonyms (addv[j], namev[i], allow_partial_matches)) {
204
205 this_part_match = EAB_CONTACT_MATCH_PART_ADDITIONAL_NAME;
206
207 g_free (addv[j]);
208 addv[j] = g_strdup ("");
209 break;
210 }
211 }
212 }
213
214 if (familyv && this_part_match == EAB_CONTACT_MATCH_PART_NONE) {
215 for (j = 0; familyv[j]; ++j) {
216 if (allow_partial_matches ? name_fragment_match_with_synonyms (familyv[j], namev[i], allow_partial_matches)
217 : !e_utf8_casefold_collate (familyv[j], namev[i])) {
218
219 this_part_match = EAB_CONTACT_MATCH_PART_FAMILY_NAME;
220
221 g_free (familyv[j]);
222 familyv[j] = g_strdup ("");
223 break;
224 }
225 }
226 }
227
228 if (this_part_match != EAB_CONTACT_MATCH_PART_NONE) {
229 ++match_count;
230 matched_character_count += g_utf8_strlen (namev[i], -1);
231 matched_parts |= this_part_match;
232 if (first_matched_part == EAB_CONTACT_MATCH_PART_NONE)
233 first_matched_part = this_part_match;
234 }
235 }
236 }
237
238 match_type = EAB_CONTACT_MATCH_NONE;
239
240 if (this_part_match != EAB_CONTACT_MATCH_PART_NONE) {
241
242 if (match_count > 0)
243 match_type = EAB_CONTACT_MATCH_VAGUE;
244
245 if (fragment_count == match_count) {
246
247 match_type = EAB_CONTACT_MATCH_EXACT;
248
249 } else if (fragment_count == match_count + 1) {
250
251 match_type = EAB_CONTACT_MATCH_PARTIAL;
252
253 }
254 }
255
256 if (matched_parts_out)
257 *matched_parts_out = matched_parts;
258 if (first_matched_part_out)
259 *first_matched_part_out = first_matched_part;
260 if (matched_character_count_out)
261 *matched_character_count_out = matched_character_count;
262
263 g_strfreev (namev);
264 g_strfreev (givenv);
265 g_strfreev (addv);
266 g_strfreev (familyv);
267
268 return match_type;
269 }
270
271 EABContactMatchType
272 eab_contact_compare_file_as (EContact *contact1,
273 EContact *contact2)
274 {
275 EABContactMatchType match_type;
276 gchar *a, *b;
277
278 g_return_val_if_fail (E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
279 g_return_val_if_fail (E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
280
281 a = e_contact_get (contact1, E_CONTACT_FILE_AS);
282 b = e_contact_get (contact2, E_CONTACT_FILE_AS);
283
284 if (a == NULL || b == NULL) {
285 g_free (a);
286 g_free (b);
287 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
288 }
289
290 if (!strcmp (a, b))
291 match_type = EAB_CONTACT_MATCH_EXACT;
292 else if (g_utf8_validate (a, -1, NULL) && g_utf8_validate (b, -1, NULL) &&
293 !g_utf8_collate (a, b))
294 match_type = EAB_CONTACT_MATCH_PARTIAL;
295 else
296 match_type = EAB_CONTACT_MATCH_NONE;
297
298 g_free (a);
299 g_free (b);
300 return match_type;
301 }
302
303 EABContactMatchType
304 eab_contact_compare_name (EContact *contact1,
305 EContact *contact2)
306 {
307 EContactName *a, *b;
308 gint matches = 0, possible = 0;
309 gboolean family_match = FALSE;
310
311 g_return_val_if_fail (E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
312 g_return_val_if_fail (E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
313
314 a = e_contact_get (contact1, E_CONTACT_NAME);
315 b = e_contact_get (contact2, E_CONTACT_NAME);
316
317 if (a == NULL || b == NULL) {
318 g_free (a);
319 g_free (b);
320 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
321 }
322
323 if (a->given && b->given && *a->given && *b->given) {
324 ++possible;
325 if (name_fragment_match_with_synonyms (a->given, b->given, FALSE /* both inputs are complete */)) {
326 ++matches;
327 }
328 }
329
330 if (a->additional && b->additional && *a->additional && *b->additional) {
331 ++possible;
332 if (name_fragment_match_with_synonyms (a->additional, b->additional, FALSE /* both inputs are complete */)) {
333 ++matches;
334 }
335 }
336
337 if (a->family && b->family && *a->family && *b->family) {
338 ++possible;
339 /* We don't allow "loose matching" (i.e. John vs. Jon) on family names */
340 if (!e_utf8_casefold_collate (a->family, b->family)) {
341 ++matches;
342 family_match = TRUE;
343 }
344 }
345
346 e_contact_name_free (a);
347 e_contact_name_free (b);
348
349 /* Now look at the # of matches and try to intelligently map
350 * an EAB_CONTACT_MATCH_* type to it. Special consideration is given
351 * to family-name matches. */
352
353 if (possible == 0)
354 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
355
356 if (possible == 1)
357 return family_match ? EAB_CONTACT_MATCH_VAGUE : EAB_CONTACT_MATCH_NONE;
358
359 if (possible == matches)
360 return family_match ? EAB_CONTACT_MATCH_EXACT : EAB_CONTACT_MATCH_PARTIAL;
361
362 if (possible == matches + 1)
363 return family_match ? EAB_CONTACT_MATCH_VAGUE : EAB_CONTACT_MATCH_NONE;
364
365 return EAB_CONTACT_MATCH_NONE;
366 }
367
368 /*** Nickname Comparisons ***/
369
370 EABContactMatchType
371 eab_contact_compare_nickname (EContact *contact1,
372 EContact *contact2)
373 {
374 g_return_val_if_fail (contact1 && E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
375 g_return_val_if_fail (contact2 && E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
376
377 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
378 }
379
380 /*** E-mail Comparisons ***/
381
382 static gboolean
383 match_email_username (const gchar *addr1,
384 const gchar *addr2)
385 {
386 gint c1, c2;
387 if (addr1 == NULL || addr2 == NULL)
388 return FALSE;
389
390 while (*addr1 && *addr2 && *addr1 != '@' && *addr2 != '@') {
391 c1 = isupper (*addr1) ? tolower (*addr1) : *addr1;
392 c2 = isupper (*addr2) ? tolower (*addr2) : *addr2;
393 if (c1 != c2)
394 return FALSE;
395 ++addr1;
396 ++addr2;
397 }
398
399 return *addr1 == *addr2;
400 }
401
402 static gboolean
403 match_email_hostname (const gchar *addr1,
404 const gchar *addr2)
405 {
406 gint c1, c2;
407 gboolean seen_at1, seen_at2;
408 if (addr1 == NULL || addr2 == NULL)
409 return FALSE;
410
411 /* Walk to the end of each string. */
412 seen_at1 = FALSE;
413 if (*addr1) {
414 while (*addr1) {
415 if (*addr1 == '@')
416 seen_at1 = TRUE;
417 ++addr1;
418 }
419 --addr1;
420 }
421
422 seen_at2 = FALSE;
423 if (*addr2) {
424 while (*addr2) {
425 if (*addr2 == '@')
426 seen_at2 = TRUE;
427 ++addr2;
428 }
429 --addr2;
430 }
431
432 if (!seen_at1 && !seen_at2)
433 return TRUE;
434 if (!seen_at1 || !seen_at2)
435 return FALSE;
436
437 while (*addr1 != '@' && *addr2 != '@') {
438 c1 = isupper (*addr1) ? tolower (*addr1) : *addr1;
439 c2 = isupper (*addr2) ? tolower (*addr2) : *addr2;
440 if (c1 != c2)
441 return FALSE;
442 --addr1;
443 --addr2;
444 }
445 if ((*addr1 == '@' && *addr2 != '@') || (*addr2 == '@' && *addr1 != '@'))
446 return FALSE;
447
448 return TRUE;
449 }
450
451 static EABContactMatchType
452 compare_email_addresses (const gchar *addr1,
453 const gchar *addr2)
454 {
455 if (addr1 == NULL || *addr1 == 0 ||
456 addr2 == NULL || *addr2 == 0)
457 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
458
459 if (match_email_username (addr1, addr2))
460 return match_email_hostname (addr1, addr2) ? EAB_CONTACT_MATCH_EXACT : EAB_CONTACT_MATCH_VAGUE;
461
462 return EAB_CONTACT_MATCH_NONE;
463 }
464
465 EABContactMatchType
466 eab_contact_compare_email (EContact *contact1,
467 EContact *contact2)
468 {
469 EABContactMatchType match = EAB_CONTACT_MATCH_NOT_APPLICABLE;
470 GList *contact1_email, *contact2_email;
471 GList *i1, *i2;
472
473 g_return_val_if_fail (contact1 && E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
474 g_return_val_if_fail (contact2 && E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
475
476 contact1_email = e_contact_get (contact1, E_CONTACT_EMAIL);
477 contact2_email = e_contact_get (contact2, E_CONTACT_EMAIL);
478
479 if (contact1_email == NULL || contact2_email == NULL) {
480 g_list_foreach (contact1_email, (GFunc) g_free, NULL);
481 g_list_free (contact1_email);
482
483 g_list_foreach (contact2_email, (GFunc) g_free, NULL);
484 g_list_free (contact2_email);
485 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
486 }
487
488 i1 = contact1_email;
489
490 /* Do pairwise-comparisons on all of the e-mail addresses. If
491 * we find an exact match, there is no reason to keep
492 * checking. */
493 while (i1 && match != EAB_CONTACT_MATCH_EXACT) {
494 gchar *addr1 = (gchar *) i1->data;
495
496 i2 = contact2_email;
497 while (i2 && match != EAB_CONTACT_MATCH_EXACT) {
498 gchar *addr2 = (gchar *) i2->data;
499
500 match = combine_comparisons (match, compare_email_addresses (addr1, addr2));
501
502 i2 = i2->next;
503 }
504
505 i1 = i1->next;
506 }
507
508 g_list_foreach (contact1_email, (GFunc) g_free, NULL);
509 g_list_free (contact1_email);
510
511 g_list_foreach (contact2_email, (GFunc) g_free, NULL);
512 g_list_free (contact2_email);
513
514 return match;
515 }
516
517 EABContactMatchType
518 eab_contact_compare_address (EContact *contact1,
519 EContact *contact2)
520 {
521 g_return_val_if_fail (contact1 && E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
522 g_return_val_if_fail (contact2 && E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
523
524 /* Unimplemented */
525
526 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
527 }
528
529 EABContactMatchType
530 eab_contact_compare_telephone (EContact *contact1,
531 EContact *contact2)
532 {
533 g_return_val_if_fail (contact1 && E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
534 g_return_val_if_fail (contact2 && E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
535
536 /* Unimplemented */
537
538 return EAB_CONTACT_MATCH_NOT_APPLICABLE;
539 }
540
541 EABContactMatchType
542 eab_contact_compare (EContact *contact1,
543 EContact *contact2)
544 {
545 EABContactMatchType result;
546
547 g_return_val_if_fail (contact1 && E_IS_CONTACT (contact1), EAB_CONTACT_MATCH_NOT_APPLICABLE);
548 g_return_val_if_fail (contact2 && E_IS_CONTACT (contact2), EAB_CONTACT_MATCH_NOT_APPLICABLE);
549
550 result = EAB_CONTACT_MATCH_NONE;
551 if (!e_contact_get (contact1, E_CONTACT_IS_LIST)) {
552 result = combine_comparisons (result, eab_contact_compare_name (contact1, contact2));
553 result = combine_comparisons (result, eab_contact_compare_nickname (contact1, contact2));
554 if (!e_contact_get (contact2, E_CONTACT_IS_LIST))
555 result = combine_comparisons (result, eab_contact_compare_email (contact1, contact2));
556 result = combine_comparisons (result, eab_contact_compare_address (contact1, contact2));
557 result = combine_comparisons (result, eab_contact_compare_telephone (contact1, contact2));
558 }
559 result = combine_comparisons (result, eab_contact_compare_file_as (contact1, contact2));
560
561 return result;
562 }
563
564 typedef struct _MatchSearchInfo MatchSearchInfo;
565 struct _MatchSearchInfo {
566 EContact *contact;
567 GList *avoid;
568 EABContactMatchQueryCallback cb;
569 gpointer closure;
570 };
571
572 static void
573 match_search_info_free (MatchSearchInfo *info)
574 {
575 if (info) {
576 g_object_unref (info->contact);
577
578 /* This should already have been deallocated, but just in case... */
579 if (info->avoid) {
580 g_list_foreach (info->avoid, (GFunc) g_object_unref, NULL);
581 g_list_free (info->avoid);
582 info->avoid = NULL;
583 }
584
585 g_free (info);
586 }
587 }
588
589 static void
590 query_cb (GObject *source_object,
591 GAsyncResult *result,
592 gpointer user_data)
593 {
594 MatchSearchInfo *info = (MatchSearchInfo *) user_data;
595 EABContactMatchType best_match = EAB_CONTACT_MATCH_NONE;
596 EContact *best_contact = NULL;
597 EBookClient *book_client = E_BOOK_CLIENT (source_object);
598 GSList *remaining_contacts = NULL;
599 GSList *contacts = NULL;
600 GError *error = NULL;
601 const GSList *ii;
602
603 if (result != NULL)
604 e_book_client_get_contacts_finish (
605 book_client, result, &contacts, &error);
606
607 if (error != NULL) {
608 g_warning (
609 "%s: Failed to get contacts: %s\n",
610 G_STRFUNC, error->message);
611 g_error_free (error);
612
613 info->cb (
614 info->contact, NULL,
615 EAB_CONTACT_MATCH_NONE,
616 info->closure);
617
618 match_search_info_free (info);
619 g_object_unref (book_client);
620 return;
621 }
622
623 /* remove the contacts we're to avoid from the list, if they're present */
624 for (ii = contacts; ii != NULL; ii = g_slist_next (ii)) {
625 EContact *this_contact = E_CONTACT (ii->data);
626 const gchar *this_uid;
627 GList *iterator;
628 gboolean avoid = FALSE;
629
630 this_uid = e_contact_get_const (this_contact, E_CONTACT_UID);
631 if (!this_uid)
632 continue;
633
634 for (iterator = info->avoid; iterator; iterator = iterator->next) {
635 const gchar *avoid_uid;
636
637 avoid_uid = e_contact_get_const (iterator->data, E_CONTACT_UID);
638 if (!avoid_uid)
639 continue;
640
641 if (!strcmp (avoid_uid, this_uid)) {
642 avoid = TRUE;
643 break;
644 }
645 }
646 if (!avoid)
647 remaining_contacts = g_slist_prepend (remaining_contacts, g_object_ref (this_contact));
648 }
649
650 remaining_contacts = g_slist_reverse (remaining_contacts);
651
652 for (ii = remaining_contacts; ii != NULL; ii = g_slist_next (ii)) {
653 EContact *this_contact = E_CONTACT (ii->data);
654 EABContactMatchType this_match = eab_contact_compare (info->contact, this_contact);
655 if ((gint) this_match > (gint) best_match) {
656 best_match = this_match;
657 best_contact = this_contact;
658 }
659 }
660
661 if (best_contact)
662 best_contact = g_object_ref (best_contact);
663
664 e_client_util_free_object_slist (contacts);
665 e_client_util_free_object_slist (remaining_contacts);
666
667 info->cb (info->contact, best_contact, best_match, info->closure);
668 match_search_info_free (info);
669 g_object_unref (book_client);
670 if (best_contact)
671 g_object_unref (best_contact);
672 }
673
674 #define MAX_QUERY_PARTS 10
675 static void
676 use_common_book_client (EBookClient *book_client,
677 MatchSearchInfo *info)
678 {
679 EContact *contact = info->contact;
680 EContactName *contact_name;
681 GList *contact_email;
682 gchar *query_parts[MAX_QUERY_PARTS];
683 gint p = 0;
684 gchar *contact_file_as, *qj;
685 EBookQuery *query = NULL;
686 gint i;
687
688 if (book_client == NULL) {
689 info->cb (info->contact, NULL, EAB_CONTACT_MATCH_NONE, info->closure);
690 match_search_info_free (info);
691 return;
692 }
693
694 contact_file_as = e_contact_get (contact, E_CONTACT_FILE_AS);
695 if (contact_file_as) {
696 query_parts[p++] = g_strdup_printf ("(contains \"file_as\" \"%s\")", contact_file_as);
697 g_free (contact_file_as);
698 }
699
700 if (!e_contact_get (contact, E_CONTACT_IS_LIST)) {
701 contact_name = e_contact_get (contact, E_CONTACT_NAME);
702 if (contact_name) {
703 if (contact_name->given && *contact_name->given)
704 query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", contact_name->given);
705
706 if (contact_name->additional && *contact_name->additional)
707 query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", contact_name->additional);
708
709 if (contact_name->family && *contact_name->family)
710 query_parts[p++] = g_strdup_printf ("(contains \"full_name\" \"%s\")", contact_name->family);
711
712 e_contact_name_free (contact_name);
713 }
714
715 contact_email = e_contact_get (contact, E_CONTACT_EMAIL);
716 if (contact_email) {
717 GList *iter;
718 for (iter = contact_email; iter && p < MAX_QUERY_PARTS; iter = iter->next) {
719 gchar *addr = g_strdup (iter->data);
720 if (addr && *addr) {
721 gchar *s = addr;
722 while (*s) {
723 if (*s == '@') {
724 *s = '\0';
725 break;
726 }
727 ++s;
728 }
729 query_parts[p++] = g_strdup_printf ("(beginswith \"email\" \"%s\")", addr);
730 g_free (addr);
731 }
732 }
733 }
734 g_list_foreach (contact_email, (GFunc) g_free, NULL);
735 g_list_free (contact_email);
736 }
737
738 /* Build up our full query from the parts. */
739 query_parts[p] = NULL;
740 qj = g_strjoinv (" ", query_parts);
741 for (i = 0; query_parts[i] != NULL; i++)
742 g_free (query_parts[i]);
743 if (p > 1) {
744 gchar *s;
745 s = g_strdup_printf ("(or %s)", qj);
746 query = e_book_query_from_string (s);
747 g_free (s);
748 }
749 else if (p == 1) {
750 query = e_book_query_from_string (qj);
751 }
752 else {
753 query = NULL;
754 }
755
756 if (query) {
757 gchar *query_str = e_book_query_to_string (query);
758
759 e_book_client_get_contacts (book_client, query_str, NULL, query_cb, info);
760
761 g_free (query_str);
762 } else
763 query_cb (G_OBJECT (book_client), NULL, info);
764
765 g_free (qj);
766 if (query)
767 e_book_query_unref (query);
768 }
769
770 static void
771 book_loaded_cb (GObject *source_object,
772 GAsyncResult *result,
773 gpointer user_data)
774 {
775 ESource *source = E_SOURCE (source_object);
776 MatchSearchInfo *info = user_data;
777 EClient *client = NULL;
778
779 e_client_utils_open_new_finish (source, result, &client, NULL);
780
781 /* Client may be NULL; don't use a type cast macro. */
782 use_common_book_client ((EBookClient *) client, info);
783 }
784
785 void
786 eab_contact_locate_match (ESourceRegistry *registry,
787 EContact *contact,
788 EABContactMatchQueryCallback cb,
789 gpointer closure)
790 {
791 eab_contact_locate_match_full (
792 registry, NULL, contact, NULL, cb, closure);
793 }
794
795 /**
796 * e_contact_locate_match_full:
797 * @registry: an #ESourceRegistry
798 * @book: The book to look in. If this is NULL, use the default
799 * addressbook.
800 * @contact: The contact to compare to.
801 * @avoid: A list of contacts to not match. These will not show up in the search.
802 * @cb: The function to call.
803 * @closure: The closure to add to the call.
804 *
805 * Look for the best match and return it using the EABContactMatchQueryCallback.
806 **/
807 void
808 eab_contact_locate_match_full (ESourceRegistry *registry,
809 EBookClient *book_client,
810 EContact *contact,
811 GList *avoid,
812 EABContactMatchQueryCallback cb,
813 gpointer closure)
814 {
815 MatchSearchInfo *info;
816 ESource *source;
817
818 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
819 g_return_if_fail (E_IS_CONTACT (contact));
820 g_return_if_fail (cb != NULL);
821
822 info = g_new0 (MatchSearchInfo, 1);
823 info->contact = g_object_ref (contact);
824 info->cb = cb;
825 info->closure = closure;
826 info->avoid = g_list_copy (avoid);
827 g_list_foreach (info->avoid, (GFunc) g_object_ref, NULL);
828
829 if (book_client) {
830 use_common_book_client (g_object_ref (book_client), info);
831 return;
832 }
833
834 source = e_source_registry_ref_default_address_book (registry);
835
836 e_client_utils_open_new (
837 source, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
838 book_loaded_cb, info);
839
840 g_object_unref (source);
841 }