linphone-ios/coreapi/bellesip_sal/sal_op_call.c
2013-03-20 17:15:17 +01:00

704 lines
26 KiB
C

/*
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"
#include "offeranswer.h"
static void sdp_process(SalOp *h){
ms_message("Doing SDP offer/answer process of type %s",h->sdp_offering ? "outgoing" : "incoming");
if (h->result){
sal_media_description_unref(h->result);
}
h->result=sal_media_description_new();
if (h->sdp_offering){
offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result);
}else{
int i;
if (h->sdp_answer){
belle_sip_object_unref(h->sdp_answer);
}
offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec);
h->sdp_answer=(belle_sdp_session_description_t *)belle_sip_object_ref(media_description_to_sdp(h->result));
/*once we have generated the SDP answer, we modify the result description for processing by the upper layer.
It should contains media parameters constraint from the remote offer, not our response*/
strcpy(h->result->addr,h->base.remote_media->addr);
h->result->bandwidth=h->base.remote_media->bandwidth;
for(i=0;i<h->result->n_active_streams;++i){
/*fixme add rtcp*/
strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr);
h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime;
h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth;
h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port;
if (h->result->streams[i].proto == SalProtoRtpSavp) {
h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0];
}
}
}
}
static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) {
belle_sip_header_content_type_t* content_type ;
belle_sip_header_content_length_t* content_length;
int length;
char buff[1024];
if (session_desc) {
content_type = belle_sip_header_content_type_create("application","sdp");
length = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,0,sizeof(buff));
if (length==sizeof(buff)) {
ms_error("Buffer too small or sdp too big");
}
content_length= belle_sip_header_content_length_create(length);
belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type));
belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length));
belle_sip_message_set_body(msg,buff,length);
return 0;
} else {
return -1;
}
}
static int set_sdp_from_desc(belle_sip_message_t *msg, const SalMediaDescription *desc){
return set_sdp(msg,media_description_to_sdp(desc));
}
static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
SalOp* op=(SalOp*)user_ctx;
if (!op->dialog) {
/*call terminated very early*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Service Unavailable",503);
op->state=SalOpStateTerminated;
} else {
/*dialog will terminated shortly, nothing to do*/
}
}
static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
SalOp* op=(SalOp*)ctx;
if (op->dialog) {
if (belle_sip_dialog_get_previous_state(op->dialog) == BELLE_SIP_DIALOG_CONFIRMED) {
/*this is probably a "normal termination from a BYE*/
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
op->state=SalOpStateTerminating;
} else {
/*let the process response handle this case*/
}
belle_sip_object_unref(op->dialog);
op->dialog=NULL;
}
}
static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
belle_sdp_session_description_t* sdp;
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(response)))) {
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
if (op->base.local_media) sdp_process(op);
}
}
static void cancelling_invite(SalOp* op ){
belle_sip_request_t* cancel;
ms_message("Cancelling INVITE requets from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op));
cancel = belle_sip_client_transaction_create_cancel(op->pending_inv_client_trans);
sal_op_send_request(op,cancel);
op->state=SalOpStateTerminated;
}
static void call_response_event(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
belle_sip_request_t* ack;
belle_sip_dialog_state_t dialog_state;
belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
belle_sip_request_t* req;
belle_sip_response_t* response=belle_sip_response_event_get_response(event);
int code = belle_sip_response_get_status_code(response);
char* reason;
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
if (!client_transaction) {
ms_warning("Discarding state less response [%i] on op [%p]",code,op);
return;
}
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
reason=(char*)belle_sip_response_get_reason_phrase(response);
if (reason_header){
reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header)));
}
/*FIXME should be limited to early/NULL dialog*/
if (code >=400) {
sal_compute_sal_errors_from_code(code,&error,&sr);
op->base.root->callbacks.call_failure(op,error,sr,reason,code);
op->state=SalOpStateTerminated;
if (reason_header != NULL){
ms_free(reason);
}
return;
}
set_or_update_dialog(op,belle_sip_response_event_get_dialog(event));
/*check if op is terminating*/
if (op->state == SalOpStateTerminating) {
if( strcmp("INVITE",belle_sip_request_get_method(req))==0
&& (!op->dialog
|| belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_NULL
|| belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_EARLY)) {
/*FIXME if DIALOG_CONFIRM then ACK+BYE*/
cancelling_invite(op);
return;
} else if (strcmp("BYE",belle_sip_request_get_method(req))==0) {
return; /*probably a 200ok for a bye*/
}
}
if (!op->dialog) {
ms_message("call 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: {
ms_error("call op [%p] receive an unexpected answer [%i]",op,code);
break;
}
case BELLE_SIP_DIALOG_EARLY: {
if (code >= 180) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
} else {
ms_error("call op [%p] receive an unexpected answer [%i]",op,code);
}
break;
}
case BELLE_SIP_DIALOG_CONFIRMED: {
switch (op->state) {
case SalOpStateEarly:/*invite case*/
case SalOpStateActive: /*re-invite case*/
if (code >=200
&& code<300
&& strcmp("INVITE",belle_sip_request_get_method(req))==0) {
handle_sdp_from_response(op,response);
ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog));
if (ack==NULL) {
ms_error("This call has been already terminated.");
return ;
}
if (op->sdp_answer){
set_sdp(BELLE_SIP_MESSAGE(response),op->sdp_answer);
op->sdp_answer=NULL;
}
belle_sip_dialog_send_ack(op->dialog,ack);
op->base.root->callbacks.call_accepted(op); /*INVITE*/
op->state=SalOpStateActive;
} else {
/*nop*/
}
break;
case SalOpStateTerminated:
default:
ms_error("call op [%p] receive answer [%i] not implemented",op,code);
}
break;
}
case BELLE_SIP_DIALOG_TERMINATED:
default: {
ms_error("call op [%p] receive answer [%i] not implemented",op,code);
}
/* no break */
}
}
static void call_set_released(SalOp* op){
op->base.root->callbacks.call_released(op);
}
static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
SalOp* op=(SalOp*)user_ctx;
if (!op->dialog) {
/*call terminated very early*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Request Timeout",408);
op->state=SalOpStateTerminated;
} else {
/*dialog will terminated shortly, nothing to do*/
}
}
static void call_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
SalOp* op = (SalOp*)user_ctx;
belle_sip_client_transaction_t *client_transaction=belle_sip_transaction_terminated_event_get_client_transaction(event);
belle_sip_server_transaction_t *server_transaction=belle_sip_transaction_terminated_event_get_server_transaction(event);
belle_sip_request_t* req;
belle_sip_response_t* resp;
if (client_transaction) {
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(client_transaction));
} else {
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction));
resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction));
}
if (strcmp("BYE",belle_sip_request_get_method(req))==0
&& (!resp || (belle_sip_response_get_status_code(resp) !=401
&& belle_sip_response_get_status_code(resp) !=407))) {
call_set_released(op);
}
}
static void call_terminated(SalOp* op,belle_sip_server_transaction_t* server_transaction, belle_sip_request_t* request,int status_code) {
belle_sip_response_t* resp;
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
resp=belle_sip_response_create_from_request(request,status_code);
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}
static void unsupported_method(belle_sip_server_transaction_t* server_transaction,belle_sip_request_t* request) {
belle_sip_response_t* resp;
resp=belle_sip_response_create_from_request(request,500);
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}
static void process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) {
belle_sdp_session_description_t* sdp;
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(invite)))) {
op->sdp_offering=FALSE;
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
belle_sip_object_unref(sdp);
}else
op->sdp_offering=TRUE;
}
static void 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_sdp_session_description_t* sdp;
belle_sip_request_t* req = belle_sip_request_event_get_request(event);
belle_sip_dialog_state_t dialog_state;
belle_sip_response_t* resp;
belle_sip_header_t* call_info;
if (server_transaction) belle_sip_object_ref(server_transaction); /*ACK does'nt create srv transaction*/
if (strcmp("INVITE",belle_sip_request_get_method(req))==0) {
if (op->pending_server_trans)belle_sip_object_unref(op->pending_server_trans);
/*updating pending invite transaction*/
op->pending_server_trans=server_transaction;
}
if (!op->dialog) {
set_or_update_dialog(op,belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(op->pending_server_trans)));
ms_message("new incoming call 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: {
if (strcmp("INVITE",belle_sip_request_get_method(req))==0) {
if (!op->replaces && (op->replaces=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_replaces_t))) {
belle_sip_object_ref(op->replaces);
} else if(op->replaces) {
ms_warning("replace header already set");
}
process_sdp_for_invite(op,req);
if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) {
if( strstr(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(call_info)),"answer-after=") != NULL) {
op->auto_answer_asked=TRUE;
ms_message("The caller asked to automatically answer the call(Emergency?)\n");
}
}
op->base.root->callbacks.call_received(op);
break;
} /* else same behavior as for EARLY state*/
}
case BELLE_SIP_DIALOG_EARLY: {
//hmm probably a cancel
if (strcmp("CANCEL",belle_sip_request_get_method(req))==0) {
if(belle_sip_request_event_get_server_transaction(event)) {
/*first answer 200 ok to cancel*/
belle_sip_server_transaction_send_response(server_transaction
,belle_sip_response_create_from_request(req,200));
/*terminate invite transaction*/
call_terminated(op
,op->pending_server_trans
,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),487);
} else {
/*call leg does not exist*/
belle_sip_server_transaction_send_response(server_transaction
,belle_sip_response_create_from_request(req,481));
}
} else if (strcmp("PRACK",belle_sip_request_get_method(req))==0) {
resp=belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
} else {
belle_sip_error("Unexpected method [%s] for dialog state BELLE_SIP_DIALOG_EARLY");
unsupported_method(server_transaction,req);
}
break;
}
case BELLE_SIP_DIALOG_CONFIRMED:
/*great ACK received*/
if (strcmp("ACK",belle_sip_request_get_method(req))==0) {
if (op->sdp_offering){
if ((sdp=belle_sdp_session_description_create(BELLE_SIP_MESSAGE(req)))){
if (op->base.remote_media)
sal_media_description_unref(op->base.remote_media);
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_process(op);
belle_sip_object_unref(sdp);
}
}
/*FIXME
if (op->reinvite){
op->reinvite=FALSE;
}*/
op->base.root->callbacks.call_ack(op);
} else if(strcmp("BYE",belle_sip_request_get_method(req))==0) {
resp=belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
/*call end is notified by dialog deletion*/
} else if(strcmp("INVITE",belle_sip_request_get_method(req))==0) {
/*re-invite*/
if (op->base.remote_media){
sal_media_description_unref(op->base.remote_media);
op->base.remote_media=NULL;
}
if (op->result){
sal_media_description_unref(op->result);
op->result=NULL;
}
process_sdp_for_invite(op,req);
op->base.root->callbacks.call_updating(op);
} else if (strcmp("INFO",belle_sip_request_get_method(req))==0
&& belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))
&& strstr(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)),"picture_fast_update")) {
/*vfu request*/
ms_message("Receiving VFU request on op [%p]",op);
if (op->base.root->callbacks.vfu_request){
op->base.root->callbacks.vfu_request(op);
}
resp=belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
}else if (strcmp("REFER",belle_sip_request_get_method(req))==0) {
sal_op_process_refer(op,event);
} else if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) {
sal_op_call_process_notify(op,event);
} else if (strcmp("OPTIONS",belle_sip_request_get_method(req))==0) {
resp=belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
} else{
ms_error("unexpected method [%s] for dialog [%p]",belle_sip_request_get_method(req),op->dialog);
unsupported_method(server_transaction,req);
}
break;
default: {
ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
}
/* no break */
}
}
/*Call API*/
int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){
if (desc)
sal_media_description_ref(desc);
if (op->base.local_media)
sal_media_description_unref(op->base.local_media);
op->base.local_media=desc;
return 0;
}
static void sal_op_fill_invite(SalOp *op, belle_sip_request_t* invite) {
belle_sip_header_allow_t* header_allow;
header_allow = belle_sip_header_allow_create("INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(header_allow));
if (op->base.root->session_expires!=0){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "200"));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Supported", "timer"));
}
if (op->base.local_media){
op->sdp_offering=TRUE;
set_sdp_from_desc(BELLE_SIP_MESSAGE(invite),op->base.local_media);
}else op->sdp_offering=FALSE;
return;
}
int sal_call(SalOp *op, const char *from, const char *to){
belle_sip_request_t* invite;
op->dir=SalOpDirOutgoing;
sal_op_set_from(op,from);
sal_op_set_to(op,to);
ms_message("[%s] calling [%s] on op [%p]", from, to, op);
invite=sal_op_build_request(op,"INVITE");
sal_op_fill_invite(op,invite);
sal_op_call_fill_cbs(op);
if (op->replaces){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->replaces));
if (op->referred_by)
belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(op->referred_by));
}
return sal_op_send_request(op,invite);
}
void sal_op_call_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=call_process_io_error;
op->callbacks.process_response_event=call_response_event;
op->callbacks.process_timeout=call_process_timeout;
op->callbacks.process_transaction_terminated=call_process_transaction_terminated;
op->callbacks.process_request_event=process_request_event;
op->callbacks.process_dialog_terminated=process_dialog_terminated;
}
static void handle_offer_answer_response(SalOp* op, belle_sip_response_t* response) {
if (op->base.local_media){
/*this is the case where we received an invite without SDP*/
if (op->sdp_offering) {
set_sdp_from_desc(BELLE_SIP_MESSAGE(response),op->base.local_media);
}else{
if (op->sdp_answer==NULL) sdp_process(op);
if (op->sdp_answer){
set_sdp(BELLE_SIP_MESSAGE(response),op->sdp_answer);
op->sdp_answer=NULL;
}
}
}else{
ms_error("You are accepting a call but not defined any media capabilities !");
}
}
int sal_call_notify_ringing(SalOp *op, bool_t early_media){
int status_code =early_media?183:180;
belle_sip_request_t* req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans));
belle_sip_response_t* ringing_response = belle_sip_response_create_from_request(req,status_code);
if (early_media){
handle_offer_answer_response(op,ringing_response);
}
/*fixme it should support PRACK in the right way*/
if (belle_sip_message_get_header((belle_sip_message_t*)req,"Require")) {
belle_sip_header_address_t* contact= (belle_sip_header_address_t*)sal_op_get_contact_address(op);
belle_sip_header_contact_t* contact_header;
belle_sip_message_add_header((belle_sip_message_t*)ringing_response,belle_sip_message_get_header((belle_sip_message_t*)req,"Require"));
belle_sip_message_add_header((belle_sip_message_t*)ringing_response,BELLE_SIP_HEADER(belle_sip_header_extension_create("RSeq","1")));
if (contact && (contact_header=belle_sip_header_contact_create(contact))) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(ringing_response),BELLE_SIP_HEADER(contact_header));
}
}
belle_sip_server_transaction_send_response(op->pending_server_trans,ringing_response);
return 0;
}
/*accept an incoming call or, during a call accept a reINVITE*/
int sal_call_accept(SalOp*h){
belle_sip_response_t *response;
belle_sip_header_address_t* contact= (belle_sip_header_address_t*)sal_op_get_contact_address(h);
belle_sip_header_contact_t* contact_header;
if (!h->pending_server_trans) {
ms_error("No transaction to accept for op [%p]",h);
return -1;
}
/* sends a 200 OK */
response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(h->pending_server_trans)),200);
if (response==NULL){
ms_error("Fail to build answer for call");
return -1;
}
if (h->base.root->session_expires!=0){
if (h->supports_session_timers) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create( "Supported", "timer"));
}
}
if (contact && (contact_header=belle_sip_header_contact_create(contact))) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact_header));
}
handle_offer_answer_response(h,response);
belle_sip_server_transaction_send_response(h->pending_server_trans,response);
return 0;
}
int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*optional*/){
belle_sip_response_t* response;
belle_sip_header_contact_t* contact=NULL;
int status;
switch(reason) {
case SalReasonBusy:
status=486;
break;
case SalReasonTemporarilyUnavailable:
status=480;
break;
case SalReasonDoNotDisturb:
status=600;
break;
case SalReasonMedia:
status=415;
break;
case SalReasonDeclined:
status=603;
break;
case SalReasonRedirect:
if(redirection!=NULL) {
if (strstr(redirection,"sip:")!=0) status=302;
status=380;
contact= belle_sip_header_contact_new();
belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact),belle_sip_uri_parse(redirection));
break;
} else {
ms_error("Cannot redirect to null");
}
/* no break */
default:
status=500;
ms_error("Unexpected decline reason [%i]",reason);
/* no break */
}
response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),status);
if (contact) belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact));
belle_sip_server_transaction_send_response(op->pending_server_trans,response);
return 0;
}
int sal_call_update(SalOp *op, const char *subject){
belle_sip_request_t *reinvite=belle_sip_dialog_create_request(op->dialog,"INVITE");
belle_sip_message_add_header(BELLE_SIP_MESSAGE(reinvite),belle_sip_header_create( "Subject", subject));
sal_op_fill_invite(op, reinvite);
return sal_op_send_request(op,reinvite);
}
SalMediaDescription * sal_call_get_remote_media_description(SalOp *h){
return h->base.remote_media;;
}
SalMediaDescription * sal_call_get_final_media_description(SalOp *h){
if (h->base.local_media && h->base.remote_media && !h->result){
sdp_process(h);
}
return h->result;
}
int sal_call_send_dtmf(SalOp *h, char dtmf){
ms_fatal("sal_call_send_dtmf not implemented yet");
return -1;
}
int sal_call_terminate(SalOp *op){
belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; /*no dialog = dialog in NULL state*/
op->state=SalOpStateTerminating;
switch(dialog_state) {
case BELLE_SIP_DIALOG_CONFIRMED: {
sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE"));
op->state=SalOpStateTerminating;
break;
}
case BELLE_SIP_DIALOG_NULL: {
if (op->dir == SalOpDirIncoming) {
sal_call_decline(op, SalReasonDeclined,NULL);
op->state=SalOpStateTerminated;
} else if (op->pending_inv_client_trans
&& belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_inv_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){
cancelling_invite(op);
break;
} else {
ms_error("Don't know how to termination NUlL dialog for op [%p]",op);
}
break;
}
case BELLE_SIP_DIALOG_EARLY: {
if (op->dir == SalOpDirIncoming) {
sal_call_decline(op, SalReasonDeclined,NULL);
op->state=SalOpStateTerminated;
} else {
cancelling_invite(op);
}
break;
}
default: {
ms_fatal("sal_call_terminate not implemented yet for dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
return -1;
}
}
return 0;
}
bool_t sal_call_autoanswer_asked(SalOp *op){
return op->auto_answer_asked;
}
void sal_call_send_vfu_request(SalOp *op){
char info_body[] =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
"<media_control>"
" <vc_primitive>"
" <to_encoder>"
" <picture_fast_update></picture_fast_update>"
" </to_encoder>"
" </vc_primitive>"
"</media_control>";
size_t content_lenth = sizeof(info_body) - 1;
belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; /*no dialog = dialog in NULL state*/
if (dialog_state == BELLE_SIP_DIALOG_CONFIRMED) {
belle_sip_request_t* info = belle_sip_dialog_create_request(op->dialog,"INFO");
belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","media_control+xml")));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_lenth)));
belle_sip_message_set_body(BELLE_SIP_MESSAGE(info),info_body,content_lenth);
sal_op_send_request(op,info);
} else {
ms_warning("Cannot send vfu request to [%s] because dialog [%p] in wrong state [%s]",sal_op_get_to(op)
,op->dialog
,belle_sip_dialog_state_to_string(dialog_state));
}
return ;
}
int sal_call_is_offerer(const SalOp *h){
return h->sdp_offering;
}
void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled){
ms_warning("sal_expire_old_registration_contacts not implemented ");
}
void sal_use_dates(Sal *ctx, bool_t enabled){
ms_warning("sal_use_dates not implemented yet");
}