/* MultiSync Evolution Plugin - Synchronize Ximian Evolution data Addressbook synchronization. Copyright (C) 2002-2003 Tobias Karlsson and Bo Lincoln This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * $Id: addr_sync.c,v 1.22 2003/11/03 18:49:30 lincoln Exp $ */ #include #include #include #include "evolution_sync.h" #include "addr_sync.h" extern gboolean multisync_debug; // Returns the UID of a vcard as a g_malloced string. char* evo_addr_get_uid(char *vcard) { char *pos = vcard; while (pos) { if (!strncmp(pos, "UID:", 4)) { char uid[256]; if (sscanf(pos, "UID:%255[^\r\n]", uid) > 0) { return(g_strdup(uid)); } } pos = strstr(pos, "\n"); if (pos) pos++; } return(NULL); } void evo_addr_change(evolution_connection *conn, GList *ids, GList *cards, int change_type) { switch(conn->addr_mode) { case EVO_ADDR_MODE_GETVIEW: break; case EVO_ADDR_MODE_GETCHANGES: case EVO_ADDR_MODE_GETALL: { // Add to change list int n; if (change_type == SYNC_OBJ_ADDED || change_type == SYNC_OBJ_MODIFIED) { for (n = 0; n < g_list_length(cards); n++) { ECard *ecard; changed_object* change; char *tmp; change = g_malloc0(sizeof(changed_object)); g_assert(change); ecard = g_list_nth_data(cards, n); tmp = e_card_get_vcard_assume_utf8(ecard); if (tmp) { change->comp = sync_vtype_convert(tmp, 0, NULL); // Shape it up g_free(tmp); } change->change_type = change_type; change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK; change->uid = evo_addr_get_uid(change->comp); conn->addr_changes = evo_append_change(conn->addr_changes, change); } } if (!cards && ids) { for (n = 0; n < g_list_length(ids); n++) { char *id = g_list_nth_data(ids, n); changed_object* change; change = g_malloc0(sizeof(changed_object)); g_assert(change); change->change_type = change_type; change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK; change->uid = g_strdup(id); conn->addr_changes = evo_append_change(conn->addr_changes, change); } } } break; case EVO_ADDR_MODE_MODIFIED: // Remove from change list break; case EVO_ADDR_MODE_WAITING: conn->addr_mode = EVO_ADDR_MODE_GOT_CHANGE; break; { // We are just removing our own changes from the change db // Check that it is in fact OUR change we get here } default: break; } } // FIXME: Changed in 1.4 void evo_addr_removed_cb (EBookView *book_view, GList *ids, gpointer data) { evolution_connection *conn = data; evo_addr_change(conn, ids, NULL, SYNC_OBJ_HARDDELETED); } void evo_addr_added_cb (EBookView *book_view, GList *cards, gpointer data) { evolution_connection *conn = data; evo_addr_change(conn, NULL, cards, SYNC_OBJ_ADDED); } void evo_addr_changed_cb (EBookView *book_view, GList *cards, gpointer data) { evolution_connection *conn = data; evo_addr_change(conn, NULL, cards, SYNC_OBJ_MODIFIED); } void evo_addr_seqcompl_cb (EBookView *book_view, EBookViewStatus status, gpointer data) { evolution_connection *conn = data; switch(conn->addr_mode) { case EVO_ADDR_MODE_GETVIEW: dd(printf("Get view done.\n")); conn->addr_mode = EVO_ADDR_MODE_WAITING; if (conn->callback) (conn->callback)(NULL, conn); // We have loaded the DB break; case EVO_ADDR_MODE_GETCHANGES: dd(printf("Get changes done.\n")); if (conn->callback) (conn->callback)(conn->addr_changes, conn); g_object_unref (G_OBJECT (book_view)); conn->addr_mode = EVO_ADDR_MODE_WAITING; break; case EVO_ADDR_MODE_REMOVINGCHANGE: if (conn->callback) (conn->callback)(conn->addr_changes, conn); g_object_unref (G_OBJECT (book_view)); conn->addr_mode = EVO_ADDR_MODE_WAITING; break; case EVO_ADDR_MODE_GETALL: dd(printf("Get all done.\n")); conn->addr_mode = EVO_ADDR_MODE_REMOVINGCHANGE; e_book_get_changes (conn->ebook, conn->changedbname, evo_addr_view_cb, conn); g_object_unref (G_OBJECT (book_view)); break; case EVO_ADDR_MODE_MODIFIED: dd(printf("Modification done\n")); evo_addr_modify_next(conn, FALSE); break; case EVO_ADDR_MODE_GOT_CHANGE: sync_object_changed(conn->sync_pair); conn->addr_mode = EVO_ADDR_MODE_WAITING; break; case EVO_ADDR_MODE_WAITING: break; default: conn->addr_mode = EVO_ADDR_MODE_WAITING; break; } } void evo_addr_view_cb (EBook *book, EBookStatus status, EBookView *book_view, gpointer data) { evolution_connection *conn = data; if (status == E_BOOK_STATUS_SUCCESS) { if (conn->addr_mode == EVO_ADDR_MODE_GETVIEW) conn->ebookview = book_view; g_object_ref (G_OBJECT (book_view)); g_signal_connect (G_OBJECT (book_view), "card_changed", G_CALLBACK (evo_addr_changed_cb), conn); g_signal_connect (G_OBJECT (book_view), "card_added", G_CALLBACK (evo_addr_added_cb), conn); g_signal_connect (G_OBJECT (book_view), "card_removed", G_CALLBACK (evo_addr_removed_cb), conn); g_signal_connect (G_OBJECT (book_view), "sequence_complete", G_CALLBACK (evo_addr_seqcompl_cb), conn); } } void evo_addr_bookloaded_cb(EBook *book, EBookStatus status, gpointer data) { evolution_connection *conn = data; if (status == E_BOOK_STATUS_SUCCESS) { conn->addr_mode = EVO_ADDR_MODE_GETVIEW; e_book_get_book_view (book,"(contains \"full_name\" \"\")", evo_addr_view_cb, conn); } else { // If load of addressbook failed, don't lock up if (conn->callback) (conn->callback)(NULL, conn); } } void evo_addr_connect(evolution_connection *conn) { char *path; if (conn->commondata.object_types & SYNC_OBJECT_TYPE_PHONEBOOK) { conn->nodbs++; conn->ebook = e_book_new(); if (conn->addressbookpath && strlen(conn->addressbookpath) > 0) path = g_strdup_printf("file://%s", conn->addressbookpath); else path = g_strdup_printf ("file://%s/evolution/local/Contacts/addressbook.db", g_get_home_dir()); e_book_load_uri(conn->ebook, path, evo_addr_bookloaded_cb, conn); g_free(path); } } void evo_addr_disconnect(evolution_connection *conn) { dd(printf("Disconnecting view...\n")); if (conn->ebookview) g_object_unref (G_OBJECT (conn->ebookview)); dd(printf("Disconnecting ebook...\n")); if (conn->ebook) { e_book_unload_uri(conn->ebook); g_object_unref (G_OBJECT(conn->ebook)); } dd(printf("Disconnecting addressbook done.\n")); conn->ebook = NULL; } void evo_addr_get_changes(GList *changes, evolution_connection *conn, evo_sync_cb cb) { if (conn->ebook) { conn->addr_mode = EVO_ADDR_MODE_GETCHANGES; conn->addr_changes = changes; conn->callback = cb; e_book_get_changes (conn->ebook, conn->changedbname, evo_addr_view_cb, conn); } else { (cb)(changes, conn); } } void evo_addr_get_all(GList *changes, evolution_connection *conn, evo_sync_cb cb) { if (conn->ebook) { conn->addr_mode = EVO_ADDR_MODE_GETALL; conn->addr_changes = changes; conn->callback = cb; e_book_get_book_view (conn->ebook,"(contains \"full_name\" \"\")", evo_addr_view_cb, conn); } else { (cb)(changes, conn); } } void evo_addr_add_cb (EBook *book, EBookStatus status, const char *id, gpointer data) { evolution_connection *conn = data; if (status == E_BOOK_STATUS_SUCCESS) { syncobj_modify_result *result = g_list_nth_data(conn->modify_results, conn->modify_no); if (result) { if (result->returnuid) g_free(result->returnuid); result->returnuid = g_strdup(id); result->result = SYNC_MSG_REQDONE; } } else { evo_addr_modify_next(conn, FALSE); } } void evo_addr_modify_cb (EBook *book, EBookStatus status, gpointer data) { evolution_connection *conn = data; dd(printf("Modify CB\n")); if (status != E_BOOK_STATUS_SUCCESS) evo_addr_modify_next(conn, TRUE); } // Add or replace an UID field in the vcard. The returned vcard is g_malloced. // If uid is NULL, the UID field is removed. char *evo_addr_set_uid(char *vcard, char* uid) { char *olduid = evo_addr_get_uid(vcard); GString* buf = g_string_new(""); char *newcard; if (olduid) { char *pos = vcard; // We need to find it g_free(olduid); while (pos) { if (!strncmp(pos, "UID:", 4)) { if (uid) { g_string_append(buf, "UID:"); g_string_append(buf, uid); g_string_append(buf, "\r\n"); } } else { char *end = strstr(pos, "\n"); if (end) { char *line = g_strndup(pos, end-pos+1); g_string_append(buf, line); g_free(line); } else { // End of card g_string_append(buf, pos); } } pos = strstr(pos, "\n"); if (pos) pos++; } } else { char *pos = vcard; while (pos) { char *end = strstr(pos, "\n"); char *line = g_strndup(pos, end?(end-pos+1):strlen(pos)); if (!strncmp(pos, "BEGIN:VCARD", 11)) { if (end) { g_string_append(buf, line); if (uid) { g_string_append(buf, "UID:"); g_string_append(buf, uid); g_string_append(buf, "\r\n"); } g_string_append(buf, end+1); pos = NULL; } } else { if (end) { g_string_append(buf, line); } else { // End of card g_string_append(buf, pos); } } g_free(line); if (pos) { pos = strstr(pos, "\n"); if (pos) pos++; } } } newcard = g_strdup(buf->str); g_string_free(buf, TRUE); return(newcard); } // If tryadd == TRUE, then a modification just failed, and we should // try to add the same card instead void evo_addr_modify_next(evolution_connection *conn, gboolean tryadd) { changed_object *obj = NULL; if (!tryadd) conn->modify_no++; while ((obj = g_list_nth_data(conn->modify_objects, conn->modify_no)) && (obj->object_type != SYNC_OBJECT_TYPE_PHONEBOOK)) conn->modify_no++; if (!obj) { if (conn->callback) (conn->callback)(NULL, conn); // We have done the modifications return; } switch (obj->change_type) { case SYNC_OBJ_MODIFIED: case SYNC_OBJ_ADDED: { // Modify or add // Remove PHOTO first char *tmp = sync_vtype_convert(obj->comp, VOPTION_REMOVEPHOTO, NULL); char *newcard = evo_addr_set_uid(tmp, obj->uid); g_free(tmp); conn->addr_mode = EVO_ADDR_MODE_MODIFIED; if (!obj->uid || tryadd) { // Add new card e_book_add_vcard (conn->ebook, newcard, evo_addr_add_cb, conn); } else { // Modify a card e_book_commit_vcard (conn->ebook, newcard, evo_addr_modify_cb, conn); } //g_free(newcard); } break; case SYNC_OBJ_HARDDELETED: { if (obj->uid) { conn->addr_mode = EVO_ADDR_MODE_MODIFIED; e_book_remove_card_by_id (conn->ebook, obj->uid, evo_addr_modify_cb, conn); } else evo_addr_modify_next(conn, FALSE); // Do the next } break; default: evo_addr_modify_next(conn, FALSE); } } gboolean evo_addr_modify(gpointer data) { evolution_connection *conn = data; if (conn->ebook && conn->modify_objects) { conn->modify_no = -1; evo_addr_modify_next(conn, FALSE); // Start doing the modifications } else { if (conn->callback) (conn->callback)(NULL, conn); // We have done the modifications } return(FALSE); }