From 510f3b7200039ced8023d37e9802ebe9c45d7556 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Thu, 13 Dec 2012 18:05:16 +0100 Subject: [PATCH] implement presence --- .cproject | 2 +- coreapi/Makefile.am | 3 +- coreapi/bellesip_sal/sal_impl.c | 28 +- coreapi/bellesip_sal/sal_impl.h | 6 + coreapi/bellesip_sal/sal_op_impl.c | 103 ++-- coreapi/bellesip_sal/sal_op_presence.c | 628 +++++++++++++++++++++ coreapi/bellesip_sal/sal_op_registration.c | 8 +- coreapi/friend.c | 3 +- coreapi/sal.c | 15 + coreapi/sal.h | 2 + tester/liblinphone_tester.c | 58 +- tester/marie_rc | 5 + tester/pauline_rc | 5 + 13 files changed, 817 insertions(+), 49 deletions(-) create mode 100644 coreapi/bellesip_sal/sal_op_presence.c diff --git a/.cproject b/.cproject index c44aac2bb..83757a0eb 100644 --- a/.cproject +++ b/.cproject @@ -59,7 +59,7 @@ - + diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 61ee670a0..3e56f1b48 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -46,7 +46,8 @@ liblinphone_la_SOURCES+= bellesip_sal/sal_address_impl.c \ bellesip_sal/sal_op_call.c \ bellesip_sal/sal_op_registration.c \ bellesip_sal/sal_sdp.c \ - bellesip_sal/sal_op_message.c + bellesip_sal/sal_op_message.c \ + bellesip_sal/sal_op_presence.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 855596610..b9cdd0b61 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -35,12 +35,28 @@ static void sal_add_pending_auth(Sal *sal, SalOp *op){ } void sal_process_authentication(SalOp *op, belle_sip_response_t *response) { - belle_sip_message_remove_header(BELLE_SIP_MESSAGE(op->request),BELLE_SIP_AUTHORIZATION); - belle_sip_message_remove_header(BELLE_SIP_MESSAGE(op->request),BELLE_SIP_PROXY_AUTHORIZATION); - if (belle_sip_provider_add_authorization(op->base.root->prov,op->request,response)) { - sal_op_resend_request(op,op->request); + belle_sip_request_t* request; + bool_t is_within_dialog=FALSE; + if (op->dialog && belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_CONFIRMED) { + request = belle_sip_dialog_create_request_from(op->dialog,(const belle_sip_request_t *)op->request); + is_within_dialog=TRUE; + } else { + request=op->request; + belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION); + belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION); + + } + if (belle_sip_provider_add_authorization(op->base.root->prov,request,response)) { + if (is_within_dialog) { + sal_op_resend_request(op,request); + } else { + sal_op_send_request(op,request); + } }else { ms_message("No auth info found for [%s]",sal_op_get_from(op)); + if (is_within_dialog) { + belle_sip_object_unref(request); + } sal_add_pending_auth(op->base.root,op); } @@ -76,6 +92,10 @@ 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) { + op=sal_op_new((Sal*)sal); + op->dir=SalOpDirIncoming; + sal_op_presence_fill_cbs(op); } else if (strcmp("MESSAGE",belle_sip_request_get_method(req))==0) { content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t); if (content_type diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h index 069b3934a..1b036cc92 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -66,6 +66,7 @@ struct SalOp{ bool_t supports_session_timers; SalOpSate_t state; SalOpDir_t dir; + belle_sip_refresher_t* refresher; }; belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescription *sal); @@ -80,4 +81,9 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request); void sal_op_resend_request(SalOp* op, belle_sip_request_t* request); void sal_process_authentication(SalOp *op, belle_sip_response_t *response); +bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size); + +/*presence*/ +void sal_op_presence_fill_cbs(SalOp*op); + #endif /* SAL_IMPL_H_ */ diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 5cce9bb1a..a79d179ce 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -88,40 +88,6 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) { - -/*presence Subscribe/notify*/ -int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ - ms_fatal("sal_subscribe_presence not implemented yet"); - return -1; -} -int sal_unsubscribe(SalOp *op){ - ms_fatal("sal_unsubscribe not implemented yet"); - return -1; -} -int sal_subscribe_accept(SalOp *op){ - ms_fatal("sal_subscribe_accept not implemented yet"); - return -1; -} -int sal_subscribe_decline(SalOp *op){ - ms_fatal("sal_subscribe_decline not implemented yet"); - return -1; -} -int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message){ - ms_fatal("sal_notify_presence not implemented yet"); - return -1; -} -int sal_notify_close(SalOp *op){ - ms_fatal("sal_notify_close not implemented yet"); - return -1; -} - -/*presence publish */ -int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus status){ - ms_fatal("sal_publish not implemented yet"); - return -1; -} - - /*ping: main purpose is to obtain its own contact address behind firewalls*/ int sal_ping(SalOp *op, const char *from, const char *to){ ms_fatal("sal_ping not implemented yet"); @@ -141,6 +107,11 @@ void sal_op_resend_request(SalOp* op, belle_sip_request_t* request) { belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); sal_op_send_request(op,request); } +static bool_t is_request_creating_dialog(belle_sip_request_t* request) { + return strcmp("INVITE",belle_sip_request_get_method(request))==0 + || + strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0; +} int sal_op_send_request(SalOp* op, belle_sip_request_t* request) { belle_sip_client_transaction_t* client_transaction; belle_sip_provider_t* prov=op->base.root->prov; @@ -155,7 +126,7 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request) { client_transaction = belle_sip_provider_create_client_transaction(prov,request); belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),op); /*in case DIALOG is in state NULL create a new dialog*/ - if (!op->dialog && strcmp("INVITE",belle_sip_request_get_method(request))==0) { + if (!op->dialog && is_request_creating_dialog(request)) { op->dialog=belle_sip_provider_create_dialog(prov,BELLE_SIP_TRANSACTION(client_transaction)); op->pending_inv_client_trans=client_transaction; /*update pending inv for being able to cancel*/ belle_sip_dialog_set_application_data(op->dialog,op); @@ -167,3 +138,65 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request) { return belle_sip_client_transaction_send_request(client_transaction); } +/*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) { + int code = belle_sip_response_get_status_code(response); + belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason"); + *sal_err=SalErrorUnknown; + *sal_reason = SalReasonUnknown; + + if (reason_header){ + snprintf(reason + ,reason_size + ,"%s %s" + ,belle_sip_response_get_reason_phrase(response) + ,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header))); + } else { + strncpy(reason,belle_sip_response_get_reason_phrase(response),reason_size); + } + if (code >=400) { + switch(code) { + case 400: + *sal_err=SalErrorUnknown; + 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; + default: + if (code>0){ + *sal_err=SalErrorFailure; + *sal_reason=SalReasonUnknown; + }else *sal_err=SalErrorNoResponse; + /* no break */ + } + return TRUE; + } else { + return FALSE; + } +} diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c new file mode 100644 index 000000000..6cb1762b6 --- /dev/null +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -0,0 +1,628 @@ +/* +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" + +typedef enum { + PIDF = 0, + RFCxxxx = 1, + MSOLDPRES = 2 +} presence_type_t; + +/* + * REVISIT: this static variable forces every dialog to use the same presence description type depending + * on what is received on a single dialog... + */ +static presence_type_t presence_style = PIDF; + +static void mk_presence_body (const SalPresenceStatus online_status, const char *contact_info, + char *buf, size_t buflen, presence_type_t ptype) { + switch (ptype) { + case RFCxxxx: { + /* definition from http://msdn.microsoft.com/en-us/library/cc246202%28PROT.10%29.aspx */ + int atom_id = 1000; + + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + } + break; + } + case MSOLDPRES: { + /* Couldn't find schema http://schemas.microsoft.com/2002/09/sip/presence + * so messages format has been taken from Communigate that can send notify + * requests with this schema + */ + int atom_id = 1000; + + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + } + break; + } + default: { /* use pidf+xml as default format, rfc4479, rfc4480, rfc3863 */ + + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +Out to lunch \n\ +\n\ +", +contact_info, contact_info); + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +closed\n\ +%s\n\ +\n\ +\n", contact_info, contact_info); + } + break; + } + } // switch + +} + +static void add_presence_info(belle_sip_message_t *notify, SalPresenceStatus online_status) { + char buf[1000]; + char *contact_info; + size_t content_length; + + belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(notify,belle_sip_header_from_t); + + contact_info=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from))); + mk_presence_body (online_status, contact_info, buf, sizeof (buf), presence_style); + + + + belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) + ,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application",presence_style?"xpidf+xml":"pidf+xml"))); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) + ,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(buf)))); + belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),buf,content_length); + ms_free(contact_info); +} + +static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ + ms_error("presence_process_io_error not implemented yet"); +} +static void presence_process_dialog_terminated(void *op, const belle_sip_dialog_terminated_event_t *event) { + if (((SalOp*)op)->dialog) ((SalOp*)op)->dialog=NULL; +} + +static void presence_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; + 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); + 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("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); + } + if (expires>0){ + op->refresher=belle_sip_client_transaction_create_refresher(client_transaction); + } + } + 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("op [%p] receive answer [%i] not implemented",op,code); + } + /* no break */ + } + + +} +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_error("presence_process_timeout 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)); + 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);; + const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); + SalPresenceStatus estatus=SalPresenceOffline; + SalSubscribeState sub_state; + belle_sip_response_t* resp; + 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; + + 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); + 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,sal_op_get_from(op)); + 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 (body==NULL){ + ms_error("No body in NOTIFY received from [%s]",sal_op_get_from(op)); + return; + } + if (strstr(body,"pending")!=NULL){ + estatus=SalPresenceOffline; + }else if (strstr(body,"busy")!=NULL){ + estatus=SalPresenceBusy; + }else if (strstr(body,"berightback")!=NULL + || strstr(body,"in-transit")!=NULL ){ + estatus=SalPresenceBerightback; + }else if (strstr(body,"away")!=NULL + || strstr(body,"idle")){ + estatus=SalPresenceAway; + }else if (strstr(body,"onthephone")!=NULL + || strstr(body,"on-the-phone")!=NULL){ + estatus=SalPresenceOnthephone; + }else if (strstr(body,"outtolunch")!=NULL + || strstr(body,"meal")!=NULL){ + estatus=SalPresenceOuttolunch; + }else if (strstr(body,"closed")!=NULL){ + estatus=SalPresenceOffline; + }else if ((strstr(body,"online")!=NULL) || (strstr(body,"open")!=NULL)) { + estatus=SalPresenceOnline; + }else{ + estatus=SalPresenceOffline; + } + ms_message("We are notified that [%s] has online status %i",sal_op_get_from(op),estatus); + 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("And outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); + } else + sub_state=SalSubscribeActive; + + op->base.root->callbacks.notify_presence(op,sub_state, estatus,NULL); + resp=belle_sip_response_create_from_request(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) { + op->base.root->callbacks.subscribe_received(op,sal_op_get_from(op)); + } else if(expires) { + ms_message("Unsubscribe received from [%s]",sal_op_get_from(op)); + resp=belle_sip_response_create_from_request(req,200); + belle_sip_server_transaction_send_response(server_transaction,resp); + } + } + break; + default: { + ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); + } + /* 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; + op->callbacks.process_timeout=presence_process_timeout; + op->callbacks.process_transaction_terminated=presence_process_transaction_terminated; + op->callbacks.process_request_event=presence_process_request_event; + op->callbacks.process_dialog_terminated=presence_process_dialog_terminated; +} + +/*presence publish */ +int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus status){ + ms_fatal("sal_publish not implemented yet"); + return -1; +} +/*presence Subscribe/notify*/ +int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ + belle_sip_request_t *req=NULL; + if (from) + sal_op_set_from(op,from); + if (to) + sal_op_set_to(op,to); + + sal_op_presence_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","Presence")); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(600))); + + return sal_op_send_request(op,req); +} +int sal_unsubscribe(SalOp *op){ + belle_sip_request_t* req=belle_sip_dialog_create_request(op->dialog,"SUBSCRIBE"); + 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 = belle_sip_response_create_from_request(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; +} +int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message){ + belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); +/* belle_sip_header_address_t* identity=sal_op_get_contact_address(op); + if (identity==NULL) identity=sal_op_get_to_address(op); + _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(msg,identity);*/ + add_presence_info(BELLE_SIP_MESSAGE(notify),status); /*FIXME, what about expires ??*/ + belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) + ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600))); + return sal_op_send_request(op,notify); +} +int sal_notify_close(SalOp *op){ + belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); + add_presence_info(BELLE_SIP_MESSAGE(notify),SalPresenceOffline); /*FIXME, what about expires ??*/ + 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_registration.c b/coreapi/bellesip_sal/sal_op_registration.c index ae7a6e116..7ad1c404e 100644 --- a/coreapi/bellesip_sal/sal_op_registration.c +++ b/coreapi/bellesip_sal/sal_op_registration.c @@ -30,11 +30,7 @@ static void register_refresh(SalOp* op) { belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); sal_op_send_request(op,op->request); } -static bool_t is_contact_equal(belle_sip_header_contact_t* a,belle_sip_header_contact_t* b) { - if (!a | !b) return FALSE; - return !belle_sip_uri_equals(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(a)) - ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(b))); -} + static void register_response_event(void *user_ctx, const belle_sip_response_event_t *event){ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); @@ -52,7 +48,7 @@ static void register_response_event(void *user_ctx, const belle_sip_response_eve contact_header_list = belle_sip_message_get_headers(BELLE_SIP_MESSAGE(response),BELLE_SIP_CONTACT); if (contact_header_list) { - contact_header_list = belle_sip_list_find_custom((belle_sip_list_t*)contact_header_list,(belle_sip_compare_func)is_contact_equal, (const void*)sal_op_get_contact_address(op)); + contact_header_list = belle_sip_list_find_custom((belle_sip_list_t*)contact_header_list,(belle_sip_compare_func)belle_sip_header_contact_equals, (const void*)sal_op_get_contact_address(op)); if (!contact_header_list) { ms_error("no matching contact for [%s]", sal_op_get_contact(op)); } else { diff --git a/coreapi/friend.c b/coreapi/friend.c index d71dd25c6..7c89991ba 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -338,8 +338,8 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ ms_warning("No sip url defined."); return; } - fr->lc=lc; + linphone_core_write_friends_config(lc); if (fr->inc_subscribe_pending){ @@ -390,6 +390,7 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) return ; } lc->friends=ms_list_append(lc->friends,lf); + lf->lc=lc; if ( linphone_core_ready(lc)) linphone_friend_apply(lf,lc); else lf->commit=TRUE; return ; diff --git a/coreapi/sal.c b/coreapi/sal.c index 90fa26fab..461f54dfa 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -414,3 +414,18 @@ const char* sal_stream_dir_to_string(SalStreamDir type) { } } + +const char* sal_reason_to_string(const SalReason reason) { + switch (reason) { + case SalReasonDeclined : return "SalReasonDeclined"; + case SalReasonBusy: return "SalReasonBusy"; + case SalReasonRedirect: return "SalReasonRedirect"; + case SalReasonTemporarilyUnavailable: return "SalReasonTemporarilyUnavailable"; + case SalReasonNotFound: return "SalReasonNotFound"; + case SalReasonDoNotDisturb: return "SalReasonDoNotDisturb"; + case SalReasonMedia: return "SalReasonMedia"; + case SalReasonForbidden: return "SalReasonForbidden"; + case SalReasonUnknown: return "SalReasonUnknown"; + default: return "Unkown reason"; + } +} diff --git a/coreapi/sal.h b/coreapi/sal.h index 543d7e9f2..8e38c39f6 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -201,6 +201,8 @@ typedef enum SalReason{ SalReasonUnknown }SalReason; +const char* sal_reason_to_string(const SalReason reason); + typedef enum SalPresenceStatus{ SalPresenceOffline, SalPresenceOnline, diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 23c50f88b..736c1f380 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -84,6 +84,9 @@ typedef struct _stats { int number_of_LinphoneCallReleased; int number_of_LinphoneMessageReceived; + + int number_of_NewSubscriptionRequest; + int number_of_NotifyReceived; }stats; static stats global_stat; @@ -296,7 +299,6 @@ static void call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCal } static void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from_address, const char *message) { - char* from=linphone_address_as_string(from_address); ms_message("Message from [%s] is [%s]",from,message); ms_free(from); @@ -304,6 +306,21 @@ static void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, cons counters->number_of_LinphoneMessageReceived++; } +void new_subscribtion_request(LinphoneCore *lc, LinphoneFriend *lf, const char *url){ + char* from=linphone_address_as_string(linphone_friend_get_address(lf)); + ms_message("New subscription request from [%s] url [%s]",from,url); + ms_free(from); + stats* counters = (stats*)linphone_core_get_user_data(lc); + counters->number_of_NewSubscriptionRequest++; + +} +static void notify_presence_received(LinphoneCore *lc, LinphoneFriend * lf) { + char* from=linphone_address_as_string(linphone_friend_get_address(lf)); + ms_message("New Notify request from [%s] ",from); + ms_free(from); + stats* counters = (stats*)linphone_core_get_user_data(lc); + counters->number_of_NotifyReceived++; +} static bool_t wait_for(LinphoneCore* lc_1, LinphoneCore* lc_2,int* counter,int value) { int retry=0; while (*counterv_table.registration_state_changed=registration_state_changed; mgr->v_table.call_state_changed=call_state_changed; mgr->v_table.text_received=text_message_received; + mgr->v_table.new_subscription_request=new_subscribtion_request; + mgr->v_table.notify_presence_recv=notify_presence_received; mgr->lc=configure_lc_from(&mgr->v_table,rc_file,1); enable_codec(mgr->lc,"PCMU",8000); linphone_core_set_user_data(mgr->lc,&mgr->stat); @@ -551,6 +570,37 @@ static void text_message() { linphone_core_manager_destroy(pauline); } +static void simple_publish() { + LinphoneCoreManager* marie = linphone_core_manager_new("./tester/marie_rc"); + LinphoneProxyConfig* proxy; + linphone_core_get_default_proxy(marie->lc,&proxy); + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_done(proxy); + linphone_core_iterate(marie->lc); + linphone_core_manager_destroy(marie); +} + + +static void simple_subscribe() { + LinphoneCoreManager* marie = linphone_core_manager_new("./tester/marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("./tester/pauline_rc"); + const MSList* marie_friends = linphone_core_get_friend_list(marie->lc); + CU_ASSERT_PTR_NOT_NULL_FATAL(marie_friends); + LinphoneFriend* friend = (LinphoneFriend*) marie_friends->data; + linphone_friend_edit(friend); + linphone_friend_enable_subscribes(friend,TRUE); + linphone_friend_done(friend); + + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_NewSubscriptionRequest,1)); + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_NotifyReceived,1)); + + linphone_core_manager_destroy(marie); + CU_ASSERT_TRUE(wait_for(NULL,pauline->lc,&pauline->stat.number_of_NewSubscriptionRequest,2)); /*wait for unsubscribe*/ + + linphone_core_manager_destroy(pauline); +} + int init_test_suite () { CU_pSuite pSuite = CU_add_suite("liblinphone", init, uninit); @@ -604,6 +654,12 @@ CU_pSuite pSuite = CU_add_suite("liblinphone", init, uninit); if (NULL == CU_add_test(pSuite, "text_message", text_message)) { return CU_get_error(); } + if (NULL == CU_add_test(pSuite, "simple_subscribe", simple_subscribe)) { + return CU_get_error(); + } + if (NULL == CU_add_test(pSuite, "simple_publish", simple_publish)) { + return CU_get_error(); + } return 0; } int main (int argc, char *argv[]) { diff --git a/tester/marie_rc b/tester/marie_rc index 95c30a0e1..26cb42992 100644 --- a/tester/marie_rc +++ b/tester/marie_rc @@ -22,6 +22,11 @@ reg_sendregister=1 publish=0 dial_escape_plus=0 +[friend_0] +url="Paupoche" +pol=accept +subscribe=0 + [rtp] audio_rtp_port=8070 diff --git a/tester/pauline_rc b/tester/pauline_rc index 07e0efa25..f2da8991c 100644 --- a/tester/pauline_rc +++ b/tester/pauline_rc @@ -22,6 +22,11 @@ reg_sendregister=1 publish=0 dial_escape_plus=0 +#[friend_0] +#url="Mariette" +#pol=accept +#subscribe=0 + [rtp] audio_rtp_port=8090 video_rtp_port=8092