From 218badc69f28bc273faad787376c94ce5a95e9ca Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli Date: Sun, 14 Mar 2010 10:38:56 +0000 Subject: [PATCH] skypiax: preparing for renaming to mod_skypopen git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@16983 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- .../mod_skypiax/mod_skypopen/Makefile.am | 7 + .../endpoints/mod_skypiax/mod_skypopen/README | 9 + .../mod_skypiax/mod_skypopen/mod_skypopen.c | 2558 +++++++++++++++++ .../mod_skypiax/mod_skypopen/skypopen.h | 338 +++ .../mod_skypopen/skypopen_protocol.c | 1715 +++++++++++ 5 files changed, 4627 insertions(+) create mode 100644 src/mod/endpoints/mod_skypiax/mod_skypopen/Makefile.am create mode 100644 src/mod/endpoints/mod_skypiax/mod_skypopen/README create mode 100644 src/mod/endpoints/mod_skypiax/mod_skypopen/mod_skypopen.c create mode 100644 src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen.h create mode 100644 src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen_protocol.c diff --git a/src/mod/endpoints/mod_skypiax/mod_skypopen/Makefile.am b/src/mod/endpoints/mod_skypiax/mod_skypopen/Makefile.am new file mode 100644 index 0000000000..141470c20e --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypopen/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/build/modmake.rulesam +MODNAME=mod_skypopen +mod_LTLIBRARIES = mod_skypopen.la +mod_skypopen_la_SOURCES = mod_skypopen.c skypopen_protocol.c +mod_skypopen_la_CFLAGS = $(AM_CFLAGS) -DSKYPIAX_SVN_VERSION=\"`cat $(switch_builddir)/.version`\" -I../../../../libs/spandsp/src -I../../../..//libs/tiff-3.8.2/libtiff +mod_skypopen_la_LIBADD = $(switch_builddir)/libfreeswitch.la +mod_skypopen_la_LDFLAGS = -L../../../../libs/spandsp/src -avoid-version -module -no-undefined -shared -lX11 -lspandsp diff --git a/src/mod/endpoints/mod_skypiax/mod_skypopen/README b/src/mod/endpoints/mod_skypiax/mod_skypopen/README new file mode 100644 index 0000000000..ca71d53087 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypopen/README @@ -0,0 +1,9 @@ +Skypopen, Skype Endpoint and Trunk + +All documentation on compiling, using, configuring, +tricks and tweaks, possible problems at: + +http://wiki.freeswitch.org/wiki/Skypiax_Skype_Endpoint_and_Trunk + +< gmaruzz at gmail dot com > + diff --git a/src/mod/endpoints/mod_skypiax/mod_skypopen/mod_skypopen.c b/src/mod/endpoints/mod_skypiax/mod_skypopen/mod_skypopen.c new file mode 100644 index 0000000000..e7584a1812 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypopen/mod_skypopen.c @@ -0,0 +1,2558 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * This module (mod_skypopen) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * + * mod_skypopen.c -- Skype compatible Endpoint Module + * + */ + +#include "skypopen.h" +#define MDL_CHAT_PROTO "skype" + +#ifdef WIN32 +/***************/ +// from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ + +#include + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else /* */ +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif /* */ +struct sk_timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +int gettimeofday(struct timeval *tv, struct sk_timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch */ + tmpres /= 10; /*convert into microseconds */ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long) (tmpres / 1000000UL); + tv->tv_usec = (long) (tmpres % 1000000UL); + } + if (NULL != tz) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + return 0; +} + +/***************/ +#endif /* WIN32 */ +SWITCH_MODULE_LOAD_FUNCTION(mod_skypopen_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skypopen_shutdown); +SWITCH_MODULE_DEFINITION(mod_skypopen, mod_skypopen_load, mod_skypopen_shutdown, NULL); +SWITCH_STANDARD_API(sk_function); +/* BEGIN: Changes here */ +#define SK_SYNTAX "list [full] || console || skype_API_msg || remove < skypeusername | #interface_name | #interface_id > || reload" +/* END: Changes heres */ +SWITCH_STANDARD_API(skypopen_function); +#define SKYPOPEN_SYNTAX "interface_name skype_API_msg" + +SWITCH_STANDARD_API(skypopen_chat_function); +#define SKYPOPEN_CHAT_SYNTAX "interface_name remote_skypename TEXT" +#define FULL_RELOAD 0 +#define SOFT_RELOAD 1 + +char *interface_status[] = { /* should match SKYPOPEN_STATE_xxx in skypopen.h */ + "IDLE", + "DOWN", + "RING", + "DIALING", + "BUSY", + "UP", + "RINGING", + "PRERING", + "DOUBLE", + "SELECTD", + "HANG_RQ", + "PREANSW" +}; +char *skype_callflow[] = { /* should match CALLFLOW_XXX in skypopen.h */ + "CALL_IDLE", + "CALL_DOWN", + "INCOMING_RNG", + "CALL_DIALING", + "CALL_LINEBUSY", + "CALL_ACTIVE", + "INCOMING_HNG", + "CALL_RLEASD", + "CALL_NOCARR", + "CALL_INFLUX", + "CALL_INCOMING", + "CALL_FAILED", + "CALL_NOSRVC", + "CALL_OUTRESTR", + "CALL_SECFAIL", + "CALL_NOANSWER", + "STATUS_FNSHED", + "STATUS_CANCLED", + "STATUS_FAILED", + "STATUS_REFUSED", + "STATUS_RINGING", + "STATUS_INPROGRS", + "STATUS_UNPLACD", + "STATUS_ROUTING", + "STATUS_EARLYMD", + "INCOMING_CLID", + "STATUS_RMTEHOLD" +}; + + +static struct { + int debug; + char *context; + char *dialplan; + char *destination; + char *skype_user; + char *report_incoming_chatmessages; + char *silent_mode; + char *write_silence_when_idle; + int calls; + int real_interfaces; + int next_interface; + private_t SKYPOPEN_INTERFACES[SKYPOPEN_MAX_INTERFACES]; + switch_mutex_t *mutex; + private_t *sk_console; + int start_port; +} globals; + +switch_endpoint_interface_t *skypopen_endpoint_interface; +switch_memory_pool_t *skypopen_module_pool = NULL; +int running = 0; + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, globals.context); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_destination, globals.destination); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_skype_user, globals.skype_user); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_report_incoming_chatmessages, globals.report_incoming_chatmessages); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_silent_mode, globals.silent_mode); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_write_silence_when_idle, globals.write_silence_when_idle); + +static switch_status_t interface_exists(char *the_interface); +static switch_status_t remove_interface(char *the_interface); + +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_consume_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); +static switch_status_t skypopen_tech_init(private_t * tech_pvt, switch_core_session_t *session); + +static switch_status_t skypopen_codec(private_t * tech_pvt, int sample_rate, int codec_ms) +{ + switch_core_session_t *session = 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) { + ERRORA("Can't load codec?\n", SKYPOPEN_P_LOG); + 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) { + ERRORA("Can't load codec?\n", SKYPOPEN_P_LOG); + 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; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + + if (session) { + switch_core_session_set_read_codec(session, &tech_pvt->read_codec); + switch_core_session_set_write_codec(session, &tech_pvt->write_codec); + switch_core_session_rwunlock(session); + } else { + ERRORA("no session\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t skypopen_tech_init(private_t * tech_pvt, switch_core_session_t *session) +{ + + switch_assert(tech_pvt != NULL); + switch_assert(session != NULL); + 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); + switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(session), sizeof(tech_pvt->session_uuid_str)); + if (!strlen(tech_pvt->session_uuid_str)) { + ERRORA("no tech_pvt->session_uuid_str\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + if (skypopen_codec(tech_pvt, SAMPLERATE_SKYPOPEN, 20) != SWITCH_STATUS_SUCCESS) { + ERRORA("skypopen_codec FAILED\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + dtmf_rx_init(&tech_pvt->dtmf_state, NULL, NULL); + dtmf_rx_parms(&tech_pvt->dtmf_state, 0, 10, 10, -99); + + + DEBUGA_SKYPE("skypopen_tech_init SUCCESS\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t interface_exists(char *the_interface) +{ + int i; + int interface_id; + + if (*the_interface == '#') { /* look by interface id or interface name */ + the_interface++; + switch_assert(the_interface); + interface_id = atoi(the_interface); + + /* take a number as interface id */ + if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) { + if (strlen(globals.SKYPOPEN_INTERFACES[interface_id].name)) { + return SWITCH_STATUS_SUCCESS; + } + } else { + /* interface name */ + for (interface_id = 0; interface_id < SKYPOPEN_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPOPEN_INTERFACES[interface_id].name, the_interface) == 0) { + return SWITCH_STATUS_SUCCESS; + break; + } + } + } + } else { /* look by skype_user */ + + + for (i = 0; i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].skype_user)) { + if (strcmp(globals.SKYPOPEN_INTERFACES[i].skype_user, the_interface) == 0) { + return SWITCH_STATUS_SUCCESS; + } + } + } + } + return SWITCH_STATUS_FALSE; +} + +static switch_status_t remove_interface(char *the_interface) +{ + int x = 10; + unsigned int howmany = 8; + int interface_id = -1; + private_t *tech_pvt = NULL; + switch_status_t status; + + + if (*the_interface == '#') { /* remove by interface id or interface name */ + the_interface++; + switch_assert(the_interface); + interface_id = atoi(the_interface); + + if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) { + /* take a number as interface id */ + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + } else { + + for (interface_id = 0; interface_id < SKYPOPEN_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPOPEN_INTERFACES[interface_id].name, the_interface) == 0) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + break; + } + } + } + } else { /* remove by skype_user */ + for (interface_id = 0; interface_id < SKYPOPEN_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPOPEN_INTERFACES[interface_id].skype_user, the_interface) == 0) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + break; + } + } + } + + if (!tech_pvt) { + DEBUGA_SKYPE("interface '%s' does not exist\n", SKYPOPEN_P_LOG, the_interface); + goto end; + } + + if (strlen(globals.SKYPOPEN_INTERFACES[interface_id].session_uuid_str)) { + DEBUGA_SKYPE("interface '%s' is busy\n", SKYPOPEN_P_LOG, the_interface); + goto end; + } + + globals.SKYPOPEN_INTERFACES[interface_id].running = 0; + + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread) { +#ifdef WIN32 + skypopen_signaling_write(tech_pvt, "DIE"); + switch_sleep(10000); + switch_file_write(tech_pvt->SkypopenHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die +#else /* WIN32 */ + howmany = write(tech_pvt->SkypopenHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ + } + + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread) { +#ifdef WIN32 + if (SendMessage(tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) { // let's the skypopen_api_thread_func die + DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPOPEN_P_LOG, GetLastError()); + globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread = NULL; + } +#else + if (tech_pvt->running && tech_pvt->SkypopenHandles.disp) { + XEvent e; + Atom atom1 = XInternAtom(tech_pvt->SkypopenHandles.disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False); + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom1; /* leading message */ + e.xclient.display = tech_pvt->SkypopenHandles.disp; + e.xclient.window = tech_pvt->SkypopenHandles.skype_win; + e.xclient.format = 8; + + XSendEvent(tech_pvt->SkypopenHandles.disp, tech_pvt->SkypopenHandles.win, False, 0, &e); + XSync(tech_pvt->SkypopenHandles.disp, False); + } +#endif + } + + while (x) { + x--; + switch_yield(50000); + } + + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread) { + switch_thread_join(&status, globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread); + } + + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread) { + switch_thread_join(&status, globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread); + } + + switch_mutex_lock(globals.mutex); + if (globals.sk_console == &globals.SKYPOPEN_INTERFACES[interface_id]) { + DEBUGA_SKYPE("interface '%s' no more console\n", SKYPOPEN_P_LOG, the_interface); + globals.sk_console = NULL; + } else { + DEBUGA_SKYPE("interface '%s' STILL console\n", SKYPOPEN_P_LOG, the_interface); + } + memset(&globals.SKYPOPEN_INTERFACES[interface_id], '\0', sizeof(private_t)); + globals.real_interfaces--; + switch_mutex_unlock(globals.mutex); + + DEBUGA_SKYPE("interface '%s' deleted successfully\n", SKYPOPEN_P_LOG, the_interface); + globals.SKYPOPEN_INTERFACES[interface_id].running = 1; + end: + return SWITCH_STATUS_SUCCESS; +} + + +/* + 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); + switch_assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + switch_channel_set_variable(channel, "skype_user", tech_pvt->skype_user); + switch_set_flag(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); + switch_mutex_lock(globals.mutex); + globals.calls++; + + switch_mutex_unlock(globals.mutex); + DEBUGA_SKYPE("%s CHANNEL INIT %s\n", SKYPOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_destroy(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + + + if (tech_pvt) { + DEBUGA_SKYPE("%s CHANNEL DESTROY %s\n", SKYPOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session)); + + 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); + } + + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_destroy(&tech_pvt->timer_read); + } + + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next){ + switch_core_timer_destroy(&tech_pvt->timer_write); + } + + if(tech_pvt->read_buffer){ + switch_buffer_destroy(&tech_pvt->read_buffer); + } + if(tech_pvt->write_buffer){ + switch_buffer_destroy(&tech_pvt->write_buffer); + } + + *tech_pvt->session_uuid_str = '\0'; + tech_pvt->interface_state = SKYPOPEN_STATE_IDLE; + if (tech_pvt->skype_callflow == CALLFLOW_STATUS_FINISHED) { + tech_pvt->skype_callflow = CALLFLOW_CALL_IDLE; + } + switch_core_session_set_private(session, NULL); + } else { + DEBUGA_SKYPE("!!!!!!NO tech_pvt!!!! CHANNEL DESTROY %s\n", SKYPOPEN_P_LOG, switch_core_session_get_uuid(session)); + } + + 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; + char msg_to_skype[256]; + switch_status_t status; + + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_test_flag(channel, CF_ANSWERED)) { + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + tech_pvt->ob_failed_calls++; + } else { + tech_pvt->ib_failed_calls++; + } + } + + switch_clear_flag(tech_pvt, TFLAG_IO); + switch_clear_flag(tech_pvt, TFLAG_VOICE); + + tech_pvt->interface_state = SKYPOPEN_STATE_HANGUP_REQUESTED; + + if (strlen(tech_pvt->skype_call_id)) { + DEBUGA_SKYPE("hanging up skype call: %s\n", SKYPOPEN_P_LOG, tech_pvt->skype_call_id); + sprintf(msg_to_skype, "ALTER CALL %s HANGUP", tech_pvt->skype_call_id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + + if (tech_pvt->tcp_cli_thread) { + switch_thread_join(&status, tech_pvt->tcp_cli_thread); + } + if (tech_pvt->tcp_srv_thread) { + switch_thread_join(&status, tech_pvt->tcp_srv_thread); + } + DEBUGA_SKYPE("%s CHANNEL HANGUP\n", SKYPOPEN_P_LOG, tech_pvt->name); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + + tech_pvt->interface_state = SKYPOPEN_STATE_IDLE; + if (tech_pvt->skype_callflow == CALLFLOW_STATUS_FINISHED) { + tech_pvt->skype_callflow = CALLFLOW_CALL_IDLE; + } + switch_mutex_unlock(globals.mutex); + + 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); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_SKYPE("%s CHANNEL ROUTING\n", SKYPOPEN_P_LOG, tech_pvt->name); + + 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); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_SKYPE("%s CHANNEL EXECUTE\n", SKYPOPEN_P_LOG, tech_pvt->name); + + 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); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_SKYPE("%s CHANNEL KILL_CHANNEL\n", SKYPOPEN_P_LOG, tech_pvt->name); + switch (sig) { + case SWITCH_SIG_KILL: + switch_mutex_lock(tech_pvt->flag_mutex); + DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_KILL\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + if (tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD) { + DEBUGA_SKYPE("FYI %s CHANNEL in CALLFLOW_STATUS_REMOTEHOLD got SWITCH_SIG_KILL\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + } + if (switch_channel_get_state(channel) == CS_NEW) { + ERRORA("FYI %s CHANNEL in CS_NEW state got SWITCH_SIG_KILL\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + } + if (switch_channel_get_state(channel) != CS_NEW && switch_channel_get_state(channel) < CS_EXECUTE) { + ERRORA("FYI %s CHANNEL in %d state got SWITCH_SIG_KILL\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel), switch_channel_get_state(channel)); + } + switch_clear_flag(tech_pvt, TFLAG_IO); + switch_clear_flag(tech_pvt, TFLAG_VOICE); + switch_set_flag(tech_pvt, TFLAG_HANGUP); + switch_mutex_unlock(tech_pvt->flag_mutex); + break; + case SWITCH_SIG_BREAK: + DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_BREAK\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + switch_mutex_lock(tech_pvt->flag_mutex); + switch_set_flag(tech_pvt, TFLAG_BREAK); + switch_mutex_unlock(tech_pvt->flag_mutex); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} +static switch_status_t channel_on_consume_media(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + + DEBUGA_SKYPE("%s CHANNEL CONSUME_MEDIA\n", SKYPOPEN_P_LOG, tech_pvt->name); + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t channel_on_exchange_media(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + tech_pvt = switch_core_session_get_private(session); + DEBUGA_SKYPE("%s CHANNEL EXCHANGE_MEDIA\n", SKYPOPEN_P_LOG, tech_pvt->name); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_soft_execute(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + tech_pvt = switch_core_session_get_private(session); + DEBUGA_SKYPE("%s CHANNEL SOFT_EXECUTE\n", SKYPOPEN_P_LOG, tech_pvt->name); + 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); + + DEBUGA_SKYPE("%s CHANNEL SEND_DTMF\n", SKYPOPEN_P_LOG, tech_pvt->name); + DEBUGA_SKYPE("DTMF: %c\n", SKYPOPEN_P_LOG, dtmf->digit); + + skypopen_senddigit(tech_pvt, dtmf->digit); + + 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; + switch_byte_t *data; + char digit_str[256]; + short *frame_16_khz; + short frame_8_khz[160]; + unsigned int i; + unsigned int a; + size_t bytes_read = 0; + int try=0; + + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) { + ERRORA("channel not ready \n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + if (!tech_pvt->read_buffer) { + int32_t max_len = 640 * 10; + + switch_buffer_create(skypopen_module_pool, &tech_pvt->read_buffer, max_len); + switch_assert(tech_pvt->read_buffer); + switch_buffer_zero(tech_pvt->read_buffer); + tech_pvt->begin_to_read = 1; + } + + + tech_pvt->read_frame.flags = SFF_NONE; + *frame = NULL; + + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_next(&tech_pvt->timer_read); + } + +read: + + + if (tech_pvt && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA + || tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) { + switch_mutex_lock(tech_pvt->mutex_audio_srv); + if (switch_buffer_inuse(tech_pvt->read_buffer)) { + bytes_read = switch_buffer_read(tech_pvt->read_buffer, tech_pvt->read_frame.data, 640); + tech_pvt->read_frame.datalen = bytes_read; + } + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + + try=0; + if (!bytes_read) { + switch_sleep(1000); + try++; + if(try < 5) + goto read; + DEBUGA_SKYPE("skypopen_audio_read Silence\n", SKYPOPEN_P_LOG); + memset(tech_pvt->read_frame.data, 255, SAMPLES_PER_FRAME * sizeof(short)); + tech_pvt->read_frame.datalen = 640; + + } + } else { + memset(tech_pvt->read_frame.data, 255, SAMPLES_PER_FRAME * sizeof(short)); + tech_pvt->read_frame.datalen = 640; + } + + switch_set_flag(tech_pvt, TFLAG_VOICE); + while (switch_test_flag(tech_pvt, TFLAG_IO)) { + if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { + switch_clear_flag(tech_pvt, TFLAG_BREAK); + DEBUGA_SKYPE("CHANNEL READ FRAME goto CNG\n", SKYPOPEN_P_LOG); + goto cng; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + DEBUGA_SKYPE("CHANNEL READ FRAME not IO\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_IO) && switch_test_flag(tech_pvt, TFLAG_VOICE)) { + switch_clear_flag(tech_pvt, TFLAG_VOICE); + if (!tech_pvt->read_frame.datalen) { + DEBUGA_SKYPE("CHANNEL READ CONTINUE\n", SKYPOPEN_P_LOG); + continue; + } + *frame = &tech_pvt->read_frame; + + + if (switch_true(switch_channel_get_variable(channel, "skype_get_inband_dtmf"))) { + + frame_16_khz = tech_pvt->read_frame.data; + + a = 0; + for (i = 0; i < tech_pvt->read_frame.datalen / sizeof(short); i++) { + frame_8_khz[a] = frame_16_khz[i]; + i++; + a++; + } + + memset(digit_str, 0, sizeof(digit_str)); + dtmf_rx(&tech_pvt->dtmf_state, (int16_t *) frame_8_khz, 160); + dtmf_rx_get(&tech_pvt->dtmf_state, digit_str, sizeof(digit_str)); + + + if (digit_str[0]) { + switch_time_t new_dtmf_timestamp = switch_time_now(); + if ((new_dtmf_timestamp - tech_pvt->old_dtmf_timestamp) > 350000) { //FIXME: make it configurable + char *p = digit_str; + switch_channel_t *channel = switch_core_session_get_channel(session); + + while (p && *p) { + switch_dtmf_t dtmf; + dtmf.digit = *p; + dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION; + switch_channel_queue_dtmf(channel, &dtmf); + p++; + } + NOTICA("DTMF DETECTED: [%s] new_dtmf_timestamp: %u, delta_t: %u\n", SKYPOPEN_P_LOG, digit_str, (unsigned int) new_dtmf_timestamp, + (unsigned int) (new_dtmf_timestamp - tech_pvt->old_dtmf_timestamp)); + tech_pvt->old_dtmf_timestamp = new_dtmf_timestamp; + } + } + } +#if SWITCH_BYTE_ORDER == __BIG_ENDIAN + if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { + switch_swap_linear((*frame)->data, (int) (*frame)->datalen / 2); + } +#endif + return SWITCH_STATUS_SUCCESS; + } + DEBUGA_SKYPE("CHANNEL READ no TFLAG_IO\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + DEBUGA_SKYPE("CHANNEL READ FALSE\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + + 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_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) { + ERRORA("channel not ready \n", SKYPOPEN_P_LOG); + 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 + if (!tech_pvt->write_buffer) { + int32_t max_len = 640 * 3; + + switch_buffer_create(skypopen_module_pool, &tech_pvt->write_buffer, max_len); + switch_assert(tech_pvt->write_buffer); + } + + switch_mutex_lock(tech_pvt->mutex_audio_cli); + if (switch_buffer_freespace(tech_pvt->write_buffer) < frame->datalen) { + DEBUGA_SKYPE("NO SPACE WRITE: %d\n", SKYPOPEN_P_LOG, frame->datalen); + //switch_buffer_toss(tech_pvt->write_buffer, frame->datalen); + switch_buffer_zero(tech_pvt->write_buffer); + } + switch_buffer_write(tech_pvt->write_buffer, frame->data, frame->datalen); + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + + tech_pvt->begin_to_write = 1; + + return SWITCH_STATUS_SUCCESS; +} + +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); + switch_assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_SKYPE("ANSWERED! \n", SKYPOPEN_P_LOG); + + 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; + //int i; + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + DEBUGA_SKYPE("%s CHANNEL got SWITCH_MESSAGE_INDICATE_ANSWER\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + + channel_answer_channel(session); + + if(tech_pvt->read_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_srv); + switch_buffer_zero(tech_pvt->read_buffer); + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_read); + } + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + } + + if(tech_pvt->write_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_cli); + switch_buffer_zero(tech_pvt->write_buffer); + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_write); + } + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + } + DEBUGA_SKYPE("Synching audio\n", SKYPOPEN_P_LOG); + + } + break; + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + + DEBUGA_SKYPE("%s CHANNEL got SWITCH_MESSAGE_INDICATE_AUDIO_SYNC\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + + if(tech_pvt->read_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_srv); + switch_buffer_zero(tech_pvt->read_buffer); + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_read); + } + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + } + + if(tech_pvt->write_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_cli); + switch_buffer_zero(tech_pvt->write_buffer); + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_write); + } + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + } + DEBUGA_SKYPE("Synching audio\n", SKYPOPEN_P_LOG); + break; + case SWITCH_MESSAGE_INDICATE_BRIDGE: + DEBUGA_SKYPE("%s CHANNEL got SWITCH_MESSAGE_INDICATE_BRIDGE\n", SKYPOPEN_P_LOG, switch_channel_get_name(channel)); + + if(tech_pvt->read_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_srv); + switch_buffer_zero(tech_pvt->read_buffer); + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_read); + } + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + } + + if(tech_pvt->write_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_cli); + switch_buffer_zero(tech_pvt->write_buffer); + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_write); + } + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + } + DEBUGA_SKYPE("Synching audio\n", SKYPOPEN_P_LOG); + break; + + default: + { + + DEBUGA_SKYPE("MSG_ID=%d\n", SKYPOPEN_P_LOG, msg->message_id); + } + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +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 = ""; + } + + WARNINGA("event: |||%s|||\n", SKYPOPEN_P_LOG, body); + + return SWITCH_STATUS_SUCCESS; +} + +switch_state_handler_table_t skypopen_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 */ channel_on_consume_media, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park */ NULL, + /*.on_reporting */ NULL, + /*.on_destroy */ channel_on_destroy +}; + +switch_io_routines_t skypopen_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 +}; + +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) +{ + private_t *tech_pvt = NULL; + if ((*new_session = switch_core_session_request(skypopen_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, pool)) != 0) { + switch_channel_t *channel = NULL; + switch_caller_profile_t *caller_profile; + char *rdest; + int found = 0; + char interface_name[256]; + + DEBUGA_SKYPE("1 SESSION_REQUEST %s\n", SKYPOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_add_stream(*new_session, NULL); + + + if (!zstr(outbound_profile->destination_number)) { + int i; + char *slash; + + switch_copy_string(interface_name, outbound_profile->destination_number, 255); + slash = strrchr(interface_name, '/'); + *slash = '\0'; + + switch_mutex_lock(globals.mutex); + if (strncmp("ANY", interface_name, strlen(interface_name)) == 0 || strncmp("RR", interface_name, strlen(interface_name)) == 0) { + /* Find the first idle interface using Round Robin */ + DEBUGA_SKYPE("Finding one available skype interface RR\n", SKYPOPEN_P_LOG); + tech_pvt = find_available_skypopen_interface_rr(NULL); + if (tech_pvt) + found = 1; + } + + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].name, interface_name, strlen(interface_name)) == 0)) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].session_uuid_str)) { + DEBUGA_SKYPE + ("globals.SKYPOPEN_INTERFACES[%d].name=|||%s||| session_uuid_str=|||%s||| is BUSY\n", + SKYPOPEN_P_LOG, i, globals.SKYPOPEN_INTERFACES[i].name, globals.SKYPOPEN_INTERFACES[i].session_uuid_str); + DEBUGA_SKYPE("1 SESSION_DESTROY %s\n", SKYPOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + + DEBUGA_SKYPE("globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||?\n", SKYPOPEN_P_LOG, i, globals.SKYPOPEN_INTERFACES[i].name); + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + found = 1; + break; + } + + } + + } else { + ERRORA("Doh! no destination number?\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (!found) { + DEBUGA_SKYPE("Doh! no available interface for |||%s|||?\n", SKYPOPEN_P_LOG, interface_name); + DEBUGA_SKYPE("2 SESSION_DESTROY %s\n", SKYPOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + + channel = switch_core_session_get_channel(*new_session); + if (!channel) { + ERRORA("Doh! no channel?\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + switch_channel_set_variable(channel, "waste", "false"); + if (skypopen_tech_init(tech_pvt, *new_session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + + if (outbound_profile) { + char name[128]; + + if (strncmp("ANY", outbound_profile->destination_number, 3) == 0) { + snprintf(name, sizeof(name), "skypopen/ANY/%s%s", tech_pvt->name, outbound_profile->destination_number + 3); + } else if (strncmp("RR", outbound_profile->destination_number, 2) == 0) { + snprintf(name, sizeof(name), "skypopen/RR/%s%s", tech_pvt->name, outbound_profile->destination_number + 2); + } else { + snprintf(name, sizeof(name), "skypopen/%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 { + ERRORA("Doh! no caller profile\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + tech_pvt->ob_calls++; + + rdest = strchr(caller_profile->destination_number, '/'); + *rdest++ = '\0'; + + switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(*new_session), sizeof(tech_pvt->session_uuid_str)); + caller_profile = tech_pvt->caller_profile; + caller_profile->destination_number = rdest; + + switch_channel_set_flag(channel, CF_OUTBOUND); + switch_set_flag(tech_pvt, TFLAG_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + skypopen_call(tech_pvt, rdest, 30); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_SUCCESS; + } + + ERRORA("Doh! no new_session\n", SKYPOPEN_P_LOG); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; +} + +/*! + * \brief This thread runs during a call, and monitor the interface for signaling, like hangup, caller id, etc most of signaling is handled inside the skypopen_signaling_read function + * + */ +static void *SWITCH_THREAD_FUNC skypopen_signaling_thread_func(switch_thread_t * thread, void *obj) +{ + private_t *tech_pvt = obj; + int res; + int forever = 1; + + if (!tech_pvt) + return NULL; + + DEBUGA_SKYPE("In skypopen_signaling_thread_func: started, p=%p\n", SKYPOPEN_P_LOG, (void *) tech_pvt); + + while (forever) { + if (!(running && tech_pvt->running)) + break; + res = skypopen_signaling_read(tech_pvt); + if (res == CALLFLOW_INCOMING_HANGUP || tech_pvt->skype_callflow == CALLFLOW_INCOMING_HANGUP) { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + DEBUGA_SKYPE("skype call ended\n", SKYPOPEN_P_LOG); + + if (tech_pvt) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + channel = switch_core_session_get_channel(session); + if (channel) { + switch_channel_state_t state = switch_channel_get_state(channel); + if (state < CS_EXECUTE) { + switch_sleep(10000); //10 msec, let the state evolve from CS_NEW + } + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + } else { + ERRORA("no channel?\n", SKYPOPEN_P_LOG); + } + switch_core_session_rwunlock(session); + } else { + DEBUGA_SKYPE("no session\n", SKYPOPEN_P_LOG); + } + switch_mutex_lock(globals.mutex); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + *tech_pvt->session_uuid_str = '\0'; + *tech_pvt->skype_call_id = '\0'; + *tech_pvt->initial_skype_user = '\0'; + switch_mutex_unlock(globals.mutex); + + switch_sleep(300000); //0.3 sec + switch_mutex_lock(globals.mutex); + tech_pvt->skype_callflow = CALLFLOW_CALL_IDLE; + tech_pvt->interface_state = SKYPOPEN_STATE_IDLE; + switch_mutex_unlock(globals.mutex); + } else { + ERRORA("no tech_pvt?\n", SKYPOPEN_P_LOG); + } + } + } + DEBUGA_SKYPE("EXITING\n", SKYPOPEN_P_LOG); + return NULL; +} + +static switch_status_t load_config(int reload_type) +{ + char *cf = "skypopen.conf"; + switch_xml_t cfg, xml, global_settings, param, interfaces, myinterface; + private_t *tech_pvt = NULL; + + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, skypopen_module_pool); + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + ERRORA("open of %s failed\n", SKYPOPEN_P_LOG, cf); + running = 0; + switch_xml_free(xml); + return SWITCH_STATUS_TERM; + } + + switch_mutex_lock(globals.mutex); + if ((global_settings = switch_xml_child(cfg, "global_settings"))) { + 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 (!strcasecmp(var, "debug")) { + globals.debug = atoi(val); + DEBUGA_SKYPE("globals.debug=%d\n", SKYPOPEN_P_LOG, globals.debug); + + } else if (!strcmp(var, "context")) { + set_global_context(val); + DEBUGA_SKYPE("globals.context=%s\n", SKYPOPEN_P_LOG, globals.context); + } else if (!strcmp(var, "dialplan")) { + set_global_dialplan(val); + DEBUGA_SKYPE("globals.dialplan=%s\n", SKYPOPEN_P_LOG, globals.dialplan); + } else if (!strcmp(var, "destination")) { + set_global_destination(val); + DEBUGA_SKYPE("globals.destination=%s\n", SKYPOPEN_P_LOG, globals.destination); + } else if (!strcmp(var, "skype_user")) { + set_global_skype_user(val); + DEBUGA_SKYPE("globals.skype_user=%s\n", SKYPOPEN_P_LOG, globals.skype_user); + } else if (!strcmp(var, "report_incoming_chatmessages")) { + set_global_report_incoming_chatmessages(val); + DEBUGA_SKYPE("globals.report_incoming_chatmessages=%s\n", SKYPOPEN_P_LOG, globals.report_incoming_chatmessages); + } else if (!strcmp(var, "silent_mode")) { + set_global_silent_mode(val); + DEBUGA_SKYPE("globals.silent_mode=%s\n", SKYPOPEN_P_LOG, globals.silent_mode); + } else if (!strcmp(var, "write_silence_when_idle")) { + set_global_write_silence_when_idle(val); + DEBUGA_SKYPE("globals.write_silence_when_idle=%s\n", SKYPOPEN_P_LOG, globals.write_silence_when_idle); + } + + } + } + + globals.start_port = 32769; + if ((interfaces = switch_xml_child(cfg, "per_interface_settings"))) { + int i = 0; + + for (myinterface = switch_xml_child(interfaces, "interface"); myinterface; myinterface = myinterface->next) { + char *id = (char *) switch_xml_attr(myinterface, "id"); + char *name = (char *) switch_xml_attr(myinterface, "name"); + char *context = "default"; + char *dialplan = "XML"; + char *destination = "5000"; + char *X11_display = NULL; + char *skype_user = NULL; + char *report_incoming_chatmessages = "true"; + char *silent_mode = "false"; + char *write_silence_when_idle = "true"; + uint32_t interface_id = 0; + + if(globals.context) + context=globals.context; + if(globals.dialplan) + dialplan=globals.dialplan; + if(globals.destination) + destination=globals.destination; + if(globals.skype_user) + skype_user=globals.skype_user; + if(globals.report_incoming_chatmessages) + report_incoming_chatmessages=globals.report_incoming_chatmessages; + if(globals.silent_mode) + silent_mode=globals.silent_mode; + if(globals.write_silence_when_idle) + write_silence_when_idle=globals.write_silence_when_idle; + + tech_pvt = NULL; + + for (param = switch_xml_child(myinterface, "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, "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, "report_incoming_chatmessages")) { + report_incoming_chatmessages = val; + } else if (!strcasecmp(var, "silent_mode")) { + silent_mode = val; + } else if (!strcasecmp(var, "write_silence_when_idle")) { + write_silence_when_idle = val; + } else if (!strcasecmp(var, "X11-display") || !strcasecmp(var, "X11_display")) { + X11_display = val; + } + + } + if (!skype_user) { + ERRORA("interface missing REQUIRED param 'skype_user'\n", SKYPOPEN_P_LOG); + continue; + } + + if (reload_type == SOFT_RELOAD) { + char the_interface[256]; + sprintf(the_interface, "#%s", name); + + if (interface_exists(the_interface) == SWITCH_STATUS_SUCCESS) { + continue; + } + } +#ifndef WIN32 + if (!X11_display) { + ERRORA("interface missing REQUIRED param 'X11_display'\n", SKYPOPEN_P_LOG); + continue; + } +#endif + if (!id) { + ERRORA("interface missing REQUIRED param 'id'\n", SKYPOPEN_P_LOG); + continue; + } + if (switch_is_number(id)) { + interface_id = atoi(id); + DEBUGA_SKYPE("interface_id=%d\n", SKYPOPEN_P_LOG, interface_id); + } else { + ERRORA("interface param 'id' MUST be a number, now id='%s'\n", SKYPOPEN_P_LOG, id); + continue; + } + + if (!name) { + WARNINGA("interface missing param 'name', not nice, but works\n", SKYPOPEN_P_LOG); + } + + if (name) { + DEBUGA_SKYPE("name=%s\n", SKYPOPEN_P_LOG, name); + } +#ifndef WIN32 + if (!XInitThreads()) { + ERRORA("Not initialized XInitThreads!\n", SKYPOPEN_P_LOG); + } else { + DEBUGA_SKYPE("Initialized XInitThreads!\n", SKYPOPEN_P_LOG); + } + switch_sleep(1000); +#endif /* WIN32 */ + + if (interface_id && interface_id < SKYPOPEN_MAX_INTERFACES) { + private_t newconf; + switch_threadattr_t *skypopen_api_thread_attr = NULL; + switch_threadattr_t *skypopen_signaling_thread_attr = NULL; + + memset(&newconf, '\0', sizeof(newconf)); + globals.SKYPOPEN_INTERFACES[interface_id] = newconf; + globals.SKYPOPEN_INTERFACES[interface_id].running = 1; + + + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].interface_id, id); + if (name) { + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].name, name); + } else { + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].name, "N/A"); + } + DEBUGA_SKYPE("CONFIGURING interface_id=%d\n", SKYPOPEN_P_LOG, interface_id); + + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].context, context); + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].dialplan, dialplan); + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].destination, destination); + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].X11_display, X11_display); + switch_set_string(globals.SKYPOPEN_INTERFACES[interface_id].skype_user, skype_user); + + if (!strcmp(report_incoming_chatmessages, "true") || !strcmp(report_incoming_chatmessages, "1")) { + globals.SKYPOPEN_INTERFACES[interface_id].report_incoming_chatmessages = 1; + } else { + globals.SKYPOPEN_INTERFACES[interface_id].report_incoming_chatmessages = 0; //redundant, just in case + + } + + if (!strcmp(silent_mode, "true") || !strcmp(silent_mode, "1")) { + globals.SKYPOPEN_INTERFACES[interface_id].silent_mode = 1; + } else { + globals.SKYPOPEN_INTERFACES[interface_id].silent_mode = 0; //redundant, just in case + + } + + if (!strcmp(write_silence_when_idle, "true") || !strcmp(write_silence_when_idle, "1")) { + globals.SKYPOPEN_INTERFACES[interface_id].write_silence_when_idle = 1; + } else { + globals.SKYPOPEN_INTERFACES[interface_id].write_silence_when_idle = 0; //redundant, just in case + + } + + DEBUGA_SKYPE("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].name=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].name); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].context=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].context); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].dialplan=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].dialplan); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].destination=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].destination); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].X11_display=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].X11_display); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].skype_user=%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].skype_user); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].report_incoming_chatmessages=%d\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].report_incoming_chatmessages); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].silent_mode=%d\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].silent_mode); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPOPEN_INTERFACES[interface_id].write_silence_when_idle=%d\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].write_silence_when_idle); + + WARNINGA("STARTING interface_id=%d\n", SKYPOPEN_P_LOG, interface_id); + + switch_threadattr_create(&skypopen_api_thread_attr, skypopen_module_pool); + switch_threadattr_detach_set(skypopen_api_thread_attr, 0); + switch_threadattr_stacksize_set(skypopen_api_thread_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread, + skypopen_api_thread_attr, skypopen_do_skypeapi_thread, &globals.SKYPOPEN_INTERFACES[interface_id], skypopen_module_pool); + + switch_sleep(100000); + + switch_threadattr_create(&skypopen_signaling_thread_attr, skypopen_module_pool); + switch_threadattr_detach_set(skypopen_signaling_thread_attr, 0); + switch_threadattr_stacksize_set(skypopen_signaling_thread_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&globals.SKYPOPEN_INTERFACES[interface_id]. + skypopen_signaling_thread, skypopen_signaling_thread_attr, + skypopen_signaling_thread_func, &globals.SKYPOPEN_INTERFACES[interface_id], skypopen_module_pool); + + switch_sleep(100000); + + skypopen_audio_init(&globals.SKYPOPEN_INTERFACES[interface_id]); + switch_mutex_init(&globals.SKYPOPEN_INTERFACES[interface_id].mutex_audio_srv, SWITCH_MUTEX_NESTED, skypopen_module_pool); + switch_mutex_init(&globals.SKYPOPEN_INTERFACES[interface_id].mutex_audio_cli, SWITCH_MUTEX_NESTED, skypopen_module_pool); + + NOTICA + ("WAITING roughly 10 seconds to find a running Skype client and connect to its SKYPE API for interface_id=%d\n", + SKYPOPEN_P_LOG, interface_id); + i = 0; + while (globals.SKYPOPEN_INTERFACES[interface_id].SkypopenHandles.api_connected == 0 && running && i < 200) { // 10 seconds! thanks Jeff Lenk + switch_sleep(50000); + i++; + } + if (globals.SKYPOPEN_INTERFACES[interface_id].SkypopenHandles.api_connected) { + NOTICA + ("Found a running Skype client, connected to its SKYPE API for interface_id=%d, waiting 60 seconds for CURRENTUSERHANDLE==%s\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].skype_user); + } else { + ERRORA + ("Failed to connect to a SKYPE API for interface_id=%d, no SKYPE client running, please (re)start Skype client. Skypopen exiting\n", + SKYPOPEN_P_LOG, interface_id); + running = 0; + switch_mutex_unlock(globals.mutex); + switch_xml_free(xml); + return SWITCH_STATUS_FALSE; + } + + i = 0; + while (globals.SKYPOPEN_INTERFACES[interface_id].SkypopenHandles.currentuserhandle == 0 && running && i < 1200) { // 60 seconds! thanks Jeff Lenk + switch_sleep(50000); + i++; + } + if (globals.SKYPOPEN_INTERFACES[interface_id].SkypopenHandles.currentuserhandle) { + WARNINGA + ("Interface_id=%d is now STARTED, the Skype client to which we are connected gave us the correct CURRENTUSERHANDLE (%s)\n", + SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].skype_user); + + + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "PROTOCOL 7"); + switch_sleep(10000); + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET AUTOAWAY OFF"); + switch_sleep(10000); + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET WINDOWSTATE HIDDEN"); + switch_sleep(10000); + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET USERSTATUS ONLINE"); + switch_sleep(10000); + if (globals.SKYPOPEN_INTERFACES[interface_id].silent_mode) { + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET SILENT_MODE ON"); + switch_sleep(10000); + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET SILENT_MODE OFF"); + switch_sleep(10000); + skypopen_signaling_write(&globals.SKYPOPEN_INTERFACES[interface_id], "SET SILENT_MODE ON"); + switch_sleep(10000); + } + } else { + ERRORA + ("The Skype client to which we are connected FAILED to gave us CURRENTUSERHANDLE=%s, interface_id=%d FAILED to start. No Skype client logged in as '%s' has been found. Please (re)launch a Skype client logged in as '%s'. Skypopen exiting now\n", + SKYPOPEN_P_LOG, globals.SKYPOPEN_INTERFACES[interface_id].skype_user, + interface_id, globals.SKYPOPEN_INTERFACES[interface_id].skype_user, globals.SKYPOPEN_INTERFACES[interface_id].skype_user); + running = 0; + switch_mutex_unlock(globals.mutex); + switch_xml_free(xml); + return SWITCH_STATUS_FALSE; + } + + } else { + ERRORA("interface id %d is higher than SKYPOPEN_MAX_INTERFACES (%d)\n", SKYPOPEN_P_LOG, interface_id, SKYPOPEN_MAX_INTERFACES); + continue; + } + + } + + for (i = 0; i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name)) { + /* How many real intterfaces */ + globals.real_interfaces = i + 1; + + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].interface_id=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].interface_id); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].name=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].name); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].context=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].context); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].dialplan=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].dialplan); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].destination=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].destination); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].X11_display=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].X11_display); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].skype_user=%s\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].skype_user); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].report_incoming_chatmessages=%d\n", SKYPOPEN_P_LOG, i, i, + globals.SKYPOPEN_INTERFACES[i].report_incoming_chatmessages); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].silent_mode=%d\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].silent_mode); + DEBUGA_SKYPE("i=%d globals.SKYPOPEN_INTERFACES[%d].write_silence_when_idle=%d\n", SKYPOPEN_P_LOG, i, i, globals.SKYPOPEN_INTERFACES[i].write_silence_when_idle); + } + } + } + + switch_mutex_unlock(globals.mutex); + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} +static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, + const char *hint) +{ + char *user = NULL, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL; + private_t *tech_pvt = NULL; + int i = 0, found = 0, tried = 0; + char skype_msg[1024]; + + switch_assert(proto != NULL); + + DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + hint ? hint : "NULL"); + + if (!to || !strlen(to)) { + ERRORA("Missing To: header.\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + if ((!from && !hint) || (!strlen(from) && !strlen(hint))) { + ERRORA("Missing From: AND Hint: headers.\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + if (from && (f_user = strdup(from))) { + if ((f_host = strchr(f_user, '@'))) { + *f_host++ = '\0'; + if ((f_resource = strchr(f_host, '/'))) { + *f_resource++ = '\0'; + } + } + } + + if (to && (user = strdup(to))) { + if ((host = strchr(user, '@'))) { + *host++ = '\0'; + } + + DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + hint ? hint : "NULL"); + if (hint && strlen(hint)) { + //in hint we receive the interface name to use + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].name, hint, strlen(hint)) == 0)) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + DEBUGA_SKYPE("Using interface: globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||\n", SKYPOPEN_P_LOG, i, globals.SKYPOPEN_INTERFACES[i].name); + found = 1; + break; + } + } + } else { + //we have no a predefined interface name to use (hint is NULL), so let's choose an interface from the username (from) + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].skype_user, from, strlen(from)) == 0)) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + DEBUGA_SKYPE("Using interface: globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||\n", SKYPOPEN_P_LOG, i, globals.SKYPOPEN_INTERFACES[i].name); + found = 1; + break; + } + } + } + if (!found) { + ERRORA("ERROR: A Skypopen interface with name='%s' or one with skypeuser='%s' was not found\n", SKYPOPEN_P_LOG, hint ? hint : "NULL", + from ? from : "NULL"); + goto end; + } else { + + snprintf(skype_msg, sizeof(skype_msg), "CHAT CREATE %s", to); + skypopen_signaling_write(tech_pvt, skype_msg); + switch_sleep(1000); + } + + found = 0; + + while (!found) { + for (i = 0; i < MAX_CHATS; i++) { + if (!strcmp(tech_pvt->chats[i].dialog_partner, to)) { + snprintf(skype_msg, sizeof(skype_msg), "CHATMESSAGE %s %s", tech_pvt->chats[i].chatname, body); + skypopen_signaling_write(tech_pvt, skype_msg); + found = 1; + break; + } + } + if (found) { + break; + } + if (tried > 1000) { + ERRORA("No chat with dialog_partner='%s' was found\n", SKYPOPEN_P_LOG, to); + break; + } + switch_sleep(1000); + } + + } + end: + switch_safe_free(user); + switch_safe_free(f_user); + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_skypopen_load) +{ + switch_api_interface_t *commands_api_interface; + switch_chat_interface_t *chat_interface; + + skypopen_module_pool = pool; + memset(&globals, '\0', sizeof(globals)); + + running = 1; + + if (load_config(FULL_RELOAD) != SWITCH_STATUS_SUCCESS) { + running = 0; + return SWITCH_STATUS_FALSE; + } + + if (switch_event_reserve_subclass(MY_EVENT_INCOMING_CHATMESSAGE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n"); + return SWITCH_STATUS_GENERR; + } + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + skypopen_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + skypopen_endpoint_interface->interface_name = "skypopen"; + skypopen_endpoint_interface->io_routines = &skypopen_io_routines; + skypopen_endpoint_interface->state_handler = &skypopen_state_handlers; + + if (running) { + + SWITCH_ADD_API(commands_api_interface, "sk", "Skypopen console commands", sk_function, SK_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "skypopen", "Skypopen interface commands", skypopen_function, SKYPOPEN_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "skypopen_chat", "Skypopen_chat interface remote_skypename TEXT", skypopen_chat_function, SKYPOPEN_CHAT_SYNTAX); + SWITCH_ADD_CHAT(chat_interface, MDL_CHAT_PROTO, chat_send); + + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; + } else + return SWITCH_STATUS_FALSE; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skypopen_shutdown) +{ + int x; + private_t *tech_pvt = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + unsigned int howmany = 8; + int interface_id; + + running = 0; + + for (interface_id = 0; interface_id < SKYPOPEN_MAX_INTERFACES; interface_id++) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + + if (strlen(globals.SKYPOPEN_INTERFACES[interface_id].name)) { + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread) { +#ifdef WIN32 + skypopen_signaling_write(tech_pvt, "DIE"); + switch_sleep(10000); + switch_file_write(tech_pvt->SkypopenHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die + +#else /* WIN32 */ + howmany = write(tech_pvt->SkypopenHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ + } + + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread) { +#ifdef WIN32 + if (SendMessage(tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) { // let's the skypopen_api_thread_func die + DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPOPEN_P_LOG, GetLastError()); + tech_pvt->skypopen_api_thread=NULL; + } +#else + if (tech_pvt->SkypopenHandles.disp) { + XEvent e; + Atom atom1 = XInternAtom(tech_pvt->SkypopenHandles.disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", + False); + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom1; /* leading message */ + e.xclient.display = tech_pvt->SkypopenHandles.disp; + e.xclient.window = tech_pvt->SkypopenHandles.skype_win; + e.xclient.format = 8; + + XSendEvent(tech_pvt->SkypopenHandles.disp, tech_pvt->SkypopenHandles.win, False, 0, &e); + XSync(tech_pvt->SkypopenHandles.disp, False); + } +#endif + } + x = 10; + while (x) { //FIXME 0.5 seconds? + x--; + switch_yield(50000); + } + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread) { + switch_thread_join(&status, globals.SKYPOPEN_INTERFACES[interface_id].skypopen_signaling_thread); + } + if (status != SWITCH_STATUS_SUCCESS) + DEBUGA_SKYPE("got FALSE here, thread was not joined\n", SKYPOPEN_P_LOG); + if (globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread) { + switch_thread_join(&status, globals.SKYPOPEN_INTERFACES[interface_id].skypopen_api_thread); + } + if (status != SWITCH_STATUS_SUCCESS) + DEBUGA_SKYPE("got FALSE here, thread was not joined\n", SKYPOPEN_P_LOG); +#ifndef WIN32 + WARNINGA("SHUTDOWN interface_id=%d\n", SKYPOPEN_P_LOG, interface_id); + shutdown(tech_pvt->audiopipe_cli[0], 2); + close(tech_pvt->audiopipe_cli[0]); + shutdown(tech_pvt->audiopipe_cli[1], 2); + close(tech_pvt->audiopipe_cli[1]); + shutdown(tech_pvt->audiopipe_srv[0], 2); + close(tech_pvt->audiopipe_srv[0]); + shutdown(tech_pvt->audiopipe_srv[1], 2); + close(tech_pvt->audiopipe_srv[1]); + shutdown(tech_pvt->SkypopenHandles.fdesc[0], 2); + close(tech_pvt->SkypopenHandles.fdesc[0]); + shutdown(tech_pvt->SkypopenHandles.fdesc[1], 2); + close(tech_pvt->SkypopenHandles.fdesc[1]); +#endif /* WIN32 */ + } + + } + switch_event_free_subclass(MY_EVENT_INCOMING_CHATMESSAGE); + + switch_safe_free(globals.context); + switch_safe_free(globals.dialplan); + switch_safe_free(globals.destination); + switch_safe_free(globals.skype_user); + switch_safe_free(globals.report_incoming_chatmessages); + switch_safe_free(globals.silent_mode); + switch_safe_free(globals.write_silence_when_idle); + + return SWITCH_STATUS_SUCCESS; +} + +void *SWITCH_THREAD_FUNC skypopen_do_tcp_srv_thread(switch_thread_t * thread, void *obj) +{ + return skypopen_do_tcp_srv_thread_func(obj); +} + +void *SWITCH_THREAD_FUNC skypopen_do_tcp_cli_thread(switch_thread_t * thread, void *obj) +{ + return skypopen_do_tcp_cli_thread_func(obj); +} + +void *SWITCH_THREAD_FUNC skypopen_do_skypeapi_thread(switch_thread_t * thread, void *obj) +{ + return skypopen_do_skypeapi_thread_func(obj); +} + +int dtmf_received(private_t * tech_pvt, char *value) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + channel = switch_core_session_get_channel(session); + + if (channel) { + + if (switch_channel_test_flag(channel, CF_BRIDGED) + && !switch_true(switch_channel_get_variable(channel, "skype_add_outband_dtmf_also_when_bridged"))) { + + + NOTICA + ("received DTMF '%c' on channel %s, but we're BRIDGED, so we DO NOT relay it out of band. If you DO want to relay it out of band when bridged too, on top of audio DTMF, set the channel variable 'skype_add_outband_dtmf_also_when_bridged=true' \n", + SKYPOPEN_P_LOG, value[0], switch_channel_get_name(channel)); + + } else { + + + + switch_dtmf_t dtmf = { (char) value[0], switch_core_default_dtmf_duration(0) }; + DEBUGA_SKYPE("received DTMF %c on channel %s\n", SKYPOPEN_P_LOG, dtmf.digit, switch_channel_get_name(channel)); + switch_mutex_lock(tech_pvt->flag_mutex); + switch_channel_queue_dtmf(channel, &dtmf); + switch_set_flag(tech_pvt, TFLAG_DTMF); + switch_mutex_unlock(tech_pvt->flag_mutex); + } + } else { + WARNINGA("received %c DTMF, but no channel?\n", SKYPOPEN_P_LOG, value[0]); + } + switch_core_session_rwunlock(session); + + return 0; +} + +int start_audio_threads(private_t * tech_pvt) +{ + switch_threadattr_t *thd_attr = NULL; + + tech_pvt->begin_to_write = 0; + tech_pvt->begin_to_read = 0; + + if (switch_core_timer_init(&tech_pvt->timer_read, "soft", 20, 320, skypopen_module_pool) != SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_read); + + if (switch_core_timer_init(&tech_pvt->timer_write, "soft", 20, 320, skypopen_module_pool) != SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", SKYPOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_write); + + switch_threadattr_create(&thd_attr, skypopen_module_pool); + switch_threadattr_detach_set(thd_attr, 0); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&tech_pvt->tcp_srv_thread, thd_attr, skypopen_do_tcp_srv_thread, tech_pvt, skypopen_module_pool) == SWITCH_STATUS_SUCCESS) { + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPOPEN_P_LOG); + } else { + ERRORA("failed to start tcp_srv_thread thread.\n", SKYPOPEN_P_LOG); + return -1; + } + + switch_threadattr_create(&thd_attr, skypopen_module_pool); + switch_threadattr_detach_set(thd_attr, 0); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&tech_pvt->tcp_cli_thread, thd_attr, skypopen_do_tcp_cli_thread, tech_pvt, skypopen_module_pool) == SWITCH_STATUS_SUCCESS) { + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPOPEN_P_LOG); + } else { + ERRORA("failed to start tcp_cli_thread thread.\n", SKYPOPEN_P_LOG); + return -1; + } + switch_sleep(100000); + + if (tech_pvt->tcp_cli_thread == NULL || tech_pvt->tcp_srv_thread == NULL) { + ERRORA("tcp_cli_thread or tcp_srv_thread exited\n", SKYPOPEN_P_LOG); + return -1; + } + + return 0; +} + +int new_inbound_channel(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if ((session = switch_core_session_request(skypopen_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL)) != 0) { + DEBUGA_SKYPE("2 SESSION_REQUEST %s\n", SKYPOPEN_P_LOG, switch_core_session_get_uuid(session)); + switch_core_session_add_stream(session, NULL); + channel = switch_core_session_get_channel(session); + if (!channel) { + ERRORA("Doh! no channel?\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + switch_channel_set_variable(channel, "waste", "false"); + if (skypopen_tech_init(tech_pvt, session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + + if ((tech_pvt->caller_profile = + switch_caller_profile_new(switch_core_session_get_pool(session), "skypopen", + tech_pvt->dialplan, tech_pvt->callid_name, + tech_pvt->callid_number, NULL, NULL, NULL, NULL, "mod_skypopen", tech_pvt->context, tech_pvt->destination)) != 0) { + char name[128]; + switch_snprintf(name, sizeof(name), "skypopen/%s", tech_pvt->name); + switch_channel_set_name(channel, name); + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + } + switch_channel_set_state(channel, CS_INIT); + if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Error spawning thread\n", SKYPOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + } + if (channel) { + switch_channel_mark_answered(channel); + switch_channel_set_variable(channel, "skype_user", tech_pvt->skype_user); + switch_channel_set_variable(channel, "initial_skype_user", tech_pvt->initial_skype_user); + } + + DEBUGA_SKYPE("new_inbound_channel\n", SKYPOPEN_P_LOG); + + return 0; +} + +int remote_party_is_ringing(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", SKYPOPEN_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No session???\n", SKYPOPEN_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_ring_ready(channel); + DEBUGA_SKYPE("skype_call: REMOTE PARTY RINGING\n", SKYPOPEN_P_LOG); + } else { + ERRORA("No channel???\n", SKYPOPEN_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + return 0; +} + +int remote_party_is_early_media(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n\n\n", SKYPOPEN_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + switch_core_session_add_stream(session, NULL); + } else { + ERRORA("No session???\n", SKYPOPEN_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_pre_answered(channel); + DEBUGA_SKYPE("skype_call: REMOTE PARTY EARLY MEDIA\n", SKYPOPEN_P_LOG); + } else { + ERRORA("No channel???\n", SKYPOPEN_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + return 0; +} + +int outbound_channel_answered(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", SKYPOPEN_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No channel???\n", SKYPOPEN_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_answered(channel); + } else { + ERRORA("No channel???\n", SKYPOPEN_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + DEBUGA_SKYPE("outbound_channel_answered!\n", SKYPOPEN_P_LOG); + + return 0; +} + +private_t *find_available_skypopen_interface_rr(private_t * tech_pvt_calling) +{ + private_t *tech_pvt = NULL; + int i; + + switch_mutex_lock(globals.mutex); + + /* Fact is the real interface start from 1 */ + //XXX no, is just a convention, but you can have it start from 0. I do not, for aestetic reasons :-) + //if (globals.next_interface == 0) globals.next_interface = 1; + + for (i = 0; i < SKYPOPEN_MAX_INTERFACES; i++) { + int interface_id; + + interface_id = globals.next_interface; + globals.next_interface = interface_id + 1 < SKYPOPEN_MAX_INTERFACES ? interface_id + 1 : 0; + + if (strlen(globals.SKYPOPEN_INTERFACES[interface_id].name)) { + int skype_state = 0; + + tech_pvt = &globals.SKYPOPEN_INTERFACES[interface_id]; + skype_state = tech_pvt->interface_state; + //DEBUGA_SKYPE("skype interface: %d, name: %s, state: %d\n", SKYPOPEN_P_LOG, interface_id, globals.SKYPOPEN_INTERFACES[interface_id].name, skype_state); + if ((tech_pvt_calling ? strcmp(tech_pvt->skype_user, tech_pvt_calling->skype_user) : 1) + && (SKYPOPEN_STATE_DOWN == skype_state || 0 == skype_state) && (tech_pvt->skype_callflow == CALLFLOW_STATUS_FINISHED + || 0 == tech_pvt->skype_callflow)) { + DEBUGA_SKYPE("returning as available skype interface name: %s, state: %d callflow: %d\n", SKYPOPEN_P_LOG, tech_pvt->name, skype_state, + tech_pvt->skype_callflow); + if (tech_pvt_calling == NULL) { + tech_pvt->interface_state = SKYPOPEN_STATE_SELECTED; + } + + switch_mutex_unlock(globals.mutex); + return tech_pvt; + } + } + } + + switch_mutex_unlock(globals.mutex); + return NULL; +} + +SWITCH_STANDARD_API(sk_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (globals.sk_console) + stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_console->name); + else + stream->write_function(stream, "sk console is NOT yet assigned\n"); + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc || !argv[0]) { + stream->write_function(stream, "%s", SK_SYNTAX); + goto end; + } + + if (!strcasecmp(argv[0], "list")) { + int i; + int ib = 0; + int ib_failed = 0; + int ob = 0; + int ob_failed = 0; + char next_flag_char = ' '; + + stream->write_function(stream, "F ID\t Name \tIB (F/T) OB (F/T)\tState\tCallFlw\t\tUUID\n"); + stream->write_function(stream, "= ====\t ======== \t======= =======\t======\t============\t======\n"); + + for (i = 0; i < SKYPOPEN_MAX_INTERFACES; i++) { + next_flag_char = i == globals.next_interface ? '*' : ' '; + ib += globals.SKYPOPEN_INTERFACES[i].ib_calls; + ib_failed += globals.SKYPOPEN_INTERFACES[i].ib_failed_calls; + ob += globals.SKYPOPEN_INTERFACES[i].ob_calls; + ob_failed += globals.SKYPOPEN_INTERFACES[i].ob_failed_calls; + + if (strlen(globals.SKYPOPEN_INTERFACES[i].name)) { + stream->write_function(stream, + "%c %d\t[%s]\t%3u/%u\t%6u/%u\t%s\t%s\t%s\n", + next_flag_char, + i, globals.SKYPOPEN_INTERFACES[i].name, + globals.SKYPOPEN_INTERFACES[i].ib_failed_calls, + globals.SKYPOPEN_INTERFACES[i].ib_calls, + globals.SKYPOPEN_INTERFACES[i].ob_failed_calls, + globals.SKYPOPEN_INTERFACES[i].ob_calls, + interface_status[globals.SKYPOPEN_INTERFACES[i].interface_state], + skype_callflow[globals.SKYPOPEN_INTERFACES[i].skype_callflow], globals.SKYPOPEN_INTERFACES[i].session_uuid_str); + } else if (argc > 1 && !strcasecmp(argv[1], "full")) { + stream->write_function(stream, "%c\t%d\n", next_flag_char, i); + } + + } + stream->write_function(stream, "\nTotal Interfaces: %d IB Calls(Failed/Total): %ld/%ld OB Calls(Failed/Total): %ld/%ld\n", + globals.real_interfaces > 0 ? globals.real_interfaces - 1 : 0, ib_failed, ib, ob_failed, ob); + + } else if (!strcasecmp(argv[0], "console")) { + int i; + int found = 0; + + if (argc == 2) { + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].name, argv[1], strlen(argv[1])) == 0)) { + globals.sk_console = &globals.SKYPOPEN_INTERFACES[i]; + stream->write_function(stream, + "sk console is now: globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPOPEN_INTERFACES[i].name); + stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_console->name); + found = 1; + break; + } + + } + if (!found) + stream->write_function(stream, "ERROR: A Skypopen interface with name='%s' was not found\n", argv[1]); + } else { + + stream->write_function(stream, "-ERR Usage: sk console interface_name\n"); + goto end; + } + + } else if (!strcasecmp(argv[0], "ciapalino")) { + + } else if (!strcasecmp(argv[0], "reload")) { + if (load_config(SOFT_RELOAD) != SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "sk reload failed\n"); + } else { + stream->write_function(stream, "sk reload success\n"); + } + } else if (!strcasecmp(argv[0], "remove")) { + if (argc == 2) { + if (remove_interface(argv[1]) == SWITCH_STATUS_SUCCESS) { + if (interface_exists(argv[1]) == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "sk remove '%s' failed\n", argv[1]); + } else { + stream->write_function(stream, "sk remove '%s' success\n", argv[1]); + } + } + } else { + stream->write_function(stream, "-ERR Usage: sk remove interface_name\n"); + goto end; + } + + } else { + if (globals.sk_console) + skypopen_signaling_write(globals.sk_console, (char *) cmd); + else + stream->write_function(stream, "sk console is NOT yet assigned\n"); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_API(skypopen_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_SYNTAX); + goto end; + } + + if (argc < 2) { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_SYNTAX); + goto end; + } + + if (argv[0]) { + int i; + int found = 0; + + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A Skypopen interface with name='%s' was not found\n", argv[0]); + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; + } else { + skypopen_signaling_write(tech_pvt, (char *) &cmd[strlen(argv[0]) + 1]); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_SYNTAX); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +int skypopen_answer(private_t * tech_pvt, char *id, char *value) +{ + char msg_to_skype[1024]; + int i; + int found = 0; + private_t *giovatech; + struct timeval timenow; + + switch_mutex_lock(globals.mutex); + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name)) { + + giovatech = &globals.SKYPOPEN_INTERFACES[i]; + if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPOPEN_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->callid_number, value)) && ((((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) < 1000000)) { //XXX 1.5sec - can have a max of 1 call coming from the same skypename to the same skypename each 1.5 seconds + found = 1; + DEBUGA_SKYPE + ("FOUND (name=%s, giovatech->interface_state=%d != SKYPOPEN_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n", + SKYPOPEN_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value) + if (tech_pvt->interface_state == SKYPOPEN_STATE_PRERING) { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } else if (tech_pvt->interface_state != 0 && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN) { + WARNINGA("Why an interface_state %d HERE?\n", SKYPOPEN_P_LOG, tech_pvt->interface_state); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } + + break; + } + } + } + + if (found) { + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPOPEN_P_LOG); + + if (tech_pvt && tech_pvt->skype_call_id && !strlen(tech_pvt->skype_call_id)) { + /* we are not inside an active call */ + + tech_pvt->ib_calls++; + + sprintf(msg_to_skype, "GET CALL %s PARTNER_DISPNAME", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + switch_sleep(10000); + tech_pvt->interface_state = SKYPOPEN_STATE_PREANSWER; + sprintf(msg_to_skype, "ALTER CALL %s ANSWER", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + DEBUGA_SKYPE("We answered a Skype RING on skype_call %s\n", SKYPOPEN_P_LOG, id); + gettimeofday(&tech_pvt->answer_time, NULL); + switch_copy_string(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + + switch_copy_string(tech_pvt->callid_number, value, sizeof(tech_pvt->callid_number) - 1); + + DEBUGA_SKYPE + ("NEW! name: %s, state: %d, value=%s, tech_pvt->callid_number=%s, tech_pvt->skype_user=%s\n", + SKYPOPEN_P_LOG, tech_pvt->name, tech_pvt->interface_state, value, tech_pvt->callid_number, tech_pvt->skype_user); + } else if (!tech_pvt || !tech_pvt->skype_call_id) { + ERRORA("No Call ID?\n", SKYPOPEN_P_LOG); + } else { + DEBUGA_SKYPE("We're in a call now (%s), let's refuse this one (%s)\n", SKYPOPEN_P_LOG, tech_pvt->skype_call_id, id); + sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + + switch_mutex_unlock(globals.mutex); + return 0; +} +int skypopen_transfer(private_t * tech_pvt, char *id, char *value) +{ + char msg_to_skype[1024]; + int i; + int found = 0; + private_t *giovatech; + struct timeval timenow; + + switch_mutex_lock(globals.mutex); + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name)) { + + giovatech = &globals.SKYPOPEN_INTERFACES[i]; + if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPOPEN_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->callid_number, value)) && ((((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) < 500000)) { //0.5sec + found = 1; + DEBUGA_SKYPE + ("FOUND (name=%s, giovatech->interface_state=%d != SKYPOPEN_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n", + SKYPOPEN_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value) + break; + } + } + } + + if (found) { + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPOPEN_P_LOG); + + if (!tech_pvt || !tech_pvt->skype_call_id || !strlen(tech_pvt->skype_call_id)) { + /* we are not inside an active call */ + DEBUGA_SKYPE("We're NO MORE in a call now %s\n", SKYPOPEN_P_LOG, (tech_pvt && tech_pvt->skype_call_id) ? tech_pvt->skype_call_id : ""); + switch_mutex_unlock(globals.mutex); + + } else { + + /* we're in a call, let's try to transfer */ + /************************** TODO + Checking here if it is possible to transfer this call to Test2 + -> GET CALL 288 CAN_TRANSFER Test2 + <- CALL 288 CAN_TRANSFER test2 TRUE + **********************************/ + + private_t *available_skypopen_interface = NULL; + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPOPEN_INTERFACES[i].name)) { + + giovatech = &globals.SKYPOPEN_INTERFACES[i]; + if (strlen(giovatech->skype_transfer_call_id) && (giovatech->interface_state != SKYPOPEN_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->transfer_callid_number, value)) && ((((timenow.tv_sec - giovatech->transfer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->transfer_time.tv_usec)) < 1000000)) { //1.0 sec + found = 1; + DEBUGA_SKYPE + ("FOUND (name=%s, giovatech->interface_state=%d != SKYPOPEN_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->transfer_callid_number=%s == value=%s)\n", + SKYPOPEN_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->transfer_callid_number, value) + break; + } + } + } + + if (found) { + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPOPEN_P_LOG); + + available_skypopen_interface = find_available_skypopen_interface_rr(tech_pvt); + if (available_skypopen_interface) { + /* there is a skypopen interface idle, let's transfer the call to it */ + + switch_copy_string(available_skypopen_interface->initial_skype_user, tech_pvt->skype_user, sizeof(tech_pvt->skype_user) - 1); + + gettimeofday(&tech_pvt->transfer_time, NULL); + switch_copy_string(tech_pvt->skype_transfer_call_id, id, sizeof(tech_pvt->skype_transfer_call_id) - 1); + + switch_copy_string(tech_pvt->transfer_callid_number, value, sizeof(tech_pvt->transfer_callid_number) - 1); + + DEBUGA_SKYPE + ("Let's transfer the skype_call %s to %s interface (with skype_user: %s), because we are already in a skypopen call(%s)\n", + SKYPOPEN_P_LOG, tech_pvt->skype_call_id, available_skypopen_interface->name, available_skypopen_interface->skype_user, id); + + + sprintf(msg_to_skype, "ALTER CALL %s TRANSFER %s", id, available_skypopen_interface->skype_user); + skypopen_signaling_write(tech_pvt, msg_to_skype); + if (tech_pvt->interface_state == SKYPOPEN_STATE_SELECTED) { + tech_pvt->interface_state = SKYPOPEN_STATE_IDLE; //we marked it SKYPOPEN_STATE_SELECTED just in case it has to make an outbound call + } + } else { + /* no skypopen interfaces idle, do nothing */ + DEBUGA_SKYPE + ("Not answering the skype_call %s, because we are already in a skypopen call(%s) and not transferring, because no other skypopen interfaces are available\n", + SKYPOPEN_P_LOG, id, tech_pvt->skype_call_id); + sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + switch_sleep(10000); + DEBUGA_SKYPE + ("We have NOT answered a Skype RING from skype_call %s, because we are already in a skypopen call (%s)\n", + SKYPOPEN_P_LOG, id, tech_pvt->skype_call_id); + + switch_mutex_unlock(globals.mutex); + } + return 0; +} + +int incoming_chatmessage(private_t * tech_pvt, int which) +{ + switch_event_t *event; + switch_core_session_t *session = NULL; + int event_sent_to_esl = 0; + + DEBUGA_SKYPE("received CHATMESSAGE on interface %s\n", SKYPOPEN_P_LOG, tech_pvt->name); + + if (!tech_pvt->report_incoming_chatmessages) { + DEBUGA_SKYPE("I will not generate an Event, report_incoming_chatmessages is %d\n", SKYPOPEN_P_LOG, tech_pvt->report_incoming_chatmessages); + return 0; + } + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "chatname", tech_pvt->chatmessages[which].chatname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "id", tech_pvt->chatmessages[which].id); + switch_event_add_body(event, "%s", tech_pvt->chatmessages[which].body); + if (session) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "true"); + if (switch_core_session_queue_event(session, &event) != SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } else { //no session + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "false"); + switch_event_fire(&event); + event_sent_to_esl = 1; + } + + } else { + ERRORA("cannot create event on interface %s. WHY?????\n", SKYPOPEN_P_LOG, tech_pvt->name); + } + + if (!event_sent_to_esl) { + + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "chatname", tech_pvt->chatmessages[which].chatname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "id", tech_pvt->chatmessages[which].id); + switch_event_add_body(event, "%s", tech_pvt->chatmessages[which].body); + if (session) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "true"); + } else { //no session + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "false"); + } + switch_event_fire(&event); + } else { + ERRORA("cannot create event on interface %s. WHY?????\n", SKYPOPEN_P_LOG, tech_pvt->name); + } + } + + if (session) { + switch_core_session_rwunlock(session); + } + memset(&tech_pvt->chatmessages[which], '\0', sizeof(&tech_pvt->chatmessages[which])); + return 0; +} + + +SWITCH_STANDARD_API(skypopen_chat_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + int i; + int found = 0; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_CHAT_SYNTAX); + goto end; + } + + if (argc < 3) { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_CHAT_SYNTAX); + goto end; + } + + if (argv[0]) { + for (i = 0; !found && i < SKYPOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.SKYPOPEN_INTERFACES[i].name) + && (strncmp(globals.SKYPOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.SKYPOPEN_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.SKYPOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A Skypopen interface with name='%s' was not found\n", argv[0]); + goto end; + } else { + + NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPOPEN_P_LOG, MDL_CHAT_PROTO, tech_pvt->skype_user, + argv[1], "SIMPLE MESSAGE", switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), tech_pvt->name); + + chat_send(MDL_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", + switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name); + + } + } else { + stream->write_function(stream, "ERROR, usage: %s", SKYPOPEN_CHAT_SYNTAX); + goto end; + } + + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +int next_port(void) +{ + switch_mutex_lock(globals.mutex); + globals.start_port++; + if (globals.start_port == 65000) + globals.start_port = 32769; + switch_mutex_unlock(globals.mutex); + return (globals.start_port - 1); +} + + +/* 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: + */ diff --git a/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen.h b/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen.h new file mode 100644 index 0000000000..d1d0c5dd00 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen.h @@ -0,0 +1,338 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * This module (mod_skypopen) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * mod_skypopen.c -- Skype compatible Endpoint Module + * + */ + +#include +#include + +#ifndef WIN32 +#include +#include +#include +#include +#endif //WIN32 + +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES +#include +#include + +#ifndef WIN32 +#include +#endif + +#ifdef _MSC_VER +//Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + +#define MY_EVENT_INCOMING_CHATMESSAGE "skypopen::incoming_chatmessage" + +#define SAMPLERATE_SKYPOPEN 16000 +#define SAMPLES_PER_FRAME SAMPLERATE_SKYPOPEN/50 + +#ifndef SKYPOPEN_SVN_VERSION +#define SKYPOPEN_SVN_VERSION SWITCH_VERSION_REVISION +#endif /* SKYPOPEN_SVN_VERSION */ + +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), + TFLAG_BREAK = (1 << 8) +} TFLAGS; + +typedef enum { + GFLAG_MY_CODEC_PREFS = (1 << 0) +} GFLAGS; + +#define DEBUGA_SKYPE(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_SKYPE %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_CALL(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_CALL %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_PBX(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_PBX %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define ERRORA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][ERRORA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define WARNINGA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][WARNINGA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define NOTICA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "rev "SKYPOPEN_SVN_VERSION "[%p|%-7lx][NOTICA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); + +#define SKYPOPEN_P_LOG NULL, (unsigned long)55, __LINE__, tech_pvt ? tech_pvt->name ? tech_pvt->name : "none" : "none", -1, tech_pvt ? tech_pvt->interface_state : -1, tech_pvt ? tech_pvt->skype_callflow : -1 + +/*********************************/ +#define SKYPOPEN_CAUSE_NORMAL 1 +/*********************************/ +#define SKYPOPEN_FRAME_DTMF 1 +/*********************************/ +#define SKYPOPEN_CONTROL_RINGING 1 +#define SKYPOPEN_CONTROL_ANSWER 2 + +/*********************************/ +#define SKYPOPEN_STATE_IDLE 0 +#define SKYPOPEN_STATE_DOWN 1 +#define SKYPOPEN_STATE_RING 2 +#define SKYPOPEN_STATE_DIALING 3 +#define SKYPOPEN_STATE_BUSY 4 +#define SKYPOPEN_STATE_UP 5 +#define SKYPOPEN_STATE_RINGING 6 +#define SKYPOPEN_STATE_PRERING 7 +#define SKYPOPEN_STATE_ERROR_DOUBLE_CALL 8 +#define SKYPOPEN_STATE_SELECTED 9 +#define SKYPOPEN_STATE_HANGUP_REQUESTED 10 +#define SKYPOPEN_STATE_PREANSWER 11 +/*********************************/ +/* call flow from the device */ +#define CALLFLOW_CALL_IDLE 0 +#define CALLFLOW_CALL_DOWN 1 +#define CALLFLOW_INCOMING_RING 2 +#define CALLFLOW_CALL_DIALING 3 +#define CALLFLOW_CALL_LINEBUSY 4 +#define CALLFLOW_CALL_ACTIVE 5 +#define CALLFLOW_INCOMING_HANGUP 6 +#define CALLFLOW_CALL_RELEASED 7 +#define CALLFLOW_CALL_NOCARRIER 8 +#define CALLFLOW_CALL_INFLUX 9 +#define CALLFLOW_CALL_INCOMING 10 +#define CALLFLOW_CALL_FAILED 11 +#define CALLFLOW_CALL_NOSERVICE 12 +#define CALLFLOW_CALL_OUTGOINGRESTRICTED 13 +#define CALLFLOW_CALL_SECURITYFAIL 14 +#define CALLFLOW_CALL_NOANSWER 15 +#define CALLFLOW_STATUS_FINISHED 16 +#define CALLFLOW_STATUS_CANCELLED 17 +#define CALLFLOW_STATUS_FAILED 18 +#define CALLFLOW_STATUS_REFUSED 19 +#define CALLFLOW_STATUS_RINGING 20 +#define CALLFLOW_STATUS_INPROGRESS 21 +#define CALLFLOW_STATUS_UNPLACED 22 +#define CALLFLOW_STATUS_ROUTING 23 +#define CALLFLOW_STATUS_EARLYMEDIA 24 +#define CALLFLOW_INCOMING_CALLID 25 +#define CALLFLOW_STATUS_REMOTEHOLD 26 + +/*********************************/ + +#define SKYPOPEN_MAX_INTERFACES 64 + +#ifndef WIN32 +struct SkypopenHandles { + Window skype_win; + Display *disp; + Window win; + int currentuserhandle; + int api_connected; + int fdesc[2]; +}; +#else //WIN32 + +struct SkypopenHandles { + HWND win32_hInit_MainWindowHandle; + HWND win32_hGlobal_SkypeAPIWindowHandle; + HINSTANCE win32_hInit_ProcessHandle; + char win32_acInit_WindowClassName[128]; + UINT win32_uiGlobal_MsgID_SkypeControlAPIAttach; + UINT win32_uiGlobal_MsgID_SkypeControlAPIDiscover; + int currentuserhandle; + int api_connected; + switch_file_t *fdesc[2]; +}; +#endif //WIN32 + +#define MAX_CHATS 10 + +struct chat { + char chatname[256]; + char dialog_partner[256]; +}; +typedef struct chat chat_t; + +#define MAX_CHATMESSAGES 10 + +struct chatmessage { + char id[256]; + char type[256]; + char chatname[256]; + char from_handle[256]; + char from_dispname[256]; + char body[512]; +}; +typedef struct chatmessage chatmessage_t; +struct private_object { + unsigned int flags; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + char session_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_caller_profile_t *caller_profile; + switch_mutex_t *mutex; + switch_mutex_t *flag_mutex; + + char interface_id[80]; + char name[80]; + char dialplan[80]; + char context[80]; + char dial_regex[256]; + char fail_dial_regex[256]; + char hold_music[256]; + char type[256]; + char X11_display[256]; +#ifdef WIN32 + unsigned short tcp_cli_port; + unsigned short tcp_srv_port; +#else + int tcp_cli_port; + int tcp_srv_port; +#endif + struct SkypopenHandles SkypopenHandles; + + int interface_state; + char language[80]; + char exten[80]; + int skypopen_sound_rate; + char callid_name[50]; + char callid_number[50]; + double playback_boost; + double capture_boost; + int stripmsd; + char skype_call_id[512]; + int skype_call_ongoing; + char skype_friends[4096]; + char skype_fullname[512]; + char skype_displayname[512]; + int skype_callflow; + int skype; + int control_to_send; +#ifdef WIN32 + switch_file_t *audiopipe_srv[2]; + switch_file_t *audiopipe_cli[2]; + switch_file_t *skypopen_sound_capt_fd; +#else /* WIN32 */ + int audiopipe_srv[2]; + int audiopipe_cli[2]; + int skypopen_sound_capt_fd; +#endif /* WIN32 */ + switch_thread_t *tcp_srv_thread; + switch_thread_t *tcp_cli_thread; + switch_thread_t *skypopen_signaling_thread; + switch_thread_t *skypopen_api_thread; + short audiobuf[SAMPLES_PER_FRAME]; + int audiobuf_is_loaded; + short audiobuf_cli[SAMPLES_PER_FRAME]; + switch_mutex_t *mutex_audio_cli; + int flag_audio_cli; + short audiobuf_srv[SAMPLES_PER_FRAME]; + switch_mutex_t *mutex_audio_srv; + int flag_audio_srv; + + FILE *phonebook_writing_fp; + int skypopen_dir_entry_extension_prefix; + char skype_user[256]; + char initial_skype_user[256]; + char skype_password[256]; + char destination[256]; + struct timeval answer_time; + + struct timeval transfer_time; + char transfer_callid_number[50]; + char skype_transfer_call_id[512]; + int running; + uint32_t ib_calls; + uint32_t ob_calls; + uint32_t ib_failed_calls; + uint32_t ob_failed_calls; + + chatmessage_t chatmessages[MAX_CHATMESSAGES]; + chat_t chats[MAX_CHATS]; + uint32_t report_incoming_chatmessages; + switch_timer_t timer_read; + switch_timer_t timer_write; + int begin_to_write; + int begin_to_read; + dtmf_rx_state_t dtmf_state; + switch_time_t old_dtmf_timestamp; + switch_buffer_t *write_buffer; + switch_buffer_t *read_buffer; + int silent_mode; + int write_silence_when_idle; + +}; + +typedef struct private_object private_t; + +void *SWITCH_THREAD_FUNC skypopen_api_thread_func(switch_thread_t * thread, void *obj); +int skypopen_audio_read(private_t * tech_pvt); +int skypopen_audio_init(private_t * tech_pvt); +int skypopen_signaling_write(private_t * tech_pvt, char *msg_to_skype); +int skypopen_signaling_read(private_t * tech_pvt); + +int skypopen_call(private_t * tech_pvt, char *idest, int timeout); +int skypopen_senddigit(private_t * tech_pvt, char digit); + +void *skypopen_do_tcp_srv_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypopen_do_tcp_srv_thread(switch_thread_t * thread, void *obj); + +void *skypopen_do_tcp_cli_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypopen_do_tcp_cli_thread(switch_thread_t * thread, void *obj); + +void *skypopen_do_skypeapi_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypopen_do_skypeapi_thread(switch_thread_t * thread, void *obj); +int dtmf_received(private_t * tech_pvt, char *value); +int start_audio_threads(private_t * tech_pvt); +int new_inbound_channel(private_t * tech_pvt); +int outbound_channel_answered(private_t * tech_pvt); +int skypopen_signaling_write(private_t * tech_pvt, char *msg_to_skype); +#if defined(WIN32) && !defined(__CYGWIN__) +int skypopen_pipe_read(switch_file_t * pipe, short *buf, int howmany); +int skypopen_pipe_write(switch_file_t * pipe, short *buf, int howmany); +/* Visual C do not have strsep ? */ +char *strsep(char **stringp, const char *delim); +#else +int skypopen_pipe_read(int pipe, short *buf, int howmany); +int skypopen_pipe_write(int pipe, short *buf, int howmany); +#endif /* WIN32 */ +int skypopen_close_socket(unsigned int fd); +private_t *find_available_skypopen_interface_rr(private_t * tech_pvt_calling); +int remote_party_is_ringing(private_t * tech_pvt); +int remote_party_is_early_media(private_t * tech_pvt); +int skypopen_answer(private_t * tech_pvt, char *id, char *value); +int skypopen_transfer(private_t * tech_pvt, char *id, char *value); +#ifndef WIN32 +int skypopen_socket_create_and_bind(private_t * tech_pvt, int *which_port); +#else +int skypopen_socket_create_and_bind(private_t * tech_pvt, unsigned short *which_port); +#endif //WIN32 +int incoming_chatmessage(private_t * tech_pvt, int which); +int next_port(void); diff --git a/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen_protocol.c b/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen_protocol.c new file mode 100644 index 0000000000..3e13b9c825 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypopen/skypopen_protocol.c @@ -0,0 +1,1715 @@ +#include "skypopen.h" + +#ifdef ASTERISK +#define skypopen_sleep usleep +#define skypopen_strncpy strncpy +#define tech_pvt p +extern int skypopen_debug; +extern char *skypopen_console_active; +#else /* FREESWITCH */ +#define skypopen_sleep switch_sleep +#define skypopen_strncpy switch_copy_string +extern switch_memory_pool_t *skypopen_module_pool; +extern switch_endpoint_interface_t *skypopen_endpoint_interface; +#endif /* ASTERISK */ +int samplerate_skypopen = SAMPLERATE_SKYPOPEN; + +extern int running; + +/*************************************/ +/* suspicious globals FIXME */ +#ifdef WIN32 +DWORD win32_dwThreadId; +#else +XErrorHandler old_handler = 0; +int xerror = 0; +#endif /* WIN32 */ +/*************************************/ +#ifndef WIN32 +int skypopen_socket_create_and_bind(private_t * tech_pvt, int *which_port) +#else +int skypopen_socket_create_and_bind(private_t * tech_pvt, unsigned short *which_port) +#endif //WIN32 +{ + int s = -1; + struct sockaddr_in my_addr; +#ifndef WIN32 + int start_port = 6001; + unsigned int size = sizeof(int); +#else + unsigned short start_port = 6001; + int size = sizeof(int); +#endif //WIN32 + int sockbufsize = 0; + int flag = 0; + + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_addr.s_addr = htonl(0x7f000001); /* use the localhost */ + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ERRORA("socket Error\n", SKYPOPEN_P_LOG); + return -1; + } + + if (*which_port != 0) + start_port = *which_port; +#ifdef WIN32 + start_port = (unsigned short) next_port(); +#else + start_port = (unsigned short) next_port(); +#endif + my_addr.sin_port = htons(start_port); + *which_port = start_port; + while (bind(s, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) < 0) { + DEBUGA_SKYPE("*which_port=%d, tech_pvt->tcp_cli_port=%d, tech_pvt->tcp_srv_port=%d\n", SKYPOPEN_P_LOG, *which_port, tech_pvt->tcp_cli_port, + tech_pvt->tcp_srv_port); + DEBUGA_SKYPE("bind errno=%d, error: %s\n", SKYPOPEN_P_LOG, errno, strerror(errno)); + start_port++; + my_addr.sin_port = htons(start_port); + *which_port = start_port; + DEBUGA_SKYPE("*which_port=%d, tech_pvt->tcp_cli_port=%d, tech_pvt->tcp_srv_port=%d\n", SKYPOPEN_P_LOG, *which_port, tech_pvt->tcp_cli_port, + tech_pvt->tcp_srv_port); + + if (start_port > 65000) { + ERRORA("NO MORE PORTS! *which_port=%d, tech_pvt->tcp_cli_port=%d, tech_pvt->tcp_srv_port=%d\n", SKYPOPEN_P_LOG, *which_port, + tech_pvt->tcp_cli_port, tech_pvt->tcp_srv_port); + return -1; + } + } + + DEBUGA_SKYPE("Binded! *which_port=%d, tech_pvt->tcp_cli_port=%d, tech_pvt->tcp_srv_port=%d\n", SKYPOPEN_P_LOG, *which_port, tech_pvt->tcp_cli_port, + tech_pvt->tcp_srv_port); + + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("1 SO_RCVBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("1 SO_SNDBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + + + +/* for virtual machines, eg: Linux domU-12-31-39-02-68-28 2.6.18-xenU-ec2-v1.0 #2 SMP Tue Feb 19 10:51:53 EST 2008 i686 athlon i386 GNU/Linux + * use: + * sockbufsize=SAMPLES_PER_FRAME * 8; + */ +#ifdef WIN32 + sockbufsize = SAMPLES_PER_FRAME * 8; +#else + sockbufsize = SAMPLES_PER_FRAME * 8; +#endif //WIN32 + size = sizeof(int); + setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &sockbufsize, size); + + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("2 SO_RCVBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + +/* for virtual machines, eg: Linux domU-12-31-39-02-68-28 2.6.18-xenU-ec2-v1.0 #2 SMP Tue Feb 19 10:51:53 EST 2008 i686 athlon i386 GNU/Linux + * use: + * sockbufsize=SAMPLES_PER_FRAME * 8; + */ +#ifdef WIN32 + sockbufsize = SAMPLES_PER_FRAME * 8; +#else + sockbufsize = SAMPLES_PER_FRAME * 8; +#endif //WIN32 + size = sizeof(int); + setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &sockbufsize, size); + + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("2 SO_SNDBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + + flag = 0; + getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, &size); + DEBUGA_SKYPE("TCP_NODELAY is %d\n", SKYPOPEN_P_LOG, flag); + flag = 1; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, size); + flag = 0; + getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, &size); + DEBUGA_SKYPE("TCP_NODELAY is %d\n", SKYPOPEN_P_LOG, flag); + + + + + return s; +} + +int skypopen_signaling_read(private_t * tech_pvt) +{ + char read_from_pipe[4096]; + char message[4096]; + char message_2[4096]; + char *buf, obj[512] = "", id[512] = "", prop[512] = "", value[512] = "", *where; + char **stringp = NULL; + int a; + unsigned int howmany; + unsigned int i; + + memset(read_from_pipe, 0, 4096); + memset(message, 0, 4096); + memset(message_2, 0, 4096); + + howmany = skypopen_pipe_read(tech_pvt->SkypopenHandles.fdesc[0], (short *) read_from_pipe, sizeof(read_from_pipe)); + + a = 0; + for (i = 0; i < howmany; i++) { + message[a] = read_from_pipe[i]; + a++; + + if (read_from_pipe[i] == '\0') { + + //if (!strstr(message, "DURATION")) { + DEBUGA_SKYPE("READING: |||%s||| \n", SKYPOPEN_P_LOG, message); + //} + if (!strcasecmp(message, "SILENT_MODE OFF")) { + if (tech_pvt->silent_mode) { + DEBUGA_SKYPE("Resetting SILENT_MODE on skype_call: %s.\n", SKYPOPEN_P_LOG, id); + skypopen_signaling_write(tech_pvt, "SET SILENT_MODE ON"); + switch_sleep(1000); + } + } + if (!strcasecmp(message, "ERROR 68")) { + DEBUGA_SKYPE + ("If I don't connect immediately, please give the Skype client authorization to be connected by Skypopen (and to not ask you again)\n", + SKYPOPEN_P_LOG); + skypopen_sleep(1000000); + skypopen_signaling_write(tech_pvt, "PROTOCOL 7"); + skypopen_sleep(10000); + return 0; + } + if (!strncasecmp(message, "ERROR 92 CALL", 12)) { + ERRORA("Skype got ERROR: |||%s|||, the (skypeout) number we called was not recognized as valid\n", SKYPOPEN_P_LOG, message); + tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + DEBUGA_SKYPE("skype_call now is DOWN\n", SKYPOPEN_P_LOG); + tech_pvt->skype_call_id[0] = '\0'; + + if (tech_pvt->interface_state != SKYPOPEN_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } + } + + if (!strncasecmp(message, "ERROR", 4)) { + if (!strncasecmp(message, "ERROR 96 CALL", 12)) { + DEBUGA_SKYPE + ("Skype got ERROR: |||%s|||, we are trying to use this interface to make or receive a call, but another call is half-active on this interface. Let's the previous one to continue.\n", + SKYPOPEN_P_LOG, message); + } else if (!strncasecmp(message, "ERROR 99 CALL", 12)) { + ERRORA("Skype got ERROR: |||%s|||, another call is active on this interface\n\n\n", SKYPOPEN_P_LOG, message); + tech_pvt->interface_state = SKYPOPEN_STATE_ERROR_DOUBLE_CALL; + } else if (!strncasecmp(message, "ERROR 592 ALTER CALL", 19)) { + ERRORA("Skype got ERROR about TRANSFERRING, no problem: |||%s|||\n", SKYPOPEN_P_LOG, message); + } else if (!strncasecmp(message, "ERROR 559 CALL", 13)) { + if (tech_pvt->interface_state == SKYPOPEN_STATE_PREANSWER) { + DEBUGA_SKYPE("Skype got ERROR about a failed action (probably TRYING to ANSWER A CALL), let's go down: |||%s|||\n", SKYPOPEN_P_LOG, + message); + tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + ERRORA("skype_call now is DOWN\n", SKYPOPEN_P_LOG); + tech_pvt->skype_call_id[0] = '\0'; + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + + } else { + DEBUGA_SKYPE("Skype got ERROR about a failed action (probably TRYING to HANGUP A CALL), no problem: |||%s|||\n", SKYPOPEN_P_LOG, + message); + } + } else { + ERRORA("Skype got ERROR: |||%s|||\n", SKYPOPEN_P_LOG, message); + tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + ERRORA("skype_call now is DOWN\n", SKYPOPEN_P_LOG); + tech_pvt->skype_call_id[0] = '\0'; + + if (tech_pvt->interface_state != SKYPOPEN_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } + } + } + + skypopen_strncpy(message_2, message, sizeof(message) - 1); + buf = message; + stringp = &buf; + where = strsep(stringp, " "); + if (!where) { + WARNINGA("Skype MSG without spaces: %s\n", SKYPOPEN_P_LOG, message); + } + + if (!strcasecmp(message, "CURRENTUSERHANDLE")) { + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + if (!strcasecmp(id, tech_pvt->skype_user)) { + tech_pvt->SkypopenHandles.currentuserhandle = 1; + DEBUGA_SKYPE + ("Skype MSG: message: %s, currentuserhandle: %s, cuh: %s, skype_user: %s!\n", + SKYPOPEN_P_LOG, message, obj, id, tech_pvt->skype_user); + } + } + if (!strcasecmp(message, "USER")) { + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(prop, where, sizeof(prop) - 1); + if (!strcasecmp(prop, "RECEIVEDAUTHREQUEST")) { + char msg_to_skype[256]; + DEBUGA_SKYPE("Skype MSG: message: %s, obj: %s, id: %s, prop: %s!\n", SKYPOPEN_P_LOG, message, obj, id, prop); + //TODO: allow authorization based on config param + sprintf(msg_to_skype, "SET USER %s ISAUTHORIZED TRUE", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + } + if (!strcasecmp(message, "MESSAGE")) { + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(prop, where, sizeof(prop) - 1); + if (!strcasecmp(prop, "STATUS")) { + where = strsep(stringp, " "); + skypopen_strncpy(value, where, sizeof(value) - 1); + if (!strcasecmp(value, "RECEIVED")) { + char msg_to_skype[256]; + DEBUGA_SKYPE("Skype MSG: message: %s, obj: %s, id: %s, prop: %s value: %s!\n", SKYPOPEN_P_LOG, message, obj, id, prop, value); + //TODO: authomatically flag messages as read based on config param + sprintf(msg_to_skype, "SET MESSAGE %s SEEN", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + } else if (!strcasecmp(prop, "BODY")) { + char msg_to_skype[256]; + DEBUGA_SKYPE("Skype MSG: message: %s, obj: %s, id: %s, prop: %s!\n", SKYPOPEN_P_LOG, message, obj, id, prop); + //TODO: authomatically flag messages as read based on config param + sprintf(msg_to_skype, "SET MESSAGE %s SEEN", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + } + if (!strcasecmp(message, "CHAT")) { + char msg_to_skype[256]; + int i; + int found; + + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(prop, where, sizeof(prop) - 1); + skypopen_strncpy(value, *stringp, sizeof(value) - 1); + + if (!strcasecmp(prop, "STATUS") && !strcasecmp(value, "DIALOG")) { + DEBUGA_SKYPE("CHAT %s is DIALOG\n", SKYPOPEN_P_LOG, id); + sprintf(msg_to_skype, "GET CHAT %s DIALOG_PARTNER", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + + if (!strcasecmp(prop, "DIALOG_PARTNER")) { + DEBUGA_SKYPE("CHAT %s has DIALOG_PARTNER %s\n", SKYPOPEN_P_LOG, id, value); + found = 0; + for (i = 0; i < MAX_CHATS; i++) { + if (strlen(tech_pvt->chats[i].chatname) == 0 || !strcmp(tech_pvt->chats[i].chatname, id)) { + strncpy(tech_pvt->chats[i].chatname, id, sizeof(tech_pvt->chats[i].chatname)); + strncpy(tech_pvt->chats[i].dialog_partner, value, sizeof(tech_pvt->chats[i].dialog_partner)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why we do not have a chats slot free? we have more than %d chats in parallel?\n", SKYPOPEN_P_LOG, MAX_CHATS); + } + + DEBUGA_SKYPE("CHAT %s is in position %d in the chats array, chatname=%s, dialog_partner=%s\n", SKYPOPEN_P_LOG, id, i, + tech_pvt->chats[i].chatname, tech_pvt->chats[i].dialog_partner); + } + + } + + + if (!strcasecmp(message, "CHATMESSAGE")) { + char msg_to_skype[256]; + int i; + int found; + + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(prop, where, sizeof(prop) - 1); + skypopen_strncpy(value, *stringp, sizeof(value) - 1); + + if (!strcasecmp(prop, "STATUS") && !strcasecmp(value, "RECEIVED")) { + DEBUGA_SKYPE("RECEIVED CHATMESSAGE %s, let's see which type it is\n", SKYPOPEN_P_LOG, id); + sprintf(msg_to_skype, "GET CHATMESSAGE %s TYPE", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + + if (!strcasecmp(prop, "TYPE") && !strcasecmp(value, "SAID")) { + DEBUGA_SKYPE("CHATMESSAGE %s is of type SAID, let's get the other infos\n", SKYPOPEN_P_LOG, id); + found = 0; + for (i = 0; i < MAX_CHATMESSAGES; i++) { + if (strlen(tech_pvt->chatmessages[i].id) == 0) { + strncpy(tech_pvt->chatmessages[i].id, id, sizeof(tech_pvt->chatmessages[i].id)); + strncpy(tech_pvt->chatmessages[i].type, value, sizeof(tech_pvt->chatmessages[i].type)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why we do not have a chatmessages slot free? we have more than %d chatmessages in parallel?\n", SKYPOPEN_P_LOG, + MAX_CHATMESSAGES); + } else { + DEBUGA_SKYPE("CHATMESSAGE %s is in position %d in the chatmessages array, type=%s, id=%s\n", SKYPOPEN_P_LOG, id, i, + tech_pvt->chatmessages[i].type, tech_pvt->chatmessages[i].id); + sprintf(msg_to_skype, "GET CHATMESSAGE %s CHATNAME", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s FROM_HANDLE", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s FROM_DISPNAME", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s BODY", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + } + + if (!strcasecmp(prop, "CHATNAME")) { + DEBUGA_SKYPE("CHATMESSAGE %s belongs to the CHAT %s\n", SKYPOPEN_P_LOG, id, value); + found = 0; + for (i = 0; i < MAX_CHATMESSAGES; i++) { + if (!strcmp(tech_pvt->chatmessages[i].id, id)) { + strncpy(tech_pvt->chatmessages[i].chatname, value, sizeof(tech_pvt->chatmessages[i].chatname)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why chatmessage %s was not found in the chatmessages array??\n", SKYPOPEN_P_LOG, id); + } + } + if (!strcasecmp(prop, "FROM_HANDLE")) { + DEBUGA_SKYPE("CHATMESSAGE %s was sent by FROM_HANDLE %s\n", SKYPOPEN_P_LOG, id, value); + found = 0; + for (i = 0; i < MAX_CHATMESSAGES; i++) { + if (!strcmp(tech_pvt->chatmessages[i].id, id)) { + strncpy(tech_pvt->chatmessages[i].from_handle, value, sizeof(tech_pvt->chatmessages[i].from_handle)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why chatmessage %s was not found in the chatmessages array??\n", SKYPOPEN_P_LOG, id); + } + + } + if (!strcasecmp(prop, "FROM_DISPNAME")) { + DEBUGA_SKYPE("CHATMESSAGE %s was sent by FROM_DISPNAME %s\n", SKYPOPEN_P_LOG, id, value); + found = 0; + for (i = 0; i < MAX_CHATMESSAGES; i++) { + if (!strcmp(tech_pvt->chatmessages[i].id, id)) { + strncpy(tech_pvt->chatmessages[i].from_dispname, value, sizeof(tech_pvt->chatmessages[i].from_dispname)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why chatmessage %s was not found in the chatmessages array??\n", SKYPOPEN_P_LOG, id); + } + + } + if (!strcasecmp(prop, "BODY")) { + DEBUGA_SKYPE("CHATMESSAGE %s has BODY %s\n", SKYPOPEN_P_LOG, id, value); + found = 0; + for (i = 0; i < MAX_CHATMESSAGES; i++) { + if (!strcmp(tech_pvt->chatmessages[i].id, id)) { + strncpy(tech_pvt->chatmessages[i].body, value, sizeof(tech_pvt->chatmessages[i].body)); + found = 1; + break; + } + } + if (!found) { + DEBUGA_SKYPE("why chatmessage %s was not found in the chatmessages array??\n", SKYPOPEN_P_LOG, id); + } else { + DEBUGA_SKYPE + ("CHATMESSAGE %s is in position %d in the chatmessages array, type=%s, id=%s, chatname=%s, from_handle=%s, from_dispname=%s, body=%s\n", + SKYPOPEN_P_LOG, id, i, tech_pvt->chatmessages[i].type, tech_pvt->chatmessages[i].id, tech_pvt->chatmessages[i].chatname, + tech_pvt->chatmessages[i].from_handle, tech_pvt->chatmessages[i].from_dispname, tech_pvt->chatmessages[i].body); + if (strcmp(tech_pvt->chatmessages[i].from_handle, tech_pvt->skype_user)) { //if the message was not sent by myself + incoming_chatmessage(tech_pvt, i); + } + } + + } + + } + + + if (!strcasecmp(message, "CALL")) { + skypopen_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(prop, where, sizeof(prop) - 1); + where = strsep(stringp, " "); + skypopen_strncpy(value, where, sizeof(value) - 1); + where = strsep(stringp, " "); + + //DEBUGA_SKYPE + //("Skype MSG: message: %s, obj: %s, id: %s, prop: %s, value: %s,where: %s!\n", + //SKYPOPEN_P_LOG, message, obj, id, prop, value, where ? where : "NULL"); + + if (!strcasecmp(prop, "PARTNER_HANDLE")) { + if (tech_pvt->interface_state != SKYPOPEN_STATE_SELECTED && (!strlen(tech_pvt->skype_call_id) || !strlen(tech_pvt->session_uuid_str))) { + /* we are NOT inside an active call */ + DEBUGA_SKYPE("Call %s TRY ANSWER\n", SKYPOPEN_P_LOG, id); + skypopen_answer(tech_pvt, id, value); + } else { + /* we are inside an active call */ + if (!strcasecmp(tech_pvt->skype_call_id, id)) { + /* this is the call in which we are calling out */ + DEBUGA_SKYPE("Call %s DO NOTHING\n", SKYPOPEN_P_LOG, id); + } else { + skypopen_sleep(400000); //0.4 seconds + DEBUGA_SKYPE("Call %s TRY TRANSFER\n", SKYPOPEN_P_LOG, id); + skypopen_transfer(tech_pvt, id, value); + } + } + } + if (!strcasecmp(prop, "PARTNER_DISPNAME")) { + snprintf(tech_pvt->callid_name, sizeof(tech_pvt->callid_name) - 1, "%s%s%s", value, where ? " " : "", where ? where : ""); + } + if (!strcasecmp(prop, "CONF_ID") && !strcasecmp(value, "0")) { + } + if (!strcasecmp(prop, "CONF_ID") && strcasecmp(value, "0")) { + DEBUGA_SKYPE("the skype_call %s is a conference call\n", SKYPOPEN_P_LOG, id); + } + if (!strcasecmp(prop, "DTMF")) { + DEBUGA_SKYPE("Call %s received a DTMF: %s\n", SKYPOPEN_P_LOG, id, value); + dtmf_received(tech_pvt, value); + } + if (!strcasecmp(prop, "FAILUREREASON")) { + DEBUGA_SKYPE("Skype FAILED on skype_call %s. Let's wait for the FAILED message.\n", SKYPOPEN_P_LOG, id); + } + if (!strcasecmp(prop, "DURATION")) { /* each 20 seconds, we zero the buffers and sync the timers */ + if (!((atoi(value) % 20))) { + if(tech_pvt->read_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_srv); + switch_buffer_zero(tech_pvt->read_buffer); + if (tech_pvt->timer_read.timer_interface && tech_pvt->timer_read.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_read); + } + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + } + + if(tech_pvt->write_buffer){ + switch_mutex_lock(tech_pvt->mutex_audio_cli); + switch_buffer_zero(tech_pvt->write_buffer); + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next){ + switch_core_timer_sync(&tech_pvt->timer_write); + } + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + } + DEBUGA_SKYPE("Synching audio on skype_call: %s.\n", SKYPOPEN_P_LOG, id); + } + } + if (!strcasecmp(prop, "DURATION") && (!strcasecmp(value, "1"))) { + if (strcasecmp(id, tech_pvt->skype_call_id)) { + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("We called a Skype contact and he answered us on skype_call: %s.\n", SKYPOPEN_P_LOG, id); + } + } + + if (!strcasecmp(prop, "DURATION") && (tech_pvt->interface_state == SKYPOPEN_STATE_ERROR_DOUBLE_CALL)) { + char msg_to_skype[1024]; + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + ERRORA("We are in a double call situation, trying to get out hanging up call id: %s.\n", SKYPOPEN_P_LOG, id); + sprintf(msg_to_skype, "ALTER CALL %s HANGUP", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(10000); + } + + if (!strcasecmp(prop, "STATUS")) { + + if (!strcasecmp(value, "RINGING")) { + char msg_to_skype[1024]; + if ((tech_pvt->interface_state != SKYPOPEN_STATE_SELECTED && tech_pvt->interface_state != SKYPOPEN_STATE_DIALING) + && (!strlen(tech_pvt->skype_call_id) || !strlen(tech_pvt->session_uuid_str))) { + /* we are NOT inside an active call */ + + DEBUGA_SKYPE("NO ACTIVE calls in this moment, skype_call %s is RINGING, to ask PARTNER_HANDLE\n", SKYPOPEN_P_LOG, id); + sprintf(msg_to_skype, "GET CALL %s PARTNER_HANDLE", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(10000); + } else { + /* we are inside an active call */ + if (!strcasecmp(tech_pvt->skype_call_id, id)) { + /* this is the call in which we are calling out */ + tech_pvt->skype_callflow = CALLFLOW_STATUS_RINGING; + tech_pvt->interface_state = SKYPOPEN_STATE_RINGING; + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("Our remote party in skype_call %s is RINGING\n", SKYPOPEN_P_LOG, id); + remote_party_is_ringing(tech_pvt); + } else { + DEBUGA_SKYPE + ("We are in another call, but skype_call %s is RINGING on us, let's ask PARTNER_HANDLE, so maybe we'll TRANSFER\n", + SKYPOPEN_P_LOG, id); + sprintf(msg_to_skype, "GET CALL %s PARTNER_HANDLE", id); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(10000); + } + } + } else if (!strcasecmp(value, "EARLYMEDIA")) { + char msg_to_skype[1024]; + tech_pvt->skype_callflow = CALLFLOW_STATUS_EARLYMEDIA; + tech_pvt->interface_state = SKYPOPEN_STATE_DIALING; + DEBUGA_SKYPE("Our remote party in skype_call %s is EARLYMEDIA\n", SKYPOPEN_P_LOG, id); + if (tech_pvt->tcp_cli_thread == NULL) { + DEBUGA_SKYPE("START start_audio_threads\n", SKYPOPEN_P_LOG); + if (start_audio_threads(tech_pvt)) { + ERRORA("start_audio_threads FAILED\n", SKYPOPEN_P_LOG); + return CALLFLOW_INCOMING_HANGUP; + } + } + skypopen_sleep(1000); + sprintf(msg_to_skype, "ALTER CALL %s SET_INPUT PORT=\"%d\"", id, tech_pvt->tcp_cli_port); + skypopen_signaling_write(tech_pvt, msg_to_skype); + sprintf(msg_to_skype, "#output ALTER CALL %s SET_OUTPUT PORT=\"%d\"", id, tech_pvt->tcp_srv_port); + skypopen_signaling_write(tech_pvt, msg_to_skype); + + remote_party_is_early_media(tech_pvt); + } else if (!strcasecmp(value, "MISSED")) { + DEBUGA_SKYPE("We missed skype_call %s\n", SKYPOPEN_P_LOG, id); + } else if (!strcasecmp(value, "FINISHED")) { + if (!strcasecmp(tech_pvt->skype_call_id, id)) { + DEBUGA_SKYPE("skype_call %s is MY call, now I'm going DOWN\n", SKYPOPEN_P_LOG, id); + if (tech_pvt->interface_state != SKYPOPEN_STATE_HANGUP_REQUESTED) { + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } + } else { + DEBUGA_SKYPE("skype_call %s is NOT MY call, ignoring\n", SKYPOPEN_P_LOG, id); + } + + } else if (!strcasecmp(value, "CANCELLED")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_CANCELLED; + DEBUGA_SKYPE("we tried to call Skype on skype_call %s and Skype has now CANCELLED\n", SKYPOPEN_P_LOG, id); + tech_pvt->skype_call_id[0] = '\0'; + if (tech_pvt->interface_state != SKYPOPEN_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + } + } else if (!strcasecmp(value, "FAILED")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_FAILED; + DEBUGA_SKYPE("we tried to call Skype on skype_call %s and Skype has now FAILED\n", SKYPOPEN_P_LOG, id); + tech_pvt->skype_call_id[0] = '\0'; + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else if (!strcasecmp(value, "REFUSED")) { + if (!strcasecmp(id, tech_pvt->skype_call_id)) { + /* this is the id of the call we are in, probably we generated it */ + tech_pvt->skype_callflow = CALLFLOW_STATUS_REFUSED; + DEBUGA_SKYPE("we tried to call Skype on skype_call %s and Skype has now REFUSED\n", SKYPOPEN_P_LOG, id); + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + tech_pvt->skype_call_id[0] = '\0'; + return CALLFLOW_INCOMING_HANGUP; + } else { + /* we're here because were us that refused an incoming call */ + DEBUGA_SKYPE("we REFUSED skype_call %s\n", SKYPOPEN_P_LOG, id); + } + } else if (!strcasecmp(value, "TRANSFERRING")) { + DEBUGA_SKYPE("skype_call %s is transferring\n", SKYPOPEN_P_LOG, id); + } else if (!strcasecmp(value, "TRANSFERRED")) { + DEBUGA_SKYPE("skype_call %s has been transferred\n", SKYPOPEN_P_LOG, id); + } else if (!strcasecmp(value, "ROUTING")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_ROUTING; + tech_pvt->interface_state = SKYPOPEN_STATE_DIALING; + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now ROUTING\n", SKYPOPEN_P_LOG, id); + } else if (!strcasecmp(value, "UNPLACED")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_UNPLACED; + tech_pvt->interface_state = SKYPOPEN_STATE_DIALING; + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now UNPLACED\n", SKYPOPEN_P_LOG, id); + } else if (!strcasecmp(value, "INPROGRESS")) { + char msg_to_skype[1024]; + + if (!strlen(tech_pvt->session_uuid_str)) { + DEBUGA_SKYPE("no tech_pvt->session_uuid_str\n", SKYPOPEN_P_LOG); + } + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_REMOTEHOLD) { + if (!strlen(tech_pvt->session_uuid_str) || !strlen(tech_pvt->skype_call_id) + || !strcasecmp(tech_pvt->skype_call_id, id)) { + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now active\n", SKYPOPEN_P_LOG, id); + + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_EARLYMEDIA) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + tech_pvt->interface_state = SKYPOPEN_STATE_UP; + + if (tech_pvt->tcp_cli_thread == NULL) { + DEBUGA_SKYPE("START start_audio_threads\n", SKYPOPEN_P_LOG); + if (start_audio_threads(tech_pvt)) { + ERRORA("start_audio_threads FAILED\n", SKYPOPEN_P_LOG); + return CALLFLOW_INCOMING_HANGUP; + } + } + skypopen_sleep(1000); + sprintf(msg_to_skype, "ALTER CALL %s SET_INPUT PORT=\"%d\"", id, tech_pvt->tcp_cli_port); + skypopen_signaling_write(tech_pvt, msg_to_skype); + skypopen_sleep(1000); + sprintf(msg_to_skype, "#output ALTER CALL %s SET_OUTPUT PORT=\"%d\"", id, tech_pvt->tcp_srv_port); + skypopen_signaling_write(tech_pvt, msg_to_skype); + } + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + if (!strlen(tech_pvt->session_uuid_str)) { + DEBUGA_SKYPE("New Inbound Channel!\n\n\n\n", SKYPOPEN_P_LOG); + new_inbound_channel(tech_pvt); + } else { + tech_pvt->interface_state = SKYPOPEN_STATE_UP; + DEBUGA_SKYPE("Outbound Channel Answered! session_uuid_str=%s\n", SKYPOPEN_P_LOG, tech_pvt->session_uuid_str); + outbound_channel_answered(tech_pvt); + } + } else { + DEBUGA_SKYPE("I'm on %s, skype_call %s is NOT MY call, ignoring\n", SKYPOPEN_P_LOG, tech_pvt->skype_call_id, id); + } + } else { + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + DEBUGA_SKYPE("Back from REMOTEHOLD!\n", SKYPOPEN_P_LOG); + } + + } else if (!strcasecmp(value, "REMOTEHOLD")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_REMOTEHOLD; + DEBUGA_SKYPE("skype_call: %s is now REMOTEHOLD\n", SKYPOPEN_P_LOG, id); + + } else if (!strcasecmp(value, "BUSY")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_FAILED; + DEBUGA_SKYPE + ("we tried to call Skype on skype_call %s and remote party (destination) was BUSY. Our outbound call has failed\n", + SKYPOPEN_P_LOG, id); + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + tech_pvt->skype_call_id[0] = '\0'; + skypopen_sleep(1000); + return CALLFLOW_INCOMING_HANGUP; + } else if (!strcasecmp(value, "WAITING_REDIAL_COMMAND")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_FAILED; + DEBUGA_SKYPE + ("we tried to call Skype on skype_call %s and remote party (destination) has rejected us (WAITING_REDIAL_COMMAND). Our outbound call has failed\n", + SKYPOPEN_P_LOG, id); + skypopen_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPOPEN_STATE_DOWN; + tech_pvt->skype_call_id[0] = '\0'; + skypopen_sleep(1000); + return CALLFLOW_INCOMING_HANGUP; + } else { + WARNINGA("skype_call: %s, STATUS: %s is not recognized\n", SKYPOPEN_P_LOG, id, value); + } + } //STATUS + } //CALL + /* the "numbered" messages that follows are used by the directory application, not yet ported */ + if (!strcasecmp(message, "#333")) { + memset(tech_pvt->skype_friends, 0, 4096); + skypopen_strncpy(tech_pvt->skype_friends, &message_2[11], 4095); + } + if (!strcasecmp(message, "#222")) { + memset(tech_pvt->skype_fullname, 0, 512); + skypopen_strncpy(tech_pvt->skype_fullname, &message_2[10], 511); + } + if (!strcasecmp(message, "#765")) { + memset(tech_pvt->skype_displayname, 0, 512); + skypopen_strncpy(tech_pvt->skype_displayname, &message_2[10], 511); + } + a = 0; + } //message end + } //read_from_pipe + return 0; +} + +void *skypopen_do_tcp_srv_thread_func(void *obj) +{ + private_t *tech_pvt = obj; + int s; + unsigned int len; +#if defined(WIN32) && !defined(__CYGWIN__) + int sin_size; + int size = sizeof(int); +#else /* WIN32 */ + unsigned int sin_size; + unsigned int size = sizeof(int); +#endif /* WIN32 */ + unsigned int fd; + short srv_in[SAMPLES_PER_FRAME * 10]; + struct sockaddr_in remote_addr; + int sockbufsize = 0; + + s = skypopen_socket_create_and_bind(tech_pvt, &tech_pvt->tcp_srv_port); + if (s < 0) { + ERRORA("skypopen_socket_create_and_bind error!\n", SKYPOPEN_P_LOG); + return NULL; + } + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPOPEN_P_LOG); + + listen(s, 6); + + sin_size = sizeof(remote_addr); + + while (tech_pvt && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA + || tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) { + + unsigned int fdselectgio; + int rtgio; + fd_set fsgio; + struct timeval togio; + + if (!(running && tech_pvt->running)) + break; + FD_ZERO(&fsgio); + togio.tv_usec = 20000; //20msec + togio.tv_sec = 0; + fdselectgio = s; + FD_SET(fdselectgio, &fsgio); + + rtgio = select(fdselectgio + 1, &fsgio, NULL, NULL, &togio); + + if (rtgio) { + + while (s > 0 && (fd = accept(s, (struct sockaddr *) &remote_addr, &sin_size)) > 0) { + DEBUGA_SKYPE("ACCEPTED here I send you %d\n", SKYPOPEN_P_LOG, tech_pvt->tcp_srv_port); + + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("3 SO_RCVBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("3 SO_SNDBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + + + if (!(running && tech_pvt->running)) + break; + while (tech_pvt && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA + || tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) { + + unsigned int fdselect; + int rt; + fd_set fs; + struct timeval to; + int nospace; + + if (!(running && tech_pvt->running)) + break; + fdselect = fd; + FD_ZERO(&fs); + FD_SET(fdselect, &fs); + to.tv_usec = 60000; //60 msec + to.tv_sec = 0; + + rt = select(fdselect + 1, &fs, NULL, NULL, &to); + if (rt > 0) { + + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_REMOTEHOLD) { + len = recv(fd, (char *) srv_in, 640, 0); + } else { + skypopen_sleep(10000); + continue; + } + if (tech_pvt->begin_to_read == 0) { + DEBUGA_SKYPE("len=%d\n", SKYPOPEN_P_LOG, len); + skypopen_sleep(10000); + continue; + } + + if (len == -1) { + DEBUGA_SKYPE("len=%d, error: %s\n", SKYPOPEN_P_LOG, len, strerror(errno)); + break; + } + nospace = 0; + if (len > 0) { + switch_mutex_lock(tech_pvt->mutex_audio_srv); + if (switch_buffer_freespace(tech_pvt->read_buffer) < len) { + switch_buffer_zero(tech_pvt->read_buffer); + nospace = 1; + //switch_buffer_toss(tech_pvt->read_buffer, len); + } + switch_buffer_write(tech_pvt->read_buffer, srv_in, len); + switch_mutex_unlock(tech_pvt->mutex_audio_srv); + if (nospace) { + DEBUGA_SKYPE("NO SPACE READ: there was no space for: %d\n", SKYPOPEN_P_LOG, len); + } + } else if (len == 0) { + DEBUGA_SKYPE("CLOSED\n", SKYPOPEN_P_LOG); + break; + } else { + DEBUGA_SKYPE("len=%d\n", SKYPOPEN_P_LOG, len); + } + + } else if (rt == 0) { + continue; + } else { + DEBUGA_SKYPE("SRV rt=%d\n", SKYPOPEN_P_LOG, rt); + break; + } + + } + + DEBUGA_SKYPE("Skype incoming audio GONE\n", SKYPOPEN_P_LOG); + tech_pvt->skype_callflow = CALLFLOW_INCOMING_HANGUP; + skypopen_close_socket(fd); + break; + } + break; + } + } + + DEBUGA_SKYPE("incoming audio (read) server (I am it) EXITING\n", SKYPOPEN_P_LOG); + skypopen_close_socket(s); + s = -1; + tech_pvt->tcp_srv_thread = NULL; + return NULL; +} + +void *skypopen_do_tcp_cli_thread_func(void *obj) +{ + private_t *tech_pvt = obj; + int s; + struct sockaddr_in remote_addr; + unsigned int len; + unsigned int fd; + short cli_out[SAMPLES_PER_FRAME * 2 * 10]; +#ifdef WIN32 + int sin_size; + int size = sizeof(int); +#else + unsigned int sin_size; + unsigned int size = sizeof(int); +#endif /* WIN32 */ + int sockbufsize = 0; + + s = skypopen_socket_create_and_bind(tech_pvt, &tech_pvt->tcp_cli_port); + if (s < 0) { + ERRORA("skypopen_socket_create_and_bind error!\n", SKYPOPEN_P_LOG); + return NULL; + } + + + + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPOPEN_P_LOG); + + listen(s, 6); + + sin_size = sizeof(remote_addr); + + while (tech_pvt && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA + || tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) { + + unsigned int fdselectgio; + int rtgio; + fd_set fsgio; + struct timeval togio; + + if (!(running && tech_pvt->running)) + break; + FD_ZERO(&fsgio); + togio.tv_usec = 60000; //20msec + togio.tv_sec = 0; + fdselectgio = s; + FD_SET(fdselectgio, &fsgio); + + rtgio = select(fdselectgio + 1, &fsgio, NULL, NULL, &togio); + + if (rtgio) { + + while (s > 0 && (fd = accept(s, (struct sockaddr *) &remote_addr, &sin_size)) > 0) { + DEBUGA_SKYPE("ACCEPTED here you send me %d\n", SKYPOPEN_P_LOG, tech_pvt->tcp_cli_port); + + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("4 SO_RCVBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + sockbufsize = 0; + size = sizeof(int); + getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &sockbufsize, &size); + DEBUGA_SKYPE("4 SO_SNDBUF is %d, size is %d\n", SKYPOPEN_P_LOG, sockbufsize, size); + + + + if (!(running && tech_pvt->running)) + break; + while (tech_pvt && tech_pvt->interface_state != SKYPOPEN_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == CALLFLOW_STATUS_EARLYMEDIA + || tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD || tech_pvt->skype_callflow == SKYPOPEN_STATE_UP)) { + size_t bytes_to_write; + + if (!(running && tech_pvt->running)) + break; + + if (tech_pvt->timer_write.timer_interface && tech_pvt->timer_write.timer_interface->timer_next + && tech_pvt->interface_state != SKYPOPEN_STATE_HANGUP_REQUESTED) { + switch_core_timer_next(&tech_pvt->timer_write); + } + + if (tech_pvt->begin_to_write == 0) { + memset(cli_out, 255, sizeof(cli_out)); + bytes_to_write = 640; + len = send(fd, (char *) cli_out, bytes_to_write, 0); + if (len == -1) { + DEBUGA_SKYPE("len=%d, error: %s\n", SKYPOPEN_P_LOG, len, strerror(errno)); + break; + } + skypopen_sleep(10000); + continue; + } else { + + bytes_to_write = 0; + + if (tech_pvt->skype_callflow == CALLFLOW_INCOMING_HANGUP) { + break; + } + switch_mutex_lock(tech_pvt->mutex_audio_cli); + if (switch_buffer_inuse(tech_pvt->write_buffer)) { + bytes_to_write = switch_buffer_read(tech_pvt->write_buffer, cli_out, 640); + } + switch_mutex_unlock(tech_pvt->mutex_audio_cli); + + if (!bytes_to_write) { + if(tech_pvt->write_silence_when_idle){ + memset(cli_out, 255, sizeof(cli_out)); + bytes_to_write = 640; + //DEBUGA_SKYPE("WRITE Silence!\n", SKYPOPEN_P_LOG); + }else{ + continue; + } + } + /* send the 16khz frame to the Skype client waiting for incoming audio to be sent to the remote party */ + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_REMOTEHOLD) { + len = send(fd, (char *) cli_out, bytes_to_write, 0); + if (len == -1) { + DEBUGA_SKYPE("len=%d, error: %s\n", SKYPOPEN_P_LOG, len, strerror(errno)); + break; + } + if (len != bytes_to_write) { + DEBUGA_SKYPE("len=%d\n", SKYPOPEN_P_LOG, len); + } + } + } + + } + DEBUGA_SKYPE("Skype outbound audio GONE\n", SKYPOPEN_P_LOG); + tech_pvt->skype_callflow = CALLFLOW_INCOMING_HANGUP; + skypopen_close_socket(fd); + break; + } + break; + } + } + + DEBUGA_SKYPE("outbound audio server (I am it) EXITING\n", SKYPOPEN_P_LOG); + skypopen_close_socket(s); + s = -1; + tech_pvt->tcp_cli_thread = NULL; + return NULL; +} + +int skypopen_senddigit(private_t * tech_pvt, char digit) +{ + char msg_to_skype[1024]; + + DEBUGA_SKYPE("DIGIT received: %c\n", SKYPOPEN_P_LOG, digit); + sprintf(msg_to_skype, "SET CALL %s DTMF %c", tech_pvt->skype_call_id, digit); + skypopen_signaling_write(tech_pvt, msg_to_skype); + + return 0; +} + +int skypopen_call(private_t * tech_pvt, char *rdest, int timeout) +{ + char msg_to_skype[1024]; + + DEBUGA_SKYPE("Calling Skype, rdest is: %s\n", SKYPOPEN_P_LOG, rdest); + + sprintf(msg_to_skype, "CALL %s", rdest); + if (skypopen_signaling_write(tech_pvt, msg_to_skype) < 0) { + ERRORA("failed to communicate with Skype client, now exit\n", SKYPOPEN_P_LOG); + return -1; + } + return 0; +} + +/***************************/ +/* PLATFORM SPECIFIC */ +/***************************/ +#if defined(WIN32) && !defined(__CYGWIN__) +int skypopen_pipe_read(switch_file_t * pipe, short *buf, int howmany) +{ + switch_size_t quantity; + + quantity = howmany; + + switch_file_read(pipe, buf, &quantity); + + howmany = quantity; + + return howmany; +} + +int skypopen_pipe_write(switch_file_t * pipe, short *buf, int howmany) +{ + switch_size_t quantity; + + quantity = howmany; + + switch_file_write(pipe, buf, &quantity); + + howmany = quantity; + + return howmany; +} + +int skypopen_close_socket(unsigned int fd) +{ + int res; + + res = closesocket(fd); + + return res; +} + +int skypopen_audio_init(private_t * tech_pvt) +{ + switch_status_t rv; + rv = switch_file_pipe_create(&tech_pvt->audiopipe_srv[0], &tech_pvt->audiopipe_srv[1], skypopen_module_pool); + rv = switch_file_pipe_create(&tech_pvt->audiopipe_cli[0], &tech_pvt->audiopipe_cli[1], skypopen_module_pool); + return 0; +} +#else /* WIN32 */ +int skypopen_pipe_read(int pipe, short *buf, int howmany) +{ + howmany = read(pipe, buf, howmany); + return howmany; +} + +int skypopen_pipe_write(int pipe, short *buf, int howmany) +{ + if (buf) { + howmany = write(pipe, buf, howmany); + return howmany; + } else { + return 0; + } +} + +int skypopen_close_socket(unsigned int fd) +{ + int res; + + res = close(fd); + + return res; +} + +int skypopen_audio_init(private_t * tech_pvt) +{ + if (pipe(tech_pvt->audiopipe_srv)) { + fcntl(tech_pvt->audiopipe_srv[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->audiopipe_srv[1], F_SETFL, O_NONBLOCK); + } + if (pipe(tech_pvt->audiopipe_cli)) { + fcntl(tech_pvt->audiopipe_cli[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->audiopipe_cli[1], F_SETFL, O_NONBLOCK); + } + +/* this pipe is the audio fd for asterisk to poll on during a call. FS do not use it */ + tech_pvt->skypopen_sound_capt_fd = tech_pvt->audiopipe_srv[0]; + + return 0; +} +#endif /* WIN32 */ + +#ifdef WIN32 + +enum { + SKYPECONTROLAPI_ATTACH_SUCCESS = 0, /* Client is successfully + attached and API window handle can be found + in wParam parameter */ + SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION = 1, /* Skype has acknowledged + connection request and is waiting + for confirmation from the user. */ + /* The client is not yet attached + * and should wait for SKYPECONTROLAPI_ATTACH_SUCCESS message */ + SKYPECONTROLAPI_ATTACH_REFUSED = 2, /* User has explicitly + denied access to client */ + SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE = 3, /* API is not available + at the moment. + For example, this happens when no user + is currently logged in. */ + /* Client should wait for + * SKYPECONTROLAPI_ATTACH_API_AVAILABLE + * broadcast before making any further */ + /* connection attempts. */ + SKYPECONTROLAPI_ATTACH_API_AVAILABLE = 0x8001 +}; + +/* Visual C do not have strsep? */ +char + *strsep(char **stringp, const char *delim) +{ + char *res; + + if (!stringp || !*stringp || !**stringp) + return (char *) 0; + + res = *stringp; + while (**stringp && !strchr(delim, **stringp)) + ++(*stringp); + + if (**stringp) { + **stringp = '\0'; + ++(*stringp); + } + + return res; +} + +int skypopen_signaling_write(private_t * tech_pvt, char *msg_to_skype) +{ + static char acInputRow[1024]; + COPYDATASTRUCT oCopyData; + + DEBUGA_SKYPE("SENDING: |||%s||||\n", SKYPOPEN_P_LOG, msg_to_skype); + + sprintf(acInputRow, "%s", msg_to_skype); + /* send command to skype */ + oCopyData.dwData = 0; + oCopyData.lpData = acInputRow; + oCopyData.cbData = strlen(acInputRow) + 1; + if (oCopyData.cbData != 1) { + if (SendMessage + (tech_pvt->SkypopenHandles.win32_hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, + (WPARAM) tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle, (LPARAM) & oCopyData) == FALSE) { + ERRORA("Sending message failed - probably Skype crashed.\n\nPlease shutdown Skypopen, then launch Skypopen and try again.\n", SKYPOPEN_P_LOG); + return -1; + } + } + + return 0; + +} + +LRESULT APIENTRY skypopen_present(HWND hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam) +{ + LRESULT lReturnCode; + int fIssueDefProc; + private_t *tech_pvt = NULL; + + lReturnCode = 0; + fIssueDefProc = 0; + tech_pvt = (private_t *) GetWindowLong(hWindow, GWL_USERDATA); + + if (!running) { + DEBUGA_SKYPE("let's DIE!\n", SKYPOPEN_P_LOG); + tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle = NULL; + PostQuitMessage(0); + return lReturnCode; + } + switch (uiMessage) { + case WM_CREATE: + tech_pvt = (private_t *) ((LPCREATESTRUCT) ulParam)->lpCreateParams; + SetWindowLong(hWindow, GWL_USERDATA, (LONG) tech_pvt); + DEBUGA_SKYPE("got CREATE\n", SKYPOPEN_P_LOG); + break; + case WM_DESTROY: + DEBUGA_SKYPE("got DESTROY\n", SKYPOPEN_P_LOG); + tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle = NULL; + PostQuitMessage(0); + break; + case WM_COPYDATA: + if (tech_pvt->SkypopenHandles.win32_hGlobal_SkypeAPIWindowHandle == (HWND) uiParam) { + unsigned int howmany; + char msg_from_skype[2048]; + + PCOPYDATASTRUCT poCopyData = (PCOPYDATASTRUCT) ulParam; + + memset(msg_from_skype, '\0', sizeof(msg_from_skype)); + skypopen_strncpy(msg_from_skype, (const char *) poCopyData->lpData, sizeof(msg_from_skype) - 2); + + howmany = strlen(msg_from_skype) + 1; + howmany = skypopen_pipe_write(tech_pvt->SkypopenHandles.fdesc[1], (short *) msg_from_skype, howmany); + lReturnCode = 1; + } + break; + default: + if (tech_pvt && tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach) { + if (uiMessage == tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach) { + switch (ulParam) { + case SKYPECONTROLAPI_ATTACH_SUCCESS: + if (!tech_pvt->SkypopenHandles.currentuserhandle) { + //DEBUGA_SKYPE("\n\n\tConnected to Skype API!\n", SKYPOPEN_P_LOG); + tech_pvt->SkypopenHandles.api_connected = 1; + tech_pvt->SkypopenHandles.win32_hGlobal_SkypeAPIWindowHandle = (HWND) uiParam; + tech_pvt->SkypopenHandles.win32_hGlobal_SkypeAPIWindowHandle = tech_pvt->SkypopenHandles.win32_hGlobal_SkypeAPIWindowHandle; + } + break; + case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION: + skypopen_sleep(5000); + break; + case SKYPECONTROLAPI_ATTACH_REFUSED: + ERRORA("Skype client refused to be connected by Skypopen!\n", SKYPOPEN_P_LOG); + break; + case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE: + ERRORA("Skype API not (yet?) available\n", SKYPOPEN_P_LOG); + break; + case SKYPECONTROLAPI_ATTACH_API_AVAILABLE: + DEBUGA_SKYPE("Skype API available\n", SKYPOPEN_P_LOG); + skypopen_sleep(5000); + break; + default: + WARNINGA("GOT AN UNKNOWN SKYPE WINDOWS MSG\n", SKYPOPEN_P_LOG); + } + lReturnCode = 1; + break; + } + } + fIssueDefProc = 1; + break; + } + if (fIssueDefProc) + lReturnCode = DefWindowProc(hWindow, uiMessage, uiParam, ulParam); + return (lReturnCode); +} + +int win32_Initialize_CreateWindowClass(private_t * tech_pvt) +{ + unsigned char *paucUUIDString; + RPC_STATUS lUUIDResult; + int fReturnStatus; + UUID oUUID; + + fReturnStatus = 0; + lUUIDResult = UuidCreate(&oUUID); + tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle = (HINSTANCE) OpenProcess(PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId()); + if (tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle != NULL && (lUUIDResult == RPC_S_OK || lUUIDResult == RPC_S_UUID_LOCAL_ONLY)) { + if (UuidToString(&oUUID, &paucUUIDString) == RPC_S_OK) { + WNDCLASS oWindowClass; + + strcpy(tech_pvt->SkypopenHandles.win32_acInit_WindowClassName, "Skype-API-Skypopen-"); + strcat(tech_pvt->SkypopenHandles.win32_acInit_WindowClassName, (char *) paucUUIDString); + + oWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + oWindowClass.lpfnWndProc = (WNDPROC) & skypopen_present; + oWindowClass.cbClsExtra = 0; + oWindowClass.cbWndExtra = 0; + oWindowClass.hInstance = tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle; + oWindowClass.hIcon = NULL; + oWindowClass.hCursor = NULL; + oWindowClass.hbrBackground = NULL; + oWindowClass.lpszMenuName = NULL; + oWindowClass.lpszClassName = tech_pvt->SkypopenHandles.win32_acInit_WindowClassName; + + if (RegisterClass(&oWindowClass) != 0) + fReturnStatus = 1; + + RpcStringFree(&paucUUIDString); + } + } + if (fReturnStatus == 0) + CloseHandle(tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle); + tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle = NULL; + return (fReturnStatus); +} + +void win32_DeInitialize_DestroyWindowClass(private_t * tech_pvt) +{ + UnregisterClass(tech_pvt->SkypopenHandles.win32_acInit_WindowClassName, tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle); + CloseHandle(tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle); + tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle = NULL; +} + +int win32_Initialize_CreateMainWindow(private_t * tech_pvt) +{ + tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle = + CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, + tech_pvt->SkypopenHandles.win32_acInit_WindowClassName, "", + WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, + 128, 128, NULL, 0, tech_pvt->SkypopenHandles.win32_hInit_ProcessHandle, tech_pvt); + return (tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle != NULL ? 1 : 0); +} + +void win32_DeInitialize_DestroyMainWindow(private_t * tech_pvt) +{ + if (tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle != NULL) + DestroyWindow(tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle), tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle = NULL; +} + +void *skypopen_do_skypeapi_thread_func(void *obj) +{ + private_t *tech_pvt = obj; +#if defined(WIN32) && !defined(__CYGWIN__) + switch_status_t rv; + + switch_file_pipe_create(&tech_pvt->SkypopenHandles.fdesc[0], &tech_pvt->SkypopenHandles.fdesc[1], skypopen_module_pool); + rv = switch_file_pipe_create(&tech_pvt->SkypopenHandles.fdesc[0], &tech_pvt->SkypopenHandles.fdesc[1], skypopen_module_pool); +#else /* WIN32 */ + if (pipe(tech_pvt->SkypopenHandles.fdesc)) { + fcntl(tech_pvt->SkypopenHandles.fdesc[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->SkypopenHandles.fdesc[1], F_SETFL, O_NONBLOCK); + } +#endif /* WIN32 */ + + tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach"); + tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover"); + + skypopen_sleep(200000); //0,2 sec + + if (tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach != 0 + && tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover != 0) { + if (win32_Initialize_CreateWindowClass(tech_pvt)) { + if (win32_Initialize_CreateMainWindow(tech_pvt)) { + if (SendMessage + (HWND_BROADCAST, + tech_pvt->SkypopenHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover, + (WPARAM) tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle, 0) != 0) { + tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle = tech_pvt->SkypopenHandles.win32_hInit_MainWindowHandle; + while (running && tech_pvt->running) { + MSG oMessage; + if (!(running && tech_pvt->running)) + break; + while (GetMessage(&oMessage, 0, 0, 0)) { + TranslateMessage(&oMessage); + DispatchMessage(&oMessage); + } + } + } + win32_DeInitialize_DestroyMainWindow(tech_pvt); + } + win32_DeInitialize_DestroyWindowClass(tech_pvt); + } + } + DEBUGA_SKYPE("EXITING\n", SKYPOPEN_P_LOG); + return NULL; +} + +#else /* NOT WIN32 */ +int X11_errors_handler(Display * dpy, XErrorEvent * err) +{ + private_t *tech_pvt = NULL; + (void) dpy; + + xerror = err->error_code; + ERRORA("Received error code %d from X Server\n\n", SKYPOPEN_P_LOG, xerror); + return 0; /* ignore the error */ +} + +static void X11_errors_trap(void) +{ + xerror = 0; + old_handler = XSetErrorHandler(X11_errors_handler); +} + +static int X11_errors_untrap(void) +{ + XSetErrorHandler(old_handler); + return (xerror != BadValue) && (xerror != BadWindow); +} + +int skypopen_send_message(private_t * tech_pvt, const char *message_P) +{ + struct SkypopenHandles *SkypopenHandles = &tech_pvt->SkypopenHandles; + Window w_P = SkypopenHandles->skype_win; + Display *disp = SkypopenHandles->disp; + Window handle_P = SkypopenHandles->win; + int ok; + + + Atom atom1 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False); + Atom atom2 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE", False); + unsigned int pos = 0; + unsigned int len = strlen(message_P); + XEvent e; + + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom1; /* leading message */ + e.xclient.display = disp; + e.xclient.window = handle_P; + e.xclient.format = 8; + + X11_errors_trap(); + do { + unsigned int i; + for (i = 0; i < 20 && i + pos <= len; ++i) + e.xclient.data.b[i] = message_P[i + pos]; + XSendEvent(disp, w_P, False, 0, &e); + + e.xclient.message_type = atom2; /* following messages */ + pos += i; + } while (pos <= len); + + XSync(disp, False); + ok = X11_errors_untrap(); + + if (!ok) { + ERRORA("Sending message failed with status %d\n", SKYPOPEN_P_LOG, xerror); + tech_pvt->running = 0; + return 0; + } + + return 1; +} + +int skypopen_signaling_write(private_t * tech_pvt, char *msg_to_skype) +{ + + DEBUGA_SKYPE("SENDING: |||%s||||\n", SKYPOPEN_P_LOG, msg_to_skype); + + + if (!skypopen_send_message(tech_pvt, msg_to_skype)) { + ERRORA + ("Sending message failed - probably Skype crashed.\n\nPlease shutdown Skypopen, then restart Skype, then launch Skypopen and try again.\n", + SKYPOPEN_P_LOG); + return -1; + } + + return 0; + +} + +int skypopen_present(struct SkypopenHandles *SkypopenHandles) +{ + Atom skype_inst = XInternAtom(SkypopenHandles->disp, "_SKYPE_INSTANCE", True); + + Atom type_ret; + int format_ret; + unsigned long nitems_ret; + unsigned long bytes_after_ret; + unsigned char *prop; + int status; + private_t *tech_pvt = NULL; + + X11_errors_trap(); + status = + XGetWindowProperty(SkypopenHandles->disp, DefaultRootWindow(SkypopenHandles->disp), + skype_inst, 0, 1, False, XA_WINDOW, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &prop); + X11_errors_untrap(); + + /* sanity check */ + if (status != Success || format_ret != 32 || nitems_ret != 1) { + SkypopenHandles->skype_win = (Window) - 1; + DEBUGA_SKYPE("Skype instance not found\n", SKYPOPEN_P_LOG); + running = 0; + SkypopenHandles->api_connected = 0; + return 0; + } + + SkypopenHandles->skype_win = *(const unsigned long *) prop & 0xffffffff; + DEBUGA_SKYPE("Skype instance found with id #%d\n", SKYPOPEN_P_LOG, (unsigned int) SkypopenHandles->skype_win); + SkypopenHandles->api_connected = 1; + return 1; +} + +void skypopen_clean_disp(void *data) +{ + + int *dispptr; + int disp; + private_t *tech_pvt = NULL; + + dispptr = data; + disp = *dispptr; + + if (disp) { + DEBUGA_SKYPE("to be destroyed disp %d\n", SKYPOPEN_P_LOG, disp); + close(disp); + DEBUGA_SKYPE("destroyed disp\n", SKYPOPEN_P_LOG); + } else { + DEBUGA_SKYPE("NOT destroyed disp\n", SKYPOPEN_P_LOG); + } + DEBUGA_SKYPE("OUT destroyed disp\n", SKYPOPEN_P_LOG); + skypopen_sleep(1000); +} + +void *skypopen_do_skypeapi_thread_func(void *obj) +{ + + private_t *tech_pvt = obj; + struct SkypopenHandles *SkypopenHandles; + char buf[512]; + Display *disp = NULL; + Window root = -1; + Window win = -1; + int xfd; + + if (!strlen(tech_pvt->X11_display)) + strcpy(tech_pvt->X11_display, getenv("DISPLAY")); + + if (!tech_pvt->tcp_srv_port) + tech_pvt->tcp_srv_port = 10160; + + if (!tech_pvt->tcp_cli_port) + tech_pvt->tcp_cli_port = 10161; + + if (pipe(tech_pvt->SkypopenHandles.fdesc)) { + fcntl(tech_pvt->SkypopenHandles.fdesc[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->SkypopenHandles.fdesc[1], F_SETFL, O_NONBLOCK); + } + SkypopenHandles = &tech_pvt->SkypopenHandles; + disp = XOpenDisplay(tech_pvt->X11_display); + if (!disp) { + ERRORA("Cannot open X Display '%s', exiting skype thread\n", SKYPOPEN_P_LOG, tech_pvt->X11_display); + running = 0; + return NULL; + } else { + DEBUGA_SKYPE("X Display '%s' opened\n", SKYPOPEN_P_LOG, tech_pvt->X11_display); + } + + xfd = XConnectionNumber(disp); + fcntl(xfd, F_SETFD, FD_CLOEXEC); + + SkypopenHandles->disp = disp; + + if (skypopen_present(SkypopenHandles)) { + root = DefaultRootWindow(disp); + win = XCreateSimpleWindow(disp, root, 0, 0, 1, 1, 0, BlackPixel(disp, DefaultScreen(disp)), BlackPixel(disp, DefaultScreen(disp))); + + SkypopenHandles->win = win; + + snprintf(buf, 512, "NAME skypopen"); + + if (!skypopen_send_message(tech_pvt, buf)) { + ERRORA("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypopen again\n", SKYPOPEN_P_LOG); + running = 0; + return NULL; + } + + snprintf(buf, 512, "PROTOCOL 7"); + if (!skypopen_send_message(tech_pvt, buf)) { + ERRORA("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypopen again\n", SKYPOPEN_P_LOG); + running = 0; + return NULL; + } + + { + /* perform an events loop */ + XEvent an_event; + char buf[21]; /* can't be longer */ + char buffer[17000]; + char continuebuffer[17000]; + char *b; + int i; + int continue_is_broken = 0; + int there_were_continues = 0; + Atom atom_begin = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False); + Atom atom_continue = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE", False); + + memset(buffer, '\0', 17000); + memset(continuebuffer, '\0', 17000); + b = buffer; + + while (running && tech_pvt->running) { + XNextEvent(disp, &an_event); + if (!(running && tech_pvt->running)) + break; + switch (an_event.type) { + case ClientMessage: + + if (an_event.xclient.format != 8) { + skypopen_sleep(1000); //0.1 msec + break; + } + + for (i = 0; i < 20 && an_event.xclient.data.b[i] != '\0'; ++i) + buf[i] = an_event.xclient.data.b[i]; + + buf[i] = '\0'; + + if (an_event.xclient.message_type == atom_begin) { + if (strlen(buffer)) { + unsigned int howmany; + howmany = strlen(b) + 1; + howmany = write(SkypopenHandles->fdesc[1], b, howmany); + WARNINGA + ("A begin atom while the previous message is not closed???? value of previous message (between vertical bars) is=|||%s|||, will be lost\n", + SKYPOPEN_P_LOG, buffer); + memset(buffer, '\0', 17000); + } + if (continue_is_broken) { + continue_is_broken = 0; + there_were_continues = 1; + } + } + if (an_event.xclient.message_type == atom_continue) { + if (!strlen(buffer)) { + DEBUGA_SKYPE + ("Got a 'continue' XAtom without a previous 'begin'. It's value (between vertical bars) is=|||%s|||, let's store it and hope next 'begin' will be the good one\n", + SKYPOPEN_P_LOG, buf); + strcat(continuebuffer, buf); + continue_is_broken = 1; + if (!strncmp(buf, "ognised identity", 15)) { + WARNINGA + ("Got a 'continue' XAtom without a previous 'begin'. It's value (between vertical bars) is=|||%s|||. Let's introduce a 1 second delay.\n", + SKYPOPEN_P_LOG, buf); + skypopen_sleep(1000000); //1 sec + } + skypopen_sleep(1000); //0.1 msec + break; + } + } + if (continue_is_broken) { + XFlush(disp); + skypopen_sleep(1000); //0.1 msec + continue; + } + strcat(buffer, buf); + strcat(buffer, continuebuffer); + memset(continuebuffer, '\0', 17000); + + if (i < 20 || there_were_continues) { /* last fragment */ + unsigned int howmany; + + howmany = strlen(b) + 1; + howmany = write(SkypopenHandles->fdesc[1], b, howmany); + memset(buffer, '\0', 17000); + XFlush(disp); + there_were_continues = 0; + } + + skypopen_sleep(1000); //0.1 msec + break; + default: + skypopen_sleep(1000); //0.1 msec + break; + } + } + } + } else { + ERRORA("Skype is not running, maybe crashed. Please run/restart Skype and relaunch Skypopen\n", SKYPOPEN_P_LOG); + running = 0; + return NULL; + } + return NULL; + +} +#endif // WIN32