From 873493b62832293da85c3fa5e18d44c660b1d8ca Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 7 Jan 2016 15:44:59 +0100 Subject: [PATCH] More work on CardDAV sync: store etag and url for each vcard in db, call the correct callbacks and improved tester using stats --- coreapi/carddav.c | 92 +++++++++++++++++++++++++++++++++++++++---- coreapi/carddav.h | 27 +++++++++++-- coreapi/friend.c | 30 ++++++++++---- coreapi/vcard.cc | 25 ++++++++++++ coreapi/vcard.h | 8 ++++ coreapi/vcard_stubs.c | 20 ++++++++++ tester/vcard_tester.c | 42 +++++++++++++++++++- 7 files changed, 223 insertions(+), 21 deletions(-) diff --git a/coreapi/carddav.c b/coreapi/carddav.c index 812d18139..13554178a 100644 --- a/coreapi/carddav.c +++ b/coreapi/carddav.c @@ -61,7 +61,10 @@ void linphone_carddav_synchronize(LinphoneCardDavContext *cdc) { static void linphone_carddav_sync_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (success) { + ms_debug("CardDAV sync successful, saving new cTag: %i", cdc->ctag); linphone_core_set_carddav_current_ctag(cdc->lc, cdc->ctag); + } else { + ms_error("CardDAV sync failure: %s", msg); } if (cdc->sync_done_cb) { @@ -69,9 +72,37 @@ static void linphone_carddav_sync_done(LinphoneCardDavContext *cdc, bool_t succe } } +static int find_matching_friend(LinphoneFriend *lf1, LinphoneFriend *lf2) { + LinphoneVCard *lvc1 = linphone_friend_get_vcard(lf1); + LinphoneVCard *lvc2 = linphone_friend_get_vcard(lf2); + return strcmp(linphone_vcard_get_uid(lvc1), linphone_vcard_get_uid(lvc2)); +} + static void linphone_carddav_vcards_pulled(LinphoneCardDavContext *cdc, MSList *vCards) { if (vCards != NULL && ms_list_size(vCards) > 0) { - //TODO: find out which one is new and which one is an update and call the according callback + MSList *localFriends = linphone_core_fetch_friends_from_db(cdc->lc); + while (vCards) { + LinphoneCardDavResponse *vCard = (LinphoneCardDavResponse *)vCards->data; + if (vCard) { + LinphoneVCard *lvc = linphone_vcard_new_from_vcard4_buffer(vCard->vcard); + LinphoneFriend *lf = linphone_friend_new_from_vcard(lvc); + MSList *local_friend = ms_list_find_custom(localFriends, (int (*)(const void*, const void*))find_matching_friend, lf); + if (local_friend) { + LinphoneFriend *lf2 = (LinphoneFriend *)local_friend->data; + if (cdc->contact_updated_cb) { + ms_debug("Contact updated: %s", linphone_friend_get_name(lf)); + cdc->contact_updated_cb(cdc, lf, lf2); + } + } else { + if (cdc->contact_created_cb) { + ms_debug("Contact created: %s", linphone_friend_get_name(lf)); + cdc->contact_created_cb(cdc, lf); + } + } + } + vCards = ms_list_next(vCards); + } + ms_list_free(localFriends); } ms_list_free(vCards); linphone_carddav_sync_done(cdc, TRUE, ""); @@ -116,11 +147,42 @@ end: return result; } +static int find_matching_vcard(LinphoneCardDavResponse *response, LinphoneFriend *lf) { + LinphoneVCard *lvc1 = linphone_vcard_new_from_vcard4_buffer(response->vcard); + LinphoneVCard *lvc2 = linphone_friend_get_vcard(lf); + return strcmp(linphone_vcard_get_uid(lvc1), linphone_vcard_get_uid(lvc2)); +} + static void linphone_carddav_vcards_fetched(LinphoneCardDavContext *cdc, MSList *vCards) { if (vCards != NULL && ms_list_size(vCards) > 0) { - //MSList *localFriends = linphone_core_fetch_friends_from_db(cdc->lc); - //TODO: call onDelete from the ones that are in localFriends but not in vCards - //TODO: remove from vCards the one that are in localFriends and for which the eTag hasn't changed + MSList *localFriends = linphone_core_fetch_friends_from_db(cdc->lc); + MSList *friends = localFriends; + while (friends) { + LinphoneFriend *lf = (LinphoneFriend *)friends->data; + if (lf) { + MSList *vCard = ms_list_find_custom(vCards, (int (*)(const void*, const void*))find_matching_vcard, lf); + if (!vCard) { + ms_debug("Local friend %s isn't in the remote vCard list, delete it", linphone_friend_get_name(lf)); + if (cdc->contact_removed_cb) { + lf = linphone_friend_ref(lf); + ms_debug("Contact removed: %s", linphone_friend_get_name(lf)); + cdc->contact_removed_cb(cdc, lf); + } + } else { + LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)vCard->data; + ms_debug("Local friend %s is in the remote vCard list, check eTag", linphone_friend_get_name(lf)); + if (response) { + LinphoneVCard *lvc = linphone_friend_get_vcard(lf); + ms_debug("Local friend eTag is %s, remote vCard eTag is %s", linphone_vcard_get_etag(lvc), response->etag); + if (lvc && strcmp(linphone_vcard_get_etag(lvc), response->etag) == 0) { + ms_list_remove(vCards, vCard); + } + } + } + } + friends = ms_list_next(friends); + } + ms_list_free(localFriends); linphone_carddav_pull_vcards(cdc, vCards); } ms_list_free(vCards); @@ -291,6 +353,18 @@ void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext * cdc->sync_done_cb = cb; } +void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb) { + cdc->contact_created_cb = cb; +} + +void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb) { + cdc->contact_updated_cb = cb; +} + +void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb) { + cdc->contact_removed_cb = cb; +} + static LinphoneCardDavQuery* linphone_carddav_create_propfind_query(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; @@ -340,10 +414,12 @@ static LinphoneCardDavQuery* linphone_carddav_create_addressbook_multiget_query( sprintf(body, "%s", ""); while (iterator) { LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)iterator->data; - char temp_body[100]; - sprintf(temp_body, "%s", response->url); - sprintf(body, "%s%s", body, temp_body); - iterator = ms_list_next(iterator); + if (response) { + char temp_body[100]; + sprintf(temp_body, "%s", response->url); + sprintf(body, "%s%s", body, temp_body); + iterator = ms_list_next(iterator); + } } sprintf(body, "%s%s", body, ""); query->body = ms_strdup(body); diff --git a/coreapi/carddav.h b/coreapi/carddav.h index 8cb4b5bc1..1a8a22b26 100644 --- a/coreapi/carddav.h +++ b/coreapi/carddav.h @@ -46,17 +46,17 @@ typedef struct _LinphoneCardDavResponse LinphoneCardDavResponse; /** * Callback used to notify a new contact has been created on the CardDAV server **/ -typedef void (*LinphoneCardDavContactCreatedCb)(LinphoneFriend *lf); +typedef void (*LinphoneCardDavContactCreatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been updated on the CardDAV server **/ -typedef void (*LinphoneCardDavContactUpdatedCb)(LinphoneFriend *lf); +typedef void (*LinphoneCardDavContactUpdatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *new_friend, LinphoneFriend *old_friend); /** * Callback used to notify a contact has been removed on the CardDAV server **/ -typedef void (*LinphoneCardDavContactRemovedCb)(LinphoneFriend *lf); +typedef void (*LinphoneCardDavContactRemovedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been removed on the CardDAV server @@ -117,6 +117,27 @@ LINPHONE_PUBLIC void linphone_carddav_delete_vcard(LinphoneCardDavContext *cdc, */ LINPHONE_PUBLIC void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext *cdc, LinphoneCardDavSynchronizationDoneCb cb); +/** + * Set the new contact callback. + * @param cdc LinphoneCardDavContext object + * @param cb The new contact callback to be used. + */ +LINPHONE_PUBLIC void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb); + +/** + * Set the updated contact callback. + * @param cdc LinphoneCardDavContext object + * @param cb The updated contact callback to be used. + */ +LINPHONE_PUBLIC void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb); + +/** + * Set the removed contact callback. + * @param cdc LinphoneCardDavContext object + * @param cb The removed contact callback to be used. + */ +LINPHONE_PUBLIC void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb); + /** * Retrieves the current cTag value for the remote server * @param cdc LinphoneCardDavContext object diff --git a/coreapi/friend.c b/coreapi/friend.c index afb07f711..5baf9393c 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -935,6 +935,8 @@ static void linphone_create_table(sqlite3* db) { "send_subscribe INTEGER," "ref_key TEXT," "vCard TEXT," + "vCard_etag TEXT," + "vCard_url TEXT," "presence_received INTEGER" ");", 0, 0, &errmsg); @@ -992,7 +994,9 @@ void linphone_core_friends_storage_close(LinphoneCore *lc) { * | 3 | send_subscribe * | 4 | ref_key * | 5 | vCard - * | 6 | presence_received + * | 6 | vCard eTag + * | 7 | vCard URL + * | 8 | presence_received */ static int create_friend(void *data, int argc, char **argv, char **colName) { MSList **list = (MSList **)data; @@ -1001,7 +1005,11 @@ static int create_friend(void *data, int argc, char **argv, char **colName) { unsigned int storage_id = atoi(argv[0]); vcard = linphone_vcard_new_from_vcard4_buffer(argv[5]); - lf = linphone_friend_new_from_vcard(vcard); + if (vcard) { + linphone_vcard_set_etag(vcard, argv[6]); + linphone_vcard_set_url(vcard, argv[7]); + lf = linphone_friend_new_from_vcard(vcard); + } if (!lf) { LinphoneAddress *addr = linphone_address_new(argv[1]); lf = linphone_friend_new(); @@ -1009,8 +1017,8 @@ static int create_friend(void *data, int argc, char **argv, char **colName) { } linphone_friend_set_inc_subscribe_policy(lf, atoi(argv[2])); linphone_friend_send_subscribe(lf, atoi(argv[3])); - linphone_friend_set_ref_key(lf, argv[4]); - lf->presence_received = atoi(argv[6]); + linphone_friend_set_ref_key(lf, ms_strdup(argv[4])); + lf->presence_received = atoi(argv[8]); lf->storage_id = storage_id; *list = ms_list_append(*list, linphone_friend_ref(lf)); @@ -1044,27 +1052,33 @@ void linphone_core_store_friend_in_db(LinphoneCore *lc, LinphoneFriend *lf) { if (lc && lc->friends_db) { char *buf; int store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); + LinphoneVCard *vcard = linphone_friend_get_vcard(lf); + if (!store_friends) { return; } if (lf->storage_id > 0) { - buf = sqlite3_mprintf("UPDATE friends SET sip_uri=%Q,subscribe_policy=%i,send_subscribe=%i,ref_key=%Q,vCard=%Q,presence_received=%i WHERE (id = %i);", + buf = sqlite3_mprintf("UPDATE friends SET sip_uri=%Q,subscribe_policy=%i,send_subscribe=%i,ref_key=%Q,vCard=%Q,vCard_etag=%Q,vCard_url=%Q,presence_received=%i WHERE (id = %i);", linphone_address_as_string(linphone_friend_get_address(lf)), lf->pol, lf->subscribe, lf->refkey, - linphone_vcard_as_vcard4_string(linphone_friend_get_vcard(lf)), + linphone_vcard_as_vcard4_string(vcard), + linphone_vcard_get_etag(vcard), + linphone_vcard_get_url(vcard), lf->presence_received, lf->storage_id ); } else { - buf = sqlite3_mprintf("INSERT INTO friends VALUES(NULL,%Q,%i,%i,%Q,%Q,%i);", + buf = sqlite3_mprintf("INSERT INTO friends VALUES(NULL,%Q,%i,%i,%Q,%Q,%Q,%Q,%i);", linphone_address_as_string(linphone_friend_get_address(lf)), lf->pol, lf->subscribe, lf->refkey, - linphone_vcard_as_vcard4_string(linphone_friend_get_vcard(lf)), + linphone_vcard_as_vcard4_string(vcard), + linphone_vcard_get_etag(vcard), + linphone_vcard_get_url(vcard), lf->presence_received ); } diff --git a/coreapi/vcard.cc b/coreapi/vcard.cc index 0ecff260c..039ec200f 100644 --- a/coreapi/vcard.cc +++ b/coreapi/vcard.cc @@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. struct _LinphoneVCard { shared_ptr belCard; + const char *etag; + const char *url; }; extern "C" LinphoneVCard* linphone_vcard_new(void) { @@ -150,4 +152,27 @@ extern "C" MSList* linphone_vcard_get_sip_addresses(const LinphoneVCard *vCard) } } return result; +} + +extern "C" const char* linphone_vcard_get_uid(const LinphoneVCard *vCard) { + if (vCard->belCard->getUniqueId()) { + return vCard->belCard->getUniqueId()->getValue().c_str(); + } + return NULL; +} + +extern "C" void linphone_vcard_set_etag(LinphoneVCard *vCard, const char * etag) { + vCard->etag = ms_strdup(etag); +} + +extern "C" const char* linphone_vcard_get_etag(const LinphoneVCard *vCard) { + return vCard->etag; +} + +extern "C" void linphone_vcard_set_url(LinphoneVCard *vCard, const char * url) { + vCard->url = ms_strdup(url); +} + +extern "C" const char* linphone_vcard_get_url(const LinphoneVCard *vCard) { + return vCard->url; } \ No newline at end of file diff --git a/coreapi/vcard.h b/coreapi/vcard.h index 4ad094915..c8eeec45d 100644 --- a/coreapi/vcard.h +++ b/coreapi/vcard.h @@ -119,6 +119,14 @@ LINPHONE_PUBLIC void linphone_vcard_edit_main_sip_address(LinphoneVCard *vCard, */ LINPHONE_PUBLIC MSList* linphone_vcard_get_sip_addresses(const LinphoneVCard *vCard); +const char* linphone_vcard_get_uid(const LinphoneVCard *vCard); + +void linphone_vcard_set_etag(LinphoneVCard *vCard, const char * etag); +const char* linphone_vcard_get_etag(const LinphoneVCard *vCard); + +void linphone_vcard_set_url(LinphoneVCard *vCard, const char * url); +const char* linphone_vcard_get_url(const LinphoneVCard *vCard); + /** * @} */ diff --git a/coreapi/vcard_stubs.c b/coreapi/vcard_stubs.c index d50ab29b9..148858e09 100644 --- a/coreapi/vcard_stubs.c +++ b/coreapi/vcard_stubs.c @@ -69,4 +69,24 @@ void linphone_vcard_edit_main_sip_address(LinphoneVCard *vCard, const char *sip_ MSList* linphone_vcard_get_sip_addresses(const LinphoneVCard *vCard) { return NULL; +} + +const char* linphone_vcard_get_uid(const LinphoneVCard *vCard) { + return NULL; +} + +void linphone_vcard_set_etag(LinphoneVCard *vCard, const char * etag) { + +} + +const char* linphone_vcard_get_etag(const LinphoneVCard *vCard) { + return NULL; +} + +void linphone_vcard_set_url(LinphoneVCard *vCard, const char * url) { + +} + +const char* linphone_vcard_get_url(const LinphoneVCard *vCard) { + return NULL; } \ No newline at end of file diff --git a/tester/vcard_tester.c b/tester/vcard_tester.c index 7a1506cf1..f6531eb40 100644 --- a/tester/vcard_tester.c +++ b/tester/vcard_tester.c @@ -184,24 +184,62 @@ end: } #endif +typedef struct _LinphoneCardDAVStats { + int sync_done_count; + int new_contact_count; + int removed_contact_count; + int updated_contact_count; +} LinphoneCardDAVStats; + static void carddav_sync_done(LinphoneCardDavContext *c, bool_t success, const char *message) { + LinphoneCardDAVStats *stats = (LinphoneCardDAVStats *)linphone_carddav_get_user_data(c); BC_ASSERT_TRUE(success); + stats->sync_done_count++; linphone_carddav_destroy(c); } +static void carddav_new_contact(LinphoneCardDavContext *c, LinphoneFriend *lf) { + LinphoneCardDAVStats *stats = (LinphoneCardDAVStats *)linphone_carddav_get_user_data(c); + BC_ASSERT_PTR_NOT_NULL_FATAL(lf); + stats->new_contact_count++; + linphone_friend_unref(lf); +} + +static void carddav_removed_contact(LinphoneCardDavContext *c, LinphoneFriend *lf) { + LinphoneCardDAVStats *stats = (LinphoneCardDAVStats *)linphone_carddav_get_user_data(c); + BC_ASSERT_PTR_NOT_NULL_FATAL(lf); + stats->removed_contact_count++; + linphone_friend_unref(lf); +} + +static void carddav_updated_contact(LinphoneCardDavContext *c, LinphoneFriend *lf1, LinphoneFriend *lf2) { + LinphoneCardDAVStats *stats = (LinphoneCardDAVStats *)linphone_carddav_get_user_data(c); + BC_ASSERT_PTR_NOT_NULL_FATAL(lf1); + BC_ASSERT_PTR_NOT_NULL_FATAL(lf2); + stats->updated_contact_count++; + linphone_friend_unref(lf1); + linphone_friend_unref(lf2); +} + static void carddav_sync(void) { LinphoneCoreManager *manager = linphone_core_manager_new2("carddav_rc", FALSE); LinphoneCardDavContext *c = linphone_core_create_carddav_context(manager->lc); + LinphoneCardDAVStats *stats = (LinphoneCardDAVStats *)ms_new0(LinphoneCardDAVStats, 1); BC_ASSERT_PTR_NOT_NULL_FATAL(c); BC_ASSERT_PTR_NOT_NULL(c->server_url); BC_ASSERT_PTR_NOT_NULL(c->username); BC_ASSERT_PTR_NOT_NULL(c->ha1); + linphone_carddav_set_user_data(c, stats); linphone_carddav_set_synchronization_done_callback(c, carddav_sync_done); + linphone_carddav_set_new_contact_callback(c, carddav_new_contact); + linphone_carddav_set_removed_contact_callback(c, carddav_removed_contact); + linphone_carddav_set_updated_contact_callback(c, carddav_updated_contact); linphone_carddav_synchronize(c); - wait_for_until(manager->lc, NULL, NULL, 1, 1000); + wait_for_until(manager->lc, NULL, &stats->new_contact_count, 1, 2000); + wait_for_until(manager->lc, NULL, &stats->sync_done_count, 1, 2000); linphone_core_manager_destroy(manager); } #else @@ -219,7 +257,7 @@ test_t vcard_tests[] = { { "Friends storage migration from rc to db", friends_migration }, { "Friends storage in sqlite database", friends_sqlite_storage }, #endif - { "CardDAV synchronization", carddav_sync } + { "CardDAV synchronization", carddav_sync }, #else { "Dummy test", dummy_test } #endif