1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-book-backend-file.c - File contact backend.
5 * Copyright (C) 2005 Novell, Inc.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Authors: Nat Friedman <nat@novell.com>
22 * Chris Toshok <toshok@ximian.com>
23 * Hans Petter Jansson <hpj@novell.com>
40 #include <glib/gstdio.h>
41 #include <glib/gi18n-lib.h>
43 #include "libedataserver/e-dbhash.h"
44 #include "libedataserver/e-db3-utils.h"
45 #include "libedataserver/e-data-server-util.h"
46 #include "libedataserver/e-flag.h"
48 #include "libebook/e-contact.h"
50 #include "libedata-book/e-book-backend-sexp.h"
51 #include "libedata-book/e-book-backend-summary.h"
52 #include "libedata-book/e-data-book.h"
53 #include "libedata-book/e-data-book-view.h"
55 #include "e-book-backend-file.h"
59 #define CHANGES_DB_SUFFIX ".changes.db"
61 #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
62 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
64 #define PAS_ID_PREFIX "pas-id-"
65 #define SUMMARY_FLUSH_TIMEOUT 5000
67 static EBookBackendSyncClass *e_book_backend_file_parent_class;
69 struct _EBookBackendFilePrivate {
72 char *summary_filename;
75 EBookBackendSummary *summary;
83 static GStaticMutex global_env_lock = G_STATIC_MUTEX_INIT;
89 static EBookBackendSyncStatus
90 db_error_to_status (const int db_error)
94 return GNOME_Evolution_Addressbook_Success;
96 return GNOME_Evolution_Addressbook_ContactNotFound;
98 return GNOME_Evolution_Addressbook_PermissionDenied;
100 return GNOME_Evolution_Addressbook_OtherError;
105 string_to_dbt(const char *str, DBT *dbt)
107 memset (dbt, 0, sizeof (*dbt));
108 dbt->data = (void*)str;
109 dbt->size = strlen (str) + 1;
110 dbt->flags = DB_DBT_USERMEM;
114 create_contact (char *uid, const char *vcard)
116 EContact *contact = e_contact_new_from_vcard (vcard);
117 if (!e_contact_get_const (contact, E_CONTACT_UID))
118 e_contact_set (contact, E_CONTACT_UID, uid);
124 build_summary (EBookBackendFilePrivate *bfpriv)
126 DB *db = bfpriv->file_db;
129 DBT id_dbt, vcard_dbt;
131 db_error = db->cursor (db, NULL, &dbc, 0);
134 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
138 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
139 memset (&id_dbt, 0, sizeof (id_dbt));
140 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_FIRST);
142 while (db_error == 0) {
144 /* don't include the version in the list of cards */
145 if (id_dbt.size != strlen(E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
146 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
147 EContact *contact = create_contact (id_dbt.data, vcard_dbt.data);
148 e_book_backend_summary_add_contact (bfpriv->summary, contact);
149 g_object_unref (contact);
152 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_NEXT);
160 e_book_backend_file_create_unique_id (void)
162 /* use a 32 counter and the 32 bit timestamp to make an id.
163 it's doubtful 2^32 id's will be created in a second, so we
166 return g_strdup_printf (PAS_ID_PREFIX "%08lX%08X", time(NULL), c++);
170 set_revision (EContact *contact)
172 char time_string[25] = {0};
173 const struct tm *tm = NULL;
176 g_get_current_time (&tv);
177 tm = gmtime (&tv.tv_sec);
179 strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
180 e_contact_set (contact, E_CONTACT_REV, time_string);
184 static EBookBackendSyncStatus
185 do_create(EBookBackendFile *bf,
186 const char *vcard_req,
189 DB *db = bf->priv->file_db;
190 DBT id_dbt, vcard_dbt;
197 g_assert (vcard_req);
200 id = e_book_backend_file_create_unique_id ();
202 string_to_dbt (id, &id_dbt);
204 *contact = e_contact_new_from_vcard (vcard_req);
205 e_contact_set (*contact, E_CONTACT_UID, id);
206 rev = e_contact_get_const (*contact, E_CONTACT_REV);
208 set_revision (*contact);
210 vcard = e_vcard_to_string (E_VCARD (*contact), EVC_FORMAT_VCARD_30);
212 string_to_dbt (vcard, &vcard_dbt);
214 db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
219 db_error = db->sync (db, 0);
221 g_warning ("db->sync failed with %s", db_strerror (db_error));
224 g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
225 g_object_unref (*contact);
230 return db_error_to_status (db_error);
233 static EBookBackendSyncStatus
234 e_book_backend_file_create_contact (EBookBackendSync *backend,
240 EBookBackendSyncStatus status;
241 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
243 status = do_create (bf, vcard, contact);
244 if (status == GNOME_Evolution_Addressbook_Success) {
245 e_book_backend_summary_add_contact (bf->priv->summary, *contact);
250 static EBookBackendSyncStatus
251 e_book_backend_file_remove_contacts (EBookBackendSync *backend,
257 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
258 DB *db = bf->priv->file_db;
263 GList *removed_cards = NULL;
264 GNOME_Evolution_Addressbook_CallStatus rv = GNOME_Evolution_Addressbook_Success;
266 for (l = id_list; l; l = l->next) {
269 string_to_dbt (id, &id_dbt);
271 db_error = db->del (db, NULL, &id_dbt, 0);
273 g_warning (G_STRLOC ": db->del failed with %s", db_strerror (db_error));
274 rv = db_error_to_status (db_error);
278 removed_cards = g_list_prepend (removed_cards, id);
281 /* if we actually removed some, try to sync */
283 db_error = db->sync (db, 0);
285 g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
288 *ids = removed_cards;
290 for (l = removed_cards; l; l = l->next) {
292 e_book_backend_summary_remove_contact (bf->priv->summary, id);
298 static EBookBackendSyncStatus
299 e_book_backend_file_modify_contact (EBookBackendSync *backend,
305 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
306 DB *db = bf->priv->file_db;
307 DBT id_dbt, vcard_dbt;
309 const char *id, *lookup_id;
310 char *vcard_with_rev;
312 *contact = e_contact_new_from_vcard (vcard);
313 id = e_contact_get_const (*contact, E_CONTACT_UID);
316 return GNOME_Evolution_Addressbook_OtherError;
318 /* update the revision (modified time of contact) */
319 set_revision (*contact);
320 vcard_with_rev = e_vcard_to_string (E_VCARD (*contact), EVC_FORMAT_VCARD_30);
322 /* This is disgusting, but for a time cards were added with
323 ID's that are no longer used (they contained both the uri
324 and the id.) If we recognize it as a uri (file:///...) trim
325 off everything before the last '/', and use that as the
327 if (!strncmp (id, "file:///", strlen ("file:///"))) {
328 lookup_id = strrchr (id, '/') + 1;
333 string_to_dbt (lookup_id, &id_dbt);
334 string_to_dbt (vcard_with_rev, &vcard_dbt);
336 db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
339 db_error = db->sync (db, 0);
341 g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
343 e_book_backend_summary_remove_contact (bf->priv->summary, id);
344 e_book_backend_summary_add_contact (bf->priv->summary, *contact);
347 g_warning (G_STRLOC ": db->put failed with %s", db_strerror(db_error));
349 g_free (vcard_with_rev);
352 return GNOME_Evolution_Addressbook_Success;
354 return db_error_to_status (db_error);
357 static EBookBackendSyncStatus
358 e_book_backend_file_get_contact (EBookBackendSync *backend,
364 EBookBackendFile *bf;
366 DBT id_dbt, vcard_dbt;
369 bf = E_BOOK_BACKEND_FILE (backend);
370 db = bf->priv->file_db;
372 string_to_dbt (id, &id_dbt);
373 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
374 vcard_dbt.flags = DB_DBT_MALLOC;
376 db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
379 *vcard = vcard_dbt.data;
380 return GNOME_Evolution_Addressbook_Success;
382 g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
383 *vcard = g_strdup ("");
384 return GNOME_Evolution_Addressbook_ContactNotFound;
388 static EBookBackendSyncStatus
389 e_book_backend_file_get_contact_list (EBookBackendSync *backend,
395 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
396 DB *db = bf->priv->file_db;
399 DBT id_dbt, vcard_dbt;
400 EBookBackendSExp *card_sexp = NULL;
401 gboolean search_needed;
402 const char *search = query;
403 GList *contact_list = NULL;
404 EBookBackendSyncStatus status;
406 d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
407 status = GNOME_Evolution_Addressbook_Success;
408 if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
410 /* do a summary query */
411 GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
414 for (i = 0; i < ids->len; i ++) {
415 char *id = g_ptr_array_index (ids, i);
416 string_to_dbt (id, &id_dbt);
417 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
418 vcard_dbt.flags = DB_DBT_MALLOC;
420 db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
422 contact_list = g_list_prepend (contact_list, vcard_dbt.data);
424 g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
425 status = db_error_to_status (db_error);
429 g_ptr_array_free (ids, TRUE);
431 search_needed = TRUE;
432 if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
433 search_needed = FALSE;
435 card_sexp = e_book_backend_sexp_new (search);
437 /* XXX this needs to be an invalid query error of some sort*/
438 return GNOME_Evolution_Addressbook_OtherError;
441 db_error = db->cursor (db, NULL, &dbc, 0);
444 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
445 /* XXX this needs to be some CouldNotOpen error */
446 return db_error_to_status (db_error);
449 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
450 vcard_dbt.flags = DB_DBT_MALLOC;
451 memset (&id_dbt, 0, sizeof (id_dbt));
452 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_FIRST);
454 while (db_error == 0) {
456 /* don't include the version in the list of cards */
457 if (id_dbt.size != strlen(E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
458 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
460 if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard (card_sexp, vcard_dbt.data))) {
461 contact_list = g_list_prepend (contact_list, vcard_dbt.data);
465 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_NEXT);
468 g_object_unref (card_sexp);
470 if (db_error == DB_NOTFOUND) {
471 status = GNOME_Evolution_Addressbook_Success;
473 g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error));
474 status = db_error_to_status (db_error);
477 db_error = dbc->c_close(dbc);
479 g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error));
483 *contacts = contact_list;
488 EBookBackendFile *bf;
491 } FileBackendSearchClosure;
494 closure_destroy (FileBackendSearchClosure *closure)
496 d(printf ("destroying search closure\n"));
497 e_flag_free (closure->running);
501 static FileBackendSearchClosure*
502 init_closure (EDataBookView *book_view, EBookBackendFile *bf)
504 FileBackendSearchClosure *closure = g_new (FileBackendSearchClosure, 1);
507 closure->thread = NULL;
508 closure->running = e_flag_new ();
510 g_object_set_data_full (G_OBJECT (book_view), "EBookBackendFile.BookView::closure",
511 closure, (GDestroyNotify)closure_destroy);
516 static FileBackendSearchClosure*
517 get_closure (EDataBookView *book_view)
519 return g_object_get_data (G_OBJECT (book_view), "EBookBackendFile.BookView::closure");
523 book_view_thread (gpointer data)
525 EDataBookView *book_view;
526 FileBackendSearchClosure *closure;
527 EBookBackendFile *bf;
530 DBT id_dbt, vcard_dbt;
532 gboolean allcontacts;
534 g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL);
537 closure = get_closure (book_view);
539 g_warning (G_STRLOC ": NULL closure in book view thread");
544 d(printf ("starting initial population of book view\n"));
546 /* ref the book view because it'll be removed and unrefed
547 when/if it's stopped */
548 bonobo_object_ref (book_view);
550 db = bf->priv->file_db;
551 query = e_data_book_view_get_card_query (book_view);
553 if ( ! strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
554 e_data_book_view_notify_status_message (book_view, _("Loading..."));
557 e_data_book_view_notify_status_message (book_view, _("Searching..."));
561 d(printf ("signalling parent thread\n"));
562 e_flag_set (closure->running);
564 if (e_book_backend_summary_is_summary_query (bf->priv->summary, query)) {
565 /* do a summary query */
566 GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, e_data_book_view_get_card_query (book_view));
569 for (i = 0; i < ids->len; i ++) {
570 char *id = g_ptr_array_index (ids, i);
572 if (!e_flag_is_set (closure->running))
575 string_to_dbt (id, &id_dbt);
576 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
577 vcard_dbt.flags = DB_DBT_MALLOC;
579 db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
582 e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard_dbt.data);
583 g_free (vcard_dbt.data);
586 g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
590 g_ptr_array_free (ids, TRUE);
593 /* iterate over the db and do the query there */
596 memset (&id_dbt, 0, sizeof (id_dbt));
597 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
598 vcard_dbt.flags = DB_DBT_MALLOC;
600 db_error = db->cursor (db, NULL, &dbc, 0);
603 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_FIRST);
604 while (db_error == 0) {
606 if (!e_flag_is_set (closure->running))
609 /* don't include the version in the list of cards */
610 if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
612 e_data_book_view_notify_update_prefiltered_vcard (book_view, id_dbt.data, vcard_dbt.data);
614 e_data_book_view_notify_update_vcard (book_view, vcard_dbt.data);
615 g_free (vcard_dbt.data);
617 g_free (vcard_dbt.data);
620 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_NEXT);
624 if (db_error && db_error != DB_NOTFOUND)
625 g_warning ("e_book_backend_file_search: error building list: %s",
626 db_strerror (db_error));
628 else if (db_error == DB_RUNRECOVERY) {
629 g_warning ("e_book_backend_file_search: error getting the cursor for %s",
637 if (e_flag_is_set (closure->running))
638 e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_Success);
641 printf("book_view file uref \n");
642 bonobo_object_unref (book_view);
644 d(printf ("finished population of book view\n"));
650 e_book_backend_file_start_book_view (EBookBackend *backend,
651 EDataBookView *book_view)
653 FileBackendSearchClosure *closure = init_closure (book_view, E_BOOK_BACKEND_FILE (backend));
655 d(printf ("starting book view thread\n"));
656 closure->thread = g_thread_create (book_view_thread, book_view, TRUE, NULL);
658 e_flag_wait (closure->running);
660 /* at this point we know the book view thread is actually running */
661 d(printf ("returning from start_book_view\n"));
665 e_book_backend_file_stop_book_view (EBookBackend *backend,
666 EDataBookView *book_view)
668 FileBackendSearchClosure *closure = get_closure (book_view);
671 d(printf ("stopping query\n"));
672 need_join = e_flag_is_set (closure->running);
673 e_flag_clear (closure->running);
676 g_thread_join (closure->thread);
688 } EBookBackendFileChangeContext;
691 e_book_backend_file_changes_foreach_key (const char *key, gpointer user_data)
693 EBookBackendFileChangeContext *ctx = user_data;
695 DBT id_dbt, vcard_dbt;
698 string_to_dbt (key, &id_dbt);
699 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
700 vcard_dbt.flags = DB_DBT_MALLOC;
702 db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
706 char *id = id_dbt.data;
709 contact = e_contact_new ();
710 e_contact_set (contact, E_CONTACT_UID, id);
712 vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
714 ctx->del_ids = g_list_append (ctx->del_ids,
716 ctx->del_cards = g_list_append (ctx->del_cards,
719 g_object_unref (contact);
721 g_free (vcard_dbt.data);
725 static EBookBackendSyncStatus
726 e_book_backend_file_get_changes (EBookBackendSync *backend,
729 const char *change_id,
732 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
734 DBT id_dbt, vcard_dbt;
738 DB *db = bf->priv->file_db;
740 GList *changes = NULL;
741 EBookBackendFileChangeContext ctx;
742 EBookBackendSyncStatus result;
744 memset (&id_dbt, 0, sizeof (id_dbt));
745 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
747 memset (&ctx, 0, sizeof (ctx));
751 /* Find the changed ids */
752 filename = g_strdup_printf ("%s/%s" CHANGES_DB_SUFFIX, bf->priv->dirname, change_id);
753 ehash = e_dbhash_new (filename);
756 db_error = db->cursor (db, NULL, &dbc, 0);
759 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
761 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_FIRST);
763 while (db_error == 0) {
765 /* don't include the version in the list of cards */
766 if (id_dbt.size != strlen(E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
767 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
769 char *id = id_dbt.data;
772 /* Remove fields the user can't change
773 * and can change without the rest of the
776 contact = create_contact (id_dbt.data, vcard_dbt.data);
779 g_object_set (card, "last_use", NULL, "use_score", 0.0, NULL);
781 vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
782 g_object_unref (contact);
784 /* check what type of change has occurred, if any */
785 switch (e_dbhash_compare (ehash, id, vcard_string)) {
786 case E_DBHASH_STATUS_SAME:
787 g_free(vcard_string);
789 case E_DBHASH_STATUS_NOT_FOUND:
790 ctx.add_cards = g_list_append (ctx.add_cards, vcard_string);
791 ctx.add_ids = g_list_append (ctx.add_ids, g_strdup(id));
793 case E_DBHASH_STATUS_DIFFERENT:
794 ctx.mod_cards = g_list_append (ctx.mod_cards, vcard_string);
795 ctx.mod_ids = g_list_append (ctx.mod_ids, g_strdup(id));
800 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_NEXT);
805 e_dbhash_foreach_key (ehash, (EDbHashFunc)e_book_backend_file_changes_foreach_key, &ctx);
807 /* Send the changes */
808 if (db_error != DB_NOTFOUND) {
809 g_warning ("e_book_backend_file_changes: error building list\n");
811 result = db_error_to_status (db_error);
814 /* Update the hash and build our changes list */
815 for (i = ctx.add_ids, v = ctx.add_cards; i != NULL; i = i->next, v = v->next){
817 char *vcard = v->data;
819 e_dbhash_add (ehash, id, vcard);
820 changes = g_list_prepend (changes,
821 e_book_backend_change_add_new (vcard));
826 for (i = ctx.mod_ids, v = ctx.mod_cards; i != NULL; i = i->next, v = v->next){
828 char *vcard = v->data;
830 e_dbhash_add (ehash, id, vcard);
831 changes = g_list_prepend (changes,
832 e_book_backend_change_modify_new (vcard));
837 for (i = ctx.del_ids, v = ctx.del_cards; i != NULL; i = i->next, v = v->next){
839 char *vcard = v->data;
841 e_dbhash_remove (ehash, id);
843 changes = g_list_prepend (changes,
844 e_book_backend_change_delete_new (vcard));
849 e_dbhash_write (ehash);
851 result = GNOME_Evolution_Addressbook_Success;
852 *changes_out = changes;
855 e_dbhash_destroy (ehash);
857 return GNOME_Evolution_Addressbook_Success;
861 e_book_backend_file_extract_path_from_uri (const char *uri)
863 g_assert (g_ascii_strncasecmp (uri, "file://", 7) == 0);
865 return g_filename_from_uri (uri, NULL, NULL);
868 static EBookBackendSyncStatus
869 e_book_backend_file_authenticate_user (EBookBackendSync *backend,
874 const char *auth_method)
876 return GNOME_Evolution_Addressbook_Success;
879 static EBookBackendSyncStatus
880 e_book_backend_file_get_required_fields (EBookBackendSync *backend,
885 GList *fields = NULL;
887 fields = g_list_append (fields , g_strdup(e_contact_field_name (E_CONTACT_FILE_AS)));
888 *fields_out = fields;
889 return GNOME_Evolution_Addressbook_Success;
893 static EBookBackendSyncStatus
894 e_book_backend_file_get_supported_fields (EBookBackendSync *backend,
899 GList *fields = NULL;
902 /* XXX we need a way to say "we support everything", since the
904 for (i = 1; i < E_CONTACT_FIELD_LAST; i ++)
905 fields = g_list_append (fields, g_strdup (e_contact_field_name (i)));
907 *fields_out = fields;
908 return GNOME_Evolution_Addressbook_Success;
914 ** 0.0 just a list of cards
916 ** 0.1 same as 0.0, but with the version tag
918 ** 0.2 not a real format upgrade, just a hack to fix broken ids caused
919 ** by a bug in early betas, but we only need to convert them if
920 ** the previous version is 0.1, since the bug existed after 0.1
924 e_book_backend_file_upgrade_db (EBookBackendFile *bf, char *old_version)
926 DB *db = bf->priv->file_db;
928 DBT version_name_dbt, version_dbt;
930 if (strcmp (old_version, "0.0")
931 && strcmp (old_version, "0.1")) {
932 g_warning ("unsupported version '%s' found in PAS backend file\n",
937 if (!strcmp (old_version, "0.1")) {
938 /* we just loop through all the cards in the db,
939 giving them valid ids if they don't have them */
940 DBT id_dbt, vcard_dbt;
944 db_error = db->cursor (db, NULL, &dbc, 0);
946 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
950 memset (&id_dbt, 0, sizeof (id_dbt));
951 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
953 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_FIRST);
955 while (db_error == 0) {
956 if (id_dbt.size != strlen(E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
957 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
960 contact = create_contact (id_dbt.data, vcard_dbt.data);
962 /* the cards we're looking for are
963 created with a normal id dbt, but
964 with the id field in the vcard set
965 to something that doesn't match.
966 so, we need to modify the card to
967 have the same id as the the dbt. */
968 if (strcmp (id_dbt.data, e_contact_get_const (contact, E_CONTACT_UID))) {
971 e_contact_set (contact, E_CONTACT_UID, id_dbt.data);
973 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
974 string_to_dbt (vcard, &vcard_dbt);
976 db_error = db->put (db, NULL,
977 &id_dbt, &vcard_dbt, 0);
985 g_object_unref (contact);
988 db_error = dbc->c_get(dbc, &id_dbt, &vcard_dbt, DB_NEXT);
992 g_warning ("failed to update %d cards", card_failed);
997 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt);
998 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION, &version_dbt);
1000 db_error = db->put (db, NULL, &version_name_dbt, &version_dbt, 0);
1008 e_book_backend_file_maybe_upgrade_db (EBookBackendFile *bf)
1010 DB *db = bf->priv->file_db;
1011 DBT version_name_dbt, version_dbt;
1014 gboolean ret_val = TRUE;
1016 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt);
1017 memset (&version_dbt, 0, sizeof (version_dbt));
1018 version_dbt.flags = DB_DBT_MALLOC;
1020 db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0);
1021 if (db_error == 0) {
1023 version = version_dbt.data;
1026 /* key was not in file */
1027 version = g_strdup ("0.0");
1030 if (strcmp (version, E_BOOK_BACKEND_FILE_VERSION))
1031 ret_val = e_book_backend_file_upgrade_db (bf, version);
1038 #ifdef CREATE_DEFAULT_VCARD
1039 # include <libedata-book/ximian-vcard.h>
1043 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
1044 file_errcall (const DB_ENV *env, const char *buf1, const char *buf2)
1046 file_errcall (const char *buf1, char *buf2)
1049 g_warning ("libdb error: %s", buf2);
1053 static GNOME_Evolution_Addressbook_CallStatus
1054 e_book_backend_file_load_source (EBookBackend *backend,
1056 gboolean only_if_exists)
1058 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1059 char *dirname, *filename;
1060 gboolean writable = FALSE;
1068 uri = e_source_get_uri (source);
1070 dirname = e_book_backend_file_extract_path_from_uri (uri);
1071 filename = g_build_filename (dirname, "addressbook.db", NULL);
1074 db_error = e_db3_utils_maybe_recover (filename);
1075 if (db_error != 0) {
1076 g_warning ("db recovery failed with %s", db_strerror (db_error));
1079 return db_error_to_status (db_error);
1082 g_static_mutex_lock(&global_env_lock);
1083 if (global_env.ref_count > 0) {
1084 env = global_env.env;
1085 global_env.ref_count++;
1087 db_error = db_env_create (&env, 0);
1088 if (db_error != 0) {
1089 g_warning ("db_env_create failed with %s", db_strerror (db_error));
1090 g_static_mutex_unlock(&global_env_lock);
1093 return db_error_to_status (db_error);
1096 env->set_errcall (env, file_errcall);
1098 /* Set the allocation routines to the non-aborting GLib functions */
1099 env->set_alloc (env, (void *(*)(size_t))g_try_malloc,
1100 (void *(*)(void *, size_t))g_try_realloc,
1103 db_error = (*env->open) (env, NULL, DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0);
1104 if (db_error != 0) {
1106 g_warning ("db_env_open failed with %s", db_strerror (db_error));
1107 g_static_mutex_unlock(&global_env_lock);
1110 return db_error_to_status (db_error);
1113 global_env.env = env;
1114 global_env.ref_count = 1;
1116 g_static_mutex_unlock(&global_env_lock);
1118 bf->priv->env = env;
1120 db_error = db_create (&db, env, 0);
1121 if (db_error != 0) {
1122 g_warning ("db_create failed with %s", db_strerror (db_error));
1125 return db_error_to_status (db_error);
1128 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
1130 if (db_error == DB_OLD_VERSION) {
1131 db_error = e_db3_utils_upgrade_format (filename);
1133 if (db_error != 0) {
1134 g_warning ("db format upgrade failed with %s", db_strerror (db_error));
1137 return db_error_to_status (db_error);
1140 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
1143 bf->priv->file_db = db;
1145 if (db_error == 0) {
1150 db_error = db_create (&db, env, 0);
1151 if (db_error != 0) {
1152 g_warning ("db_create failed with %s", db_strerror (db_error));
1155 return GNOME_Evolution_Addressbook_OtherError;
1157 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD, 0666);
1159 if (db_error != 0 && !only_if_exists) {
1162 /* the database didn't exist, so we create the
1163 directory then the .db */
1165 rv = g_mkdir_with_parents (dirname, 0777);
1166 if (rv == -1 && errno != EEXIST) {
1167 g_warning ("failed to make directory %s: %s", dirname, strerror (errno));
1170 if (errno == EACCES || errno == EPERM)
1171 return GNOME_Evolution_Addressbook_PermissionDenied;
1173 return GNOME_Evolution_Addressbook_OtherError;
1176 db_error = db_create (&db, env, 0);
1177 if (db_error != 0) {
1178 g_warning ("db_create failed with %s", db_strerror (db_error));
1181 return GNOME_Evolution_Addressbook_OtherError;
1184 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0666);
1185 if (db_error != 0) {
1187 g_warning ("db->open (... DB_CREATE ...) failed with %s", db_strerror (db_error));
1190 #ifdef CREATE_DEFAULT_VCARD
1191 EContact *contact = NULL;
1193 do_create(bf, XIMIAN_VCARD, &contact);
1194 /* XXX check errors here */
1196 g_object_unref (contact);
1204 if (db_error != 0) {
1205 bf->priv->file_db = NULL;
1208 return db_error_to_status (db_error);
1211 if (!e_book_backend_file_maybe_upgrade_db (bf)) {
1213 bf->priv->file_db = NULL;
1214 g_warning ("e_book_backend_file_maybe_upgrade_db failed");
1217 return GNOME_Evolution_Addressbook_OtherError;
1220 g_free (bf->priv->dirname);
1221 g_free (bf->priv->filename);
1222 bf->priv->dirname = dirname;
1223 bf->priv->filename = filename;
1225 if (g_stat (bf->priv->filename, &sb) == -1) {
1227 bf->priv->file_db = NULL;
1228 g_warning ("stat(%s) failed", bf->priv->filename);
1229 return GNOME_Evolution_Addressbook_OtherError;
1231 db_mtime = sb.st_mtime;
1233 g_free (bf->priv->summary_filename);
1234 bf->priv->summary_filename = g_strconcat (bf->priv->filename, ".summary", NULL);
1235 bf->priv->summary = e_book_backend_summary_new (bf->priv->summary_filename, SUMMARY_FLUSH_TIMEOUT);
1237 if (e_book_backend_summary_is_up_to_date (bf->priv->summary, db_mtime) == FALSE
1238 || e_book_backend_summary_load (bf->priv->summary) == FALSE ) {
1239 build_summary (bf->priv);
1242 e_book_backend_set_is_loaded (backend, TRUE);
1243 e_book_backend_set_is_writable (backend, writable);
1244 return GNOME_Evolution_Addressbook_Success;
1248 select_changes (const char *name)
1252 if (strlen (name) < strlen (CHANGES_DB_SUFFIX))
1255 p = strstr (name, CHANGES_DB_SUFFIX);
1259 if (strlen (p) != strlen (CHANGES_DB_SUFFIX))
1265 static EBookBackendSyncStatus
1266 e_book_backend_file_remove (EBookBackendSync *backend,
1270 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1273 if (-1 == g_unlink (bf->priv->filename)) {
1274 if (errno == EACCES || errno == EPERM)
1275 return GNOME_Evolution_Addressbook_PermissionDenied;
1277 return GNOME_Evolution_Addressbook_OtherError;
1280 /* unref the summary before we remove the file so it's not written out again */
1281 g_object_unref (bf->priv->summary);
1282 bf->priv->summary = NULL;
1283 if (-1 == g_unlink (bf->priv->summary_filename))
1284 g_warning ("failed to remove summary file `%s`: %s", bf->priv->summary_filename, strerror (errno));
1286 dir = g_dir_open (bf->priv->dirname, 0, NULL);
1290 while ((name = g_dir_read_name (dir))) {
1291 if (select_changes (name)) {
1292 char *full_path = g_build_filename (bf->priv->dirname, name, NULL);
1293 if (-1 == g_unlink (full_path)) {
1294 g_warning ("failed to remove change db `%s': %s", full_path, strerror (errno));
1303 if (-1 == g_rmdir (bf->priv->dirname))
1304 g_warning ("failed to remove directory `%s`: %s", bf->priv->dirname, strerror (errno));
1306 /* we may not have actually succeeded in removing the
1307 backend's files/dirs, but there's nothing we can do about
1308 it here.. the only time we should return failure is if we
1309 failed to remove the actual data. a failure should mean
1310 that the addressbook is still valid */
1311 return GNOME_Evolution_Addressbook_Success;
1315 e_book_backend_file_get_static_capabilities (EBookBackend *backend)
1317 return g_strdup("local,do-initial-query,bulk-removes,contact-lists");
1320 static GNOME_Evolution_Addressbook_CallStatus
1321 e_book_backend_file_cancel_operation (EBookBackend *backend, EDataBook *book)
1323 return GNOME_Evolution_Addressbook_CouldNotCancel;
1326 e_book_backend_file_set_mode (EBookBackend *backend, GNOME_Evolution_Addressbook_BookMode mode)
1328 if (e_book_backend_is_loaded (backend)) {
1329 e_book_backend_notify_writable (backend, TRUE);
1330 e_book_backend_notify_connection_status (backend, TRUE);
1335 e_book_backend_file_sync (EBookBackend *backend)
1337 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1340 g_return_if_fail (bf != NULL);
1342 if (bf->priv->file_db) {
1343 db_error = bf->priv->file_db->sync (bf->priv->file_db, 0);
1345 g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
1350 e_book_backend_file_construct (EBookBackendFile *backend)
1352 g_assert (backend != NULL);
1353 g_assert (E_IS_BOOK_BACKEND_FILE (backend));
1355 if (! e_book_backend_construct (E_BOOK_BACKEND (backend)))
1362 * e_book_backend_file_new:
1365 e_book_backend_file_new (void)
1367 EBookBackendFile *backend;
1369 backend = g_object_new (E_TYPE_BOOK_BACKEND_FILE, NULL);
1371 if (! e_book_backend_file_construct (backend)) {
1372 g_object_unref (backend);
1377 return E_BOOK_BACKEND (backend);
1381 e_book_backend_file_dispose (GObject *object)
1383 EBookBackendFile *bf;
1385 bf = E_BOOK_BACKEND_FILE (object);
1387 if (bf->priv->file_db) {
1388 bf->priv->file_db->close (bf->priv->file_db, 0);
1389 bf->priv->file_db = NULL;
1392 g_static_mutex_lock(&global_env_lock);
1393 global_env.ref_count--;
1394 if (global_env.ref_count == 0) {
1395 global_env.env->close (global_env.env, 0);
1396 global_env.env = NULL;
1398 g_static_mutex_unlock (&global_env_lock);
1400 if (bf->priv->summary) {
1401 g_object_unref (bf->priv->summary);
1402 bf->priv->summary = NULL;
1405 G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
1409 e_book_backend_file_finalize (GObject *object)
1411 EBookBackendFile *bf;
1413 bf = E_BOOK_BACKEND_FILE (object);
1415 g_free (bf->priv->filename);
1416 g_free (bf->priv->dirname);
1417 g_free (bf->priv->summary_filename);
1421 G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object);
1425 /* Avoid compiler warning by providing a function with exactly the
1426 * prototype that db_env_set_func_open() wants for the open method.
1430 my_open (const char *name, int oflag, ...)
1434 if (oflag & O_CREAT) {
1436 va_start (arg, oflag);
1437 mode = va_arg (arg, int);
1441 return g_open (name, oflag, mode);
1445 my_rename (const char *oldname, const char *newname)
1447 return g_rename (oldname, newname);
1451 my_exists (const char *name, int *isdirp)
1453 if (!g_file_test (name, G_FILE_TEST_EXISTS))
1456 *isdirp = g_file_test (name, G_FILE_TEST_IS_DIR);
1461 my_unlink (const char *name)
1463 return g_unlink (name);
1469 e_book_backend_file_class_init (EBookBackendFileClass *klass)
1471 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1472 EBookBackendSyncClass *sync_class;
1473 EBookBackendClass *backend_class;
1475 e_book_backend_file_parent_class = g_type_class_peek_parent (klass);
1477 sync_class = E_BOOK_BACKEND_SYNC_CLASS (klass);
1478 backend_class = E_BOOK_BACKEND_CLASS (klass);
1480 /* Set the virtual methods. */
1481 backend_class->load_source = e_book_backend_file_load_source;
1482 backend_class->get_static_capabilities = e_book_backend_file_get_static_capabilities;
1483 backend_class->start_book_view = e_book_backend_file_start_book_view;
1484 backend_class->stop_book_view = e_book_backend_file_stop_book_view;
1485 backend_class->cancel_operation = e_book_backend_file_cancel_operation;
1486 backend_class->set_mode = e_book_backend_file_set_mode;
1487 backend_class->sync = e_book_backend_file_sync;
1488 sync_class->remove_sync = e_book_backend_file_remove;
1489 sync_class->create_contact_sync = e_book_backend_file_create_contact;
1490 sync_class->remove_contacts_sync = e_book_backend_file_remove_contacts;
1491 sync_class->modify_contact_sync = e_book_backend_file_modify_contact;
1492 sync_class->get_contact_sync = e_book_backend_file_get_contact;
1493 sync_class->get_contact_list_sync = e_book_backend_file_get_contact_list;
1494 sync_class->get_changes_sync = e_book_backend_file_get_changes;
1495 sync_class->authenticate_user_sync = e_book_backend_file_authenticate_user;
1496 sync_class->get_supported_fields_sync = e_book_backend_file_get_supported_fields;
1497 sync_class->get_required_fields_sync = e_book_backend_file_get_required_fields;
1500 object_class->dispose = e_book_backend_file_dispose;
1501 object_class->finalize = e_book_backend_file_finalize;
1504 /* Use the gstdio wrappers to open, check, rename and unlink
1507 db_env_set_func_open (my_open);
1508 db_env_set_func_close (close);
1509 db_env_set_func_exists (my_exists);
1510 db_env_set_func_rename (my_rename);
1511 db_env_set_func_unlink (my_unlink);
1516 e_book_backend_file_init (EBookBackendFile *backend)
1518 EBookBackendFilePrivate *priv;
1520 priv = g_new0 (EBookBackendFilePrivate, 1);
1522 backend->priv = priv;
1526 * e_book_backend_file_get_type:
1529 e_book_backend_file_get_type (void)
1531 static GType type = 0;
1535 sizeof (EBookBackendFileClass),
1536 NULL, /* base_class_init */
1537 NULL, /* base_class_finalize */
1538 (GClassInitFunc) e_book_backend_file_class_init,
1539 NULL, /* class_finalize */
1540 NULL, /* class_data */
1541 sizeof (EBookBackendFile),
1542 0, /* n_preallocs */
1543 (GInstanceInitFunc) e_book_backend_file_init
1546 type = g_type_register_static (E_TYPE_BOOK_BACKEND_SYNC, "EBookBackendFile", &info, 0);