From 026bbe8db3d1888014e4a65e230161dc702ef13c Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 11 May 2016 14:54:15 +0200 Subject: [PATCH] Implement support of dialog created by Notify matching subscription --- coreapi/bellesip_sal/sal_impl.c | 7 ++- coreapi/bellesip_sal/sal_impl.h | 4 +- coreapi/bellesip_sal/sal_op_events.c | 41 ++++++++++++++---- coreapi/bellesip_sal/sal_op_presence.c | 32 +++++++++++--- coreapi/friend.c | 3 ++ coreapi/linphonefriend.h | 8 ++++ coreapi/presence.c | 4 ++ coreapi/private.h | 1 + include/sal/sal.h | 1 + tester/presence_tester.c | 59 ++++++++++++++++++++++++-- 10 files changed, 139 insertions(+), 21 deletions(-) diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 1c09787bc..917faca7e 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -235,7 +235,12 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve if (dialog) { op=(SalOp*)belle_sip_dialog_get_application_data(dialog); - if (op==NULL || op->state==SalOpStateTerminated){ + + if (op == NULL && strcmp("NOTIFY",method) == 0) { + /*special case for Dialog created by notify mathing subscribe*/ + belle_sip_transaction_t * sub_trans = belle_sip_dialog_get_last_transaction(dialog); + op = (SalOp*)belle_sip_transaction_get_application_data(sub_trans); + } else if (op==NULL || op->state==SalOpStateTerminated){ ms_warning("Receiving request for null or terminated op [%p], ignored",op); return; } diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h index a77ed6855..9289958a7 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -101,7 +101,7 @@ struct SalOp{ int ref; SalOpType type; SalPrivacyMask privacy; - belle_sip_header_t *event; /*used by SalOpSubscribe kinds*/ + belle_sip_header_event_t *event; /*used by SalOpSubscribe kinds*/ SalOpSDPHandling sdp_handling; int auth_requests; /*number of auth requested for this op*/ bool_t cnx_ip_to_0000_if_sendonly_enabled; @@ -174,4 +174,6 @@ int sal_reason_to_sip_code(SalReason r); void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg); +SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg); + #endif /* SAL_IMPL_H_ */ diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c index 6a8107d93..a6d11b2aa 100644 --- a/coreapi/bellesip_sal/sal_op_events.c +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sal_impl.h" -SalSubscribeStatus get_subscription_state(belle_sip_message_t *msg){ +SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg){ belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(msg,belle_sip_header_subscription_state_t); SalSubscribeStatus sss=SalSubscribeNone; if (subscription_state_header){ @@ -73,7 +73,7 @@ static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialo /*notify the app that our subscription is dead*/ const char *eventname = NULL; if (op->event){ - eventname = belle_sip_header_get_unparsed_value(op->event); + eventname = belle_sip_header_event_get_package_name(op->event); } op->base.root->callbacks.notify(op, SalSubscribeTerminated, eventname, NULL); } @@ -115,7 +115,7 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_state_t dialog_state; belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); - belle_sip_header_t *event_header; + belle_sip_header_event_t *event_header; belle_sip_body_handler_t *body_handler; belle_sip_response_t* resp; const char *eventname=NULL; @@ -125,7 +125,7 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=server_transaction; - event_header=belle_sip_message_get_header((belle_sip_message_t*)req,"Event"); + event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t); body_handler = BELLE_SIP_BODY_HANDLER(sal_op_get_body_handler(op, BELLE_SIP_MESSAGE(req))); if (event_header==NULL){ @@ -139,7 +139,7 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque op->event=event_header; belle_sip_object_ref(op->event); } - eventname=belle_sip_header_get_unparsed_value(event_header); + eventname=belle_sip_header_event_get_package_name(event_header); if (!op->dialog) { if (strcmp(method,"SUBSCRIBE")==0){ @@ -238,10 +238,10 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event } if (eventname){ if (op->event) belle_sip_object_unref(op->event); - op->event=belle_sip_header_create("Event",eventname); + op->event=belle_sip_header_event_create(eventname); belle_sip_object_ref(op->event); } - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler)); return sal_op_send_and_create_refresher(op,req,expires,subscribe_refresher_listener); @@ -276,6 +276,29 @@ int sal_subscribe_accept(SalOp *op){ return 0; } +int sal_notify_pending_state(SalOp *op){ + + if (op->dialog != NULL && op->pending_server_trans) { + belle_sip_request_t* notify; + belle_sip_header_subscription_state_t* sub_state; + ms_message("Sending NOTIFY with subscription state pending for op [%p]",op); + if (!(notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"))) { + ms_error("Cannot create NOTIFY on op [%p]",op); + return -1; + } + if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); + sub_state=belle_sip_header_subscription_state_new(); + belle_sip_header_subscription_state_set_state(sub_state,BELLE_SIP_SUBSCRIPTION_STATE_PENDING); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify), BELLE_SIP_HEADER(sub_state)); + return sal_op_send_request(op,notify); + } else { + ms_warning("NOTIFY with subscription state pending for op [%p] not implemented in this case (either dialog pending trans does not exist",op); + } + + return 0; +} + + int sal_subscribe_decline(SalOp *op, SalReason reason){ belle_sip_response_t* resp = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)), sal_reason_to_sip_code(reason)); @@ -290,7 +313,7 @@ int sal_notify(SalOp *op, const SalBodyHandler *body_handler){ if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; - if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),op->event); + if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600))); @@ -302,7 +325,7 @@ int sal_notify_close(SalOp *op){ belle_sip_request_t* notify; if (!op->dialog) return -1; if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; - if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),op->event); + if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1))); return sal_op_send_request(op,notify); diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index dd157b3f6..62d74ba44 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -229,7 +229,7 @@ static void handle_notify(SalOp *op, belle_sip_request_t *req, belle_sip_dialog_ sub_state=SalSubscribeTerminated; ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); } else { - sub_state=SalSubscribeActive; + sub_state=belle_sip_message_get_subscription_state(BELLE_SIP_MESSAGE(req)); } presence_model = process_presence_notification(op, req); if (presence_model != NULL || body==NULL) { @@ -255,10 +255,23 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques belle_sip_dialog_state_t dialog_state; belle_sip_response_t* resp; const char *method=belle_sip_request_get_method(req); - + belle_sip_header_event_t *event_header; belle_sip_object_ref(server_transaction); if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); op->pending_server_trans=server_transaction; + event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t); + + if (event_header==NULL){ + ms_warning("No event header in incoming SUBSCRIBE."); + resp=sal_op_create_response_from_request(op,req,400); + belle_sip_server_transaction_send_response(server_transaction,resp); + if (!op->dialog) sal_op_release(op); + return; + } + if (op->event==NULL) { + op->event=event_header; + belle_sip_object_ref(op->event); + } if (!op->dialog) { @@ -272,7 +285,10 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques } set_or_update_dialog(op, dialog); ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); - }else{ /* this is a NOTIFY */ + }else if (strcmp(method,"NOTIFY")==0 && belle_sip_request_event_get_dialog(event)) { + /*special case of dialog created by notify matching subscribe*/ + set_or_update_dialog(op, belle_sip_request_event_get_dialog(event)); + } else {/* this is a NOTIFY */ ms_message("Receiving out of dialog notify"); handle_notify(op, req, belle_sip_request_event_get_dialog(event)); return; @@ -281,7 +297,11 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { - op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); + if (strcmp("NOTIFY",method)==0) { + handle_notify(op, req, belle_sip_request_event_get_dialog(event)); + } else if (strcmp("SUBSCRIBE",method)==0) { + op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); + } break; } case BELLE_SIP_DIALOG_EARLY: @@ -354,14 +374,14 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expi } } if (!op->event){ - op->event=belle_sip_header_create("Event","presence"); + op->event=belle_sip_header_event_create("presence"); belle_sip_object_ref(op->event); } belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.from_address),"tag"); belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.to_address),"tag"); req=sal_op_build_request(op,"SUBSCRIBE"); if( req ){ - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); } diff --git a/coreapi/friend.c b/coreapi/friend.c index cf5f4027b..2fa01e36d 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -1552,3 +1552,6 @@ void linphone_core_migrate_friends_from_rc_to_db(LinphoneCore *lc) { ms_debug("friends migration successful: %i friends migrated", i); lp_config_set_int(lpc, "misc", "friends_migration_done", 1); } +LinphoneSubscriptionState linphone_friend_get_subscription_state(const LinphoneFriend *lf) { + return lf->out_sub_state; +} diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h index 80192471a..1d52fc212 100644 --- a/coreapi/linphonefriend.h +++ b/coreapi/linphonefriend.h @@ -278,6 +278,14 @@ LINPHONE_PUBLIC void linphone_friend_done(LinphoneFriend *fr); */ LINPHONE_PUBLIC LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf); +/** + * Get subscription state of a friend + * @param[in] lf A #LinphoneFriend object + * @return #LinphoneSubscriptionState + */ + +LINPHONE_PUBLIC LinphoneSubscriptionState linphone_friend_get_subscription_state(const LinphoneFriend *lf); + /** * Get the presence model of a friend * @param[in] lf A #LinphoneFriend object diff --git a/coreapi/presence.c b/coreapi/presence.c index 251324d02..22887cfd1 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1513,6 +1513,9 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ if (lf!=NULL){ linphone_friend_add_incoming_subscription(lf, op); lf->inc_subscribe_pending=TRUE; + if (lp_config_get_int(lc->config,"sip","notify_pending_state",0)) { + sal_notify_pending_state(op); + } sal_subscribe_accept(op); linphone_friend_done(lf); /*this will do all necessary actions */ }else{ @@ -1904,6 +1907,7 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, Sa linphone_friend_set_presence_model(lf, presence); lf->subscribe_active=TRUE; lf->presence_received = TRUE; + lf->out_sub_state = linphone_subscription_state_from_sal(ss); 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 c1fae28c1..412e39ed7 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -702,6 +702,7 @@ struct _LinphoneFriend{ LinphoneVcard *vcard; unsigned int storage_id; LinphoneFriendList *friend_list; + LinphoneSubscriptionState out_sub_state; }; BELLE_SIP_DECLARE_VPTR(LinphoneFriend); diff --git a/include/sal/sal.h b/include/sal/sal.h index ad5f67f5e..9b3b979cf 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -770,6 +770,7 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event int sal_unsubscribe(SalOp *op); int sal_subscribe_accept(SalOp *op); int sal_subscribe_decline(SalOp *op, SalReason reason); +int sal_notify_pending_state(SalOp *op); int sal_notify(SalOp *op, const SalBodyHandler *body); int sal_notify_close(SalOp *op); int sal_publish(SalOp *op, const char *from, const char *to, const char*event_name, int expires, const SalBodyHandler *body); diff --git a/tester/presence_tester.c b/tester/presence_tester.c index e51b5fe97..484af5991 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -272,6 +272,54 @@ static void simple_subscribe(void) { linphone_core_manager_destroy(pauline); } +static void simple_subscribe_with_early_notify(void) { + + LinphoneCoreManager* marie = presence_linphone_core_manager_new("marie"); + LinphoneCoreManager* pauline = presence_linphone_core_manager_new("pauline"); + LinphoneAddress *marie_identity_addr = linphone_address_clone(marie->identity); + LpConfig *pauline_lp; + pauline_lp = linphone_core_get_config(pauline->lc); + lp_config_set_int(pauline_lp,"sip","notify_pending_state",1); + + bool_t result=FALSE; + char* pauline_identity=linphone_address_as_string_uri_only(pauline->identity); + char* marie_identity; + + LinphoneFriend* pauline_s_friend; + LinphoneFriend* marie_s_friend=linphone_core_create_friend_with_address(marie->lc,pauline_identity); + + + linphone_friend_edit(marie_s_friend); + linphone_friend_enable_subscribes(marie_s_friend,TRUE); + linphone_friend_done(marie_s_friend); + linphone_core_add_friend(marie->lc,marie_s_friend); + ms_free(pauline_identity); + + + /*to simulate pending state.*/ + + linphone_address_set_port(marie_identity_addr,0); + marie_identity=linphone_address_as_string_uri_only(marie_identity_addr); + pauline_s_friend=linphone_core_create_friend_with_address(pauline->lc,marie_identity); + linphone_core_add_friend(pauline->lc,pauline_s_friend); + + ms_free(marie_identity); + + BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_NotifyPresenceReceived,1)); + BC_ASSERT_EQUAL(linphone_friend_get_subscription_state(marie_s_friend), LinphoneSubscriptionPending,int, "%d"); + + result=wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphonePresenceActivityOnline,marie->stat.number_of_LinphonePresenceActivityOnline+1); + + BC_ASSERT_EQUAL(marie->stat.number_of_NotifyPresenceReceived,2, int, "%d"); + + linphone_friend_unref(marie_s_friend); + linphone_friend_unref(pauline_s_friend); + linphone_address_unref(marie_identity_addr); + linphone_core_manager_destroy(marie); + + linphone_core_manager_destroy(pauline); +} + static void unsubscribe_while_subscribing(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); @@ -399,8 +447,8 @@ static void presence_information(void) { static void subscribe_presence_forked(void){ LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); - LinphoneCoreManager* pauline1 = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - LinphoneCoreManager* pauline2 = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + LinphoneCoreManager* pauline1 = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_tcp_rc" : "pauline_tcp_rc"); + LinphoneCoreManager* pauline2 = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_tcp_rc" : "pauline_tcp_rc"); LinphoneFriend *lf; MSList *lcs = NULL; @@ -417,8 +465,10 @@ static void subscribe_presence_forked(void){ BC_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_NewSubscriptionRequest,1, 10000)); BC_ASSERT_TRUE(wait_for_list(lcs,&pauline2->stat.number_of_NewSubscriptionRequest,1, 2000)); - /*we should get two notifies*/ - BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphonePresenceActivityOnline,2, 10000)); + + /*we should get only one notify*/ + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphonePresenceActivityOnline,1, 10000)); + BC_ASSERT_FALSE(wait_for_list(lcs,&marie->stat.number_of_LinphonePresenceActivityOnline,2, 2000)); /*marie also shall receive two SUBSCRIBEs from the two paulines, but won't be notified to the app since Marie set Pauline as a friend.*/ @@ -501,6 +551,7 @@ static void simple_subscribe_with_friend_from_rc(void) { test_t presence_tests[] = { TEST_ONE_TAG("Simple Subscribe", simple_subscribe,"presence"), + TEST_ONE_TAG("Simple Subscribe with early NOTIFY", simple_subscribe_with_early_notify,"presence"), TEST_NO_TAG("Simple Subscribe with friend from rc", simple_subscribe_with_friend_from_rc), TEST_ONE_TAG("Simple Publish", simple_publish, "LeaksMemory"), TEST_ONE_TAG("Simple Publish with expires", publish_with_expires, "LeaksMemory"),