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_queued_request(op->dialog,"INFO"); int error=TRUE; if (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); error=sal_op_send_request(op,info); } if (error) ms_warning("Cannot send vfu request to [%s] ", sal_op_get_to(op)); } 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_refer_with_replaces(SalOp *op, SalOp *other_call_op){ belle_sip_dialog_state_t other_call_dialog_state=other_call_op->dialog?belle_sip_dialog_get_state(other_call_op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_dialog_state_t op_dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_header_replaces_t* replaces; belle_sip_header_refer_to_t* refer_to; belle_sip_header_referred_by_t* referred_by; const char* from_tag; const char* to_tag; char* escaped_replaces; /*first, build refer to*/ if ((other_call_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) && (other_call_dialog_state!=BELLE_SIP_DIALOG_EARLY)) { ms_error("wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED or BELE_SIP_DIALOG_EARLY", belle_sip_dialog_state_to_string(other_call_dialog_state), other_call_op); return -1; } if (op_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) { ms_error("wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED", belle_sip_dialog_state_to_string(op_dialog_state), op); return -1; } refer_to=belle_sip_header_refer_to_create(belle_sip_dialog_get_remote_party(other_call_op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(refer_to)); /*rfc3891 ... 4. User Agent Client Behavior: Sending a Replaces Header A User Agent that wishes to replace a single existing early or confirmed dialog with a new dialog of its own, MAY send the target User Agent an INVITE request containing a Replaces header field. The User Agent Client (UAC) places the Call-ID, to-tag, and from-tag information for the target dialog in a single Replaces header field and sends the new INVITE to the target.*/ from_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog); to_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog); replaces=belle_sip_header_replaces_create(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(other_call_op->dialog)) ,from_tag,to_tag); escaped_replaces=belle_sip_header_replaces_value_to_escaped_string(replaces); belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)),"Replaces",escaped_replaces); belle_sip_free(escaped_replaces); referred_by=belle_sip_header_referred_by_create(belle_sip_dialog_get_local_party(op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(referred_by)); return sal_call_refer_to(op,refer_to,referred_by); }
static int sal_op_check_dialog_state(SalOp *op) { belle_sip_dialog_state_t state=op->dialog?belle_sip_dialog_get_state(op->dialog): BELLE_SIP_DIALOG_NULL; if (state != BELLE_SIP_DIALOG_CONFIRMED) { ms_warning("Cannot notify presence for op [%p] because dialog in state [%s]",op, belle_sip_dialog_state_to_string(state)); return -1; } else return 0; }
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_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); belle_sip_response_t* resp; const char *method=belle_sip_request_get_method(req); 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) { if (strcmp(method,"SUBSCRIBE")==0){ op->dialog=belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction)); belle_sip_dialog_set_application_data(op->dialog,op); sal_op_ref(op); ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op)); }else{ /* this is a NOTIFY */ ms_message("Receiving out of dialog notify"); handle_notify(op,req); return; } } dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { op->base.root->callbacks.subscribe_presence_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 ",method,op->dialog); break; case BELLE_SIP_DIALOG_CONFIRMED: if (strcmp("NOTIFY",method)==0) { handle_notify(op,req); } else if (strcmp("SUBSCRIBE",method)==0) { /*either a refresh or an unsubscribe*/ if (expires && belle_sip_header_expires_get_expires(expires)>0) { op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op)); } else if(expires) { ms_message("Unsubscribe received from [%s]",sal_op_get_from(op)); resp=sal_op_create_response_from_request(op,req,200); 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)); break; } }
static void callee_process_request_event(void *user_ctx, const belle_sip_request_event_t *event) { belle_sip_dialog_t* dialog; belle_sip_response_t* ringing_response; belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; belle_sip_server_transaction_t* server_transaction = belle_sip_request_event_get_server_transaction(event); belle_sip_header_to_t* to=belle_sip_message_get_header_by_type(belle_sip_request_event_get_request(event),belle_sip_header_to_t); const char* method; if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)))) { belle_sip_message("Message [%p] not for callee, skipping",belle_sip_request_event_get_request(event)); return; /*not for the callee*/ } method = belle_sip_request_get_method(belle_sip_request_event_get_request(event)); if (!server_transaction && strcmp(method,"ACK")!=0) { server_transaction= belle_sip_provider_create_server_transaction(prov,belle_sip_request_event_get_request(event)); } belle_sip_message("callee_process_request_event received [%s] message",method); dialog = belle_sip_request_event_get_dialog(event); if (!dialog ) { BC_ASSERT_STRING_EQUAL_FATAL("INVITE",method); dialog=belle_sip_provider_create_dialog(prov,BELLE_SIP_TRANSACTION(server_transaction)); callee_dialog=dialog; inserv_transaction=server_transaction; } if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_NULL) { ringing_response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)),180); /*prepare 200ok*/ ok_response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)),200); content_length= belle_sip_header_content_length_create(strlen(sdp)); content_type = belle_sip_header_content_type_create("application","sdp"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(ok_response),BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(ok_response),BELLE_SIP_HEADER(content_length)); belle_sip_message_set_body(BELLE_SIP_MESSAGE(ok_response),sdp,strlen(sdp)); belle_sip_object_ref(ok_response); /*only send ringing*/ belle_sip_server_transaction_send_response(server_transaction,ringing_response); } else if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_CONFIRMED) { /*time to send bye*/ belle_sip_client_transaction_t* client_transaction = belle_sip_provider_create_client_transaction(prov,belle_sip_dialog_create_request(dialog,"BYE")); belle_sip_client_transaction_send_request(client_transaction); } else { belle_sip_warning("Unexpected state [%s] for dialog [%p]",belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog)),dialog ); } }
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; if (op->state==SalOpStateTerminating || op->state==SalOpStateTerminated) { ms_error("Cannot terminate op [%p] in state [%s]",op,sal_op_state_to_string(op->state)); return -1; } 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_client_trans){ if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){ cancelling_invite(op); }else{ /* Case where the CANCEL cannot be sent because no provisional response was received so far. * The Op must be kept for the time of the transaction in case a response is received later. * The state is passed to Terminating to remember to terminate later. */ op->state=SalOpStateTerminating; } } 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_error("sal_call_terminate not implemented yet for dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); return -1; } } return 0; }
int sal_call_update(SalOp *op, const char *subject, bool_t no_user_consent){ belle_sip_request_t *update; belle_sip_dialog_state_t state=belle_sip_dialog_get_state(op->dialog); /*check for dialog state*/ if ( state == BELLE_SIP_DIALOG_CONFIRMED) { if (no_user_consent) update=belle_sip_dialog_create_request(op->dialog,"UPDATE"); else update=belle_sip_dialog_create_request(op->dialog,"INVITE"); } else if (state == BELLE_SIP_DIALOG_EARLY) { update=belle_sip_dialog_create_request(op->dialog,"UPDATE"); } else { ms_error("Cannot update op [%p] with dialog [%p] in state [%s]",op, op->dialog,belle_sip_dialog_state_to_string(state)); return -1; } if (update){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(update),belle_sip_header_create( "Subject", subject)); sal_op_fill_invite(op, update); return sal_op_send_request(op,update); } return -1; }
int sal_call_refer_with_replaces(SalOp *op, SalOp *other_call_op){ belle_sip_dialog_state_t other_call_dialog_state=other_call_op->dialog?belle_sip_dialog_get_state(other_call_op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_dialog_state_t op_dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; belle_sip_header_replaces_t* replaces; belle_sip_header_refer_to_t* refer_to; belle_sip_header_referred_by_t* referred_by; const char* from_tag; const char* to_tag; char* escaped_replaces; /*first, build refer to*/ if (other_call_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) { ms_error(" wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED",belle_sip_dialog_state_to_string(other_call_dialog_state) ,other_call_op); return -1; } if (op_dialog_state!=BELLE_SIP_DIALOG_CONFIRMED) { ms_error(" wrong dialog state [%s] for op [%p], should be BELLE_SIP_DIALOG_CONFIRMED",belle_sip_dialog_state_to_string(op_dialog_state) ,op); return -1; } refer_to=belle_sip_header_refer_to_create(belle_sip_dialog_get_remote_party(other_call_op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(refer_to)); if (belle_sip_dialog_is_server(other_call_op->dialog)) { to_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog); from_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog);; } else { from_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog); to_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog);; } replaces=belle_sip_header_replaces_create(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(other_call_op->dialog)) ,from_tag,to_tag); escaped_replaces=belle_sip_header_replaces_value_to_escaped_string(replaces); belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)),"Replaces",escaped_replaces); belle_sip_free(escaped_replaces); referred_by=belle_sip_header_referred_by_create(belle_sip_dialog_get_local_party(op->dialog)); belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(referred_by)); return sal_call_refer_to(op,refer_to,referred_by); }
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=NULL; 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; const char *method=belle_sip_request_get_method(req); bool_t is_update=FALSE; if (strcmp("ACK",method)!=0){ /*ACK does'nt create srv transaction*/ server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); belle_sip_object_ref(server_transaction); belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),sal_op_ref(op)); } if (strcmp("INVITE",method)==0) { if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); /*updating pending invite transaction*/ op->pending_server_trans=server_transaction; belle_sip_object_ref(op->pending_server_trans); } if (strcmp("UPDATE",method)==0) { if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans); /*updating pending update transaction*/ op->pending_update_server_trans=server_transaction; belle_sip_object_ref(op->pending_update_server_trans); } 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",method)==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"); } if (process_sdp_for_invite(op,req) == 0) { if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) { if( strstr(belle_sip_header_get_unparsed_value(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",method)==0) { if(belle_sip_request_event_get_server_transaction(event)) { /*first answer 200 ok to cancel*/ belle_sip_server_transaction_send_response(server_transaction ,sal_op_create_response_from_request(op,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 ,sal_op_create_response_from_request(op,req,481)); } } else if (strcmp("PRACK",method)==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("UPDATE",method)==0) { sal_op_reset_descriptions(op); if (process_sdp_for_invite(op,req)==0) op->base.root->callbacks.call_updating(op,TRUE); } else { belle_sip_error("Unexpected method [%s] for dialog state BELLE_SIP_DIALOG_EARLY",belle_sip_request_get_method(req)); unsupported_method(server_transaction,req); } break; } case BELLE_SIP_DIALOG_CONFIRMED: /*great ACK received*/ if (strcmp("ACK",method)==0) { if (op->sdp_offering){ SalReason reason; if (extract_sdp(op,BELLE_SIP_MESSAGE(req),&sdp,&reason)==0){ if (sdp){ 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); }else{ ms_warning("SDP expected in ACK but not found."); } } } op->base.root->callbacks.call_ack(op); } else if(strcmp("BYE",method)==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); op->state=SalOpStateTerminating; /*call end not notified by dialog deletion because transaction can end before dialog*/ } else if(strcmp("INVITE",method)==0 || (is_update=(strcmp("UPDATE",method)==0)) ) { if (is_update && !belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) { /*session timer case*/ /*session expire should be handled. to be done when real session timer (rfc4028) will be implemented*/ resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); belle_sip_object_unref(op->pending_update_server_trans); op->pending_update_server_trans=NULL; } else { /*re-invite*/ sal_op_reset_descriptions(op); if (process_sdp_for_invite(op,req)==0) op->base.root->callbacks.call_updating(op,is_update); } } else if (strcmp("INFO",method)==0){ if (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); } }else{ SalBody salbody; if (sal_op_get_body(op,(belle_sip_message_t*)req,&salbody)) { if (sal_body_has_type(&salbody,"application","dtmf-relay")){ char tmp[10]; if (sal_lines_get_value(salbody.data, "Signal",tmp, sizeof(tmp))){ op->base.root->callbacks.dtmf_received(op,tmp[0]); } }else op->base.root->callbacks.info_received(op,&salbody); } else { op->base.root->callbacks.info_received(op,NULL); } } resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); }else if (strcmp("REFER",method)==0) { sal_op_process_refer(op,event,server_transaction); } else if (strcmp("NOTIFY",method)==0) { sal_op_call_process_notify(op,event,server_transaction); } else if (strcmp("OPTIONS",method)==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("CANCEL",method)==0) { /*call leg does not exist because 200ok already sent*/ belle_sip_server_transaction_send_response(server_transaction,sal_op_create_response_from_request(op,req,481)); } else if (strcmp("MESSAGE",method)==0){ sal_process_incoming_message(op,event); }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)); break; } if (server_transaction) belle_sip_object_unref(server_transaction); }
static void call_process_response(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); belle_sip_header_content_type_t *header_content_type=NULL; belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event); const char *method; if (!client_transaction) { ms_warning("Discarding stateless response [%i] on op [%p]",code,op); return; } req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); set_or_update_dialog(op,dialog); dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL; method=belle_sip_request_get_method(req); ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state)); /*to make sure no cb will destroy op*/ sal_op_ref(op); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { if (strcmp("INVITE",method)==0 ) { if (op->state == SalOpStateTerminating) { /*check if CANCEL was sent before*/ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) { /*it wasn't sent */ if (code<200) { cancelling_invite(op); }else{ /* no need to send the INVITE because the UAS rejected the INVITE*/ if (op->dialog==NULL) call_set_released(op); } } else { /*it was sent already, so just expect the 487 or any error response to send the call_released() notification*/ if (code>=300){ if (op->dialog==NULL) call_set_released(op); } } } else if (code >= 180 && code<200) { belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response"); if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){ handle_sdp_from_response(op,response); op->base.root->callbacks.call_ringing(op); } belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref); } else if (code>=300){ call_set_error(op,response); if (op->dialog==NULL) call_set_released(op); } } else if (code >=200 && code<300 && strcmp("UPDATE",belle_sip_request_get_method(req))==0) { handle_sdp_from_response(op,response); op->base.root->callbacks.call_accepted(op); } } break; case BELLE_SIP_DIALOG_CONFIRMED: { switch (op->state) { case SalOpStateEarly:/*invite case*/ case SalOpStateActive: /*re-invite, INFO, UPDATE case*/ if (strcmp("INVITE",method)==0){ if (code >=200 && code<300) { 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(ack),op->sdp_answer); belle_sip_object_unref(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 if (code >= 300){ call_set_error(op,response); } }else if (strcmp("INFO",method)==0){ if (code == 491 && (header_content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t)) && strcmp("application",belle_sip_header_content_type_get_type(header_content_type))==0 && strcmp("media_control+xml",belle_sip_header_content_type_get_subtype(header_content_type))==0) { unsigned int retry_in =1000*((float)rand()/RAND_MAX); belle_sip_source_t *s=sal_create_timer(op->base.root,vfu_retry,sal_op_ref(op), retry_in, "vfu request retry"); ms_message("Rejected vfu request on op [%p], just retry in [%ui] ms",op,retry_in); belle_sip_object_unref(s); }else { /*ignoring*/ } }else if (strcmp("UPDATE",method)==0){ op->base.root->callbacks.call_accepted(op); /*INVITE*/ } break; case SalOpStateTerminating: sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); break; case SalOpStateTerminated: default: ms_error("Call op [%p] receives unexpected answer [%i] while in state [%s].",op,code, sal_op_state_to_string(op->state)); } } break; case BELLE_SIP_DIALOG_TERMINATED: { if (code >= 300){ call_set_error(op,response); } } break; default: { ms_error("call op [%p] receive answer [%i] not implemented",op,code); } break; } sal_op_unref(op); }
static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher, int expires, int auth_mandatory, belle_sip_list_t** auth_infos, belle_sip_uri_t *requri) { belle_sip_request_t*old_request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction)); belle_sip_response_t*old_response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(refresher->transaction)); belle_sip_dialog_t* dialog = refresher->dialog; belle_sip_client_transaction_t* client_transaction; belle_sip_request_t* request; belle_sip_header_expires_t* expires_header; belle_sip_uri_t* preset_route=refresher->transaction->preset_route; belle_sip_provider_t* prov=refresher->transaction->base.provider; belle_sip_header_contact_t* contact; /*first remove timer if any*/ if (expires >=0) { refresher->target_expires=expires; } else { /*-1 keep last value*/ } if (!dialog) { const belle_sip_transaction_state_t state=belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)); /*create new request*/ if (belle_sip_transaction_state_is_transient(state)) { /*operation pending, cannot update authorization headers*/ belle_sip_header_cseq_t* cseq; belle_sip_message("Refresher [%p] already has transaction [%p] in state [%s]" ,refresher ,refresher->transaction ,belle_sip_transaction_state_to_string(state)); if (strcmp(belle_sip_request_get_method(old_request),"PUBLISH")==0) { belle_sip_message("Refresher [%p] new publish is delayed to end of ongoing transaction" ,refresher); refresher->publish_pending = TRUE; return 0; } else { request=belle_sip_request_clone_with_body(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction))); cseq=belle_sip_message_get_header_by_type(request,belle_sip_header_cseq_t); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); } } else { request=belle_sip_client_transaction_create_authenticated_request(refresher->transaction,auth_infos,refresher->realm); } if (requri){ /*case where we are redirected*/ belle_sip_request_set_uri(request,requri); /*remove auth headers, they are not valid for new destination*/ 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); } } else { switch (belle_sip_dialog_get_state(dialog)) { case BELLE_SIP_DIALOG_CONFIRMED: { if (belle_sip_dialog_request_pending(dialog)){ belle_sip_message("Cannot refresh now, there is a pending request in the dialog."); return -1; } request=belle_sip_dialog_create_request_from(dialog,old_request); if (strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0) { belle_sip_header_content_type_t *content_type; /*put expire header*/ if (!(expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t))) { expires_header = belle_sip_header_expires_new(); belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header)); } if ((content_type = belle_sip_message_get_header_by_type(request, belle_sip_header_content_type_t)) && strcasecmp("application", belle_sip_header_content_type_get_type(content_type)) == 0 && strcasecmp("resource-lists+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0) { /*rfc5367 3.2. Subsequent SUBSCRIBE Requests ... At this point, there are no semantics associated with resource-list bodies in subsequent SUBSCRIBE requests (although future extensions can define them). Therefore, UACs SHOULD NOT include resource-list bodies in subsequent SUBSCRIBE requests to a resource list server. */ belle_sip_message("Removing body, content type and content length for refresher [%p]",refresher); belle_sip_message_set_body(BELLE_SIP_MESSAGE(request), NULL, 0); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_TYPE); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_LENGTH); } } belle_sip_provider_add_authorization(prov,request,old_response,NULL,auth_infos,refresher->realm); break; } case BELLE_SIP_DIALOG_TERMINATED: { if (refresher->first_acknoleged_request) { char tmp[11]; belle_sip_message("Dialog [%p] is in state terminated, recreating a new one for refresher [%p]",dialog,refresher); request = refresher->first_acknoleged_request; belle_sip_header_cseq_set_seq_number(belle_sip_message_get_header_by_type(request,belle_sip_header_cseq_t) ,20); belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(belle_sip_message_get_header_by_type(request,belle_sip_header_to_t)),"tag"); belle_sip_header_call_id_set_call_id( belle_sip_message_get_header_by_type(request,belle_sip_header_call_id_t) , belle_sip_random_token(tmp,sizeof(tmp))); break; } /*else nop, error case*/ } default: { belle_sip_error("Unexpected dialog state [%s] for dialog [%p], cannot refresh [%s]" ,belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog)) ,dialog ,belle_sip_request_get_method(old_request)); return -1; } } } if (auth_mandatory && auth_infos && belle_sip_list_find_custom(*auth_infos, unfilled_auth_info, NULL)) { belle_sip_message("Auth info not found for this refresh operation on [%p]",refresher); if (request) belle_sip_object_unref(request); return -1; } refresher->on_io_error=0; /*reset this flag*/ /*update expires in any cases*/ expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t); if (expires_header) belle_sip_header_expires_set_expires(expires_header,refresher->target_expires); contact=belle_sip_message_get_header_by_type(request,belle_sip_header_contact_t); if (contact && belle_sip_header_contact_get_expires(contact)>=0) belle_sip_header_contact_set_expires(contact,refresher->target_expires); /*update the Date header if it exists*/ { belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(request,belle_sip_header_date_t); if (date){ time_t curtime=time(NULL); belle_sip_header_date_set_time(date,&curtime); } } client_transaction = belle_sip_provider_create_client_transaction(prov,request); client_transaction->base.is_internal=1; belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),refresher); if (request == refresher->first_acknoleged_request) { /*request is now ref by transaction so no need to keepo it*/ belle_sip_object_unref(refresher->first_acknoleged_request); refresher->first_acknoleged_request = NULL; } switch (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction))) { case BELLE_SIP_TRANSACTION_INIT: case BELLE_SIP_TRANSACTION_CALLING: case BELLE_SIP_TRANSACTION_TRYING: /*very early state, we can assume nobody will answer, stop retransmiting*/ belle_sip_transaction_terminate(BELLE_SIP_TRANSACTION(refresher->transaction)); break; default: /*we preserve the transaction "as is"*/ break; } /*update reference transaction for next refresh*/ belle_sip_object_unref(refresher->transaction); refresher->transaction=client_transaction; belle_sip_object_ref(refresher->transaction); if (belle_sip_client_transaction_send_request_to(client_transaction,requri?requri:preset_route)) { /*send imediatly to requri in case of redirect*/ belle_sip_error("Cannot send refresh method [%s] for refresher [%p]" ,belle_sip_request_get_method(request) ,refresher); return -1; } if (expires==0) belle_sip_refresher_stop_internal(refresher,0); /*unregister transaction must be preserved*/ return 0; }
belle_sip_request_t *belle_sip_dialog_create_request(belle_sip_dialog_t *obj, const char *method){ belle_sip_request_t *req; if (obj->state != BELLE_SIP_DIALOG_CONFIRMED && obj->state != BELLE_SIP_DIALOG_EARLY) { belle_sip_error("belle_sip_dialog_create_request(): cannot create [%s] request from dialog [%p] in state [%s]",method,obj,belle_sip_dialog_state_to_string(obj->state)); return NULL; } /*don't prevent to send a BYE in any case */ if (strcmp(method,"BYE")!=0 && obj->last_transaction && belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state(obj->last_transaction))){ if (obj->state != BELLE_SIP_DIALOG_EARLY && strcmp(method,"UPDATE")!=0) { belle_sip_error("belle_sip_dialog_create_request(): cannot create [%s] request from dialog [%p] while pending [%s] transaction in state [%s]",method,obj,belle_sip_transaction_get_method(obj->last_transaction), belle_sip_transaction_state_to_string(belle_sip_transaction_get_state(obj->last_transaction))); return NULL; } /*else UPDATE transaction can be send in // */ } belle_sip_dialog_update_local_cseq(obj,method); req=create_request(obj,method,TRUE); return req; }
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=NULL; 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 (strcmp("ACK",belle_sip_request_get_method(req))!=0){ /*ACK does'nt create srv transaction*/ server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); belle_sip_object_ref(server_transaction); belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),op); sal_op_ref(op); } 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; belle_sip_object_ref(op->pending_server_trans); } 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 ,sal_op_create_response_from_request(op,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 ,sal_op_create_response_from_request(op,req,481)); } } else if (strcmp("PRACK",belle_sip_request_get_method(req))==0) { resp=sal_op_create_response_from_request(op,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",belle_sip_request_get_method(req)); 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=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); op->state=SalOpStateTerminating; /*call end not notified by dialog deletion because transaction can end before dialog*/ } 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){ if (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); } }else{ SalBody salbody; if (sal_op_get_body(op,(belle_sip_message_t*)req,&salbody)) { op->base.root->callbacks.info_received(op,&salbody); } else { op->base.root->callbacks.info_received(op,NULL); } } resp=sal_op_create_response_from_request(op,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,server_transaction); } else if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) { sal_op_call_process_notify(op,event,server_transaction); } else if (strcmp("OPTIONS",belle_sip_request_get_method(req))==0) { resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("CANCEL",belle_sip_request_get_method(req))==0) { /*call leg does not exist because 200ok already sent*/ belle_sip_server_transaction_send_response( server_transaction ,sal_op_create_response_from_request(op,req,481)); } 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 */ } if (server_transaction) belle_sip_object_unref(server_transaction); }
static void call_process_response(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); if (!client_transaction) { ms_warning("Discarding stateless response [%i] on op [%p]",code,op); return; } req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); set_or_update_dialog(op,belle_sip_response_event_get_dialog(event)); dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,op->dialog,belle_sip_dialog_state_to_string(dialog_state)); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { if (strcmp("INVITE",belle_sip_request_get_method(req))==0 ) { if (op->state == SalOpStateTerminating) { /*check if CANCEL was sent before*/ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) { /*it wasn't sent */ if (code<200) { cancelling_invite(op); }else{ /* no need to send the INVITE because the UAS rejected the INVITE*/ if (op->dialog==NULL) call_set_released(op); } } else { /*it was sent already, so just expect the 487 or any error response to send the call_released() notification*/ if (code>=300){ if (op->dialog==NULL) call_set_released(op); } } } else if (code >= 180 && code<300) { handle_sdp_from_response(op,response); op->base.root->callbacks.call_ringing(op); } else if (code>=300){ call_set_error(op,response); if (op->dialog==NULL) call_set_released(op); } } } 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(ack),op->sdp_answer); belle_sip_object_unref(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 if (code >= 300 && strcmp("INVITE",belle_sip_request_get_method(req))==0){ call_set_error(op,response); } else { /*ignoring*/ } break; case SalOpStateTerminating: sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); break; case SalOpStateTerminated: default: ms_error("Call op [%p] receives unexpected answer [%i] while in state [%s].",op,code, sal_op_state_to_string(op->state)); } } break; case BELLE_SIP_DIALOG_TERMINATED: { if (code >= 300){ call_set_error(op,response); } } break; default: { ms_error("call op [%p] receive answer [%i] not implemented",op,code); } break; } }