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.
This commit is contained in:
Simon Morlat 2016-05-27 23:03:05 +02:00
parent fb9e35e4bd
commit e4c674c2e3
10 changed files with 168 additions and 27 deletions

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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
};

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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);

2
oRTP

@ -1 +1 @@
Subproject commit d10bdd4832efa9b3bd6ce99b71873ca33326e812
Subproject commit d94f56bb37b4575445206ef3ca784651ac81f83f

View file

@ -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,