From 36e445f97f6d45239a3271f60fc3385892503741 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 6 Jun 2013 10:49:28 +0200 Subject: [PATCH] generic subscribe notify api in progress --- console/linphonec.c | 15 +- coreapi/Makefile.am | 4 +- coreapi/bellesip_sal/sal_impl.c | 55 +++-- coreapi/bellesip_sal/sal_impl.h | 10 +- coreapi/bellesip_sal/sal_op_events.c | 303 +++++++++++++++++++++++++ coreapi/bellesip_sal/sal_op_impl.c | 162 +++++++++---- coreapi/bellesip_sal/sal_op_info.c | 23 +- coreapi/bellesip_sal/sal_op_message.c | 1 + coreapi/bellesip_sal/sal_op_presence.c | 41 +--- coreapi/bellesip_sal/sal_op_publish.c | 26 ++- coreapi/callbacks.c | 75 +++++- coreapi/event.c | 178 +++++++++++++++ coreapi/event.h | 51 ++++- coreapi/friend.c | 2 +- coreapi/info.c | 16 +- coreapi/linphonecore.c | 4 + coreapi/linphonecore.h | 90 ++++---- coreapi/misc.c | 60 +++++ coreapi/presence.c | 2 +- coreapi/private.h | 11 + coreapi/proxy.c | 2 +- include/sal/sal.h | 34 ++- mediastreamer2 | 2 +- tester/Makefile.am | 9 +- tester/eventapi_tester.c | 108 +++++++++ tester/liblinphone_tester.c | 3 + tester/liblinphone_tester.h | 9 + 27 files changed, 1084 insertions(+), 212 deletions(-) create mode 100644 coreapi/bellesip_sal/sal_op_events.c create mode 100644 coreapi/event.c create mode 100644 tester/eventapi_tester.c diff --git a/console/linphonec.c b/console/linphonec.c index 60c3c2af1..f9e329b51 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -123,7 +123,7 @@ static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_display_something (LinphoneCore * lc, const char *something); static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); -static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event); +static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state); static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, @@ -281,13 +281,14 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern * Linphone core callback */ static void -linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event) +linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state) { - if(!strcmp(event,"refer")) - { - linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n", - from,(long)linphone_call_get_user_pointer (call)); + char *remote=linphone_call_get_remote_address_as_string(call); + if (new_call_state==LinphoneCallConnected){ + linphonec_out("The distant endpoint %s of call %li has been transfered, you can safely close the call.\n", + remote,(long)linphone_call_get_user_pointer (call)); } + ms_free(remote); } @@ -638,7 +639,7 @@ main (int argc, char *argv[]) { linphonec_vtable.text_received=linphonec_text_received; linphonec_vtable.dtmf_received=linphonec_dtmf_received; linphonec_vtable.refer_received=linphonec_display_refer; - linphonec_vtable.notify_recv=linphonec_notify_received; + linphonec_vtable.transfer_state_changed=linphonec_transfer_state_changed; linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed; if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE); diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 1180bfff9..c4c60fcf7 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -46,6 +46,7 @@ liblinphone_la_SOURCES=\ conference.c \ message_storage.c \ info.c \ + event.c \ $(GITVERSION_FILE) if BUILD_UPNP @@ -63,7 +64,8 @@ liblinphone_la_SOURCES+= bellesip_sal/sal_address_impl.c \ bellesip_sal/sal_op_presence.c \ bellesip_sal/sal_op_publish.c \ bellesip_sal/sal_op_call_transfer.c \ - bellesip_sal/sal_op_info.c + bellesip_sal/sal_op_info.c \ + bellesip_sal/sal_op_events.c else liblinphone_la_SOURCES+= sal_eXosip2.c sal_eXosip2.h\ sal_eXosip2_sdp.c \ diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 960a50254..6f7fe2124 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -141,6 +141,7 @@ static void process_request_event(void *sal, const belle_sip_request_event_t *ev belle_sip_header_from_t* from_header; belle_sip_header_to_t* to; belle_sip_response_t* resp; + belle_sip_header_t *evh; from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); @@ -150,10 +151,13 @@ static void process_request_event(void *sal, const belle_sip_request_event_t *ev op=sal_op_new((Sal*)sal); op->dir=SalOpDirIncoming; sal_op_call_fill_cbs(op); - } else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0) { + } else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0 && (evh=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"))!=NULL) { op=sal_op_new((Sal*)sal); op->dir=SalOpDirIncoming; - sal_op_presence_fill_cbs(op); + if (strncmp(belle_sip_header_get_unparsed_value(evh),"presence",strlen("presence"))==0){ + sal_op_presence_fill_cbs(op); + }else + sal_op_subscribe_fill_cbs(op); } else if (strcmp("MESSAGE",belle_sip_request_get_method(req))==0) { op=sal_op_new((Sal*)sal); op->dir=SalOpDirIncoming; @@ -259,7 +263,6 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even } if (sal_op_get_contact(op)){ if (received!=NULL || rport>0) { - contact_address = BELLE_SIP_HEADER_ADDRESS(sal_address_clone(sal_op_get_contact_address(op))); contact_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact_address)); if (received && strcmp(received,belle_sip_uri_get_host(contact_uri))!=0) { @@ -302,30 +305,28 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even /*handle authorization*/ switch (response_code) { - case 200: { - break; - } - case 401: - case 407:{ - - /*belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*//*remove op from trans*/ - if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) { - /*only bye are completed*/ - belle_sip_message("Op is in state terminating, nothing else to do "); - } else { - if (op->pending_auth_transaction){ - belle_sip_object_unref(op->pending_auth_transaction); - op->pending_auth_transaction=NULL; + case 200: { + break; + } + case 401: + case 407:{ + + /*belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*//*remove op from trans*/ + if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) { + /*only bye are completed*/ + belle_sip_message("Op is in state terminating, nothing else to do "); + } else { + if (op->pending_auth_transaction){ + belle_sip_object_unref(op->pending_auth_transaction); + op->pending_auth_transaction=NULL; + } + op->pending_auth_transaction=(belle_sip_client_transaction_t*)belle_sip_object_ref(client_transaction); + sal_process_authentication(op); + return; } - op->pending_auth_transaction=(belle_sip_client_transaction_t*)belle_sip_object_ref(client_transaction); - sal_process_authentication(op); - return; } } - } - op->callbacks.process_response_event(op,event); - } else { ms_error("Unhandled event response [%p]",event); } @@ -444,10 +445,14 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ ctx->callbacks.dtmf_received=(SalOnDtmfReceived)unimplemented_stub; if (ctx->callbacks.notify==NULL) ctx->callbacks.notify=(SalOnNotify)unimplemented_stub; - if (ctx->callbacks.notify_presence==NULL) - ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub; if (ctx->callbacks.subscribe_received==NULL) ctx->callbacks.subscribe_received=(SalOnSubscribeReceived)unimplemented_stub; + if (ctx->callbacks.subscribe_closed==NULL) + ctx->callbacks.subscribe_closed=(SalOnSubscribeClosed)unimplemented_stub; + if (ctx->callbacks.notify_presence==NULL) + ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub; + if (ctx->callbacks.subscribe_presence_received==NULL) + ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub; if (ctx->callbacks.text_received==NULL) ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub; if (ctx->callbacks.ping_reply==NULL) diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h index 2a6f50cc5..0b3e7ae24 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -66,7 +66,8 @@ typedef enum SalOpType { SalOpMessage, SalOpPresence, SalOpPublish, - SalOpInfo + SalOpInfo, + SalOpSubscribe }SalOpType_t; const char* sal_op_type_to_string(const SalOpType_t type); @@ -127,6 +128,8 @@ void sal_op_message_fill_cbs(SalOp*op); /*info*/ void sal_op_info_fill_cbs(SalOp*op); +void sal_op_subscribe_fill_cbs(SalOp*op); + /*call transfer*/ void sal_op_process_refer(SalOp *op, const belle_sip_request_event_t *event); void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *event); @@ -140,4 +143,9 @@ belle_sip_response_t *sal_create_response_from_request(Sal *sal, belle_sip_reque void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming); +void sal_op_add_body(SalOp *op, belle_sip_message_t *req, const SalBody *body); +bool_t sal_op_get_body(SalOp *op, belle_sip_message_t *msg, SalBody *salbody); + +SalReason sal_reason_to_sip_code(SalReason r); + #endif /* SAL_IMPL_H_ */ diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c new file mode 100644 index 000000000..4923f2269 --- /dev/null +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -0,0 +1,303 @@ +/* +linphone +Copyright (C) 2012 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "sal_impl.h" + + +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"); +} +static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { + SalOp* op= (SalOp*)ctx; + if (op->dialog) { + sal_op_unref(op); + op->dialog=NULL; + } +} + +SalSubscribeStatus get_subscription_state(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){ + if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED)==0) + sss=SalSubscribeTerminated; + else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_PENDING)==0) + sss=SalSubscribePending; + else if (strcmp(belle_sip_header_subscription_state_get_state(subscription_state_header),BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE)==0) + sss=SalSubscribeActive; + } + return sss; +} + +static void subscribe_response_event(void *op_base, const belle_sip_response_event_t *event){ + SalOp* op = (SalOp*)op_base; + belle_sip_dialog_state_t dialog_state; + belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); + belle_sip_response_t* response=belle_sip_response_event_get_response(event); + belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); + int code = belle_sip_response_get_status_code(response); + char reason[256]={0}; + SalError error=SalErrorUnknown; + SalReason sr=SalReasonUnknown; + belle_sip_header_expires_t* expires; + SalSubscribeStatus sss=get_subscription_state(BELLE_SIP_MESSAGE(response)); + + if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) { + ms_error("subscription to [%s] rejected reason [%s]",sal_op_get_to(op),reason[0]!=0?reason:sal_reason_to_string(sr)); + op->base.root->callbacks.subscribe_response(op,SalSubscribeTerminated,error,sr); + return; + } + set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event)); + if (!op->dialog) { + ms_message("subscribe op [%p] receive out of dialog answer [%i]",op,code); + return; + } + dialog_state=belle_sip_dialog_get_state(op->dialog); + switch(dialog_state) { + case BELLE_SIP_DIALOG_NULL: + case BELLE_SIP_DIALOG_EARLY: { + ms_error("subscribe op [%p] receive an unexpected answer [%i]",op,code); + break; + } + case BELLE_SIP_DIALOG_CONFIRMED: { + if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0) { + expires=belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t); + if(op->refresher) { + belle_sip_refresher_stop(op->refresher); + belle_sip_object_unref(op->refresher); + op->refresher=NULL; + } + if (expires>0){ + op->refresher=belle_sip_client_transaction_create_refresher(client_transaction); + } + op->base.root->callbacks.subscribe_response(op,sss,SalErrorNone,SalReasonUnknown); + } + break; + } + case BELLE_SIP_DIALOG_TERMINATED: + if (op->refresher) { + belle_sip_refresher_stop(op->refresher); + belle_sip_object_unref(op->refresher); + op->refresher=NULL; + } + break; + default: { + ms_error("subscribe op [%p] receive answer [%i] not implemented",op,code); + } + /* no break */ + } +} + +static void subscribe_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { + ms_message("subscribe_process_timeout not implemented yet"); +} + +static void subscribe_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { + ms_message("subscribe_process_transaction_terminated not implemented yet"); +} + +static void subscribe_process_request_event(void *op_base, const belle_sip_request_event_t *event) { + SalOp* op = (SalOp*)op_base; + belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); + belle_sip_request_t* req = belle_sip_request_event_get_request(event); + belle_sip_dialog_state_t dialog_state; + belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t); + 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; + SalBody body; + SalSubscribeStatus sub_state; + belle_sip_response_t* resp; + const char *eventname=NULL; + + 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((belle_sip_message_t*)req,"Event"); + eventname=belle_sip_header_get_unparsed_value(event_header); + sal_op_get_body(op,(belle_sip_message_t*)req,&body); + + if (eventname==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); + return; + } + + if (!op->dialog) { + op->dialog=belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction)); + belle_sip_dialog_set_application_data(op->dialog,op); + sal_op_ref(op); + ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); + } + dialog_state=belle_sip_dialog_get_state(op->dialog); + switch(dialog_state) { + + case BELLE_SIP_DIALOG_NULL: { + op->base.root->callbacks.subscribe_received(op,eventname,body.type ? &body : NULL); + break; + } + case BELLE_SIP_DIALOG_EARLY: + ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",belle_sip_request_get_method(req),op->dialog); + break; + + case BELLE_SIP_DIALOG_CONFIRMED: + if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) { + if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { + sub_state=SalSubscribeTerminated; + ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); + } else + sub_state=SalSubscribeActive; + + op->base.root->callbacks.notify(op,sub_state,eventname,&body); + resp=sal_op_create_response_from_request(op,req,200); + belle_sip_server_transaction_send_response(server_transaction,resp); + } else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0) { + /*either a refresh of an unsubscribe*/ + if (expires && belle_sip_header_expires_get_expires(expires)>0) { + + } else if(expires) { + ms_message("Unsubscribe received from [%s]",sal_op_get_from(op)); + resp=sal_op_create_response_from_request(op,req,200); + belle_sip_server_transaction_send_response(server_transaction,resp); + op->base.root->callbacks.subscribe_closed(op); + } + } + break; + default: { + ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); + } + } +} + +void sal_op_subscribe_fill_cbs(SalOp*op) { + op->callbacks.process_io_error=subscribe_process_io_error; + op->callbacks.process_response_event=subscribe_response_event; + op->callbacks.process_timeout=subscribe_process_timeout; + op->callbacks.process_transaction_terminated=subscribe_process_transaction_terminated; + op->callbacks.process_request_event=subscribe_process_request_event; + op->callbacks.process_dialog_terminated=subscribe_process_dialog_terminated; + op->type=SalOpSubscribe; +} + +static int set_event_name(SalOp *op, belle_sip_message_t *msg){ + belle_sip_transaction_t *last_transaction; + belle_sip_request_t *req; + belle_sip_header_t *event; + + if (!op->dialog) return -1; + + last_transaction=belle_sip_dialog_get_last_transaction(op->dialog); + + if (!last_transaction) return -1; + + req=belle_sip_transaction_get_request(last_transaction); + event=belle_sip_message_get_header((belle_sip_message_t*)req,"Event"); + if (!event){ + ms_error("No event header in last request."); + return -1; + } + belle_sip_message_add_header(msg,event); + return 0; +} + +int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body){ + belle_sip_request_t *req=NULL; + + if (from) + sal_op_set_from(op,from); + if (to) + sal_op_set_to(op,to); + + if (!op->dialog){ + sal_op_subscribe_fill_cbs(op); + /*???sal_exosip_fix_route(op); make sure to ha ;lr*/ + req=sal_op_build_request(op,"SUBSCRIBE"); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event",eventname)); + }else{ + belle_sip_transaction_t *last=belle_sip_dialog_get_last_transaction(op->dialog); + belle_sip_message_t *msg=BELLE_SIP_MESSAGE(belle_sip_transaction_get_request(last)); + req=belle_sip_dialog_create_request(op->dialog,"SUBSCRIBE"); + if (expires==-1){ + belle_sip_header_expires_t *eh=belle_sip_message_get_header_by_type(msg,belle_sip_header_expires_t); + expires=belle_sip_header_expires_get_expires(eh); + } + set_event_name(op,(belle_sip_message_t*)req); + } + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); + sal_op_add_body(op,(belle_sip_message_t*)req,body); + return sal_op_send_request(op,req); +} + +int sal_unsubscribe(SalOp *op){ + belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"SUBSCRIBE"):NULL; /*cannot create request if dialog not set yet*/ + if (!req) { + ms_error("Cannot unsubscribe to [%s]",sal_op_get_to(op)); + return -1; + } + if (op->refresher) + belle_sip_refresher_stop(op->refresher); + set_event_name(op,(belle_sip_message_t*)req); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(0))); + return sal_op_send_request(op,req); +} + +int sal_subscribe_accept(SalOp *op){ + belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)); + belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); + belle_sip_response_t* resp = sal_op_create_response_from_request(op,req,200); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(expires)); + belle_sip_server_transaction_send_response(op->pending_server_trans,resp); + 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)); + belle_sip_server_transaction_send_response(op->pending_server_trans,resp); + return 0; +} + +int sal_notify(SalOp *op, const SalBody *body){ + belle_sip_request_t* notify; + + if (!op->dialog) return -1; + + notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); + if (set_event_name(op,(belle_sip_message_t*)notify)==-1){ + belle_sip_object_unref(notify); + return -1; + } + + belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) + ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600))); + + sal_op_add_body(op,(belle_sip_message_t*)notify, body); + return sal_op_send_request(op,notify); +} + +int sal_notify_close(SalOp *op){ + belle_sip_request_t* notify; + if (!op->dialog) return -1; + notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); + set_event_name(op,(belle_sip_message_t*)notify); + 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_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index ffd2b3b86..ea8dd9c34 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -256,56 +256,93 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request) { return _sal_op_send_request_with_contact(op, request,need_contact); } +SalReason sal_reason_to_sip_code(SalReason r){ + int ret=500; + switch(r){ + case SalReasonUnknown: + ret=400; + break; + + case SalReasonBusy: + ret=486; + break; + case SalReasonDeclined: + ret=603; + break; + case SalReasonDoNotDisturb: + ret=600; + break; + case SalReasonForbidden: + ret=403; + break; + case SalReasonMedia: + ret=415; + break; + case SalReasonNotFound: + ret=404; + break; + case SalReasonRedirect: + ret=302; + break; + case SalReasonTemporarilyUnavailable: + ret=480; + break; + case SalReasonServiceUnavailable: + ret=503; + break; + } + return ret; +} void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal_reason) { - switch(code) { - case 400: - *sal_err=SalErrorUnknown; - break; - case 403: + switch(code) { + case 400: + *sal_err=SalErrorUnknown; + break; + case 403: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonForbidden; + break; + case 404: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonNotFound; + break; + case 415: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonMedia; + break; + case 422: + ms_error ("422 not implemented yet");; + break; + case 480: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonTemporarilyUnavailable; + break; + case 486: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonBusy; + break; + case 487: + break; + case 600: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonDoNotDisturb; + break; + case 603: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonDeclined; + break; + case 503: + *sal_err=SalErrorFailure; + *sal_reason=SalReasonServiceUnavailable; + break; + default: + if (code>0){ *sal_err=SalErrorFailure; - *sal_reason=SalReasonForbidden; - break; - case 404: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonNotFound; - break; - case 415: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonMedia; - break; - case 422: - ms_error ("422 not implemented yet");; - break; - case 480: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonTemporarilyUnavailable; - break; - case 486: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonBusy; - break; - case 487: - break; - case 503: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonServiceUnavailable; - break; - case 600: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonDoNotDisturb; - break; - case 603: - *sal_err=SalErrorFailure; - *sal_reason=SalReasonDeclined; - break; - default: - if (code>0){ - *sal_err=SalErrorFailure; - *sal_reason=SalReasonUnknown; - }else *sal_err=SalErrorNoResponse; - /* no break */ - } + *sal_reason=SalReasonUnknown; + }else *sal_err=SalErrorNoResponse; + /* no break */ + } } /*return TRUE if error code*/ bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size) { @@ -415,4 +452,35 @@ const char *sal_op_get_remote_contact(const SalOp *op){ return sal_custom_header_find(op->base.recv_custom_headers,"Contact"); } +void sal_op_add_body(SalOp *op, belle_sip_message_t *req, const SalBody *body){ + if (body && body->type && body->subtype && body->data){ + belle_sip_message_add_header((belle_sip_message_t*)req, + (belle_sip_header_t*)belle_sip_header_content_type_create(body->type,body->subtype)); + belle_sip_message_add_header((belle_sip_message_t*)req, + (belle_sip_header_t*)belle_sip_header_content_length_create(body->size)); + belle_sip_message_set_body((belle_sip_message_t*)req,(const char*)body->data,body->size); + } +} + +bool_t sal_op_get_body(SalOp *op, belle_sip_message_t *msg, SalBody *salbody){ + const char *body = NULL; + belle_sip_header_content_type_t *content_type; + belle_sip_header_content_length_t *clen=NULL; + + content_type=belle_sip_message_get_header_by_type(msg,belle_sip_header_content_type_t); + if (content_type){ + body=belle_sip_message_get_body(msg); + clen=belle_sip_message_get_header_by_type(msg,belle_sip_header_content_length_t); + } + + if (content_type && body && clen) { + salbody->type=belle_sip_header_content_type_get_type(content_type); + salbody->subtype=belle_sip_header_content_type_get_subtype(content_type); + salbody->data=body; + salbody->size=belle_sip_header_content_length_get_content_length(clen); + return TRUE; + } + memset(salbody,0,sizeof(salbody)); + return FALSE; +} diff --git a/coreapi/bellesip_sal/sal_op_info.c b/coreapi/bellesip_sal/sal_op_info.c index a86328fbb..c3f600aaa 100644 --- a/coreapi/bellesip_sal/sal_op_info.c +++ b/coreapi/bellesip_sal/sal_op_info.c @@ -31,23 +31,10 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t SalOp* op = (SalOp*)op_base; belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,req); - belle_sip_header_content_type_t* content_type; - belle_sip_header_content_length_t *clen=NULL; belle_sip_response_t* resp; SalBody salbody; - const char *body = NULL; - content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t); - if (content_type){ - body=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); - clen=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_length_t); - } - - if (content_type && body && clen) { - salbody.type=belle_sip_header_content_type_get_type(content_type); - salbody.subtype=belle_sip_header_content_type_get_subtype(content_type); - salbody.data=body; - salbody.size=belle_sip_header_content_length_get_content_length(clen); + if (sal_op_get_body(op,(belle_sip_message_t*)req,&salbody)) { op->base.root->callbacks.info_received(op,&salbody); } else { op->base.root->callbacks.info_received(op,NULL); @@ -60,13 +47,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t int sal_send_info(SalOp *op, const char *from, const char *to, const SalBody *body){ belle_sip_request_t *req=sal_op_build_request(op,"INFO"); sal_op_info_fill_cbs(op); - if (body && body->type && body->subtype && body->data){ - belle_sip_message_add_header((belle_sip_message_t*)req, - (belle_sip_header_t*)belle_sip_header_content_type_create(body->type,body->subtype)); - belle_sip_message_add_header((belle_sip_message_t*)req, - (belle_sip_header_t*)belle_sip_header_content_length_create(body->size)); - belle_sip_message_set_body((belle_sip_message_t*)req,(const char*)body->data,body->size); - } + sal_op_add_body(op,(belle_sip_message_t*)req,body); return sal_op_send_request(op,req); } diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index 08eef45c7..140dbe85f 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -149,6 +149,7 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co return sal_op_send_request(op,req); } + int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) { return sal_message_send(op,from,to,"text/plain",msg); } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index 8d800ef9d..b68ef1f33 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -424,6 +424,7 @@ static void presence_response_event(void *op_base, const belle_sip_response_even SalError error=SalErrorUnknown; SalReason sr=SalReasonUnknown; belle_sip_header_expires_t* expires; + if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) { ms_error("subscription to [%s] rejected reason [%s]",sal_op_get_to(op),reason[0]!=0?reason:sal_reason_to_string(sr)); op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, SalPresenceOffline,NULL); @@ -436,9 +437,7 @@ static void presence_response_event(void *op_base, const belle_sip_response_even } dialog_state=belle_sip_dialog_get_state(op->dialog); - - switch(dialog_state) { - + switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { ms_error("presence op [%p] receive an unexpected answer [%i]",op,code); @@ -450,6 +449,7 @@ static void presence_response_event(void *op_base, const belle_sip_response_even if(op->refresher) { belle_sip_refresher_stop(op->refresher); belle_sip_object_unref(op->refresher); + op->refresher=NULL; } if (expires>0){ op->refresher=belle_sip_client_transaction_create_refresher(client_transaction); @@ -475,9 +475,11 @@ static void presence_response_event(void *op_base, const belle_sip_response_even static void presence_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { ms_error("presence_process_timeout not implemented yet"); } + static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { ms_message("presence_process_transaction_terminated not implemented yet"); } + static void presence_process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); @@ -504,7 +506,7 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { - op->base.root->callbacks.subscribe_received(op,sal_op_get_from(op)); + op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); break; } case BELLE_SIP_DIALOG_EARLY: @@ -553,7 +555,7 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques } else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0) { /*either a refresh of an unsubscribe*/ if (expires && belle_sip_header_expires_get_expires(expires)>0) { - op->base.root->callbacks.subscribe_received(op,sal_op_get_from(op)); + op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); } else if(expires) { ms_message("Unsubscribe received from [%s]",sal_op_get_from(op)); resp=sal_op_create_response_from_request(op,req,200); @@ -566,9 +568,8 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques } /* no break */ } - - } + void sal_op_presence_fill_cbs(SalOp*op) { op->callbacks.process_io_error=presence_process_io_error; op->callbacks.process_response_event=presence_response_event; @@ -597,29 +598,7 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ return sal_op_send_request(op,req); } -int sal_unsubscribe(SalOp *op){ - belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"SUBSCRIBE"):NULL; /*cannot create request if dialog not set yet*/ - if (!req) { - ms_error("Cannot unsubscribe to [%s]",sal_op_get_to(op)); - return -1; - } - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence")); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(0))); - return sal_op_send_request(op,req); -} -int sal_subscribe_accept(SalOp *op){ - belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)); - belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); - belle_sip_response_t* resp = sal_op_create_response_from_request(op,req,200); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(expires)); - belle_sip_server_transaction_send_response(op->pending_server_trans,resp); - return 0; -} -int sal_subscribe_decline(SalOp *op){ - belle_sip_response_t* resp = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),403); - belle_sip_server_transaction_send_response(op->pending_server_trans,resp); - return 0; -} + static belle_sip_request_t *create_presence_notify(SalOp *op){ belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); @@ -635,7 +614,7 @@ int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_ return sal_op_send_request(op,notify); } -int sal_notify_close(SalOp *op){ +int sal_notify_presence_close(SalOp *op){ belle_sip_request_t* notify=create_presence_notify(op); sal_add_presence_info(BELLE_SIP_MESSAGE(notify),SalPresenceOffline); /*FIXME, what about expires ??*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c index 29d78cd72..cf9dc8f54 100644 --- a/coreapi/bellesip_sal/sal_op_publish.c +++ b/coreapi/bellesip_sal/sal_op_publish.c @@ -30,7 +30,7 @@ static void publish_refresher_listener ( const belle_sip_refresher_t* refresher } /*presence publish */ -int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus status){ +int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceStatus status){ belle_sip_request_t *req=NULL; if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { if (from) @@ -53,3 +53,27 @@ int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus s } } +int sal_publish(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body){ + belle_sip_request_t *req=NULL; + if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { + if (from) + sal_op_set_from(op,from); + if (to) + sal_op_set_to(op,to); + + op->type=SalOpPublish; + req=sal_op_build_request(op,"PUBLISH"); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event",eventname)); + if (body) sal_op_add_body(op,BELLE_SIP_MESSAGE(req),body); + return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener); + } else { + /*update status*/ + const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher); + belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans)); + /*update body*/ + if (body) sal_op_add_body(op,BELLE_SIP_MESSAGE(last_publish),body); + return belle_sip_refresher_refresh(op->refresher,BELLE_SIP_REFRESHER_REUSE_EXPIRES); + } +} + + diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 35ae87a24..1fe12bd1a 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -872,25 +872,17 @@ static void text_received(SalOp *op, const SalMessage *msg){ } } -static void notify(SalOp *op, const char *from, const char *msg){ - LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); - ms_message("get a %s notify from %s",msg,from); - if(lc->vtable.notify_recv) - lc->vtable.notify_recv(lc,call,from,msg); -} - static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_notify_recv(lc,op,ss,status); } -static void subscribe_received(SalOp *op, const char *from){ +static void subscribe_presence_received(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_subscription_new(lc,op,from); } -static void subscribe_closed(SalOp *op, const char *from){ +static void subscribe_presence_closed(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_subscription_closed(lc,op); } @@ -1008,6 +1000,62 @@ static void info_received(SalOp *op, const SalBody *body){ linphone_core_notify_info_message(lc,op,body); } +static void subscribe_response(SalOp *op, SalSubscribeStatus status, SalError error, SalReason reason){ + LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); + + if (lev==NULL) return; + + if (status==SalSubscribeActive){ + linphone_event_set_state(lev,LinphoneSubscriptionActive); + }else if (status==SalSubscribePending){ + linphone_event_set_state(lev,LinphoneSubscriptionPending); + }else{ + linphone_event_set_reason(lev, linphone_reason_from_sal(reason)); + linphone_event_set_state(lev,LinphoneSubscriptionError); + linphone_event_destroy(lev); + } +} + +static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, const SalBody *body){ + LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneContent content; + + if (lev==NULL) { + /*out of subscribe notify */ + lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionOutgoing); + } + if (lc->vtable.notify_received){ + lc->vtable.notify_received(lc,lev,eventname,linphone_content_from_sal_body(&content,body)); + } + if (st!=SalSubscribeNone){ + linphone_event_set_state(lev,linphone_subscription_state_from_sal(st)); + if (st==SalSubscribeTerminated) + linphone_event_destroy(lev); + } + +} + +static void subscribe_received(SalOp *op, const char *eventname, const SalBody *body){ + LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + + if (lev==NULL) { + lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming); + linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived); + }else{ + /*subscribe refresh, unhandled*/ + } + +} + +static void subscribe_closed(SalOp *op){ + LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op); + + linphone_event_set_state(lev,LinphoneSubscriptionTerminated); + linphone_event_destroy(lev); +} + SalCallbacks linphone_sal_callbacks={ call_received, call_ringing, @@ -1030,11 +1078,14 @@ SalCallbacks linphone_sal_callbacks={ refer_received, text_received, text_delivery_update, - notify, - notify_presence, notify_refer, subscribe_received, subscribe_closed, + subscribe_response, + notify, + subscribe_presence_received, + subscribe_presence_closed, + notify_presence, ping_reply, auth_requested, info_received diff --git a/coreapi/event.c b/coreapi/event.c new file mode 100644 index 000000000..d3a774fb1 --- /dev/null +++ b/coreapi/event.c @@ -0,0 +1,178 @@ +/* +linphone +Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "private.h" + + +struct _LinphoneEvent{ + LinphoneSubscriptionDir dir; + LinphoneCore *lc; + SalOp *op; + LinphoneSubscriptionState state; + LinphoneReason reason; + void *userdata; +}; + +LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss){ + switch(ss){ + case SalSubscribeNone: return LinphoneSubscriptionNone; + case SalSubscribePending: return LinphoneSubscriptionPending; + case SalSubscribeTerminated: return LinphoneSubscriptionTerminated; + case SalSubscribeActive: return LinphoneSubscriptionActive; + } + return LinphoneSubscriptionNone; +} + +LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir){ + LinphoneEvent *lev=ms_new0(LinphoneEvent,1); + lev->lc=lc; + lev->dir=dir; + lev->op=sal_op_new(lc->sal); + sal_op_set_user_pointer(lev->op,lev); + return lev; +} + +LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir){ + LinphoneEvent *lev=ms_new0(LinphoneEvent,1); + lev->lc=lc; + lev->dir=LinphoneSubscriptionIncoming; + lev->op=op; + sal_op_set_user_pointer(lev->op,lev); + return lev; +} + +void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state){ + LinphoneCore *lc=lev->lc; + if (lev->state!=state){ + lev->state=state; + if (lc->vtable.subscription_state_changed){ + lc->vtable.subscription_state_changed(lev->lc,lev,state); + } + } +} + +void linphone_event_set_reason(LinphoneEvent *lev, LinphoneReason reason){ + lev->reason=reason; +} + +LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){ + return lev->reason; +} + +LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ + LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing); + SalBody salbody; + linphone_configure_op(lc,lev->op,resource,NULL,TRUE); + sal_subscribe(lev->op,NULL,NULL,event,expires,sal_body_from_content(&salbody,body)); + linphone_event_set_state(lev,LinphoneSubscriptionOutoingInit); + return lev; +} + + +int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ + SalBody salbody; + if (lev->state!=LinphoneSubscriptionActive){ + ms_error("linphone_event_update_subscribe(): cannot update subscription if subscription wasn't accepted."); + return -1; + } + if (lev->dir!=LinphoneSubscriptionOutgoing){ + ms_error("linphone_event_deny_subscription(): cannot update an incoming subscription."); + return -1; + } + return sal_subscribe(lev->op,NULL,NULL,NULL,-1,sal_body_from_content(&salbody,body)); +} + +int linphone_event_accept_subscription(LinphoneEvent *lev){ + int err; + if (lev->state!=LinphoneSubscriptionIncomingReceived){ + ms_error("linphone_event_accept_subscription(): cannot accept subscription if subscription wasn't just received."); + return -1; + } + err=sal_subscribe_accept(lev->op); + if (err==0){ + linphone_event_set_state(lev,LinphoneSubscriptionActive); + } + return err; +} + +int linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){ + if (lev->state!=LinphoneSubscriptionIncomingReceived){ + ms_error("linphone_event_deny_subscription(): cannot deny subscription if subscription wasn't just received."); + return -1; + } + return sal_subscribe_decline(lev->op,linphone_reason_to_sal(reason)); +} + +int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){ + SalBody salbody; + if (lev->state!=LinphoneSubscriptionActive){ + ms_error("linphone_event_notify(): cannot notify if subscription is not active."); + return -1; + } + if (lev->dir!=LinphoneSubscriptionIncoming){ + ms_error("linphone_event_notify(): cannot notify if not an incoming subscription."); + return -1; + } + return sal_notify(lev->op,sal_body_from_content(&salbody,body)); +} + +LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ + SalBody salbody; + LinphoneEvent *lev=linphone_event_new(lc,LinphoneSubscriptionInvalidDir); + linphone_configure_op(lc,lev->op,resource,NULL,FALSE); + sal_publish(lev->op,NULL,NULL,event,expires,sal_body_from_content(&salbody,body)); + return lev; +} + + +int linphone_event_update_publish(LinphoneEvent *lev, const LinphoneContent *body){ + SalBody salbody; + return sal_publish(lev->op,NULL,NULL,NULL,-1,sal_body_from_content(&salbody,body)); +} + +void linphone_event_set_user_pointer(LinphoneEvent *ev, void *up){ + ev->userdata=up; +} + +void *linphone_event_get_user_pointer(const LinphoneEvent *ev){ + return ev->userdata; +} + +void linphone_event_terminate(LinphoneEvent *lev){ + if (lev->dir==LinphoneSubscriptionIncoming){ + sal_notify_close(lev->op); + }else if (lev->dir==LinphoneSubscriptionOutgoing){ + sal_unsubscribe(lev->op); + } + + if (lev->state!=LinphoneSubscriptionNone){ + linphone_event_set_state(lev,LinphoneSubscriptionTerminated); + } + linphone_event_destroy(lev); +} + +void linphone_event_destroy(LinphoneEvent *lev){ + if (lev->op) + sal_op_release(lev->op); + ms_free(lev); +} + +LinphoneSubscriptionDir linphone_event_get_dir(LinphoneEvent *lev){ + return lev->dir; +} diff --git a/coreapi/event.h b/coreapi/event.h index 68417a846..aa87a42cc 100644 --- a/coreapi/event.h +++ b/coreapi/event.h @@ -19,6 +19,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef LINPHONEEVENT_H #define LINPHONEEVENT_H +/** + * @addtogroup subscriptions + * @{ +**/ + struct _LinphoneEvent; typedef struct _LinphoneEvent LinphoneEvent; @@ -28,13 +33,14 @@ typedef struct _LinphoneEvent LinphoneEvent; **/ enum _LinphoneSubscriptionDir{ LinphoneSubscriptionIncoming, - LinphoneSubscriptionOutgoing + LinphoneSubscriptionOutgoing, + LinphoneSubscriptionInvalidDir }; /** * Typedef alias for _LinphoneSubscriptionDir **/ -typedef _LinphoneSubscriptionDir LinphoneSubscriptionDir; +typedef enum _LinphoneSubscriptionDir LinphoneSubscriptionDir; /** * Enum for subscription states. @@ -49,7 +55,7 @@ enum _LinphoneSubscriptionState{ LinphoneSubscriptionError /**insub){ - sal_notify_close(lf->insub); + sal_notify_presence_close(lf->insub); } } diff --git a/coreapi/info.c b/coreapi/info.c index 961fa0b04..11ec0b7fd 100644 --- a/coreapi/info.c +++ b/coreapi/info.c @@ -58,13 +58,13 @@ static void linphone_content_copy(LinphoneContent *obj, const LinphoneContent *r obj->size=ref->size; } -static void linphone_content_uninit(LinphoneContent * obj){ +void linphone_content_uninit(LinphoneContent * obj){ if (obj->type) ms_free(obj->type); if (obj->subtype) ms_free(obj->subtype); if (obj->data) ms_free(obj->data); } -static LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj, const SalBody *ref){ +LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj, const SalBody *ref){ SET_STRING(obj,type,ref->type); SET_STRING(obj,subtype,ref->subtype); if (obj->data) { @@ -80,7 +80,17 @@ static LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj return obj; } -static SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *lc){ +const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref){ + if (ref && ref->type){ + obj->type=(char*)ref->type; + obj->subtype=(char*)ref->subtype; + obj->data=(void*)ref->data; + obj->size=ref->size; + } + return NULL; +} + +SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *lc){ if (lc->type){ body->type=lc->type; body->subtype=lc->subtype; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b420dbd3c..f90124fd0 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -5655,6 +5655,10 @@ const char *linphone_reason_to_string(LinphoneReason err){ return "Not answered"; case LinphoneReasonBusy: return "Busy"; + case LinphoneReasonMedia: + return "Incompatible media capabilities"; + case LinphoneReasonIOError: + return "IO error"; } return "unknown error"; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index e363192ac..5cbffe51c 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -91,10 +91,58 @@ typedef struct _LCSipTransports{ * @var LinphoneAddress */ typedef struct SalAddress LinphoneAddress; + +/** + * The LinphoneContent struct holds data that can be embedded in a signaling message. + * @ingroup misc +**/ +struct _LinphoneContent{ + char *type; /**subscribers,uri,&lf)){ if (lf->pol==LinphoneSPDeny){ ms_message("Rejecting %s because we already rejected it once.",from); - sal_subscribe_decline(op); + sal_subscribe_decline(op,SalReasonDeclined); } else { /* else it is in wait for approval state, because otherwise it is in the friend list.*/ diff --git a/coreapi/private.h b/coreapi/private.h index 6afc16c4b..a93bcec47 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -742,6 +742,17 @@ const char *linphone_core_create_uuid(LinphoneCore *lc); void linphone_configure_op(LinphoneCore *lc, SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact); void linphone_call_create_op(LinphoneCall *call); void linphone_core_notify_info_message(LinphoneCore* lc,SalOp *op, const SalBody *body); +LinphoneContent *linphone_content_copy_from_sal_body(LinphoneContent *obj, const SalBody *ref); +SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *lc); +SalReason linphone_reason_to_sal(LinphoneReason reason); +LinphoneReason linphone_reason_from_sal(SalReason reason); +LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir); +LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir); +void linphone_event_destroy(LinphoneEvent *lev); +void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state); +void linphone_event_set_reason(LinphoneEvent *lev, LinphoneReason reason); +LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss); +const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref); #ifdef __cplusplus } diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 253922713..b5d613f5f 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -858,7 +858,7 @@ int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, sal_op_set_from(proxy->publish_op,linphone_proxy_config_get_identity(proxy)); sal_op_set_to(proxy->publish_op,linphone_proxy_config_get_identity(proxy)); } - err=sal_publish(proxy->publish_op,NULL,NULL,linphone_online_status_to_sal(presence_mode)); + err=sal_publish_presence(proxy->publish_op,NULL,NULL,linphone_online_status_to_sal(presence_mode)); return err; } diff --git a/include/sal/sal.h b/include/sal/sal.h index 9759f2931..d3efc5722 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -255,6 +255,7 @@ typedef struct SalOpBase{ typedef enum SalError{ + SalErrorNone, SalErrorNoResponse, SalErrorProtocol, SalErrorFailure, /* see SalReason for more details */ @@ -298,6 +299,8 @@ typedef enum SalReferStatus{ }SalReferStatus; typedef enum SalSubscribeStatus{ + SalSubscribeNone, + SalSubscribePending, SalSubscribeActive, SalSubscribeTerminated }SalSubscribeStatus; @@ -345,11 +348,14 @@ typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf); typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto); typedef void (*SalOnTextReceived)(SalOp *op, const SalMessage *msg); typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus status); -typedef void (*SalOnNotify)(SalOp *op, const char *from, const char *event); typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state); +typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, SalError error, SalReason reason); +typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, const SalBody *body); +typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *event, const SalBody *body); +typedef void (*SalOnSubscribeClosed)(SalOp *salop); typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg); -typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *from); -typedef void (*SalOnSubscribeClosed)(SalOp *salop, const char *from); +typedef void (*SalOnSubscribePresenceReceived)(SalOp *salop, const char *from); +typedef void (*SalOnSubscribePresenceClosed)(SalOp *salop, const char *from); typedef void (*SalOnPingReply)(SalOp *salop); typedef void (*SalOnInfoReceived)(SalOp *salop, const SalBody *body); /*allows sal implementation to access auth info if available, return TRUE if found*/ @@ -378,11 +384,14 @@ typedef struct SalCallbacks{ SalOnRefer refer_received; SalOnTextReceived text_received; SalOnTextDeliveryUpdate text_delivery_update; - SalOnNotify notify; - SalOnNotifyPresence notify_presence; SalOnNotifyRefer notify_refer; SalOnSubscribeReceived subscribe_received; SalOnSubscribeClosed subscribe_closed; + SalOnSubscribeResponse subscribe_response; + SalOnNotify notify; + SalOnSubscribePresenceReceived subscribe_presence_received; + SalOnSubscribePresenceClosed subscribe_presence_closed; + SalOnNotifyPresence notify_presence; SalOnPingReply ping_reply; SalOnAuthRequested auth_requested; SalOnInfoReceived info_received; @@ -516,14 +525,11 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co /*presence Subscribe/notify*/ int sal_subscribe_presence(SalOp *op, const char *from, const char *to); -int sal_unsubscribe(SalOp *op); -int sal_subscribe_accept(SalOp *op); -int sal_subscribe_decline(SalOp *op); int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message); -int sal_notify_close(SalOp *op); +int sal_notify_presence_close(SalOp *op); /*presence publish */ -int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus status); +int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceStatus status); /*ping: main purpose is to obtain its own contact address behind firewalls*/ @@ -532,6 +538,14 @@ int sal_ping(SalOp *op, const char *from, const char *to); /*info messages*/ int sal_send_info(SalOp *op, const char *from, const char *to, const SalBody *body); +/*generic subscribe/notify/publish api*/ +int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body); +int sal_unsubscribe(SalOp *op); +int sal_subscribe_accept(SalOp *op); +int sal_subscribe_decline(SalOp *op, SalReason reason); +int sal_notify(SalOp *op, const SalBody *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 SalBody *body); #define payload_type_set_number(pt,n) (pt)->user_data=(void*)((long)n); diff --git a/mediastreamer2 b/mediastreamer2 index 5b7873ee3..dd0116eb8 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 5b7873ee3cbc460138015ba244330d99aa861d7f +Subproject commit dd0116eb8e26f128a2366b38115b602def4b4073 diff --git a/tester/Makefile.am b/tester/Makefile.am index 6b5e9edae..25ac64865 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -6,7 +6,14 @@ if BUILD_CUNIT_TESTS noinst_PROGRAMS=liblinphone_tester TESTS=$(noinst_PROGRAMS) -liblinphone_tester_SOURCES= liblinphone_tester.c setup_tester.c register_tester.c message_tester.c call_tester.c presence_tester.c upnp_tester.c +liblinphone_tester_SOURCES= liblinphone_tester.c \ + setup_tester.c \ + register_tester.c \ + message_tester.c \ + call_tester.c \ + presence_tester.c \ + upnp_tester.c \ + eventapi_tester.c #liblinphone_tester_CFLAGS=$(CUNIT_CFLAGS) diff --git a/tester/eventapi_tester.c b/tester/eventapi_tester.c new file mode 100644 index 000000000..c132abd65 --- /dev/null +++ b/tester/eventapi_tester.c @@ -0,0 +1,108 @@ +/* + belle-sip - SIP (RFC3261) library. + Copyright (C) 2010 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include "CUnit/Basic.h" +#include "linphonecore.h" +#include "private.h" +#include +#include "liblinphone_tester.h" + + +static const char *subscribe_content="blabla"; +static const char *notify_content="blabla"; + +void linphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *eventname, const LinphoneContent *content){ +} + +void linphone_subscription_state_change(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) { + stats* counters = (stats*)linphone_core_get_user_data(lc); + LinphoneContent content; + + content.type="application"; + content.subtype="somexml2"; + content.data=(void*)notify_content; + content.size=strlen(notify_content); + + switch(state){ + case LinphoneSubscriptionNone: + break; + case LinphoneSubscriptionIncomingReceived: + counters->number_of_LinphoneSubscriptionIncomingReceived++; + linphone_event_accept_subscription(lev); + break; + case LinphoneSubscriptionOutoingInit: + counters->number_of_LinphoneSubscriptionOutgoingInit++; + break; + case LinphoneSubscriptionPending: + counters->number_of_LinphoneSubscriptionPending++; + break; + case LinphoneSubscriptionActive: + counters->number_of_LinphoneSubscriptionActive++; + if (linphone_event_get_dir(lev)==LinphoneSubscriptionIncoming) + linphone_event_notify(lev,&content); + break; + case LinphoneSubscriptionTerminated: + counters->number_of_LinphoneSubscriptionTerminated++; + break; + case LinphoneSubscriptionError: + counters->number_of_LinphoneSubscriptionError++; + break; + } +} + + + + + +static void subscribe_test() { + LinphoneCoreManager* marie = linphone_core_manager_new(liblinphone_tester_file_prefix, "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new(liblinphone_tester_file_prefix, "pauline_rc"); + LinphoneContent content; + MSList* lcs=ms_list_append(NULL,marie->lc); + lcs=ms_list_append(lcs,pauline->lc); + + content.type="application"; + content.subtype="somexml"; + content.data=(char*)subscribe_content; + content.size=strlen(subscribe_content); + + linphone_core_subscribe(marie->lc,pauline->identity,"dodo",600,&content); + + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneSubscriptionOutgoingInit,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneSubscriptionIncomingReceived,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneSubscriptionActive,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneSubscriptionActive,1,1000)); + + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +test_t subscribe_tests[] = { + { "Subscribe", subscribe_test }, +}; + +test_suite_t subscribe_test_suite = { + "Subscribe", + NULL, + NULL, + sizeof(subscribe_tests) / sizeof(subscribe_tests[0]), + subscribe_tests +}; + diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 40cf3df09..f71b29345 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -192,6 +192,8 @@ LinphoneCoreManager* linphone_core_manager_new2(const char* path, const char* rc mgr->v_table.notify_presence_recv=notify_presence_received; mgr->v_table.transfer_state_changed=linphone_transfer_state_changed; mgr->v_table.info_received=info_message_received; + mgr->v_table.subscription_state_changed=linphone_subscription_state_change; + mgr->v_table.notify_received=linphone_notify_received; mgr->lc=configure_lc_from(&mgr->v_table, path, rc_file, check_for_proxies?(rc_file?1:0):0); enable_codec(mgr->lc,"PCMU",8000); linphone_core_set_user_data(mgr->lc,&mgr->stat); @@ -297,6 +299,7 @@ void liblinphone_tester_init(void) { #ifdef UPNP add_test_suite(&upnp_test_suite); #endif + add_test_suite(&subscribe_test_suite); } void liblinphone_tester_uninit(void) { diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 7aa3cebb9..9e8eb6f24 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -51,6 +51,7 @@ extern test_suite_t call_test_suite; extern test_suite_t message_test_suite; extern test_suite_t presence_test_suite; extern test_suite_t upnp_test_suite; +extern test_suite_t subscribe_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -137,6 +138,12 @@ typedef struct _stats { int number_of_inforeceived; int number_of_inforeceived_with_body; + int number_of_LinphoneSubscriptionIncomingReceived; + int number_of_LinphoneSubscriptionOutgoingInit; + int number_of_LinphoneSubscriptionPending; + int number_of_LinphoneSubscriptionActive; + int number_of_LinphoneSubscriptionTerminated; + int number_of_LinphoneSubscriptionError; }stats; @@ -162,6 +169,8 @@ void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMess void info_message_received(LinphoneCore *lc, const LinphoneInfoMessage *msg); void new_subscribtion_request(LinphoneCore *lc, LinphoneFriend *lf, const char *url); void auth_info_requested(LinphoneCore *lc, const char *realm, const char *username); +void linphone_subscription_state_change(LinphoneCore *lc, LinphoneEvent *ev, LinphoneSubscriptionState state); +void linphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *eventname, const LinphoneContent *content); LinphoneCore* create_lc_with_auth(unsigned int with_auth) ; LinphoneAddress * create_linphone_address(const char * domain);