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