864 lines
25 KiB
C
Executable File
864 lines
25 KiB
C
Executable File
/*
|
|
* Copyright (C) 2009 Mamadou Diop.
|
|
*
|
|
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
|
|
*
|
|
* This file is part of Open Source Doubango Framework.
|
|
*
|
|
* DOUBANGO is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* DOUBANGO 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 DOUBANGO.
|
|
*
|
|
*/
|
|
#include "common.h"
|
|
|
|
#include "invite.h"
|
|
#include "message.h"
|
|
#include "options.h"
|
|
#include "publish.h"
|
|
#include "register.h"
|
|
#include "subscribe.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* === default values === */
|
|
#define DEFAULT_REALM "open-ims.test"
|
|
#define DEFAULT_IMPI "bob@"DEFAULT_REALM
|
|
#define DEFAULT_IMPU "sip:bob@"DEFAULT_REALM
|
|
|
|
#ifndef DEFAULT_LOCAL_IP
|
|
//# ifdef ANDROID /* On the emulator */
|
|
//# define DEFAULT_LOCAL_IP "10.0.2.15"
|
|
//# define DEFAULT_LOCAL_IP "192.168.0.14"
|
|
//# else
|
|
# define DEFAULT_LOCAL_IP TNET_SOCKET_HOST_ANY
|
|
//# endif
|
|
#endif
|
|
|
|
|
|
extern ctx_t* ctx;
|
|
|
|
int stack_callback(const tsip_event_t *sipevent);
|
|
int session_handle_event(const tsip_event_t *sipevent);
|
|
int session_tostring(const session_t* session);
|
|
|
|
/* our SIP callback function */
|
|
int stack_callback(const tsip_event_t *_event)
|
|
{
|
|
int ret = 0;
|
|
|
|
if(!_event) { /* should never happen ...but who know? */
|
|
TSK_DEBUG_WARN("Null SIP event.");
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
tsk_safeobj_lock(ctx);
|
|
#endif
|
|
|
|
switch(_event->type) {
|
|
case tsip_event_register: {
|
|
/* REGISTER */
|
|
ret = register_handle_event(_event);
|
|
break;
|
|
}
|
|
case tsip_event_invite: {
|
|
/* INVITE */
|
|
ret = invite_handle_event(_event);
|
|
break;
|
|
}
|
|
case tsip_event_message: {
|
|
/* MESSAGE */
|
|
ret = message_handle_event(_event);
|
|
break;
|
|
}
|
|
case tsip_event_options: {
|
|
/* OPTIONS */
|
|
ret = options_handle_event(_event);
|
|
break;
|
|
}
|
|
case tsip_event_publish: {
|
|
/* PUBLISH */
|
|
ret = publish_handle_event(_event);
|
|
break;
|
|
}
|
|
case tsip_event_subscribe: {
|
|
/* SUBSCRIBE */
|
|
ret = subscribe_handle_event(_event);
|
|
break;
|
|
}
|
|
|
|
case tsip_event_dialog: {
|
|
/* Common to all dialogs */
|
|
ret = session_handle_event(_event);
|
|
break;
|
|
}
|
|
|
|
case tsip_event_stack: {
|
|
switch(_event->code) {
|
|
case tsip_event_code_stack_started:
|
|
TSK_DEBUG_INFO("Stack started");
|
|
break;
|
|
case tsip_event_code_stack_stopped:
|
|
TSK_DEBUG_INFO("Stack stopped");
|
|
break;
|
|
case tsip_event_code_stack_failed_to_start:
|
|
TSK_DEBUG_INFO("Stack failed to start");
|
|
break;
|
|
case tsip_event_code_stack_failed_to_stop:
|
|
TSK_DEBUG_INFO("Stack failed to stop");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
/* Unsupported */
|
|
TSK_DEBUG_WARN("%d not supported as SIP event.", _event->type);
|
|
ret = -3;
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
tsk_safeobj_unlock(ctx);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* ==================================================================
|
|
========================== Context =================================
|
|
*/
|
|
|
|
ctx_t* ctx_create()
|
|
{
|
|
return tsk_object_new(ctx_def_t);
|
|
}
|
|
|
|
static tsk_object_t* ctx_ctor(tsk_object_t * self, va_list * app)
|
|
{
|
|
ctx_t *ctx = self;
|
|
if(ctx) {
|
|
/* stack */
|
|
ctx->stack = tsip_stack_create(stack_callback, DEFAULT_REALM, DEFAULT_IMPI, DEFAULT_IMPU, /* Mandatory parameters */
|
|
TSIP_STACK_SET_LOCAL_IP(DEFAULT_LOCAL_IP), /* local IP */
|
|
TSIP_STACK_SET_SECAGREE_IPSEC_2("TCP", tsk_true),
|
|
TSIP_STACK_SET_IMS_AKA_OPERATOR_ID("22b3156098e11e177e93711d6cb0e688"),
|
|
TSIP_STACK_SET_IMS_AKA_AMF(0x8000),
|
|
TSIP_STACK_SET_NULL() /* Mandatory */);
|
|
|
|
/* SIP Sessions */
|
|
ctx->sessions = tsk_list_create();
|
|
|
|
/* user's parameters */
|
|
ctx->params = tsk_list_create();
|
|
|
|
/* init internal mutex */
|
|
tsk_safeobj_init(ctx);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
static tsk_object_t* ctx_dtor(tsk_object_t * self)
|
|
{
|
|
ctx_t *ctx = self;
|
|
if(ctx) {
|
|
/* Stop the stack (as sessions are alive, you will continue to receive callbacks)*/
|
|
tsip_stack_stop(ctx->stack);
|
|
|
|
/* sessions : should be freed before the stack as explained on the Programmer's Guide
|
|
* As all dialogs have been hanged up, the list should be empty ...but who know?*/
|
|
TSK_OBJECT_SAFE_FREE(ctx->sessions);
|
|
|
|
/* Destroy the stack */
|
|
TSK_OBJECT_SAFE_FREE(ctx->stack);
|
|
|
|
/* Identity */
|
|
TSK_FREE(ctx->identity.display_name);
|
|
TSK_FREE(ctx->identity.impu);
|
|
TSK_FREE(ctx->identity.preferred);
|
|
TSK_FREE(ctx->identity.impi);
|
|
TSK_FREE(ctx->identity.password);
|
|
|
|
/* Network */
|
|
TSK_FREE(ctx->network.local_ip);
|
|
TSK_FREE(ctx->network.proxy_cscf);
|
|
TSK_FREE(ctx->network.proxy_cscf_trans);
|
|
TSK_FREE(ctx->network.realm);
|
|
|
|
/* Security */
|
|
TSK_FREE(ctx->security.operator_id);
|
|
|
|
/* Params */
|
|
TSK_OBJECT_SAFE_FREE(ctx->params);
|
|
|
|
/* deinit internal mutex */
|
|
tsk_safeobj_deinit(ctx);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static const tsk_object_def_t ctx_def_s = {
|
|
sizeof(ctx_t),
|
|
ctx_ctor,
|
|
ctx_dtor,
|
|
tsk_null,
|
|
};
|
|
const tsk_object_def_t *ctx_def_t = &ctx_def_s;
|
|
|
|
|
|
/* ==================================================================
|
|
========================== Stack =================================
|
|
*/
|
|
|
|
int stack_dump()
|
|
{
|
|
const tsk_list_item_t* item;
|
|
|
|
tsk_list_foreach(item, ctx->sessions) {
|
|
session_tostring(item->data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int stack_config(const opts_L_t* opts)
|
|
{
|
|
const tsk_list_item_t* item;
|
|
const opt_t* opt;
|
|
int ret = 0;
|
|
tsk_param_t* param;
|
|
tsk_bool_t pcscf_changed = tsk_false;
|
|
tsk_bool_t stun_done = tsk_false;
|
|
|
|
if(!opts) {
|
|
return -1;
|
|
}
|
|
|
|
tsk_list_foreach(item, opts) {
|
|
opt = item->data;
|
|
|
|
/* Stack-level option */
|
|
if(opt->lv != lv_none && opt->lv != lv_stack) {
|
|
continue;
|
|
}
|
|
|
|
switch(opt->type) {
|
|
case opt_amf: {
|
|
break;
|
|
}
|
|
case opt_dhcpv4:
|
|
case opt_dhcpv6: {
|
|
tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_DISCOVERY_DHCP(tsk_true),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_dname: {
|
|
break;
|
|
}
|
|
case opt_dns_naptr: {
|
|
tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_DISCOVERY_NAPTR(tsk_true),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_header: {
|
|
if((param = tsk_params_parse_param(opt->value, tsk_strlen(opt->value)))) {
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_HEADER(param->name, param->value),
|
|
TSIP_STACK_SET_NULL());
|
|
TSK_OBJECT_SAFE_FREE(param);
|
|
}
|
|
break;
|
|
}
|
|
case opt_impi: {
|
|
tsk_strupdate(&ctx->identity.impi, opt->value);
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_IMPI(ctx->identity.impi),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_impu: {
|
|
tsk_strupdate(&ctx->identity.impu, opt->value);
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_IMPU(ctx->identity.impu),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_ipv6: {
|
|
pcscf_changed = tsk_true;
|
|
ctx->network.ipv6 = tsk_true;
|
|
break;
|
|
}
|
|
case opt_local_ip: {
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_LOCAL_IP(opt->value),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_local_port: {
|
|
unsigned port = (unsigned)atoi(opt->value);
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_LOCAL_PORT(port),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_opid: {
|
|
break;
|
|
}
|
|
case opt_password: {
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_PASSWORD(opt->value),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_pcscf_ip: {
|
|
pcscf_changed = tsk_true;
|
|
tsk_strupdate(&ctx->network.proxy_cscf, opt->value);
|
|
break;
|
|
}
|
|
case opt_pcscf_port: {
|
|
pcscf_changed = tsk_true;
|
|
ctx->network.proxy_cscf_port = atoi(opt->value);
|
|
break;
|
|
}
|
|
case opt_pcscf_trans: {
|
|
pcscf_changed = tsk_true;
|
|
tsk_strupdate(&ctx->network.proxy_cscf_trans, opt->value);
|
|
break;
|
|
}
|
|
case opt_realm: {
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_REALM(opt->value),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
|
|
case opt_sigcomp_id: {
|
|
/* add compartment */
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_SIGCOMP_NEW_COMPARTMENT(opt->value),
|
|
TSIP_STACK_SET_NULL());
|
|
break;
|
|
}
|
|
|
|
case opt_stun_ip:
|
|
case opt_stun_pwd:
|
|
case opt_stun_port:
|
|
case opt_stun_usr: {
|
|
if(!stun_done) {
|
|
const opt_t* _opt;
|
|
const char* ip = tsk_null, *usr = tsk_null, *pwd = tsk_null;
|
|
unsigned port = 0;
|
|
|
|
if((_opt = opt_get_by_type(opts, opt_stun_ip))) {
|
|
ip = _opt->value;
|
|
}
|
|
if((_opt = opt_get_by_type(opts, opt_stun_port))) {
|
|
port = atoi(_opt->value);
|
|
}
|
|
if((_opt = opt_get_by_type(opts, opt_stun_usr))) {
|
|
usr = _opt->value;
|
|
}
|
|
if((_opt = opt_get_by_type(opts, opt_stun_pwd))) {
|
|
pwd = _opt->value;
|
|
}
|
|
|
|
if(ip && port) {
|
|
tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_STUN_SERVER(ip, port),
|
|
TSIP_STACK_SET_NULL());
|
|
}
|
|
if(usr) {
|
|
tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_STUN_CRED(usr, pwd),
|
|
TSIP_STACK_SET_NULL());
|
|
}
|
|
|
|
stun_done = tsk_true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}/* switch */
|
|
|
|
} /* foreach */
|
|
|
|
/* whether Proxy-CSCF config has changed */
|
|
if(pcscf_changed) {
|
|
ret = tsip_stack_set(ctx->stack,
|
|
TSIP_STACK_SET_PROXY_CSCF(ctx->network.proxy_cscf, ctx->network.proxy_cscf_port, ctx->network.proxy_cscf_trans, ctx->network.ipv6 ? "ipv6" : "ipv4"),
|
|
TSIP_STACK_SET_NULL());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int stack_run(const opts_L_t* opts)
|
|
{
|
|
if(!ctx->stack) {
|
|
TSK_DEBUG_ERROR("Stack is Null.");
|
|
return -1;
|
|
}
|
|
else {
|
|
return tsip_stack_start(ctx->stack);
|
|
}
|
|
}
|
|
|
|
/* ==================================================================
|
|
========================== Session =================================
|
|
*/
|
|
|
|
/* Find SIP session by id */
|
|
int pred_find_session_by_id(const tsk_list_item_t *item, const void* id)
|
|
{
|
|
const session_t* session;
|
|
if(item && item->data) {
|
|
session = item->data;
|
|
return (int)(tsip_ssession_get_id(session->handle)
|
|
- *((tsip_ssession_id_t*)id));
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
session_t* session_create(session_type_t type, tsip_ssession_handle_t* handle)
|
|
{
|
|
session_t* session = tsk_object_new(session_def_t, type, handle);
|
|
if(!session) {
|
|
TSK_DEBUG_ERROR("Failed to create new SIP session");
|
|
return tsk_null;
|
|
}
|
|
|
|
switch(type) {
|
|
case st_invite: {
|
|
/* Enable all features (QoS, Session timers, SigComp, ...) */
|
|
tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_USERDATA(session),
|
|
/*=== MEDIA */
|
|
TSIP_SSESSION_SET_MEDIA(
|
|
// 100rel
|
|
TSIP_MSESSION_SET_100rel(tsk_false),
|
|
// Session timers
|
|
TSIP_MSESSION_SET_TIMERS(3600, "uac"),
|
|
// QoS
|
|
TSIP_MSESSION_SET_QOS(tmedia_qos_stype_segmented, tmedia_qos_strength_optional),
|
|
// close media params
|
|
TSIP_MSESSION_SET_NULL()
|
|
),
|
|
|
|
TSIP_SSESSION_SET_NULL());
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return session;
|
|
}
|
|
|
|
const session_t* session_get_by_sid(const sessions_L_t* sessions, tsip_ssession_id_t sid)
|
|
{
|
|
const tsk_list_item_t* item;
|
|
if((item = tsk_list_find_item_by_pred(sessions, pred_find_session_by_id, &sid))) {
|
|
return item->data;
|
|
}
|
|
else {
|
|
return tsk_null;
|
|
}
|
|
}
|
|
|
|
|
|
int session_tostring(const session_t* session)
|
|
{
|
|
//char* temp = tsk_null;
|
|
printf("== Session: ");
|
|
if(session) {
|
|
/* Session Id */
|
|
printf("sid=%llu", tsip_ssession_get_id(session->handle));
|
|
/* Type */
|
|
printf(" type=");
|
|
switch(session->type) {
|
|
case st_invite:
|
|
printf("INVITE");
|
|
break;
|
|
case st_message:
|
|
printf("MESSAGE");
|
|
break;
|
|
case st_publish:
|
|
printf("PUBLISH");
|
|
break;
|
|
case st_register:
|
|
printf("REGISTER");
|
|
break;
|
|
case st_subscribe:
|
|
printf("SUBSCRIBE");
|
|
break;
|
|
default:
|
|
printf("(null)");
|
|
break;
|
|
}
|
|
/* From */
|
|
printf(" from=%s", session->from ? session->from : ctx->identity.impu);
|
|
/* From */
|
|
printf(" to=%s", session->to ? session->to : ctx->identity.impu);
|
|
}
|
|
else {
|
|
printf("(invalid)");
|
|
}
|
|
printf("\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* handle events -common to all sessions */
|
|
int session_handle_event(const tsip_event_t *_event)
|
|
{
|
|
const session_t* session;
|
|
|
|
/* Find associated session */
|
|
if(!(session = session_get_by_sid(ctx->sessions, tsip_ssession_get_id(_event->ss)))) {
|
|
/* Silentky ignore */
|
|
return 0;
|
|
}
|
|
switch(_event->code) {
|
|
/* === 7xx ==> errors === */
|
|
case tsip_event_code_dialog_transport_error:
|
|
case tsip_event_code_dialog_global_error:
|
|
case tsip_event_code_dialog_message_error:
|
|
/* do not guess that the dialog is terminated, wait for "tsip_event_code_dialog_terminated" event */
|
|
break;
|
|
|
|
/* === 8xx ==> success === */
|
|
case tsip_event_code_dialog_request_incoming:
|
|
case tsip_event_code_dialog_request_cancelled:
|
|
case tsip_event_code_dialog_request_sent:
|
|
break;
|
|
|
|
/* === 9xx ==> Informational === */
|
|
case tsip_event_code_dialog_terminated: {
|
|
/* we no longer need the session
|
|
* -> remove and destroy the session */
|
|
TSK_DEBUG_INFO("Dialog Terminated --> %s", _event->phrase);
|
|
tsk_list_remove_item_by_data(ctx->sessions, session);
|
|
break;
|
|
}
|
|
|
|
case tsip_event_code_dialog_connected:
|
|
((session_t*)session)->connected = tsk_true;
|
|
break;
|
|
case tsip_event_code_dialog_terminating:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* handle commands -common to all sessions */
|
|
const session_t* session_handle_cmd(cmd_type_t cmd, const opts_L_t* opts)
|
|
{
|
|
const session_t* session = tsk_null;
|
|
const opt_t* opt;
|
|
const tsk_list_item_t* item;
|
|
tsk_param_t* param;
|
|
int ret = 0;
|
|
|
|
/* Check if there is a session with is Id */
|
|
if((opt = opt_get_by_type(opts, opt_sid))) {
|
|
tsip_ssession_id_t sid = atoi(opt->value);
|
|
session = session_get_by_sid(ctx->sessions, sid);
|
|
}
|
|
|
|
#define TYPE_FROM_CMD(_CMD) \
|
|
((_CMD==cmd_audio || _CMD==cmd_video || _CMD==cmd_audiovideo || _CMD==cmd_file || _CMD==cmd_large_message) ? st_invite : \
|
|
((_CMD==cmd_message || _CMD==cmd_sms) ? st_message : \
|
|
(_CMD==cmd_options ? st_options : \
|
|
(_CMD==cmd_publish ? st_publish : \
|
|
(_CMD==cmd_register ? st_register : \
|
|
(_CMD==cmd_subscribe ? st_subscribe : st_none))))))
|
|
|
|
/* === Command === */
|
|
switch(cmd) {
|
|
case cmd_audio:
|
|
case cmd_video:
|
|
case cmd_audiovideo:
|
|
case cmd_file:
|
|
case cmd_large_message:
|
|
case cmd_message:
|
|
case cmd_sms:
|
|
case cmd_options:
|
|
case cmd_publish:
|
|
case cmd_register:
|
|
case cmd_subscribe: {
|
|
if(!session) { /* Create "client-side-session" */
|
|
session_t* _session;
|
|
if((_session = session_client_create(TYPE_FROM_CMD(cmd))) && (session = _session)) {
|
|
tsk_list_push_back_data(ctx->sessions, (void**)&_session);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
if(session) {
|
|
/* hold, resume, refer, update, ...all in-dialog commands */
|
|
break;
|
|
}
|
|
else {
|
|
TSK_DEBUG_WARN("Session handling: Cannot handle this command [%d]", cmd);
|
|
goto bail;
|
|
}
|
|
}
|
|
} /* switch */
|
|
|
|
if(!session) {
|
|
TSK_DEBUG_ERROR("SIP Session is Null");
|
|
goto bail;
|
|
}
|
|
|
|
/* === User Options === */
|
|
tsk_list_foreach(item, opts) {
|
|
opt = item->data;
|
|
|
|
/* Session-level option? */
|
|
if(opt->lv != lv_none && opt->lv != lv_session) {
|
|
continue;
|
|
}
|
|
|
|
switch(opt->type) {
|
|
case opt_caps: {
|
|
if(!tsk_strnullORempty(opt->value)) {
|
|
if((param = tsk_params_parse_param(opt->value, tsk_strlen(opt->value)))) {
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_CAPS(param->name, param->value),
|
|
TSIP_SSESSION_SET_NULL());
|
|
TSK_OBJECT_SAFE_FREE(param);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case opt_expires: {
|
|
if(!tsk_strnullORempty(opt->value)) {
|
|
unsigned expires = atoi(opt->value);
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_EXPIRES(expires),
|
|
TSIP_SSESSION_SET_NULL());
|
|
}
|
|
break;
|
|
}
|
|
case opt_from: {
|
|
/* You should use TSIP_SSESSION_SET_OPTION(TSIP_SSESSION_OPTION_FROM, value)
|
|
instead of TSIP_SSESSION_SET_HEADER() to set the destination URI. */
|
|
break;
|
|
}
|
|
case opt_header: {
|
|
if((param = tsk_params_parse_param(opt->value, tsk_strlen(opt->value)))) {
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_HEADER(param->name, param->value),
|
|
TSIP_SSESSION_SET_NULL());
|
|
TSK_OBJECT_SAFE_FREE(param);
|
|
}
|
|
break;
|
|
}
|
|
case opt_payload: {
|
|
/* Will be handled by the caller */
|
|
break;
|
|
}
|
|
case opt_silent: {
|
|
/* valueless option */
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_SILENT_HANGUP(tsk_true),
|
|
TSIP_SSESSION_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_sigcomp_id: {
|
|
/* sigcomp-id */
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_SIGCOMP_COMPARTMENT(opt->value),
|
|
TSIP_SSESSION_SET_NULL());
|
|
break;
|
|
}
|
|
case opt_to: {
|
|
/* You should use TSIP_SSESSION_SET_OPTION(TSIP_SSESSION_OPTION_TO, value)
|
|
instead of TSIP_SSESSION_SET_HEADER() to set the destination URI. */
|
|
if((cmd != cmd_sms) && (cmd != cmd_ect) && !tsk_strnullORempty(opt->value)) { /* SMS will use SMSC Address as Request URI */
|
|
ret = tsip_ssession_set(session->handle,
|
|
TSIP_SSESSION_SET_TO(opt->value),
|
|
TSIP_SSESSION_SET_NULL());
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
/* will be handled by the caller */
|
|
break;
|
|
}
|
|
}
|
|
|
|
} /* foreach */
|
|
|
|
bail:
|
|
return session;
|
|
}
|
|
|
|
int session_hangup(tsip_ssession_id_t sid)
|
|
{
|
|
const session_t* session;
|
|
if((session = session_get_by_sid(ctx->sessions, sid))) {
|
|
switch(session->type) {
|
|
case st_invite:
|
|
tsip_api_invite_send_bye(session->handle,
|
|
/* You can add your parameters */
|
|
TSIP_ACTION_SET_NULL());
|
|
break;
|
|
case st_message:
|
|
break;
|
|
case st_publish:
|
|
tsip_api_publish_send_unpublish(session->handle,
|
|
/* You can add your parameters */
|
|
TSIP_ACTION_SET_NULL());
|
|
break;
|
|
case st_register:
|
|
tsip_api_register_send_unregister(session->handle,
|
|
/* You can add your parameters */
|
|
TSIP_ACTION_SET_NULL());
|
|
break;
|
|
case st_subscribe:
|
|
tsip_api_subscribe_send_unsubscribe(session->handle,
|
|
/* You can add your parameters */
|
|
TSIP_ACTION_SET_NULL());
|
|
break;
|
|
default:
|
|
TSK_DEBUG_WARN("Cannot hangup session with this type [%d]", session->type);
|
|
return -2;
|
|
}
|
|
return 0;
|
|
}
|
|
else {
|
|
TSK_DEBUG_WARN("Failed to find session with sid=%llu", sid);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static tsk_object_t* session_ctor(tsk_object_t * self, va_list * app)
|
|
{
|
|
session_t *session = self;
|
|
if(session) {
|
|
session->type = va_arg(*app, session_type_t);
|
|
if((session->handle = va_arg(*app, tsip_ssession_handle_t*))) {
|
|
int ret;
|
|
/* "server-side-session" */
|
|
if((ret = tsip_ssession_take_ownership(session->handle))) {
|
|
TSK_DEBUG_ERROR("Failed to take ownership [%d]", ret);
|
|
}
|
|
}
|
|
else {
|
|
/* "client-side-session" */
|
|
session->handle = tsip_ssession_create(ctx->stack,
|
|
TSIP_SSESSION_SET_NULL());
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
static tsk_object_t* session_dtor(tsk_object_t * self)
|
|
{
|
|
session_t *session = self;
|
|
if(session) {
|
|
TSK_OBJECT_SAFE_FREE(session->handle);
|
|
|
|
TSK_FREE(session->to);
|
|
TSK_FREE(session->from);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
static int session_cmp(const tsk_object_t *_ss1, const tsk_object_t *_ss2)
|
|
{
|
|
const session_t *ss1 = _ss1;
|
|
const session_t *ss2 = _ss2;
|
|
|
|
if(ss1 && ss1) {
|
|
if(ss1->handle && ss2->handle) {
|
|
return tsk_object_cmp(ss1->handle, ss2->handle);
|
|
}
|
|
else {
|
|
return (ss2 - ss1);
|
|
}
|
|
}
|
|
else if(!ss1 && !ss2) {
|
|
return 0;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static const tsk_object_def_t session_def_s = {
|
|
sizeof(session_t),
|
|
session_ctor,
|
|
session_dtor,
|
|
session_cmp,
|
|
};
|
|
const tsk_object_def_t *session_def_t = &session_def_s;
|
|
|
|
|
|
|
|
tsip_action_handle_t* action_get_config(const opts_L_t* opts)
|
|
{
|
|
const opt_t* opt;
|
|
const tsk_list_item_t* item;
|
|
tsip_action_handle_t* action_config = tsk_null;
|
|
tsk_param_t* param;
|
|
|
|
if(TSK_LIST_IS_EMPTY(opts)) {
|
|
return tsk_null;
|
|
}
|
|
|
|
tsk_list_foreach(item, opts) {
|
|
opt = item->data;
|
|
|
|
/* action level? */
|
|
if(opt->lv != lv_action) {
|
|
continue;
|
|
}
|
|
|
|
/* create new action */
|
|
if(!action_config && !(action_config = tsip_action_create(tsip_atype_config,
|
|
TSIP_ACTION_SET_NULL()))) {
|
|
break;
|
|
}
|
|
|
|
switch(opt->type) {
|
|
case opt_header: {
|
|
if((param = tsk_params_parse_param(opt->value, tsk_strlen(opt->value)))) {
|
|
tsip_action_set(action_config,
|
|
TSIP_ACTION_SET_HEADER(param->name, param->value),
|
|
TSIP_ACTION_SET_NULL());
|
|
TSK_OBJECT_SAFE_FREE(param);
|
|
}
|
|
break;
|
|
}
|
|
case opt_payload: {
|
|
tsip_action_set(action_config,
|
|
TSIP_ACTION_SET_PAYLOAD(opt->value, tsk_strlen(opt->value)),
|
|
TSIP_ACTION_SET_NULL());
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return action_config;
|
|
}
|