From e4c674c2e3887063aa2c73163c748acccd5fd12b Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 27 May 2016 23:03:05 +0200 Subject: [PATCH] implement the sending of out-of-dialog NOTIFYs, and add non-regression test This fixes a crash producing all the time when receiving such out-of-dialog NOTIFY. --- coreapi/bellesip_sal/sal_impl.c | 2 +- coreapi/bellesip_sal/sal_op_events.c | 69 ++++++++++++++++++++++++---- coreapi/bellesip_sal/sal_op_impl.c | 10 ++++ coreapi/callbacks.c | 37 +++++++++++---- coreapi/event.c | 16 +++++-- coreapi/event.h | 12 +++++ coreapi/linphonecore.h | 9 ++++ include/sal/sal.h | 4 ++ oRTP | 2 +- tester/eventapi_tester.c | 34 +++++++++++++- 10 files changed, 168 insertions(+), 27 deletions(-) diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 90083418d..75071f1bc 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -292,7 +292,7 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve belle_sip_provider_send_response(sal->prov,resp); return; }else if (sal->enable_test_features && strcmp("PUBLISH",method)==0) { - resp=belle_sip_response_create_from_request(req,200);/*out of dialog BYE */ + resp=belle_sip_response_create_from_request(req,200);/*out of dialog PUBLISH */ belle_sip_message_add_header((belle_sip_message_t*)resp,belle_sip_header_create("SIP-Etag","4441929FFFZQOA")); belle_sip_provider_send_response(sal->prov,resp); return; diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c index a6d11b2aa..f1f6d20b1 100644 --- a/coreapi/bellesip_sal/sal_op_events.c +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -61,7 +61,22 @@ static void subscribe_refresher_listener (belle_sip_refresher_t* refresher } static void subscribe_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ - ms_error("subscribe_process_io_error not implemented yet"); + SalOp *op = (SalOp*)user_ctx; + belle_sip_object_t *src = belle_sip_io_error_event_get_source(event); + if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(src, belle_sip_client_transaction_t)){ + belle_sip_client_transaction_t *tr = BELLE_SIP_CLIENT_TRANSACTION(src); + belle_sip_request_t* req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); + const char *method=belle_sip_request_get_method(req); + + if (!op->dialog) { + /*this is handling outgoing out-of-dialog notifies*/ + if (strcmp(method,"NOTIFY")==0){ + SalErrorInfo *ei=&op->error_info; + sal_error_info_set(ei,SalReasonIOError,0,NULL,NULL); + op->base.root->callbacks.on_notify_response(op); + } + } + } } static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { @@ -83,9 +98,41 @@ static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialo } static void subscribe_response_event(void *op_base, const belle_sip_response_event_t *event){ + SalOp *op = (SalOp*)op_base; + belle_sip_request_t * req; + const char *method; + belle_sip_client_transaction_t *tr = belle_sip_response_event_get_client_transaction(event); + + if (!tr) return; + req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); + method = belle_sip_request_get_method(req); + + if (!op->dialog) { + if (strcmp(method,"NOTIFY")==0){ + sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event)); + op->base.root->callbacks.on_notify_response(op); + } + } } static void subscribe_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { + SalOp *op = (SalOp*)user_ctx; + belle_sip_request_t * req; + const char *method; + belle_sip_client_transaction_t *tr = belle_sip_timeout_event_get_client_transaction(event); + + if (!tr) return; + req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr); + method = belle_sip_request_get_method(req); + + if (!op->dialog) { + /*this is handling outgoing out-of-dialog notifies*/ + if (strcmp(method,"NOTIFY")==0){ + SalErrorInfo *ei=&op->error_info; + sal_error_info_set(ei,SalReasonRequestTimeout,0,NULL,NULL); + op->base.root->callbacks.on_notify_response(op); + } + } } static void subscribe_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { @@ -236,11 +283,7 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event if( req == NULL ) { return -1; } - if (eventname){ - if (op->event) belle_sip_object_unref(op->event); - op->event=belle_sip_header_event_create(eventname); - belle_sip_object_ref(op->event); - } + sal_op_set_event(op, eventname); 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)); @@ -309,14 +352,20 @@ int sal_subscribe_decline(SalOp *op, SalReason reason){ int sal_notify(SalOp *op, const SalBodyHandler *body_handler){ 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->dialog){ + if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; + }else{ + sal_op_subscribe_fill_cbs(op); + notify = sal_op_build_request(op, "NOTIFY"); + } 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))); + ,op->dialog ? + BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)) : + BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,0)) + ); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(notify), BELLE_SIP_BODY_HANDLER(body_handler)); return sal_op_send_request(op,notify); } diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index b58be0f2b..51b7d23a5 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -799,3 +799,13 @@ int sal_op_refresh(SalOp *op) { ms_warning("sal_refresh on op [%p] of type [%s] no refresher",op,sal_op_type_to_string(op->type)); return -1; } + +void sal_op_set_event(SalOp *op, const char *eventname){ + belle_sip_header_event_t *header = NULL; + if (op->event) belle_sip_object_unref(op->event); + if (eventname){ + header = belle_sip_header_event_create(eventname); + belle_sip_object_ref(header); + } + op->event = header; +} diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 8f9f50690..9cb4572e6 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -1315,8 +1315,8 @@ static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, SalB LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); bool_t out_of_dialog = (lev==NULL); if (out_of_dialog) { - /*out of subscribe notify */ - lev=linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname); + /*out of dialog notify */ + lev = linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname); } { LinphoneContent *ct=linphone_content_from_sal_body_handler(body_handler); @@ -1325,14 +1325,12 @@ static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, SalB linphone_content_unref(ct); } } - if (st!=SalSubscribeNone){ + if (out_of_dialog){ + /*out of dialog NOTIFY do not create an implicit subscription*/ + linphone_event_set_state(lev, LinphoneSubscriptionTerminated); + }else if (st!=SalSubscribeNone){ linphone_event_set_state(lev,linphone_subscription_state_from_sal(st)); } - - if (out_of_dialog) { - linphone_event_unref(lev); - } - } static void subscribe_received(SalOp *op, const char *eventname, const SalBodyHandler *body_handler){ @@ -1373,6 +1371,7 @@ static void on_publish_response(SalOp* op){ } } + static void on_expire(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); @@ -1385,6 +1384,25 @@ static void on_expire(SalOp *op){ } } +static void on_notify_response(SalOp *op){ + LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); + + if (lev==NULL) return; + /*this is actually handling out of dialogs notify - for the moment*/ + if (!lev->is_out_of_dialog_op) return; + switch (linphone_event_get_subscription_state(lev)){ + case LinphoneSubscriptionIncomingReceived: + if (sal_op_get_error_info(op)->reason == SalReasonNone){ + linphone_event_set_state(lev, LinphoneSubscriptionTerminated); + }else{ + linphone_event_set_state(lev, LinphoneSubscriptionError); + } + break; + default: + ms_warning("Unhandled on_notify_response() case %s", linphone_subscription_state_to_string(linphone_event_get_subscription_state(lev))); + } +} + SalCallbacks linphone_sal_callbacks={ call_received, call_ringing, @@ -1417,7 +1435,8 @@ SalCallbacks linphone_sal_callbacks={ auth_requested, info_received, on_publish_response, - on_expire + on_expire, + on_notify_response }; diff --git a/coreapi/event.c b/coreapi/event.c index 2d6e7eaf5..79d3845b3 100644 --- a/coreapi/event.c +++ b/coreapi/event.c @@ -161,13 +161,21 @@ LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAd return lev; } +LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){ + LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionIncoming, event, -1); + linphone_configure_op(lc,lev->op,resource,NULL,TRUE); + lev->subscription_state = LinphoneSubscriptionIncomingReceived; + sal_op_set_event(lev->op, event); + lev->is_out_of_dialog_op = TRUE; + return lev; +} + LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ LinphoneEvent *lev=linphone_core_create_subscribe(lc,resource,event,expires); linphone_event_send_subscribe(lev,body); return lev; } - int linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; int err; @@ -241,7 +249,7 @@ int linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){ int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; - if (lev->subscription_state!=LinphoneSubscriptionActive){ + if (lev->subscription_state!=LinphoneSubscriptionActive && lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_notify(): cannot notify if subscription is not active."); return -1; } @@ -392,7 +400,7 @@ const char *linphone_event_get_name(const LinphoneEvent *lev){ } const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){ - if (lev->is_out_of_dialog_op){ + if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){ return (LinphoneAddress*)sal_op_get_to_address(lev->op); }else{ return (LinphoneAddress*)sal_op_get_from_address(lev->op); @@ -400,7 +408,7 @@ const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){ } const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){ - if (lev->is_out_of_dialog_op){ + if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){ return (LinphoneAddress*)sal_op_get_from_address(lev->op); }else{ return (LinphoneAddress*)sal_op_get_to_address(lev->op); diff --git a/coreapi/event.h b/coreapi/event.h index d84d6d180..0c8c16215 100644 --- a/coreapi/event.h +++ b/coreapi/event.h @@ -129,6 +129,18 @@ LINPHONE_PUBLIC LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const L **/ LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires); + +/** + * Create an out-of-dialog notification, specifying the destination resource, the event name. + * The notification can be send with linphone_event_notify(). + * @param lc the #LinphoneCore + * @param resource the destination resource + * @param event the event name + * @return a LinphoneEvent holding the context of the notification. +**/ +LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event); + + /** * Send a subscription previously created by linphone_core_create_subscribe(). * @param ev the LinphoneEvent diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 775ac4a86..bbb170a83 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -4387,6 +4387,7 @@ LINPHONE_PUBLIC bool_t linphone_core_video_multicast_enabled(const LinphoneCore * @param lc the LinphoneCore * @param params the parameters used for the network simulation. * @return 0 if successful, -1 otherwise. + * @ingroup media_parameters **/ LINPHONE_PUBLIC int linphone_core_set_network_simulator_params(LinphoneCore *lc, const OrtpNetworkSimulatorParams *params); @@ -4395,6 +4396,7 @@ LINPHONE_PUBLIC int linphone_core_set_network_simulator_params(LinphoneCore *lc, * Get the previously set network simulation parameters. * @see linphone_core_set_network_simulator_params * @return a OrtpNetworkSimulatorParams structure. + * @ingroup media_parameters **/ LINPHONE_PUBLIC const OrtpNetworkSimulatorParams *linphone_core_get_network_simulator_params(const LinphoneCore *lc); @@ -4402,6 +4404,7 @@ LINPHONE_PUBLIC const OrtpNetworkSimulatorParams *linphone_core_get_network_simu * Set the video preset to be used for video calls. * @param[in] lc LinphoneCore object * @param[in] preset The name of the video preset to be used (can be NULL to use the default video preset). + * @ingroup media_parameters */ LINPHONE_PUBLIC void linphone_core_set_video_preset(LinphoneCore *lc, const char *preset); @@ -4409,6 +4412,7 @@ LINPHONE_PUBLIC void linphone_core_set_video_preset(LinphoneCore *lc, const char * Get the video preset used for video calls. * @param[in] lc LinphoneCore object * @return The name of the video preset used for video calls (can be NULL if the default video preset is used). + * @ingroup media_parameters */ LINPHONE_PUBLIC const char * linphone_core_get_video_preset(const LinphoneCore *lc); @@ -4416,6 +4420,7 @@ LINPHONE_PUBLIC const char * linphone_core_get_video_preset(const LinphoneCore * * Gets if realtime text is enabled or not * @param[in] lc LinphoneCore object * @return true if realtime text is enabled, false otherwise + * @ingroup media_parameters */ LINPHONE_PUBLIC bool_t linphone_core_realtime_text_enabled(LinphoneCore *lc); @@ -4423,6 +4428,7 @@ LINPHONE_PUBLIC bool_t linphone_core_realtime_text_enabled(LinphoneCore *lc); * Set http proxy address to be used for signaling during next channel connection. Use #linphone_core_set_network_reachable FASLE/TRUE to force channel restart. * @param[in] lc LinphoneCore object * @param[in] hostname of IP adress of the http proxy (can be NULL to disable). + * @ingroup network_parameters */ LINPHONE_PUBLIC void linphone_core_set_http_proxy_host(LinphoneCore *lc, const char *host) ; @@ -4430,6 +4436,7 @@ LINPHONE_PUBLIC void linphone_core_set_http_proxy_host(LinphoneCore *lc, const c * Set http proxy port to be used for signaling. * @param[in] lc LinphoneCore object * @param[in] port of the http proxy. + * @ingroup network_parameters */ LINPHONE_PUBLIC void linphone_core_set_http_proxy_port(LinphoneCore *lc, int port) ; @@ -4437,6 +4444,7 @@ LINPHONE_PUBLIC void linphone_core_set_http_proxy_port(LinphoneCore *lc, int por * Get http proxy address to be used for signaling. * @param[in] lc LinphoneCore object * @return hostname of IP adress of the http proxy (can be NULL to disable). + * @ingroup network_parameters */ LINPHONE_PUBLIC const char *linphone_core_get_http_proxy_host(const LinphoneCore *lc); @@ -4444,6 +4452,7 @@ LINPHONE_PUBLIC const char *linphone_core_get_http_proxy_host(const LinphoneCore * Get http proxy port to be used for signaling. * @param[in] lc LinphoneCore object * @return port of the http proxy. + * @ingroup network_parameters */ LINPHONE_PUBLIC int linphone_core_get_http_proxy_port(const LinphoneCore *lc); diff --git a/include/sal/sal.h b/include/sal/sal.h index 057c9d1b5..11b86d93b 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -508,6 +508,7 @@ typedef void (*SalOnSubscribePresenceClosed)(SalOp *salop, const char *from); typedef void (*SalOnPingReply)(SalOp *salop); typedef void (*SalOnInfoReceived)(SalOp *salop, SalBodyHandler *body); typedef void (*SalOnPublishResponse)(SalOp *salop); +typedef void (*SalOnNotifyResponse)(SalOp *salop); typedef void (*SalOnExpire)(SalOp *salop); /*allows sal implementation to access auth info if available, return TRUE if found*/ @@ -546,6 +547,7 @@ typedef struct SalCallbacks{ SalOnInfoReceived info_received; SalOnPublishResponse on_publish_response; SalOnExpire on_expire; + SalOnNotifyResponse on_notify_response; }SalCallbacks; @@ -700,6 +702,8 @@ void sal_error_info_set(SalErrorInfo *ei, SalReason reason, int code, const char /*entity tag used for publish (see RFC 3903)*/ const char *sal_op_get_entity_tag(const SalOp* op); void sal_op_set_entity_tag(SalOp *op, const char* entity_tag); +/*set the event header, for used with out of dialog SIP notify*/ +void sal_op_set_event(SalOp *op, const char *event); /*Call API*/ int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc); diff --git a/oRTP b/oRTP index d10bdd483..d94f56bb3 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit d10bdd4832efa9b3bd6ce99b71873ca33326e812 +Subproject commit d94f56bb37b4575445206ef3ca784651ac81f83f diff --git a/tester/eventapi_tester.c b/tester/eventapi_tester.c index 170d749df..9ddf99ff6 100644 --- a/tester/eventapi_tester.c +++ b/tester/eventapi_tester.c @@ -39,7 +39,7 @@ void linphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char * LinphoneCoreManager *mgr; const char * ua = linphone_event_get_custom_header(lev, "User-Agent"); if (!BC_ASSERT_PTR_NOT_NULL(content)) return; - if (!linphone_content_is_multipart(content) && (!ua || !strstr(ua, "flexisip"))) { /*disable check for full presence serveur support*/ + if (!linphone_content_is_multipart(content) && (!ua || !strstr(ua, "flexisip"))) { /*disable check for full presence server support*/ /*hack to disable content checking for list notify */ BC_ASSERT_STRING_EQUAL(notify_content,(const char*)linphone_content_get_buffer(content)); } @@ -358,6 +358,35 @@ static void publish_without_expires(void){ publish_test_with_args(TRUE,-1); } +static void out_of_dialog_notify(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); + LinphoneContent* content; + LinphoneEvent *lev; + MSList* lcs=ms_list_append(NULL,marie->lc); + lcs=ms_list_append(lcs,pauline->lc); + + content = linphone_core_create_content(marie->lc); + linphone_content_set_type(content,"application"); + linphone_content_set_subtype(content,"somexml"); + linphone_content_set_buffer(content,notify_content,strlen(notify_content)); + + lev = linphone_core_create_notify(marie->lc,pauline->identity,"dodo"); + linphone_event_ref(lev); + linphone_event_add_custom_header(lev,"CustomHeader","someValue"); + linphone_event_notify(lev,content); + + + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,1,3000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneSubscriptionTerminated,1,3000)); + + linphone_event_unref(lev); + + linphone_content_unref(content); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + test_t event_tests[] = { TEST_ONE_TAG("Subscribe declined", subscribe_test_declined, "presence"), TEST_ONE_TAG("Subscribe terminated by subscriber", subscribe_test_terminated_by_subscriber, "presence"), @@ -367,7 +396,8 @@ test_t event_tests[] = { TEST_ONE_TAG("Subscribe terminated by notifier", subscribe_test_terminated_by_notifier, "LeaksMemory"), TEST_ONE_TAG("Publish", publish_test, "presence"), TEST_ONE_TAG("Publish without expires", publish_without_expires, "presence"), - TEST_ONE_TAG("Publish without automatic refresh",publish_no_auto_test, "presence") + TEST_ONE_TAG("Publish without automatic refresh",publish_no_auto_test, "presence"), + TEST_ONE_TAG("Out of dialog notify", out_of_dialog_notify, "presence") }; test_suite_t event_test_suite = {"Event", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,