[commit] r11153 - in freeswitch/branches/ctrix/mod_airpe: . config
Author: ctrix Date: Mon Jan 12 17:24:44 2009 New Revision: 11153 Log: Initial import of airpe... Not yet fully working, don't expect too much. Added: freeswitch/branches/ctrix/mod_airpe/airpe_api.c freeswitch/branches/ctrix/mod_airpe/airpe_apps.c freeswitch/branches/ctrix/mod_airpe/airpe_if_common.c freeswitch/branches/ctrix/mod_airpe/airpe_if_osx.c freeswitch/branches/ctrix/mod_airpe/airpe_if_win32.c freeswitch/branches/ctrix/mod_airpe/airpe_if_x11.c freeswitch/branches/ctrix/mod_airpe/config/ freeswitch/branches/ctrix/mod_airpe/config/airpe.conf.xml freeswitch/branches/ctrix/mod_airpe/mod_airpe.c freeswitch/branches/ctrix/mod_airpe/mod_airpe.h Added: freeswitch/branches/ctrix/mod_airpe/airpe_api.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_api.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,262 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#include "mod_airpe.h" + +#define AIRPE_MOODTEXT_SYNTAX "airpe_moodtext <clientname> <text>" +SWITCH_STANDARD_API(airpe_moodtext_command) +{ + int argc = 0; + char *argv[2] = { 0 }; + char *mycmd = NULL; + char *client = NULL; + airpe_interface_t *airpe = NULL; + + if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + switch_assert(argv[0]); + } + + if ( argc < 1 ) { + stream->write_function(stream, "-USAGE: %s\n", AIRPE_MOODTEXT_SYNTAX); + goto done; + } + + client = argv[0]; + airpe = airpe_find_interface(client); + + if ( !airpe ) { + stream->write_function(stream, "-ERR interface not found for client "); + stream->write_function(stream, client); + stream->write_function(stream, "\n"); + goto done; + } + + if ( argc == 1 ) { + stream->write_function(stream, "+OK: "); + if ( airpe->mood_text ) + stream->write_function(stream, airpe->mood_text); + else + stream->write_function(stream, "UNKNOWN"); + stream->write_function(stream, "\n"); + } + else { + if ( airpe_set_mood_text(airpe, argv[1]) != SWITCH_STATUS_SUCCESS ) { + stream->write_function(stream, "-ERR cannot send message to the client\n"); + goto done; + } + else { + char buf[SKYPE_MSG_LEN]; + snprintf(buf, SKYPE_MSG_LEN, "SET PROFILE MOOD_TEXT %s", airpe->mood_text ); + airpe_cmd_write(airpe, buf); + } + stream->write_function(stream, "+OK mood_text set.\n"); + } + + done: + return SWITCH_STATUS_SUCCESS; +} + +#define AIRPE_INFO_SYNTAX "airpe_status <clientname>" +SWITCH_STANDARD_API(airpe_info_command) +{ + int argc = 0; + char *argv[4] = { 0 }; + char *mycmd = NULL; + char *client = NULL; + char buf[512]; + airpe_interface_t *airpe = NULL; + + if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + switch_assert(argv[0]); + } + + if ( argc < 1 ) { + stream->write_function(stream, "-USAGE: %s\n", AIRPE_INFO_SYNTAX); + goto done; + } + + client = argv[0]; + airpe = airpe_find_interface(client); + + if ( !airpe ) { + stream->write_function(stream, "-ERR interface not found for client "); + stream->write_function(stream, client); + stream->write_function(stream, "\n"); + goto done; + } + + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Client name", airpe->name); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Dialplan", airpe->dialplan); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Context", airpe->context); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Destination", airpe->destination); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Skype username", airpe->skype_user); + stream->write_function(stream, buf); + if ( airpe->audio_in_port > 0 ) + switch_snprintf(buf, sizeof(buf), "%-20s: %d\n", "Audio in port", airpe->audio_in_port); + else + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Audio in port", "NOT SET"); + stream->write_function(stream, buf); + if ( airpe->audio_in_port > 0 ) + switch_snprintf(buf, sizeof(buf), "%-20s: %d\n", "Audio out port", airpe->audio_out_port); + else + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Audio out port", "NOT SET"); + stream->write_function(stream, buf); + + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Connection status", skype_conn_status_string(airpe->conn_status) ); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Client status", skype_user_status_string(airpe->user_status) ); + stream->write_function(stream, buf); + + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Skype version", (airpe->skype_version) ? airpe->skype_version : "Unknown" ); + stream->write_function(stream, buf); + switch_snprintf(buf, sizeof(buf), "%-20s: %s\n", "Mood text", (airpe->mood_text ) ? airpe->mood_text : "Unknown" ); + stream->write_function(stream, buf); + + done: + return SWITCH_STATUS_SUCCESS; +} + +#define AIRPE_STATUS_SYNTAX "airpe_status <clientname> <status>" +SWITCH_STANDARD_API(airpe_status_command) +{ + int argc = 0; + char *argv[4] = { 0 }; + char *mycmd = NULL; + char *client = NULL; + char *cstatus= NULL; + airpe_interface_t *airpe = NULL; + + if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + switch_assert(argv[0]); + } + + if ( argc < 1 ) { + stream->write_function(stream, "-USAGE: %s\n", AIRPE_STATUS_SYNTAX); + goto done; + } + + client = argv[0]; + if ( argc > 1 ) + cstatus = argv[1]; + + airpe = airpe_find_interface(client); + + if ( !airpe ) { + stream->write_function(stream, "-ERR interface not found: \n"); + stream->write_function(stream, client); + stream->write_function(stream, "\n"); + goto done; + } + + if ( !cstatus ) { + const char *str_status = NULL; + str_status = skype_user_status_string( airpe->user_status ); + stream->write_function(stream, "+OK status is "); + if ( str_status ) + stream->write_function(stream, str_status); + else + stream->write_function(stream, "... uhmmm, ehmm, DUNNO... "); + stream->write_function(stream, "\n"); + } + else { + char buf[SKYPE_MSG_LEN]; + const char *str_status = NULL; + + if ( !strncasecmp(cstatus, "unknown", sizeof("unknown") ) ) + str_status = skype_user_status_string(USER_STATUS_UNKNOWN); + else if ( !strncasecmp(cstatus, "online", sizeof("online") ) ) + str_status = skype_user_status_string(USER_STATUS_ONLINE); + else if ( !strncasecmp(cstatus, "offline", sizeof("offline") ) ) + str_status = skype_user_status_string(USER_STATUS_OFFLINE); + else if ( !strncasecmp(cstatus, "skypeme", sizeof("skypeme") ) ) + str_status = skype_user_status_string(USER_STATUS_SKYPEME); + else if ( !strncasecmp(cstatus, "away", sizeof("away") ) ) + str_status = skype_user_status_string(USER_STATUS_AWAY); + else if ( !strncasecmp(cstatus, "na", sizeof("na") ) ) + str_status = skype_user_status_string(USER_STATUS_NA); + else if ( !strncasecmp(cstatus, "dnd", sizeof("dnd") ) ) + str_status = skype_user_status_string(USER_STATUS_DND); + else if ( !strncasecmp(cstatus, "invisible", sizeof("invisible") ) ) + str_status = skype_user_status_string(USER_STATUS_INVISIBLE); + else if ( !strncasecmp(cstatus, "loggedout", sizeof("loggedout") ) ) + str_status = skype_user_status_string(USER_STATUS_LOGGEDOUT); + + if ( str_status ) { + snprintf(buf, SKYPE_MSG_LEN, "SET USERSTATUS %s", cstatus ); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) { + stream->write_function(stream, "-ERR cannot send message to the client\n"); + goto done; + } + stream->write_function(stream, "+OK status set.\n"); + } + else { + stream->write_function(stream, "-ERR invalid status string\n"); + } + } + + done: + return SWITCH_STATUS_SUCCESS; +} + + + +switch_status_t airpe_register_api( switch_loadable_module_interface_t **module_interface ) { + + switch_api_interface_t *api; + + switch_console_set_complete("add airpe_moodtext "); + SWITCH_ADD_API(api, "airpe_moodtext", "sets the mood text of an airpe client", airpe_moodtext_command, AIRPE_MOODTEXT_SYNTAX); + + switch_console_set_complete("add airpe_info "); + SWITCH_ADD_API(api, "airpe_info", "shows the status of an airpe client", airpe_info_command, AIRPE_INFO_SYNTAX); + + switch_console_set_complete("add airpe_status "); + SWITCH_ADD_API(api, "airpe_status", "sets the status of an airpe client", airpe_status_command, AIRPE_STATUS_SYNTAX); + + 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/airpe_apps.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_apps.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,56 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#include "mod_airpe.h" + +SWITCH_STANDARD_APP(airpe_app_test) +{ +} + + +switch_status_t airpe_register_apps( switch_loadable_module_interface_t **module_interface ) { + switch_application_interface_t *app_interface; + + SWITCH_ADD_APP(app_interface, "airpe_test", "test text", "test text", airpe_app_test, "", SAF_SUPPORT_NOMEDIA); + + 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 expandtab: + */ + Added: freeswitch/branches/ctrix/mod_airpe/airpe_if_common.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_if_common.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,770 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#include "mod_airpe.h" + +static struct { + USER_STATUS status; + const char *name; +} user_status_names[] = { + { USER_STATUS_UNKNOWN, "UNKNOWN" }, + { USER_STATUS_ONLINE, "ONLINE" }, + { USER_STATUS_OFFLINE, "OFFLINE" }, + { USER_STATUS_SKYPEME, "SKYPEME" }, + { USER_STATUS_AWAY, "AWAY" }, + { USER_STATUS_NA, "NA" }, + { USER_STATUS_DND, "DND" }, + { USER_STATUS_INVISIBLE, "INVISIBLE" }, + { USER_STATUS_LOGGEDOUT, "LOGGEDOUT" }, +}; + +static struct { + CONN_STATUS status; + const char *name; +} connection_status_names[] = { + { CONN_STATUS_UNKNOWN, "UNKNOWN" }, + { CONN_STATUS_OFFLINE, "OFFLINE" }, + { CONN_STATUS_CONNECTING, "CONNECTING" }, + { CONN_STATUS_PAUSING, "PAUSING" }, + { CONN_STATUS_ONLINE, "ONLINE" }, +}; + +/* ************************************************************************* */ + +const char *skype_user_status_string(USER_STATUS st) { + int t; + const char *ret = NULL; + + for ( t = 0; t < sizeof(user_status_names) / sizeof(user_status_names[0]); t++ ) { + if ( user_status_names[t].status == st ) { + ret = user_status_names[t].name; + break; + } + } + return ret; +} + +USER_STATUS skype_user_status(const char *str) { + int t; + USER_STATUS ret = USER_STATUS_UNKNOWN; + + for ( t = 0; t < sizeof(user_status_names) / sizeof(user_status_names[0]); t++ ) { + if ( !strncasecmp(user_status_names[t].name, str, strlen(user_status_names[t].name) ) ) { + ret = user_status_names[t].status; + break; + } + } + return ret; +} + +/* ************************************************************************* */ + +const char *skype_conn_status_string(CONN_STATUS st) { + int t; + const char *ret = NULL; + + for ( t = 0; t < sizeof(connection_status_names) / sizeof(connection_status_names[0]); t++ ) { + if ( connection_status_names[t].status == st ) { + ret = connection_status_names[t].name; + break; + } + } + return ret; +} + +CONN_STATUS skype_conn_status(const char *str) { + int t; + CONN_STATUS ret = CONN_STATUS_UNKNOWN; + + for ( t = 0; t < sizeof(connection_status_names) / sizeof(connection_status_names[0]); t++ ) { + if ( !strncasecmp(connection_status_names[t].name, str, strlen(connection_status_names[t].name) ) ) { + ret = connection_status_names[t].status; + break; + } + } + return ret; +} + +/* ************************************************************************* */ + +switch_status_t airpe_set_version(airpe_interface_t *airpe, char *string) { + assert(airpe); + switch_safe_free(airpe->skype_version); + if ( string ) + airpe->skype_version = strdup(string); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_set_mood_text(airpe_interface_t *airpe, char *string) { + assert(airpe); + switch_safe_free(airpe->mood_text); + if ( string ) + airpe->mood_text = strdup(string); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_set_partner_displayname(airpe_interface_t *airpe, char *string) { + assert(airpe); + switch_safe_free(airpe->partner_displayname); + if ( string ) + airpe->partner_displayname = strdup(string); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_set_partner_handle(airpe_interface_t *airpe, char *string) { + assert(airpe); + switch_safe_free(airpe->partner_handle); + if ( string ) + airpe->partner_handle = strdup(string); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_cmd_write(airpe_interface_t *airpe, char *msg) { + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "msg to airpe client %s: %s\n", airpe->name, msg); + return airpe_skype_send_message(airpe->skype_window, msg); +} + +switch_status_t airpe_originate(airpe_interface_t *airpe, char *dest) { + char msg[SKYPE_MSG_LEN]; + + /* + Those commands are outdated and shouldn't be used + Documented so that no one adds them. + airpe_cmd_write(p, "SET AGC OFF"); + airpe_cmd_write(p, "SET AEC OFF"); + */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "airpe client %s: Originating to %s\n", airpe->name, dest); + + snprintf(msg, sizeof(msg), "CALL %s", dest); + + if ( (airpe_cmd_write(airpe, msg) != SWITCH_STATUS_SUCCESS) ) { + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_explicit_hangup(airpe_interface_t *airpe) { + char buf[SKYPE_MSG_LEN]; + + if ( airpe->active_call_id > 0 ) { + snprintf(buf, sizeof(buf), "SET CALL %d STATUS FINISHED", airpe->active_call_id); + return airpe_cmd_write(airpe, buf); + } + return SWITCH_STATUS_FALSE; +} + + + +/***************************************************************************** + SKYPE AUDIO + *****************************************************************************/ + +switch_status_t airpe_hangup( private_object_t *pvt ) { + airpe_interface_t *airpe; + + assert(pvt); + assert(pvt->session); + assert(pvt->airpe); + + airpe = pvt->airpe; + + if ( airpe->audio_in_active_socket ) { + switch_socket_shutdown(airpe->audio_in_active_socket, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(airpe->audio_in_active_socket); + airpe->audio_in_active_socket = 0; + } + + if ( airpe->audio_out_active_socket ) { + switch_socket_shutdown(airpe->audio_out_active_socket, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(airpe->audio_out_active_socket); + airpe->audio_out_active_socket = 0; + } + + airpe->tech_pvt = NULL; + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_audio_write( private_object_t *pvt, switch_frame_t *frame ) { + switch_socket_t *inbound_socket = NULL; + switch_memory_pool_t *pool; + airpe_interface_t *airpe; + switch_status_t rv; + switch_size_t mlen; + char *mbuf; + + assert(pvt); + assert(pvt->session); + assert(pvt->airpe); + + airpe = pvt->airpe; + pool = switch_core_session_get_pool(pvt->session); + + if ( !airpe->audio_in_active_socket ) { + rv = switch_socket_accept(&inbound_socket, airpe->audio_in_socket, pool); + if ( rv == SWITCH_STATUS_SUCCESS ) { + airpe->audio_in_active_socket = inbound_socket; + switch_sleep(100000); + } + else { + return SWITCH_STATUS_FALSE; + } + } + + mbuf = frame->data; + mlen = frame->datalen; + rv = switch_socket_send(airpe->audio_in_active_socket, mbuf, &mlen); + + return rv; +} + +switch_status_t airpe_audio_read( private_object_t *pvt ) { + switch_socket_t *inbound_socket = NULL; + switch_memory_pool_t *pool; + airpe_interface_t *airpe; + switch_status_t rv; + switch_size_t mlen; + char *ptr; + + assert(pvt); + assert(pvt->session); + assert(pvt->airpe); + + airpe = pvt->airpe; + pool = switch_core_session_get_pool(pvt->session); + + if ( !airpe->audio_out_active_socket ) { + rv = switch_socket_accept(&inbound_socket, airpe->audio_out_socket, pool); + if ( rv == SWITCH_STATUS_SUCCESS ) { + airpe->audio_out_active_socket = inbound_socket; + switch_sleep(100000); + } + else { + return SWITCH_STATUS_FALSE; + } + } + + pvt->read_frame.datalen = 0; + ptr = pvt->read_frame.data; + mlen = sizeof(pvt->databuf); + rv = switch_socket_recv(airpe->audio_out_active_socket, ptr, &mlen); + + if ( (rv==SWITCH_STATUS_SUCCESS) ) { + pvt->read_frame.datalen = mlen; + } +// if ( mlen == 0 ) +// return SWITCH_STATUS_SUCCESS; + + return rv; +} + +/***************************************************************************** + SKYPE CONTROL MESSAGES + *****************************************************************************/ + +static switch_status_t airpe_manage_incoming_call( airpe_interface_t *airpe, int callid ) { + char chan_name[256]; + switch_channel_t *channel = NULL; + switch_core_session_t *new_session = NULL; + private_object_t *tech_pvt = NULL; + + new_session = airpe_request_session(NULL); + if ( !new_session ) { + return SWITCH_STATUS_FALSE; + } + + switch_core_session_add_stream(new_session, NULL); + + tech_pvt = (private_object_t *) switch_core_session_alloc(new_session, sizeof(private_object_t)); + if ( !tech_pvt ) { + switch_core_session_destroy(&new_session); + return SWITCH_STATUS_FALSE; + } + memset(tech_pvt, 0, sizeof(private_object_t)); + + channel = switch_core_session_get_channel(new_session); + assert(channel); + + if ( airpe_tech_init(tech_pvt, new_session) != SWITCH_STATUS_SUCCESS ) { + switch_core_session_destroy(&new_session); + return SWITCH_STATUS_FALSE; + } + tech_pvt->airpe = airpe; + + switch_snprintf(chan_name, sizeof(chan_name), MODNAME"/%s", airpe->name); + switch_channel_set_name(channel, chan_name); + + tech_pvt->caller_profile = switch_caller_profile_new( switch_core_session_get_pool(new_session), + airpe->name, /* Username */ + airpe->dialplan, /* Diaplan */ + airpe->partner_displayname, /* cid_name */ + airpe->partner_handle, /* cid_number */ + NULL, /* network_addr */ + NULL, /* ani */ + NULL, /* aniii */ + NULL, /* rdnis */ + MODNAME, /* source */ + airpe->context, /* context */ + airpe->destination /* destination_number */ + ); + + if ( !tech_pvt->caller_profile ) { + switch_core_session_destroy(&new_session); + return SWITCH_STATUS_FALSE; + } + else + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + + + switch_set_flag(tech_pvt, TFLAG_INBOUND); + airpe->active_call_id = callid; + switch_channel_set_state(channel, CS_INIT); + + if ( switch_core_session_thread_launch(new_session) != SWITCH_STATUS_SUCCESS ) { + airpe->active_call_id = 0; + switch_core_session_destroy(&new_session); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t airpe_manage_call_status( airpe_interface_t *airpe, int callid, const char *status ) { + char buf[SKYPE_MSG_LEN]; + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if ( airpe->active_call_id && airpe->active_call_id != callid ) { +ADEBUG("CASE 1 - different call\n"); + if ( !strncmp(status, "UNPLACED", strlen("UNPLACED")) ) { + snprintf(buf, sizeof(buf), "SET CALL %d STATUS FINISHED", callid); + airpe_cmd_write(airpe, buf); + } + else { + ADEBUG("*** (%d/%d) Unmanaged status %s\n", airpe->active_call_id, callid, status); + } + + } + else if ( airpe->active_call_id && airpe->active_call_id == callid ) { +ADEBUG("CASE 2 - my call\n"); + if ( airpe->tech_pvt ) { + assert(airpe->tech_pvt); + session = airpe->tech_pvt->session; + assert(session); + + channel = switch_core_session_get_channel(session); + assert(channel); + } + + if ( !strncmp(status, "ROUTING", strlen("ROUTING")) ) { + } + else if ( !strncmp(status, "RINGING", strlen("RINGING")) ) { + //if ( switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ) { + //switch_channel_set_state(channel, CS_RINGING); + switch_core_session_queue_indication(session, SWITCH_MESSAGE_INDICATE_RINGING); + switch_channel_mark_ring_ready(channel); + //} + + snprintf(buf, sizeof(buf), "ALTER CALL %d SET_INPUT PORT=\"%d\"", callid, airpe->audio_in_port); + airpe_cmd_write(airpe, buf); + snprintf(buf, sizeof(buf), "ALTER CALL %d SET_OUTPUT PORT=\"%d\"", callid, airpe->audio_out_port); + airpe_cmd_write(airpe, buf); + + + + switch_channel_set_state(channel, CS_ROUTING); + + } + else if ( !strncmp(status, "INPROGRESS", strlen("INPROGRESS")) ) { + snprintf(buf, sizeof(buf), "ALTER CALL %d STOP_VIDEO_SEND", callid ); + airpe_cmd_write(airpe, buf); + snprintf(buf, sizeof(buf), "ALTER CALL %d STOP_VIDEO_RECEIVE", callid ); + airpe_cmd_write(airpe, buf); + + if ( channel ) { + switch_channel_mark_answered(channel); + } + else { + if ( airpe_manage_incoming_call(airpe, callid) != SWITCH_STATUS_SUCCESS ) { + snprintf(buf, sizeof(buf), "ALTER CALL %d END HANGUP", callid ); + airpe_cmd_write(airpe, buf); + airpe->active_call_id = 0; + } + } + } + else if ( !strncmp(status, "FINISHED", strlen("FINISHED")) ) { + if ( channel ) + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + airpe->active_call_id = 0; + } + else if ( !strncmp(status, "FAILED", strlen("FAILED")) ) { + if ( channel ) + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + airpe->active_call_id = 0; + } + else if ( !strncmp(status, "CANCELLED", strlen("CANCELLED")) ) { + if ( channel ) + switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL); + airpe->active_call_id = 0; + } + else if ( !strncmp(status, "REFUSED", strlen("REFUSED")) ) { + if ( channel ) + switch_channel_hangup(channel, SWITCH_CAUSE_CALL_REJECTED); + airpe->active_call_id = 0; + } + else if ( !strncmp(status, "MISSED", strlen("MISSED")) ) { + if ( channel ) + switch_channel_hangup(channel, SWITCH_CAUSE_NO_ANSWER); + airpe->active_call_id = 0; + } + else if ( !strncmp(status, "UNPLACED", strlen("UNPLACED")) ) { + } + else { +ADEBUG("Unmanaged status \n"); + } + } + else { +ADEBUG("CASE 3 - no calls online\n"); + if ( !strncmp(status, "UNPLACED", strlen("UNPLACED")) ) { + airpe->active_call_id = callid; + } + else if ( !strncmp(status, "RINGING", strlen("RINGING")) ) { + airpe->active_call_id = callid; + snprintf(buf, sizeof(buf), "ALTER CALL %d ANSWER", callid ); + airpe_cmd_write(airpe, buf); + } + else { + ADEBUG("--- (%d/%d) Unmanaged status %s\n", airpe->active_call_id, callid, status); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_manage_skype_msg( airpe_interface_t *airpe, const char *msg ) { + char *buf = NULL; + int argc = 0; + char *argv[9] = { 0 }; + + assert(airpe); + assert(msg); + + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "airpe client %s: %s\n", airpe->name, msg); + + buf = strdup(msg); + argc = switch_separate_string(buf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if ( argc == 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "airpe client %s: %s - Message without arguments? uh?\n", airpe->name, msg); + free(buf); + return SWITCH_STATUS_FALSE; + } + + + + if ( !strncmp( buf, "CURRENTUSERHANDLE", strlen("CURRENTUSERHANDLE")) ) { + /* The username that the client is using. */ + // TODO ... if the user handle mismatches, disconnect the channel or simply disable + // all operations + } + else if ( !strncmp( buf, "USERSTATUS", strlen("USERSTATUS")) && argc > 1 ) { + /* The status of the user of the client (ex: NA, ONLINE) */ + airpe->user_status = skype_user_status(argv[1]); + } + else if ( !strncmp( buf, "PROTOCOL", strlen("PROTOCOL")) && argc > 1 ) { + /* The protocol of the client */ + airpe->protocol = atoi(argv[1]); + } + else if ( !strncmp( buf, "CLEAR", strlen("CLEAR")) ) { + /* something has been cleared in the UI - Ignored */ + } + else if ( !strncmp( buf, "PONG", strlen("PONG")) ) { + /* keepalive */ + airpe_skype_watchdog_reset(airpe->skype_window); + } + else if ( !strncmp( buf, "CONNSTATUS", strlen("CONNSTATUS")) && argc >= 2 ) { + /* The connection status of the client (ex: ONLINE) */ + airpe->conn_status = skype_conn_status( argv[1] ); + } + else if ( !strncmp( buf, "USER", strlen("USER")) ) { + /* Updates about the users of our list - ignored */ + } + else if ( !strncmp( buf, "GROUP", strlen("GROUP")) ) { + /* Updates about the groups of our list - ignored */ + } + else if ( !strncmp( buf, "CONTACTS", strlen("CONTACTS")) ) { + /* Other messages referring to the contact list - ignored */ + } + else if ( !strncmp( buf, "OK", strlen("OK")) ) { + /* Acknowledge - ignored */ + } + else if ( !strncmp( buf, "AUTOAWAY", strlen("AUTOAWAY")) ) { + /* Autoaway - ignored */ + } +//TODO: RECEIVEDAUTHREQUEST - Add a config option to authorize or forget about it. + else if ( !strncmp( buf, "MUTE", strlen("MUTE")) && argc >= 2 ) { + if ( !strncmp(argv[1], "ON", 2) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "airpe client %s: I don't like to be muted...\n", airpe->name); + airpe_cmd_write(airpe, "MUTE OFF"); + } + } + else if ( !strncmp( buf, "CHATMESSAGE", strlen("CHATMESSAGE")) ) { + /* a message has been received (unknown content) - ignored */ + } + else if ( !strncmp( buf, "CHAT", strlen("CHAT")) && argc >=2 ) { + /* Chat status - ignored. */ + if ( airpe->protocol > 6 ) { + //TODO : ALTER CHAT <chat_id> DISBAND - Doesn't seems to work on linux + char buf[SKYPE_MSG_LEN] = ""; + snprintf(buf, sizeof(buf), "ALTER CHAT %s DISBAND", argv[1]); + airpe_cmd_write(airpe, buf); + } + } + else if ( !strncmp( buf, "CHATMEMBER", strlen("CHATMEMBER")) ) { + /* Informations about the chat members - ignored */ + } + else if ( !strncmp( buf, "PROFILE", strlen("PROFILE")) && argc >= 2 ) { + if ( !strncmp(argv[1], "MOOD_TEXT", strlen("MOOD_TEXT")) ) { + char *tmp = NULL; + char *mood; + tmp = strdup(msg); + if ( tmp ) { + mood = strstr(tmp, "PROFILE MOOD_TEXT "); + if ( mood ) { + mood += strlen("PROFILE MOOD_TEXT "); + airpe_set_mood_text(airpe, mood); + } + switch_safe_free(tmp); + } + } + } + else if ( !strncmp( buf, "SKYPEVERSION", strlen("SKYPEVERSION")) ) { + airpe_set_version(airpe, argv[1]); + } + else if ( !strncmp( buf, "CALL", strlen("CALL")) && argc >= 3 ) { + char *cmd = argv[2]; + int callid = atoi(argv[1]); + + if ( !strncmp(cmd, "DURATION", strlen("DURATION")) ) { + /* Ignore the duration that is updated every second */ + if ( callid != airpe->active_call_id ) { + char buf[SKYPE_MSG_LEN] = ""; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "airpe client %s: hanging up unknown call id %d\n", airpe->name, callid); + snprintf(buf, sizeof(buf), "SET CALL %d STATUS FINISHED", callid); + airpe_cmd_write(airpe, buf); + } + //TODO: maybe we could consider this message as a keepalive signal + } + else if ( !strncmp(cmd, "STATUS", strlen("STATUS")) && argc >=4 ) { + char *callstatus = argv[3]; + airpe_manage_call_status( airpe, callid, callstatus ); + } + else if ( !strncmp(cmd, "CONF_ID", strlen("CONF_ID")) ) { + char buf[SKYPE_MSG_LEN]; + + if ( !airpe->active_call_id ) { + airpe_set_partner_displayname(airpe, NULL); + airpe_set_partner_handle(airpe, NULL); + + snprintf(buf, sizeof(buf), "GET CALL %d PARTNER_DISPNAME", callid); + airpe_cmd_write(airpe, buf); + + snprintf(buf, sizeof(buf), "GET CALL %d PARTNER_HANDLE", callid); + airpe_cmd_write(airpe, buf); + + airpe_cmd_write(airpe, "PING"); + } + else { + snprintf(buf, sizeof(buf), "ALTER CALL %d END HANGUP", callid ); + airpe_cmd_write(airpe, buf); + } + } + else if ( !strncmp(cmd, "VAA_INPUT_STATUS", strlen("VAA_INPUT_STATUS")) ) { + /* Let's ignore */ + } + else if ( !strncmp(cmd, "DTMF", strlen("DTMF")) ) { + /* This is an incoming DTMF */ + switch_core_session_t *session; + switch_channel_t *channel; + switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) }; + const char *signal_ptr; + int tmp; + + assert(airpe->tech_pvt); + session = airpe->tech_pvt->session; + assert(session); + channel = switch_core_session_get_channel(session); + assert(channel); + + signal_ptr = argv[3]; + + if (*signal_ptr && (*signal_ptr == '*' || *signal_ptr == '#' || *signal_ptr == 'A' || *signal_ptr == 'B' || *signal_ptr == 'C' || *signal_ptr == 'D')) { + dtmf.digit = *signal_ptr; + } else { + tmp = atoi(signal_ptr); + dtmf.digit = switch_rfc2833_to_char(tmp); + } + + switch_mutex_lock(airpe->tech_pvt->flag_mutex); + switch_channel_queue_dtmf(channel, &dtmf); + switch_mutex_unlock(airpe->tech_pvt->flag_mutex); + } + else if ( !strncmp(cmd, "FAILUREREASON", strlen("FAILUREREASON")) && argc >=2 ) { + airpe->last_fail_reason = atoi(argv[1]); + airpe_cmd_write(airpe, "CLEAR CALLHISTORY ALL"); + } + else if ( !strncmp(cmd, "SEEN", strlen("SEEN")) && argc >=4 ) { + /* + char buf[SKYPE_MSG_LEN] = ""; + char *seenstatus = argv[3]; + if ( !strncmp(seenstatus, "FALSE", strlen("FALSE")) ) { + snprintf(buf, sizeof(buf), "SET CALL %d SEEN", callid); + airpe_cmd_write(airpe, buf); + } + */ + airpe_cmd_write(airpe, "CLEAR CALLHISTORY ALL"); + } + else if ( !strncmp(cmd, "PARTNER_DISPNAME", strlen("PARTNER_DISPNAME")) && argc >=2 ) { + char *data, *tmp = NULL; + tmp = strdup(msg); + if ( tmp && (callid == airpe->active_call_id) ) { + data = strstr(tmp, "PARTNER_DISPNAME "); + if ( data ) { + data += strlen("PARTNER_DISPNAME "); + airpe_set_partner_displayname(airpe, data); + } + } + switch_safe_free(tmp); + } + else if ( !strncmp(cmd, "PARTNER_HANDLE", strlen("PARTNER_HANDLE")) && argc >=2 ) { + char *data, *tmp = NULL; + tmp = strdup(msg); + if ( tmp && (callid == airpe->active_call_id) ) { + data = strstr(tmp, "PARTNER_HANDLE "); + if ( data ) { + data += strlen("PARTNER_HANDLE "); + airpe_set_partner_handle(airpe, data); + } + switch_safe_free(tmp); + } + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CALL ID %d\n", callid); + } + } + else if ( !strncmp( buf, "DTMF", strlen("DTMF")) && argc>3 ) { + /* We are alerted that a DTMF we was sent to the other party - Ignored */ + } + else if ( !strncmp( msg, "ERROR 68", strlen("ERROR 68")) ) { + /* The client needs authorization. Probably we reconnected to a new skype window. */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Airpe client %s needs authorization.\n", airpe->name); + switch_sleep(1000000); + airpe_on_connect_messages(airpe, airpe->protocol); + } + else if ( !strncmp( msg, "ALTER CALL", strlen("ALTER CALL")) ) { + /* The response to our hangup - Ignored */ + } + else if ( !strncmp( msg, "SET CALL", strlen("SET CALL")) ) { + /* Ignoring by now */ + } + else if ( !strncmp( buf, "ERROR", strlen("ERROR")) ) { + //TODO If we are in the middle of a call, hangup + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unmanaged message\n"); + } + + switch_safe_free(buf); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_on_connect_messages( airpe_interface_t *airpe, int protocol ) { + char buf[SKYPE_MSG_LEN] = ""; + + snprintf(buf, sizeof(buf), "NAME airpe"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "PROTOCOL %d", protocol); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "CLEAR CALLHISTORY ALL"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "CLEAR CHATHISTORY"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "GET SKYPEVERSION"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "GET PROFILE MOOD_TEXT"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "GET PROFILE FULLNAME"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "GET CONNSTATUS"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "SET AUTOAWAY OFF"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "SET MUTE OFF"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + + /* + The following two raise an error on linux, even if are supported from protocol 6 upwards. + That's not a big problem so we'll send them. + */ + + snprintf(buf, sizeof(buf), "SET PCSPEAKER OFF"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + snprintf(buf, sizeof(buf), "SET SILENT_MODE ON"); + if ( airpe_cmd_write(airpe, buf) != SWITCH_STATUS_SUCCESS ) + return SWITCH_STATUS_FALSE; + + 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/airpe_if_osx.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_if_osx.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,19 @@ + +#include "mod_airpe.h" + +#if defined (MACOSX) || defined(DARWIN) + +#error OSX Not yet supported + +#endif + +/* 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/airpe_if_win32.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_if_win32.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,19 @@ + +#include "mod_airpe.h" + +#ifdef WIN32 + +#error WIN32 Not yet supported + +#endif + +/* 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/airpe_if_x11.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/airpe_if_x11.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,355 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#if defined (__GNUC__) && !(defined (MACOSX) || defined(DARWIN) ) + +#include "mod_airpe.h" + +#include <X11/Xlib.h> +#include <X11/Xlibint.h> +#include <X11/Xatom.h> + +#define X11_MSG_LEN 20 + +struct skype_window_handler_s { + Display *disp; + Window win; + uint8_t api_connected; + int fdesc[2]; + Window skype_win; + int watchdog; +}; + + + + +static XErrorHandler old_handler = 0; +static int xerror = 0; + +static int xerrhandler(Display *dpy, XErrorEvent *err) +{ + xerror = err->error_code; + /* Return 0 so that the error is ignored */ + return 0; +} + +static void trap_errors() +{ + xerror = 0; + old_handler = XSetErrorHandler(xerrhandler); +} + +static int untrap_errors() +{ + XSetErrorHandler(old_handler); + return (xerror != BadValue) && (xerror != BadWindow); +} + +switch_status_t airpe_skype_watchdog_reset(skype_window_handler_t *window) { + window->watchdog = 0; + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_skype_watchdog_increment(skype_window_handler_t *window) { + window->watchdog++; + + if ( window->watchdog >= 5 ) { + } + + return SWITCH_STATUS_SUCCESS; +} + +skype_window_handler_t *skype_window_alloc( switch_memory_pool_t *pool) { + return switch_core_alloc( pool, sizeof(skype_window_handler_t)); +} + +switch_status_t skype_restart( skype_window_handler_t *window, const char *params ) { + + switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "HEY! where is my skype window gone ? Restarting skype for client...\n"); +/* + + TODO AND TO USE + + int fid; + const char *args[] = { "skype", "--actiondispatch", params, 0 }; + + if((fid = fork()) == 0) { + if(execvp("skype", (char *const*)args) == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client: cannot launch Skype, error %d: %s.\n", errno, strerror(errno) ); + return SWITCH_STATUS_FALSE; + } + } + else if(fid == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client: cannot launch Skype.\n"); + return SWITCH_STATUS_FALSE; + } +*/ + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_skype_send_message(skype_window_handler_t *window, const char *message_P) { + Window w_P; + Display *disp; + Window handle_P; + Atom atom1; + Atom atom2; + unsigned int pos; + unsigned int len; + XEvent xev; + int ok; + + w_P = window->skype_win; + disp = window->disp; + handle_P = window->win; + + atom1 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False); + atom2 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE", False); + pos = 0; + len = strlen(message_P); + + memset(&xev, 0, sizeof(XEvent)); + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom1; + xev.xclient.display = disp; + xev.xclient.window = handle_P; + xev.xclient.format = 8; + + trap_errors(); + do { + unsigned int i; + for (i = 0; i < 20 && i + pos <= len; ++i) + xev.xclient.data.b[i] = message_P[i + pos]; + XSendEvent(disp, w_P, False, 0, &xev); + + xev.xclient.message_type = atom2; + pos += i; + } while (pos <= len); + + XSync(disp, False); + ok = untrap_errors(); + if (!ok) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client X11 error.\n"); + airpe_skype_avalaible(window); + } + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_skype_avalaible(skype_window_handler_t *window) +{ + + Atom skype_inst; + Atom type_ret; + int format_ret; + unsigned long nitems_ret; + unsigned long bytes_after_ret; + unsigned char *prop; + int status, ok; + + skype_inst = XInternAtom(window->disp, "_SKYPE_INSTANCE", True); + + trap_errors(); + status = XGetWindowProperty( + window->disp, + DefaultRootWindow(window->disp), + skype_inst, + 0, + 1, + False, + XA_WINDOW, + &type_ret, + &format_ret, + &nitems_ret, + &bytes_after_ret, &prop); + + ok = untrap_errors(); + if (!ok) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client X11 error.\n"); + airpe_skype_avalaible(window); + } + + /* sanity check */ + if (status != Success || format_ret != 32 || nitems_ret != 1) { + window->skype_win = (Window) - 1; + return SWITCH_STATUS_FALSE; + } + + window->skype_win = *(const unsigned long *) prop & 0xffffffff; + + return SWITCH_STATUS_SUCCESS; +} + + + +/***************************************************************************** + THREADS + *****************************************************************************/ + +void *SWITCH_THREAD_FUNC airpe_skype_thread(switch_thread_t * thread, void *obj) { + + skype_window_handler_t *skype_window = NULL; + airpe_interface_t *airpe = NULL; + Display *disp = NULL; + Window root = -1; + Window win = -1; + int xfd; + + assert(obj); + + airpe = obj; + + skype_window = airpe->skype_window; + assert(skype_window); + + disp = XOpenDisplay(airpe->X11_display); + if (!disp) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client %s: cannot open display %s\n", airpe->name, airpe->X11_display); + return NULL; + } + + xfd = XConnectionNumber(disp); + fcntl(xfd, F_SETFD, FD_CLOEXEC); + + skype_window->disp = disp; + + if ( airpe_skype_avalaible(skype_window) == SWITCH_STATUS_SUCCESS ) { + root = DefaultRootWindow(disp); + win = XCreateSimpleWindow( + disp, + root, + 0, 0, 1, 1, 0, + BlackPixel(disp, DefaultScreen(disp)), + BlackPixel(disp, DefaultScreen(disp)) + ); + + skype_window->win = win; + + if ( airpe_on_connect_messages( airpe, 6 ) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "airpe client %s. Failed to send handshake messages\n", + airpe->name); + return NULL; + } + + // perform an events loop + XEvent an_event; + char buf[X11_MSG_LEN + 2] = ""; + char buffer[SKYPE_MSG_LEN] = ""; + int i; + fd_set rset; + int x11_fd; + int empty_loops = 0; + + airpe->running = 1; + XFlush(disp); + x11_fd = ConnectionNumber(disp); + + while ( airpe->running ) { + FD_ZERO(&rset); + FD_SET( x11_fd, &rset); + + struct timeval timeout; + int ret; + + timeout.tv_usec = 10 * 1000; + ret = select(x11_fd+1, &rset, 0, 0, &timeout); + + if ( !airpe->running ) /* Quick way to skip the loop when we are signaled to exit. */ + break; + + if ( ret <=0 ) { + empty_loops ++; + if ( empty_loops >= 250 ) { + airpe_cmd_write(airpe, "PING"); + airpe_skype_watchdog_increment(skype_window); + empty_loops = 0; + } + continue; + } + + airpe_skype_watchdog_reset(skype_window); + + /* Process all the pending messages queued (not only one) */ + while ( XPending(disp) ) + { + XNextEvent(disp, &an_event); + + switch (an_event.type) + { + case ClientMessage: + if (an_event.xclient.format != 8) + break; + + for (i = 0; i < X11_MSG_LEN && an_event.xclient.data.b[i] != '\0'; ++i) { + buf[i] = an_event.xclient.data.b[i]; + } + buf[i] = '\0'; + + strcat(buffer, buf); + + if (i < X11_MSG_LEN) { + unsigned int howmany; + howmany = strlen(buffer) + 1; + airpe_manage_skype_msg( airpe, buffer); + memset(&buffer, 0, sizeof(buffer)); + } + break; + default: + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "airpe client %s: unknown event\n", airpe->name); + break; + } + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "airpe client %s: exiting thread\n", airpe->name); + + /* Hangup our call, if we have one */ + airpe_explicit_hangup(airpe); + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client %s: skype not running on display %s\n", airpe->name, airpe->X11_display); + } + + return NULL; +} + +#endif + +/* 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/config/airpe.conf.xml ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/config/airpe.conf.xml Mon Jan 12 17:24:44 2009 @@ -0,0 +1,36 @@ +<configuration name="airpe.conf" description="Airpe Configuration"> + <globals> + <param name="audio-tcp-port_start" value="21000"/> + + <!-- Those params are used for all clients if not explicitly set for each one --> + <param name="context" value="public"/> + <param name="dialplan" value="XML"/> + <param name="destination" value="5000"/> + </globals> + + <!-- one entry here per airpe client --> + <clients> + <client name="airpe1"> + <!-- Print out as notices the client messages --> + <param name="debug" value="1"/> + + <!-- Where our calls will land --> + <param name="dialplan" value="XML"/> + <param name="context" value="default"/> + <param name="destination" value="4321"/> + + <!-- Linux only --> + <param name="X11-display" value=":101"/> + + <!-- Not used yet --> + <param name="cid-name" value="Skype 1"/> + <param name="cid-num" value="0000001"/> + + <!-- Not used... maybe should be removed. + The purpose is restarting the client with those credentials --> + <param name="skype-user" value="username"/> + <param name="skype-password" value="password"/> + </client> + </clients> + +</configuration> Added: freeswitch/branches/ctrix/mod_airpe/mod_airpe.c ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/mod_airpe.c Mon Jan 12 17:24:44 2009 @@ -0,0 +1,868 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#include "mod_airpe.h" + +/***************************************************************************** + MODULE PRIVATE STRUCTS && FUNCTIONS + *****************************************************************************/ + +static switch_endpoint_interface_t *airpe_endpoint_interface; + +static struct { + char *dialplan; + char *context; + char *destination; + int audio_tcp_port_start; + + switch_mutex_t *mutex; + switch_hash_t *interfaces_hash; + int last_port_used; +} globals; + +static switch_memory_pool_t *airpe_module_pool = NULL; + + +airpe_interface_t *airpe_find_interface( const char *name) { + return switch_core_hash_find(globals.interfaces_hash, name); +} + + +/***************************************************************************** + TECH STUFF + *****************************************************************************/ + +static switch_status_t airpe_codec_init(private_object_t * tech_pvt, int sample_rate, int codec_ms) { + airpe_interface_t *airpe = NULL; + + if ( switch_core_codec_init(&tech_pvt->read_codec, "L16", NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client: %s - Cannot init read codec.\n", airpe->name); + return SWITCH_STATUS_FALSE; + } + + if ( switch_core_codec_init(&tech_pvt->write_codec, "L16", NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "airpe client: %s - Cannot init write codec.\n", airpe->name); + switch_core_codec_destroy(&tech_pvt->read_codec); + return SWITCH_STATUS_FALSE; + } + + tech_pvt->read_frame.rate = sample_rate; + tech_pvt->read_frame.codec = &tech_pvt->read_codec; + + 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); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t airpe_tech_init( private_object_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)); + + switch_core_session_set_private(session, tech_pvt); + tech_pvt->session = session; + return airpe_codec_init(tech_pvt, CODEC_SAMPLE_RATE, CODEC_INTERVAL_MS); +} + +static switch_socket_t *airpe_socket_create( const char *host, int port ) { + switch_status_t rv; + switch_socket_t *new_sock = NULL; + switch_sockaddr_t *sa; + + rv = switch_sockaddr_info_get(&sa, host, AF_INET, port, 0, airpe_module_pool); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (1)\n"); + return NULL; + } + + rv = switch_socket_create(&new_sock, AF_INET, SOCK_STREAM, SWITCH_PROTO_TCP, airpe_module_pool); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (2)\n"); + return NULL; + } + + rv = switch_socket_opt_set(new_sock, SWITCH_SO_REUSEADDR, 1); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (3)\n"); + return NULL; + } + + rv = switch_socket_opt_set(new_sock, SWITCH_SO_NONBLOCK, 1); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (3)\n"); + return NULL; + } + + rv = switch_socket_bind(new_sock, sa); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (4)\n"); + return NULL; + } + + rv = switch_socket_listen(new_sock, 5); + if ( rv != SWITCH_STATUS_SUCCESS ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Creation Error! (5)\n"); + return NULL; + } + + return new_sock; +} + +static int fetch_next_port(void) { + int port = 0; + + if ( !globals.last_port_used ) + globals.last_port_used = globals.audio_tcp_port_start; + + switch_mutex_lock(globals.mutex); + port = globals.last_port_used; + globals.last_port_used++; + switch_mutex_unlock(globals.mutex); + + return port; +} + +static switch_status_t airpe_audio_sockets_create( airpe_interface_t *airpe ) { + switch_socket_t *new_sock; + int port; + + new_sock = NULL; + while ( !new_sock ) { + port = fetch_next_port(); + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Assigning in port %d for client %s\n", port, airpe->name); + new_sock = airpe_socket_create("127.0.0.1", port); + if ( new_sock ) { + airpe->audio_in_socket = new_sock; + airpe->audio_in_port = port; + } + } + + new_sock = NULL; + while ( !new_sock ) { + port = fetch_next_port(); + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Assigning out port %d for client %s\n", port, airpe->name); + new_sock = airpe_socket_create("127.0.0.1", port); + if ( new_sock ) { + airpe->audio_out_socket = new_sock; + airpe->audio_out_port = port; + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t airpe_start_client(airpe_interface_t *airpe) { + + switch_threadattr_t *attr_skype = NULL; + + airpe->user_status = USER_STATUS_UNKNOWN; + airpe->conn_status = CONN_STATUS_UNKNOWN; + airpe->call_status = CALL_STATUS_UNKNOWN; + + airpe_audio_sockets_create(airpe); + + switch_threadattr_create(&attr_skype, airpe_module_pool); + //switch_threadattr_detach_set(attr_skype, 1); + switch_threadattr_stacksize_set(attr_skype, SWITCH_THREAD_STACKSIZE); + + switch_thread_create(&airpe->airpe_thread_skype, + attr_skype, + airpe_skype_thread, + airpe, + airpe_module_pool); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t airpe_stop_client(airpe_interface_t *airpe) { + + if ( airpe->audio_in_socket ) { + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting down socket on port %d for client %s\n", airpe->audio_in_port, airpe->name); + airpe->audio_in_port = 0; + switch_socket_shutdown(airpe->audio_in_socket, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(airpe->audio_in_socket); + } + + if ( airpe->audio_out_socket ) { + if ( airpe->debug ) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting down socket on port %d for client %s\n", airpe->audio_out_port, airpe->name); + airpe->audio_out_port = 0; + switch_socket_shutdown(airpe->audio_out_socket, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(airpe->audio_out_socket); + } + + airpe_set_version(airpe, NULL); + airpe_set_mood_text(airpe, NULL); + airpe_set_partner_displayname(airpe, NULL); + airpe_set_partner_handle(airpe, NULL); + + return SWITCH_STATUS_SUCCESS; +} + +switch_core_session_t *airpe_request_session( switch_memory_pool_t **pool ) { + /* I prefer not to expose airpe_endpoint_interface out of this file. */ + return switch_core_session_request(airpe_endpoint_interface, pool); +} + + +/***************************************************************************** + CHANNEL STATE HANDLERS + *****************************************************************************/ + +static switch_status_t channel_on_init(switch_core_session_t * session) +{ + switch_channel_t *channel; + private_object_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + assert(tech_pvt->airpe); + + tech_pvt->airpe->tech_pvt = tech_pvt; + + if ( switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ) { + airpe_originate(tech_pvt->airpe, tech_pvt->destination ); + // TODO if fail switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + if ( switch_test_flag(tech_pvt, TFLAG_INBOUND) ) { + char buf[SKYPE_MSG_LEN]; + snprintf(buf, sizeof(buf), "ALTER CALL %d SET_INPUT PORT=\"%d\"", tech_pvt->airpe->active_call_id, tech_pvt->airpe->audio_in_port); + airpe_cmd_write(tech_pvt->airpe, buf); + snprintf(buf, sizeof(buf), "ALTER CALL %d SET_OUTPUT PORT=\"%d\"", tech_pvt->airpe->active_call_id, tech_pvt->airpe->audio_out_port); + airpe_cmd_write(tech_pvt->airpe, buf); + + switch_channel_set_state(channel, CS_ROUTING); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_routing(switch_core_session_t * session) { + char buf[SKYPE_MSG_LEN]; + switch_channel_t *channel = NULL; + private_object_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + if ( switch_test_flag(tech_pvt, TFLAG_INBOUND) ) { + snprintf(buf, sizeof(buf), "ALTER CALL %d ANSWER", tech_pvt->airpe->active_call_id); + airpe_cmd_write(tech_pvt->airpe, buf); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_execute(switch_core_session_t * session) { + switch_channel_t *channel = NULL; + private_object_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_hangup(switch_core_session_t * session) +{ + switch_channel_t *channel = NULL; + private_object_t *tech_pvt = NULL; + airpe_interface_t *airpe = NULL; + char msg[SKYPE_MSG_LEN]; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + airpe = tech_pvt->airpe; + assert(airpe); + + if ( airpe->active_call_id ) { + snprintf(msg, sizeof(msg), "ALTER CALL %d HANGUP", airpe->active_call_id); + airpe_cmd_write(airpe, msg); +// airpe->active_call_id = 0; +// airpe->tech_pvt = NULL; + } + + airpe_hangup( tech_pvt ); + + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_exchange_media(switch_core_session_t * session) { + return SWITCH_STATUS_SUCCESS; +} + +/***************************************************************************** + CHANNEL IO INTERFACE / ROUTINES + *****************************************************************************/ +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_caller_profile_t *caller_profile = NULL; + private_object_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + + assert(outbound_profile); + + /* New session creation */ + *new_session = airpe_request_session(pool); + if ( *new_session ) + { + char *chan_interface = NULL; + char *chan_destination = NULL; + + switch_core_session_add_stream(*new_session, NULL); + + tech_pvt = (private_object_t *) switch_core_session_alloc(*new_session, sizeof(private_object_t)); + + if ( tech_pvt ) { + channel = switch_core_session_get_channel(*new_session); + if ( airpe_tech_init( tech_pvt, *new_session ) != SWITCH_STATUS_SUCCESS ) { + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + } else { + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + + chan_interface = switch_core_session_strdup( *new_session, outbound_profile->destination_number); + if (( chan_destination = strchr(chan_interface, '/'))) { + *chan_destination++ = '\0'; + } + if ( !chan_interface || !chan_destination ) { + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_INVALID_NUMBER_FORMAT; + } + + if ( (tech_pvt->airpe = airpe_find_interface(chan_interface))==NULL ) { + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + tech_pvt->destination = chan_destination; + + if ( tech_pvt->airpe->active_call_id != 0 ) { + /* We're busy, man! */ + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL; + } + + if (outbound_profile) { + char name[128]; + snprintf(name, sizeof(name), "airpe/%s", outbound_profile->destination_number); + switch_channel_set_name(channel, name); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + switch_channel_set_caller_profile(channel, caller_profile); + tech_pvt->caller_profile = caller_profile; + } + else + { + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + switch_channel_set_flag(channel, CF_OUTBOUND); + switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + return SWITCH_CAUSE_SUCCESS; + } + else + { + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } +} + +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_object_t *tech_pvt = NULL; + switch_byte_t *data; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + tech_pvt->read_frame.flags = SFF_NONE; + *frame = NULL; + + if ( airpe_audio_read(tech_pvt) != SWITCH_STATUS_SUCCESS) { + ADEBUG("airpe_audio_read ERROR\n"); + return SWITCH_STATUS_FALSE; + } + + if (!tech_pvt->read_frame.datalen) { + goto cng; + } + + *frame = &tech_pvt->read_frame; + + return SWITCH_STATUS_SUCCESS; + +cng: + data = (switch_byte_t *) tech_pvt->read_frame.data; + data[0] = 65; + data[1] = 0; + tech_pvt->read_frame.datalen = 2; + tech_pvt->read_frame.flags = SFF_CNG; + *frame = &tech_pvt->read_frame; + return SWITCH_STATUS_SUCCESS; +} + +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_object_t *tech_pvt = NULL; + airpe_interface_t *airpe = NULL; + switch_status_t rv; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + if ( !tech_pvt->airpe ) { + return SWITCH_STATUS_FALSE; + } + + airpe = tech_pvt->airpe; + + rv = airpe_audio_write(tech_pvt, frame); + return rv; +} + +static switch_status_t channel_kill_channel(switch_core_session_t * session, int sig) +{ + switch_channel_t *channel = NULL; + private_object_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt); + + switch (sig) { + case SWITCH_SIG_KILL: + break; + case SWITCH_SIG_BREAK: + break; + default: + //TODO Log unhandled signal + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_send_dtmf(switch_core_session_t * session, const switch_dtmf_t * dtmf) { + private_object_t *tech_pvt = switch_core_session_get_private(session); + airpe_interface_t *airpe; + char buf[SKYPE_MSG_LEN]; + + switch_assert(tech_pvt); + + airpe = tech_pvt->airpe; + + if ( airpe->active_call_id ) { + snprintf(buf, sizeof(buf), "SET CALL %d DTMF %c", airpe->active_call_id, dtmf->digit); + airpe_cmd_write(airpe, buf); + } + + 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_object_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_ANSWER: + ADEBUG("received message indicate answer...\n"); +/* + //switch_answer_channel(session); + char buf[SKYPE_MSG_LEN]; + snprintf(buf, sizeof(buf), "ALTER CALL %d ANSWER", tech_pvt->airpe->active_call_id ); + airpe_cmd_write(tech_pvt->airpe, buf); +*/ + break; + case SWITCH_MESSAGE_INDICATE_UNBRIDGE: + ADEBUG("received message indicate unbridge...\n"); + break; + case SWITCH_MESSAGE_INDICATE_TRANSCODING_NECESSARY: + ADEBUG("received message indicate transcode necessary...\n"); + break; + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + ADEBUG("received message indicate audio sync...\n"); + break; + case SWITCH_MESSAGE_INDICATE_TRANSFER: + ADEBUG("received message indicate transfer...\n"); + break; + default: + ADEBUG("Unmanaged received message... %d\n", msg->message_id); + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_event(switch_core_session_t * session, switch_event_t * event) +{ + char *body = NULL; + switch_channel_t *channel; + private_object_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt); + + body = switch_event_get_body(event); + + if (!body) { + body = ""; + } + + return SWITCH_STATUS_SUCCESS; +} + + +/***************************************************************************** + CONFIGURATION + *****************************************************************************/ + +static switch_status_t load_config(void) { + switch_xml_t cfg, + xml, + global_settings, + param, + clients, + client; + switch_memory_pool_t *pool; + + pool = airpe_module_pool; + + if (!(xml = switch_xml_open_cfg("airpe.conf", &cfg, NULL))) { + return SWITCH_STATUS_TERM; + } + + if ( (global_settings = switch_xml_child(cfg, "globals") ) ) { + for (param = switch_xml_child(global_settings, "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, "dialplan")) { + globals.dialplan = switch_core_strdup(pool, val); + } else if (!strcmp(var, "context")) { + globals.context = switch_core_strdup(pool, val); + } else if (!strcmp(var, "destination")) { + globals.destination = switch_core_strdup(pool, val); + } else if (!strcmp(var, "audio-tcp-port_start")) { + globals.audio_tcp_port_start = atoi(val); + } + } + } + + if ( globals.audio_tcp_port_start <= 0 ) { + globals.audio_tcp_port_start = 21000; + } + + if ( !globals.dialplan ) + globals.dialplan = switch_core_strdup(pool, "XML"); + if ( !globals.context ) + globals.context = switch_core_strdup(pool, "default"); + if ( !globals.destination ) + globals.destination = switch_core_strdup(pool, "4321"); + + if ((clients = switch_xml_child(cfg, "clients"))) { + + for (client = switch_xml_child(clients, "client"); client; client = client->next) + { + char *name = (char *) switch_xml_attr(client, "name"); + char *context = NULL; + char *dialplan = NULL; + char *destination = NULL; + char *X11_display = NULL; + char *skype_user = NULL; + char *skype_password= NULL; + char *cid_name = NULL; + char *cid_num = NULL; + int debug = 0; + + airpe_interface_t *newconf = NULL; + + if (!name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No name attribute. Skipping unnamed client.\n"); + continue; + } + + if ( (newconf = switch_core_hash_find(globals.interfaces_hash, name)) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Duplicated client %s. Skipping\n", name); + continue; + } + + for (param = switch_xml_child(client, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "debug")) { + debug = switch_true(val) ? 1 : 0; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "destination")) { + destination = val; + } else if (!strcasecmp(var, "skype-user")) { + skype_user = val; + } else if (!strcasecmp(var, "skype-password")) { + skype_password = val; + } else if (!strcasecmp(var, "X11-display")) { + X11_display = val; + } else if (!strcasecmp(var, "cid-name")) { + cid_name = val; + } else if (!strcasecmp(var, "cid-num")) { + cid_num = val; + } + else + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown config param %s\n", var); + } + if (!skype_user) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No skype-user param for client %s. Skipping\n", name); + continue; + } + if (!skype_password) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No skype-password param for client %s. Skipping\n", name); + continue; + } + if (!X11_display) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No X11-display param for client %s. Skipping\n", name); + continue; + } + + if ( !dialplan ) + dialplan = globals.dialplan; + if ( !context ) + context = globals.context; + if ( !destination ) + destination = globals.destination; + + newconf = (airpe_interface_t *) switch_core_alloc( pool, sizeof(airpe_interface_t) ); + + if ( newconf ) { + switch_status_t status; + + memset(newconf, 0, sizeof(airpe_interface_t)); + + newconf->name = switch_core_strdup(pool, name); + newconf->dialplan = switch_core_strdup(pool, dialplan); + newconf->context = switch_core_strdup(pool, context); + newconf->destination = switch_core_strdup(pool, destination); + newconf->skype_user = switch_core_strdup(pool, skype_user); + newconf->skype_password = switch_core_strdup(pool, skype_password); + newconf->X11_display = switch_core_strdup(pool, X11_display); + newconf->audio_in_port = 0; + newconf->audio_in_port = 0; + newconf->debug = debug; + + newconf->skype_window = skype_window_alloc(pool); + + if ( newconf->skype_window ) { + status = airpe_start_client(newconf); + + if ( status == SWITCH_STATUS_SUCCESS ) { + switch_core_hash_insert(globals.interfaces_hash, name, newconf); + } + else { + // LOG OUR ERROR + } + } + else { + // LOG OUR ERROR + } + + } + else { + //TODO LOG ERROR ABOUT CREATING INTERFACE + } + } + + } + + switch_xml_free(xml); + return SWITCH_STATUS_SUCCESS; +} + + +/***************************************************************************** + MODULE INITIALIZATION FUNCTIONS + *****************************************************************************/ + +static switch_state_handler_table_t airpe_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 */ NULL +}; + +static switch_io_routines_t airpe_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 +}; + +SWITCH_MODULE_LOAD_FUNCTION(mod_airpe_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_airpe_shutdown); +SWITCH_MODULE_DEFINITION(mod_airpe, mod_airpe_load, mod_airpe_shutdown, NULL); + +SWITCH_MODULE_LOAD_FUNCTION(mod_airpe_load) +{ + airpe_module_pool = pool; + + memset(&globals, 0, sizeof(globals)); + + switch_core_hash_init(&globals.interfaces_hash, pool); + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool); + + load_config(); + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + airpe_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + + airpe_endpoint_interface->interface_name = "airpe"; + airpe_endpoint_interface->io_routines = &airpe_io_routines; + airpe_endpoint_interface->state_handler = &airpe_state_handlers; + + airpe_register_api( &*module_interface ); + airpe_register_apps( &*module_interface ); + + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_airpe_shutdown) +{ + switch_memory_pool_t *pool; + airpe_interface_t *airpe; + switch_hash_index_t *hi; + void *val; + + pool = airpe_module_pool; + + for (hi = switch_hash_first(NULL, globals.interfaces_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, NULL, NULL, &val); + airpe = ( airpe_interface_t *) val; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting down airpe client: %s\n", airpe->name); + airpe->running = 0; + /* Wake up the socket */ + airpe_cmd_write(airpe, "PING"); + airpe_stop_client(airpe); + /* + we won't delete the hash element. we're going to destroy the whole pool + switch_core_hash_delete(globals.interfaces_hash, airpe->name); + */ + } + switch_yield(500000); + + /* TODO... should we check that all interfaces has been switched off ? */ + + switch_core_hash_destroy(&globals.interfaces_hash); + switch_core_destroy_memory_pool(&pool); + return SWITCH_STATUS_UNLOAD; +} + +/* 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 expandtab: + */ Added: freeswitch/branches/ctrix/mod_airpe/mod_airpe.h ============================================================================== --- (empty file) +++ freeswitch/branches/ctrix/mod_airpe/mod_airpe.h Mon Jan 12 17:24:44 2009 @@ -0,0 +1,235 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.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 mod_airpe. + * + * The Initial Developer of the Original Code is + * Massimo Cetra <devel@navynet.it> + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Thanks to gmaruzz for his work on skypiax (both * and freeswitch). + * Some of his code inspired this module + * + */ + +#include <switch.h> +#include <switch_version.h> + +#define MODNAME "airpe" + +#define SKYPE_MSG_LEN 1024 + +#define CODEC_SAMPLE_RATE 16000 +#define CODEC_INTERVAL_MS 20 +#define FRAMES_PER_SECOND 1000 / CODEC_INTERVAL_MS +#define SAMPLES_PER_FRAME CODEC_SAMPLE_RATE / FRAMES_PER_SECOND + +#define ADEBUG(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, __VA_ARGS__ ); + +typedef enum { + CONN_STATUS_UNKNOWN, + CONN_STATUS_OFFLINE, + CONN_STATUS_CONNECTING, + CONN_STATUS_PAUSING, + CONN_STATUS_ONLINE, +} CONN_STATUS; + +typedef enum { + USER_STATUS_UNKNOWN, + USER_STATUS_ONLINE, + USER_STATUS_OFFLINE, + USER_STATUS_SKYPEME, + USER_STATUS_AWAY, + USER_STATUS_NA, + USER_STATUS_DND, + USER_STATUS_INVISIBLE, + USER_STATUS_LOGGEDOUT +} USER_STATUS; + +typedef enum { + CALL_STATUS_UNKNOWN, + CALL_STATUS_UNPLACED, + CALL_STATUS_ROUTING, + CALL_STATUS_EARLYMEDIA, + CALL_STATUS_FAILED, + CALL_STATUS_RINGING, + CALL_STATUS_INPROGRESS, + CALL_STATUS_ONHOLD, + CALL_STATUS_LOCALHOLD, + CALL_STATUS_REMOTEHOLD, + CALL_STATUS_FINISHED, + CALL_STATUS_MISSED, + CALL_STATUS_REFUSED, + CALL_STATUS_BUSY, + CALL_STATUS_CANCELLED, + CALL_STATUS_TRANSFERRING, /* Protocol 7 */ + CALL_STATUS_TRANSFERRED, /* Protocol 7 */ + + CALL_STATUS_VM_BUFFERING_GREETING, + CALL_STATUS_VM_PLAYING_GREETING, + CALL_STATUS_VM_RECORDING, + CALL_STATUS_VM_UPLOADING, + CALL_STATUS_VM_SENT, + CALL_STATUS_VM_CANCELLED , + CALL_STATUS_VM_FAILED, + CALL_STATUS_WAITING_REDIAL_COMMAND, + CALL_STATUS_REDIAL_PENDING, /* Protocol 8 */ + +} CALL_STATUS; + +typedef enum { + FAILURE_CODE_MISC = 1, + FAILURE_CODE_BAD_DESTINATION_NUM = 2, + FAILURE_CODE_USER_OFFLINE = 3, + FAILURE_CODE_NO_PROXY_FOUND = 4, + FAILURE_CODE_SESSION_TERMINATED = 5, + FAILURE_CODE_NO_CODEC_FOUND = 6, + FAILURE_CODE_LOCAL_SOUND_ERR = 7, + FAILURE_CODE_REMOTE_SOUND_ERR = 8, + FAILURE_CODE_CALL_BLOCKED = 9, + FAILURE_CODE_RECIPIENT_NOT_FRIEND = 10, + FAILURE_CODE_CALL_UNAUTHORIZED = 11, + FAILURE_CODE_SOUND_REC_ERR = 12, +} FAILURE_CODES; + + +typedef enum { + TFLAG_INBOUND = (1 << 1), + TFLAG_OUTBOUND = (1 << 2), +/* + TFLAG_IO = (1 << 0), + TFLAG_DTMF = (1 << 3), + TFLAG_VOICE = (1 << 4), + TFLAG_HANGUP = (1 << 5), + TFLAG_LINEAR = (1 << 6), + TFLAG_CODEC = (1 << 7), + TFLAG_BREAK = (1 << 8), + TFLAG_ANSWER = (1 << 9) +*/ +} TFLAGS; + +typedef struct skype_window_handler_s skype_window_handler_t; + +typedef struct airpe_interface_s airpe_interface_t; +typedef struct private_object_s private_object_t; + +struct airpe_interface_s { + char *name; + int debug; + char *dialplan; + char *context; + char *destination; + char *X11_display; + char *skype_user; + char *skype_password; + + int audio_in_port; + int audio_out_port; + switch_socket_t *audio_in_socket; + switch_socket_t *audio_out_socket; + switch_socket_t *audio_in_active_socket; + switch_socket_t *audio_out_active_socket; + + switch_thread_t *airpe_thread_skype; + skype_window_handler_t *skype_window; + + int protocol; + USER_STATUS user_status; + CALL_STATUS call_status; + CONN_STATUS conn_status; + + char *skype_version; + char *mood_text; + int active_call_id; + int last_fail_reason; + int running; + private_object_t *tech_pvt; + char *partner_displayname; + char *partner_handle; +}; + +struct private_object_s { + unsigned int flags; + switch_mutex_t *flag_mutex; + switch_mutex_t *mutex; + + airpe_interface_t *airpe; + switch_core_session_t *session; + switch_caller_profile_t *caller_profile; + char *destination; + + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + uint8_t databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; +}; + + +/* CORE */ +airpe_interface_t *airpe_find_interface( const char *name); +switch_core_session_t *airpe_request_session( switch_memory_pool_t **pool ); +switch_status_t airpe_tech_init( private_object_t * tech_pvt, switch_core_session_t * session ); + +/* API */ +switch_status_t airpe_register_api( switch_loadable_module_interface_t **module_interface ); + +/* APPS */ +switch_status_t airpe_register_apps( switch_loadable_module_interface_t **module_interface ); + +/* INTERFACE - COMMON FUNCTIONS */ +const char *skype_user_status_string(USER_STATUS st); +USER_STATUS skype_user_status(const char *str); +const char *skype_conn_status_string(CONN_STATUS st); +CONN_STATUS skype_conn_status(const char *str); + +switch_status_t airpe_set_version(airpe_interface_t *airpe, char *string); +switch_status_t airpe_set_mood_text(airpe_interface_t *airpe, char *string); +switch_status_t airpe_set_partner_displayname(airpe_interface_t *airpe, char *string); +switch_status_t airpe_set_partner_handle(airpe_interface_t *airpe, char *string); + +switch_status_t airpe_hangup( private_object_t *pvt ); +switch_status_t airpe_audio_read( private_object_t *pvt ); +switch_status_t airpe_audio_write( private_object_t *pvt, switch_frame_t *frame ); + +switch_status_t airpe_cmd_write(airpe_interface_t *p, char *msg); +switch_status_t airpe_originate(airpe_interface_t *p, char *dest); +switch_status_t airpe_explicit_hangup(airpe_interface_t *airpe); +switch_status_t airpe_manage_skype_msg( airpe_interface_t *airpe, const char *msg ); + +switch_status_t airpe_on_connect_messages( airpe_interface_t *airpe, int protocol ); + +/* INTERFACE - PLATFORM DEPENDANT FUNCTIONS */ +switch_status_t airpe_skype_watchdog_reset(skype_window_handler_t *window); +switch_status_t airpe_skype_watchdog_increment(skype_window_handler_t *window); +skype_window_handler_t *skype_window_alloc( switch_memory_pool_t *pool); +switch_status_t airpe_skype_avalaible(skype_window_handler_t *window); +switch_status_t skype_restart( skype_window_handler_t *airpe, const char *params ); +switch_status_t airpe_skype_send_message(skype_window_handler_t *window, const char *message_P); +void *SWITCH_THREAD_FUNC airpe_skype_thread(switch_thread_t * thread, void *obj); + +/* 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 expandtab: + */
participants (1)
-
FreeSWITCH SVN