linphone-iphone/coreapi/sal/sal_op.cpp
2017-09-29 17:19:52 +02:00

1025 lines
38 KiB
C++

#include <cstring>
#include "sal_op.h"
#include "bellesip_sal/sal_impl.h"
using namespace std;
LINPHONE_BEGIN_NAMESPACE
SalOp::SalOp(Sal *sal) {
this->root = sal;
this->sdp_handling = sal->default_sdp_handling;
memset(&this->error_info, 0, sizeof(this->error_info));
memset(&this->reason_error_info, 0, sizeof(this->reason_error_info));
ref();
}
SalOp::~SalOp() {
ms_message("Destroying op [%p] of type [%s]",this,to_string(this->type));
if (this->pending_auth_transaction) belle_sip_object_unref(this->pending_auth_transaction);
this->root->remove_pending_auth(this);
if (this->auth_info) {
sal_auth_info_delete(this->auth_info);
}
if (this->sdp_answer) belle_sip_object_unref(this->sdp_answer);
if (this->refresher) {
belle_sip_object_unref(this->refresher);
this->refresher=NULL;
}
if (this->result)
sal_media_description_unref(this->result);
if(this->replaces) belle_sip_object_unref(this->replaces);
if(this->referred_by) belle_sip_object_unref(this->referred_by);
if (this->pending_client_trans) belle_sip_object_unref(this->pending_client_trans);
if (this->pending_server_trans) belle_sip_object_unref(this->pending_server_trans);
if (this->pending_update_server_trans) belle_sip_object_unref(this->pending_update_server_trans);
if (this->event) belle_sip_object_unref(this->event);
sal_error_info_reset(&this->error_info);
if (this->from_address){
sal_address_destroy(this->from_address);
this->from_address=NULL;
}
if (this->to_address){
sal_address_destroy(this->to_address);
this->to_address=NULL;
}
if (this->service_route){
sal_address_destroy(this->service_route);
this->service_route=NULL;
}
if (this->origin_address){
sal_address_destroy(this->origin_address);
this->origin_address=NULL;
}
if (this->from) {
ms_free(this->from);
this->from=NULL;
}
if (this->to) {
ms_free(this->to);
this->to=NULL;
}
if (this->route) {
ms_free(this->route);
this->route=NULL;
}
if (this->realm) {
ms_free(this->realm);
this->realm=NULL;
}
if (this->contact_address) {
sal_address_destroy(this->contact_address);
}
if (this->origin){
ms_free(this->origin);
this->origin=NULL;
}
if (this->remote_ua){
ms_free(this->remote_ua);
this->remote_ua=NULL;
}
if (this->remote_contact){
ms_free(this->remote_contact);
this->remote_contact=NULL;
}
if (this->remote_contact_address){
sal_address_destroy(this->remote_contact_address);
}
if (this->local_media)
sal_media_description_unref(this->local_media);
if (this->remote_media)
sal_media_description_unref(this->remote_media);
if (this->custom_body) {
sal_custom_body_unref(this->custom_body);
}
if (this->call_id)
ms_free((void *)this->call_id);
if (this->service_route) {
sal_address_destroy(this->service_route);
}
if (this->route_addresses){
bctbx_list_for_each(this->route_addresses,(void (*)(void*)) sal_address_destroy);
this->route_addresses=bctbx_list_free(this->route_addresses);
}
if (this->recv_custom_headers)
sal_custom_header_free(this->recv_custom_headers);
if (this->sent_custom_headers)
sal_custom_header_free(this->sent_custom_headers);
if (this->entity_tag != NULL){
ms_free(this->entity_tag);
this->entity_tag = NULL;
}
}
SalOp *SalOp::ref() {
_ref++;
return this;
}
void *SalOp::unref() {
_ref--;
if (_ref==0) {
delete this;
} else if (_ref<0) {
ms_fatal("SalOp [%p]: too many unrefs.",this);
}
return NULL;
}
void SalOp::set_contact_address(const SalAddress *address) {
if (this->contact_address) sal_address_destroy(this->contact_address);
this->contact_address=address?sal_address_clone(address):NULL;
}
void SalOp::assign_address(SalAddress** address, const char *value) {
if (*address){
sal_address_destroy(*address);
*address=NULL;
}
if (value)
*address=sal_address_new(value);
}
void SalOp::assign_string(char **str, const char *arg) {
if (*str){
ms_free(*str);
*str=NULL;
}
if (arg)
*str=ms_strdup(arg);
}
void SalOp::set_route(const char *route) {
char* route_string=NULL;
if (this->route_addresses) {
bctbx_list_for_each(this->route_addresses,(void (*)(void *))sal_address_destroy);
this->route_addresses=bctbx_list_free(this->route_addresses);
}
if (route) {
this->route_addresses=bctbx_list_append(NULL,NULL);
assign_address((SalAddress**)&(this->route_addresses->data),route);
route_string=sal_address_as_string((SalAddress*)this->route_addresses->data);
}
assign_string(&this->route,route_string);
if(route_string) ms_free(route_string);
}
void SalOp::set_route_address(const SalAddress *address){
char* address_string=sal_address_as_string(address); /*can probably be optimized*/
set_route(address_string);
ms_free(address_string);
}
void SalOp::add_route_address(const SalAddress *address) {
if (this->route_addresses) {
this->route_addresses=bctbx_list_append(this->route_addresses,(void*)sal_address_clone(address));
} else {
set_route_address(address);
}
}
void SalOp::set_realm(const char *realm) {
if (this->realm != NULL){
ms_free(this->realm);
}
this->realm = ms_strdup(realm);
}
#define SET_PARAM(name) \
char* name##_string=NULL; \
assign_address(&this->name##_address,name); \
if (this->name##_address) { \
name##_string=sal_address_as_string(this->name##_address); \
}\
assign_string(&this->name,name##_string); \
if(name##_string) ms_free(name##_string);
void SalOp::set_from(const char *from){
SET_PARAM(from);
}
void SalOp::set_from_address(const SalAddress *from) {
char* address_string=sal_address_as_string(from); /*can probably be optimized*/
set_from(address_string);
ms_free(address_string);
}
void SalOp::set_to(const char *to) {
SET_PARAM(to);
}
void SalOp::set_to_address(const SalAddress *to) {
char* address_string=sal_address_as_string(to); /*can probably be optimized*/
set_to(address_string);
ms_free(address_string);
}
void SalOp::set_diversion_address(const SalAddress *diversion) {
if (this->diversion_address) sal_address_destroy(this->diversion_address);
this->diversion_address=diversion ? sal_address_clone(diversion) : NULL;
}
int SalOp::refresh() {
if (this->refresher) {
belle_sip_refresher_refresh(this->refresher,belle_sip_refresher_get_expires(this->refresher));
return 0;
}
ms_warning("sal_refresh on op [%p] of type [%s] no refresher",this,to_string(this->type));
return -1;
}
void SalOp::kill_dialog() {
ms_warning("op [%p]: force kill of dialog [%p]", this, this->dialog);
belle_sip_dialog_delete(this->dialog);
}
void SalOp::release() {
/*if in terminating state, keep this state because it means we are waiting for a response to be able to terminate the operation.*/
if (this->state!=State::Terminating)
this->state=State::Terminated;
set_user_pointer(NULL);/*mandatory because releasing op doesn't not mean freeing op. Make sure back pointer will not be used later*/
if (this->release_cb)
this->release_cb(this);
if (this->refresher) {
belle_sip_refresher_stop(this->refresher);
}
this->op_released = TRUE;
unref();
}
int SalOp::send_request_with_contact(belle_sip_request_t* request, bool_t add_contact) {
belle_sip_client_transaction_t* client_transaction;
belle_sip_provider_t* prov=this->root->prov;
belle_sip_uri_t* outbound_proxy=NULL;
belle_sip_header_contact_t* contact;
int result =-1;
belle_sip_uri_t *next_hop_uri=NULL;
if (add_contact && !belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_contact_t)) {
contact = create_contact();
belle_sip_message_set_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(contact));
} /*keep existing*/
add_custom_headers((belle_sip_message_t*)request);
if (!this->dialog || belle_sip_dialog_get_state(this->dialog) == BELLE_SIP_DIALOG_NULL) {
/*don't put route header if dialog is in confirmed state*/
const MSList *elem=get_route_addresses();
const char *transport;
const char *method=belle_sip_request_get_method(request);
belle_sip_listening_point_t *udplp=belle_sip_provider_get_listening_point(prov,"UDP");
if (elem) {
outbound_proxy=belle_sip_header_address_get_uri((belle_sip_header_address_t*)elem->data);
next_hop_uri=outbound_proxy;
}else{
next_hop_uri=(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_request_get_uri(request));
}
transport=belle_sip_uri_get_transport_param(next_hop_uri);
if (transport==NULL){
/*compatibility mode: by default it should be udp as not explicitely set and if no udp listening point is available, then use
* the first available transport*/
if (!belle_sip_uri_is_secure(next_hop_uri)){
if (udplp==NULL){
if (belle_sip_provider_get_listening_point(prov,"TCP")!=NULL){
transport="tcp";
}else if (belle_sip_provider_get_listening_point(prov,"TLS")!=NULL ){
transport="tls";
}
}
if (transport){
belle_sip_message("Transport is not specified, using %s because UDP is not available.",transport);
belle_sip_uri_set_transport_param(next_hop_uri,transport);
}
}
}else{
#ifdef TUNNEL_ENABLED
if (udplp && BELLE_SIP_OBJECT_IS_INSTANCE_OF(udplp,belle_sip_tunnel_listening_point_t)){
/* our tunnel mode only supports UDP. Force transport to be set to UDP */
belle_sip_uri_set_transport_param(next_hop_uri,"udp");
}
#endif
}
/*because in case of tunnel, transport can be changed*/
transport=belle_sip_uri_get_transport_param(next_hop_uri);
if ((strcmp(method,"REGISTER")==0 || strcmp(method,"SUBSCRIBE")==0) && transport &&
(strcasecmp(transport,"TCP")==0 || strcasecmp(transport,"TLS")==0)){
/*RFC 5923: add 'alias' parameter to tell the server that we want it to keep the connection for future requests*/
belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_via_t);
belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(via),"alias",NULL);
}
}
client_transaction = belle_sip_provider_create_client_transaction(prov,request);
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),ref());
if (this->pending_client_trans) belle_sip_object_unref(this->pending_client_trans);
this->pending_client_trans=client_transaction; /*update pending inv for being able to cancel*/
belle_sip_object_ref(this->pending_client_trans);
if (belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_user_agent_t)==NULL)
belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(this->root->user_agent));
if (!belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION)
&& !belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION)) {
/*hmm just in case we already have authentication param in cache*/
belle_sip_provider_add_authorization(this->root->prov,request,NULL,NULL,NULL,this->realm);
}
result = belle_sip_client_transaction_send_request_to(client_transaction,next_hop_uri/*might be null*/);
/*update call id if not set yet for this OP*/
if (result == 0 && !this->call_id) {
this->call_id=ms_strdup(belle_sip_header_call_id_get_call_id(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request), belle_sip_header_call_id_t))));
}
return result;
}
int SalOp::send_request(belle_sip_request_t* request) {
bool_t need_contact=FALSE;
if (request==NULL) {
return -1; /*sanity check*/
}
/*
Header field where proxy ACK BYE CAN INV OPT REG
___________________________________________________________
Contact R o - - m o o
*/
if (strcmp(belle_sip_request_get_method(request),"INVITE")==0
||strcmp(belle_sip_request_get_method(request),"REGISTER")==0
||strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0
||strcmp(belle_sip_request_get_method(request),"OPTIONS")==0
||strcmp(belle_sip_request_get_method(request),"REFER")==0) /* Despite contact seems not mandatory, call flow example show a Contact in REFER requests*/
need_contact=TRUE;
return send_request_with_contact(request,need_contact);
}
void SalOp::resend_request(belle_sip_request_t* request) {
belle_sip_header_cseq_t* cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CSEQ);
belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1);
send_request(request);
}
void SalOp::process_authentication() {
belle_sip_request_t* initial_request=belle_sip_transaction_get_request((belle_sip_transaction_t*)this->pending_auth_transaction);
belle_sip_request_t* new_request;
bool_t is_within_dialog=FALSE;
belle_sip_list_t* auth_list=NULL;
belle_sip_auth_event_t* auth_event;
belle_sip_response_t *response=belle_sip_transaction_get_response((belle_sip_transaction_t*)this->pending_auth_transaction);
belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(initial_request,belle_sip_header_from_t);
belle_sip_uri_t *from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from);
if (strcasecmp(belle_sip_uri_get_host(from_uri),"anonymous.invalid")==0){
/*prefer using the from from the SalOp*/
from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)get_from_address());
}
if (this->dialog && belle_sip_dialog_get_state(this->dialog)==BELLE_SIP_DIALOG_CONFIRMED) {
new_request = belle_sip_dialog_create_request_from(this->dialog,initial_request);
if (!new_request)
new_request = belle_sip_dialog_create_queued_request_from(this->dialog,initial_request);
is_within_dialog=TRUE;
} else {
new_request=initial_request;
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_AUTHORIZATION);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(new_request),BELLE_SIP_PROXY_AUTHORIZATION);
}
if (new_request==NULL) {
ms_error("sal_process_authentication() op=[%p] cannot obtain new request from dialog.",this);
return;
}
if (belle_sip_provider_add_authorization(this->root->prov,new_request,response,from_uri,&auth_list,this->realm)) {
if (is_within_dialog) {
send_request(new_request);
} else {
resend_request(new_request);
}
this->root->remove_pending_auth(this);
}else {
belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(response,belle_sip_header_from_t);
char *tmp=belle_sip_object_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)));
ms_message("No auth info found for [%s]",tmp);
belle_sip_free(tmp);
this->root->add_pending_auth(this);
if (is_within_dialog) {
belle_sip_object_unref(new_request);
}
}
/*always store auth info, for case of wrong credential*/
if (this->auth_info) {
sal_auth_info_delete(this->auth_info);
this->auth_info=NULL;
}
if (auth_list){
auth_event=(belle_sip_auth_event_t*)(auth_list->data);
this->auth_info=sal_auth_info_create(auth_event);
belle_sip_list_free_with_data(auth_list,(void (*)(void*))belle_sip_auth_event_destroy);
}
}
char *SalOp::get_dialog_id() const {
if (this->dialog != NULL) {
return ms_strdup_printf("%s;to-tag=%s;from-tag=%s", this->call_id,
belle_sip_dialog_get_remote_tag(this->dialog), belle_sip_dialog_get_local_tag(this->dialog));
}
return NULL;
}
int SalOp::get_address_family() const {
belle_sip_transaction_t *tr=NULL;
belle_sip_header_address_t *contact;
if (this->refresher)
tr=(belle_sip_transaction_t *)belle_sip_refresher_get_transaction(this->refresher);
if (tr==NULL)
tr=(belle_sip_transaction_t *)this->pending_client_trans;
if (tr==NULL)
tr=(belle_sip_transaction_t *)this->pending_server_trans;
if (tr==NULL){
ms_error("Unable to determine IP version from signaling operation.");
return AF_UNSPEC;
}
if (this->refresher) {
belle_sip_response_t *resp = belle_sip_transaction_get_response(tr);
belle_sip_header_via_t *via = resp ?belle_sip_message_get_header_by_type(resp,belle_sip_header_via_t):NULL;
if (!via){
ms_error("Unable to determine IP version from signaling operation, no via header found.");
return AF_UNSPEC;
}
return (strchr(belle_sip_header_via_get_host(via),':') != NULL) ? AF_INET6 : AF_INET;
} else {
belle_sip_request_t *req = belle_sip_transaction_get_request(tr);
contact=(belle_sip_header_address_t*)belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t);
if (!contact){
ms_error("Unable to determine IP version from signaling operation, no contact header found.");
}
return sal_address_is_ipv6((SalAddress*)contact) ? AF_INET6 : AF_INET;
}
}
bool_t SalOp::is_idle() const {
if (this->dialog){
return !belle_sip_dialog_request_pending(this->dialog);
}
return TRUE;
}
void SalOp::set_entity_tag(const char* entity_tag) {
if (this->entity_tag != NULL) ms_free(this->entity_tag);
this->entity_tag = entity_tag ? ms_strdup(entity_tag) : NULL;
}
void SalOp::set_event(const char *eventname) {
belle_sip_header_event_t *header = NULL;
if (this->event) belle_sip_object_unref(this->event);
if (eventname){
header = belle_sip_header_event_create(eventname);
belle_sip_object_ref(header);
}
this->event = header;
}
void SalOp::add_initial_route_set(belle_sip_request_t *request, const MSList *list) {
const MSList *elem;
for (elem=list;elem!=NULL;elem=elem->next){
SalAddress *addr=(SalAddress*)elem->data;
belle_sip_header_route_t *route;
belle_sip_uri_t *uri;
/*Optimization: if the initial route set only contains one URI which is the same as the request URI, ommit it*/
if (elem==list && list->next==NULL){
belle_sip_uri_t *requri=belle_sip_request_get_uri(request);
/*skip the first route it is the same as the request uri*/
if (strcmp(sal_address_get_domain(addr),belle_sip_uri_get_host(requri))==0 ){
ms_message("Skipping top route of initial route-set because same as request-uri.");
continue;
}
}
route=belle_sip_header_route_create((belle_sip_header_address_t*)addr);
uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)route);
belle_sip_uri_set_lr_param(uri,1);
belle_sip_message_add_header((belle_sip_message_t*)request,(belle_sip_header_t*)route);
}
}
belle_sip_request_t* SalOp::build_request(const char* method) {
belle_sip_header_from_t* from_header;
belle_sip_header_to_t* to_header;
belle_sip_provider_t* prov=this->root->prov;
belle_sip_request_t *req;
belle_sip_uri_t* req_uri;
belle_sip_uri_t* to_uri;
belle_sip_header_call_id_t *call_id_header;
const SalAddress* to_address;
const MSList *elem=get_route_addresses();
char token[10];
/* check that the op has a correct to address */
to_address = get_to_address();
if( to_address == NULL ){
ms_error("No To: address, cannot build request");
return NULL;
}
to_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to_address));
if( to_uri == NULL ){
ms_error("To: address is invalid, cannot build request");
return NULL;
}
if (strcmp("REGISTER",method)==0 || this->privacy==SalPrivacyNone) {
from_header = belle_sip_header_from_create(BELLE_SIP_HEADER_ADDRESS(get_from_address())
,belle_sip_random_token(token,sizeof(token)));
} else {
from_header=belle_sip_header_from_create2("Anonymous <sip:anonymous@anonymous.invalid>",belle_sip_random_token(token,sizeof(token)));
}
/*make sure to preserve components like headers or port*/
req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)to_uri);
belle_sip_uri_set_secure(req_uri,is_secure());
to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(to_address),NULL);
call_id_header = belle_sip_provider_create_call_id(prov);
if (get_call_id()) {
belle_sip_header_call_id_set_call_id(call_id_header, get_call_id());
}
req=belle_sip_request_create(
req_uri,
method,
call_id_header,
belle_sip_header_cseq_create(20,method),
from_header,
to_header,
belle_sip_header_via_new(),
70);
if (this->privacy & SalPrivacyId) {
belle_sip_header_p_preferred_identity_t* p_preferred_identity=belle_sip_header_p_preferred_identity_create(BELLE_SIP_HEADER_ADDRESS(get_from_address()));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(p_preferred_identity));
}
if (elem && strcmp(method,"REGISTER")!=0 && !this->root->no_initial_route){
add_initial_route_set(req,elem);
}
if (strcmp("REGISTER",method)!=0 && this->privacy!=SalPrivacyNone ){
belle_sip_header_privacy_t* privacy_header=belle_sip_header_privacy_new();
if (this->privacy&SalPrivacyCritical)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyCritical));
if (this->privacy&SalPrivacyHeader)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyHeader));
if (this->privacy&SalPrivacyId)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyId));
if (this->privacy&SalPrivacyNone)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyNone));
if (this->privacy&SalPrivacySession)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacySession));
if (this->privacy&SalPrivacyUser)
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyUser));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(privacy_header));
}
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),this->root->supported);
return req;
}
void SalOp::set_error_info_from_response(belle_sip_response_t *response) {
int code = belle_sip_response_get_status_code(response);
const char *reason_phrase=belle_sip_response_get_reason_phrase(response);
belle_sip_header_t *warning=belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Warning");
SalErrorInfo *ei=&this->error_info;
const char *warnings;
warnings=warning ? belle_sip_header_get_unparsed_value(warning) : NULL;
sal_error_info_set(ei,SalReasonUnknown,"SIP", code,reason_phrase,warnings);
set_reason_error_info(BELLE_SIP_MESSAGE(response));
}
const char* SalOp::to_string(const State value) {
switch(value) {
case State::Early: return"SalOpStateEarly";
case State::Active: return "SalOpStateActive";
case State::Terminating: return "SalOpStateTerminating";
case State::Terminated: return "SalOpStateTerminated";
default:
return "Unknown";
}
}
void SalOp::set_reason_error_info(belle_sip_message_t *msg) {
belle_sip_header_reason_t* reason_header = belle_sip_message_get_header_by_type(msg,belle_sip_header_reason_t);
if (reason_header){
SalErrorInfo *ei=&this->reason_error_info; // ?//
const char *protocol = belle_sip_header_reason_get_protocol(reason_header);
int code = belle_sip_header_reason_get_cause(reason_header);
const char *text = belle_sip_header_reason_get_text(reason_header);
sal_error_info_set(ei, SalReasonUnknown, protocol, code, text, NULL);
}
}
void SalOp::set_referred_by(belle_sip_header_referred_by_t* referred_by) {
if (this->referred_by){
belle_sip_object_unref(this->referred_by);
}
this->referred_by=referred_by;
belle_sip_object_ref(this->referred_by);
}
void SalOp::set_replaces(belle_sip_header_replaces_t* replaces) {
if (this->replaces){
belle_sip_object_unref(this->replaces);
}
this->replaces=replaces;
belle_sip_object_ref(this->replaces);
}
int SalOp::send_request_with_expires(belle_sip_request_t* request,int expires) {
belle_sip_header_expires_t* expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_EXPIRES);
if (!expires_header && expires>=0) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header=belle_sip_header_expires_new()));
}
if (expires_header) belle_sip_header_expires_set_expires(expires_header,expires);
return send_request(request);
}
int SalOp::send_and_create_refresher(belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener) {
if (send_request_with_expires(req,expires)==0) {
if (this->refresher) {
belle_sip_refresher_stop(this->refresher);
belle_sip_object_unref(this->refresher);
}
if ((this->refresher = belle_sip_client_transaction_create_refresher(this->pending_client_trans))) {
/*since refresher acquires the transaction, we should remove our context from the transaction, because we won't be notified
* that it is terminated anymore.*/
unref();/*loose the reference that was given to the transaction when creating it*/
/* Note that the refresher will replace our data with belle_sip_transaction_set_application_data().
Something in the design is not very good here, it makes things complicated to the belle-sip user.
Possible ideas to improve things: refresher shall not use belle_sip_transaction_set_application_data() internally, refresher should let the first transaction
notify the user as a normal transaction*/
belle_sip_refresher_set_listener(this->refresher,listener, this);
belle_sip_refresher_set_retry_after(this->refresher,this->root->refresher_retry_after);
belle_sip_refresher_set_realm(this->refresher,this->realm);
belle_sip_refresher_enable_manual_mode(this->refresher, this->manual_refresher);
return 0;
} else {
return -1;
}
}
return -1;
}
belle_sip_header_contact_t *SalOp::create_contact() {
belle_sip_header_contact_t* contact_header;
belle_sip_uri_t* contact_uri;
if (get_contact_address()) {
contact_header = belle_sip_header_contact_create(BELLE_SIP_HEADER_ADDRESS(get_contact_address()));
} else {
contact_header= belle_sip_header_contact_new();
}
if (!(contact_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact_header)))) {
/*no uri, just creating a new one*/
contact_uri=belle_sip_uri_new();
belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact_header),contact_uri);
}
belle_sip_uri_set_user_password(contact_uri,NULL);
belle_sip_uri_set_secure(contact_uri,is_secure());
if (this->privacy!=SalPrivacyNone){
belle_sip_uri_set_user(contact_uri,NULL);
}
belle_sip_header_contact_set_automatic(contact_header,this->root->auto_contacts);
if (this->root->uuid){
if (belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance")==0){
char *instance_id=belle_sip_strdup_printf("\"<urn:uuid:%s>\"",this->root->uuid);
belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(contact_header),"+sip.instance",instance_id);
belle_sip_free(instance_id);
}
}
return contact_header;
}
void SalOp::unlink_op_with_dialog(belle_sip_dialog_t* dialog) {
belle_sip_dialog_set_application_data(dialog,NULL);
unref();
belle_sip_object_unref(dialog);
}
belle_sip_dialog_t *SalOp::link_op_with_dialog(belle_sip_dialog_t* dialog) {
belle_sip_dialog_set_application_data(dialog,ref());
belle_sip_object_ref(dialog);
return dialog;
}
void SalOp::set_or_update_dialog(belle_sip_dialog_t* dialog) {
ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]", this, this->dialog,dialog);
ref();
if (this->dialog!=dialog){
if (this->dialog){
/*FIXME: shouldn't we delete unconfirmed dialogs ?*/
unlink_op_with_dialog(this->dialog);
this->dialog=NULL;
}
if (dialog) {
this->dialog=link_op_with_dialog(dialog);
belle_sip_dialog_enable_pending_trans_checking(dialog,this->root->pending_trans_checking);
}
}
unref();
}
int SalOp::ping(const char *from, const char *to) {
set_from(from);
set_to(to);
return send_request(build_request("OPTIONS"));
}
int SalOp::send_info(const char *from, const char *to, const SalBodyHandler *body_handler) {
if (this->dialog && belle_sip_dialog_get_state(this->dialog) == BELLE_SIP_DIALOG_CONFIRMED) {
belle_sip_request_t *req;
belle_sip_dialog_enable_pending_trans_checking(this->dialog,this->root->pending_trans_checking);
req=belle_sip_dialog_create_queued_request(this->dialog,"INFO");
belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler));
return send_request(req);
}else{
ms_error("Cannot send INFO message on op [%p] because dialog is not in confirmed state yet.", this);
}
return -1;
}
SalBodyHandler *SalOp::get_body_handler(belle_sip_message_t *msg) {
belle_sip_body_handler_t *body_handler = belle_sip_message_get_body_handler(msg);
if (body_handler != NULL) {
belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t);
belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_length_t);
belle_sip_header_t *content_encoding = belle_sip_message_get_header(msg, "Content-Encoding");
if (content_type != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_type));
if (content_length != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_length));
if (content_encoding != NULL) belle_sip_body_handler_add_header(body_handler, content_encoding);
}
return (SalBodyHandler *)body_handler;
}
void SalOp::assign_recv_headers(belle_sip_message_t *incoming) {
if (incoming) belle_sip_object_ref(incoming);
if (this->recv_custom_headers){
belle_sip_object_unref(this->recv_custom_headers);
this->recv_custom_headers=NULL;
}
if (incoming){
this->recv_custom_headers=(SalCustomHeader*)incoming;
}
}
void SalOp::set_remote_contact(const char* remote_contact) {
assign_address(&this->remote_contact_address,remote_contact);
/*to preserve header params*/
assign_string(&this->remote_contact,remote_contact);
}
void SalOp::set_network_origin(const char *origin) {
SET_PARAM(origin);
}
void SalOp::set_network_origin_address(SalAddress *origin){
char* address_string=sal_address_as_string(origin); /*can probably be optimized*/
set_network_origin(address_string);
ms_free(address_string);
}
/*
rfc3323
4.2 Expressing Privacy Preferences
When a Privacy header is constructed, it MUST consist of either the
value 'none', or one or more of the values 'user', 'header' and
'session' (each of which MUST appear at most once) which MAY in turn
be followed by the 'critical' indicator.
*/
void SalOp::set_privacy_from_message(belle_sip_message_t* msg) {
belle_sip_header_privacy_t* privacy = belle_sip_message_get_header_by_type(msg,belle_sip_header_privacy_t);
if (!privacy) {
set_privacy(SalPrivacyNone);
} else {
belle_sip_list_t* privacy_list=belle_sip_header_privacy_get_privacy(privacy);
set_privacy(0);
for (;privacy_list!=NULL;privacy_list=privacy_list->next) {
char* privacy_value=(char*)privacy_list->data;
if(strcmp(sal_privacy_to_string(SalPrivacyCritical),privacy_value) == 0)
set_privacy(get_privacy()|SalPrivacyCritical);
if(strcmp(sal_privacy_to_string(SalPrivacyHeader),privacy_value) == 0)
set_privacy(get_privacy()|SalPrivacyHeader);
if(strcmp(sal_privacy_to_string(SalPrivacyId),privacy_value) == 0)
set_privacy(get_privacy()|SalPrivacyId);
if(strcmp(sal_privacy_to_string(SalPrivacyNone),privacy_value) == 0) {
set_privacy(SalPrivacyNone);
break;
}
if(strcmp(sal_privacy_to_string(SalPrivacySession),privacy_value) == 0)
set_privacy(get_privacy()|SalPrivacySession);
if(strcmp(sal_privacy_to_string(SalPrivacyUser),privacy_value) == 0)
set_privacy(get_privacy()|SalPrivacyUser);
}
}
}
void SalOp::set_remote_ua(belle_sip_message_t* message) {
belle_sip_header_user_agent_t* user_agent=belle_sip_message_get_header_by_type(message,belle_sip_header_user_agent_t);
char user_agent_string[256];
if (user_agent && belle_sip_header_user_agent_get_products_as_string(user_agent,user_agent_string,sizeof(user_agent_string))>0) {
if (this->remote_ua!=NULL){
ms_free(this->remote_ua);
}
this->remote_ua=ms_strdup(user_agent_string);
}
}
const char *SalOp::to_string(const Type type) {
switch(type) {
case Type::Register: return "SalOpRegister";
case Type::Call: return "SalOpCall";
case Type::Message: return "SalOpMessage";
case Type::Presence: return "SalOpPresence";
default: return "SalOpUnknown";
}
}
bool_t SalOp::is_secure() const {
const SalAddress* from = get_from_address();
const SalAddress* to = get_to_address();
return from && to && strcasecmp("sips",sal_address_get_scheme(from))==0 && strcasecmp("sips",sal_address_get_scheme(to))==0;
}
/*
* Warning: this function takes owneship of the custom headers
*/
void SalOp::set_sent_custom_header(SalCustomHeader* ch){
if (this->sent_custom_headers){
sal_custom_header_free(this->sent_custom_headers);
this->sent_custom_headers=NULL;
}
if (ch) belle_sip_object_ref((belle_sip_message_t*)ch);
this->sent_custom_headers=ch;
}
void SalOp::add_headers(belle_sip_header_t *h, belle_sip_message_t *msg){
if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_contact_t)){
belle_sip_header_contact_t* newct;
/*special case for contact, we want to keep everything from the custom contact but set automatic mode and add our own parameters as well*/
set_contact_address((SalAddress*)BELLE_SIP_HEADER_ADDRESS(h));
newct = create_contact();
belle_sip_message_set_header(BELLE_SIP_MESSAGE(msg),BELLE_SIP_HEADER(newct));
return;
}
/*if a header already exists in the message, replace it*/
belle_sip_message_set_header(msg,h);
}
void SalOp::add_custom_headers(belle_sip_message_t *msg){
if (this->sent_custom_headers){
belle_sip_message_t *ch=(belle_sip_message_t*)this->sent_custom_headers;
belle_sip_list_t *l=belle_sip_message_get_all_headers(ch);
belle_sip_list_t *elem;
for(elem=l;elem!=NULL;elem=elem->next){
add_headers((belle_sip_header_t*)elem->data,msg);
}
belle_sip_list_free(l);
}
}
int SalOp::unsubscribe(){
if (this->refresher){
const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(this->refresher);
belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr);
belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0);
belle_sip_refresher_refresh(this->refresher,0);
return 0;
}
return -1;
}
void SalOp::process_incoming_message(const belle_sip_request_event_t *event) {
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(this->root->prov,req);
belle_sip_header_address_t* address;
belle_sip_header_from_t* from_header;
belle_sip_header_content_type_t* content_type;
belle_sip_response_t* resp;
int errcode = 500;
belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t);
belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t);
belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t);
char* from;
bool_t external_body=FALSE;
from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
if (content_type) {
SalMessage salmsg;
char message_id[256]={0};
if (this->pending_server_trans) belle_sip_object_unref(this->pending_server_trans);
this->pending_server_trans=server_transaction;
belle_sip_object_ref(this->pending_server_trans);
external_body=is_external_body(content_type);
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
snprintf(message_id,sizeof(message_id)-1,"%s%i"
,belle_sip_header_call_id_get_call_id(call_id)
,belle_sip_header_cseq_get_seq_number(cseq));
salmsg.from=from;
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
salmsg.url=NULL;
salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type));
if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
}
salmsg.message_id=message_id;
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
this->root->callbacks.message_received(this,&salmsg);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
ms_free((char *)salmsg.content_type);
} else {
ms_error("Unsupported MESSAGE (no Content-Type)");
resp = belle_sip_response_create_from_request(req, errcode);
add_message_accept((belle_sip_message_t*)resp);
belle_sip_server_transaction_send_response(server_transaction,resp);
release();
}
}
bool_t SalOp::is_external_body(belle_sip_header_content_type_t* content_type) {
return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0;
}
int SalOp::reply_message(SalReason reason) {
if (this->pending_server_trans){
int code=to_sip_code(reason);
belle_sip_response_t *resp = belle_sip_response_create_from_request(
belle_sip_transaction_get_request((belle_sip_transaction_t*)this->pending_server_trans),code);
belle_sip_server_transaction_send_response(this->pending_server_trans,resp);
return 0;
}else ms_error("sal_message_reply(): no server transaction");
return -1;
}
void SalOp::add_message_accept(belle_sip_message_t *msg) {
bctbx_list_t *item;
const char *str;
char *old;
char *header = ms_strdup("xml/cipher, application/cipher.vnd.gsma.rcs-ft-http+xml");
for (item = this->root->supported_content_types; item != NULL; item = bctbx_list_next(item)) {
str = (const char *)bctbx_list_get_data(item);
old = header;
header = ms_strdup_printf("%s, %s", old, str);
ms_free(old);
}
belle_sip_message_add_header(msg, belle_sip_header_create("Accept", header));
ms_free(header);
}
void SalOp::set_service_route(const SalAddress* service_route) {
if (this->service_route)
sal_address_destroy(this->service_route);
this->service_route=service_route?sal_address_clone(service_route):NULL;
}
LINPHONE_END_NAMESPACE