/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * pas-backend-summary.c * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Chris Toshok * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include "ebook/e-card-simple.h" #include "pas-backend-summary.h" #include "e-util/e-sexp.h" static GObjectClass *parent_class; struct _PASBackendSummaryPrivate { char *summary_path; FILE *fp; guint32 file_version; time_t mtime; gboolean upgraded; gboolean dirty; int flush_timeout_millis; int flush_timeout; GPtrArray *items; GHashTable *id_to_item; guint32 num_items; /* used only for loading */ #ifdef SUMMARY_STATS int size; #endif }; typedef struct { char *id; char *nickname; char *full_name; char *given_name; char *surname; char *file_as; char *email_1; char *email_2; char *email_3; gboolean wants_html; gboolean wants_html_set; gboolean list; gboolean list_show_addresses; } PASBackendSummaryItem; typedef struct { /* these lengths do *not* including the terminating \0, as it's not stored on disk. */ guint16 id_len; guint16 nickname_len; guint16 full_name_len; /* version 3.0 field */ guint16 given_name_len; guint16 surname_len; guint16 file_as_len; guint16 email_1_len; guint16 email_2_len; guint16 email_3_len; guint8 wants_html; guint8 wants_html_set; guint8 list; guint8 list_show_addresses; } PASBackendSummaryDiskItem; typedef struct { guint32 file_version; guint32 num_items; guint32 summary_mtime; /* version 2.0 field */ } PASBackendSummaryHeader; #define PAS_SUMMARY_MAGIC "PAS-SUMMARY" #define PAS_SUMMARY_MAGIC_LEN 11 #define PAS_SUMMARY_FILE_VERSION_1_0 1000 #define PAS_SUMMARY_FILE_VERSION_2_0 2000 #define PAS_SUMMARY_FILE_VERSION_3_0 3000 #define PAS_SUMMARY_FILE_VERSION_4_0 4000 #define PAS_SUMMARY_FILE_VERSION PAS_SUMMARY_FILE_VERSION_4_0 static void free_summary_item (PASBackendSummaryItem *item) { g_free (item->id); g_free (item->nickname); g_free (item->full_name); g_free (item->given_name); g_free (item->surname); g_free (item->file_as); g_free (item->email_1); g_free (item->email_2); g_free (item->email_3); g_free (item); } static void clear_items (PASBackendSummary *summary) { int i; int num = summary->priv->items->len; for (i = 0; i < num; i++) { PASBackendSummaryItem *item = g_ptr_array_remove_index_fast (summary->priv->items, 0); g_hash_table_remove (summary->priv->id_to_item, item->id); free_summary_item (item); } } PASBackendSummary* pas_backend_summary_new (const char *summary_path, int flush_timeout_millis) { PASBackendSummary *summary = g_object_new (PAS_TYPE_BACKEND_SUMMARY, NULL); summary->priv->summary_path = g_strdup (summary_path); summary->priv->flush_timeout_millis = flush_timeout_millis; summary->priv->file_version = PAS_SUMMARY_FILE_VERSION_4_0; return summary; } static void pas_backend_summary_dispose (GObject *object) { PASBackendSummary *summary = PAS_BACKEND_SUMMARY (object); if (summary->priv) { if (summary->priv->dirty) g_warning ("Destroying dirty summary"); if (summary->priv->flush_timeout) { g_source_remove (summary->priv->flush_timeout); summary->priv->flush_timeout = 0; } if (summary->priv->fp) fclose (summary->priv->fp); g_free (summary->priv->summary_path); clear_items (summary); g_ptr_array_free (summary->priv->items, TRUE); g_hash_table_destroy (summary->priv->id_to_item); g_free (summary->priv); summary->priv = NULL; } if (G_OBJECT_CLASS (parent_class)->dispose) G_OBJECT_CLASS (parent_class)->dispose (object); } static void pas_backend_summary_class_init (PASBackendSummaryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); /* Set the virtual methods. */ object_class->dispose = pas_backend_summary_dispose; } static void pas_backend_summary_init (PASBackendSummary *summary) { PASBackendSummaryPrivate *priv; priv = g_new(PASBackendSummaryPrivate, 1); summary->priv = priv; priv->summary_path = NULL; priv->fp = NULL; priv->dirty = FALSE; priv->upgraded = FALSE; priv->items = g_ptr_array_new(); priv->id_to_item = g_hash_table_new (g_str_hash, g_str_equal); priv->flush_timeout_millis = 0; priv->flush_timeout = 0; #ifdef SUMMARY_STATS priv->size = 0; #endif } /** * pas_backend_summary_get_type: */ GType pas_backend_summary_get_type (void) { static GType type = 0; if (! type) { GTypeInfo info = { sizeof (PASBackendSummaryClass), NULL, /* base_class_init */ NULL, /* base_class_finalize */ (GClassInitFunc) pas_backend_summary_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (PASBackendSummary), 0, /* n_preallocs */ (GInstanceInitFunc) pas_backend_summary_init }; type = g_type_register_static (G_TYPE_OBJECT, "PASBackendSummary", &info, 0); } return type; } static gboolean pas_backend_summary_check_magic (PASBackendSummary *summary, FILE *fp) { char buf [PAS_SUMMARY_MAGIC_LEN + 1]; int rv; memset (buf, 0, sizeof (buf)); rv = fread (buf, PAS_SUMMARY_MAGIC_LEN, 1, fp); if (rv != 1) return FALSE; if (strcmp (buf, PAS_SUMMARY_MAGIC)) return FALSE; return TRUE; } static gboolean pas_backend_summary_load_header (PASBackendSummary *summary, FILE *fp, PASBackendSummaryHeader *header) { int rv; rv = fread (&header->file_version, sizeof (header->file_version), 1, fp); if (rv != 1) return FALSE; header->file_version = ntohl (header->file_version); if (header->file_version < PAS_SUMMARY_FILE_VERSION) { return FALSE; /* this will cause the entire summary to be rebuilt */ } rv = fread (&header->num_items, sizeof (header->num_items), 1, fp); if (rv != 1) return FALSE; header->num_items = ntohl (header->num_items); rv = fread (&header->summary_mtime, sizeof (header->summary_mtime), 1, fp); if (rv != 1) return FALSE; header->summary_mtime = ntohl (header->summary_mtime); return TRUE; } static char * read_string (FILE *fp, int len) { char *buf; int rv; buf = g_new0 (char, len + 1); rv = fread (buf, len, 1, fp); if (rv != 1) { g_free (buf); return NULL; } return buf; } static gboolean pas_backend_summary_load_item (PASBackendSummary *summary, PASBackendSummaryItem **new_item) { PASBackendSummaryItem *item; char *buf; FILE *fp = summary->priv->fp; if (summary->priv->file_version >= PAS_SUMMARY_FILE_VERSION_4_0) { PASBackendSummaryDiskItem disk_item; int rv = fread (&disk_item, sizeof (disk_item), 1, fp); if (rv != 1) return FALSE; disk_item.id_len = ntohs (disk_item.id_len); disk_item.nickname_len = ntohs (disk_item.nickname_len); disk_item.full_name_len = ntohs (disk_item.full_name_len); disk_item.given_name_len = ntohs (disk_item.given_name_len); disk_item.surname_len = ntohs (disk_item.surname_len); disk_item.file_as_len = ntohs (disk_item.file_as_len); disk_item.email_1_len = ntohs (disk_item.email_1_len); disk_item.email_2_len = ntohs (disk_item.email_2_len); disk_item.email_3_len = ntohs (disk_item.email_3_len); item = g_new0 (PASBackendSummaryItem, 1); item->wants_html = disk_item.wants_html; item->wants_html_set = disk_item.wants_html_set; item->list = disk_item.list; item->list_show_addresses = disk_item.list_show_addresses; if (disk_item.id_len) { buf = read_string (fp, disk_item.id_len); if (!buf) { free_summary_item (item); return FALSE; } item->id = buf; } if (disk_item.nickname_len) { buf = read_string (fp, disk_item.nickname_len); if (!buf) { free_summary_item (item); return FALSE; } item->nickname = buf; } if (disk_item.full_name_len) { buf = read_string (fp, disk_item.full_name_len); if (!buf) { free_summary_item (item); return FALSE; } item->full_name = buf; } if (disk_item.given_name_len) { buf = read_string (fp, disk_item.given_name_len); if (!buf) { free_summary_item (item); return FALSE; } item->given_name = buf; } if (disk_item.surname_len) { buf = read_string (fp, disk_item.surname_len); if (!buf) { free_summary_item (item); return FALSE; } item->surname = buf; } if (disk_item.file_as_len) { buf = read_string (fp, disk_item.file_as_len); if (!buf) { free_summary_item (item); return FALSE; } item->file_as = buf; } if (disk_item.email_1_len) { buf = read_string (fp, disk_item.email_1_len); if (!buf) { free_summary_item (item); return FALSE; } item->email_1 = buf; } if (disk_item.email_2_len) { buf = read_string (fp, disk_item.email_2_len); if (!buf) { free_summary_item (item); return FALSE; } item->email_2 = buf; } if (disk_item.email_3_len) { buf = read_string (fp, disk_item.email_3_len); if (!buf) { free_summary_item (item); return FALSE; } item->email_3 = buf; } /* the only field that has to be there is the id */ if (!item->id) { free_summary_item (item); return FALSE; } } else { /* unhandled file version */ return FALSE; } *new_item = item; return TRUE; } /* opens the file and loads the header */ static gboolean pas_backend_summary_open (PASBackendSummary *summary) { FILE *fp; PASBackendSummaryHeader header; struct stat sb; if (summary->priv->fp) return TRUE; if (stat (summary->priv->summary_path, &sb) == -1) { /* if there's no summary present, look for the .new file and rename it if it's there, and attempt to load that */ char *new_filename = g_strconcat (summary->priv->summary_path, ".new", NULL); if (stat (new_filename, &sb) == -1) { g_warning ("no summary present"); g_free (new_filename); return FALSE; } else { rename (new_filename, summary->priv->summary_path); g_free (new_filename); } } fp = fopen (summary->priv->summary_path, "r"); if (!fp) { g_warning ("failed to open summary file"); return FALSE; } if (!pas_backend_summary_check_magic (summary, fp)) { g_warning ("file is not a valid summary file"); fclose (fp); return FALSE; } if (!pas_backend_summary_load_header (summary, fp, &header)) { g_warning ("failed to read summary header"); fclose (fp); return FALSE; } summary->priv->num_items = header.num_items; summary->priv->file_version = header.file_version; summary->priv->mtime = header.summary_mtime; summary->priv->fp = fp; return TRUE; } gboolean pas_backend_summary_load (PASBackendSummary *summary) { PASBackendSummaryItem *new_item; int i; if (!pas_backend_summary_open (summary)) return FALSE; for (i = 0; i < summary->priv->num_items; i ++) { if (!pas_backend_summary_load_item (summary, &new_item)) { g_warning ("error while reading summary item"); clear_items (summary); fclose (summary->priv->fp); summary->priv->fp = NULL; summary->priv->dirty = FALSE; return FALSE; } g_ptr_array_add (summary->priv->items, new_item); g_hash_table_insert (summary->priv->id_to_item, new_item->id, new_item); } if (summary->priv->upgraded) { pas_backend_summary_save (summary); } summary->priv->dirty = FALSE; return TRUE; } static gboolean pas_backend_summary_save_magic (FILE *fp) { int rv; rv = fwrite (PAS_SUMMARY_MAGIC, PAS_SUMMARY_MAGIC_LEN, 1, fp); if (rv != 1) return FALSE; return TRUE; } static gboolean pas_backend_summary_save_header (PASBackendSummary *summary, FILE *fp) { PASBackendSummaryHeader header; int rv; header.file_version = htonl (PAS_SUMMARY_FILE_VERSION); header.num_items = htonl (summary->priv->items->len); header.summary_mtime = htonl (time (NULL)); rv = fwrite (&header, sizeof (header), 1, fp); if (rv != 1) return FALSE; return TRUE; } static gboolean save_string (const char *str, FILE *fp) { int rv; if (!str || !*str) return TRUE; rv = fwrite (str, strlen (str), 1, fp); return (rv == 1); } static gboolean pas_backend_summary_save_item (PASBackendSummary *summary, FILE *fp, PASBackendSummaryItem *item) { PASBackendSummaryDiskItem disk_item; int len; int rv; len = item->id ? strlen (item->id) : 0; disk_item.id_len = htons (len); len = item->nickname ? strlen (item->nickname) : 0; disk_item.nickname_len = htons (len); len = item->given_name ? strlen (item->given_name) : 0; disk_item.given_name_len = htons (len); len = item->full_name ? strlen (item->full_name) : 0; disk_item.full_name_len = htons (len); len = item->surname ? strlen (item->surname) : 0; disk_item.surname_len = htons (len); len = item->file_as ? strlen (item->file_as) : 0; disk_item.file_as_len = htons (len); len = item->email_1 ? strlen (item->email_1) : 0; disk_item.email_1_len = htons (len); len = item->email_2 ? strlen (item->email_2) : 0; disk_item.email_2_len = htons (len); len = item->email_3 ? strlen (item->email_3) : 0; disk_item.email_3_len = htons (len); disk_item.wants_html = item->wants_html; disk_item.wants_html_set = item->wants_html_set; disk_item.list = item->list; disk_item.list_show_addresses = item->list_show_addresses; rv = fwrite (&disk_item, sizeof(disk_item), 1, fp); if (rv != 1) return FALSE; if (!save_string (item->id, fp)) return FALSE; if (!save_string (item->nickname, fp)) return FALSE; if (!save_string (item->full_name, fp)) return FALSE; if (!save_string (item->given_name, fp)) return FALSE; if (!save_string (item->surname, fp)) return FALSE; if (!save_string (item->file_as, fp)) return FALSE; if (!save_string (item->email_1, fp)) return FALSE; if (!save_string (item->email_2, fp)) return FALSE; if (!save_string (item->email_3, fp)) return FALSE; return TRUE; } gboolean pas_backend_summary_save (PASBackendSummary *summary) { struct stat sb; FILE *fp = NULL; char *new_filename = NULL; int i; if (!summary->priv->dirty) return TRUE; new_filename = g_strconcat (summary->priv->summary_path, ".new", NULL); fp = fopen (new_filename, "w"); if (!fp) { g_warning ("could not create new summary file"); goto lose; } if (!pas_backend_summary_save_magic (fp)) { g_warning ("could not write magic to new summary file"); goto lose; } if (!pas_backend_summary_save_header (summary, fp)) { g_warning ("could not write header to new summary file"); goto lose; } for (i = 0; i < summary->priv->items->len; i ++) { PASBackendSummaryItem *item = g_ptr_array_index (summary->priv->items, i); if (!pas_backend_summary_save_item (summary, fp, item)) { g_warning ("failed to write an item to new summary file, errno = %d", errno); goto lose; } } fclose (fp); /* if we have a queued flush, clear it (since we just flushed) */ if (summary->priv->flush_timeout) { g_source_remove (summary->priv->flush_timeout); summary->priv->flush_timeout = 0; } /* unlink the old summary and rename the new one */ unlink (summary->priv->summary_path); rename (new_filename, summary->priv->summary_path); g_free (new_filename); /* lastly, update the in memory mtime to that of the file */ if (stat (summary->priv->summary_path, &sb) == -1) { g_warning ("error stat'ing saved summary"); } else { summary->priv->mtime = sb.st_mtime; } return TRUE; lose: if (fp) fclose (fp); if (new_filename) unlink (new_filename); g_free (new_filename); return FALSE; } void pas_backend_summary_add_card (PASBackendSummary *summary, const char *vcard) { ECard *card; ECardSimple *simple; PASBackendSummaryItem *new_item; card = e_card_new ((char*)vcard); simple = e_card_simple_new (card); new_item = g_new (PASBackendSummaryItem, 1); new_item->id = g_strdup (e_card_simple_get_id (simple)); new_item->nickname = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_NICKNAME); new_item->full_name = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_FULL_NAME); new_item->given_name = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_GIVEN_NAME); new_item->surname = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_FAMILY_NAME); new_item->file_as = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_FILE_AS); new_item->email_1 = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_EMAIL); new_item->email_2 = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_EMAIL_2); new_item->email_3 = e_card_simple_get (simple, E_CARD_SIMPLE_FIELD_EMAIL_3); new_item->list = e_card_evolution_list (card); new_item->list_show_addresses = e_card_evolution_list_show_addresses (card); new_item->wants_html = card->wants_html; new_item->wants_html_set = card->wants_html_set; g_ptr_array_add (summary->priv->items, new_item); g_hash_table_insert (summary->priv->id_to_item, new_item->id, new_item); g_object_unref (simple); g_object_unref (card); #ifdef SUMMARY_STATS summary->priv->size += sizeof (PASBackendSummaryItem); summary->priv->size += new_item->id ? strlen (new_item->id) : 0; summary->priv->size += new_item->nickname ? strlen (new_item->nickname) : 0; summary->priv->size += new_item->full_name ? strlen (new_item->full_name) : 0; summary->priv->size += new_item->given_name ? strlen (new_item->given_name) : 0; summary->priv->size += new_item->surname ? strlen (new_item->surname) : 0; summary->priv->size += new_item->file_as ? strlen (new_item->file_as) : 0; summary->priv->size += new_item->email_1 ? strlen (new_item->email_1) : 0; summary->priv->size += new_item->email_2 ? strlen (new_item->email_2) : 0; summary->priv->size += new_item->email_3 ? strlen (new_item->email_3) : 0; #endif pas_backend_summary_touch (summary); } void pas_backend_summary_remove_card (PASBackendSummary *summary, const char *id) { PASBackendSummaryItem *item = g_hash_table_lookup (summary->priv->id_to_item, id); if (item) { g_ptr_array_remove (summary->priv->items, item); g_hash_table_remove (summary->priv->id_to_item, id); free_summary_item (item); pas_backend_summary_touch (summary); return; } g_warning ("pas_backend_summary_remove_card: unable to locate id `%s'", id); } static gboolean summary_flush_func (gpointer data) { PASBackendSummary *summary = PAS_BACKEND_SUMMARY (data); if (!summary->priv->dirty) { summary->priv->flush_timeout = 0; return FALSE; } if (!pas_backend_summary_save (summary)) { /* this isn't fatal, as we can just either 1) flush out with the next change, or 2) regen the summary when we next load the uri */ g_warning ("failed to flush summary file to disk"); return TRUE; /* try again after the next timeout */ } g_warning ("flushed summary to disk"); /* we only want this to execute once, so return FALSE and set summary->flush_timeout to 0 */ summary->priv->flush_timeout = 0; return FALSE; } void pas_backend_summary_touch (PASBackendSummary *summary) { summary->priv->dirty = TRUE; if (!summary->priv->flush_timeout && summary->priv->flush_timeout_millis) summary->priv->flush_timeout = g_timeout_add (summary->priv->flush_timeout_millis, summary_flush_func, summary); } gboolean pas_backend_summary_is_up_to_date (PASBackendSummary *summary, time_t t) { if (!pas_backend_summary_open (summary)) return FALSE; else return summary->priv->mtime >= t; } /* we only want to do summary queries if the query is over the set fields in the summary */ static ESExpResult * func_check(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) { ESExpResult *r; int truth = FALSE; if (argc == 2 && argv[0]->type == ESEXP_RES_STRING && argv[1]->type == ESEXP_RES_STRING) { char *query_name = argv[0]->value.string; if (!strcmp (query_name, "nickname") || !strcmp (query_name, "full_name") || !strcmp (query_name, "file_as") || !strcmp (query_name, "email")) { truth = TRUE; } } r = e_sexp_result_new(f, ESEXP_RES_BOOL); r->value.bool = truth; return r; } /* 'builtin' functions */ static struct { char *name; ESExpFunc *func; int type; /* set to 1 if a function can perform shortcut evaluation, or doesn't execute everything, 0 otherwise */ } check_symbols[] = { { "contains", func_check, 0 }, { "is", func_check, 0 }, { "beginswith", func_check, 0 }, { "endswith", func_check, 0 }, }; gboolean pas_backend_summary_is_summary_query (PASBackendSummary *summary, const char *query) { ESExp *sexp; ESExpResult *r; gboolean retval; int i; int esexp_error; sexp = e_sexp_new(); for(i=0;itype == ESEXP_RES_BOOL && r->value.bool); e_sexp_result_free(sexp, r); e_sexp_unref (sexp); return retval; } /* the actual query mechanics */ static ESExpResult * do_compare (PASBackendSummary *summary, struct _ESExp *f, int argc, struct _ESExpResult **argv, char *(*compare)(const char*, const char*)) { GPtrArray *result = g_ptr_array_new (); ESExpResult *r; int i; if (argc == 2 && argv[0]->type == ESEXP_RES_STRING && argv[1]->type == ESEXP_RES_STRING) { for (i = 0; i < summary->priv->items->len; i ++) { PASBackendSummaryItem *item = g_ptr_array_index (summary->priv->items, i); if (!strcmp (argv[0]->value.string, "full_name")) { char *given = item->given_name; char *surname = item->surname; if ((given && compare (given, argv[1]->value.string)) || (surname && compare (surname, argv[1]->value.string))) g_ptr_array_add (result, item->id); } else if (!strcmp (argv[0]->value.string, "email")) { char *email_1 = item->email_1; char *email_2 = item->email_2; char *email_3 = item->email_3; if ((email_1 && compare (email_1, argv[1]->value.string)) || (email_2 && compare (email_2, argv[1]->value.string)) || (email_3 && compare (email_3, argv[1]->value.string))) g_ptr_array_add (result, item->id); } else if (!strcmp (argv[0]->value.string, "file_as")) { char *file_as = item->file_as; if (file_as && compare (file_as, argv[1]->value.string)) g_ptr_array_add (result, item->id); } else if (!strcmp (argv[0]->value.string, "nickname")) { char *nickname = item->nickname; if (nickname && compare (nickname, argv[1]->value.string)) g_ptr_array_add (result, item->id); } } } r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR); r->value.ptrarray = result; return r; } static ESExpResult * func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) { PASBackendSummary *summary = data; return do_compare (summary, f, argc, argv, (char *(*)(const char*, const char*)) e_utf8_strstrcase); } static char * is_helper (const char *s1, const char *s2) { if (!strcasecmp(s1, s2)) return (char*)s1; else return NULL; } static ESExpResult * func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) { PASBackendSummary *summary = data; return do_compare (summary, f, argc, argv, is_helper); } static char * endswith_helper (const char *s1, const char *s2) { char *p; if ((p = (char*)e_utf8_strstrcase(s1, s2)) && (strlen(p) == strlen(s2))) return p; else return NULL; } static ESExpResult * func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) { PASBackendSummary *summary = data; return do_compare (summary, f, argc, argv, endswith_helper); } static char * beginswith_helper (const char *s1, const char *s2) { char *p; if ((p = (char*)e_utf8_strstrcase(s1, s2)) && (p == s1)) return p; else return NULL; } static ESExpResult * func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) { PASBackendSummary *summary = data; return do_compare (summary, f, argc, argv, beginswith_helper); } /* 'builtin' functions */ static struct { char *name; ESExpFunc *func; int type; /* set to 1 if a function can perform shortcut evaluation, or doesn't execute everything, 0 otherwise */ } symbols[] = { { "contains", func_contains, 0 }, { "is", func_is, 0 }, { "beginswith", func_beginswith, 0 }, { "endswith", func_endswith, 0 }, }; GPtrArray* pas_backend_summary_search (PASBackendSummary *summary, const char *query) { ESExp *sexp; ESExpResult *r; GPtrArray *retval = g_ptr_array_new(); int i; int esexp_error; sexp = e_sexp_new(); for(i=0;itype == ESEXP_RES_ARRAY_PTR && r->value.ptrarray) { GPtrArray *ptrarray = r->value.ptrarray; int i; for (i = 0; i < ptrarray->len; i ++) g_ptr_array_add (retval, g_ptr_array_index (ptrarray, i)); } e_sexp_result_free(sexp, r); e_sexp_unref (sexp); return retval; } char* pas_backend_summary_get_summary_vcard(PASBackendSummary *summary, const char *id) { PASBackendSummaryItem *item = g_hash_table_lookup (summary->priv->id_to_item, id); if (item) { ECard *card = e_card_new (""); ECardSimple *simple = e_card_simple_new (card); char *vcard; e_card_simple_set_id (simple, item->id); e_card_simple_set (simple, E_CARD_SIMPLE_FIELD_FILE_AS, item->file_as); e_card_simple_set (simple, E_CARD_SIMPLE_FIELD_GIVEN_NAME, item->given_name); e_card_simple_set (simple, E_CARD_SIMPLE_FIELD_FAMILY_NAME, item->surname); e_card_simple_set (simple, E_CARD_SIMPLE_FIELD_NICKNAME, item->nickname); e_card_simple_set (simple, E_CARD_SIMPLE_FIELD_FULL_NAME, item->full_name); e_card_simple_set_email (simple, E_CARD_SIMPLE_EMAIL_ID_EMAIL, item->email_1); e_card_simple_set_email (simple, E_CARD_SIMPLE_EMAIL_ID_EMAIL_2, item->email_2); e_card_simple_set_email (simple, E_CARD_SIMPLE_EMAIL_ID_EMAIL_3, item->email_3); e_card_simple_sync_card (simple); card->list = item->list; card->wants_html = item->wants_html; card->wants_html_set = item->wants_html_set; card->list_show_addresses = item->list_show_addresses; vcard = e_card_simple_get_vcard (simple); g_object_unref (simple); g_object_unref (card); return vcard; } else { g_warning ("in unable to locate card `%s' in summary", id); return NULL; } }