diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index b19f468f1..1046a308c 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -1122,7 +1122,7 @@ static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) } static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { - linphone_notify_parse_presence(op, content_type, content_subtype, body, result); + linphone_notify_parse_presence(content_type, content_subtype, body, result); } static void convert_presence_to_xml_requested(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) { diff --git a/coreapi/friend.c b/coreapi/friend.c index e15fed661..228a037c3 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -389,6 +389,13 @@ const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend return lf->presence; } +void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence) { + if (lf->presence != NULL) { + linphone_presence_model_unref(lf->presence); + } + lf->presence = presence; +} + BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ return lf->info; } diff --git a/coreapi/friendlist.c b/coreapi/friendlist.c index 099433fff..2b27bc8da 100644 --- a/coreapi/friendlist.c +++ b/coreapi/friendlist.c @@ -91,6 +91,64 @@ static char * create_resource_list_xml(const LinphoneFriendList *list) { return xml_content; } +static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) { + xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); + xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); + xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0); + if (xml_ctx->doc != NULL) { + char xpath_str[MAX_XPATH_LENGTH]; + LinphoneFriend *friend; + LinphoneContent *presence_part; + xmlXPathObjectPtr resource_object; + const char *uri = NULL; + 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"); + 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++) { + snprintf(xpath_str, sizeof(xpath_str), "/rlmi:list/rlmi:resource[%i]/@uri", i); + uri = linphone_get_xml_text_content(xml_ctx, xpath_str); + if (uri == NULL) continue; + friend = linphone_friend_list_find_friend_by_uri(list, uri); + if (friend != NULL) { + const char *state = NULL; + snprintf(xpath_str, sizeof(xpath_str),"/rlmi:list/rlmi:resource[%i]/rlmi:instance/@state", i); + state = linphone_get_xml_text_content(xml_ctx, xpath_str); + if ((state != NULL) && (strcmp(state, "active") == 0)) { + const char *cid = NULL; + snprintf(xpath_str, sizeof(xpath_str),"/rlmi:list/rlmi:resource[%i]/rlmi:instance/@cid", i); + cid = linphone_get_xml_text_content(xml_ctx, xpath_str); + if (cid != NULL) { + presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid); + if (presence_part == NULL) { + ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid); + } else { + 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) { + linphone_friend_set_presence_model(friend, (LinphonePresenceModel *)presence); + linphone_core_notify_notify_presence_received(list->lc, friend); + } + } + } + if (cid != NULL) linphone_free_xml_text_content(cid); + } + if (state != NULL) linphone_free_xml_text_content(state); + } + linphone_free_xml_text_content(uri); + } + } + if (resource_object != NULL) xmlXPathFreeObject(resource_object); + } else { + ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer); + } + +end: + linphone_xmlparsing_context_destroy(xml_ctx); +} + static LinphoneFriendList * linphone_friend_list_new(void) { LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList); belle_sip_object_ref(list); @@ -249,6 +307,7 @@ void linphone_friend_list_update_subscriptions(LinphoneFriendList *list, Linphon int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600); event = linphone_core_create_subscribe(list->lc, address, "presence", expires); linphone_event_add_custom_header(event, "Require", "recipient-list-subscribe"); + linphone_event_add_custom_header(event, "Supported", "eventlist"); linphone_event_add_custom_header(event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml"); linphone_event_add_custom_header(event, "Content-Disposition", "recipient-list"); content = linphone_core_create_content(list->lc); @@ -282,3 +341,31 @@ void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePres linphone_friend_notify(friend, presence); } } + +void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) { + if (linphone_content_is_multipart(body)) { + LinphoneContent *first_part; + const char *type = linphone_content_get_type(body); + const char *subtype = linphone_content_get_subtype(body); + + if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) { + ms_warning("multipart presence notified but it is not 'multipart/related'"); + return; + } + + first_part = linphone_content_get_part(body, 0); + if (first_part == NULL) { + ms_warning("'multipart/related' presence notified but it doesn't contain any part"); + return; + } + + type = linphone_content_get_type(first_part); + subtype = linphone_content_get_subtype(first_part); + if ((strcmp(type, "application") != 0) || (strcmp(subtype, "rlmi+xml") != 0)) { + ms_warning("multipart presence notified but first part is not 'application/rlmi+xml'"); + return; + } + + linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part)); + } +} diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 060856238..79235453f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1664,10 +1664,17 @@ static void linphone_core_register_default_codecs(LinphoneCore *lc){ linphone_core_register_static_payloads(lc); } +static void linphone_core_internal_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *notified_event, const LinphoneContent *body) { + if (strcmp(notified_event, "Presence") == 0) { + linphone_friend_list_notify_presence_received(lc->friendlist, lev, body); + } +} + static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata){ const char *rls_uri = NULL; const char *remote_provisioning_uri = NULL; LinphoneCoreVTable* local_vtable= linphone_core_v_table_new(); + LinphoneCoreVTable *internal_vtable = linphone_core_v_table_new(); ms_message("Initializing LinphoneCore %s", linphone_core_get_version()); lc->config=lp_config_ref(config); @@ -1679,6 +1686,8 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_friend_list_set_rls_uri(lc->friendlist, rls_uri); linphone_task_list_init(&lc->hooks); + internal_vtable->notify_received = linphone_core_internal_notify_received; + _linphone_core_add_listener(lc, internal_vtable, TRUE); memcpy(local_vtable,vtable,sizeof(LinphoneCoreVTable)); _linphone_core_add_listener(lc, local_vtable, TRUE); diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h index 1a6535fed..53f747651 100644 --- a/coreapi/linphonefriend.h +++ b/coreapi/linphonefriend.h @@ -240,6 +240,13 @@ LINPHONE_PUBLIC LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFr */ LINPHONE_PUBLIC const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend *lf); +/** + * Set the presence model of a friend + * @param[in] lf A #LinphoneFriend object + * @param[in] presence The #LinphonePresenceModel object to set for the friend + */ +LINPHONE_PUBLIC void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence); + /** * Store user pointer to friend object. **/ diff --git a/coreapi/presence.c b/coreapi/presence.c index d97ff865f..8465adcd0 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1508,7 +1508,7 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ ms_free(tmp); } -void linphone_notify_parse_presence(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { +void linphone_notify_parse_presence(const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { xmlparsing_context_t *xml_ctx; LinphonePresenceModel *model = NULL; @@ -1866,10 +1866,7 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, Sa activity_str = linphone_presence_activity_to_string(activity); ms_message("We are notified that [%s] has presence [%s]", tmp, activity_str); if (activity_str != NULL) ms_free(activity_str); - if (lf->presence != NULL) { - linphone_presence_model_unref(lf->presence); - } - lf->presence = presence; + linphone_friend_set_presence_model(lf, presence); lf->subscribe_active=TRUE; linphone_core_notify_notify_presence_received(lc,(LinphoneFriend*)lf); ms_free(tmp); diff --git a/coreapi/private.h b/coreapi/private.h index e279c6bdd..5f134e484 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -377,6 +377,7 @@ void _linphone_proxy_config_release(LinphoneProxyConfig *cfg); const LinphoneAddress* linphone_proxy_config_get_service_route(const LinphoneProxyConfig* cfg); void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list); +void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body); void linphone_friend_invalidate_subscription(LinphoneFriend *lf); void linphone_friend_close_subscriptions(LinphoneFriend *lf); void linphone_friend_update_subscribes(LinphoneFriend *fr, LinphoneProxyConfig *cfg, bool_t only_when_registered); @@ -434,7 +435,7 @@ void linphone_process_authentication(LinphoneCore* lc, SalOp *op); void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from); void linphone_core_send_presence(LinphoneCore *lc, LinphonePresenceModel *presence); -void linphone_notify_parse_presence(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result); +void linphone_notify_parse_presence(const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result); void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presence, const char *contact, char **content); void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model); void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op);