1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * pas-backend-summary.c
4 * Copyright 2000, 2001, Ximian, Inc.
7 * Chris Toshok <toshok@ximian.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License, version 2, as published by the Free Software Foundation.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 #include <sys/types.h>
34 #include <glib/gstdio.h>
36 #include "libedataserver/e-sexp.h"
37 #include "libedataserver/e-data-server-util.h"
39 #include "libebook/e-contact.h"
41 #include "e-book-backend-summary.h"
43 static GObjectClass *parent_class;
45 struct _EBookBackendSummaryPrivate {
52 int flush_timeout_millis;
55 GHashTable *id_to_item;
56 guint32 num_items; /* used only for loading */
74 gboolean wants_html_set;
76 gboolean list_show_addresses;
77 } EBookBackendSummaryItem;
80 /* these lengths do *not* including the terminating \0, as
81 it's not stored on disk. */
84 guint16 full_name_len; /* version 3.0 field */
85 guint16 given_name_len;
93 guint8 wants_html_set;
95 guint8 list_show_addresses;
96 } EBookBackendSummaryDiskItem;
101 guint32 summary_mtime; /* version 2.0 field */
102 } EBookBackendSummaryHeader;
104 #define PAS_SUMMARY_MAGIC "PAS-SUMMARY"
105 #define PAS_SUMMARY_MAGIC_LEN 11
107 #define PAS_SUMMARY_FILE_VERSION_1_0 1000
108 #define PAS_SUMMARY_FILE_VERSION_2_0 2000
109 #define PAS_SUMMARY_FILE_VERSION_3_0 3000
110 #define PAS_SUMMARY_FILE_VERSION_4_0 4000
111 #define PAS_SUMMARY_FILE_VERSION_5_0 5000
113 #define PAS_SUMMARY_FILE_VERSION PAS_SUMMARY_FILE_VERSION_5_0
116 free_summary_item (EBookBackendSummaryItem *item)
119 g_free (item->nickname);
120 g_free (item->full_name);
121 g_free (item->given_name);
122 g_free (item->surname);
123 g_free (item->file_as);
124 g_free (item->email_1);
125 g_free (item->email_2);
126 g_free (item->email_3);
127 g_free (item->email_4);
132 clear_items (EBookBackendSummary *summary)
135 int num = summary->priv->items->len;
136 for (i = 0; i < num; i++) {
137 EBookBackendSummaryItem *item = g_ptr_array_remove_index_fast (summary->priv->items, 0);
139 g_hash_table_remove (summary->priv->id_to_item, item->id);
140 free_summary_item (item);
146 * e_book_backend_summary_new:
147 * @summary_path: a local file system path
148 * @flush_timeout_millis: a flush interval, in milliseconds
150 * Creates an #EBookBackendSummary object without loading it
151 * or otherwise affecting the file. @flush_timeout_millis
152 * specifies how much time should elapse, at a minimum, from
153 * the summary is changed until it is flushed to disk.
155 * Return value: A new #EBookBackendSummary.
158 e_book_backend_summary_new (const char *summary_path, int flush_timeout_millis)
160 EBookBackendSummary *summary = g_object_new (E_TYPE_BACKEND_SUMMARY, NULL);
162 summary->priv->summary_path = g_strdup (summary_path);
163 summary->priv->flush_timeout_millis = flush_timeout_millis;
164 summary->priv->file_version = PAS_SUMMARY_FILE_VERSION_4_0;
170 e_book_backend_summary_dispose (GObject *object)
172 EBookBackendSummary *summary = E_BOOK_BACKEND_SUMMARY (object);
175 if (summary->priv->fp)
176 fclose (summary->priv->fp);
177 if (summary->priv->dirty)
178 e_book_backend_summary_save (summary);
180 utime (summary->priv->summary_path, NULL);
182 if (summary->priv->flush_timeout) {
183 g_source_remove (summary->priv->flush_timeout);
184 summary->priv->flush_timeout = 0;
187 g_free (summary->priv->summary_path);
188 clear_items (summary);
189 g_ptr_array_free (summary->priv->items, TRUE);
191 g_hash_table_destroy (summary->priv->id_to_item);
193 g_free (summary->priv);
194 summary->priv = NULL;
197 if (G_OBJECT_CLASS (parent_class)->dispose)
198 G_OBJECT_CLASS (parent_class)->dispose (object);
202 e_book_backend_summary_class_init (EBookBackendSummaryClass *klass)
204 GObjectClass *object_class = G_OBJECT_CLASS (klass);
206 parent_class = g_type_class_peek_parent (klass);
208 /* Set the virtual methods. */
210 object_class->dispose = e_book_backend_summary_dispose;
214 e_book_backend_summary_init (EBookBackendSummary *summary)
216 EBookBackendSummaryPrivate *priv;
218 priv = g_new(EBookBackendSummaryPrivate, 1);
220 summary->priv = priv;
222 priv->summary_path = NULL;
225 priv->upgraded = FALSE;
226 priv->items = g_ptr_array_new();
227 priv->id_to_item = g_hash_table_new (g_str_hash, g_str_equal);
228 priv->flush_timeout_millis = 0;
229 priv->flush_timeout = 0;
236 * e_book_backend_summary_get_type:
239 e_book_backend_summary_get_type (void)
241 static GType type = 0;
245 sizeof (EBookBackendSummaryClass),
246 NULL, /* base_class_init */
247 NULL, /* base_class_finalize */
248 (GClassInitFunc) e_book_backend_summary_class_init,
249 NULL, /* class_finalize */
250 NULL, /* class_data */
251 sizeof (EBookBackendSummary),
253 (GInstanceInitFunc) e_book_backend_summary_init
256 type = g_type_register_static (G_TYPE_OBJECT, "EBookBackendSummary", &info, 0);
264 e_book_backend_summary_check_magic (EBookBackendSummary *summary, FILE *fp)
266 char buf [PAS_SUMMARY_MAGIC_LEN + 1];
269 memset (buf, 0, sizeof (buf));
271 rv = fread (buf, PAS_SUMMARY_MAGIC_LEN, 1, fp);
274 if (strcmp (buf, PAS_SUMMARY_MAGIC))
281 e_book_backend_summary_load_header (EBookBackendSummary *summary, FILE *fp,
282 EBookBackendSummaryHeader *header)
286 rv = fread (&header->file_version, sizeof (header->file_version), 1, fp);
290 header->file_version = g_ntohl (header->file_version);
292 if (header->file_version < PAS_SUMMARY_FILE_VERSION) {
293 return FALSE; /* this will cause the entire summary to be rebuilt */
296 rv = fread (&header->num_items, sizeof (header->num_items), 1, fp);
300 header->num_items = g_ntohl (header->num_items);
302 rv = fread (&header->summary_mtime, sizeof (header->summary_mtime), 1, fp);
305 header->summary_mtime = g_ntohl (header->summary_mtime);
311 read_string (FILE *fp, int len)
316 buf = g_new0 (char, len + 1);
318 rv = fread (buf, len, 1, fp);
328 e_book_backend_summary_load_item (EBookBackendSummary *summary,
329 EBookBackendSummaryItem **new_item)
331 EBookBackendSummaryItem *item;
333 FILE *fp = summary->priv->fp;
335 if (summary->priv->file_version >= PAS_SUMMARY_FILE_VERSION_4_0) {
336 EBookBackendSummaryDiskItem disk_item;
337 int rv = fread (&disk_item, sizeof (disk_item), 1, fp);
341 disk_item.id_len = g_ntohs (disk_item.id_len);
342 disk_item.nickname_len = g_ntohs (disk_item.nickname_len);
343 disk_item.full_name_len = g_ntohs (disk_item.full_name_len);
344 disk_item.given_name_len = g_ntohs (disk_item.given_name_len);
345 disk_item.surname_len = g_ntohs (disk_item.surname_len);
346 disk_item.file_as_len = g_ntohs (disk_item.file_as_len);
347 disk_item.email_1_len = g_ntohs (disk_item.email_1_len);
348 disk_item.email_2_len = g_ntohs (disk_item.email_2_len);
349 disk_item.email_3_len = g_ntohs (disk_item.email_3_len);
350 disk_item.email_4_len = g_ntohs (disk_item.email_4_len);
352 item = g_new0 (EBookBackendSummaryItem, 1);
354 item->wants_html = disk_item.wants_html;
355 item->wants_html_set = disk_item.wants_html_set;
356 item->list = disk_item.list;
357 item->list_show_addresses = disk_item.list_show_addresses;
359 if (disk_item.id_len) {
360 buf = read_string (fp, disk_item.id_len);
362 free_summary_item (item);
368 if (disk_item.nickname_len) {
369 buf = read_string (fp, disk_item.nickname_len);
371 free_summary_item (item);
374 item->nickname = buf;
377 if (disk_item.full_name_len) {
378 buf = read_string (fp, disk_item.full_name_len);
380 free_summary_item (item);
383 item->full_name = buf;
386 if (disk_item.given_name_len) {
387 buf = read_string (fp, disk_item.given_name_len);
389 free_summary_item (item);
392 item->given_name = buf;
395 if (disk_item.surname_len) {
396 buf = read_string (fp, disk_item.surname_len);
398 free_summary_item (item);
404 if (disk_item.file_as_len) {
405 buf = read_string (fp, disk_item.file_as_len);
407 free_summary_item (item);
413 if (disk_item.email_1_len) {
414 buf = read_string (fp, disk_item.email_1_len);
416 free_summary_item (item);
422 if (disk_item.email_2_len) {
423 buf = read_string (fp, disk_item.email_2_len);
425 free_summary_item (item);
431 if (disk_item.email_3_len) {
432 buf = read_string (fp, disk_item.email_3_len);
434 free_summary_item (item);
440 if (disk_item.email_4_len) {
441 buf = read_string (fp, disk_item.email_4_len);
443 free_summary_item (item);
449 /* the only field that has to be there is the id */
451 free_summary_item (item);
456 /* unhandled file version */
464 /* opens the file and loads the header */
466 e_book_backend_summary_open (EBookBackendSummary *summary)
469 EBookBackendSummaryHeader header;
472 if (summary->priv->fp)
475 if (g_stat (summary->priv->summary_path, &sb) == -1) {
476 /* if there's no summary present, look for the .new
477 file and rename it if it's there, and attempt to
479 char *new_filename = g_strconcat (summary->priv->summary_path, ".new", NULL);
480 if (g_stat (new_filename, &sb) == -1) {
481 g_free (new_filename);
485 g_rename (new_filename, summary->priv->summary_path);
486 g_free (new_filename);
490 fp = g_fopen (summary->priv->summary_path, "rb");
492 g_warning ("failed to open summary file");
496 if (!e_book_backend_summary_check_magic (summary, fp)) {
497 g_warning ("file is not a valid summary file");
502 if (!e_book_backend_summary_load_header (summary, fp, &header)) {
503 g_warning ("failed to read summary header");
508 summary->priv->num_items = header.num_items;
509 summary->priv->file_version = header.file_version;
510 summary->priv->mtime = sb.st_mtime;
511 summary->priv->fp = fp;
517 * e_book_backend_summary_load:
518 * @summary: an #EBookBackendSummary
520 * Attempts to load @summary from disk. The load is successful if
521 * the file was located, it was in the correct format, and it was
524 * Return value: %TRUE if the load succeeded, %FALSE if it failed.
527 e_book_backend_summary_load (EBookBackendSummary *summary)
529 EBookBackendSummaryItem *new_item;
532 clear_items (summary);
534 if (!e_book_backend_summary_open (summary))
537 for (i = 0; i < summary->priv->num_items; i ++) {
538 if (!e_book_backend_summary_load_item (summary, &new_item)) {
539 g_warning ("error while reading summary item");
540 clear_items (summary);
541 fclose (summary->priv->fp);
542 summary->priv->fp = NULL;
543 summary->priv->dirty = FALSE;
547 g_ptr_array_add (summary->priv->items, new_item);
548 g_hash_table_insert (summary->priv->id_to_item, new_item->id, new_item);
551 if (summary->priv->upgraded) {
552 e_book_backend_summary_save (summary);
554 summary->priv->dirty = FALSE;
560 e_book_backend_summary_save_magic (FILE *fp)
563 rv = fwrite (PAS_SUMMARY_MAGIC, PAS_SUMMARY_MAGIC_LEN, 1, fp);
571 e_book_backend_summary_save_header (EBookBackendSummary *summary, FILE *fp)
573 EBookBackendSummaryHeader header;
576 header.file_version = g_htonl (PAS_SUMMARY_FILE_VERSION);
577 header.num_items = g_htonl (summary->priv->items->len);
578 header.summary_mtime = g_htonl (time (NULL));
580 rv = fwrite (&header, sizeof (header), 1, fp);
588 save_string (const char *str, FILE *fp)
595 rv = fwrite (str, strlen (str), 1, fp);
600 e_book_backend_summary_save_item (EBookBackendSummary *summary, FILE *fp, EBookBackendSummaryItem *item)
602 EBookBackendSummaryDiskItem disk_item;
606 len = item->id ? strlen (item->id) : 0;
607 disk_item.id_len = g_htons (len);
609 len = item->nickname ? strlen (item->nickname) : 0;
610 disk_item.nickname_len = g_htons (len);
612 len = item->given_name ? strlen (item->given_name) : 0;
613 disk_item.given_name_len = g_htons (len);
615 len = item->full_name ? strlen (item->full_name) : 0;
616 disk_item.full_name_len = g_htons (len);
618 len = item->surname ? strlen (item->surname) : 0;
619 disk_item.surname_len = g_htons (len);
621 len = item->file_as ? strlen (item->file_as) : 0;
622 disk_item.file_as_len = g_htons (len);
624 len = item->email_1 ? strlen (item->email_1) : 0;
625 disk_item.email_1_len = g_htons (len);
627 len = item->email_2 ? strlen (item->email_2) : 0;
628 disk_item.email_2_len = g_htons (len);
630 len = item->email_3 ? strlen (item->email_3) : 0;
631 disk_item.email_3_len = g_htons (len);
633 len = item->email_4 ? strlen (item->email_4) : 0;
634 disk_item.email_4_len = g_htons (len);
636 disk_item.wants_html = item->wants_html;
637 disk_item.wants_html_set = item->wants_html_set;
638 disk_item.list = item->list;
639 disk_item.list_show_addresses = item->list_show_addresses;
641 rv = fwrite (&disk_item, sizeof(disk_item), 1, fp);
645 if (!save_string (item->id, fp))
647 if (!save_string (item->nickname, fp))
649 if (!save_string (item->full_name, fp))
651 if (!save_string (item->given_name, fp))
653 if (!save_string (item->surname, fp))
655 if (!save_string (item->file_as, fp))
657 if (!save_string (item->email_1, fp))
659 if (!save_string (item->email_2, fp))
661 if (!save_string (item->email_3, fp))
663 if (!save_string (item->email_4, fp))
670 * e_book_backend_summary_save:
671 * @summary: an #EBookBackendSummary
673 * Attempts to save @summary to disk.
675 * Return value: %TRUE if the save succeeded, %FALSE otherwise.
678 e_book_backend_summary_save (EBookBackendSummary *summary)
682 char *new_filename = NULL;
685 if (!summary->priv->dirty)
688 new_filename = g_strconcat (summary->priv->summary_path, ".new", NULL);
690 fp = g_fopen (new_filename, "wb");
692 g_warning ("could not create new summary file");
696 if (!e_book_backend_summary_save_magic (fp)) {
697 g_warning ("could not write magic to new summary file");
701 if (!e_book_backend_summary_save_header (summary, fp)) {
702 g_warning ("could not write header to new summary file");
706 for (i = 0; i < summary->priv->items->len; i ++) {
707 EBookBackendSummaryItem *item = g_ptr_array_index (summary->priv->items, i);
708 if (!e_book_backend_summary_save_item (summary, fp, item)) {
709 g_warning ("failed to write an item to new summary file, errno = %d", errno);
716 /* if we have a queued flush, clear it (since we just flushed) */
717 if (summary->priv->flush_timeout) {
718 g_source_remove (summary->priv->flush_timeout);
719 summary->priv->flush_timeout = 0;
722 /* unlink the old summary and rename the new one */
723 g_unlink (summary->priv->summary_path);
724 g_rename (new_filename, summary->priv->summary_path);
726 g_free (new_filename);
728 /* lastly, update the in memory mtime to that of the file */
729 if (g_stat (summary->priv->summary_path, &sb) == -1) {
730 g_warning ("error stat'ing saved summary");
733 summary->priv->mtime = sb.st_mtime;
736 summary->priv->dirty = FALSE;
743 g_unlink (new_filename);
744 g_free (new_filename);
749 * e_book_backend_summary_add_contact:
750 * @summary: an #EBookBackendSummary
751 * @contact: an #EContact to add
753 * Adds a summary of @contact to @summary. Does not check if
754 * the contact already has a summary.
757 e_book_backend_summary_add_contact (EBookBackendSummary *summary, EContact *contact)
759 EBookBackendSummaryItem *new_item;
762 /* ID normally should not be NULL for a contact. */
763 /* Added this check as groupwise server sometimes returns
764 * contacts with NULL id
766 id = e_contact_get (contact, E_CONTACT_UID);
768 g_warning ("found a contact with NULL uid");
772 new_item = g_new0 (EBookBackendSummaryItem, 1);
775 new_item->nickname = e_contact_get (contact, E_CONTACT_NICKNAME);
776 new_item->full_name = e_contact_get (contact, E_CONTACT_FULL_NAME);
777 new_item->given_name = e_contact_get (contact, E_CONTACT_GIVEN_NAME);
778 new_item->surname = e_contact_get (contact, E_CONTACT_FAMILY_NAME);
779 new_item->file_as = e_contact_get (contact, E_CONTACT_FILE_AS);
780 new_item->email_1 = e_contact_get (contact, E_CONTACT_EMAIL_1);
781 new_item->email_2 = e_contact_get (contact, E_CONTACT_EMAIL_2);
782 new_item->email_3 = e_contact_get (contact, E_CONTACT_EMAIL_3);
783 new_item->email_4 = e_contact_get (contact, E_CONTACT_EMAIL_4);
784 new_item->list = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST));
785 new_item->list_show_addresses = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_LIST_SHOW_ADDRESSES));
786 new_item->wants_html = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_WANTS_HTML));
788 g_ptr_array_add (summary->priv->items, new_item);
789 g_hash_table_insert (summary->priv->id_to_item, new_item->id, new_item);
792 summary->priv->size += sizeof (EBookBackendSummaryItem);
793 summary->priv->size += new_item->id ? strlen (new_item->id) : 0;
794 summary->priv->size += new_item->nickname ? strlen (new_item->nickname) : 0;
795 summary->priv->size += new_item->full_name ? strlen (new_item->full_name) : 0;
796 summary->priv->size += new_item->given_name ? strlen (new_item->given_name) : 0;
797 summary->priv->size += new_item->surname ? strlen (new_item->surname) : 0;
798 summary->priv->size += new_item->file_as ? strlen (new_item->file_as) : 0;
799 summary->priv->size += new_item->email_1 ? strlen (new_item->email_1) : 0;
800 summary->priv->size += new_item->email_2 ? strlen (new_item->email_2) : 0;
801 summary->priv->size += new_item->email_3 ? strlen (new_item->email_3) : 0;
802 summary->priv->size += new_item->email_4 ? strlen (new_item->email_4) : 0;
804 e_book_backend_summary_touch (summary);
808 * e_book_backend_summary_remove_contact:
809 * @summary: an #EBookBackendSummary
810 * @id: a unique contact ID string
812 * Removes the summary of the contact identified by @id from @summary.
815 e_book_backend_summary_remove_contact (EBookBackendSummary *summary, const char *id)
817 EBookBackendSummaryItem *item = g_hash_table_lookup (summary->priv->id_to_item, id);
820 g_ptr_array_remove (summary->priv->items, item);
821 g_hash_table_remove (summary->priv->id_to_item, id);
822 free_summary_item (item);
823 e_book_backend_summary_touch (summary);
827 g_warning ("e_book_backend_summary_remove_contact: unable to locate id `%s'", id);
831 * e_book_backend_summary_check_contact:
832 * @summary: an #EBookBackendSummary
833 * @id: a unique contact ID string
835 * Checks if a summary of the contact identified by @id
836 * exists in @summary.
838 * Return value: %TRUE if the summary exists, %FALSE otherwise.
841 e_book_backend_summary_check_contact (EBookBackendSummary *summary, const char *id)
843 return g_hash_table_lookup (summary->priv->id_to_item, id) != NULL;
847 summary_flush_func (gpointer data)
849 EBookBackendSummary *summary = E_BOOK_BACKEND_SUMMARY (data);
851 if (!summary->priv->dirty) {
852 summary->priv->flush_timeout = 0;
856 if (!e_book_backend_summary_save (summary)) {
857 /* this isn't fatal, as we can just either 1) flush
858 out with the next change, or 2) regen the summary
859 when we next load the uri */
860 g_warning ("failed to flush summary file to disk");
861 return TRUE; /* try again after the next timeout */
864 g_message ("Flushed summary to disk");
866 /* we only want this to execute once, so return FALSE and set
867 summary->flush_timeout to 0 */
868 summary->priv->flush_timeout = 0;
873 * e_book_backend_summary_touch:
874 * @summary: an #EBookBackendSummary
876 * Indicates that @summary has changed and should be flushed to disk.
879 e_book_backend_summary_touch (EBookBackendSummary *summary)
881 summary->priv->dirty = TRUE;
882 if (!summary->priv->flush_timeout
883 && summary->priv->flush_timeout_millis)
884 summary->priv->flush_timeout = g_timeout_add (summary->priv->flush_timeout_millis,
885 summary_flush_func, summary);
889 * e_book_backend_summary_is_up_to_date:
890 * @summary: an #EBookBackendSummary
891 * @t: the time to compare with
893 * Checks if @summary is more recent than @t.
895 * Return value: %TRUE if the summary is up to date, %FALSE otherwise.
898 e_book_backend_summary_is_up_to_date (EBookBackendSummary *summary, time_t t)
900 if (!e_book_backend_summary_open (summary))
903 return summary->priv->mtime >= t;
907 /* we only want to do summary queries if the query is over the set fields in the summary */
910 func_check(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
916 && argv[0]->type == ESEXP_RES_STRING
917 && argv[1]->type == ESEXP_RES_STRING) {
918 char *query_name = argv[0]->value.string;
920 if (!strcmp (query_name, "nickname") ||
921 !strcmp (query_name, "full_name") ||
922 !strcmp (query_name, "file_as") ||
923 !strcmp (query_name, "email")) {
928 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
929 r->value.bool = truth;
934 /* 'builtin' functions */
938 int type; /* set to 1 if a function can perform shortcut evaluation, or
939 doesn't execute everything, 0 otherwise */
940 } check_symbols[] = {
941 { "contains", func_check, 0 },
942 { "is", func_check, 0 },
943 { "beginswith", func_check, 0 },
944 { "endswith", func_check, 0 },
945 { "exists", func_check, 0 }
949 * e_book_backend_summary_is_summary_query:
950 * @summary: an #EBookBackendSummary
951 * @query: an s-expression to check
953 * Checks if @query can be satisfied by searching only the fields
954 * stored by @summary.
956 * Return value: %TRUE if the query can be satisfied, %FALSE otherwise.
959 e_book_backend_summary_is_summary_query (EBookBackendSummary *summary, const char *query)
969 for(i=0;i<sizeof(check_symbols)/sizeof(check_symbols[0]);i++) {
970 if (check_symbols[i].type == 1) {
971 e_sexp_add_ifunction(sexp, 0, check_symbols[i].name,
972 (ESExpIFunc *)check_symbols[i].func, summary);
974 e_sexp_add_function(sexp, 0, check_symbols[i].name,
975 check_symbols[i].func, summary);
979 e_sexp_input_text(sexp, query, strlen(query));
980 esexp_error = e_sexp_parse(sexp);
982 if (esexp_error == -1) {
986 r = e_sexp_eval(sexp);
988 retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
990 e_sexp_result_free(sexp, r);
999 /* the actual query mechanics */
1000 static ESExpResult *
1001 do_compare (EBookBackendSummary *summary, struct _ESExp *f, int argc,
1002 struct _ESExpResult **argv,
1003 char *(*compare)(const char*, const char*))
1005 GPtrArray *result = g_ptr_array_new ();
1010 && argv[0]->type == ESEXP_RES_STRING
1011 && argv[1]->type == ESEXP_RES_STRING) {
1013 for (i = 0; i < summary->priv->items->len; i ++) {
1014 EBookBackendSummaryItem *item = g_ptr_array_index (summary->priv->items, i);
1015 if (!strcmp (argv[0]->value.string, "full_name")) {
1016 char *given = item->given_name;
1017 char *surname = item->surname;
1018 char *full_name = item->full_name;
1020 if ((given && compare (given, argv[1]->value.string))
1021 || (surname && compare (surname, argv[1]->value.string))
1022 || (full_name && compare (full_name, argv[1]->value.string)))
1023 g_ptr_array_add (result, item->id);
1025 else if (!strcmp (argv[0]->value.string, "email")) {
1026 char *email_1 = item->email_1;
1027 char *email_2 = item->email_2;
1028 char *email_3 = item->email_3;
1029 char *email_4 = item->email_4;
1030 if ((email_1 && compare (email_1, argv[1]->value.string))
1031 || (email_2 && compare (email_2, argv[1]->value.string))
1032 || (email_3 && compare (email_3, argv[1]->value.string))
1033 || (email_4 && compare (email_4, argv[1]->value.string)))
1034 g_ptr_array_add (result, item->id);
1036 else if (!strcmp (argv[0]->value.string, "file_as")) {
1037 char *file_as = item->file_as;
1038 if (file_as && compare (file_as, argv[1]->value.string))
1039 g_ptr_array_add (result, item->id);
1041 else if (!strcmp (argv[0]->value.string, "nickname")) {
1042 char *nickname = item->nickname;
1043 if (nickname && compare (nickname, argv[1]->value.string))
1044 g_ptr_array_add (result, item->id);
1049 r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR);
1050 r->value.ptrarray = result;
1055 static ESExpResult *
1056 func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1058 EBookBackendSummary *summary = data;
1060 return do_compare (summary, f, argc, argv, (char *(*)(const char*, const char*)) e_util_utf8_strstrcase);
1064 is_helper (const char *s1, const char *s2)
1066 if (!e_util_utf8_strcasecmp(s1, s2))
1072 static ESExpResult *
1073 func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1075 EBookBackendSummary *summary = data;
1077 return do_compare (summary, f, argc, argv, is_helper);
1081 endswith_helper (const char *s1, const char *s2)
1084 if ((p = (char*)e_util_utf8_strstrcase(s1, s2))
1085 && (strlen(p) == strlen(s2)))
1091 static ESExpResult *
1092 func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1094 EBookBackendSummary *summary = data;
1096 return do_compare (summary, f, argc, argv, endswith_helper);
1100 beginswith_helper (const char *s1, const char *s2)
1103 if ((p = (char*)e_util_utf8_strstrcase(s1, s2))
1110 static ESExpResult *
1111 func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1113 EBookBackendSummary *summary = data;
1115 return do_compare (summary, f, argc, argv, beginswith_helper);
1118 /* 'builtin' functions */
1122 int type; /* set to 1 if a function can perform shortcut evaluation, or
1123 doesn't execute everything, 0 otherwise */
1125 { "contains", func_contains, 0 },
1126 { "is", func_is, 0 },
1127 { "beginswith", func_beginswith, 0 },
1128 { "endswith", func_endswith, 0 },
1132 * e_book_backend_summary_search:
1133 * @summary: an #EBookBackendSummary
1134 * @query: an s-expression
1136 * Searches @summary for contacts matching @query.
1138 * Return value: A #GPtrArray of pointers to contact ID strings.
1141 e_book_backend_summary_search (EBookBackendSummary *summary, const char *query)
1145 GPtrArray *retval = g_ptr_array_new();
1149 sexp = e_sexp_new();
1151 for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
1152 if (symbols[i].type == 1) {
1153 e_sexp_add_ifunction(sexp, 0, symbols[i].name,
1154 (ESExpIFunc *)symbols[i].func, summary);
1156 e_sexp_add_function(sexp, 0, symbols[i].name,
1157 symbols[i].func, summary);
1161 e_sexp_input_text(sexp, query, strlen(query));
1162 esexp_error = e_sexp_parse(sexp);
1164 if (esexp_error == -1) {
1168 r = e_sexp_eval(sexp);
1170 if (r && r->type == ESEXP_RES_ARRAY_PTR && r->value.ptrarray) {
1171 GPtrArray *ptrarray = r->value.ptrarray;
1174 for (i = 0; i < ptrarray->len; i ++)
1175 g_ptr_array_add (retval, g_ptr_array_index (ptrarray, i));
1178 e_sexp_result_free(sexp, r);
1180 e_sexp_unref (sexp);
1186 * e_book_backend_summary_get_summary_vcard:
1187 * @summary: an #EBookBackendSummary
1188 * @id: a unique contact ID
1190 * Constructs and returns a VCard from the contact summary specified
1193 * Return value: A new VCard, or %NULL if the contact summary didn't exist.
1196 e_book_backend_summary_get_summary_vcard(EBookBackendSummary *summary, const char *id)
1198 EBookBackendSummaryItem *item = g_hash_table_lookup (summary->priv->id_to_item, id);
1201 EContact *contact = e_contact_new ();
1204 e_contact_set (contact, E_CONTACT_UID, item->id);
1205 e_contact_set (contact, E_CONTACT_FILE_AS, item->file_as);
1206 e_contact_set (contact, E_CONTACT_GIVEN_NAME, item->given_name);
1207 e_contact_set (contact, E_CONTACT_FAMILY_NAME, item->surname);
1208 e_contact_set (contact, E_CONTACT_NICKNAME, item->nickname);
1209 e_contact_set (contact, E_CONTACT_FULL_NAME, item->full_name);
1210 e_contact_set (contact, E_CONTACT_EMAIL_1, item->email_1);
1211 e_contact_set (contact, E_CONTACT_EMAIL_2, item->email_2);
1212 e_contact_set (contact, E_CONTACT_EMAIL_3, item->email_3);
1213 e_contact_set (contact, E_CONTACT_EMAIL_4, item->email_4);
1215 e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (item->list));
1216 e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (item->list_show_addresses));
1217 e_contact_set (contact, E_CONTACT_WANTS_HTML, GINT_TO_POINTER (item->wants_html));
1219 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1221 g_object_unref (contact);
1226 g_warning ("in unable to locate card `%s' in summary", id);