1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * pas-backend-card-sexp.c
4 * Copyright 1999, 2000, 2001, Ximian, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License, version 2, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 #include "libedataserver/e-sexp.h"
23 #include "libedataserver/e-data-server-util.h"
24 #include "e-book-backend-sexp.h"
26 static GObjectClass *parent_class;
28 typedef struct _SearchContext SearchContext;
30 struct _EBookBackendSExpPrivate {
32 SearchContext *search_context;
35 struct _SearchContext {
40 compare_im (EContact *contact, const char *str,
41 char *(*compare)(const char*, const char*),
42 EContactField im_field)
45 gboolean found_it = FALSE;
47 aims = e_contact_get (contact, im_field);
49 for (l = aims; l != NULL; l = l->next) {
50 char *im = (char *) l->data;
52 if (im && compare (im, str)) {
58 g_list_foreach (aims, (GFunc)g_free, NULL);
65 compare_im_aim (EContact *contact, const char *str,
66 char *(*compare)(const char*, const char*))
68 return compare_im (contact, str, compare, E_CONTACT_IM_AIM);
72 compare_im_msn (EContact *contact, const char *str,
73 char *(*compare)(const char*, const char*))
75 return compare_im (contact, str, compare, E_CONTACT_IM_MSN);
79 compare_im_icq (EContact *contact, const char *str,
80 char *(*compare)(const char*, const char*))
82 return compare_im (contact, str, compare, E_CONTACT_IM_ICQ);
86 compare_im_yahoo (EContact *contact, const char *str,
87 char *(*compare)(const char*, const char*))
89 return compare_im (contact, str, compare, E_CONTACT_IM_YAHOO);
93 compare_im_gadugadu (EContact *contact, const char *str,
94 char *(*compare)(const char*, const char*))
96 return compare_im (contact, str, compare, E_CONTACT_IM_GADUGADU);
101 compare_im_jabber (EContact *contact, const char *str,
102 char *(*compare)(const char*, const char*))
104 return compare_im (contact, str, compare, E_CONTACT_IM_JABBER);
108 compare_im_groupwise (EContact *contact, const char *str,
109 char *(*compare)(const char*, const char*))
111 return compare_im (contact, str, compare, E_CONTACT_IM_GROUPWISE);
115 compare_email (EContact *contact, const char *str,
116 char *(*compare)(const char*, const char*))
120 for (i = E_CONTACT_EMAIL_1; i <= E_CONTACT_EMAIL_4; i ++) {
121 const char *email = e_contact_get_const (contact, i);
123 if (email && compare(email, str))
131 compare_phone (EContact *contact, const char *str,
132 char *(*compare)(const char*, const char*))
137 for (i = E_CONTACT_FIRST_PHONE_ID; i <= E_CONTACT_LAST_PHONE_ID; i ++) {
138 char *phone = e_contact_get (contact, i);
140 rv = phone && compare(phone, str);
151 compare_name (EContact *contact, const char *str,
152 char *(*compare)(const char*, const char*))
156 name = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
157 if (name && compare (name, str))
160 name = e_contact_get_const (contact, E_CONTACT_FAMILY_NAME);
161 if (name && compare (name, str))
164 name = e_contact_get_const (contact, E_CONTACT_GIVEN_NAME);
165 if (name && compare (name, str))
168 name = e_contact_get_const (contact, E_CONTACT_NICKNAME);
169 if (name && compare (name, str))
176 compare_address (EContact *contact, const char *str,
177 char *(*compare)(const char*, const char*))
183 for (i = E_CONTACT_FIRST_ADDRESS_ID; i <= E_CONTACT_LAST_ADDRESS_ID; i ++) {
184 EContactAddress *address = e_contact_get (contact, i);
186 rv = (address->po && compare(address->po, str)) ||
187 (address->street && compare(address->street, str)) ||
188 (address->ext && compare(address->ext, str)) ||
189 (address->locality && compare(address->locality, str)) ||
190 (address->region && compare(address->region, str)) ||
191 (address->code && compare(address->code, str)) ||
192 (address->country && compare(address->country, str));
194 e_contact_address_free (address);
206 compare_category (EContact *contact, const char *str,
207 char *(*compare)(const char*, const char*))
211 gboolean ret_val = FALSE;
213 categories = e_contact_get (contact, E_CONTACT_CATEGORY_LIST);
215 for (iterator = categories; iterator; iterator = iterator->next) {
216 const char *category = iterator->data;
218 if (compare(category, str)) {
224 g_list_foreach (categories, (GFunc)g_free, NULL);
225 g_list_free (categories);
230 static struct prop_info {
231 EContactField field_id;
232 const char *query_prop;
233 #define PROP_TYPE_NORMAL 0x01
234 #define PROP_TYPE_LIST 0x02
236 gboolean (*list_compare)(EContact *contact, const char *str,
237 char *(*compare)(const char*, const char*));
239 } prop_info_table[] = {
240 #define NORMAL_PROP(f,q) {f, q, PROP_TYPE_NORMAL, NULL}
241 #define LIST_PROP(q,c) {0, q, PROP_TYPE_LIST, c}
243 /* query prop, type, list compare function */
244 NORMAL_PROP ( E_CONTACT_FILE_AS, "file_as" ),
245 NORMAL_PROP ( E_CONTACT_UID, "id" ),
246 LIST_PROP ( "full_name", compare_name), /* not really a list, but we need to compare both full and surname */
247 NORMAL_PROP ( E_CONTACT_HOMEPAGE_URL, "url"),
248 NORMAL_PROP ( E_CONTACT_BLOG_URL, "blog_url"),
249 NORMAL_PROP ( E_CONTACT_CALENDAR_URI, "calurl"),
250 NORMAL_PROP ( E_CONTACT_FREEBUSY_URL, "fburl"),
251 NORMAL_PROP ( E_CONTACT_ICS_CALENDAR, "icscalendar"),
252 NORMAL_PROP ( E_CONTACT_VIDEO_URL, "video_url"),
254 NORMAL_PROP ( E_CONTACT_MAILER, "mailer"),
255 NORMAL_PROP ( E_CONTACT_ORG, "org"),
256 NORMAL_PROP ( E_CONTACT_ORG_UNIT, "org_unit"),
257 NORMAL_PROP ( E_CONTACT_OFFICE, "office"),
258 NORMAL_PROP ( E_CONTACT_TITLE, "title"),
259 NORMAL_PROP ( E_CONTACT_ROLE, "role"),
260 NORMAL_PROP ( E_CONTACT_MANAGER, "manager"),
261 NORMAL_PROP ( E_CONTACT_ASSISTANT, "assistant"),
262 NORMAL_PROP ( E_CONTACT_NICKNAME, "nickname"),
263 NORMAL_PROP ( E_CONTACT_SPOUSE, "spouse" ),
264 NORMAL_PROP ( E_CONTACT_NOTE, "note"),
265 LIST_PROP ( "im_aim", compare_im_aim ),
266 LIST_PROP ( "im_msn", compare_im_msn ),
267 LIST_PROP ( "im_icq", compare_im_icq ),
268 LIST_PROP ( "im_jabber", compare_im_jabber ),
269 LIST_PROP ( "im_yahoo", compare_im_yahoo ),
270 LIST_PROP ( "im_gadugadu", compare_im_gadugadu ),
271 LIST_PROP ( "im_groupwise", compare_im_groupwise ),
272 LIST_PROP ( "email", compare_email ),
273 LIST_PROP ( "phone", compare_phone ),
274 LIST_PROP ( "address", compare_address ),
275 LIST_PROP ( "category_list", compare_category ),
277 static int num_prop_infos = sizeof(prop_info_table) / sizeof(prop_info_table[0]);
280 entry_compare(SearchContext *ctx, struct _ESExp *f,
281 int argc, struct _ESExpResult **argv,
282 char *(*compare)(const char*, const char*))
288 && argv[0]->type == ESEXP_RES_STRING
289 && argv[1]->type == ESEXP_RES_STRING) {
291 struct prop_info *info = NULL;
295 propname = argv[0]->value.string;
297 any_field = !strcmp(propname, "x-evolution-any-field");
298 for (i = 0; i < num_prop_infos; i ++) {
300 || !strcmp (prop_info_table[i].query_prop, propname)) {
301 info = &prop_info_table[i];
303 if (any_field && info->field_id == E_CONTACT_UID) {
304 /* We need to skip UID from any field contains search
305 * any-field search should be supported for the
306 * visible fields only.
310 else if (info->prop_type == PROP_TYPE_NORMAL) {
311 const char *prop = NULL;
312 /* straight string property matches */
314 prop = e_contact_get_const (ctx->contact, info->field_id);
316 if (prop && compare(prop, argv[1]->value.string)) {
319 if ((!prop) && compare("", argv[1]->value.string)) {
323 else if (info->prop_type == PROP_TYPE_LIST) {
324 /* the special searches that match any of the list elements */
325 truth = info->list_compare (ctx->contact, argv[1]->value.string, compare);
328 /* if we're looking at all fields and find a match,
329 or if we're just looking at this one field,
331 if ((any_field && truth)
338 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
339 r->value.bool = truth;
345 func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
347 SearchContext *ctx = data;
349 return entry_compare (ctx, f, argc, argv, (char *(*)(const char*, const char*)) e_util_utf8_strstrcase);
353 is_helper (const char *s1, const char *s2)
355 if (!e_util_utf8_strcasecmp (s1, s2))
362 func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
364 SearchContext *ctx = data;
366 return entry_compare (ctx, f, argc, argv, is_helper);
370 endswith_helper (const char *s1, const char *s2)
373 if ((p = (char*) e_util_utf8_strstrcase(s1, s2))
374 && (strlen(p) == strlen(s2)))
381 func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
383 SearchContext *ctx = data;
385 return entry_compare (ctx, f, argc, argv, endswith_helper);
389 beginswith_helper (const char *s1, const char *s2)
392 if ((p = (char*) e_util_utf8_strstrcase(s1, s2))
400 func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
402 SearchContext *ctx = data;
404 return entry_compare (ctx, f, argc, argv, beginswith_helper);
408 func_exists(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
410 SearchContext *ctx = data;
415 && argv[0]->type == ESEXP_RES_STRING) {
417 struct prop_info *info = NULL;
420 propname = argv[0]->value.string;
422 for (i = 0; i < num_prop_infos; i ++) {
423 if (!strcmp (prop_info_table[i].query_prop, propname)) {
424 info = &prop_info_table[i];
426 if (info->prop_type == PROP_TYPE_NORMAL) {
427 const char *prop = NULL;
428 /* searches where the query's property
429 maps directly to an ecard property */
431 prop = e_contact_get_const (ctx->contact, info->field_id);
436 else if (info->prop_type == PROP_TYPE_LIST) {
437 /* the special searches that match any of the list elements */
438 truth = info->list_compare (ctx->contact, "", (char *(*)(const char*, const char*)) e_util_utf8_strstrcase);
446 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
447 r->value.bool = truth;
453 func_exists_vcard(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
455 SearchContext *ctx = data;
459 if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
460 const char *attr_name;
461 EVCardAttribute *attr;
465 attr_name = argv[0]->value.string;
466 attr = e_vcard_get_attribute (E_VCARD (ctx->contact), attr_name);
468 values = e_vcard_attribute_get_values (attr);
469 if (g_list_length (values) > 0) {
478 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
479 r->value.bool = truth;
484 /* 'builtin' functions */
488 int type; /* set to 1 if a function can perform shortcut evaluation, or
489 doesn't execute everything, 0 otherwise */
491 { "contains", func_contains, 0 },
492 { "is", func_is, 0 },
493 { "beginswith", func_beginswith, 0 },
494 { "endswith", func_endswith, 0 },
495 { "exists", func_exists, 0 },
496 { "exists_vcard", func_exists_vcard, 0 },
500 * e_book_backend_sexp_match_contact:
501 * @sexp: an #EBookBackendSExp
502 * @contact: an #EContact
504 * Checks if @contact matches @sexp.
506 * Return value: %TRUE if the contact matches, %FALSE otherwise.
509 e_book_backend_sexp_match_contact (EBookBackendSExp *sexp, EContact *contact)
515 g_warning ("null EContact passed to e_book_backend_sexp_match_contact");
519 sexp->priv->search_context->contact = g_object_ref (contact);
521 r = e_sexp_eval(sexp->priv->search_sexp);
523 retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
525 g_object_unref(sexp->priv->search_context->contact);
527 e_sexp_result_free(sexp->priv->search_sexp, r);
533 * e_book_backend_sexp_match_vcard:
534 * @sexp: an #EBookBackendSExp
535 * @vcard: a VCard string
537 * Checks if @vcard matches @sexp.
539 * Return value: %TRUE if the VCard matches, %FALSE otherwise.
542 e_book_backend_sexp_match_vcard (EBookBackendSExp *sexp, const char *vcard)
547 contact = e_contact_new_from_vcard (vcard);
549 retval = e_book_backend_sexp_match_contact (sexp, contact);
551 g_object_unref(contact);
559 * e_book_backend_sexp_new:
560 * @text: an s-expression to parse
562 * Creates a new #EBookBackendSExp from @text.
564 * Return value: A new #EBookBackendSExp.
567 e_book_backend_sexp_new (const char *text)
569 EBookBackendSExp *sexp = g_object_new (E_TYPE_BACKEND_SEXP, NULL);
573 sexp->priv->search_sexp = e_sexp_new();
575 for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
576 if (symbols[i].type == 1) {
577 e_sexp_add_ifunction(sexp->priv->search_sexp, 0, symbols[i].name,
578 (ESExpIFunc *)symbols[i].func, sexp->priv->search_context);
581 e_sexp_add_function(sexp->priv->search_sexp, 0, symbols[i].name,
582 symbols[i].func, sexp->priv->search_context);
586 e_sexp_input_text(sexp->priv->search_sexp, text, strlen(text));
587 esexp_error = e_sexp_parse(sexp->priv->search_sexp);
589 if (esexp_error == -1) {
590 g_object_unref (sexp);
598 e_book_backend_sexp_dispose (GObject *object)
600 EBookBackendSExp *sexp = E_BOOK_BACKEND_SEXP (object);
603 e_sexp_unref(sexp->priv->search_sexp);
605 g_free (sexp->priv->search_context);
610 if (G_OBJECT_CLASS (parent_class)->dispose)
611 G_OBJECT_CLASS (parent_class)->dispose (object);
615 e_book_backend_sexp_class_init (EBookBackendSExpClass *klass)
617 GObjectClass *object_class = G_OBJECT_CLASS (klass);
619 parent_class = g_type_class_peek_parent (klass);
621 /* Set the virtual methods. */
623 object_class->dispose = e_book_backend_sexp_dispose;
627 e_book_backend_sexp_init (EBookBackendSExp *sexp)
629 EBookBackendSExpPrivate *priv;
631 priv = g_new0 (EBookBackendSExpPrivate, 1);
634 priv->search_context = g_new (SearchContext, 1);
638 * e_book_backend_sexp_get_type:
641 e_book_backend_sexp_get_type (void)
643 static GType type = 0;
647 sizeof (EBookBackendSExpClass),
648 NULL, /* base_class_init */
649 NULL, /* base_class_finalize */
650 (GClassInitFunc) e_book_backend_sexp_class_init,
651 NULL, /* class_finalize */
652 NULL, /* class_data */
653 sizeof (EBookBackendSExp),
655 (GInstanceInitFunc) e_book_backend_sexp_init
658 type = g_type_register_static (G_TYPE_OBJECT, "EBookBackendSExp", &info, 0);