From fccaa5e36eb67ed6843dbd787b14ef1d461068c1 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 30 Nov 2015 11:39:21 +0100 Subject: [PATCH] Handle versioning of presence list notifications + store information that we have received presence information for a friend. --- coreapi/friend.c | 7 ++++++ coreapi/friendlist.c | 51 +++++++++++++++++++++++++++++++++++++++- coreapi/linphonefriend.h | 7 ++++++ coreapi/presence.c | 1 + coreapi/private.h | 3 +++ coreapi/xml.c | 27 +++++++++++++++++++++ 6 files changed, 95 insertions(+), 1 deletion(-) diff --git a/coreapi/friend.c b/coreapi/friend.c index 228a037c3..8b602843f 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -396,6 +396,10 @@ void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceMode lf->presence = presence; } +bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf) { + return lf->presence_received; +} + BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ return lf->info; } @@ -597,6 +601,8 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde } a=lp_config_get_int(config,item,"subscribe",0); linphone_friend_send_subscribe(lf,a); + a = lp_config_get_int(config, item, "presence_received", 0); + lf->presence_received = (bool_t)a; linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL)); return lf; @@ -639,6 +645,7 @@ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf, } lp_config_set_string(config,key,"pol",__policy_enum_to_str(lf->pol)); lp_config_set_int(config,key,"subscribe",lf->subscribe); + lp_config_set_int(config, key, "presence_received", lf->presence_received); refkey=linphone_friend_get_ref_key(lf); if (refkey){ diff --git a/coreapi/friendlist.c b/coreapi/friendlist.c index 2b27bc8da..ef9c13a23 100644 --- a/coreapi/friendlist.c +++ b/coreapi/friendlist.c @@ -100,11 +100,47 @@ static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList LinphoneFriend *friend; LinphoneContent *presence_part; xmlXPathObjectPtr resource_object; + const char *version_str = NULL; + const char *full_state_str = NULL; const char *uri = NULL; + bool_t full_state = FALSE; + int version; int i; if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi"); + + version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version"); + if (version_str == NULL) { + ms_warning("rlmi+xml: No version attribute in list"); + goto end; + } + version = atoi(version_str); + if (version < list->expected_notification_version) { + ms_warning("rlmi+xml: Discarding received notification with version %d because %d was expected", version, list->expected_notification_version); + goto end; + } + + full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState"); + if (full_state_str == NULL) { + ms_warning("rlmi+xml: No fullState attribute in list"); + goto end; + } + if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) { + MSList *l = list->friends; + for (; l != NULL; l = l->next) { + friend = (LinphoneFriend *)l->data; + linphone_friend_set_presence_model(friend, NULL); + } + full_state = TRUE; + } + linphone_free_xml_text_content(full_state_str); + if ((list->expected_notification_version == 0) && (full_state == FALSE)) { + ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid"); + goto end; + } + list->expected_notification_version = version + 1; + resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource"); if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) { for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) { @@ -128,8 +164,11 @@ static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList SalPresenceModel *presence = NULL; linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence); if (presence != NULL) { + friend->presence_received = TRUE; linphone_friend_set_presence_model(friend, (LinphonePresenceModel *)presence); - linphone_core_notify_notify_presence_received(list->lc, friend); + if (full_state == FALSE) { + linphone_core_notify_notify_presence_received(list->lc, friend); + } } } } @@ -141,6 +180,16 @@ static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList } } if (resource_object != NULL) xmlXPathFreeObject(resource_object); + + if (full_state == TRUE) { + MSList *l = list->friends; + for (; l != NULL; l = l->next) { + friend = (LinphoneFriend *)l->data; + if (linphone_friend_is_presence_received(friend) == TRUE) { + linphone_core_notify_notify_presence_received(list->lc, friend); + } + } + } } else { ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer); } diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h index 53f747651..e158d41b5 100644 --- a/coreapi/linphonefriend.h +++ b/coreapi/linphonefriend.h @@ -247,6 +247,13 @@ LINPHONE_PUBLIC const LinphonePresenceModel * linphone_friend_get_presence_model */ LINPHONE_PUBLIC void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence); +/** + * Tells whether we already received presence information for a friend. + * @param[in] lf A #LinphoneFriend object + * @return TRUE if presence information has been received for the friend, FALSE otherwise. + */ +LINPHONE_PUBLIC bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf); + /** * Store user pointer to friend object. **/ diff --git a/coreapi/presence.c b/coreapi/presence.c index 8465adcd0..a25261d52 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1868,6 +1868,7 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, Sa if (activity_str != NULL) ms_free(activity_str); linphone_friend_set_presence_model(lf, presence); lf->subscribe_active=TRUE; + lf->presence_received = TRUE; linphone_core_notify_notify_presence_received(lc,(LinphoneFriend*)lf); ms_free(tmp); if (op != lf->outsub){ diff --git a/coreapi/private.h b/coreapi/private.h index 5f134e484..61fe10265 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -647,6 +647,7 @@ struct _LinphoneFriend{ bool_t inc_subscribe_pending; bool_t commit; bool_t initial_subscribes_sent; /*used to know if initial subscribe message was sent or not*/ + bool_t presence_received; }; BELLE_SIP_DECLARE_VPTR(LinphoneFriend); @@ -659,6 +660,7 @@ struct _LinphoneFriendList { char *display_name; char *rls_uri; MSList *friends; + int expected_notification_version; }; BELLE_SIP_DECLARE_VPTR(LinphoneFriendList); @@ -1245,6 +1247,7 @@ void linphone_xmlparsing_context_destroy(xmlparsing_context_t *ctx); void linphone_xmlparsing_genericxml_error(void *ctx, const char *fmt, ...); int linphone_create_xml_xpath_context(xmlparsing_context_t *xml_ctx); char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression); +const char * linphone_get_xml_attribute_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression, const char *attribute_name); void linphone_free_xml_text_content(const char *text); xmlXPathObjectPtr linphone_get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression); diff --git a/coreapi/xml.c b/coreapi/xml.c index 8c7c3beaf..28d773add 100644 --- a/coreapi/xml.c +++ b/coreapi/xml.c @@ -89,6 +89,33 @@ char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char * return (char *)text; } +const char * linphone_get_xml_attribute_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression, const char *attribute_name) { + xmlXPathObjectPtr xpath_obj; + xmlChar *text = NULL; + + xpath_obj = xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); + if (xpath_obj != NULL) { + if (xpath_obj->nodesetval != NULL) { + xmlNodeSetPtr nodes = xpath_obj->nodesetval; + if ((nodes != NULL) && (nodes->nodeNr >= 1)) { + xmlNodePtr node = nodes->nodeTab[0]; + xmlAttr *attr = node->properties; + while (attr) { + if (strcmp((char *)attr->name, attribute_name) == 0) { + text = xmlStrcat(text, attr->children->content); + attr = NULL; + } else { + attr = attr->next; + } + } + } + } + xmlXPathFreeObject(xpath_obj); + } + + return (const char *)text; +} + void linphone_free_xml_text_content(const char *text) { xmlFree((xmlChar *)text); }