2010-02-24 11:57:37 +00:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
* Copyright ( C ) 2010 , Mathieu Parent < math . parent @ gmail . com >
*
* Version : MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
*
* The Initial Developer of the Original Code is
* Mathieu Parent < math . parent @ gmail . com >
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
* Mathieu Parent < math . parent @ gmail . com >
*
*
* mod_skinny . c - - Skinny Call Control Protocol ( SCCP ) Endpoint Module
*
*/
# include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION ( mod_skinny_load ) ;
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_skinny_shutdown ) ;
SWITCH_MODULE_RUNTIME_FUNCTION ( mod_skinny_runtime ) ;
SWITCH_MODULE_DEFINITION ( mod_skinny , mod_skinny_load , mod_skinny_shutdown , mod_skinny_runtime ) ;
2010-02-24 11:58:54 +00:00
# define SKINNY_EVENT_REGISTER "skinny::register"
2010-02-24 11:59:18 +00:00
# define SKINNY_EVENT_UNREGISTER "skinny::unregister"
# define SKINNY_EVENT_EXPIRE "skinny::expire"
2010-02-24 12:01:13 +00:00
# define SKINNY_EVENT_ALARM "skinny::alarm"
2010-02-24 11:57:37 +00:00
switch_endpoint_interface_t * skinny_endpoint_interface ;
static switch_memory_pool_t * module_pool = NULL ;
2010-02-24 11:59:41 +00:00
struct skinny_profile {
2010-02-24 11:57:45 +00:00
/* prefs */
2010-02-24 11:59:41 +00:00
char * name ;
2010-02-24 11:59:02 +00:00
char * domain ;
2010-02-24 11:57:37 +00:00
char * ip ;
2010-02-24 11:57:45 +00:00
unsigned int port ;
2010-02-24 11:57:37 +00:00
char * dialplan ;
2010-02-24 11:59:41 +00:00
uint32_t keep_alive ;
char date_format [ 6 ] ;
/* db */
char * dbname ;
char * odbc_dsn ;
char * odbc_user ;
char * odbc_pass ;
switch_odbc_handle_t * master_odbc ;
2010-02-24 12:00:11 +00:00
/* stats */
uint32_t ib_calls ;
uint32_t ob_calls ;
uint32_t ib_failed_calls ;
uint32_t ob_failed_calls ;
2010-02-24 11:59:41 +00:00
/* listener */
int listener_threads ;
switch_mutex_t * listener_mutex ;
switch_socket_t * sock ;
switch_mutex_t * sock_mutex ;
struct listener * listeners ;
uint8_t listener_ready ;
2010-02-24 12:01:47 +00:00
/* sessions */
switch_hash_t * session_hash ;
switch_mutex_t * sessions_mutex ;
2010-02-24 11:59:41 +00:00
} ;
typedef struct skinny_profile skinny_profile_t ;
struct skinny_globals {
/* prefs */
int debug ;
2010-02-24 11:57:37 +00:00
char * codec_string ;
char * codec_order [ SWITCH_MAX_CODECS ] ;
int codec_order_last ;
char * codec_rates_string ;
char * codec_rates [ SWITCH_MAX_CODECS ] ;
int codec_rates_last ;
unsigned int flags ;
2010-02-24 11:59:41 +00:00
/* data */
2010-02-24 11:57:37 +00:00
int calls ;
2010-02-24 11:59:41 +00:00
switch_mutex_t * calls_mutex ;
switch_hash_t * profile_hash ;
switch_event_node_t * heartbeat_node ;
int running ;
2010-02-24 11:57:37 +00:00
} ;
2010-02-24 11:59:41 +00:00
typedef struct skinny_globals skinny_globals_t ;
2010-02-24 11:57:37 +00:00
2010-02-24 11:59:41 +00:00
static skinny_globals_t globals ;
2010-02-24 11:57:37 +00:00
SWITCH_DECLARE_GLOBAL_STRING_FUNC ( set_global_codec_string , globals . codec_string ) ;
SWITCH_DECLARE_GLOBAL_STRING_FUNC ( set_global_codec_rates_string , globals . codec_rates_string ) ;
2010-02-24 11:59:41 +00:00
/*****************************************************************************/
/* SQL TYPES */
/*****************************************************************************/
static char devices_sql [ ] =
" CREATE TABLE skinny_devices ( \n "
2010-02-24 12:00:45 +00:00
" name VARCHAR(16), \n "
2010-02-24 11:59:41 +00:00
" user_id INTEGER, \n "
" instance INTEGER, \n "
" ip VARCHAR(255), \n "
2010-02-24 12:00:45 +00:00
" type INTEGER, \n "
2010-02-24 11:59:41 +00:00
" max_streams INTEGER, \n "
" port INTEGER, \n "
" codec_string VARCHAR(255) \n "
" ); \n " ;
2010-02-24 12:00:45 +00:00
static char buttons_sql [ ] =
" CREATE TABLE skinny_buttons ( \n "
2010-02-24 11:59:41 +00:00
" device_name VARCHAR(16), \n "
2010-02-24 12:00:45 +00:00
" position INTEGER, \n "
" type VARCHAR(10), \n "
" label VARCHAR(40), \n "
" value VARCHAR(24), \n "
" settings VARCHAR(44) \n "
2010-02-24 11:59:41 +00:00
" ); \n " ;
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
2010-02-24 11:59:34 +00:00
/* CHANNEL TYPES */
/*****************************************************************************/
typedef enum {
TFLAG_IO = ( 1 < < 0 ) ,
TFLAG_INBOUND = ( 1 < < 1 ) ,
TFLAG_OUTBOUND = ( 1 < < 2 ) ,
TFLAG_DTMF = ( 1 < < 3 ) ,
TFLAG_VOICE = ( 1 < < 4 ) ,
TFLAG_HANGUP = ( 1 < < 5 ) ,
TFLAG_LINEAR = ( 1 < < 6 ) ,
TFLAG_CODEC = ( 1 < < 7 ) ,
2010-02-24 12:04:08 +00:00
TFLAG_BREAK = ( 1 < < 8 ) ,
TFLAG_READING = ( 1 < < 9 ) ,
TFLAG_WRITING = ( 1 < < 10 )
2010-02-24 11:59:34 +00:00
} TFLAGS ;
typedef enum {
GFLAG_MY_CODEC_PREFS = ( 1 < < 0 )
} GFLAGS ;
2010-02-24 11:59:41 +00:00
struct private_object {
unsigned int flags ;
switch_frame_t read_frame ;
unsigned char databuf [ SWITCH_RECOMMENDED_BUFFER_SIZE ] ;
switch_core_session_t * session ;
switch_caller_profile_t * caller_profile ;
switch_mutex_t * mutex ;
switch_mutex_t * flag_mutex ;
2010-02-24 12:01:47 +00:00
/* identification */
2010-02-24 12:04:08 +00:00
struct listener * listener ;
uint32_t line ;
2010-02-24 12:01:47 +00:00
uint32_t call_id ;
2010-02-24 12:04:08 +00:00
uint32_t party_id ;
/* codec */
char * iananame ;
switch_codec_t read_codec ;
switch_codec_t write_codec ;
switch_codec_implementation_t read_impl ;
switch_codec_implementation_t write_impl ;
unsigned long rm_rate ;
uint32_t codec_ms ;
char * rm_encoding ;
char * rm_fmtp ;
switch_payload_t agreed_pt ;
/* RTP */
switch_rtp_t * rtp_session ;
char * local_sdp_audio_ip ;
switch_port_t local_sdp_audio_port ;
char * remote_sdp_audio_ip ;
switch_port_t remote_sdp_audio_port ;
2010-02-24 11:59:41 +00:00
} ;
typedef struct private_object private_t ;
2010-02-24 11:59:34 +00:00
/*****************************************************************************/
/* SKINNY MESSAGE TYPES */
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
2010-02-24 11:58:00 +00:00
2010-02-24 11:58:36 +00:00
/* KeepAliveMessage */
2010-02-24 11:58:00 +00:00
# define KEEP_ALIVE_MESSAGE 0x0000
2010-02-24 11:58:36 +00:00
/* RegisterMessage */
2010-02-24 11:58:00 +00:00
# define REGISTER_MESSAGE 0x0001
struct register_message {
2010-02-24 12:03:46 +00:00
char device_name [ 16 ] ;
uint32_t user_id ;
2010-02-24 11:58:00 +00:00
uint32_t instance ;
2010-02-24 11:58:15 +00:00
struct in_addr ip ;
2010-02-24 12:03:46 +00:00
uint32_t device_type ;
uint32_t max_streams ;
2010-02-24 11:58:00 +00:00
} ;
2010-02-24 12:01:21 +00:00
/* PortMessage */
2010-02-24 11:58:00 +00:00
# define PORT_MESSAGE 0x0002
2010-02-24 12:01:31 +00:00
/* KeypadButtonMessage */
# define KEYPAD_BUTTON_MESSAGE 0x0003
struct keypad_button_message {
uint32_t button ;
uint32_t line_instance ;
uint32_t call_id ;
} ;
/* StimulusMessage */
# define STIMULUS_MESSAGE 0x0005
struct stimulus_message {
2010-02-24 12:01:47 +00:00
uint32_t instance_type ; /* See enum skinny_button_definition */
2010-02-24 12:01:31 +00:00
uint32_t instance ;
uint32_t call_reference ;
} ;
2010-02-24 12:01:47 +00:00
/* OffHookMessage */
# define OFF_HOOK_MESSAGE 0x0006
struct off_hook_message {
uint32_t line_instance ;
uint32_t call_id ;
} ;
2010-02-24 12:01:31 +00:00
/* OnHookMessage */
# define ON_HOOK_MESSAGE 0x0007
struct on_hook_message {
uint32_t line_instance ;
uint32_t call_id ;
} ;
2010-02-24 12:01:21 +00:00
/* SpeedDialStatReqMessage */
2010-02-24 12:00:55 +00:00
# define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
struct speed_dial_stat_req_message {
uint32_t number ;
} ;
2010-02-24 11:59:02 +00:00
/* LineStatReqMessage */
# define LINE_STAT_REQ_MESSAGE 0x000B
struct line_stat_req_message {
uint32_t number ;
} ;
2010-02-24 12:03:46 +00:00
/* ConfigStatReqMessage */
# define CONFIG_STAT_REQ_MESSAGE 0x000C
2010-02-24 11:58:36 +00:00
2010-02-24 12:01:39 +00:00
/* TimeDateReqMessage */
# define TIME_DATE_REQ_MESSAGE 0x000D
2010-02-24 12:00:45 +00:00
/* ButtonTemplateReqMessage */
# define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
2010-02-24 12:01:21 +00:00
/* CapabilitiesResMessage */
2010-02-24 11:58:36 +00:00
# define CAPABILITIES_RES_MESSAGE 0x0010
2010-02-24 12:03:46 +00:00
struct station_capabilities {
uint32_t codec ;
uint16_t frames ;
char reserved [ 10 ] ;
} ;
2010-02-24 11:58:36 +00:00
struct capabilities_res_message {
uint32_t count ;
struct station_capabilities caps [ SWITCH_MAX_CODECS ] ;
} ;
2010-02-24 12:00:35 +00:00
/* AlarmMessage */
# define ALARM_MESSAGE 0x0020
struct alarm_message {
uint32_t alarm_severity ;
char display_message [ 80 ] ;
uint32_t alarm_param1 ;
uint32_t alarm_param2 ;
} ;
2010-02-24 12:01:31 +00:00
/* OpenReceiveChannelAck */
# define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
struct open_receive_channel_ack_message {
uint32_t status ;
2010-02-24 12:01:47 +00:00
struct in_addr ip ;
2010-02-24 12:01:31 +00:00
uint32_t port ;
uint32_t pass_thru_party_id ;
} ;
2010-02-24 12:00:45 +00:00
/* SoftKeySetReqMessage */
# define SOFT_KEY_SET_REQ_MESSAGE 0x0025
2010-02-24 12:01:31 +00:00
/* SoftKeyEventMessage */
# define SOFT_KEY_EVENT_MESSAGE 0x0026
struct soft_key_event_message {
uint32_t soft_key_event ;
uint32_t line_instance ;
uint32_t callreference ;
} ;
2010-02-24 11:59:18 +00:00
/* UnregisterMessage */
# define UNREGISTER_MESSAGE 0x0027
2010-02-24 12:00:45 +00:00
/* SoftKeyTemplateReqMessage */
# define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
2010-02-24 12:01:21 +00:00
/* HeadsetStatusMessage */
# define HEADSET_STATUS_MESSAGE 0x002B
struct headset_status_message {
uint32_t mode ;
} ;
2010-02-24 11:59:25 +00:00
/* RegisterAvailableLinesMessage */
# define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
struct register_available_lines_message {
uint32_t count ;
} ;
2010-02-24 11:58:36 +00:00
/* RegisterAckMessage */
2010-02-24 11:58:00 +00:00
# define REGISTER_ACK_MESSAGE 0x0081
struct register_ack_message {
uint32_t keepAlive ;
char dateFormat [ 6 ] ;
char reserved [ 2 ] ;
uint32_t secondaryKeepAlive ;
char reserved2 [ 4 ] ;
} ;
2010-02-24 12:01:31 +00:00
/* StartToneMessage */
# define START_TONE_MESSAGE 0x0082
struct start_tone_message {
uint32_t tone ; /* see enum skinny_tone */
uint32_t reserved ;
uint32_t line_instance ;
uint32_t call_id ;
} ;
enum skinny_tone {
SKINNY_TONE_SILENCE = 0x00 ,
SKINNY_TONE_DIALTONE = 0x21 ,
SKINNY_TONE_BUSYTONE = 0x23 ,
SKINNY_TONE_ALERT = 0x24 ,
SKINNY_TONE_REORDER = 0x25 ,
SKINNY_TONE_CALLWAITTONE = 0x2D ,
SKINNY_TONE_NOTONE = 0x7F ,
} ;
/* StopToneMessage */
# define STOP_TONE_MESSAGE 0x0083
struct stop_tone_message {
uint32_t line_instance ;
uint32_t call_id ;
} ;
/* SetRingerMessage */
# define SET_RINGER_MESSAGE 0x0085
struct set_ringer_message {
uint32_t ring_type ; /* See enum skinny_ring_type */
2010-02-24 12:01:47 +00:00
uint32_t ring_mode ; /* See enum skinny_ring_mode */
uint32_t unknown ; /* ?? */
2010-02-24 12:01:31 +00:00
} ;
enum skinny_ring_type {
2010-02-24 12:01:47 +00:00
SKINNY_RING_OFF = 1 ,
SKINNY_RING_INSIDE = 2 ,
SKINNY_RING_OUTSIDE = 3 ,
SKINNY_RING_FEATURE = 4
2010-02-24 12:01:31 +00:00
} ;
2010-02-24 12:01:47 +00:00
enum skinny_ring_mode {
SKINNY_RING_FOREVER = 1 ,
SKINNY_RING_ONCE = 2 ,
2010-02-24 12:01:31 +00:00
} ;
/* SetLampMessage */
# define SET_LAMP_MESSAGE 0x0086
struct set_lamp_message {
uint32_t stimulus ;
uint32_t stimulus_instance ;
uint32_t mode ; /* See enum skinny_lamp_mode */
} ;
enum skinny_lamp_mode {
SKINNY_LAMP_OFF = 1 ,
SKINNY_LAMP_ON = 2 ,
SKINNY_LAMP_WINK = 3 ,
SKINNY_LAMP_FLASH = 4 ,
SKINNY_LAMP_BLINK = 5 ,
} ;
/* SetSpeakerModeMessage */
# define SET_SPEAKER_MODE_MESSAGE 0x0088
struct set_speaker_mode_message {
uint32_t mode ; /* See enum skinny_speaker_mode */
} ;
enum skinny_speaker_mode {
SKINNY_SPEAKER_ON = 1 ,
SKINNY_SPEAKER_OFF = 2 ,
} ;
/* StartMediaTransmissionMessage */
# define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
struct start_media_transmission_message {
uint32_t conference_id ;
uint32_t pass_thru_party_id ;
uint32_t remote_ip ;
uint32_t remote_port ;
uint32_t ms_per_packet ;
uint32_t payload_capacity ;
uint32_t precedence ;
uint32_t silence_suppression ;
uint16_t max_frames_per_packet ;
uint32_t g723_bitrate ;
/* ... */
} ;
/* StopMediaTransmissionMessage */
# define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
struct stop_media_transmission_message {
uint32_t conference_id ;
uint32_t pass_thru_party_id ;
2010-02-24 12:01:47 +00:00
uint32_t conference_id2 ;
2010-02-24 12:01:31 +00:00
/* ... */
} ;
/* CallInfoMessage */
# define CALL_INFO_MESSAGE 0x008F
struct call_info_message {
char calling_party_name [ 40 ] ;
char calling_party [ 24 ] ;
char called_party_name [ 40 ] ;
char called_party [ 24 ] ;
uint32_t line_instance ;
uint32_t call_id ;
uint32_t call_type ; /* See enum skinny_call_type */
char original_called_party_name [ 40 ] ;
char original_called_party [ 24 ] ;
char last_redirecting_party_name [ 40 ] ;
char last_redirecting_party [ 24 ] ;
uint32_t original_called_party_redirect_reason ;
uint32_t last_redirecting_reason ;
char calling_party_voice_mailbox [ 24 ] ;
char called_party_voice_mailbox [ 24 ] ;
char original_called_party_voice_mailbox [ 24 ] ;
char last_redirecting_voice_mailbox [ 24 ] ;
uint32_t call_instance ;
uint32_t call_security_status ;
uint32_t party_pi_restriction_bits ;
} ;
2010-02-24 12:01:47 +00:00
enum skinny_call_type {
SKINNY_INBOUND_CALL = 1 ,
SKINNY_OUTBOUND_CALL = 2 ,
SKINNY_FORWARD_CALL = 3 ,
} ;
2010-02-24 12:00:55 +00:00
/* SpeedDialStatMessage */
# define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
struct speed_dial_stat_res_message {
uint32_t number ;
char line [ 24 ] ;
char label [ 40 ] ;
} ;
2010-02-24 11:59:02 +00:00
/* LineStatMessage */
# define LINE_STAT_RES_MESSAGE 0x0092
struct line_stat_res_message {
uint32_t number ;
char name [ 24 ] ;
char shortname [ 40 ] ;
char displayname [ 44 ] ;
} ;
2010-02-24 12:03:46 +00:00
/* ConfigStatMessage */
# define CONFIG_STAT_RES_MESSAGE 0x0093
struct config_stat_res_message {
char device_name [ 16 ] ;
uint32_t user_id ;
uint32_t instance ;
char user_name [ 40 ] ;
char server_name [ 40 ] ;
uint32_t number_lines ;
uint32_t number_speed_dials ;
} ;
2010-02-24 12:01:39 +00:00
/* DefineTimeDate */
# define DEFINE_TIME_DATE_MESSAGE 0x0094
struct define_time_date_message {
uint32_t year ;
uint32_t month ;
uint32_t day_of_week ; /* monday = 1 */
uint32_t day ;
uint32_t hour ;
uint32_t minute ;
uint32_t seconds ;
uint32_t milliseconds ;
uint32_t timestamp ;
} ;
2010-02-24 12:00:45 +00:00
/* ButtonTemplateMessage */
# define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
struct button_definition {
uint8_t instance_number ;
2010-02-24 12:01:47 +00:00
uint8_t button_definition ; /* See enum skinny_button_definition */
} ;
enum skinny_button_definition {
SKINNY_BUTTON_SPEED_DIAL = 0x02 ,
SKINNY_BUTTON_LINE = 0x09 ,
SKINNY_BUTTON_VOICEMAIL = 0x0F ,
SKINNY_BUTTON_UNDEFINED = 0xFF ,
2010-02-24 12:00:45 +00:00
} ;
2010-02-24 12:04:25 +00:00
# define SKINNY_MAX_BUTTON_COUNT 42
2010-02-24 12:00:45 +00:00
struct button_template_message {
uint32_t button_offset ;
uint32_t button_count ;
uint32_t total_button_count ;
2010-02-24 12:04:25 +00:00
struct button_definition btn [ SKINNY_MAX_BUTTON_COUNT ] ;
2010-02-24 12:00:45 +00:00
} ;
2010-02-24 11:58:36 +00:00
/* CapabilitiesReqMessage */
# define CAPABILITIES_REQ_MESSAGE 0x009B
2010-02-24 11:59:02 +00:00
/* RegisterRejectMessage */
# define REGISTER_REJ_MESSAGE 0x009D
struct register_rej_message {
char error [ 33 ] ;
} ;
2010-02-24 11:58:36 +00:00
/* KeepAliveAckMessage */
2010-02-24 11:58:07 +00:00
# define KEEP_ALIVE_ACK_MESSAGE 0x0100
2010-02-24 12:01:31 +00:00
/* OpenReceiveChannelMessage */
# define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
struct open_receive_channel_message {
uint32_t conference_id ;
uint32_t pass_thru_party_id ;
uint32_t packets ;
uint32_t payload_capacity ;
uint32_t echo_cancel_type ;
uint32_t g723_bitrate ;
2010-02-24 12:01:47 +00:00
uint32_t conference_id2 ;
uint32_t reserved [ 10 ] ;
} ;
/* CloseReceiveChannelMessage */
# define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
struct close_receive_channel_message {
uint32_t conference_id ;
uint32_t pass_thru_party_id ;
uint32_t conference_id2 ;
2010-02-24 12:01:31 +00:00
} ;
2010-02-24 12:00:45 +00:00
/* SoftKeyTemplateResMessage */
# define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
struct soft_key_template_definition {
char soft_key_label [ 16 ] ;
uint32_t soft_key_event ;
} ;
struct soft_key_template_res_message {
uint32_t soft_key_offset ;
uint32_t soft_key_count ;
uint32_t total_soft_key_count ;
struct soft_key_template_definition soft_key [ 32 ] ;
} ;
2010-02-24 12:03:59 +00:00
# define SOFTKEY_NONE 0x00
# define SOFTKEY_REDIAL 0x01
# define SOFTKEY_NEWCALL 0x02
# define SOFTKEY_HOLD 0x03
# define SOFTKEY_TRNSFER 0x04
# define SOFTKEY_CFWDALL 0x05
# define SOFTKEY_CFWDBUSY 0x06
# define SOFTKEY_CFWDNOANSWER 0x07
# define SOFTKEY_BKSPC 0x08
# define SOFTKEY_ENDCALL 0x09
# define SOFTKEY_RESUME 0x0A
# define SOFTKEY_ANSWER 0x0B
# define SOFTKEY_INFO 0x0C
# define SOFTKEY_CONFRN 0x0D
# define SOFTKEY_PARK 0x0E
# define SOFTKEY_JOIN 0x0F
# define SOFTKEY_MEETME 0x10
# define SOFTKEY_PICKUP 0x11
# define SOFTKEY_GPICKUP 0x12
# define SOFTKEY_DND 0x13
# define SOFTKEY_IDIVERT 0x14
static struct soft_key_template_definition soft_key_template_default [ ] = {
{ " \200 \001 " , SOFTKEY_REDIAL } ,
{ " \200 \002 " , SOFTKEY_NEWCALL } ,
{ " \200 \003 " , SOFTKEY_HOLD } ,
{ " \200 \004 " , SOFTKEY_TRNSFER } ,
{ " \200 \005 " , SOFTKEY_CFWDALL } ,
{ " \200 \006 " , SOFTKEY_CFWDBUSY } ,
{ " \200 \007 " , SOFTKEY_CFWDNOANSWER } ,
{ " \200 \010 " , SOFTKEY_BKSPC } ,
{ " \200 \011 " , SOFTKEY_ENDCALL } ,
{ " \200 \012 " , SOFTKEY_RESUME } ,
{ " \200 \013 " , SOFTKEY_ANSWER } ,
{ " \200 \014 " , SOFTKEY_INFO } ,
{ " \200 \015 " , SOFTKEY_CONFRN } ,
{ " \200 \016 " , SOFTKEY_PARK } ,
{ " \200 \017 " , SOFTKEY_JOIN } ,
{ " \200 \020 " , SOFTKEY_MEETME } ,
{ " \200 \021 " , SOFTKEY_PICKUP } ,
{ " \200 \022 " , SOFTKEY_GPICKUP } ,
{ " \200 \077 " , SOFTKEY_DND } ,
{ " \200 \120 " , SOFTKEY_IDIVERT } ,
} ;
2010-02-24 12:00:45 +00:00
/* SoftKeySetResMessage */
# define SOFT_KEY_SET_RES_MESSAGE 0x0109
struct soft_key_set_definition {
uint8_t soft_key_template_index [ 16 ] ;
uint16_t soft_key_info_index [ 16 ] ;
} ;
struct soft_key_set_res_message {
uint32_t soft_key_set_offset ;
uint32_t soft_key_set_count ;
uint32_t total_soft_key_set_count ;
struct soft_key_set_definition soft_key_set [ 16 ] ;
uint32_t res ;
} ;
2010-02-24 12:01:31 +00:00
/* SelectSoftKeysMessage */
# define SELECT_SOFT_KEYS_MESSAGE 0x0110
struct select_soft_keys_message {
uint32_t line_instance ;
uint32_t call_id ;
uint32_t soft_key_set ; /* See enum skinny_key_set */
2010-02-24 12:01:47 +00:00
uint32_t valid_key_mask ;
2010-02-24 12:01:31 +00:00
} ;
enum skinny_key_set {
SKINNY_KEY_SET_ON_HOOK = 0 ,
SKINNY_KEY_SET_CONNECTED = 1 ,
SKINNY_KEY_SET_ON_HOLD = 2 ,
SKINNY_KEY_SET_RING_IN = 3 ,
SKINNY_KEY_SET_OFF_HOOK = 4 ,
SKINNY_KEY_SET_CONNECTED_WITH_TRANSFER = 5 ,
SKINNY_KEY_SET_DIGITS_AFTER_DIALING_FIRST_DIGIT = 6 ,
SKINNY_KEY_SET_CONNECTED_WITH_CONFERENCE = 7 ,
SKINNY_KEY_SET_RING_OUT = 8 ,
SKINNY_KEY_SET_OFF_HOOK_WITH_FEATURES = 9 ,
} ;
/* CallStateMessage */
# define CALL_STATE_MESSAGE 0x0111
struct call_state_message {
2010-02-24 12:01:47 +00:00
uint32_t call_state ; /* See enum skinny_call_state */
2010-02-24 12:01:31 +00:00
uint32_t line_instance ;
uint32_t call_id ;
} ;
2010-02-24 12:01:47 +00:00
enum skinny_call_state {
SKINNY_OFF_HOOK = 1 ,
SKINNY_ON_HOOK = 2 ,
SKINNY_RING_OUT = 3 ,
SKINNY_RING_IN = 4 ,
SKINNY_CONNECTED = 5 ,
SKINNY_BUSY = 6 ,
SKINNY_CONGESTION = 7 ,
SKINNY_HOLD = 8 ,
SKINNY_CALL_WAITING = 9 ,
SKINNY_CALL_TRANSFER = 10 ,
SKINNY_CALL_PARK = 11 ,
SKINNY_PROCEED = 12 ,
SKINNY_CALL_REMOTE_MULTILINE = 13 ,
SKINNY_INVALID_NUMBER = 14
} ;
2010-02-24 12:01:31 +00:00
/* DisplayPromptStatusMessage */
# define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
struct display_prompt_status_message {
uint32_t timeout ;
char display [ 32 ] ;
uint32_t line_instance ;
uint32_t call_id ;
} ;
2010-02-24 12:01:47 +00:00
/* ClearPromptStatusMessage */
# define CLEAR_PROMPT_STATUS_MESSAGE 0x0113
struct clear_prompt_status_message {
2010-02-24 12:01:31 +00:00
uint32_t line_instance ;
uint32_t call_id ;
} ;
/* ActivateCallPlaneMessage */
# define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
struct activate_call_plane_message {
uint32_t line_instance ;
} ;
/* DialedNumberMessage */
# define DIALED_NUMBER_MESSAGE 0x011D
struct dialed_number_message {
char called_party [ 24 ] ;
uint32_t line_instance ;
uint32_t call_id ;
} ;
2010-02-24 11:59:02 +00:00
/* Message */
2010-02-24 11:57:45 +00:00
# define SKINNY_MESSAGE_FIELD_SIZE 4 /* 4-bytes field */
# define SKINNY_MESSAGE_HEADERSIZE 12 /* three 4-bytes fields */
# define SKINNY_MESSAGE_MAXSIZE 1000
union skinny_data {
2010-02-24 11:58:00 +00:00
struct register_message reg ;
2010-02-24 12:01:31 +00:00
struct keypad_button_message keypad_button ;
struct stimulus_message stimulus ;
2010-02-24 12:01:47 +00:00
struct off_hook_message off_hook ;
2010-02-24 12:01:31 +00:00
struct on_hook_message on_hook ;
2010-02-24 12:00:55 +00:00
struct speed_dial_stat_req_message speed_dial_req ;
2010-02-24 11:59:02 +00:00
struct line_stat_req_message line_req ;
2010-02-24 11:58:36 +00:00
struct capabilities_res_message cap_res ;
2010-02-24 12:00:35 +00:00
struct alarm_message alarm ;
2010-02-24 12:01:31 +00:00
struct open_receive_channel_ack_message open_receive_channel_ack ;
struct soft_key_event_message soft_key_event ;
struct headset_status_message headset_status ;
struct register_available_lines_message reg_lines ;
struct register_ack_message reg_ack ;
struct start_tone_message start_tone ;
struct stop_tone_message stop_tone ;
struct set_ringer_message ringer ;
struct set_lamp_message lamp ;
struct set_speaker_mode_message speaker_mode ;
struct start_media_transmission_message start_media ;
struct stop_media_transmission_message stop_media ;
struct call_info_message call_info ;
2010-02-24 12:00:55 +00:00
struct speed_dial_stat_res_message speed_dial_res ;
2010-02-24 11:59:02 +00:00
struct line_stat_res_message line_res ;
2010-02-24 12:03:46 +00:00
struct config_stat_res_message config_res ;
2010-02-24 12:01:39 +00:00
struct define_time_date_message define_time_date ;
2010-02-24 12:00:45 +00:00
struct button_template_message button_template ;
2010-02-24 11:59:02 +00:00
struct register_rej_message reg_rej ;
2010-02-24 12:01:31 +00:00
struct open_receive_channel_message open_receive_channel ;
2010-02-24 12:01:47 +00:00
struct close_receive_channel_message close_receive_channel ;
2010-02-24 12:00:45 +00:00
struct soft_key_template_res_message soft_key_template ;
struct soft_key_set_res_message soft_key_set ;
2010-02-24 12:01:31 +00:00
struct select_soft_keys_message select_soft_keys ;
struct call_state_message call_state ;
struct display_prompt_status_message display_prompt_status ;
2010-02-24 12:01:47 +00:00
struct clear_prompt_status_message clear_prompt_status ;
2010-02-24 12:01:31 +00:00
struct activate_call_plane_message activate_call_plane ;
struct dialed_number_message dialed_number ;
2010-02-24 11:58:00 +00:00
2010-02-24 11:58:25 +00:00
uint16_t as_uint16 ;
2010-02-24 11:58:00 +00:00
char as_char ;
2010-02-24 11:57:45 +00:00
void * raw ;
} ;
2010-02-24 12:00:28 +00:00
/*
* header is length + reserved
* body is type + data
*/
2010-02-24 11:57:45 +00:00
struct skinny_message {
int length ;
int reserved ;
int type ;
union skinny_data data ;
} ;
typedef struct skinny_message skinny_message_t ;
2010-02-24 11:59:02 +00:00
/*****************************************************************************/
/* SKINNY TYPES */
/*****************************************************************************/
2010-02-24 11:58:15 +00:00
typedef switch_status_t ( * skinny_command_t ) ( char * * argv , int argc , switch_stream_handle_t * stream ) ;
2010-02-24 11:58:36 +00:00
enum skinny_codecs {
2010-02-24 11:59:02 +00:00
SKINNY_CODEC_ALAW_64K = 2 ,
SKINNY_CODEC_ALAW_56K = 3 ,
SKINNY_CODEC_ULAW_64K = 4 ,
SKINNY_CODEC_ULAW_56K = 5 ,
SKINNY_CODEC_G722_64K = 6 ,
SKINNY_CODEC_G722_56K = 7 ,
SKINNY_CODEC_G722_48K = 8 ,
2010-02-24 11:58:36 +00:00
SKINNY_CODEC_G723_1 = 9 ,
2010-02-24 11:59:02 +00:00
SKINNY_CODEC_G728 = 10 ,
SKINNY_CODEC_G729 = 11 ,
2010-02-24 11:58:36 +00:00
SKINNY_CODEC_G729A = 12 ,
2010-02-24 11:59:02 +00:00
SKINNY_CODEC_IS11172 = 13 ,
SKINNY_CODEC_IS13818 = 14 ,
SKINNY_CODEC_G729B = 15 ,
SKINNY_CODEC_G729AB = 16 ,
SKINNY_CODEC_GSM_FULL = 18 ,
SKINNY_CODEC_GSM_HALF = 19 ,
SKINNY_CODEC_GSM_EFULL = 20 ,
SKINNY_CODEC_WIDEBAND_256K = 25 ,
SKINNY_CODEC_DATA_64K = 32 ,
SKINNY_CODEC_DATA_56K = 33 ,
SKINNY_CODEC_GSM = 80 ,
SKINNY_CODEC_ACTIVEVOICE = 81 ,
SKINNY_CODEC_G726_32K = 82 ,
SKINNY_CODEC_G726_24K = 83 ,
SKINNY_CODEC_G726_16K = 84 ,
SKINNY_CODEC_G729B_BIS = 85 ,
SKINNY_CODEC_G729B_LOW = 86 ,
2010-02-24 11:58:36 +00:00
SKINNY_CODEC_H261 = 100 ,
2010-02-24 11:59:02 +00:00
SKINNY_CODEC_H263 = 101 ,
SKINNY_CODEC_VIDEO = 102 ,
SKINNY_CODEC_T120 = 105 ,
SKINNY_CODEC_H224 = 106 ,
SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257
2010-02-24 11:58:36 +00:00
} ;
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
/* LISTENERS TYPES */
/*****************************************************************************/
typedef enum {
LFLAG_RUNNING = ( 1 < < 0 ) ,
} event_flag_t ;
struct listener {
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
2010-02-24 11:59:49 +00:00
char device_name [ 16 ] ;
2010-02-24 12:04:25 +00:00
switch_core_session_t * session [ SKINNY_MAX_BUTTON_COUNT ] ;
2010-02-24 11:59:41 +00:00
2010-02-24 11:57:45 +00:00
switch_socket_t * sock ;
switch_memory_pool_t * pool ;
switch_thread_rwlock_t * rwlock ;
switch_sockaddr_t * sa ;
char remote_ip [ 50 ] ;
switch_mutex_t * flag_mutex ;
uint32_t flags ;
switch_port_t remote_port ;
uint32_t id ;
time_t expire_time ;
struct listener * next ;
} ;
typedef struct listener listener_t ;
2010-02-24 11:58:15 +00:00
typedef switch_status_t ( * skinny_listener_callback_func_t ) ( listener_t * listener , void * pvt ) ;
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
2010-02-24 11:58:07 +00:00
/* FUNCTIONS */
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
2010-02-24 12:04:25 +00:00
/* SQL FUNCTIONS */
static void skinny_execute_sql ( skinny_profile_t * profile , char * sql , switch_mutex_t * mutex ) ;
static switch_bool_t skinny_execute_sql_callback ( skinny_profile_t * profile ,
switch_mutex_t * mutex , char * sql , switch_core_db_callback_func_t callback , void * pdata ) ;
2010-02-24 11:58:07 +00:00
/* CHANNEL FUNCTIONS */
2010-02-24 11:57:37 +00:00
static switch_status_t channel_on_init ( switch_core_session_t * session ) ;
static switch_status_t channel_on_hangup ( switch_core_session_t * session ) ;
static switch_status_t channel_on_destroy ( switch_core_session_t * session ) ;
static switch_status_t channel_on_routing ( switch_core_session_t * session ) ;
static switch_status_t channel_on_exchange_media ( switch_core_session_t * session ) ;
static switch_status_t channel_on_soft_execute ( switch_core_session_t * session ) ;
static switch_call_cause_t channel_outgoing_channel ( switch_core_session_t * session , switch_event_t * var_event ,
switch_caller_profile_t * outbound_profile ,
switch_core_session_t * * new_session , switch_memory_pool_t * * pool , switch_originate_flag_t flags , switch_call_cause_t * cancel_cause ) ;
static switch_status_t channel_read_frame ( switch_core_session_t * session , switch_frame_t * * frame , switch_io_flag_t flags , int stream_id ) ;
static switch_status_t channel_write_frame ( switch_core_session_t * session , switch_frame_t * frame , switch_io_flag_t flags , int stream_id ) ;
static switch_status_t channel_kill_channel ( switch_core_session_t * session , int sig ) ;
2010-02-24 11:58:36 +00:00
/* SKINNY FUNCTIONS */
2010-02-24 12:00:28 +00:00
# define skinny_check_data_length(message, len) \
if ( message - > length < len + 4 ) { \
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Received Too Short Skinny Message (Expected %d, got %d). \n " , len + 4 , message - > length ) ; \
return SWITCH_STATUS_FALSE ; \
}
2010-02-24 12:03:04 +00:00
static switch_status_t start_tone ( listener_t * listener ,
uint32_t tone ,
uint32_t reserved ,
uint32_t line_instance ,
uint32_t call_id ) ;
static switch_status_t stop_tone ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id ) ;
static switch_status_t set_ringer ( listener_t * listener ,
uint32_t ring_type ,
uint32_t ring_mode ,
uint32_t unknown ) ;
static switch_status_t set_lamp ( listener_t * listener ,
uint32_t stimulus ,
uint32_t stimulus_instance ,
uint32_t mode ) ;
static switch_status_t set_speaker_mode ( listener_t * listener ,
uint32_t mode ) ;
static switch_status_t start_media_transmission ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t remote_ip ,
uint32_t remote_port ,
uint32_t ms_per_packet ,
uint32_t payload_capacity ,
uint32_t precedence ,
uint32_t silence_suppression ,
uint16_t max_frames_per_packet ,
uint32_t g723_bitrate ) ;
static switch_status_t stop_media_transmission ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t conference_id2 ) ;
static switch_status_t send_call_info ( listener_t * listener ,
char calling_party_name [ 40 ] ,
char calling_party [ 24 ] ,
char called_party_name [ 40 ] ,
char called_party [ 24 ] ,
uint32_t line_instance ,
uint32_t call_id ,
uint32_t call_type ,
char original_called_party_name [ 40 ] ,
char original_called_party [ 24 ] ,
char last_redirecting_party_name [ 40 ] ,
char last_redirecting_party [ 24 ] ,
uint32_t original_called_party_redirect_reason ,
uint32_t last_redirecting_reason ,
char calling_party_voice_mailbox [ 24 ] ,
char called_party_voice_mailbox [ 24 ] ,
char original_called_party_voice_mailbox [ 24 ] ,
char last_redirecting_voice_mailbox [ 24 ] ,
uint32_t call_instance ,
uint32_t call_security_status ,
uint32_t party_pi_restriction_bits ) ;
static switch_status_t open_receive_channel ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t packets ,
uint32_t payload_capacity ,
uint32_t echo_cancel_type ,
uint32_t g723_bitrate ,
uint32_t conference_id2 ,
uint32_t reserved [ 10 ] ) ;
static switch_status_t close_receive_channel ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t conference_id2 ) ;
static switch_status_t send_select_soft_keys ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id ,
uint32_t soft_key_set ,
uint32_t valid_key_mask ) ;
static switch_status_t send_call_state ( listener_t * listener ,
uint32_t call_state ,
uint32_t line_instance ,
uint32_t call_id ) ;
static switch_status_t display_prompt_status ( listener_t * listener ,
uint32_t timeout ,
char display [ 32 ] ,
uint32_t line_instance ,
uint32_t call_id ) ;
static switch_status_t clear_prompt_status ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id ) ;
static switch_status_t activate_call_plane ( listener_t * listener ,
uint32_t line_instance ) ;
static switch_status_t send_dialed_number ( listener_t * listener ,
char called_party [ 24 ] ,
uint32_t line_instance ,
uint32_t call_id ) ;
2010-02-24 11:58:36 +00:00
static switch_status_t skinny_send_reply ( listener_t * listener , skinny_message_t * reply ) ;
2010-02-24 11:58:07 +00:00
/* LISTENER FUNCTIONS */
2010-02-24 11:58:15 +00:00
static switch_status_t keepalive_listener ( listener_t * listener , void * pvt ) ;
2010-02-24 11:58:07 +00:00
2010-02-24 11:59:49 +00:00
/*****************************************************************************/
/* PROFILES FUNCTIONS */
/*****************************************************************************/
static switch_status_t dump_profile ( const skinny_profile_t * profile , switch_stream_handle_t * stream )
{
const char * line = " ================================================================================================= " ;
switch_assert ( profile ) ;
stream - > write_function ( stream , " %s \n " , line ) ;
2010-02-24 12:00:11 +00:00
/* prefs */
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " Name \t %s \n " , profile - > name ) ;
stream - > write_function ( stream , " Domain Name \t %s \n " , profile - > domain ) ;
stream - > write_function ( stream , " IP \t %s \n " , profile - > ip ) ;
stream - > write_function ( stream , " Port \t %d \n " , profile - > port ) ;
stream - > write_function ( stream , " Dialplan \t %s \n " , profile - > dialplan ) ;
stream - > write_function ( stream , " Keep-Alive \t %d \n " , profile - > keep_alive ) ;
stream - > write_function ( stream , " Date-Format \t %s \n " , profile - > date_format ) ;
2010-02-24 12:00:11 +00:00
/* db */
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " DBName \t %s \n " , profile - > dbname ? profile - > dbname : switch_str_nil ( profile - > odbc_dsn ) ) ;
2010-02-24 12:00:11 +00:00
/* stats */
stream - > write_function ( stream , " CALLS-IN \t %d \n " , profile - > ib_calls ) ;
stream - > write_function ( stream , " FAILED-CALLS-IN \t %d \n " , profile - > ib_failed_calls ) ;
stream - > write_function ( stream , " CALLS-OUT \t %d \n " , profile - > ob_calls ) ;
stream - > write_function ( stream , " FAILED-CALLS-OUT \t %d \n " , profile - > ob_failed_calls ) ;
/* listener */
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " Listener-Threads \t %d \n " , profile - > listener_threads ) ;
stream - > write_function ( stream , " %s \n " , line ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:00:11 +00:00
static skinny_profile_t * skinny_find_profile ( const char * profile_name )
2010-02-24 11:59:49 +00:00
{
return ( skinny_profile_t * ) switch_core_hash_find ( globals . profile_hash , profile_name ) ;
}
2010-02-24 12:04:25 +00:00
struct skinny_profile_find_listener_helper {
skinny_profile_t * profile ;
listener_t * listener ;
uint32_t line ;
} ;
static int skinny_profile_find_listener_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
struct skinny_profile_find_listener_helper * helper = pArg ;
skinny_profile_t * profile = helper - > profile ;
char * device_name = argv [ 0 ] ;
/* uint32_t position = atoi(argv[1]); */
uint32_t relative_position = atoi ( argv [ 2 ] ) ;
switch_mutex_lock ( profile - > listener_mutex ) ;
for ( listener_t * l = profile - > listeners ; l ; l = l - > next ) {
if ( ! strcmp ( l - > device_name , device_name ) ) {
helper - > listener = l ;
}
}
switch_mutex_unlock ( profile - > listener_mutex ) ;
if ( helper - > listener ) {
helper - > line = relative_position ;
}
return 0 ;
}
static switch_status_t skinny_profile_find_listener ( skinny_profile_t * profile , const char * dest , listener_t * * l , uint32_t * line )
{
char * sql ;
struct skinny_profile_find_listener_helper helper = { 0 } ;
helper . profile = profile ;
if ( ( sql = switch_mprintf ( " SELECT device_name, position, "
" (SELECT count(*) from skinny_buttons sb2 "
" WHERE sb2.device_name= sb1.device_name AND sb2.type='line' AND sb2.position <= sb1.position) AS relative_position "
" FROM skinny_buttons sb1 WHERE type='line' and value='%s' " ,
dest ) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_profile_find_listener_callback , & helper ) ;
switch_safe_free ( sql ) ;
}
* line = helper . line ;
* l = helper . listener ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:41 +00:00
/*****************************************************************************/
/* SQL FUNCTIONS */
/*****************************************************************************/
static void skinny_execute_sql ( skinny_profile_t * profile , char * sql , switch_mutex_t * mutex )
{
switch_core_db_t * db ;
if ( mutex ) {
switch_mutex_lock ( mutex ) ;
}
if ( switch_odbc_available ( ) & & profile - > odbc_dsn ) {
switch_odbc_statement_handle_t stmt ;
2010-02-24 11:59:56 +00:00
if ( switch_odbc_handle_exec ( profile - > master_odbc , sql , & stmt , NULL ) ! = SWITCH_ODBC_SUCCESS ) {
2010-02-24 11:59:41 +00:00
char * err_str ;
err_str = switch_odbc_handle_get_error ( profile - > master_odbc , stmt ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " ERR: [%s] \n [%s] \n " , sql , switch_str_nil ( err_str ) ) ;
switch_safe_free ( err_str ) ;
}
switch_odbc_statement_handle_free ( & stmt ) ;
} else {
if ( ! ( db = switch_core_db_open_file ( profile - > dbname ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB %s \n " , profile - > dbname ) ;
goto end ;
}
2010-02-24 11:59:49 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SQL: %s \n " , sql ) ;
2010-02-24 11:59:41 +00:00
switch_core_db_persistant_execute ( db , sql , 1 ) ;
switch_core_db_close ( db ) ;
}
end :
if ( mutex ) {
switch_mutex_unlock ( mutex ) ;
}
}
static switch_bool_t skinny_execute_sql_callback ( skinny_profile_t * profile ,
switch_mutex_t * mutex , char * sql , switch_core_db_callback_func_t callback , void * pdata )
{
switch_bool_t ret = SWITCH_FALSE ;
switch_core_db_t * db ;
char * errmsg = NULL ;
if ( mutex ) {
switch_mutex_lock ( mutex ) ;
}
if ( switch_odbc_available ( ) & & profile - > odbc_dsn ) {
switch_odbc_handle_callback_exec ( profile - > master_odbc , sql , callback , pdata , NULL ) ;
} else {
if ( ! ( db = switch_core_db_open_file ( profile - > dbname ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB %s \n " , profile - > dbname ) ;
goto end ;
}
2010-02-24 11:59:49 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SQL: %s \n " , sql ) ;
2010-02-24 11:59:41 +00:00
switch_core_db_exec ( db , sql , callback , pdata , & errmsg ) ;
if ( errmsg ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SQL ERR: [%s] %s \n " , sql , errmsg ) ;
free ( errmsg ) ;
}
if ( db ) {
switch_core_db_close ( db ) ;
}
}
end :
if ( mutex ) {
switch_mutex_unlock ( mutex ) ;
}
return ret ;
}
2010-02-24 11:58:07 +00:00
/*****************************************************************************/
/* CHANNEL FUNCTIONS */
/*****************************************************************************/
2010-02-24 12:04:08 +00:00
static switch_status_t skinny_tech_set_codec ( private_t * tech_pvt , int force )
{
int ms ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
int resetting = 0 ;
if ( ! tech_pvt - > iananame ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_ERROR , " No audio codec available \n " ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
if ( switch_core_codec_ready ( & tech_pvt - > read_codec ) ) {
if ( ! force ) {
switch_goto_status ( SWITCH_STATUS_SUCCESS , end ) ;
}
if ( strcasecmp ( tech_pvt - > read_impl . iananame , tech_pvt - > iananame ) | |
tech_pvt - > read_impl . samples_per_second ! = tech_pvt - > rm_rate | |
tech_pvt - > codec_ms ! = ( uint32_t ) tech_pvt - > read_impl . microseconds_per_packet / 1000 ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_DEBUG , " Changing Codec from %s@%dms to %s@%dms \n " ,
tech_pvt - > read_impl . iananame , tech_pvt - > read_impl . microseconds_per_packet / 1000 ,
tech_pvt - > rm_encoding , tech_pvt - > codec_ms ) ;
switch_core_session_lock_codec_write ( tech_pvt - > session ) ;
switch_core_session_lock_codec_read ( tech_pvt - > session ) ;
resetting = 1 ;
switch_core_codec_destroy ( & tech_pvt - > read_codec ) ;
switch_core_codec_destroy ( & tech_pvt - > write_codec ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_DEBUG , " Already using %s \n " , tech_pvt - > read_impl . iananame ) ;
switch_goto_status ( SWITCH_STATUS_SUCCESS , end ) ;
}
}
if ( switch_core_codec_init ( & tech_pvt - > read_codec ,
tech_pvt - > iananame ,
tech_pvt - > rm_fmtp ,
tech_pvt - > rm_rate ,
tech_pvt - > codec_ms ,
1 ,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt->profile->codec_flags */ ,
NULL , switch_core_session_get_pool ( tech_pvt - > session ) ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_ERROR , " Can't load codec? \n " ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
if ( switch_core_codec_init ( & tech_pvt - > write_codec ,
tech_pvt - > iananame ,
tech_pvt - > rm_fmtp ,
tech_pvt - > rm_rate ,
tech_pvt - > codec_ms ,
1 ,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt->profile->codec_flags */ ,
NULL , switch_core_session_get_pool ( tech_pvt - > session ) ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_ERROR , " Can't load codec? \n " ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
switch_assert ( tech_pvt - > read_codec . implementation ) ;
switch_assert ( tech_pvt - > write_codec . implementation ) ;
tech_pvt - > read_impl = * tech_pvt - > read_codec . implementation ;
tech_pvt - > write_impl = * tech_pvt - > write_codec . implementation ;
switch_core_session_set_read_impl ( tech_pvt - > session , tech_pvt - > read_codec . implementation ) ;
switch_core_session_set_write_impl ( tech_pvt - > session , tech_pvt - > write_codec . implementation ) ;
if ( switch_rtp_ready ( tech_pvt - > rtp_session ) ) {
switch_assert ( tech_pvt - > read_codec . implementation ) ;
if ( switch_rtp_change_interval ( tech_pvt - > rtp_session ,
tech_pvt - > read_impl . microseconds_per_packet ,
tech_pvt - > read_impl . samples_per_packet
) ! = SWITCH_STATUS_SUCCESS ) {
/* TODO
switch_channel_hangup ( tech_pvt - > channel , SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ) ;
*/
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
}
tech_pvt - > read_frame . rate = tech_pvt - > rm_rate ;
ms = tech_pvt - > write_codec . implementation - > microseconds_per_packet / 1000 ;
if ( ! switch_core_codec_ready ( & tech_pvt - > read_codec ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_ERROR , " Can't load codec? \n " ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_DEBUG , " Set Codec %s %s/%ld %d ms %d samples \n " ,
" " /* TODO switch_channel_get_name(tech_pvt->channel)*/ , tech_pvt - > iananame , tech_pvt - > rm_rate , tech_pvt - > codec_ms ,
tech_pvt - > read_impl . samples_per_packet ) ;
tech_pvt - > read_frame . codec = & tech_pvt - > read_codec ;
tech_pvt - > write_codec . agreed_pt = tech_pvt - > agreed_pt ;
tech_pvt - > read_codec . agreed_pt = tech_pvt - > agreed_pt ;
if ( force ! = 2 ) {
switch_core_session_set_read_codec ( tech_pvt - > session , & tech_pvt - > read_codec ) ;
switch_core_session_set_write_codec ( tech_pvt - > session , & tech_pvt - > write_codec ) ;
}
/* TODO
tech_pvt - > fmtp_out = switch_core_session_strdup ( tech_pvt - > session , tech_pvt - > write_codec . fmtp_out ) ;
*/
/* TODO
if ( switch_rtp_ready ( tech_pvt - > rtp_session ) ) {
switch_rtp_set_default_payload ( tech_pvt - > rtp_session , tech_pvt - > pt ) ;
}
*/
end :
if ( resetting ) {
switch_core_session_unlock_codec_write ( tech_pvt - > session ) ;
switch_core_session_unlock_codec_read ( tech_pvt - > session ) ;
}
return status ;
}
2010-02-24 11:57:37 +00:00
static void tech_init ( private_t * tech_pvt , switch_core_session_t * session )
{
tech_pvt - > read_frame . data = tech_pvt - > databuf ;
tech_pvt - > read_frame . buflen = sizeof ( tech_pvt - > databuf ) ;
switch_mutex_init ( & tech_pvt - > mutex , SWITCH_MUTEX_NESTED , switch_core_session_get_pool ( session ) ) ;
switch_mutex_init ( & tech_pvt - > flag_mutex , SWITCH_MUTEX_NESTED , switch_core_session_get_pool ( session ) ) ;
2010-02-24 12:04:08 +00:00
tech_pvt - > call_id = 12345 ; /* TODO */
2010-02-24 11:57:37 +00:00
switch_core_session_set_private ( session , tech_pvt ) ;
tech_pvt - > session = session ;
}
/*
State methods they get called when the state changes to the specific state
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it .
*/
static switch_status_t channel_on_init ( switch_core_session_t * session )
{
switch_channel_t * channel ;
private_t * tech_pvt = NULL ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
switch_set_flag_locked ( tech_pvt , TFLAG_IO ) ;
/* Move channel's state machine to ROUTING. This means the call is trying
to get from the initial start where the call because , to the point
where a destination has been identified . If the channel is simply
left in the initial state , nothing will happen . */
switch_channel_set_state ( channel , CS_ROUTING ) ;
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( globals . calls_mutex ) ;
2010-02-24 11:57:37 +00:00
globals . calls + + ;
2010-02-24 11:59:41 +00:00
switch_mutex_unlock ( globals . calls_mutex ) ;
2010-02-24 11:57:37 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL INIT \n " , switch_channel_get_name ( channel ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_routing ( switch_core_session_t * session )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL ROUTING \n " , switch_channel_get_name ( channel ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_execute ( switch_core_session_t * session )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL EXECUTE \n " , switch_channel_get_name ( channel ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_destroy ( switch_core_session_t * session )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
if ( tech_pvt ) {
if ( switch_core_codec_ready ( & tech_pvt - > read_codec ) ) {
switch_core_codec_destroy ( & tech_pvt - > read_codec ) ;
}
if ( switch_core_codec_ready ( & tech_pvt - > write_codec ) ) {
switch_core_codec_destroy ( & tech_pvt - > write_codec ) ;
}
}
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL DESTROY \n " , switch_channel_get_name ( channel ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_hangup ( switch_core_session_t * session )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
2010-02-24 12:04:08 +00:00
listener_t * listener = NULL ;
2010-02-24 11:57:37 +00:00
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
2010-02-24 12:04:08 +00:00
listener = tech_pvt - > listener ;
assert ( listener ! = NULL ) ;
2010-02-24 11:57:37 +00:00
switch_clear_flag_locked ( tech_pvt , TFLAG_IO ) ;
switch_clear_flag_locked ( tech_pvt , TFLAG_VOICE ) ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL HANGUP \n " , switch_channel_get_name ( channel ) ) ;
2010-02-24 12:04:08 +00:00
stop_tone ( listener , tech_pvt - > line , tech_pvt - > call_id ) ;
set_lamp ( listener , SKINNY_BUTTON_LINE , tech_pvt - > line , SKINNY_LAMP_OFF ) ;
clear_prompt_status ( listener , tech_pvt - > line , tech_pvt - > call_id ) ;
close_receive_channel ( listener ,
tech_pvt - > call_id , /* uint32_t conference_id, */
tech_pvt - > party_id , /* uint32_t pass_thru_party_id, */
tech_pvt - > call_id /* uint32_t conference_id2, */
) ;
stop_media_transmission ( listener ,
tech_pvt - > call_id , /* uint32_t conference_id, */
tech_pvt - > party_id , /* uint32_t pass_thru_party_id, */
tech_pvt - > call_id /* uint32_t conference_id2, */
) ;
send_call_state ( listener ,
SKINNY_ON_HOOK ,
tech_pvt - > line ,
tech_pvt - > call_id ) ;
send_select_soft_keys ( listener , tech_pvt - > line , tech_pvt - > call_id ,
SKINNY_KEY_SET_ON_HOOK , 0xffff ) ;
/* TODO: DefineTimeDate */
set_speaker_mode ( listener , SKINNY_SPEAKER_OFF ) ;
set_ringer ( listener , SKINNY_RING_OFF , SKINNY_RING_FOREVER , 0 ) ;
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( globals . calls_mutex ) ;
2010-02-24 11:57:37 +00:00
globals . calls - - ;
if ( globals . calls < 0 ) {
globals . calls = 0 ;
}
2010-02-24 11:59:41 +00:00
switch_mutex_unlock ( globals . calls_mutex ) ;
2010-02-24 11:57:37 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_kill_channel ( switch_core_session_t * session , int sig )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
switch ( sig ) {
case SWITCH_SIG_KILL :
switch_clear_flag_locked ( tech_pvt , TFLAG_IO ) ;
switch_clear_flag_locked ( tech_pvt , TFLAG_VOICE ) ;
switch_channel_hangup ( channel , SWITCH_CAUSE_NORMAL_CLEARING ) ;
2010-02-24 11:57:45 +00:00
//switch_thread_cond_sigpnal(tech_pvt->cond);
2010-02-24 11:57:37 +00:00
break ;
case SWITCH_SIG_BREAK :
switch_set_flag_locked ( tech_pvt , TFLAG_BREAK ) ;
break ;
default :
break ;
}
2010-02-24 12:04:08 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " %s CHANNEL KILL %d \n " , switch_channel_get_name ( channel ) , sig ) ;
2010-02-24 11:57:37 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_exchange_media ( switch_core_session_t * session )
{
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " CHANNEL LOOPBACK \n " ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_on_soft_execute ( switch_core_session_t * session )
{
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " CHANNEL TRANSMIT \n " ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_send_dtmf ( switch_core_session_t * session , const switch_dtmf_t * dtmf )
{
private_t * tech_pvt = switch_core_session_get_private ( session ) ;
switch_assert ( tech_pvt ! = NULL ) ;
2010-02-24 12:04:16 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " DTMF ON CALL %d [%c] \n " , tech_pvt - > call_id , dtmf - > digit ) ;
2010-02-24 11:57:37 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_read_frame ( switch_core_session_t * session , switch_frame_t * * frame , switch_io_flag_t flags , int stream_id )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
2010-02-24 12:04:08 +00:00
int payload = 0 ;
2010-02-24 11:57:37 +00:00
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
2010-02-24 12:04:08 +00:00
while ( ! ( tech_pvt - > read_codec . implementation & & switch_rtp_ready ( tech_pvt - > rtp_session ) ) ) {
if ( switch_channel_ready ( channel ) ) {
switch_yield ( 10000 ) ;
} else {
return SWITCH_STATUS_GENERR ;
2010-02-24 11:57:37 +00:00
}
2010-02-24 12:04:08 +00:00
}
tech_pvt - > read_frame . datalen = 0 ;
switch_set_flag_locked ( tech_pvt , TFLAG_READING ) ;
if ( switch_test_flag ( tech_pvt , TFLAG_IO ) ) {
switch_status_t status ;
switch_assert ( tech_pvt - > rtp_session ! = NULL ) ;
tech_pvt - > read_frame . datalen = 0 ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:04:08 +00:00
while ( switch_test_flag ( tech_pvt , TFLAG_IO ) & & tech_pvt - > read_frame . datalen = = 0 ) {
tech_pvt - > read_frame . flags = SFF_NONE ;
status = switch_rtp_zerocopy_read_frame ( tech_pvt - > rtp_session , & tech_pvt - > read_frame , flags ) ;
if ( status ! = SWITCH_STATUS_SUCCESS & & status ! = SWITCH_STATUS_BREAK ) {
return SWITCH_STATUS_FALSE ;
2010-02-24 11:57:37 +00:00
}
2010-02-24 12:04:08 +00:00
payload = tech_pvt - > read_frame . payload ;
if ( switch_rtp_has_dtmf ( tech_pvt - > rtp_session ) ) {
switch_dtmf_t dtmf = { 0 } ;
switch_rtp_dequeue_dtmf ( tech_pvt - > rtp_session , & dtmf ) ;
switch_channel_queue_dtmf ( channel , & dtmf ) ;
2010-02-24 11:57:37 +00:00
}
2010-02-24 12:04:08 +00:00
if ( tech_pvt - > read_frame . datalen > 0 ) {
size_t bytes = 0 ;
int frames = 1 ;
if ( ! switch_test_flag ( ( & tech_pvt - > read_frame ) , SFF_CNG ) ) {
if ( ( bytes = tech_pvt - > read_codec . implementation - > encoded_bytes_per_packet ) ) {
frames = ( tech_pvt - > read_frame . datalen / bytes ) ;
}
tech_pvt - > read_frame . samples = ( int ) ( frames * tech_pvt - > read_codec . implementation - > samples_per_packet ) ;
}
break ;
}
}
2010-02-24 11:57:37 +00:00
}
2010-02-24 12:04:08 +00:00
switch_clear_flag_locked ( tech_pvt , TFLAG_READING ) ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:04:08 +00:00
if ( tech_pvt - > read_frame . datalen = = 0 ) {
* frame = NULL ;
return SWITCH_STATUS_GENERR ;
}
2010-02-24 11:57:37 +00:00
* frame = & tech_pvt - > read_frame ;
2010-02-24 12:04:08 +00:00
return SWITCH_STATUS_SUCCESS ;
2010-02-24 11:57:37 +00:00
}
static switch_status_t channel_write_frame ( switch_core_session_t * session , switch_frame_t * frame , switch_io_flag_t flags , int stream_id )
{
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
//switch_frame_t *pframe;
2010-02-24 12:04:08 +00:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2010-02-24 11:57:37 +00:00
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
if ( ! switch_test_flag ( tech_pvt , TFLAG_IO ) ) {
return SWITCH_STATUS_FALSE ;
}
# if SWITCH_BYTE_ORDER == __BIG_ENDIAN
if ( switch_test_flag ( tech_pvt , TFLAG_LINEAR ) ) {
switch_swap_linear ( frame - > data , ( int ) frame - > datalen / 2 ) ;
}
# endif
2010-02-24 12:04:08 +00:00
switch_set_flag_locked ( tech_pvt , TFLAG_WRITING ) ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:04:08 +00:00
switch_rtp_write_frame ( tech_pvt - > rtp_session , frame ) ;
switch_clear_flag_locked ( tech_pvt , TFLAG_WRITING ) ;
return status ;
2010-02-24 11:57:37 +00:00
}
static switch_status_t channel_answer_channel ( switch_core_session_t * session )
{
private_t * tech_pvt ;
switch_channel_t * channel = NULL ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t channel_receive_message ( switch_core_session_t * session , switch_core_session_message_t * msg )
{
switch_channel_t * channel ;
private_t * tech_pvt ;
channel = switch_core_session_get_channel ( session ) ;
assert ( channel ! = NULL ) ;
tech_pvt = ( private_t * ) switch_core_session_get_private ( session ) ;
assert ( tech_pvt ! = NULL ) ;
switch ( msg - > message_id ) {
case SWITCH_MESSAGE_INDICATE_ANSWER :
{
channel_answer_channel ( session ) ;
}
break ;
default :
break ;
}
return SWITCH_STATUS_SUCCESS ;
}
/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
that allocate memory or you will have 1 channel with memory allocated from another channel ' s pool !
*/
static switch_call_cause_t channel_outgoing_channel ( switch_core_session_t * session , switch_event_t * var_event ,
switch_caller_profile_t * outbound_profile ,
switch_core_session_t * * new_session , switch_memory_pool_t * * pool , switch_originate_flag_t flags , switch_call_cause_t * cancel_cause )
{
2010-02-24 12:00:11 +00:00
switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ;
switch_core_session_t * nsession = NULL ;
private_t * tech_pvt ;
char * profile_name , * dest ;
skinny_profile_t * profile = NULL ;
char name [ 128 ] ;
switch_channel_t * channel ;
switch_caller_profile_t * caller_profile ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
if ( ! outbound_profile | | zstr ( outbound_profile - > destination_number ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Invalid Destination \n " ) ;
goto error ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
if ( ! ( nsession = switch_core_session_request ( skinny_endpoint_interface , SWITCH_CALL_DIRECTION_OUTBOUND , pool ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Error Creating Session \n " ) ;
goto error ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
if ( ! ( tech_pvt = ( struct private_object * ) switch_core_session_alloc ( nsession , sizeof ( * tech_pvt ) ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_CRIT , " Error Creating Session private object \n " ) ;
goto error ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
tech_init ( tech_pvt , nsession ) ;
if ( ! ( profile_name = switch_core_session_strdup ( nsession , outbound_profile - > destination_number ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_CRIT , " Error Creating Session Info \n " ) ;
goto error ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
if ( ! ( dest = strchr ( profile_name , ' / ' ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Invalid Skinny URL. Should be skinny/<profile>/<number>. \n " ) ;
cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT ;
goto error ;
}
* dest + + = ' \0 ' ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
profile = skinny_find_profile ( profile_name ) ;
if ( ! ( profile = skinny_find_profile ( profile_name ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Invalid Profile %s \n " , profile_name ) ;
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER ;
goto error ;
2010-02-24 11:57:37 +00:00
}
2010-02-24 12:00:11 +00:00
snprintf ( name , sizeof ( name ) , " SKINNY/%s/%s " , profile - > name , dest ) ;
channel = switch_core_session_get_channel ( nsession ) ;
switch_channel_set_name ( channel , name ) ;
caller_profile = switch_caller_profile_clone ( nsession , outbound_profile ) ;
switch_channel_set_caller_profile ( channel , caller_profile ) ;
tech_pvt - > caller_profile = caller_profile ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
switch_channel_set_flag ( channel , CF_OUTBOUND ) ;
switch_set_flag_locked ( tech_pvt , TFLAG_OUTBOUND ) ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:04:25 +00:00
if ( ( skinny_profile_find_listener ( profile , dest , & tech_pvt - > listener , & tech_pvt - > line ) ! = SWITCH_STATUS_SUCCESS ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Problem while retrieving listener and line for destination %s in profile %s \n " , dest , profile_name ) ;
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER ;
goto error ;
}
2010-02-24 12:04:08 +00:00
if ( ! tech_pvt - > listener ) {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Invalid destination or phone not registred %s in profile %s \n " , dest , profile_name ) ;
2010-02-24 12:04:08 +00:00
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER ;
2010-02-24 12:00:11 +00:00
goto error ;
}
2010-02-24 12:04:08 +00:00
2010-02-24 12:04:25 +00:00
if ( tech_pvt - > line = = 0 ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Invalid destination or phone not registred %s in profile %s \n " , dest , profile_name ) ;
cause = SWITCH_CAUSE_UNALLOCATED_NUMBER ;
goto error ;
}
2010-02-24 12:00:11 +00:00
2010-02-24 12:04:25 +00:00
if ( tech_pvt - > listener - > session [ tech_pvt - > line ] ) { /* Line is busy */
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Device line is busy %s in profile %s \n " , dest , profile_name ) ;
cause = SWITCH_CAUSE_USER_BUSY ;
goto error ;
}
tech_pvt - > listener - > session [ tech_pvt - > line ] = nsession ;
2010-02-24 12:04:08 +00:00
send_call_state ( tech_pvt - > listener , SKINNY_RING_IN , tech_pvt - > line , tech_pvt - > call_id ) ;
send_select_soft_keys ( tech_pvt - > listener , tech_pvt - > line , tech_pvt - > call_id ,
SKINNY_KEY_SET_RING_IN , 0xffff ) ;
display_prompt_status ( tech_pvt - > listener , 0 , " \200 \027 tel " , tech_pvt - > line , tech_pvt - > call_id ) ;
/* displayprinotifiymessage */
send_call_info ( tech_pvt - > listener ,
" TODO " , /* char calling_party_name[40], */
" TODO " , /* char calling_party[24], */
" TODO " , /* char called_party_name[40], */
" TODO " , /* char called_party[24], */
tech_pvt - > line , /* uint32_t line_instance, */
tech_pvt - > call_id , /* uint32_t call_id, */
SKINNY_OUTBOUND_CALL , /* uint32_t call_type, */
" TODO " , /* char original_called_party_name[40], */
" TODO " , /* char original_called_party[24], */
" TODO " , /* char last_redirecting_party_name[40], */
" TODO " , /* char last_redirecting_party[24], */
0 , /* uint32_t original_called_party_redirect_reason, */
0 , /* uint32_t last_redirecting_reason, */
" TODO " , /* char calling_party_voice_mailbox[24], */
" TODO " , /* char called_party_voice_mailbox[24], */
" TODO " , /* char original_called_party_voice_mailbox[24], */
" TODO " , /* char last_redirecting_voice_mailbox[24], */
1 , /* uint32_t call_instance, */
1 , /* uint32_t call_security_status, */
0 /* uint32_t party_pi_restriction_bits */
) ;
set_lamp ( tech_pvt - > listener , SKINNY_BUTTON_LINE , tech_pvt - > line , SKINNY_LAMP_BLINK ) ;
set_ringer ( tech_pvt - > listener , SKINNY_RING_OUTSIDE , SKINNY_RING_FOREVER , 0 ) ;
2010-02-24 12:00:11 +00:00
* new_session = nsession ;
2010-02-24 12:04:08 +00:00
/* ?? switch_channel_mark_ring_ready(channel); */
if ( switch_channel_get_state ( channel ) = = CS_NEW ) {
switch_channel_set_state ( channel , CS_INIT ) ;
}
2010-02-24 12:00:11 +00:00
cause = SWITCH_CAUSE_SUCCESS ;
goto done ;
error :
if ( nsession ) {
switch_core_session_destroy ( & nsession ) ;
}
* pool = NULL ;
done :
if ( profile ) {
if ( cause = = SWITCH_CAUSE_SUCCESS ) {
profile - > ob_calls + + ;
} else {
profile - > ob_failed_calls + + ;
}
}
return cause ;
2010-02-24 11:57:37 +00:00
}
static switch_status_t channel_receive_event ( switch_core_session_t * session , switch_event_t * event )
{
struct private_object * tech_pvt = switch_core_session_get_private ( session ) ;
char * body = switch_event_get_body ( event ) ;
switch_assert ( tech_pvt ! = NULL ) ;
if ( ! body ) {
body = " " ;
}
return SWITCH_STATUS_SUCCESS ;
}
switch_state_handler_table_t skinny_state_handlers = {
/*.on_init */ channel_on_init ,
/*.on_routing */ channel_on_routing ,
/*.on_execute */ channel_on_execute ,
/*.on_hangup */ channel_on_hangup ,
/*.on_exchange_media */ channel_on_exchange_media ,
/*.on_soft_execute */ channel_on_soft_execute ,
/*.on_consume_media*/ NULL ,
/*.on_hibernate*/ NULL ,
/*.on_reset*/ NULL ,
/*.on_park*/ NULL ,
/*.on_reporting*/ NULL ,
/*.on_destroy*/ channel_on_destroy
} ;
switch_io_routines_t skinny_io_routines = {
/*.outgoing_channel */ channel_outgoing_channel ,
/*.read_frame */ channel_read_frame ,
/*.write_frame */ channel_write_frame ,
/*.kill_channel */ channel_kill_channel ,
/*.send_dtmf */ channel_send_dtmf ,
/*.receive_message */ channel_receive_message ,
/*.receive_event */ channel_receive_event
} ;
2010-02-24 11:57:45 +00:00
/*****************************************************************************/
/* SKINNY FUNCTIONS */
/*****************************************************************************/
2010-02-24 11:58:36 +00:00
static char * skinny_codec2string ( enum skinny_codecs skinnycodec )
{
switch ( skinnycodec ) {
2010-02-24 11:59:02 +00:00
case SKINNY_CODEC_ALAW_64K :
case SKINNY_CODEC_ALAW_56K :
return " ALAW " ;
case SKINNY_CODEC_ULAW_64K :
case SKINNY_CODEC_ULAW_56K :
return " ULAW " ;
case SKINNY_CODEC_G722_64K :
case SKINNY_CODEC_G722_56K :
case SKINNY_CODEC_G722_48K :
return " G722 " ;
case SKINNY_CODEC_G723_1 :
return " G723 " ;
case SKINNY_CODEC_G728 :
return " G728 " ;
case SKINNY_CODEC_G729 :
case SKINNY_CODEC_G729A :
return " G729 " ;
case SKINNY_CODEC_IS11172 :
return " IS11172 " ;
case SKINNY_CODEC_IS13818 :
return " IS13818 " ;
case SKINNY_CODEC_G729B :
case SKINNY_CODEC_G729AB :
return " G729 " ;
case SKINNY_CODEC_GSM_FULL :
case SKINNY_CODEC_GSM_HALF :
case SKINNY_CODEC_GSM_EFULL :
return " GSM " ;
case SKINNY_CODEC_WIDEBAND_256K :
return " WIDEBAND " ;
case SKINNY_CODEC_DATA_64K :
case SKINNY_CODEC_DATA_56K :
return " DATA " ;
case SKINNY_CODEC_GSM :
return " GSM " ;
case SKINNY_CODEC_ACTIVEVOICE :
return " ACTIVEVOICE " ;
case SKINNY_CODEC_G726_32K :
case SKINNY_CODEC_G726_24K :
case SKINNY_CODEC_G726_16K :
return " G726 " ;
case SKINNY_CODEC_G729B_BIS :
case SKINNY_CODEC_G729B_LOW :
return " G729 " ;
case SKINNY_CODEC_H261 :
return " H261 " ;
case SKINNY_CODEC_H263 :
return " H263 " ;
case SKINNY_CODEC_VIDEO :
return " VIDEO " ;
case SKINNY_CODEC_T120 :
return " T120 " ;
case SKINNY_CODEC_H224 :
return " H224 " ;
case SKINNY_CODEC_RFC2833_DYNPAYLOAD :
return " RFC2833_DYNPAYLOAD " ;
default :
return " " ;
2010-02-24 11:58:36 +00:00
}
}
2010-02-24 11:58:07 +00:00
static switch_status_t skinny_read_packet ( listener_t * listener , skinny_message_t * * req )
2010-02-24 11:57:45 +00:00
{
skinny_message_t * request ;
switch_size_t mlen , bytes = 0 ;
char mbuf [ SKINNY_MESSAGE_MAXSIZE ] = " " ;
char * ptr ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2010-02-24 11:58:00 +00:00
request = switch_core_alloc ( listener - > pool , SKINNY_MESSAGE_MAXSIZE ) ;
2010-02-24 11:57:45 +00:00
if ( ! request ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to allocate memory. \n " ) ;
return SWITCH_STATUS_MEMERR ;
}
2010-02-24 11:59:41 +00:00
if ( ! globals . running ) {
2010-02-24 11:57:45 +00:00
return SWITCH_STATUS_FALSE ;
}
ptr = mbuf ;
2010-02-24 11:59:41 +00:00
while ( listener - > sock & & globals . running ) {
2010-02-24 11:57:45 +00:00
uint8_t do_sleep = 1 ;
if ( bytes < SKINNY_MESSAGE_FIELD_SIZE ) {
/* We have nothing yet, get length header field */
mlen = SKINNY_MESSAGE_FIELD_SIZE - bytes ;
} else {
/* We now know the message size */
mlen = request - > length + 2 * SKINNY_MESSAGE_FIELD_SIZE - bytes ;
}
status = switch_socket_recv ( listener - > sock , ptr , & mlen ) ;
2010-02-24 11:59:41 +00:00
if ( ! globals . running | | ( ! SWITCH_STATUS_IS_BREAK ( status ) & & status ! = SWITCH_STATUS_SUCCESS ) ) {
2010-02-24 11:57:45 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Socket break. \n " ) ;
return SWITCH_STATUS_FALSE ;
}
if ( mlen ) {
bytes + = mlen ;
if ( bytes > = SKINNY_MESSAGE_FIELD_SIZE ) {
do_sleep = 0 ;
ptr + = mlen ;
memcpy ( request , mbuf , bytes ) ;
2010-02-24 12:00:35 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG ,
2010-02-24 11:59:02 +00:00
" Got request: length=%d,reserved=%x,type=%x \n " ,
2010-02-24 11:57:45 +00:00
request - > length , request - > reserved , request - > type ) ;
if ( request - > length < SKINNY_MESSAGE_FIELD_SIZE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
2010-02-24 11:57:53 +00:00
" Skinny client sent invalid data. Length should be greater than 4 but got %d. \n " ,
2010-02-24 11:57:45 +00:00
request - > length ) ;
2010-02-24 11:57:53 +00:00
return SWITCH_STATUS_FALSE ;
}
if ( request - > length + 2 * SKINNY_MESSAGE_FIELD_SIZE > SKINNY_MESSAGE_MAXSIZE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" Skinny client sent too huge data. Got %d which is above threshold %d. \n " ,
request - > length , SKINNY_MESSAGE_MAXSIZE - 2 * SKINNY_MESSAGE_FIELD_SIZE ) ;
return SWITCH_STATUS_FALSE ;
2010-02-24 11:57:45 +00:00
}
if ( bytes > = request - > length + 2 * SKINNY_MESSAGE_FIELD_SIZE ) {
/* Message body */
2010-02-24 12:00:35 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG ,
2010-02-24 11:59:02 +00:00
" Got complete request: length=%d,reserved=%x,type=%x,data=%d \n " ,
2010-02-24 11:57:45 +00:00
request - > length , request - > reserved , request - > type , request - > data . as_char ) ;
* req = request ;
return SWITCH_STATUS_SUCCESS ;
}
}
}
2010-02-24 11:58:07 +00:00
if ( listener - > expire_time & & listener - > expire_time < switch_epoch_time_now ( NULL ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Listener timed out. \n " ) ;
switch_clear_flag_locked ( listener , LFLAG_RUNNING ) ;
return SWITCH_STATUS_FALSE ;
2010-02-24 11:57:45 +00:00
}
}
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:49 +00:00
static int skinny_device_event_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
switch_event_t * event = ( switch_event_t * ) pArg ;
char * device_name = argv [ 0 ] ;
char * user_id = argv [ 1 ] ;
char * instance = argv [ 2 ] ;
char * ip = argv [ 3 ] ;
char * device_type = argv [ 4 ] ;
char * max_streams = argv [ 5 ] ;
char * port = argv [ 6 ] ;
char * codec_string = argv [ 7 ] ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " Skinny-Device-Name " , device_name ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-User-Id " , " %s " , user_id ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Instance " , " %s " , instance ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " Skinny-IP " , ip ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Device-Type " , " %s " , device_type ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Max-Streams " , " %s " , max_streams ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Port " , " %s " , port ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " Skinny-Codecs " , codec_string ) ;
return 0 ;
}
2010-02-24 11:58:54 +00:00
static switch_status_t skinny_device_event ( listener_t * listener , switch_event_t * * ev , switch_event_types_t event_id , const char * subclass_name )
{
switch_event_t * event = NULL ;
2010-02-24 11:59:49 +00:00
char * sql ;
skinny_profile_t * profile ;
assert ( listener - > profile ) ;
profile = listener - > profile ;
2010-02-24 11:58:54 +00:00
switch_event_create_subclass ( & event , event_id , subclass_name ) ;
2010-02-24 11:59:02 +00:00
switch_assert ( event ) ;
2010-02-24 12:00:45 +00:00
if ( ( sql = switch_mprintf ( " SELECT * FROM skinny_devices WHERE name='%s' " , listener - > device_name ) ) ) {
2010-02-24 11:59:49 +00:00
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_device_event_callback , event ) ;
switch_safe_free ( sql ) ;
2010-02-24 11:58:54 +00:00
}
2010-02-24 11:59:49 +00:00
2010-02-24 11:58:54 +00:00
* ev = event ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:03:04 +00:00
/* Message helpers */
static switch_status_t start_tone ( listener_t * listener ,
uint32_t tone ,
uint32_t reserved ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . start_tone ) ) ;
message - > type = START_TONE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . start_tone ) ;
message - > data . start_tone . tone = tone ;
message - > data . start_tone . reserved = reserved ;
message - > data . start_tone . line_instance = line_instance ;
message - > data . start_tone . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t stop_tone ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . stop_tone ) ) ;
message - > type = STOP_TONE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . stop_tone ) ;
message - > data . stop_tone . line_instance = line_instance ;
message - > data . stop_tone . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t set_ringer ( listener_t * listener ,
uint32_t ring_type ,
uint32_t ring_mode ,
uint32_t unknown )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . ringer ) ) ;
message - > type = SET_RINGER_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . ringer ) ;
message - > data . ringer . ring_type = ring_type ;
message - > data . ringer . ring_mode = ring_mode ;
message - > data . ringer . unknown = unknown ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t set_lamp ( listener_t * listener ,
uint32_t stimulus ,
uint32_t stimulus_instance ,
uint32_t mode )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . lamp ) ) ;
message - > type = SET_LAMP_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . lamp ) ;
message - > data . lamp . stimulus = stimulus ;
message - > data . lamp . stimulus_instance = stimulus_instance ;
message - > data . lamp . mode = mode ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t set_speaker_mode ( listener_t * listener ,
uint32_t mode )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . speaker_mode ) ) ;
message - > type = SET_SPEAKER_MODE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . speaker_mode ) ;
message - > data . speaker_mode . mode = mode ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t start_media_transmission ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t remote_ip ,
uint32_t remote_port ,
uint32_t ms_per_packet ,
uint32_t payload_capacity ,
uint32_t precedence ,
uint32_t silence_suppression ,
uint16_t max_frames_per_packet ,
uint32_t g723_bitrate )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . start_media ) ) ;
message - > type = START_MEDIA_TRANSMISSION_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . start_media ) ;
message - > data . start_media . conference_id = conference_id ;
message - > data . start_media . pass_thru_party_id = pass_thru_party_id ;
message - > data . start_media . remote_ip = remote_ip ;
message - > data . start_media . remote_port = remote_port ;
message - > data . start_media . ms_per_packet = ms_per_packet ;
message - > data . start_media . payload_capacity = payload_capacity ;
message - > data . start_media . precedence = precedence ;
message - > data . start_media . silence_suppression = silence_suppression ;
message - > data . start_media . max_frames_per_packet = max_frames_per_packet ;
message - > data . start_media . g723_bitrate = g723_bitrate ;
/* ... */
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t stop_media_transmission ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t conference_id2 )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . stop_media ) ) ;
message - > type = STOP_MEDIA_TRANSMISSION_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . stop_media ) ;
message - > data . stop_media . conference_id = conference_id ;
message - > data . stop_media . pass_thru_party_id = pass_thru_party_id ;
message - > data . stop_media . conference_id2 = conference_id2 ;
/* ... */
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t send_call_info ( listener_t * listener ,
char calling_party_name [ 40 ] ,
char calling_party [ 24 ] ,
char called_party_name [ 40 ] ,
char called_party [ 24 ] ,
uint32_t line_instance ,
uint32_t call_id ,
uint32_t call_type ,
char original_called_party_name [ 40 ] ,
char original_called_party [ 24 ] ,
char last_redirecting_party_name [ 40 ] ,
char last_redirecting_party [ 24 ] ,
uint32_t original_called_party_redirect_reason ,
uint32_t last_redirecting_reason ,
char calling_party_voice_mailbox [ 24 ] ,
char called_party_voice_mailbox [ 24 ] ,
char original_called_party_voice_mailbox [ 24 ] ,
char last_redirecting_voice_mailbox [ 24 ] ,
uint32_t call_instance ,
uint32_t call_security_status ,
uint32_t party_pi_restriction_bits )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . call_info ) ) ;
message - > type = CALL_INFO_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . call_info ) ;
strcpy ( message - > data . call_info . calling_party_name , calling_party_name ) ;
strcpy ( message - > data . call_info . calling_party , calling_party ) ;
strcpy ( message - > data . call_info . called_party_name , called_party_name ) ;
strcpy ( message - > data . call_info . called_party , called_party ) ;
message - > data . call_info . line_instance = line_instance ;
message - > data . call_info . call_id = call_id ;
message - > data . call_info . call_type = call_type ;
strcpy ( message - > data . call_info . original_called_party_name , original_called_party_name ) ;
strcpy ( message - > data . call_info . original_called_party , original_called_party ) ;
strcpy ( message - > data . call_info . last_redirecting_party_name , last_redirecting_party_name ) ;
strcpy ( message - > data . call_info . last_redirecting_party , last_redirecting_party ) ;
message - > data . call_info . original_called_party_redirect_reason = original_called_party_redirect_reason ;
message - > data . call_info . last_redirecting_reason = last_redirecting_reason ;
strcpy ( message - > data . call_info . calling_party_voice_mailbox , calling_party_voice_mailbox ) ;
strcpy ( message - > data . call_info . called_party_voice_mailbox , called_party_voice_mailbox ) ;
strcpy ( message - > data . call_info . original_called_party_voice_mailbox , original_called_party_voice_mailbox ) ;
strcpy ( message - > data . call_info . last_redirecting_voice_mailbox , last_redirecting_voice_mailbox ) ;
message - > data . call_info . call_instance = call_instance ;
message - > data . call_info . call_security_status = call_security_status ;
message - > data . call_info . party_pi_restriction_bits = party_pi_restriction_bits ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t open_receive_channel ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t packets ,
uint32_t payload_capacity ,
uint32_t echo_cancel_type ,
uint32_t g723_bitrate ,
uint32_t conference_id2 ,
uint32_t reserved [ 10 ] )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . open_receive_channel ) ) ;
message - > type = OPEN_RECEIVE_CHANNEL_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . open_receive_channel ) ;
message - > data . open_receive_channel . conference_id = conference_id ;
message - > data . open_receive_channel . pass_thru_party_id = pass_thru_party_id ;
message - > data . open_receive_channel . packets = packets ;
message - > data . open_receive_channel . payload_capacity = payload_capacity ;
message - > data . open_receive_channel . echo_cancel_type = echo_cancel_type ;
message - > data . open_receive_channel . g723_bitrate = g723_bitrate ;
message - > data . open_receive_channel . conference_id2 = conference_id2 ;
/*
message - > data . open_receive_channel . reserved [ 0 ] = reserved [ 0 ] ;
message - > data . open_receive_channel . reserved [ 1 ] = reserved [ 1 ] ;
message - > data . open_receive_channel . reserved [ 2 ] = reserved [ 2 ] ;
message - > data . open_receive_channel . reserved [ 3 ] = reserved [ 3 ] ;
message - > data . open_receive_channel . reserved [ 4 ] = reserved [ 4 ] ;
message - > data . open_receive_channel . reserved [ 5 ] = reserved [ 5 ] ;
message - > data . open_receive_channel . reserved [ 6 ] = reserved [ 6 ] ;
message - > data . open_receive_channel . reserved [ 7 ] = reserved [ 7 ] ;
message - > data . open_receive_channel . reserved [ 8 ] = reserved [ 8 ] ;
message - > data . open_receive_channel . reserved [ 9 ] = reserved [ 9 ] ;
*/
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t close_receive_channel ( listener_t * listener ,
uint32_t conference_id ,
uint32_t pass_thru_party_id ,
uint32_t conference_id2 )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . close_receive_channel ) ) ;
message - > type = CLOSE_RECEIVE_CHANNEL_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . close_receive_channel ) ;
message - > data . close_receive_channel . conference_id = conference_id ;
message - > data . close_receive_channel . pass_thru_party_id = pass_thru_party_id ;
message - > data . close_receive_channel . conference_id2 = conference_id2 ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t send_select_soft_keys ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id ,
uint32_t soft_key_set ,
uint32_t valid_key_mask )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . select_soft_keys ) ) ;
message - > type = SELECT_SOFT_KEYS_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . select_soft_keys ) ;
message - > data . select_soft_keys . line_instance = line_instance ;
message - > data . select_soft_keys . call_id = call_id ;
message - > data . select_soft_keys . soft_key_set = soft_key_set ;
message - > data . select_soft_keys . valid_key_mask = valid_key_mask ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t send_call_state ( listener_t * listener ,
uint32_t call_state ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . call_state ) ) ;
message - > type = CALL_STATE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . call_state ) ;
message - > data . call_state . call_state = call_state ;
message - > data . call_state . line_instance = line_instance ;
message - > data . call_state . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t display_prompt_status ( listener_t * listener ,
uint32_t timeout ,
char display [ 32 ] ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . display_prompt_status ) ) ;
message - > type = DISPLAY_PROMPT_STATUS_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . display_prompt_status ) ;
message - > data . display_prompt_status . timeout = timeout ;
strcpy ( message - > data . display_prompt_status . display , display ) ;
message - > data . display_prompt_status . line_instance = line_instance ;
message - > data . display_prompt_status . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t clear_prompt_status ( listener_t * listener ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . clear_prompt_status ) ) ;
message - > type = CLEAR_PROMPT_STATUS_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . clear_prompt_status ) ;
message - > data . clear_prompt_status . line_instance = line_instance ;
message - > data . clear_prompt_status . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t activate_call_plane ( listener_t * listener ,
uint32_t line_instance )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . activate_call_plane ) ) ;
message - > type = ACTIVATE_CALL_PLANE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . activate_call_plane ) ;
message - > data . activate_call_plane . line_instance = line_instance ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t send_dialed_number ( listener_t * listener ,
char called_party [ 24 ] ,
uint32_t line_instance ,
uint32_t call_id )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . dialed_number ) ) ;
message - > type = DIALED_NUMBER_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . dialed_number ) ;
strcpy ( message - > data . dialed_number . called_party , called_party ) ;
message - > data . dialed_number . line_instance = line_instance ;
message - > data . dialed_number . call_id = call_id ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:10 +00:00
/* Message handling */
2010-02-24 12:00:35 +00:00
static switch_status_t skinny_handle_alarm ( listener_t * listener , skinny_message_t * request )
{
2010-02-24 12:01:13 +00:00
switch_event_t * event = NULL ;
2010-02-24 12:00:35 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . alarm ) ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO ,
" Received alarm: Severity=%d, DisplayMessage=%s, Param1=%d, Param2=%d. \n " ,
request - > data . alarm . alarm_severity , request - > data . alarm . display_message ,
request - > data . alarm . alarm_param1 , request - > data . alarm . alarm_param2 ) ;
2010-02-24 12:01:13 +00:00
/* skinny::alarm event */
skinny_device_event ( listener , & event , SWITCH_EVENT_CUSTOM , SKINNY_EVENT_ALARM ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Alarm-Severity " , " %d " , request - > data . alarm . alarm_severity ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Alarm-DisplayMessage " , " %s " , request - > data . alarm . display_message ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Alarm-Param1 " , " %d " , request - > data . alarm . alarm_param1 ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " Skinny-Alarm-Param2 " , " %d " , request - > data . alarm . alarm_param2 ) ;
switch_event_fire ( & event ) ;
2010-02-24 12:00:35 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:02 +00:00
static switch_status_t skinny_handle_register ( listener_t * listener , skinny_message_t * request )
{
switch_status_t status = SWITCH_STATUS_FALSE ;
skinny_message_t * message ;
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
2010-02-24 11:59:02 +00:00
switch_event_t * event = NULL ;
switch_event_t * params = NULL ;
2010-02-24 12:00:45 +00:00
switch_xml_t xroot , xdomain , xgroup , xuser , xskinny , xbuttons , xbutton ;
2010-02-24 11:59:49 +00:00
char * sql ;
2010-02-24 11:59:41 +00:00
assert ( listener - > profile ) ;
profile = listener - > profile ;
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . reg ) ) ;
2010-02-24 11:59:49 +00:00
if ( ! zstr ( listener - > device_name ) ) {
2010-02-24 11:59:02 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" A device is already registred on this listener. \n " ) ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . reg_rej ) ) ;
message - > type = REGISTER_REJ_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . reg_rej ) ;
strcpy ( message - > data . reg_rej . error , " A device is already registred on this listener " ) ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_FALSE ;
}
/* Check directory */
skinny_device_event ( listener , & params , SWITCH_EVENT_REQUEST_PARAMS , SWITCH_EVENT_SUBCLASS_ANY ) ;
switch_event_add_header_string ( params , SWITCH_STACK_BOTTOM , " action " , " skinny-auth " ) ;
2010-02-24 12:03:46 +00:00
if ( switch_xml_locate_user ( " id " , request - > data . reg . device_name , profile - > domain , " " , & xroot , & xdomain , & xuser , & xgroup , params ) ! = SWITCH_STATUS_SUCCESS ) {
2010-02-24 11:59:02 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Can't find device [%s@%s] \n "
2010-02-24 11:59:41 +00:00
" You must define a domain called '%s' in your directory and add a user with id= \" %s \" . \n "
2010-02-24 12:03:46 +00:00
, request - > data . reg . device_name , profile - > domain , profile - > domain , request - > data . reg . device_name ) ;
2010-02-24 11:59:02 +00:00
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . reg_rej ) ) ;
message - > type = REGISTER_REJ_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . reg_rej ) ;
strcpy ( message - > data . reg_rej . error , " Device not found " ) ;
skinny_send_reply ( listener , message ) ;
2010-02-24 11:59:49 +00:00
status = SWITCH_STATUS_FALSE ;
2010-02-24 11:59:02 +00:00
goto end ;
}
2010-02-24 11:59:49 +00:00
if ( ( sql = switch_mprintf (
" INSERT INTO skinny_devices "
2010-02-24 12:00:45 +00:00
" (name, user_id, instance, ip, type, max_streams, codec_string) "
2010-02-24 11:59:49 +00:00
" VALUES ('%s','%d','%d', '%s', '%d', '%d', '%s') " ,
2010-02-24 12:03:46 +00:00
request - > data . reg . device_name ,
request - > data . reg . user_id ,
2010-02-24 11:59:49 +00:00
request - > data . reg . instance ,
inet_ntoa ( request - > data . reg . ip ) ,
2010-02-24 12:03:46 +00:00
request - > data . reg . device_type ,
request - > data . reg . max_streams ,
2010-02-24 11:59:49 +00:00
" " /* codec_string */
) ) ) {
skinny_execute_sql ( profile , sql , profile - > listener_mutex ) ;
switch_safe_free ( sql ) ;
}
2010-02-24 12:03:46 +00:00
strcpy ( listener - > device_name , request - > data . reg . device_name ) ;
2010-02-24 11:59:49 +00:00
2010-02-24 11:59:41 +00:00
xskinny = switch_xml_child ( xuser , " skinny " ) ;
if ( xskinny ) {
2010-02-24 12:00:45 +00:00
xbuttons = switch_xml_child ( xskinny , " buttons " ) ;
if ( xbuttons ) {
for ( xbutton = switch_xml_child ( xbuttons , " button " ) ; xbutton ; xbutton = xbutton - > next ) {
const char * position = switch_xml_attr_soft ( xbutton , " position " ) ;
const char * type = switch_xml_attr_soft ( xbutton , " type " ) ;
const char * label = switch_xml_attr_soft ( xbutton , " label " ) ;
const char * value = switch_xml_attr_soft ( xbutton , " value " ) ;
const char * settings = switch_xml_attr_soft ( xbutton , " settings " ) ;
2010-02-24 11:59:49 +00:00
if ( ( sql = switch_mprintf (
2010-02-24 12:00:45 +00:00
" INSERT INTO skinny_buttons "
" (device_name, position, type, label, value, settings) "
" VALUES('%s', '%s', '%s', '%s', '%s', '%s') " ,
2010-02-24 12:03:46 +00:00
request - > data . reg . device_name ,
2010-02-24 11:59:49 +00:00
position ,
2010-02-24 12:00:45 +00:00
type ,
label ,
value ,
settings ) ) ) {
2010-02-24 11:59:49 +00:00
skinny_execute_sql ( profile , sql , profile - > listener_mutex ) ;
switch_safe_free ( sql ) ;
}
2010-02-24 11:59:41 +00:00
}
2010-02-24 11:59:02 +00:00
}
}
status = SWITCH_STATUS_SUCCESS ;
/* Reply with RegisterAckMessage */
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . reg_ack ) ) ;
message - > type = REGISTER_ACK_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . reg_ack ) ;
2010-02-24 11:59:41 +00:00
message - > data . reg_ack . keepAlive = profile - > keep_alive ;
memcpy ( message - > data . reg_ack . dateFormat , profile - > date_format , 6 ) ;
message - > data . reg_ack . secondaryKeepAlive = profile - > keep_alive ;
2010-02-24 11:59:02 +00:00
skinny_send_reply ( listener , message ) ;
/* Send CapabilitiesReqMessage */
message = switch_core_alloc ( listener - > pool , 12 ) ;
message - > type = CAPABILITIES_REQ_MESSAGE ;
message - > length = 4 ;
skinny_send_reply ( listener , message ) ;
/* skinny::register event */
skinny_device_event ( listener , & event , SWITCH_EVENT_CUSTOM , SKINNY_EVENT_REGISTER ) ;
switch_event_fire ( & event ) ;
keepalive_listener ( listener , NULL ) ;
end :
if ( params ) {
switch_event_destroy ( & params ) ;
}
return status ;
}
2010-02-24 12:01:21 +00:00
static switch_status_t skinny_headset_status_message ( listener_t * listener , skinny_message_t * request )
{
skinny_check_data_length ( request , sizeof ( request - > data . headset_status ) ) ;
/* Nothing to do */
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:03:46 +00:00
static int skinny_config_stat_res_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
skinny_message_t * message = pArg ;
char * device_name = argv [ 0 ] ;
int user_id = atoi ( argv [ 1 ] ) ;
int instance = atoi ( argv [ 2 ] ) ;
char * user_name = argv [ 3 ] ;
char * server_name = argv [ 4 ] ;
int number_lines = atoi ( argv [ 5 ] ) ;
int number_speed_dials = atoi ( argv [ 6 ] ) ;
strcpy ( message - > data . config_res . device_name , device_name ) ;
message - > data . config_res . user_id = user_id ;
message - > data . config_res . instance = instance ;
strcpy ( message - > data . config_res . user_name , user_name ) ;
strcpy ( message - > data . config_res . server_name , server_name ) ;
message - > data . config_res . number_lines = number_lines ;
message - > data . config_res . number_speed_dials = number_speed_dials ;
return 0 ;
}
static switch_status_t skinny_handle_config_stat_request ( listener_t * listener , skinny_message_t * request )
{
char * sql ;
skinny_message_t * message ;
skinny_profile_t * profile ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . config_res ) ) ;
message - > type = CONFIG_STAT_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . config_res ) ;
if ( ( sql = switch_mprintf (
" SELECT name, user_id, instance, '' AS user_name, '' AS server_name, "
" (SELECT COUNT(*) FROM skinny_buttons WHERE device_name='%s' AND type='line') AS number_lines, "
" (SELECT COUNT(*) FROM skinny_buttons WHERE device_name='%s' AND type='speed-dial') AS number_speed_dials "
" FROM skinny_devices WHERE name='%s' " ,
listener - > device_name ,
listener - > device_name ,
listener - > device_name
) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_config_stat_res_callback , message ) ;
switch_safe_free ( sql ) ;
}
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:10 +00:00
static switch_status_t skinny_handle_capabilities_response ( listener_t * listener , skinny_message_t * request )
2010-02-24 11:57:45 +00:00
{
2010-02-24 11:59:56 +00:00
char * sql ;
skinny_profile_t * profile ;
2010-02-24 11:58:36 +00:00
uint32_t i = 0 ;
uint32_t n = 0 ;
2010-02-24 11:59:49 +00:00
char * codec_order [ SWITCH_MAX_CODECS ] ;
char * codec_string ;
2010-02-24 11:58:36 +00:00
size_t string_len , string_pos , pos ;
2010-02-24 11:58:54 +00:00
2010-02-24 11:59:56 +00:00
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . cap_res . count ) ) ;
2010-02-24 11:59:10 +00:00
n = request - > data . cap_res . count ;
if ( n > SWITCH_MAX_CODECS ) {
n = SWITCH_MAX_CODECS ;
}
string_len = - 1 ;
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . cap_res . count ) + n * sizeof ( request - > data . cap_res . caps [ 0 ] ) ) ;
2010-02-24 11:59:10 +00:00
for ( i = 0 ; i < n ; i + + ) {
char * codec = skinny_codec2string ( request - > data . cap_res . caps [ i ] . codec ) ;
2010-02-24 11:59:49 +00:00
codec_order [ i ] = codec ;
2010-02-24 11:59:10 +00:00
string_len + = strlen ( codec ) + 1 ;
}
i = 0 ;
pos = 0 ;
2010-02-24 11:59:49 +00:00
codec_string = switch_core_alloc ( listener - > pool , string_len + 1 ) ;
2010-02-24 11:59:10 +00:00
for ( string_pos = 0 ; string_pos < string_len ; string_pos + + ) {
2010-02-24 11:59:49 +00:00
char * codec = codec_order [ i ] ;
2010-02-24 11:59:10 +00:00
switch_assert ( i < n ) ;
if ( pos = = strlen ( codec ) ) {
2010-02-24 11:59:49 +00:00
codec_string [ string_pos ] = ' , ' ;
2010-02-24 11:59:10 +00:00
i + + ;
pos = 0 ;
} else {
2010-02-24 11:59:49 +00:00
codec_string [ string_pos ] = codec [ pos + + ] ;
2010-02-24 11:59:10 +00:00
}
}
2010-02-24 11:59:49 +00:00
codec_string [ string_len ] = ' \0 ' ;
2010-02-24 11:59:56 +00:00
if ( ( sql = switch_mprintf (
2010-02-24 12:00:45 +00:00
" UPDATE skinny_devices SET codec_string='%s' WHERE name='%s' " ,
2010-02-24 11:59:56 +00:00
codec_string ,
listener - > device_name
) ) ) {
skinny_execute_sql ( profile , sql , profile - > listener_mutex ) ;
switch_safe_free ( sql ) ;
}
2010-02-24 11:59:10 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO ,
2010-02-24 11:59:49 +00:00
" Codecs %s supported. \n " , codec_string ) ;
2010-02-24 11:59:10 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t skinny_handle_port_message ( listener_t * listener , skinny_message_t * request )
{
2010-02-24 11:59:49 +00:00
char * sql ;
skinny_profile_t * profile ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
2010-02-24 11:59:10 +00:00
2010-02-24 11:59:49 +00:00
profile = listener - > profile ;
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . as_uint16 ) ) ;
2010-02-24 11:59:49 +00:00
if ( ( sql = switch_mprintf (
2010-02-24 12:00:45 +00:00
" UPDATE skinny_devices SET port='%d' WHERE name='%s' " ,
2010-02-24 11:59:49 +00:00
request - > data . as_uint16 ,
listener - > device_name
) ) ) {
skinny_execute_sql ( profile , sql , profile - > listener_mutex ) ;
switch_safe_free ( sql ) ;
}
2010-02-24 11:59:10 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:00:45 +00:00
struct button_template_helper {
skinny_message_t * message ;
int count [ 0xff + 1 ] ;
} ;
2010-02-24 11:59:49 +00:00
2010-02-24 12:00:45 +00:00
static int skinny_handle_button_template_request_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
2010-02-24 11:59:49 +00:00
{
2010-02-24 12:00:45 +00:00
struct button_template_helper * helper = pArg ;
skinny_message_t * message = helper - > message ;
char * device_name = argv [ 0 ] ;
int position = atoi ( argv [ 1 ] ) ;
char * type = argv [ 2 ] ;
int i ;
/* fill buttons between previous one and current one */
for ( i = message - > data . button_template . button_count ; i + 1 < position ; i + + ) {
message - > data . button_template . btn [ i ] . instance_number = + + helper - > count [ 0xff ] ;
message - > data . button_template . btn [ i ] . button_definition = 0xff ; /* None */
message - > data . button_template . button_count + + ;
message - > data . button_template . total_button_count + + ;
}
2010-02-24 11:59:49 +00:00
2010-02-24 12:00:45 +00:00
if ( ! strcasecmp ( type , " line " ) ) {
message - > data . button_template . btn [ i ] . instance_number = + + helper - > count [ 0x09 ] ;
message - > data . button_template . btn [ position - 1 ] . button_definition = 0x09 ; /* Line */
} else if ( ! strcasecmp ( type , " speed-dial " ) ) {
message - > data . button_template . btn [ i ] . instance_number = + + helper - > count [ 0x02 ] ;
message - > data . button_template . btn [ position - 1 ] . button_definition = 0x02 ; /* speeddial */
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO ,
" Unknown button type %s for device %s. \n " , type , device_name ) ;
}
message - > data . button_template . button_count + + ;
message - > data . button_template . total_button_count + + ;
2010-02-24 11:59:49 +00:00
return 0 ;
}
2010-02-24 12:00:45 +00:00
static switch_status_t skinny_handle_button_template_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
struct button_template_helper helper = { 0 } ;
skinny_profile_t * profile ;
char * sql ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . button_template ) ) ;
message - > type = BUTTON_TEMPLATE_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . button_template ) ;
message - > data . button_template . button_offset = 0 ;
message - > data . button_template . button_count = 0 ;
message - > data . button_template . total_button_count = 0 ;
helper . message = message ;
/* Add buttons */
if ( ( sql = switch_mprintf (
" SELECT device_name, position, type "
" FROM skinny_buttons WHERE device_name='%s' ORDER BY position " ,
listener - > device_name
) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_handle_button_template_request_callback , & helper ) ;
switch_safe_free ( sql ) ;
}
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t skinny_handle_soft_key_template_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
skinny_profile_t * profile ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . soft_key_template ) ) ;
message - > type = SOFT_KEY_TEMPLATE_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . soft_key_template ) ;
message - > data . soft_key_template . soft_key_offset = 0 ;
message - > data . soft_key_template . soft_key_count = 21 ;
message - > data . soft_key_template . total_soft_key_count = 21 ;
2010-02-24 12:03:59 +00:00
memcpy ( message - > data . soft_key_template . soft_key ,
soft_key_template_default ,
sizeof ( soft_key_template_default ) ) ;
2010-02-24 12:00:45 +00:00
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t skinny_handle_soft_key_set_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
skinny_profile_t * profile ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . soft_key_set ) ) ;
message - > type = SOFT_KEY_SET_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . soft_key_set ) ;
message - > data . soft_key_set . soft_key_set_offset = 0 ;
message - > data . soft_key_set . soft_key_set_count = 11 ;
message - > data . soft_key_set . total_soft_key_set_count = 11 ;
/* TODO fill the set */
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
static int skinny_line_stat_request_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
skinny_message_t * message = pArg ;
message - > data . line_res . number + + ;
if ( message - > data . line_res . number = = atoi ( argv [ 0 ] ) ) { /* wanted_position */
strcpy ( message - > data . line_res . name , argv [ 3 ] ) ; /* value */
strcpy ( message - > data . line_res . shortname , argv [ 2 ] ) ; /* label */
strcpy ( message - > data . line_res . displayname , argv [ 4 ] ) ; /* settings */
}
return 0 ;
}
2010-02-24 11:59:49 +00:00
2010-02-24 11:59:10 +00:00
static switch_status_t skinny_handle_line_stat_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
2010-02-24 11:59:49 +00:00
skinny_profile_t * profile ;
char * sql ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . line_req ) ) ;
2010-02-24 11:59:10 +00:00
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . line_res ) ) ;
message - > type = LINE_STAT_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . line_res ) ;
2010-02-24 12:00:45 +00:00
message - > data . line_res . number = 0 ;
2010-02-24 11:59:49 +00:00
if ( ( sql = switch_mprintf (
2010-02-24 12:00:45 +00:00
" SELECT '%d' AS wanted_position, position, label, value, settings "
" FROM skinny_buttons WHERE device_name='%s' AND type='line' "
" ORDER BY position " ,
request - > data . line_req . number ,
listener - > device_name
2010-02-24 11:59:49 +00:00
) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_line_stat_request_callback , message ) ;
switch_safe_free ( sql ) ;
2010-02-24 11:59:10 +00:00
}
2010-02-24 12:00:45 +00:00
message - > data . line_res . number = request - > data . line_req . number ;
2010-02-24 11:59:10 +00:00
skinny_send_reply ( listener , message ) ;
2010-02-24 11:59:49 +00:00
2010-02-24 11:59:10 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:00:55 +00:00
static int skinny_handle_speed_dial_request_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
skinny_message_t * message = pArg ;
message - > data . speed_dial_res . number + + ;
if ( message - > data . speed_dial_res . number = = atoi ( argv [ 0 ] ) ) { /* wanted_position */
message - > data . speed_dial_res . number = atoi ( argv [ 3 ] ) ; /* value */
strcpy ( message - > data . speed_dial_res . label , argv [ 2 ] ) ; /* label */
}
return 0 ;
}
static switch_status_t skinny_handle_speed_dial_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
skinny_profile_t * profile ;
char * sql ;
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
skinny_check_data_length ( request , sizeof ( request - > data . speed_dial_req ) ) ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . speed_dial_res ) ) ;
message - > type = SPEED_DIAL_STAT_RES_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . speed_dial_res ) ;
message - > data . speed_dial_res . number = 0 ;
if ( ( sql = switch_mprintf (
" SELECT '%d' AS wanted_position, position, label, value, settings "
" FROM skinny_buttons WHERE device_name='%s' AND type='speed-dial' "
" ORDER BY position " ,
request - > data . speed_dial_req . number ,
listener - > device_name
) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_handle_speed_dial_request_callback , message ) ;
switch_safe_free ( sql ) ;
}
message - > data . speed_dial_res . number = request - > data . speed_dial_req . number ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:25 +00:00
static switch_status_t skinny_handle_register_available_lines_message ( listener_t * listener , skinny_message_t * request )
{
2010-02-24 12:00:28 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . reg_lines ) ) ;
2010-02-24 11:59:25 +00:00
/* Do nothing */
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:01:39 +00:00
static switch_status_t skinny_handle_time_date_request ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
switch_time_t ts ;
switch_time_exp_t tm ;
message = switch_core_alloc ( listener - > pool , 12 + sizeof ( message - > data . define_time_date ) ) ;
message - > type = DEFINE_TIME_DATE_MESSAGE ;
message - > length = 4 + sizeof ( message - > data . define_time_date ) ;
ts = switch_micro_time_now ( ) ;
switch_time_exp_lt ( & tm , ts ) ;
message - > data . define_time_date . year = tm . tm_year + 1900 ;
message - > data . define_time_date . month = tm . tm_mon + 1 ;
message - > data . define_time_date . day_of_week = tm . tm_wday ;
message - > data . define_time_date . day = tm . tm_yday + 1 ;
message - > data . define_time_date . hour = tm . tm_hour ;
message - > data . define_time_date . minute = tm . tm_min ;
message - > data . define_time_date . seconds = tm . tm_sec + 1 ;
message - > data . define_time_date . milliseconds = tm . tm_usec / 1000 ;
message - > data . define_time_date . timestamp = ts / 1000000 ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:10 +00:00
static switch_status_t skinny_handle_keep_alive_message ( listener_t * listener , skinny_message_t * request )
{
skinny_message_t * message ;
message = switch_core_alloc ( listener - > pool , 12 ) ;
message - > type = KEEP_ALIVE_ACK_MESSAGE ;
message - > length = 4 ;
keepalive_listener ( listener , NULL ) ;
skinny_send_reply ( listener , message ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:04:08 +00:00
static switch_status_t skinny_handle_off_hook_message ( listener_t * listener , skinny_message_t * request )
{
skinny_profile_t * profile ;
2010-02-24 12:04:25 +00:00
uint32_t line ;
2010-02-24 12:04:08 +00:00
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
skinny_check_data_length ( request , sizeof ( request - > data . off_hook ) ) ;
2010-02-24 12:04:25 +00:00
if ( request - > data . off_hook . line_instance ) {
line = request - > data . off_hook . line_instance ;
} else {
line = 1 ;
}
if ( listener - > session [ line ] ) { /*answering a call */
2010-02-24 12:04:08 +00:00
private_t * tech_pvt = NULL ;
2010-02-24 12:04:25 +00:00
tech_pvt = switch_core_session_get_private ( listener - > session [ line ] ) ;
2010-02-24 12:04:08 +00:00
set_ringer ( listener , SKINNY_RING_OFF , SKINNY_RING_FOREVER , 0 ) ; /* TODO : here ? */
stop_tone ( listener , tech_pvt - > line , tech_pvt - > call_id ) ;
open_receive_channel ( listener ,
tech_pvt - > call_id , /* uint32_t conference_id, */
0 , /* uint32_t pass_thru_party_id, */
20 , /* uint32_t packets, */
SKINNY_CODEC_ULAW_64K , /* uint32_t payload_capacity, */
0 , /* uint32_t echo_cancel_type, */
0 , /* uint32_t g723_bitrate, */
0 , /* uint32_t conference_id2, */
0 /* uint32_t reserved[10] */
) ;
send_call_state ( listener ,
SKINNY_CONNECTED ,
tech_pvt - > line ,
tech_pvt - > call_id ) ;
send_select_soft_keys ( listener ,
tech_pvt - > line ,
tech_pvt - > call_id ,
SKINNY_KEY_SET_CONNECTED ,
0xffff ) ;
display_prompt_status ( listener ,
0 ,
" \200 \030 " ,
1 ,
tech_pvt - > call_id ) ;
}
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t skinny_handle_open_receive_channel_ack_message ( listener_t * listener , skinny_message_t * request )
{
switch_status_t status = SWITCH_STATUS_SUCCESS ;
skinny_profile_t * profile ;
2010-02-24 12:04:25 +00:00
uint32_t line ;
2010-02-24 12:04:08 +00:00
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
skinny_check_data_length ( request , sizeof ( request - > data . open_receive_channel_ack ) ) ;
2010-02-24 12:04:25 +00:00
for ( int i = 0 ; i < SKINNY_MAX_BUTTON_COUNT ; i + + ) {
if ( listener - > session [ i ] ) {
private_t * tech_pvt = NULL ;
tech_pvt = switch_core_session_get_private ( listener - > session [ i ] ) ;
if ( tech_pvt - > party_id = = request - > data . open_receive_channel_ack . pass_thru_party_id ) {
line = i ;
}
}
}
if ( listener - > session [ line ] ) {
2010-02-24 12:04:08 +00:00
const char * err = NULL ;
private_t * tech_pvt = NULL ;
switch_channel_t * channel = NULL ;
struct in_addr addr ;
2010-02-24 12:04:25 +00:00
tech_pvt = switch_core_session_get_private ( listener - > session [ line ] ) ;
channel = switch_core_session_get_channel ( listener - > session [ line ] ) ;
2010-02-24 12:04:08 +00:00
/* Codec */
tech_pvt - > iananame = " PCMU " ; /* TODO */
tech_pvt - > codec_ms = 10 ; /* TODO */
tech_pvt - > rm_rate = 8000 ; /* TODO */
tech_pvt - > rm_fmtp = NULL ; /* TODO */
tech_pvt - > agreed_pt = ( switch_payload_t ) 0 ; /* TODO */
2010-02-24 12:04:25 +00:00
tech_pvt - > rm_encoding = switch_core_strdup ( switch_core_session_get_pool ( listener - > session [ line ] ) , " " ) ;
2010-02-24 12:04:08 +00:00
skinny_tech_set_codec ( tech_pvt , 0 ) ;
if ( ( status = skinny_tech_set_codec ( tech_pvt , 0 ) ) ! = SWITCH_STATUS_SUCCESS ) {
goto end ;
}
/* Request a local port from the core's allocator */
if ( ! ( tech_pvt - > local_sdp_audio_port = switch_rtp_request_port ( listener - > profile - > ip ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_CRIT , " No RTP ports available! \n " ) ;
return SWITCH_STATUS_FALSE ;
}
2010-02-24 12:04:25 +00:00
tech_pvt - > local_sdp_audio_ip = switch_core_strdup ( switch_core_session_get_pool ( listener - > session [ line ] ) , listener - > profile - > ip ) ;
2010-02-24 12:04:08 +00:00
tech_pvt - > remote_sdp_audio_ip = inet_ntoa ( request - > data . open_receive_channel_ack . ip ) ;
tech_pvt - > remote_sdp_audio_port = request - > data . open_receive_channel_ack . port ;
tech_pvt - > rtp_session = switch_rtp_new ( tech_pvt - > local_sdp_audio_ip ,
tech_pvt - > local_sdp_audio_port ,
tech_pvt - > remote_sdp_audio_ip ,
tech_pvt - > remote_sdp_audio_port ,
tech_pvt - > agreed_pt ,
tech_pvt - > read_impl . samples_per_packet ,
tech_pvt - > codec_ms * 1000 ,
( switch_rtp_flag_t ) 0 , " soft " , & err ,
2010-02-24 12:04:25 +00:00
switch_core_session_get_pool ( listener - > session [ line ] ) ) ;
2010-02-24 12:04:08 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( tech_pvt - > session ) , SWITCH_LOG_DEBUG ,
" AUDIO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s] \n " ,
switch_channel_get_name ( channel ) ,
tech_pvt - > local_sdp_audio_ip ,
tech_pvt - > local_sdp_audio_port ,
tech_pvt - > remote_sdp_audio_ip ,
tech_pvt - > remote_sdp_audio_port ,
tech_pvt - > agreed_pt ,
tech_pvt - > read_impl . microseconds_per_packet / 1000 ,
switch_rtp_ready ( tech_pvt - > rtp_session ) ? " SUCCESS " : err ) ;
inet_aton ( tech_pvt - > local_sdp_audio_ip , & addr ) ;
start_media_transmission ( listener ,
tech_pvt - > call_id , /* uint32_t conference_id, */
tech_pvt - > party_id , /* uint32_t pass_thru_party_id, */
addr . s_addr , /* uint32_t remote_ip, */
tech_pvt - > local_sdp_audio_port , /* uint32_t remote_port, */
20 , /* uint32_t ms_per_packet, */
SKINNY_CODEC_ULAW_64K , /* uint32_t payload_capacity, */
184 , /* uint32_t precedence, */
0 , /* uint32_t silence_suppression, */
0 , /* uint16_t max_frames_per_packet, */
0 /* uint32_t g723_bitrate */
) ;
switch_channel_mark_answered ( channel ) ;
}
end :
return status ;
}
2010-02-24 12:04:16 +00:00
static switch_status_t skinny_handle_keypad_button_message ( listener_t * listener , skinny_message_t * request )
{
2010-02-24 12:04:25 +00:00
uint32_t line ;
2010-02-24 12:04:16 +00:00
skinny_check_data_length ( request , sizeof ( request - > data . keypad_button ) ) ;
2010-02-24 12:04:25 +00:00
if ( request - > data . keypad_button . line_instance ) {
line = request - > data . keypad_button . line_instance ;
} else {
/* Find first active line */
for ( int i = 0 ; i < SKINNY_MAX_BUTTON_COUNT ; i + + ) {
if ( listener - > session [ i ] ) {
line = i ;
break ;
}
}
}
if ( listener - > session [ line ] ) {
2010-02-24 12:04:16 +00:00
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
switch_dtmf_t dtmf = { 0 , switch_core_default_dtmf_duration ( 0 ) } ;
2010-02-24 12:04:25 +00:00
channel = switch_core_session_get_channel ( listener - > session [ line ] ) ;
2010-02-24 12:04:16 +00:00
assert ( channel ! = NULL ) ;
2010-02-24 12:04:25 +00:00
tech_pvt = switch_core_session_get_private ( listener - > session [ line ] ) ;
2010-02-24 12:04:16 +00:00
assert ( tech_pvt ! = NULL ) ;
/* TODO check call_id and line */
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( listener - > session [ line ] ) , SWITCH_LOG_DEBUG , " SEND DTMF ON CALL %d [%d] \n " , tech_pvt - > call_id , request - > data . keypad_button . button ) ;
2010-02-24 12:04:16 +00:00
if ( request - > data . keypad_button . button = = 14 ) {
dtmf . digit = ' * ' ;
switch_channel_queue_dtmf ( channel , & dtmf ) ;
} else if ( request - > data . keypad_button . button = = 15 ) {
dtmf . digit = ' # ' ;
switch_channel_queue_dtmf ( channel , & dtmf ) ;
} else if ( request - > data . keypad_button . button > = 0 & & request - > data . keypad_button . button < = 9 ) {
dtmf . digit = ' 0 ' + request - > data . keypad_button . button ;
switch_channel_queue_dtmf ( channel , & dtmf ) ;
} else {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( listener - > session [ line ] ) , SWITCH_LOG_WARNING , " UNKNOW DTMF RECEIVED ON CALL %d [%d] \n " , tech_pvt - > call_id , request - > data . keypad_button . button ) ;
2010-02-24 12:04:16 +00:00
}
}
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 12:04:08 +00:00
static switch_status_t skinny_handle_on_hook_message ( listener_t * listener , skinny_message_t * request )
{
switch_status_t status = SWITCH_STATUS_SUCCESS ;
skinny_profile_t * profile ;
2010-02-24 12:04:25 +00:00
uint32_t line ;
2010-02-24 12:04:08 +00:00
switch_assert ( listener - > profile ) ;
switch_assert ( listener - > device_name ) ;
profile = listener - > profile ;
skinny_check_data_length ( request , sizeof ( request - > data . on_hook ) ) ;
2010-02-24 12:04:25 +00:00
if ( request - > data . on_hook . line_instance ) {
line = request - > data . on_hook . line_instance ;
} else {
/* Find first active line */
for ( int i = 0 ; i < SKINNY_MAX_BUTTON_COUNT ; i + + ) {
if ( listener - > session [ i ] ) {
line = i ;
break ;
}
}
}
if ( listener - > session [ line ] ) {
2010-02-24 12:04:08 +00:00
switch_channel_t * channel = NULL ;
private_t * tech_pvt = NULL ;
2010-02-24 12:04:25 +00:00
channel = switch_core_session_get_channel ( listener - > session [ line ] ) ;
2010-02-24 12:04:08 +00:00
assert ( channel ! = NULL ) ;
2010-02-24 12:04:25 +00:00
tech_pvt = switch_core_session_get_private ( listener - > session [ line ] ) ;
2010-02-24 12:04:08 +00:00
assert ( tech_pvt ! = NULL ) ;
switch_clear_flag_locked ( tech_pvt , TFLAG_IO ) ;
switch_clear_flag_locked ( tech_pvt , TFLAG_VOICE ) ;
switch_channel_hangup ( channel , SWITCH_CAUSE_NORMAL_CLEARING ) ;
}
return status ;
}
2010-02-24 11:59:18 +00:00
static switch_status_t skinny_handle_unregister ( listener_t * listener , skinny_message_t * request )
{
switch_event_t * event = NULL ;
/* skinny::unregister event */
skinny_device_event ( listener , & event , SWITCH_EVENT_CUSTOM , SKINNY_EVENT_UNREGISTER ) ;
switch_event_fire ( & event ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:10 +00:00
static switch_status_t skinny_handle_request ( listener_t * listener , skinny_message_t * request )
{
2010-02-24 11:58:00 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO ,
2010-02-24 11:59:02 +00:00
" Received message (type=%x,length=%d). \n " , request - > type , request - > length ) ;
2010-02-24 12:00:35 +00:00
if ( zstr ( listener - > device_name ) & & request - > type ! = REGISTER_MESSAGE & & request - > type ! = ALARM_MESSAGE ) {
2010-02-24 11:58:00 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" Device should send a register message first. \n " ) ;
return SWITCH_STATUS_FALSE ;
}
2010-02-24 11:57:45 +00:00
switch ( request - > type ) {
2010-02-24 12:00:35 +00:00
case ALARM_MESSAGE :
return skinny_handle_alarm ( listener , request ) ;
2010-02-24 12:01:31 +00:00
/* registering phase */
2010-02-24 11:58:00 +00:00
case REGISTER_MESSAGE :
2010-02-24 11:59:02 +00:00
return skinny_handle_register ( listener , request ) ;
2010-02-24 12:01:21 +00:00
case HEADSET_STATUS_MESSAGE :
return skinny_headset_status_message ( listener , request ) ;
2010-02-24 12:03:46 +00:00
case CONFIG_STAT_REQ_MESSAGE :
return skinny_handle_config_stat_request ( listener , request ) ;
2010-02-24 11:58:36 +00:00
case CAPABILITIES_RES_MESSAGE :
2010-02-24 11:59:10 +00:00
return skinny_handle_capabilities_response ( listener , request ) ;
2010-02-24 11:58:00 +00:00
case PORT_MESSAGE :
2010-02-24 11:59:10 +00:00
return skinny_handle_port_message ( listener , request ) ;
2010-02-24 12:00:45 +00:00
case BUTTON_TEMPLATE_REQ_MESSAGE :
return skinny_handle_button_template_request ( listener , request ) ;
case SOFT_KEY_TEMPLATE_REQ_MESSAGE :
return skinny_handle_soft_key_template_request ( listener , request ) ;
case SOFT_KEY_SET_REQ_MESSAGE :
return skinny_handle_soft_key_set_request ( listener , request ) ;
2010-02-24 11:59:02 +00:00
case LINE_STAT_REQ_MESSAGE :
2010-02-24 11:59:10 +00:00
return skinny_handle_line_stat_request ( listener , request ) ;
2010-02-24 12:00:55 +00:00
case SPEED_DIAL_STAT_REQ_MESSAGE :
return skinny_handle_speed_dial_request ( listener , request ) ;
2010-02-24 11:59:25 +00:00
case REGISTER_AVAILABLE_LINES_MESSAGE :
return skinny_handle_register_available_lines_message ( listener , request ) ;
2010-02-24 12:01:39 +00:00
case TIME_DATE_REQ_MESSAGE :
return skinny_handle_time_date_request ( listener , request ) ;
2010-02-24 12:01:31 +00:00
/* live phase */
2010-02-24 11:58:00 +00:00
case KEEP_ALIVE_MESSAGE :
2010-02-24 11:59:10 +00:00
return skinny_handle_keep_alive_message ( listener , request ) ;
2010-02-24 12:04:08 +00:00
case OFF_HOOK_MESSAGE :
return skinny_handle_off_hook_message ( listener , request ) ;
case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE :
return skinny_handle_open_receive_channel_ack_message ( listener , request ) ;
2010-02-24 12:04:16 +00:00
case KEYPAD_BUTTON_MESSAGE :
return skinny_handle_keypad_button_message ( listener , request ) ;
2010-02-24 12:04:08 +00:00
case ON_HOOK_MESSAGE :
return skinny_handle_on_hook_message ( listener , request ) ;
2010-02-24 12:01:31 +00:00
/* end phase */
2010-02-24 11:59:18 +00:00
case UNREGISTER_MESSAGE :
return skinny_handle_unregister ( listener , request ) ;
2010-02-24 12:03:04 +00:00
case 0xABCDEF : /* the following commands are to avoid compile warnings (which are errors) */
activate_call_plane ( listener , 1 /* line */ ) ;
send_dialed_number ( listener , 0 /* called_party */ , 1 /* line */ , 0 /* call_id */ ) ;
start_tone ( listener , SKINNY_TONE_DIALTONE , 0 , 0 , 0 ) ;
2010-02-24 11:57:45 +00:00
default :
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
2010-02-24 11:59:10 +00:00
" Unknown request type: %x (length=%d). \n " , request - > type , request - > length ) ;
return SWITCH_STATUS_SUCCESS ;
2010-02-24 11:57:45 +00:00
}
}
static switch_status_t skinny_send_reply ( listener_t * listener , skinny_message_t * reply )
{
2010-02-24 11:58:00 +00:00
char * ptr ;
switch_size_t len ;
switch_assert ( reply ! = NULL ) ;
2010-02-24 11:58:36 +00:00
len = reply - > length + 8 ;
2010-02-24 11:58:00 +00:00
ptr = ( char * ) reply ;
2010-02-24 11:59:02 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Sending reply (type=%x,length=%d). \n " ,
reply - > type , reply - > length ) ;
2010-02-24 11:58:00 +00:00
switch_socket_send ( listener - > sock , ptr , & len ) ;
2010-02-24 11:57:45 +00:00
return SWITCH_STATUS_SUCCESS ;
}
/*****************************************************************************/
/* LISTENER FUNCTIONS */
/*****************************************************************************/
static void add_listener ( listener_t * listener )
{
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
switch_assert ( listener ) ;
assert ( listener - > profile ) ;
profile = listener - > profile ;
switch_mutex_lock ( profile - > listener_mutex ) ;
listener - > next = profile - > listeners ;
profile - > listeners = listener ;
switch_mutex_unlock ( profile - > listener_mutex ) ;
2010-02-24 11:57:45 +00:00
}
static void remove_listener ( listener_t * listener )
{
listener_t * l , * last = NULL ;
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
switch_assert ( listener ) ;
assert ( listener - > profile ) ;
profile = listener - > profile ;
2010-02-24 11:57:45 +00:00
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( profile - > listener_mutex ) ;
for ( l = profile - > listeners ; l ; l = l - > next ) {
2010-02-24 11:57:45 +00:00
if ( l = = listener ) {
if ( last ) {
last - > next = l - > next ;
} else {
2010-02-24 11:59:41 +00:00
profile - > listeners = l - > next ;
2010-02-24 11:57:45 +00:00
}
}
last = l ;
}
2010-02-24 11:59:41 +00:00
switch_mutex_unlock ( profile - > listener_mutex ) ;
2010-02-24 11:57:45 +00:00
}
2010-02-24 11:58:15 +00:00
static void walk_listeners ( skinny_listener_callback_func_t callback , void * pvt )
2010-02-24 11:57:45 +00:00
{
2010-02-24 11:59:41 +00:00
switch_hash_index_t * hi ;
void * val ;
skinny_profile_t * profile ;
2010-02-24 11:57:45 +00:00
listener_t * l ;
2010-02-24 11:59:41 +00:00
/* walk listeners */
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( skinny_profile_t * ) val ;
switch_mutex_lock ( profile - > listener_mutex ) ;
for ( l = profile - > listeners ; l ; l = l - > next ) {
callback ( l , pvt ) ;
}
switch_mutex_unlock ( profile - > listener_mutex ) ;
2010-02-24 11:57:45 +00:00
}
}
static void flush_listener ( listener_t * listener , switch_bool_t flush_log , switch_bool_t flush_events )
{
/* TODO */
}
2010-02-24 11:59:49 +00:00
static int dump_device_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
2010-02-24 11:58:15 +00:00
{
2010-02-24 11:59:49 +00:00
switch_stream_handle_t * stream = ( switch_stream_handle_t * ) pArg ;
2010-02-24 11:59:41 +00:00
2010-02-24 12:00:55 +00:00
char * device_name = argv [ 0 ] ;
2010-02-24 11:59:49 +00:00
char * user_id = argv [ 1 ] ;
char * instance = argv [ 2 ] ;
char * ip = argv [ 3 ] ;
2010-02-24 12:00:45 +00:00
char * type = argv [ 4 ] ;
2010-02-24 11:59:49 +00:00
char * max_streams = argv [ 5 ] ;
char * port = argv [ 6 ] ;
char * codec_string = argv [ 7 ] ;
const char * line = " ================================================================================================= " ;
stream - > write_function ( stream , " %s \n " , line ) ;
2010-02-24 12:00:55 +00:00
stream - > write_function ( stream , " DeviceName \t %s \n " , switch_str_nil ( device_name ) ) ;
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " UserId \t %s \n " , user_id ) ;
stream - > write_function ( stream , " Instance \t %s \n " , instance ) ;
stream - > write_function ( stream , " IP \t %s \n " , ip ) ;
2010-02-24 12:00:45 +00:00
stream - > write_function ( stream , " DeviceType \t %s \n " , type ) ;
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " MaxStreams \t %s \n " , max_streams ) ;
stream - > write_function ( stream , " Port \t %s \n " , port ) ;
stream - > write_function ( stream , " Codecs \t %s \n " , codec_string ) ;
stream - > write_function ( stream , " %s \n " , line ) ;
return 0 ;
2010-02-24 11:58:15 +00:00
}
2010-02-24 11:59:49 +00:00
static switch_status_t dump_device ( skinny_profile_t * profile , const char * device_name , switch_stream_handle_t * stream )
2010-02-24 11:58:15 +00:00
{
2010-02-24 11:59:49 +00:00
char * sql ;
2010-02-24 12:00:45 +00:00
if ( ( sql = switch_mprintf ( " SELECT * FROM skinny_devices WHERE name LIKE '%s' " ,
2010-02-24 11:59:49 +00:00
device_name ) ) ) {
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , dump_device_callback , stream ) ;
switch_safe_free ( sql ) ;
2010-02-24 11:58:15 +00:00
}
2010-02-24 11:59:49 +00:00
2010-02-24 11:58:15 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:49 +00:00
2010-02-24 12:00:19 +00:00
static void close_socket ( switch_socket_t * * sock , skinny_profile_t * profile )
2010-02-24 11:57:45 +00:00
{
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( profile - > sock_mutex ) ;
2010-02-24 11:57:45 +00:00
if ( * sock ) {
switch_socket_shutdown ( * sock , SWITCH_SHUTDOWN_READWRITE ) ;
switch_socket_close ( * sock ) ;
* sock = NULL ;
}
2010-02-24 11:59:41 +00:00
switch_mutex_unlock ( profile - > sock_mutex ) ;
2010-02-24 11:57:45 +00:00
}
2010-02-24 11:58:15 +00:00
static switch_status_t kill_listener ( listener_t * listener , void * pvt )
2010-02-24 11:57:45 +00:00
{
2010-02-24 11:58:07 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Killing listener. \n " ) ;
2010-02-24 11:57:45 +00:00
switch_clear_flag ( listener , LFLAG_RUNNING ) ;
2010-02-24 12:00:19 +00:00
close_socket ( & listener - > sock , listener - > profile ) ;
2010-02-24 11:57:45 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:58:15 +00:00
static switch_status_t kill_expired_listener ( listener_t * listener , void * pvt )
2010-02-24 11:58:07 +00:00
{
2010-02-24 11:59:18 +00:00
switch_event_t * event = NULL ;
2010-02-24 11:58:07 +00:00
if ( listener - > expire_time < switch_epoch_time_now ( NULL ) ) {
2010-02-24 11:59:18 +00:00
/* skinny::expire event */
skinny_device_event ( listener , & event , SWITCH_EVENT_CUSTOM , SKINNY_EVENT_EXPIRE ) ;
switch_event_fire ( & event ) ;
2010-02-24 11:58:15 +00:00
return kill_listener ( listener , pvt ) ;
2010-02-24 11:58:07 +00:00
}
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:58:15 +00:00
static switch_status_t keepalive_listener ( listener_t * listener , void * pvt )
2010-02-24 11:58:07 +00:00
{
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
2010-02-24 11:58:07 +00:00
switch_assert ( listener ) ;
2010-02-24 11:59:41 +00:00
assert ( listener - > profile ) ;
profile = listener - > profile ;
2010-02-24 11:58:07 +00:00
2010-02-24 11:59:41 +00:00
listener - > expire_time = switch_epoch_time_now ( NULL ) + profile - > keep_alive * 110 / 100 ;
2010-02-24 11:58:07 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:57:45 +00:00
static void * SWITCH_THREAD_FUNC listener_run ( switch_thread_t * thread , void * obj )
{
listener_t * listener = ( listener_t * ) obj ;
switch_status_t status ;
skinny_message_t * request = NULL ;
2010-02-24 11:59:41 +00:00
skinny_profile_t * profile ;
2010-02-24 12:04:25 +00:00
int destroy_pool = 1 ;
2010-02-24 11:59:41 +00:00
switch_assert ( listener ) ;
assert ( listener - > profile ) ;
profile = listener - > profile ;
2010-02-24 11:57:45 +00:00
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( profile - > listener_mutex ) ;
profile - > listener_threads + + ;
switch_mutex_unlock ( profile - > listener_mutex ) ;
2010-02-24 11:57:45 +00:00
switch_assert ( listener ! = NULL ) ;
switch_socket_opt_set ( listener - > sock , SWITCH_SO_TCP_NODELAY , TRUE ) ;
switch_socket_opt_set ( listener - > sock , SWITCH_SO_NONBLOCK , TRUE ) ;
if ( globals . debug > 0 ) {
if ( zstr ( listener - > remote_ip ) ) {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Connection Open \n " ) ;
2010-02-24 11:57:45 +00:00
} else {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Connection Open from %s:%d \n " , listener - > remote_ip , listener - > remote_port ) ;
2010-02-24 11:57:45 +00:00
}
}
switch_socket_opt_set ( listener - > sock , SWITCH_SO_NONBLOCK , TRUE ) ;
switch_set_flag_locked ( listener , LFLAG_RUNNING ) ;
2010-02-24 11:58:15 +00:00
keepalive_listener ( listener , NULL ) ;
2010-02-24 11:57:45 +00:00
add_listener ( listener ) ;
2010-02-24 11:59:41 +00:00
while ( globals . running & & switch_test_flag ( listener , LFLAG_RUNNING ) & & profile - > listener_ready ) {
2010-02-24 11:58:07 +00:00
status = skinny_read_packet ( listener , & request ) ;
2010-02-24 11:57:45 +00:00
if ( status ! = SWITCH_STATUS_SUCCESS ) {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Socket Error! \n " ) ;
2010-02-24 11:57:45 +00:00
switch_clear_flag_locked ( listener , LFLAG_RUNNING ) ;
break ;
}
if ( ! request ) {
continue ;
}
2010-02-24 11:58:43 +00:00
if ( skinny_handle_request ( listener , request ) ! = SWITCH_STATUS_SUCCESS ) {
2010-02-24 11:57:45 +00:00
switch_clear_flag_locked ( listener , LFLAG_RUNNING ) ;
break ;
}
}
remove_listener ( listener ) ;
if ( globals . debug > 0 ) {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Session complete, waiting for children \n " ) ;
2010-02-24 11:57:45 +00:00
}
switch_thread_rwlock_wrlock ( listener - > rwlock ) ;
flush_listener ( listener , SWITCH_TRUE , SWITCH_TRUE ) ;
if ( listener - > sock ) {
2010-02-24 12:00:19 +00:00
close_socket ( & listener - > sock , profile ) ;
2010-02-24 11:57:45 +00:00
}
switch_thread_rwlock_unlock ( listener - > rwlock ) ;
if ( globals . debug > 0 ) {
2010-02-24 12:04:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Connection Closed \n " ) ;
2010-02-24 11:57:45 +00:00
}
2010-02-24 12:04:25 +00:00
for ( int line = 0 ; line < SKINNY_MAX_BUTTON_COUNT ; line + + ) {
if ( listener - > session [ line ] ) {
switch_channel_clear_flag ( switch_core_session_get_channel ( listener - > session [ line ] ) , CF_CONTROLLED ) ;
//TODO switch_clear_flag_locked(listener, LFLAG_SESSION);
switch_core_session_rwunlock ( listener - > session [ line ] ) ;
destroy_pool = 0 ;
}
}
if ( destroy_pool = = 0 ) {
goto no_destroy_pool ;
}
if ( listener - > pool ) {
2010-02-24 11:57:45 +00:00
switch_memory_pool_t * pool = listener - > pool ;
switch_core_destroy_memory_pool ( & pool ) ;
}
2010-02-24 12:04:25 +00:00
no_destroy_pool :
2010-02-24 11:59:41 +00:00
switch_mutex_lock ( profile - > listener_mutex ) ;
profile - > listener_threads - - ;
switch_mutex_unlock ( profile - > listener_mutex ) ;
2010-02-24 11:57:45 +00:00
return NULL ;
}
/* Create a thread for the socket and launch it */
static void launch_listener_thread ( listener_t * listener )
{
switch_thread_t * thread ;
switch_threadattr_t * thd_attr = NULL ;
switch_threadattr_create ( & thd_attr , listener - > pool ) ;
switch_threadattr_detach_set ( thd_attr , 1 ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
switch_thread_create ( & thread , thd_attr , listener_run , listener , listener - > pool ) ;
}
2010-02-24 11:59:41 +00:00
int skinny_socket_create_and_bind ( skinny_profile_t * profile )
2010-02-24 11:57:45 +00:00
{
switch_status_t rv ;
switch_sockaddr_t * sa ;
switch_socket_t * inbound_socket = NULL ;
listener_t * listener ;
switch_memory_pool_t * pool = NULL , * listener_pool = NULL ;
uint32_t errs = 0 ;
if ( switch_core_new_memory_pool ( & pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " OH OH no pool \n " ) ;
return SWITCH_STATUS_TERM ;
}
2010-02-24 11:59:41 +00:00
while ( globals . running ) {
rv = switch_sockaddr_info_get ( & sa , profile - > ip , SWITCH_INET , profile - > port , 0 , pool ) ;
2010-02-24 11:57:45 +00:00
if ( rv )
goto fail ;
2010-02-24 11:59:41 +00:00
rv = switch_socket_create ( & profile - > sock , switch_sockaddr_get_family ( sa ) , SOCK_STREAM , SWITCH_PROTO_TCP , pool ) ;
2010-02-24 11:57:45 +00:00
if ( rv )
goto sock_fail ;
2010-02-24 11:59:41 +00:00
rv = switch_socket_opt_set ( profile - > sock , SWITCH_SO_REUSEADDR , 1 ) ;
2010-02-24 11:57:45 +00:00
if ( rv )
goto sock_fail ;
2010-02-24 11:59:41 +00:00
rv = switch_socket_bind ( profile - > sock , sa ) ;
2010-02-24 11:57:45 +00:00
if ( rv )
goto sock_fail ;
2010-02-24 11:59:41 +00:00
rv = switch_socket_listen ( profile - > sock , 5 ) ;
2010-02-24 11:57:45 +00:00
if ( rv )
goto sock_fail ;
2010-02-24 11:59:49 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Socket up listening on %s:%u \n " , profile - > ip , profile - > port ) ;
2010-02-24 11:57:45 +00:00
break ;
sock_fail :
2010-02-24 11:59:41 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Socket Error! Could not listen on %s:%u \n " , profile - > ip , profile - > port ) ;
2010-02-24 11:57:45 +00:00
switch_yield ( 100000 ) ;
}
2010-02-24 11:59:41 +00:00
profile - > listener_ready = 1 ;
2010-02-24 11:57:45 +00:00
2010-02-24 11:59:41 +00:00
while ( globals . running ) {
2010-02-24 11:57:45 +00:00
if ( switch_core_new_memory_pool ( & listener_pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " OH OH no pool \n " ) ;
goto fail ;
}
2010-02-24 11:59:41 +00:00
if ( ( rv = switch_socket_accept ( & inbound_socket , profile - > sock , listener_pool ) ) ) {
if ( ! globals . running ) {
2010-02-24 11:57:45 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " Shutting Down \n " ) ;
goto end ;
} else {
/* I wish we could use strerror_r here but its not defined everywhere =/ */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Socket Error [%s] \n " , strerror ( errno ) ) ;
if ( + + errs > 100 ) {
goto end ;
}
}
} else {
errs = 0 ;
}
if ( ! ( listener = switch_core_alloc ( listener_pool , sizeof ( * listener ) ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Memory Error \n " ) ;
break ;
}
switch_thread_rwlock_create ( & listener - > rwlock , listener_pool ) ;
listener - > sock = inbound_socket ;
listener - > pool = listener_pool ;
listener_pool = NULL ;
2010-02-24 11:59:49 +00:00
strcpy ( listener - > device_name , " " ) ;
2010-02-24 11:59:41 +00:00
listener - > profile = profile ;
2010-02-24 11:57:45 +00:00
switch_mutex_init ( & listener - > flag_mutex , SWITCH_MUTEX_NESTED , listener - > pool ) ;
switch_socket_addr_get ( & listener - > sa , SWITCH_TRUE , listener - > sock ) ;
switch_get_addr ( listener - > remote_ip , sizeof ( listener - > remote_ip ) , listener - > sa ) ;
listener - > remote_port = switch_sockaddr_get_port ( listener - > sa ) ;
launch_listener_thread ( listener ) ;
}
end :
2010-02-24 12:00:19 +00:00
close_socket ( & profile - > sock , profile ) ;
2010-02-24 11:57:45 +00:00
if ( pool ) {
switch_core_destroy_memory_pool ( & pool ) ;
}
if ( listener_pool ) {
switch_core_destroy_memory_pool ( & listener_pool ) ;
}
fail :
return SWITCH_STATUS_TERM ;
}
/*****************************************************************************/
/* MODULE FUNCTIONS */
/*****************************************************************************/
2010-02-24 11:59:41 +00:00
static void skinny_profile_set ( skinny_profile_t * profile , char * var , char * val )
{
if ( ! var )
return ;
if ( ! strcasecmp ( var , " domain " ) ) {
profile - > domain = switch_core_strdup ( module_pool , val ) ;
} else if ( ! strcasecmp ( var , " ip " ) ) {
profile - > ip = switch_core_strdup ( module_pool , val ) ;
} else if ( ! strcasecmp ( var , " dialplan " ) ) {
profile - > dialplan = switch_core_strdup ( module_pool , val ) ;
} else if ( ! strcasecmp ( var , " odbc-dsn " ) & & ! zstr ( val ) ) {
if ( switch_odbc_available ( ) ) {
profile - > odbc_dsn = switch_core_strdup ( module_pool , val ) ;
if ( ( profile - > odbc_user = strchr ( profile - > odbc_dsn , ' : ' ) ) ) {
* profile - > odbc_user + + = ' \0 ' ;
if ( ( profile - > odbc_pass = strchr ( profile - > odbc_user , ' : ' ) ) ) {
* profile - > odbc_pass + + = ' \0 ' ;
}
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " ODBC IS NOT AVAILABLE! \n " ) ;
}
}
}
2010-02-24 11:57:37 +00:00
static switch_status_t load_skinny_config ( void )
{
char * cf = " skinny.conf " ;
2010-02-24 11:59:41 +00:00
switch_xml_t xcfg , xml , xsettings , xprofiles , xprofile , xparam ;
2010-02-24 11:57:37 +00:00
memset ( & globals , 0 , sizeof ( globals ) ) ;
2010-02-24 11:59:41 +00:00
globals . running = 1 ;
switch_core_hash_init ( & globals . profile_hash , module_pool ) ;
switch_mutex_init ( & globals . calls_mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
if ( ! ( xml = switch_xml_open_cfg ( cf , & xcfg , NULL ) ) ) {
2010-02-24 11:57:37 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Open of %s failed \n " , cf ) ;
return SWITCH_STATUS_TERM ;
}
2010-02-24 11:59:41 +00:00
if ( ( xsettings = switch_xml_child ( xcfg , " settings " ) ) ) {
for ( xparam = switch_xml_child ( xsettings , " param " ) ; xparam ; xparam = xparam - > next ) {
char * var = ( char * ) switch_xml_attr_soft ( xparam , " name " ) ;
char * val = ( char * ) switch_xml_attr_soft ( xparam , " value " ) ;
2010-02-24 11:57:37 +00:00
if ( ! strcmp ( var , " debug " ) ) {
globals . debug = atoi ( val ) ;
} else if ( ! strcmp ( var , " codec-prefs " ) ) {
set_global_codec_string ( val ) ;
globals . codec_order_last = switch_separate_string ( globals . codec_string , ' , ' , globals . codec_order , SWITCH_MAX_CODECS ) ;
} else if ( ! strcmp ( var , " codec-master " ) ) {
if ( ! strcasecmp ( val , " us " ) ) {
switch_set_flag ( & globals , GFLAG_MY_CODEC_PREFS ) ;
}
} else if ( ! strcmp ( var , " codec-rates " ) ) {
set_global_codec_rates_string ( val ) ;
globals . codec_rates_last = switch_separate_string ( globals . codec_rates_string , ' , ' , globals . codec_rates , SWITCH_MAX_CODECS ) ;
}
2010-02-24 11:59:41 +00:00
} /* param */
} /* settings */
if ( ( xprofiles = switch_xml_child ( xcfg , " profiles " ) ) ) {
for ( xprofile = switch_xml_child ( xprofiles , " profile " ) ; xprofile ; xprofile = xprofile - > next ) {
char * profile_name = ( char * ) switch_xml_attr_soft ( xprofile , " name " ) ;
switch_xml_t xsettings = switch_xml_child ( xprofile , " settings " ) ;
if ( zstr ( profile_name ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" <profile> is missing name attribute \n " ) ;
continue ;
}
if ( xsettings ) {
char dbname [ 256 ] ;
switch_core_db_t * db ;
skinny_profile_t * profile = NULL ;
switch_xml_t param ;
profile = switch_core_alloc ( module_pool , sizeof ( skinny_profile_t ) ) ;
profile - > name = profile_name ;
for ( param = switch_xml_child ( xsettings , " param " ) ; param ; param = param - > next ) {
char * var = ( char * ) switch_xml_attr_soft ( param , " name " ) ;
char * val = ( char * ) switch_xml_attr_soft ( param , " value " ) ;
if ( ! strcmp ( var , " domain " ) ) {
skinny_profile_set ( profile , " domain " , val ) ;
} else if ( ! strcmp ( var , " ip " ) ) {
skinny_profile_set ( profile , " ip " , val ) ;
} else if ( ! strcmp ( var , " port " ) ) {
profile - > port = atoi ( val ) ;
} else if ( ! strcmp ( var , " dialplan " ) ) {
skinny_profile_set ( profile , " dialplan " , val ) ;
} else if ( ! strcmp ( var , " keep-alive " ) ) {
profile - > keep_alive = atoi ( val ) ;
} else if ( ! strcmp ( var , " date-format " ) ) {
memcpy ( profile - > date_format , val , 6 ) ;
}
} /* param */
if ( ! profile - > dialplan ) {
skinny_profile_set ( profile , " dialplan " , " default " ) ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 11:59:41 +00:00
if ( ! profile - > port ) {
profile - > port = 2000 ;
}
2010-02-24 11:57:37 +00:00
2010-02-24 11:59:41 +00:00
switch_snprintf ( dbname , sizeof ( dbname ) , " skinny_%s " , profile - > name ) ;
profile - > dbname = switch_core_strdup ( module_pool , dbname ) ;
if ( switch_odbc_available ( ) & & profile - > odbc_dsn ) {
if ( ! ( profile - > master_odbc = switch_odbc_handle_new ( profile - > odbc_dsn , profile - > odbc_user , profile - > odbc_pass ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot Open ODBC Database! \n " ) ;
continue ;
}
if ( switch_odbc_handle_connect ( profile - > master_odbc ) ! = SWITCH_ODBC_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot Open ODBC Database! \n " ) ;
continue ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Connected ODBC DSN: %s \n " , profile - > odbc_dsn ) ;
2010-02-24 11:59:56 +00:00
switch_odbc_handle_exec ( profile - > master_odbc , devices_sql , NULL , NULL ) ;
2010-02-24 12:00:45 +00:00
switch_odbc_handle_exec ( profile - > master_odbc , buttons_sql , NULL , NULL ) ;
2010-02-24 11:59:41 +00:00
} else {
if ( ( db = switch_core_db_open_file ( profile - > dbname ) ) ) {
2010-02-24 11:59:56 +00:00
switch_core_db_test_reactive ( db , " SELECT * FROM skinny_devices " , NULL , devices_sql ) ;
2010-02-24 12:00:45 +00:00
switch_core_db_test_reactive ( db , " SELECT * FROM skinny_buttons " , NULL , buttons_sql ) ;
2010-02-24 11:59:41 +00:00
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot Open SQL Database! \n " ) ;
continue ;
}
switch_core_db_close ( db ) ;
}
2010-02-24 12:00:28 +00:00
skinny_execute_sql_callback ( profile , profile - > listener_mutex , " DELETE FROM skinny_devices " , NULL , NULL ) ;
2010-02-24 12:00:45 +00:00
skinny_execute_sql_callback ( profile , profile - > listener_mutex , " DELETE FROM skinny_buttons " , NULL , NULL ) ;
2010-02-24 12:00:28 +00:00
2010-02-24 12:01:47 +00:00
switch_core_hash_init ( & profile - > session_hash , module_pool ) ;
2010-02-24 11:59:41 +00:00
switch_core_hash_insert ( globals . profile_hash , profile - > name , profile ) ;
profile = NULL ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" Settings are missing from profile %s. \n " , profile_name ) ;
} /* settings */
} /* profile */
}
2010-02-24 11:57:37 +00:00
switch_xml_free ( xml ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-24 11:59:49 +00:00
static switch_status_t cmd_status_profile ( const char * profile_name , switch_stream_handle_t * stream )
2010-02-24 11:58:15 +00:00
{
2010-02-24 11:59:49 +00:00
skinny_profile_t * profile ;
2010-02-24 12:00:11 +00:00
if ( ( profile = skinny_find_profile ( profile_name ) ) ) {
2010-02-24 11:59:49 +00:00
dump_profile ( profile , stream ) ;
} else {
stream - > write_function ( stream , " Profile not found! \n " ) ;
2010-02-24 11:58:15 +00:00
}
2010-02-24 11:59:49 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t cmd_status_profile_device ( const char * profile_name , const char * device_name , switch_stream_handle_t * stream )
{
skinny_profile_t * profile ;
2010-02-24 12:00:11 +00:00
if ( ( profile = skinny_find_profile ( profile_name ) ) ) {
2010-02-24 11:59:49 +00:00
dump_device ( profile , device_name , stream ) ;
2010-02-24 11:58:15 +00:00
} else {
2010-02-24 11:59:49 +00:00
stream - > write_function ( stream , " Profile not found! \n " ) ;
2010-02-24 11:58:15 +00:00
}
2010-02-24 11:59:49 +00:00
2010-02-24 11:58:15 +00:00
return SWITCH_STATUS_SUCCESS ;
}
SWITCH_STANDARD_API ( skinny_function )
{
char * argv [ 1024 ] = { 0 } ;
int argc = 0 ;
char * mycmd = NULL ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
const char * usage_string = " USAGE: \n "
" -------------------------------------------------------------------------------- \n "
" skinny help \n "
2010-02-24 11:59:49 +00:00
" skinny status profile <profile_name> \n "
" skinny status profile <profile_name> device <device_name> \n "
2010-02-24 11:58:15 +00:00
" -------------------------------------------------------------------------------- \n " ;
if ( session ) {
return SWITCH_STATUS_FALSE ;
}
if ( zstr ( cmd ) ) {
stream - > write_function ( stream , " %s " , usage_string ) ;
goto done ;
}
if ( ! ( mycmd = strdup ( cmd ) ) ) {
status = SWITCH_STATUS_MEMERR ;
goto done ;
}
if ( ! ( argc = switch_separate_string ( mycmd , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) | | ! argv [ 0 ] ) {
stream - > write_function ( stream , " %s " , usage_string ) ;
goto done ;
}
2010-02-24 11:59:49 +00:00
if ( argc = = 3 & & ! strcasecmp ( argv [ 0 ] , " status " ) & & ! strcasecmp ( argv [ 1 ] , " profile " ) ) {
status = cmd_status_profile ( argv [ 2 ] , stream ) ;
} else if ( argc = = 5 & & ! strcasecmp ( argv [ 0 ] , " status " ) & & ! strcasecmp ( argv [ 1 ] , " profile " ) & & ! strcasecmp ( argv [ 3 ] , " device " ) ) {
status = cmd_status_profile_device ( argv [ 2 ] , argv [ 4 ] , stream ) ;
2010-02-24 11:58:15 +00:00
} else if ( ! strcasecmp ( argv [ 0 ] , " help " ) ) {
stream - > write_function ( stream , " %s " , usage_string ) ;
goto done ;
} else {
stream - > write_function ( stream , " Unknown Command [%s] \n " , argv [ 0 ] ) ;
}
2010-02-24 11:59:49 +00:00
done :
2010-02-24 11:58:15 +00:00
switch_safe_free ( mycmd ) ;
return status ;
}
2010-02-24 11:57:37 +00:00
static void event_handler ( switch_event_t * event )
{
2010-02-24 11:57:45 +00:00
if ( event - > event_id = = SWITCH_EVENT_HEARTBEAT ) {
2010-02-24 11:58:15 +00:00
walk_listeners ( kill_expired_listener , NULL ) ;
2010-02-24 11:57:37 +00:00
}
}
2010-02-24 11:59:49 +00:00
static switch_status_t skinny_list_profiles ( const char * line , const char * cursor , switch_console_callback_match_t * * matches )
2010-02-24 11:58:15 +00:00
{
switch_console_callback_match_t * my_matches = NULL ;
switch_status_t status = SWITCH_STATUS_FALSE ;
2010-02-24 11:59:41 +00:00
switch_hash_index_t * hi ;
void * val ;
skinny_profile_t * profile ;
2010-02-24 11:59:49 +00:00
/* walk profiles */
2010-02-24 11:59:41 +00:00
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( skinny_profile_t * ) val ;
2010-02-24 11:59:49 +00:00
switch_console_push_match ( & my_matches , profile - > name ) ;
}
if ( my_matches ) {
* matches = my_matches ;
status = SWITCH_STATUS_SUCCESS ;
}
return status ;
}
2010-02-24 12:00:04 +00:00
struct match_helper {
switch_console_callback_match_t * my_matches ;
} ;
2010-02-24 11:59:49 +00:00
static int skinny_list_devices_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
2010-02-24 12:00:04 +00:00
struct match_helper * h = ( struct match_helper * ) pArg ;
2010-02-24 11:59:49 +00:00
char * device_name = argv [ 0 ] ;
2010-02-24 12:00:04 +00:00
switch_console_push_match ( & h - > my_matches , device_name ) ;
2010-02-24 11:59:49 +00:00
return 0 ;
}
static switch_status_t skinny_list_devices ( const char * line , const char * cursor , switch_console_callback_match_t * * matches )
{
2010-02-24 12:00:04 +00:00
struct match_helper h = { 0 } ;
2010-02-24 11:59:49 +00:00
switch_status_t status = SWITCH_STATUS_FALSE ;
skinny_profile_t * profile ;
char * sql ;
char * myline ;
char * argv [ 1024 ] = { 0 } ;
int argc = 0 ;
if ( ! ( myline = strdup ( line ) ) ) {
status = SWITCH_STATUS_MEMERR ;
return status ;
}
if ( ! ( argc = switch_separate_string ( myline , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) | | argc ! = 5 ) {
return status ;
}
2010-02-24 12:00:11 +00:00
if ( ( profile = skinny_find_profile ( argv [ 3 ] ) ) ) {
2010-02-24 12:00:45 +00:00
if ( ( sql = switch_mprintf ( " SELECT name FROM skinny_devices " ) ) ) {
2010-02-24 12:00:04 +00:00
skinny_execute_sql_callback ( profile , profile - > listener_mutex , sql , skinny_list_devices_callback , & h ) ;
2010-02-24 11:59:49 +00:00
switch_safe_free ( sql ) ;
2010-02-24 11:58:15 +00:00
}
}
2010-02-24 11:59:41 +00:00
2010-02-24 12:00:04 +00:00
if ( h . my_matches ) {
* matches = h . my_matches ;
2010-02-24 11:58:15 +00:00
status = SWITCH_STATUS_SUCCESS ;
}
return status ;
}
2010-02-24 11:57:37 +00:00
SWITCH_MODULE_LOAD_FUNCTION ( mod_skinny_load )
{
2010-02-24 11:59:41 +00:00
switch_hash_index_t * hi ;
void * val ;
skinny_profile_t * profile ;
2010-02-24 11:58:15 +00:00
switch_api_interface_t * api_interface ;
2010-02-24 11:57:37 +00:00
module_pool = pool ;
load_skinny_config ( ) ;
2010-02-24 11:59:41 +00:00
/* init listeners */
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( skinny_profile_t * ) val ;
switch_mutex_init ( & profile - > listener_mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
switch_mutex_init ( & profile - > sock_mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
2010-02-24 11:57:45 +00:00
2010-02-24 11:59:41 +00:00
}
2010-02-24 11:57:45 +00:00
if ( ( switch_event_bind_removable ( modname , SWITCH_EVENT_HEARTBEAT , NULL , event_handler , NULL , & globals . heartbeat_node ) ! = SWITCH_STATUS_SUCCESS ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Couldn't bind our heartbeat handler! \n " ) ;
/* Not such severe to prevent loading */
}
2010-02-24 11:58:54 +00:00
if ( switch_event_reserve_subclass ( SKINNY_EVENT_REGISTER ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Couldn't register subclass %s! \n " , SKINNY_EVENT_REGISTER ) ;
return SWITCH_STATUS_TERM ;
}
2010-02-24 11:57:37 +00:00
/* connect my internal structure to the blank pointer passed to me */
* module_interface = switch_loadable_module_create_module_interface ( pool , modname ) ;
skinny_endpoint_interface = switch_loadable_module_create_interface ( * module_interface , SWITCH_ENDPOINT_INTERFACE ) ;
skinny_endpoint_interface - > interface_name = " skinny " ;
skinny_endpoint_interface - > io_routines = & skinny_io_routines ;
skinny_endpoint_interface - > state_handler = & skinny_state_handlers ;
2010-02-24 11:58:15 +00:00
SWITCH_ADD_API ( api_interface , " skinny " , " Skinny Controls " , skinny_function , " <cmd> <args> " ) ;
switch_console_set_complete ( " add skinny help " ) ;
2010-02-24 11:59:49 +00:00
switch_console_set_complete ( " add skinny status profile ::skinny::list_profiles " ) ;
switch_console_set_complete ( " add skinny status profile ::skinny::list_profiles device ::skinny::list_devices " ) ;
2010-02-24 11:58:15 +00:00
2010-02-24 11:59:49 +00:00
switch_console_add_complete_func ( " ::skinny::list_profiles " , skinny_list_profiles ) ;
2010-02-24 11:58:15 +00:00
switch_console_add_complete_func ( " ::skinny::list_devices " , skinny_list_devices ) ;
2010-02-24 11:57:37 +00:00
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS ;
}
SWITCH_MODULE_RUNTIME_FUNCTION ( mod_skinny_runtime )
{
2010-02-24 11:59:41 +00:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
switch_hash_index_t * hi ;
void * val ;
skinny_profile_t * profile ;
/* launch listeners */
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( skinny_profile_t * ) val ;
status = skinny_socket_create_and_bind ( profile ) ;
if ( status ! = SWITCH_STATUS_SUCCESS ) {
return status ;
}
}
return status ;
2010-02-24 11:57:37 +00:00
}
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_skinny_shutdown )
{
2010-02-24 11:59:41 +00:00
switch_hash_index_t * hi ;
void * val ;
skinny_profile_t * profile ;
2010-02-24 11:57:45 +00:00
int sanity = 0 ;
2010-02-24 11:58:54 +00:00
switch_event_free_subclass ( SKINNY_EVENT_REGISTER ) ;
2010-02-24 11:57:45 +00:00
switch_event_unbind ( & globals . heartbeat_node ) ;
2010-02-24 11:57:37 +00:00
2010-02-24 11:59:41 +00:00
globals . running = 0 ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
/* kill listeners */
2010-02-24 11:58:15 +00:00
walk_listeners ( kill_listener , NULL ) ;
2010-02-24 11:57:37 +00:00
2010-02-24 12:00:11 +00:00
/* close sockets */
2010-02-24 11:59:41 +00:00
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( skinny_profile_t * ) val ;
2010-02-24 11:57:45 +00:00
2010-02-24 12:00:19 +00:00
close_socket ( & profile - > sock , profile ) ;
2010-02-24 11:59:41 +00:00
while ( profile - > listener_threads ) {
switch_yield ( 100000 ) ;
walk_listeners ( kill_listener , NULL ) ;
if ( + + sanity > = 200 ) {
break ;
}
2010-02-24 11:57:37 +00:00
}
}
2010-02-24 11:57:45 +00:00
2010-02-24 11:57:37 +00:00
switch_safe_free ( globals . codec_string ) ;
switch_safe_free ( globals . codec_rates_string ) ;
return SWITCH_STATUS_SUCCESS ;
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 :
*/