From f4a09d2c4eedb0af30a22077fca44f77c02ffa95 Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli Date: Sat, 13 Feb 2010 11:10:30 +0000 Subject: [PATCH] skypiax: nothing to see here git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@16632 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/endpoints/mod_skypiax/103/dummy.c | 964 ++++++ .../endpoints/mod_skypiax/103/mod_skypiax.c | 2579 +++++++++++++++++ src/mod/endpoints/mod_skypiax/103/skypiax.h | 332 +++ .../mod_skypiax/103/skypiax_protocol.c | 1926 ++++++++++++ 4 files changed, 5801 insertions(+) create mode 100644 src/mod/endpoints/mod_skypiax/103/dummy.c create mode 100644 src/mod/endpoints/mod_skypiax/103/mod_skypiax.c create mode 100644 src/mod/endpoints/mod_skypiax/103/skypiax.h create mode 100644 src/mod/endpoints/mod_skypiax/103/skypiax_protocol.c diff --git a/src/mod/endpoints/mod_skypiax/103/dummy.c b/src/mod/endpoints/mod_skypiax/103/dummy.c new file mode 100644 index 0000000000..39934ecea4 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/103/dummy.c @@ -0,0 +1,964 @@ +/* + * Dummy soundcard + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include //giova +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); + +#define GIOVAMULTI 1 +#define MAX_PCM_DEVICES 4 +#define MAX_PCM_SUBSTREAMS 128 +#define MAX_MIDI_DEVICES 2 + +#if 0 /* emu10k1 emulation */ +#define MAX_BUFFER_SIZE (128 * 1024) +static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime) +{ + int err; + err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX); + if (err < 0) + return err; + return 0; +} + +#define add_playback_constraints emu10k1_playback_constraints +#endif + +#if 0 /* RME9652 emulation */ +#define MAX_BUFFER_SIZE (26 * 64 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 26 +#define USE_CHANNELS_MAX 26 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 2 +#endif + +#if 0 /* ICE1712 emulation */ +#define MAX_BUFFER_SIZE (256 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 10 +#define USE_CHANNELS_MAX 10 +#define USE_PERIODS_MIN 1 +#define USE_PERIODS_MAX 1024 +#endif + +#if 0 /* UDA1341 emulation */ +#define MAX_BUFFER_SIZE (16380) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define USE_CHANNELS_MIN 2 +#define USE_CHANNELS_MAX 2 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 255 +#endif + +#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */ +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define USE_CHANNELS_MIN 2 +#define USE_CHANNELS_MAX 2 +#define USE_RATE SNDRV_PCM_RATE_48000 +#define USE_RATE_MIN 48000 +#define USE_RATE_MAX 48000 +#endif + +#if 0 /* CA0106 */ +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define USE_CHANNELS_MIN 2 +#define USE_CHANNELS_MAX 2 +#define USE_RATE (SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000) +#define USE_RATE_MIN 48000 +#define USE_RATE_MAX 192000 +#define MAX_BUFFER_SIZE ((65536-64)*8) +#define MAX_PERIOD_SIZE (65536-64) +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 8 +#endif + + +/* defaults */ +#ifndef MAX_BUFFER_SIZE +//#define MAX_BUFFER_SIZE (64*1024) +#define MAX_BUFFER_SIZE (256*1024) +#endif +#ifndef MAX_PERIOD_SIZE +#define MAX_PERIOD_SIZE MAX_BUFFER_SIZE +#endif +#ifndef USE_FORMATS +//#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#endif +#ifndef USE_RATE +//#define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000 +#define USE_RATE SNDRV_PCM_RATE_8000 +//#define USE_RATE_MIN 5500 +#define USE_RATE_MIN 8000 +//#define USE_RATE_MAX 48000 +#define USE_RATE_MAX 8000 +#endif +#ifndef USE_CHANNELS_MIN +#define USE_CHANNELS_MIN 1 +#endif +#ifndef USE_CHANNELS_MAX +#define USE_CHANNELS_MAX 2 +#endif +#ifndef USE_PERIODS_MIN +#define USE_PERIODS_MIN 1 +#endif +#ifndef USE_PERIODS_MAX +#define USE_PERIODS_MAX 1024 +#endif +#ifndef add_playback_constraints +#define add_playback_constraints(x) 0 +#endif +#ifndef add_capture_constraints +#define add_capture_constraints(x) 0 +#endif + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = { 1,[1 ... (SNDRV_CARDS - 1)] = 0 }; +static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; +static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128 }; + +//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for dummy soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for dummy soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); +module_param_array(pcm_devs, int, NULL, 0444); +MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); +module_param_array(pcm_substreams, int, NULL, 0444); +MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-64) for dummy driver."); +//module_param_array(midi_devs, int, NULL, 0444); +//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); + +static struct platform_device *devices[SNDRV_CARDS]; +static struct timer_list giovatimer; //giova +static int giovastarted = 0; +static int giovaindex = 0; +static spinlock_t giovalock; +struct giovadpcm { + struct snd_pcm_substream *substream; + struct snd_dummy_pcm *dpcm; + int started; +}; +static struct giovadpcm giovadpcms[MAX_PCM_SUBSTREAMS]; + +#define MIXER_ADDR_MASTER 0 +#define MIXER_ADDR_LINE 1 +#define MIXER_ADDR_MIC 2 +#define MIXER_ADDR_SYNTH 3 +#define MIXER_ADDR_CD 4 +#define MIXER_ADDR_LAST 4 + +static void snd_card_dummy_pcm_timer_function(unsigned long data); +struct snd_dummy { + struct snd_card *card; + struct snd_pcm *pcm; + spinlock_t mixer_lock; + int mixer_volume[MIXER_ADDR_LAST + 1][2]; + int capture_source[MIXER_ADDR_LAST + 1][2]; +}; + +struct snd_dummy_pcm { + struct snd_dummy *dummy; + spinlock_t lock; + struct timer_list timer; + unsigned int pcm_buffer_size; + unsigned int pcm_period_size; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_hz; /* HZ */ + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int pcm_buf_pos; /* position in buffer */ + struct snd_pcm_substream *substream; +}; + + +static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm) +{ + int i; + int found = 0; + + +//printk("giova: 1 timer_start %d %p\n", __LINE__, dpcm); + for (i = 0; i < giovaindex + 1; i++) { + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, i, giovaindex, dpcm); + } + + if (giovadpcms[i].dpcm == dpcm) { + giovadpcms[i].started = 1; + found = 1; + } + } + if (!found) { + printk("skypiax: start, NOT found?\n"); + } + +//printk("giova: 2 timer_start %d %p\n", __LINE__, dpcm); +} + +static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) +{ + //del_timer(&dpcm->timer); + int i; + int found = 0; + +//printk("giova: 1 timer_stop %d %p\n", __LINE__, dpcm); + for (i = 0; i < giovaindex + 1; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, i, giovaindex, dpcm); + } + if (giovadpcms[i].dpcm == dpcm) { + giovadpcms[i].started = 0; + found = 1; + } + } + if (!found) { + //printk("skypiax: stop, NOT found?\n"); + } else { + //printk("skypiax: stop, YES found!\n"); + } + + + +//printk("giova: 2 timer_stop %d %p\n", __LINE__, dpcm); +} + +static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dummy_pcm *dpcm = runtime->private_data; + int err = 0; + + //spin_lock(&dpcm->lock, flags); + spin_lock_bh(&giovalock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: +//printk("giova: trigger timer_start %d %p\n", __LINE__, dpcm); + snd_card_dummy_pcm_timer_start(dpcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: +//printk("giova: trigger timer_stop %d %p\n", __LINE__, dpcm); + snd_card_dummy_pcm_timer_stop(dpcm); + break; + default: + err = -EINVAL; + break; + } + //spin_unlock(&dpcm->lock, flags); + spin_unlock_bh(&giovalock); + return 0; +} + +static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dummy_pcm *dpcm = runtime->private_data; + int bps; + + bps = snd_pcm_format_width(runtime->format) * runtime->rate * runtime->channels / 8; + + if (bps <= 0) + return -EINVAL; + + dpcm->pcm_bps = bps; + dpcm->pcm_hz = HZ; + dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream); + dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + +//printk("giova: prepare %d %p\n", __LINE__, dpcm); + return 0; +} + +static void snd_card_dummy_pcm_timer_function(unsigned long data) +{ + //struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data; + struct snd_dummy_pcm *dpcm = NULL; + //unsigned long flags; + int i; + + + giovatimer.expires = (HZ/100 * (GIOVAMULTI)) + jiffies; + add_timer(&giovatimer); + + for (i = 0; i < giovaindex + 1; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, i, giovaindex, dpcm); + } +//printk("giova: timer_func %d i=%d\n", __LINE__, i); + + if (giovadpcms[i].started != 1) + continue; + dpcm = giovadpcms[i].dpcm; + if (dpcm == NULL) { + printk("giova: timer_func %d %d NULL: continue\n", __LINE__, i); + continue; + } + //spin_lock_bh(&dpcm->lock); + //if (in_irq()) + //printk("giova: timer_func %d %d we are in HARDWARE IRQ\n", __LINE__, i); + //if(in_softirq()) + //printk("giova: timer_func %d %d we are in SOFT IRQ\n", __LINE__, i); +//printk("giova: timer_func %d %d\n", __LINE__, i); + //spin_lock_irqsave(&dpcm->lock, flags); + spin_lock_bh(&dpcm->lock); +//CICOPET printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos); + //dpcm->pcm_irq_pos += dpcm->pcm_bps * ((HZ / 100) * GIOVAMULTI); + //dpcm->pcm_irq_pos += (dpcm->pcm_bps * ((HZ / 100) * GIOVAMULTI)); + dpcm->pcm_irq_pos += (dpcm->pcm_bps * ( (HZ / 100) * GIOVAMULTI)); +//CICOPET printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos); + //dpcm->pcm_buf_pos += (dpcm->pcm_bps * ((HZ / 100) * GIOVAMULTI)); + dpcm->pcm_buf_pos += (dpcm->pcm_bps * ( (HZ / 100) * GIOVAMULTI)); +//CICOPET printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos); + //dpcm->pcm_buf_pos %= (dpcm->pcm_buffer_size * dpcm->pcm_hz * GIOVAMULTI); + dpcm->pcm_buf_pos %= (dpcm->pcm_buffer_size * dpcm->pcm_hz ); +//BUONI printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u (dpcm->pcm_period_size * dpcm->pcm_hz)=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos, (dpcm->pcm_period_size * dpcm->pcm_hz)); + if (dpcm->pcm_irq_pos >= (dpcm->pcm_period_size * dpcm->pcm_hz)) { +//CICOPET printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos); + dpcm->pcm_irq_pos %= (dpcm->pcm_period_size * dpcm->pcm_hz); +//BUONI printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u PASSATO!\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos); + //spin_unlock_irqrestore(&dpcm->lock, flags); + spin_unlock_bh(&dpcm->lock); + snd_pcm_period_elapsed(dpcm->substream); + } else { + //spin_unlock_irqrestore(&dpcm->lock, flags); + spin_unlock_bh(&dpcm->lock); + } + } +} + +static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dummy_pcm *dpcm = runtime->private_data; + +snd_pcm_uframes_t ciapa=0; + + spin_lock_bh(&dpcm->lock); +//ciapa = (dpcm->pcm_buf_pos / dpcm->pcm_hz) / (dpcm->pcm_bps/8000); +//ciapa = bytes_to_frames(runtime, (dpcm->pcm_buf_pos / (dpcm->pcm_hz ))) / GIOVAMULTI; +//printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u, ciapa=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos, ciapa); +ciapa = bytes_to_frames(runtime, (dpcm->pcm_buf_pos / (dpcm->pcm_hz ))); + + spin_unlock_bh(&dpcm->lock); +//printk("%d %s, dpcm=%p, dpcm->pcm_irq_pos=%u\t\t dpcm->pcm_bps=%u\t\t dpcm->pcm_buf_pos=%u, ciapa=%u\n", __LINE__, __FILE__, dpcm, dpcm->pcm_irq_pos, dpcm->pcm_bps, dpcm->pcm_buf_pos, (unsigned int)ciapa); +return ciapa; + //return bytes_to_frames(runtime, (dpcm->pcm_buf_pos / (dpcm->pcm_hz )) / GIOVAMULTI); + //return (dpcm->pcm_buf_pos / dpcm->pcm_hz) / (dpcm->pcm_bps/8000); +} + +static struct snd_pcm_hardware snd_card_dummy_playback = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = MAX_PERIOD_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_card_dummy_capture = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = 64, + .period_bytes_max = MAX_PERIOD_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) +{ + int i; +//int found=0; + +//printk("snd_card_dummy_runtime_free giova 1 giovaindex=%d dpcm=%p runtime=%p\n", giovaindex, runtime->private_data, runtime); + spin_lock_bh(&giovalock); + + for (i = 0; i < giovaindex; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d \n", __FILE__, __LINE__, i, giovaindex); + } + //if((giovadpcms[i].substream == substream) && (giovadpcms[i].dpcm == dpcm)){ + if ((giovadpcms[i].dpcm == runtime->private_data)) { + //printk("giova, %s:%d, i=%d, giovaindex=%d %p==%p YES I AM!!!!\n", __FILE__, __LINE__, i, giovaindex, giovadpcms[i].dpcm , runtime->private_data); + //giovadpcms[i].dpcm = NULL; + //giovadpcms[i].substream = NULL; + giovadpcms[i].started = 0; + //break; + } else { + //printk("giova, %s:%d, i=%d, giovaindex=%d %p!=%p NOT ME\n", __FILE__, __LINE__, i, giovaindex, giovadpcms[i].dpcm , runtime->private_data); + } + } + + spin_unlock_bh(&giovalock); + kfree(runtime->private_data); +} + +static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) +{ + struct snd_dummy_pcm *dpcm; + int i; + int found = 0; + + //printk("giova, %s:%d, i=%d, giovaindex=%d %p==%p YES I AM!!!!\n", __FILE__, __LINE__, i, giovaindex, giovadpcms[i].dpcm , runtime->private_data); + //printk("giova, %s:%d, giovaindex=%d\n", __FILE__, __LINE__, giovaindex); + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + //printk("giova, %s:%d, giovaindex=%d\n", __FILE__, __LINE__, giovaindex); + if (!dpcm) { + //spin_unlock_bh(&giovalock); + printk("giova, %s:%d, giovaindex=%d NO MEMORY!!!!\n", __FILE__, __LINE__, giovaindex); + return dpcm; + } + //printk("giova, %s:%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, giovaindex, dpcm); + init_timer(&dpcm->timer); + //dpcm->timer.data = (unsigned long) dpcm; + //dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + + spin_lock_bh(&giovalock); + //printk("giova 1 giovaindex=%d dpcm=%p substream=%p sizeof=%lu\n", giovaindex, dpcm, substream, sizeof(*dpcm)); + for (i = 0; i < giovaindex; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, i, giovaindex, dpcm); + } + //if((giovadpcms[i].substream == substream) && (giovadpcms[i].dpcm == dpcm)) + if ((giovadpcms[i].substream == substream)) { + found = 1; + break; + } + + } + + if (!found) { + + giovadpcms[giovaindex].substream = substream; + giovaindex++; + //printk("giova 2 giovaindex=%d dpcm=%p substream=%p\n", giovaindex, dpcm, substream); + } + + + + found = 0; + for (i = 0; i < giovaindex; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, i, giovaindex, dpcm); + } + if (giovadpcms[i].substream == substream) { + giovadpcms[i].dpcm = dpcm; + giovadpcms[i].started = 0; + found = 1; + //printk("giova 3 giovaindex=%d dpcm=%p substream=%p\n", giovaindex, dpcm, substream); + break; + } + + } + + spin_unlock_bh(&giovalock); + if (!found) { + printk("skypiax giovaindex=%d NOT found????\n", giovaindex); + } + //printk("giova, %s:%d, giovaindex=%d\n", __FILE__, __LINE__, giovaindex); + return dpcm; +} + +static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dummy_pcm *dpcm; + int err; + + if ((dpcm = new_pcm_stream(substream)) == NULL) + return -ENOMEM; + //printk("giova, %s:%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, giovaindex, dpcm); + runtime->private_data = dpcm; + /* makes the infrastructure responsible for freeing dpcm */ + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_playback; + if (substream->pcm->device & 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); + err = add_playback_constraints(runtime); + if (err < 0) + return err; + + return 0; +} + +static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dummy_pcm *dpcm; + int err; + + if ((dpcm = new_pcm_stream(substream)) == NULL) + return -ENOMEM; + //printk("giova, %s:%d, giovaindex=%d dpcm=%p\n", __FILE__, __LINE__, giovaindex, dpcm); + runtime->private_data = dpcm; + /* makes the infrastructure responsible for freeing dpcm */ + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_capture; + if (substream->pcm->device == 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); + err = add_capture_constraints(runtime); + if (err < 0) + return err; + + return 0; +} + +static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream) +{ +//printk("play giova 1 giovaindex=%d dpcm=%p substream=%p\n", giovaindex, substream->private_data, substream); + snd_card_dummy_pcm_timer_stop(substream->private_data); + return 0; +} + +static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) +{ +//printk("capt giova 2 giovaindex=%d dpcm=%p substream=%p\n", giovaindex, substream->private_data, substream); + snd_card_dummy_pcm_timer_stop(substream->private_data); + return 0; +} + +static struct snd_pcm_ops snd_card_dummy_playback_ops = { + .open = snd_card_dummy_playback_open, + .close = snd_card_dummy_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_card_dummy_hw_params, + .hw_free = snd_card_dummy_hw_free, + .prepare = snd_card_dummy_pcm_prepare, + .trigger = snd_card_dummy_pcm_trigger, + .pointer = snd_card_dummy_pcm_pointer, +}; + +static struct snd_pcm_ops snd_card_dummy_capture_ops = { + .open = snd_card_dummy_capture_open, + .close = snd_card_dummy_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_card_dummy_hw_params, + .hw_free = snd_card_dummy_hw_free, + .prepare = snd_card_dummy_pcm_prepare, + .trigger = snd_card_dummy_pcm_trigger, + .pointer = snd_card_dummy_pcm_pointer, +}; + +static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, int substreams) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm); + if (err < 0) + return err; + dummy->pcm = pcm; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); + pcm->private_data = dummy; + pcm->info_flags = 0; + strcpy(pcm->name, "Dummy PCM"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 128 * 1024, 1024 * 1024); + + return 0; +} + +#define DUMMY_VOLUME(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .name = xname, .index = xindex, \ + .info = snd_dummy_volume_info, \ + .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ + .private_value = addr, \ + .tlv = { .p = db_scale_dummy } } + +static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = -50; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_dummy_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + //unsigned long flags; + + if (in_irq()) + printk("giova: line %d we are in HARDWARE IRQ\n", __LINE__); +//printk("giova: volume get %d %d\n", __LINE__, addr); + //spin_lock_irq(&dummy->mixer_lock); + //spin_lock_irqsave(&dummy->mixer_lock, flags); + spin_lock_bh(&dummy->mixer_lock); + ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; + //spin_unlock_irqrestore(&dummy->mixer_lock, flags); + spin_unlock_bh(&dummy->mixer_lock); + return 0; +} + +static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + //unsigned long flags; + + if (in_irq()) + printk("giova: line %d we are in HARDWARE IRQ\n", __LINE__); + left = ucontrol->value.integer.value[0]; + if (left < -50) + left = -50; + if (left > 100) + left = 100; + right = ucontrol->value.integer.value[1]; + if (right < -50) + right = -50; + if (right > 100) + right = 100; +//printk("giova: volume put %d %d\n", __LINE__, addr); + //spin_lock_irq(&dummy->mixer_lock); + //spin_lock_irqsave(&dummy->mixer_lock, flags); + spin_lock_bh(&dummy->mixer_lock); + change = dummy->mixer_volume[addr][0] != left || dummy->mixer_volume[addr][1] != right; + dummy->mixer_volume[addr][0] = left; + dummy->mixer_volume[addr][1] = right; + //spin_unlock_irq(&dummy->mixer_lock); + //spin_unlock_irqrestore(&dummy->mixer_lock, flags); + spin_unlock_bh(&dummy->mixer_lock); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0); + +#define DUMMY_CAPSRC(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_dummy_capsrc_info, \ + .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \ + .private_value = addr } + +#define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info + +static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + //unsigned long flags; + + if (in_irq()) + printk("giova: line %d we are in HARDWARE IRQ\n", __LINE__); + //spin_lock_irq(&dummy->mixer_lock); + //spin_lock_irqsave(&dummy->mixer_lock, flags); + spin_lock_bh(&dummy->mixer_lock); + ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; + ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; + //spin_unlock_irq(&dummy->mixer_lock); + //spin_unlock_irqrestore(&dummy->mixer_lock, flags); + spin_unlock_bh(&dummy->mixer_lock); +//printk("giova: capsrc_get %d %d\n", __LINE__, addr); + return 0; +} + +static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + //unsigned long flags; + + if (in_irq()) + printk("giova: line %d we are in HARDWARE IRQ\n", __LINE__); + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + //spin_lock_irq(&dummy->mixer_lock); + //spin_lock_irqsave(&dummy->mixer_lock, flags); + spin_lock_bh(&dummy->mixer_lock); + change = dummy->capture_source[addr][0] != left && dummy->capture_source[addr][1] != right; + dummy->capture_source[addr][0] = left; + dummy->capture_source[addr][1] = right; + //spin_unlock_irq(&dummy->mixer_lock); + //spin_unlock_irqrestore(&dummy->mixer_lock, flags); + spin_unlock_bh(&dummy->mixer_lock); +//printk("giova: capsrc_put %d %d\n", __LINE__, addr); + return change; +} + +static struct snd_kcontrol_new snd_dummy_controls[] = { + DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), + DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), + DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), + DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH), + DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), + DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE), + DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), + DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC), + DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), + DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD) +}; + +static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy) +{ + struct snd_card *card = dummy->card; + unsigned int idx; + int err; + + //giova if (snd_BUG_ON(!dummy)) + //giova return -EINVAL; + spin_lock_init(&dummy->mixer_lock); + strcpy(card->mixername, "Dummy Mixer"); +//printk("giova: new_mixer %d\n", __LINE__); + return 0; //giova no mixer + + for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy)); + if (err < 0) + return err; + } + return 0; +} + +static int __devinit snd_dummy_probe(struct platform_device *devptr) +{ + struct snd_card *card; + struct snd_dummy *dummy; + int idx, err; + int dev = devptr->id; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct snd_dummy)); + if (card == NULL) + return -ENOMEM; + + //giova err = snd_card_create(index[dev], id[dev], THIS_MODULE, + //giova sizeof(struct snd_dummy), &card); + //giova if (err < 0) + //giova return err; + dummy = card->private_data; + dummy->card = card; + for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) { + if (pcm_substreams[dev] < 1) + pcm_substreams[dev] = 1; + if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) + pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; + err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]); + if (err < 0) + goto __nodev; + } + err = snd_card_dummy_new_mixer(dummy); + if (err < 0) + goto __nodev; + strcpy(card->driver, "Dummy"); + strcpy(card->shortname, "Dummy"); + sprintf(card->longname, "Dummy %i", dev + 1); + + snd_card_set_dev(card, &devptr->dev); + + err = snd_card_register(card); + if (err == 0) { + platform_set_drvdata(devptr, card); + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __devexit snd_dummy_remove(struct platform_device *devptr) +{ + + del_timer(&giovatimer); + snd_card_free(platform_get_drvdata(devptr)); + platform_set_drvdata(devptr, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int snd_dummy_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_card *card = platform_get_drvdata(pdev); + struct snd_dummy *dummy = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(dummy->pcm); + return 0; +} + +static int snd_dummy_resume(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + +#define SND_DUMMY_DRIVER "snd_dummy" + +static struct platform_driver snd_dummy_driver = { + .probe = snd_dummy_probe, + .remove = __devexit_p(snd_dummy_remove), +#ifdef CONFIG_PM + .suspend = snd_dummy_suspend, + .resume = snd_dummy_resume, +#endif + .driver = { + .name = SND_DUMMY_DRIVER}, +}; + +static void snd_dummy_unregister_all(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(devices); ++i) + platform_device_unregister(devices[i]); + platform_driver_unregister(&snd_dummy_driver); +} + +static int __init alsa_card_dummy_init(void) +{ + int i, cards, err; + + err = platform_driver_register(&snd_dummy_driver); + if (err < 0) + return err; + + if (!giovastarted) { + giovastarted = 1; + spin_lock_init(&giovalock); + + spin_lock_bh(&giovalock); + for (i = 0; i < MAX_PCM_SUBSTREAMS; i++) { + + if (i > MAX_PCM_SUBSTREAMS || giovaindex > MAX_PCM_SUBSTREAMS) { + printk("giova, %s:%d, i=%d, giovaindex=%d \n", __FILE__, __LINE__, i, giovaindex); + } + giovadpcms[i].substream = NULL; + giovadpcms[i].dpcm = NULL; + giovadpcms[i].started = 0; + } + init_timer(&giovatimer); + //giovatimer.data = (unsigned long) dpcm; + giovatimer.data = (unsigned long) &giovadpcms; + giovatimer.function = snd_card_dummy_pcm_timer_function; + giovatimer.expires = ((HZ / 1000) *GIOVAMULTI)+ jiffies; + add_timer(&giovatimer); + printk("003 snd-dummy skypiax driver, %s:%d working on a machine with %dHZ kernel\n", __FILE__, __LINE__, HZ); + spin_unlock_bh(&giovalock); + } + + + cards = 0; + for (i = 0; i < SNDRV_CARDS; i++) { + struct platform_device *device; + if (!enable[i]) + continue; + device = platform_device_register_simple(SND_DUMMY_DRIVER, i, NULL, 0); + if (IS_ERR(device)) + continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } + devices[i] = device; + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Dummy soundcard not found or device busy\n"); +#endif + snd_dummy_unregister_all(); + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_dummy_exit(void) +{ + del_timer(&giovatimer); + snd_dummy_unregister_all(); +} + +module_init(alsa_card_dummy_init) + module_exit(alsa_card_dummy_exit) diff --git a/src/mod/endpoints/mod_skypiax/103/mod_skypiax.c b/src/mod/endpoints/mod_skypiax/103/mod_skypiax.c new file mode 100644 index 0000000000..a724658254 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/103/mod_skypiax.c @@ -0,0 +1,2579 @@ +/* + * 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_skypiax) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * + * mod_skypiax.c -- Skype compatible Endpoint Module + * + */ + +#include "skypiax.h" +#define MDL_CHAT_PROTO "skype" +#define TIMERS_ON +#ifdef TIMERS_ON +#undef TIMER_WRITE +#else +#undef TIMER_WRITE +#endif + +#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_skypiax_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skypiax_shutdown); +SWITCH_MODULE_DEFINITION(mod_skypiax, mod_skypiax_load, mod_skypiax_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(skypiax_function); +#define SKYPIAX_SYNTAX "interface_name skype_API_msg" + +SWITCH_STANDARD_API(skypiax_chat_function); +#define SKYPIAX_CHAT_SYNTAX "interface_name remote_skypename TEXT" +/* BEGIN: Changes here */ +#define FULL_RELOAD 0 +#define SOFT_RELOAD 1 +/* END: Changes heres */ + +char *interface_status[] = { /* should match SKYPIAX_STATE_xxx in skypiax.h */ + "IDLE", + "DOWN", + "RING", + "DIALING", + "BUSY", + "UP", + "RINGING", + "PRERING", + "DOUBLE", + "SELECTD", + "HANG_RQ", + "PREANSW" +}; +char *skype_callflow[] = { /* should match CALLFLOW_XXX in skypiax.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 *ip; + int port; + char *dialplan; + char *destination; + char *context; + char *codec_string; + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last; + char *codec_rates_string; + char *codec_rates[SWITCH_MAX_CODECS]; + int codec_rates_last; + unsigned int flags; + int fd; + int calls; + int real_interfaces; + int next_interface; + char hold_music[256]; + private_t SKYPIAX_INTERFACES[SKYPIAX_MAX_INTERFACES]; + switch_mutex_t *mutex; + private_t *sk_console; +} globals; + +switch_endpoint_interface_t *skypiax_endpoint_interface; +switch_memory_pool_t *skypiax_module_pool = NULL; +int running = 0; + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, globals.context); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_destination, globals.destination); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_rates_string, globals.codec_rates_string); + +/* BEGIN: Changes here */ +static switch_status_t interface_exists(char *the_interface); +static switch_status_t remove_interface(char *the_interface); +/* END: Changes here */ + +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 skypiax_tech_init(private_t * tech_pvt, switch_core_session_t *session); + +static switch_status_t skypiax_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t skypiax_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", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } + if (skypiax_codec(tech_pvt, SAMPLERATE_SKYPIAX, 20) != SWITCH_STATUS_SUCCESS) { + ERRORA("skypiax_codec FAILED\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } + +#ifdef TIMERS_ON + if (switch_core_timer_init(&tech_pvt->timer_read, "soft", 20, tech_pvt->read_codec.implementation->samples_per_packet, skypiax_module_pool) != + SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_read); +#endif// TIMERS_ON + +#ifdef TIMER_WRITE + if (switch_core_timer_init(&tech_pvt->timer_write, "soft", 20, tech_pvt->write_codec.implementation->samples_per_packet, skypiax_module_pool) != + SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_write); +#endif // TIMER_WRITE + + DEBUGA_SKYPE("skypiax_tech_init SUCCESS\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_SUCCESS; +} + +/* BEGIN: Changes here */ +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.SKYPIAX_INTERFACES[interface_id].name)) { + return SWITCH_STATUS_SUCCESS; + } + } else { + /* interface name */ + for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].name, the_interface) == 0) { + return SWITCH_STATUS_SUCCESS; + break; + } + } + } + } else { /* look by skype_user */ + + + for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].skype_user)) { + if (strcmp(globals.SKYPIAX_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; + + //running = 0; + + + 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.SKYPIAX_INTERFACES[interface_id]; + } else { + + for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].name, the_interface) == 0) { + tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id]; + break; + } + } + } + } else { /* remove by skype_user */ + for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].skype_user, the_interface) == 0) { + tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id]; + break; + } + } + } + + if (!tech_pvt) { + DEBUGA_SKYPE("interface '%s' does not exist\n", SKYPIAX_P_LOG, the_interface); + goto end; + } + + if (strlen(globals.SKYPIAX_INTERFACES[interface_id].session_uuid_str)) { + DEBUGA_SKYPE("interface '%s' is busy\n", SKYPIAX_P_LOG, the_interface); + goto end; + } + + globals.SKYPIAX_INTERFACES[interface_id].running = 0; + + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) { +#ifdef WIN32 + switch_file_write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die +#else /* WIN32 */ + howmany = write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ + } + + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) { +#ifdef WIN32 + if (SendMessage(tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) { // let's the skypiax_api_thread_func die + DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPIAX_P_LOG, GetLastError()); + globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread = NULL; + } +#else + if (tech_pvt->running && tech_pvt->SkypiaxHandles.disp) { + XEvent e; + Atom atom1 = XInternAtom(tech_pvt->SkypiaxHandles.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->SkypiaxHandles.disp; + e.xclient.window = tech_pvt->SkypiaxHandles.skype_win; + e.xclient.format = 8; + + XSendEvent(tech_pvt->SkypiaxHandles.disp, tech_pvt->SkypiaxHandles.win, False, 0, &e); + XSync(tech_pvt->SkypiaxHandles.disp, False); + } +#endif + } + + while (x) { + x--; + switch_yield(50000); + } + + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) { + switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread); + } + + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) { + switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread); + } + + switch_mutex_lock(globals.mutex); + if (globals.sk_console == &globals.SKYPIAX_INTERFACES[interface_id]) { + DEBUGA_SKYPE("interface '%s' no more console\n", SKYPIAX_P_LOG, the_interface); + globals.sk_console = NULL; + } else { + DEBUGA_SKYPE("interface '%s' STILL console\n", SKYPIAX_P_LOG, the_interface); + } + memset(&globals.SKYPIAX_INTERFACES[interface_id], '\0', sizeof(private_t)); + globals.real_interfaces--; + switch_mutex_unlock(globals.mutex); + + DEBUGA_SKYPE("interface '%s' deleted successfully\n", SKYPIAX_P_LOG, the_interface); + globals.SKYPIAX_INTERFACES[interface_id].running = 1; + end: + //running = 1; + return SWITCH_STATUS_SUCCESS; +} + +/* END: Changes here */ + +/* + 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); + //ERRORA("%s CHANNEL INIT\n", SKYPIAX_P_LOG, tech_pvt->name); + 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", SKYPIAX_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", SKYPIAX_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); + } + +#ifdef TIMERS_ON + switch_core_timer_destroy(&tech_pvt->timer_read); +#endif// TIMERS_ON +#ifdef TIMER_WRITE + + switch_core_timer_destroy(&tech_pvt->timer_write); +#endif // TIMER_WRITE + + + *tech_pvt->session_uuid_str = '\0'; + tech_pvt->interface_state = SKYPIAX_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", SKYPIAX_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]; + + + 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); + //switch_set_flag(tech_pvt, TFLAG_HANGUP); + + if (strlen(tech_pvt->skype_call_id)) { + //switch_thread_cond_signal(tech_pvt->cond); + DEBUGA_SKYPE("hanging up skype call: %s\n", SKYPIAX_P_LOG, tech_pvt->skype_call_id); + sprintf(msg_to_skype, "ALTER CALL %s HANGUP", tech_pvt->skype_call_id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + //memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str)); + //*tech_pvt->session_uuid_str = '\0'; + DEBUGA_SKYPE("%s CHANNEL HANGUP\n", SKYPIAX_P_LOG, tech_pvt->name); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + + tech_pvt->interface_state = SKYPIAX_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG, tech_pvt->name); + switch (sig) { + case SWITCH_SIG_KILL: + DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_KILL\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + switch_mutex_lock(tech_pvt->flag_mutex); + switch_clear_flag(tech_pvt, TFLAG_IO); + switch_clear_flag(tech_pvt, TFLAG_VOICE); + switch_set_flag(tech_pvt, TFLAG_HANGUP); + if (tech_pvt->skype_callflow == CALLFLOW_STATUS_REMOTEHOLD) { + ERRORA("FYI %s CHANNEL in CALLFLOW_STATUS_REMOTEHOLD got SWITCH_SIG_KILL\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + channel_on_hangup(session); + } + if (switch_channel_get_state(channel) == CS_NEW) { + ERRORA("FYI %s CHANNEL in CS_NEW state got SWITCH_SIG_KILL\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + channel_on_hangup(session); + } + 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", SKYPIAX_P_LOG, switch_channel_get_name(channel), switch_channel_get_state(channel)); + channel_on_hangup(session); + } + switch_mutex_unlock(tech_pvt->flag_mutex); + break; + case SWITCH_SIG_BREAK: + DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_BREAK\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + //switch_set_flag(tech_pvt, TFLAG_BREAK); + 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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG, tech_pvt->name); + DEBUGA_SKYPE("DTMF: %c\n", SKYPIAX_P_LOG, dtmf->digit); + + skypiax_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; +//unsigned int len; + + 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", SKYPIAX_P_LOG); + //TODO: kill the bastard + return SWITCH_STATUS_FALSE; + } + + tech_pvt->read_frame.flags = SFF_NONE; + *frame = NULL; + + //switch_core_timer_next(&tech_pvt->timer_read); + + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_REMOTEHOLD) { + tech_pvt->read_frame.datalen = recv(tech_pvt->readfd, (char *) tech_pvt->read_frame.data, 320, 0); //seems that Skype only sends 320 bytes at time + } else { + tech_pvt->read_frame.datalen = 0; + } + + //if (!skypiax_audio_read(tech_pvt)) { + if (!tech_pvt->read_frame.datalen) { + + ERRORA("skypiax_audio_read ERROR\n", SKYPIAX_P_LOG); + + } else { + 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", SKYPIAX_P_LOG); + goto cng; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + DEBUGA_SKYPE("CHANNEL READ FRAME not IO\n", SKYPIAX_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", SKYPIAX_P_LOG); + continue; + } +#ifdef TIMERS_ON + switch_core_timer_check(&tech_pvt->timer_read, SWITCH_TRUE); +#endif// TIMERS_ON + *frame = &tech_pvt->read_frame; +#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", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + + } + + DEBUGA_SKYPE("CHANNEL READ FALSE\n", SKYPIAX_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; + unsigned int sent=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", SKYPIAX_P_LOG); + //TODO: kill the bastard + 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 0 + sent = frame->datalen; +#ifdef WIN32 + //switch_file_write(tech_pvt->audiopipe_cli[1], frame->data, &sent); +#else /* WIN32 */ + //sent = write(tech_pvt->audiopipe_cli[1], frame->data, sent); +#endif /* WIN32 */ + //if (tech_pvt->flag_audio_cli == 0) { +#ifdef TIMER_WRITE + switch_core_timer_next(&tech_pvt->timer_write); +#endif // TIMER_WRITE + + //while (tech_pvt->flag_audio_cli == 1) { + //switch_sleep(1000); //10 millisec + //WARNINGA("write now is 1\n", SKYPIAX_P_LOG); + //} + switch_mutex_lock(tech_pvt->flag_audio_cli_mutex); + memcpy(tech_pvt->audiobuf_cli, frame->data, frame->datalen); + tech_pvt->flag_audio_cli = 1; + switch_mutex_unlock(tech_pvt->flag_audio_cli_mutex); + //} + //NOTICA("write \n", SKYPIAX_P_LOG); +#endif //0 + +#ifdef TIMER_WRITE +/* NONBLOCKING ? */ + //switch_core_timer_next(&tech_pvt->timer_write); + //switch_core_timer_check(&tech_pvt->timer_write, SWITCH_TRUE); +#endif // TIMER_WRITE + /* 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) { + sent = send(tech_pvt->writefd, (char *) frame->data, frame->datalen, 0); + if (sent == -1) { + ERRORA("EXIT? sent=%d\n", SKYPIAX_P_LOG, sent); + } else if (sent != frame->datalen) { + ERRORA("sent=%d\n", SKYPIAX_P_LOG, sent); + } + } + + +#ifdef TIMER_WRITE +/* BLOCKING ? */ + switch_core_timer_check(&tech_pvt->timer_write, SWITCH_TRUE); +#endif // TIMER_WRITE + if (sent != frame->datalen && sent != -1) { + DEBUGA_SKYPE("CLI PIPE write %d\n", SKYPIAX_P_LOG, sent); + } + + 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", SKYPIAX_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("MSG_ID=%d, TO BE ANSWERED!\n", SKYPIAX_P_LOG, msg->message_id); + +#ifdef TIMERS_ON + switch_core_timer_sync(&tech_pvt->timer_read); +#endif// TIMERS_ON +#ifdef TIMER_WRITE + switch_core_timer_sync(&tech_pvt->timer_write); +#endif // TIMER_WRITE + channel_answer_channel(session); + } + break; + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + + DEBUGA_SKYPE("%s CHANNEL got SWITCH_MESSAGE_INDICATE_AUDIO_SYNC\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + +#ifdef TIMERS_ON + switch_core_timer_sync(&tech_pvt->timer_read); +#endif// TIMERS_ON +#ifdef TIMER_WRITE + switch_core_timer_sync(&tech_pvt->timer_write); +#endif // TIMER_WRITE + break; + default: + { + +#ifdef TIMERS_ON + switch_core_timer_sync(&tech_pvt->timer_read); +#endif// TIMERS_ON +#ifdef TIMER_WRITE + + switch_core_timer_sync(&tech_pvt->timer_write); +#endif // TIMER_WRITE + + DEBUGA_SKYPE("MSG_ID=%d\n", SKYPIAX_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", SKYPIAX_P_LOG, body); + + return SWITCH_STATUS_SUCCESS; +} + +switch_state_handler_table_t skypiax_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 skypiax_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(skypiax_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", SKYPIAX_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) { + /* we've been asked for the "ANY" interface, let's find the first idle interface */ + //DEBUGA_SKYPE("Finding one available skype interface\n", SKYPIAX_P_LOG); + //tech_pvt = find_available_skypiax_interface(NULL); + //if (tech_pvt) + //found = 1; + //} else if (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", SKYPIAX_P_LOG); + tech_pvt = find_available_skypiax_interface_rr(NULL); + if (tech_pvt) + found = 1; + } + + for (i = 0; !found && i < SKYPIAX_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.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].name, interface_name, strlen(interface_name)) == 0)) { + if (strlen(globals.SKYPIAX_INTERFACES[i].session_uuid_str)) { + DEBUGA_SKYPE + ("globals.SKYPIAX_INTERFACES[%d].name=|||%s||| session_uuid_str=|||%s||| is BUSY\n", + SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name, globals.SKYPIAX_INTERFACES[i].session_uuid_str); + DEBUGA_SKYPE("1 SESSION_DESTROY %s\n", SKYPIAX_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + DEBUGA_SKYPE("globals.SKYPIAX_INTERFACES[%d].name=|||%s|||?\n", SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name); + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + found = 1; + break; + } + + } + + } else { + ERRORA("Doh! no destination number?\n", SKYPIAX_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", SKYPIAX_P_LOG, interface_name); + DEBUGA_SKYPE("2 SESSION_DESTROY %s\n", SKYPIAX_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + //return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + return SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + + channel = switch_core_session_get_channel(*new_session); + if (!channel) { + ERRORA("Doh! no channel?\n", SKYPIAX_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + if (skypiax_tech_init(tech_pvt, *new_session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", SKYPIAX_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), "skypiax/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), "skypiax/RR/%s%s", tech_pvt->name, outbound_profile->destination_number + 2); + } else { + snprintf(name, sizeof(name), "skypiax/%s", outbound_profile->destination_number); + } + + //snprintf(name, sizeof(name), "skypiax/%s", outbound_profile->destination_number); + //snprintf(name, sizeof(name), "skypiax/%s", tech_pvt->name); + 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", SKYPIAX_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'; + + //skypiax_call(tech_pvt, rdest, 30); + + 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); + skypiax_call(tech_pvt, rdest, 30); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_SUCCESS; + } + + ERRORA("Doh! no new_session\n", SKYPIAX_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 skypiax_signaling_read function + * + */ +static void *SWITCH_THREAD_FUNC skypiax_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 skypiax_signaling_thread_func: started, p=%p\n", SKYPIAX_P_LOG, (void *) tech_pvt); + + while (forever) { + if (!(running && tech_pvt->running)) + break; + res = skypiax_signaling_read(tech_pvt); + if (res == CALLFLOW_INCOMING_HANGUP) { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + //private_t *tech_pvt = NULL; + DEBUGA_SKYPE("skype call ended\n", SKYPIAX_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", SKYPIAX_P_LOG); + } + switch_core_session_rwunlock(session); + } else { + DEBUGA_SKYPE("no session\n", SKYPIAX_P_LOG); + } + switch_mutex_lock(globals.mutex); + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + *tech_pvt->session_uuid_str = '\0'; + *tech_pvt->skype_call_id = '\0'; + switch_mutex_unlock(globals.mutex); + + //ERRORA("LET'S WAIT\n", SKYPIAX_P_LOG); + switch_sleep(300000); //0.3 sec + //ERRORA("WAIT'S OVER\n", SKYPIAX_P_LOG); + //tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + //switch_sleep(30000); //0.03 sec + switch_mutex_lock(globals.mutex); + tech_pvt->skype_callflow = CALLFLOW_CALL_IDLE; + tech_pvt->interface_state = SKYPIAX_STATE_IDLE; + switch_mutex_unlock(globals.mutex); + } else { + ERRORA("no tech_pvt?\n", SKYPIAX_P_LOG); + } + } + } + return NULL; +} + +/* BEGIN: Changes heres */ +static switch_status_t load_config(int reload_type) +/* END: Changes heres */ +{ + char *cf = "skypiax.conf"; + switch_xml_t cfg, xml, global_settings, param, interfaces, myinterface; + private_t *tech_pvt = NULL; + + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, skypiax_module_pool); + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + ERRORA("open of %s failed\n", SKYPIAX_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")) { + DEBUGA_SKYPE("globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug); + globals.debug = atoi(val); + DEBUGA_SKYPE("globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug); + } else if (!strcasecmp(var, "hold-music")) { + switch_set_string(globals.hold_music, val); + DEBUGA_SKYPE("globals.hold_music=%s\n", SKYPIAX_P_LOG, globals.hold_music); + } else if (!strcmp(var, "port")) { + globals.port = atoi(val); + DEBUGA_SKYPE("globals.port=%d\n", SKYPIAX_P_LOG, globals.port); + } else if (!strcmp(var, "codec-master")) { + if (!strcasecmp(val, "us")) { + switch_set_flag(&globals, GFLAG_MY_CODEC_PREFS); + } + DEBUGA_SKYPE("codec-master globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug); + } else if (!strcmp(var, "dialplan")) { + set_global_dialplan(val); + DEBUGA_SKYPE("globals.dialplan=%s\n", SKYPIAX_P_LOG, globals.dialplan); + } else if (!strcmp(var, "destination")) { + set_global_destination(val); + DEBUGA_SKYPE("globals.destination=%s\n", SKYPIAX_P_LOG, globals.destination); + } else if (!strcmp(var, "context")) { + set_global_context(val); + DEBUGA_SKYPE("globals.context=%s\n", SKYPIAX_P_LOG, globals.context); + } else if (!strcmp(var, "codec-prefs")) { + set_global_codec_string(val); + DEBUGA_SKYPE("globals.codec_string=%s\n", SKYPIAX_P_LOG, globals.codec_string); + globals.codec_order_last = switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS); + } else if (!strcmp(var, "codec-rates")) { + set_global_codec_rates_string(val); + DEBUGA_SKYPE("globals.codec_rates_string=%s\n", SKYPIAX_P_LOG, globals.codec_rates_string); + globals.codec_rates_last = switch_separate_string(globals.codec_rates_string, ',', globals.codec_rates, SWITCH_MAX_CODECS); + } + + } + } + + 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 *tonegroup = NULL; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *hotline = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + char *enable_callerid = "true"; + char *X11_display = NULL; + char *tcp_cli_port = NULL; + char *tcp_srv_port = NULL; + char *skype_user = NULL; + char *report_incoming_chatmessages = "true"; + + uint32_t interface_id = 0, to = 0, max = 0; + + 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, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "destination")) { + destination = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "enable-callerid")) { + enable_callerid = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = 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, "tcp_cli_port")) { + tcp_cli_port = val; + } else if (!strcasecmp(var, "tcp_srv_port")) { + tcp_srv_port = val; + } else if (!strcasecmp(var, "X11-display") || !strcasecmp(var, "X11_display")) { + X11_display = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "hotline")) { + hotline = val; + } + + } + if (!skype_user) { + ERRORA("interface missing REQUIRED param 'skype_user'\n", SKYPIAX_P_LOG); + continue; + } + + /* BEGIN: Changes here */ + if (reload_type == SOFT_RELOAD) { + char the_interface[256]; + sprintf(the_interface, "#%s", name); + + if (interface_exists(the_interface) == SWITCH_STATUS_SUCCESS) { + continue; + } + } + /* END: Changes here */ + + if (!X11_display) { + ERRORA("interface missing REQUIRED param 'X11_display'\n", SKYPIAX_P_LOG); + continue; + } + if (!tcp_cli_port) { + ERRORA("interface missing REQUIRED param 'tcp_cli_port'\n", SKYPIAX_P_LOG); + continue; + } + + if (!tcp_srv_port) { + ERRORA("interface missing REQUIRED param 'tcp_srv_port'\n", SKYPIAX_P_LOG); + continue; + } + if (!id) { + ERRORA("interface missing REQUIRED param 'id'\n", SKYPIAX_P_LOG); + continue; + } + if (switch_is_number(id)) { + interface_id = atoi(id); + DEBUGA_SKYPE("interface_id=%d\n", SKYPIAX_P_LOG, interface_id); + } else { + ERRORA("interface param 'id' MUST be a number, now id='%s'\n", SKYPIAX_P_LOG, id); + continue; + } + + if (!name) { + WARNINGA("interface missing param 'name', not nice, but works\n", SKYPIAX_P_LOG); + } + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + if (name) { + DEBUGA_SKYPE("name=%s\n", SKYPIAX_P_LOG, name); + } +#ifndef WIN32 + if (!XInitThreads()) { + ERRORA("Not initialized XInitThreads!\n", SKYPIAX_P_LOG); + } else { + DEBUGA_SKYPE("Initialized XInitThreads!\n", SKYPIAX_P_LOG); + } + switch_sleep(1000); +#endif /* WIN32 */ + + if (interface_id && interface_id < SKYPIAX_MAX_INTERFACES) { + private_t newconf; + switch_threadattr_t *skypiax_api_thread_attr = NULL; + switch_threadattr_t *skypiax_signaling_thread_attr = NULL; + + memset(&newconf, '\0', sizeof(newconf)); + globals.SKYPIAX_INTERFACES[interface_id] = newconf; + globals.SKYPIAX_INTERFACES[interface_id].running = 1; + + + tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id]; + + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].interface_id, id); + if (name) { + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].name, name); + } else { + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].name, "N/A"); + } + DEBUGA_SKYPE("CONFIGURING interface_id=%d\n", SKYPIAX_P_LOG, interface_id); +#ifdef WIN32 + globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port = (unsigned short) atoi(tcp_cli_port); + globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port = (unsigned short) atoi(tcp_srv_port); +#else /* WIN32 */ + globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port = atoi(tcp_cli_port); + globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port = atoi(tcp_srv_port); +#endif /* WIN32 */ + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].X11_display, X11_display); + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].skype_user, skype_user); + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].context, context); + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].dialplan, dialplan); + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].destination, destination); + switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].context, context); + + if (!strcmp(report_incoming_chatmessages, "true") || !strcmp(report_incoming_chatmessages, "1")) { + globals.SKYPIAX_INTERFACES[interface_id].report_incoming_chatmessages = 1; + } else { + globals.SKYPIAX_INTERFACES[interface_id].report_incoming_chatmessages = 0; //redundant, just in case + + } + + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].X11_display=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].X11_display); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].skype_user=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port=%d\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port=%d\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port); + DEBUGA_SKYPE("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].name=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].name); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].context=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].context); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].dialplan=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].dialplan); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].destination=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].destination); + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].context=%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].context); + + DEBUGA_SKYPE + ("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].report_incoming_chatmessages=%d\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].report_incoming_chatmessages); + + WARNINGA("STARTING interface_id=%d\n", SKYPIAX_P_LOG, interface_id); + + switch_threadattr_create(&skypiax_api_thread_attr, skypiax_module_pool); + switch_threadattr_detach_set(skypiax_api_thread_attr, 1); + switch_threadattr_stacksize_set(skypiax_api_thread_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread, + skypiax_api_thread_attr, skypiax_do_skypeapi_thread, &globals.SKYPIAX_INTERFACES[interface_id], skypiax_module_pool); + + switch_sleep(100000); + + switch_threadattr_create(&skypiax_signaling_thread_attr, skypiax_module_pool); + switch_threadattr_detach_set(skypiax_signaling_thread_attr, 1); + switch_threadattr_stacksize_set(skypiax_signaling_thread_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&globals.SKYPIAX_INTERFACES[interface_id]. + skypiax_signaling_thread, skypiax_signaling_thread_attr, + skypiax_signaling_thread_func, &globals.SKYPIAX_INTERFACES[interface_id], skypiax_module_pool); + + switch_sleep(100000); + + skypiax_audio_init(&globals.SKYPIAX_INTERFACES[interface_id]); + + switch_mutex_init(&globals.SKYPIAX_INTERFACES[interface_id].flag_audio_srv_mutex, SWITCH_MUTEX_NESTED, skypiax_module_pool); + switch_mutex_init(&globals.SKYPIAX_INTERFACES[interface_id].flag_audio_cli_mutex, SWITCH_MUTEX_NESTED, skypiax_module_pool); + NOTICA + ("WAITING roughly 10 seconds to find a running Skype client and connect to its SKYPE API for interface_id=%d\n", + SKYPIAX_P_LOG, interface_id); + i = 0; + while (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected == 0 && running && i < 200) { // 10 seconds! thanks Jeff Lenk + switch_sleep(50000); + i++; + } + if (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected) { + NOTICA + ("Found a running Skype client, connected to its SKYPE API for interface_id=%d, waiting 60 seconds for CURRENTUSERHANDLE==%s\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_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. Skypiax exiting\n", + SKYPIAX_P_LOG, interface_id); + running = 0; + switch_mutex_unlock(globals.mutex); + switch_xml_free(xml); + return SWITCH_STATUS_FALSE; + } + + i = 0; + while (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.currentuserhandle == 0 && running && i < 1200) { // 60 seconds! thanks Jeff Lenk + switch_sleep(50000); + i++; + } + if (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.currentuserhandle) { + WARNINGA + ("Interface_id=%d is now STARTED, the Skype client to which we are connected gave us the correct CURRENTUSERHANDLE (%s)\n", + SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user); + + + skypiax_signaling_write(&globals.SKYPIAX_INTERFACES[interface_id], "PROTOCOL 7"); + switch_sleep(10000); + skypiax_signaling_write(&globals.SKYPIAX_INTERFACES[interface_id], "SET AUTOAWAY OFF"); + } 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'. Skypiax exiting now\n", + SKYPIAX_P_LOG, globals.SKYPIAX_INTERFACES[interface_id].skype_user, + interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user, globals.SKYPIAX_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 SKYPIAX_MAX_INTERFACES (%d)\n", SKYPIAX_P_LOG, interface_id, SKYPIAX_MAX_INTERFACES); + continue; + } + + } + + for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name)) { + /* How many real intterfaces */ + globals.real_interfaces = i + 1; + + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].interface_id=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].interface_id); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].X11_display=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].X11_display); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].name=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].name); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].context=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].context); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].dialplan=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].dialplan); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].destination=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].destination); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].context=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].context); + DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].report_incoming_chatmessages=%d\n", SKYPIAX_P_LOG, i, i, + globals.SKYPIAX_INTERFACES[i].report_incoming_chatmessages); + } + } + } + + 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, *host, *f_user = NULL, *ffrom = NULL, *f_host = NULL, *f_resource = NULL; + char *user = NULL, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL; + //mdl_profile_t *profile = 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", SKYPIAX_P_LOG, proto, from, to, subject, body, type, + hint ? hint : "NULL"); + + if (!to || !strlen(to)) { + ERRORA("Missing To: header.\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + if ((!from && !hint) || (!strlen(from) && !strlen(hint))) { + ERRORA("Missing From: AND Hint: headers.\n", SKYPIAX_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'; + } + //if (!strcmp(proto, MDL_CHAT_PROTO)) { + + DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPIAX_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 < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].name, hint, strlen(hint)) == 0)) { + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + DEBUGA_SKYPE("Using interface: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", SKYPIAX_P_LOG, i, globals.SKYPIAX_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 < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].skype_user, from, strlen(from)) == 0)) { + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + DEBUGA_SKYPE("Using interface: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name); + found = 1; + break; + } + } + } + if (!found) { + ERRORA("ERROR: A Skypiax interface with name='%s' or one with skypeuser='%s' was not found\n", SKYPIAX_P_LOG, hint ? hint : "NULL", + from ? from : "NULL"); + goto end; + } else { + + snprintf(skype_msg, sizeof(skype_msg), "CHAT CREATE %s", to); + skypiax_signaling_write(tech_pvt, skype_msg); + switch_sleep(1000); + } + //} else { + //FIXME don't know how to do here, let's hope this is correct + //char *p; + //ffrom = switch_mprintf("%s+%s", proto, from); + //from = ffrom; + //if ((p = strchr(from, '/'))) { + //*p = '\0'; + //} + //NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPIAX_P_LOG, proto, from, to, subject, body, type, hint?hint:"NULL"); + //switch_core_chat_send(proto, proto, from, to, subject, body, type, hint); + //return SWITCH_STATUS_SUCCESS; + //} + + 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); + skypiax_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", SKYPIAX_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_skypiax_load) +{ + switch_api_interface_t *commands_api_interface; + switch_chat_interface_t *chat_interface; + + skypiax_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); + skypiax_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + skypiax_endpoint_interface->interface_name = "skypiax"; + skypiax_endpoint_interface->io_routines = &skypiax_io_routines; + skypiax_endpoint_interface->state_handler = &skypiax_state_handlers; + + if (running) { + + SWITCH_ADD_API(commands_api_interface, "sk", "Skypiax console commands", sk_function, SK_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "skypiax", "Skypiax interface commands", skypiax_function, SKYPIAX_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "skypiax_chat", "Skypiax_chat interface remote_skypename TEXT", skypiax_chat_function, SKYPIAX_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_skypiax_shutdown) +{ + int x; + private_t *tech_pvt = NULL; + switch_status_t status; + unsigned int howmany = 8; + int interface_id; + + running = 0; + + for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) { + tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id]; + + if (strlen(globals.SKYPIAX_INTERFACES[interface_id].name)) { + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) { +#ifdef WIN32 + switch_file_write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die +#else /* WIN32 */ + howmany = write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ + } + + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) { +#ifdef WIN32 + if (SendMessage(tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) { // let's the skypiax_api_thread_func die + DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPIAX_P_LOG, GetLastError()); + globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread = NULL; + } +#else + if (tech_pvt->SkypiaxHandles.disp) { + XEvent e; + Atom atom1 = XInternAtom(tech_pvt->SkypiaxHandles.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->SkypiaxHandles.disp; + e.xclient.window = tech_pvt->SkypiaxHandles.skype_win; + e.xclient.format = 8; + + XSendEvent(tech_pvt->SkypiaxHandles.disp, tech_pvt->SkypiaxHandles.win, False, 0, &e); + XSync(tech_pvt->SkypiaxHandles.disp, False); + } +#endif + } + x = 10; + while (x) { //FIXME 0.5 seconds? + x--; + switch_yield(50000); + } + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) { + switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread); + } + if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) { + switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread); + } +#ifndef WIN32 + WARNINGA("SHUTDOWN interface_id=%d\n", SKYPIAX_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->SkypiaxHandles.fdesc[0], 2); + close(tech_pvt->SkypiaxHandles.fdesc[0]); + shutdown(tech_pvt->SkypiaxHandles.fdesc[1], 2); + close(tech_pvt->SkypiaxHandles.fdesc[1]); +#endif /* WIN32 */ + } + + } + switch_event_free_subclass(MY_EVENT_INCOMING_CHATMESSAGE); + + switch_safe_free(globals.dialplan); + switch_safe_free(globals.context); + switch_safe_free(globals.destination); + switch_safe_free(globals.codec_string); + switch_safe_free(globals.codec_rates_string); + + return SWITCH_STATUS_SUCCESS; +} + +void *SWITCH_THREAD_FUNC skypiax_do_tcp_srv_thread(switch_thread_t * thread, void *obj) +{ + return skypiax_do_tcp_srv_thread_func(obj); +} + +void *SWITCH_THREAD_FUNC skypiax_do_tcp_cli_thread(switch_thread_t * thread, void *obj) +{ + return skypiax_do_tcp_cli_thread_func(obj); +} + +void *SWITCH_THREAD_FUNC skypiax_do_skypeapi_thread(switch_thread_t * thread, void *obj) +{ + return skypiax_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_dtmf_t dtmf = { (char) value[0], switch_core_default_dtmf_duration(0) }; + DEBUGA_SKYPE("received DTMF %c on channel %s\n", SKYPIAX_P_LOG, dtmf.digit, switch_channel_get_name(channel)); + switch_mutex_lock(tech_pvt->flag_mutex); + //FIXME: why sometimes DTMFs from here do not seems to be get by FS? + switch_channel_queue_dtmf(channel, &dtmf); + switch_set_flag(tech_pvt, TFLAG_DTMF); + switch_mutex_unlock(tech_pvt->flag_mutex); + //} else { + //NOTICA + //("received a DTMF on channel %s, but we're BRIDGED, so let's NOT relay it out of band\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + //} + } else { + WARNINGA("received %c DTMF, but no channel?\n", SKYPIAX_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; + + switch_threadattr_create(&thd_attr, skypiax_module_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&tech_pvt->tcp_srv_thread, thd_attr, skypiax_do_tcp_srv_thread, tech_pvt, skypiax_module_pool) == SWITCH_STATUS_SUCCESS) { + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + } else { + ERRORA("failed to start tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + return -1; + } + + switch_threadattr_create(&thd_attr, skypiax_module_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&tech_pvt->tcp_cli_thread, thd_attr, skypiax_do_tcp_cli_thread, tech_pvt, skypiax_module_pool) == SWITCH_STATUS_SUCCESS) { + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + } else { + ERRORA("failed to start tcp_cli_thread thread.\n", SKYPIAX_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", SKYPIAX_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(skypiax_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL)) != 0) { + DEBUGA_SKYPE("2 SESSION_REQUEST %s\n", SKYPIAX_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", SKYPIAX_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + if (skypiax_tech_init(tech_pvt, session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", SKYPIAX_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + + if ((tech_pvt->caller_profile = + switch_caller_profile_new(switch_core_session_get_pool(session), "skypiax", + tech_pvt->dialplan, tech_pvt->callid_name, + tech_pvt->callid_number, NULL, NULL, NULL, NULL, "mod_skypiax", tech_pvt->context, tech_pvt->destination)) != 0) { + char name[128]; + //switch_snprintf(name, sizeof(name), "skypiax/%s/%s", tech_pvt->name, tech_pvt->caller_profile->destination_number); + switch_snprintf(name, sizeof(name), "skypiax/%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", SKYPIAX_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + } + if (channel) { + switch_channel_mark_answered(channel); + } + + DEBUGA_SKYPE("new_inbound_channel\n", SKYPIAX_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", SKYPIAX_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_ring_ready(channel); + DEBUGA_SKYPE("skype_call: REMOTE PARTY RINGING\n", SKYPIAX_P_LOG); + } else { + ERRORA("No channel???\n", SKYPIAX_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", SKYPIAX_P_LOG); + //TODO: kill the bastard + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + switch_core_session_add_stream(session, NULL); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + //TODO: kill the bastard + goto done; + } + if (channel) { + switch_channel_mark_pre_answered(channel); + DEBUGA_SKYPE("skype_call: REMOTE PARTY EARLY MEDIA\n", SKYPIAX_P_LOG); + } else { + ERRORA("No channel???\n", SKYPIAX_P_LOG); + //TODO: kill the bastard + } + + 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", SKYPIAX_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No channel???\n", SKYPIAX_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_answered(channel); + //DEBUGA_SKYPE("skype_call: %s, answered\n", SKYPIAX_P_LOG, id); + } else { + ERRORA("No channel???\n", SKYPIAX_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + DEBUGA_SKYPE("outbound_channel_answered!\n", SKYPIAX_P_LOG); + + return 0; +} + +private_t *find_available_skypiax_interface_rr(private_t * tech_pvt_calling) +{ + private_t *tech_pvt = NULL; + int i; + //int num_interfaces = SKYPIAX_MAX_INTERFACES; + //int num_interfaces = globals.real_interfaces; + + 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 < SKYPIAX_MAX_INTERFACES; i++) { + int interface_id; + + interface_id = globals.next_interface; + //interface_id = interface_id < SKYPIAX_MAX_INTERFACES ? interface_id : interface_id - SKYPIAX_MAX_INTERFACES + 1; + globals.next_interface = interface_id + 1 < SKYPIAX_MAX_INTERFACES ? interface_id + 1 : 0; + + if (strlen(globals.SKYPIAX_INTERFACES[interface_id].name)) { + int skype_state = 0; + + tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id]; + skype_state = tech_pvt->interface_state; + //DEBUGA_SKYPE("skype interface: %d, name: %s, state: %d\n", SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].name, skype_state); + if ((tech_pvt_calling ? strcmp(tech_pvt->skype_user, tech_pvt_calling->skype_user) : 1) + && (SKYPIAX_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", SKYPIAX_P_LOG, tech_pvt->name, skype_state, + tech_pvt->skype_callflow); + /*set to Dialing state to avoid other thread fint it, don't know if it is safe */ + //XXX no, it's not safe + if (tech_pvt_calling == NULL) { + tech_pvt->interface_state = SKYPIAX_STATE_SELECTED; + } + + switch_mutex_unlock(globals.mutex); + return tech_pvt; + } + } // else { + //DEBUGA_SKYPE("Skype interface: %d blank!! A hole here means we cannot hunt the last interface.\n", SKYPIAX_P_LOG, interface_id); + //} + } + + 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 < SKYPIAX_MAX_INTERFACES; i++) { + next_flag_char = i == globals.next_interface ? '*' : ' '; + ib += globals.SKYPIAX_INTERFACES[i].ib_calls; + ib_failed += globals.SKYPIAX_INTERFACES[i].ib_failed_calls; + ob += globals.SKYPIAX_INTERFACES[i].ob_calls; + ob_failed += globals.SKYPIAX_INTERFACES[i].ob_failed_calls; + + if (strlen(globals.SKYPIAX_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.SKYPIAX_INTERFACES[i].name, + globals.SKYPIAX_INTERFACES[i].ib_failed_calls, + globals.SKYPIAX_INTERFACES[i].ib_calls, + globals.SKYPIAX_INTERFACES[i].ob_failed_calls, + globals.SKYPIAX_INTERFACES[i].ob_calls, + interface_status[globals.SKYPIAX_INTERFACES[i].interface_state], + skype_callflow[globals.SKYPIAX_INTERFACES[i].skype_callflow], globals.SKYPIAX_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 < SKYPIAX_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.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].name, argv[1], strlen(argv[1])) == 0)) { + globals.sk_console = &globals.SKYPIAX_INTERFACES[i]; + stream->write_function(stream, + "sk console is now: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPIAX_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 Skypiax 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")) { + +/* BEGIN: Changes heres */ + } 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; + } +/* END: Changes heres */ + + } else { + if (globals.sk_console) + skypiax_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(skypiax_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", SKYPIAX_SYNTAX); + goto end; + } + + if (argc < 2) { + stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_SYNTAX); + goto end; + } + + if (argv[0]) { + int i; + int found = 0; + + for (i = 0; !found && i < SKYPIAX_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.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPIAX_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A Skypiax interface with name='%s' was not found\n", argv[0]); + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; + } else { + skypiax_signaling_write(tech_pvt, (char *) &cmd[strlen(argv[0]) + 1]); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_SYNTAX); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +int skypiax_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 < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name)) { + + giovatech = &globals.SKYPIAX_INTERFACES[i]; + //NOTICA("interface=%d, name=%s, giovatech->skype_call_id=%s, giovatech->interface_state=%d, giovatech->skype_user=%s, tech_pvt->skype_user=%s, giovatech->callid_number=%s, value=%s, delta=%ld 500000\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->skype_call_id, giovatech->interface_state, giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value, (((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) ); + if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPIAX_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 != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n", + SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value) + if (tech_pvt->interface_state == SKYPIAX_STATE_PRERING) { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + } else if (tech_pvt->interface_state != 0 && tech_pvt->interface_state != SKYPIAX_STATE_DOWN) { + WARNINGA("Why an interface_state %d HERE?\n", SKYPIAX_P_LOG, tech_pvt->interface_state); + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + } + + break; + } + } + } + + if (found) { + //tech_pvt->callid_number[0]='\0'; + //sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + //skypiax_signaling_write(tech_pvt, msg_to_skype); + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_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); + skypiax_signaling_write(tech_pvt, msg_to_skype); + switch_sleep(10000); + tech_pvt->interface_state = SKYPIAX_STATE_PREANSWER; + sprintf(msg_to_skype, "ALTER CALL %s ANSWER", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + DEBUGA_SKYPE("We answered a Skype RING on skype_call %s\n", SKYPIAX_P_LOG, id); + //FIXME write a timestamp here + 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", + SKYPIAX_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", SKYPIAX_P_LOG); + } else { + DEBUGA_SKYPE("We're in a call now (%s), let's refuse this one (%s)\n", SKYPIAX_P_LOG, tech_pvt->skype_call_id, id); + sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + + switch_mutex_unlock(globals.mutex); + return 0; +} +int skypiax_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 < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name)) { + + giovatech = &globals.SKYPIAX_INTERFACES[i]; + //NOTICA("skype interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->skype_user=%s\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->skype_user); + //FIXME check a timestamp here + if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPIAX_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 != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n", + SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value) + break; + } + } + } + + if (found) { + //tech_pvt->callid_number[0]='\0'; + //sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + //skypiax_signaling_write(tech_pvt, msg_to_skype); + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_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", SKYPIAX_P_LOG, (tech_pvt && tech_pvt->skype_call_id) ? tech_pvt->skype_call_id : ""); + switch_mutex_unlock(globals.mutex); + + } else { + + /* we're owned, 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_skypiax_interface = NULL; + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name)) { + + giovatech = &globals.SKYPIAX_INTERFACES[i]; + //NOTICA("skype interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->skype_user=%s\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->skype_user); + //FIXME check a timestamp here + if (strlen(giovatech->skype_transfer_call_id) && (giovatech->interface_state != SKYPIAX_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 != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->transfer_callid_number=%s == value=%s)\n", + SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->skype_user, tech_pvt->skype_user, giovatech->transfer_callid_number, value) + break; + } + } + } + + if (found) { + //tech_pvt->callid_number[0]='\0'; + //sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + //skypiax_signaling_write(tech_pvt, msg_to_skype); + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_P_LOG); + + available_skypiax_interface = find_available_skypiax_interface_rr(tech_pvt); + if (available_skypiax_interface) { + /* there is a skypiax interface idle, let's transfer the call to it */ + + //FIXME write a timestamp here + 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 skypiax call(%s)\n", + SKYPIAX_P_LOG, tech_pvt->skype_call_id, available_skypiax_interface->name, available_skypiax_interface->skype_user, id); + + //FIXME why this? the inbound call will come, eventually, on that other interface + //available_skypiax_interface->ib_calls++; + + sprintf(msg_to_skype, "ALTER CALL %s TRANSFER %s", id, available_skypiax_interface->skype_user); + skypiax_signaling_write(tech_pvt, msg_to_skype); + if (tech_pvt->interface_state == SKYPIAX_STATE_SELECTED) { + tech_pvt->interface_state = SKYPIAX_STATE_IDLE; //we marked it SKYPIAX_STATE_SELECTED just in case it has to make an outbound call + } + } else { + /* no skypiax interfaces idle, do nothing */ + DEBUGA_SKYPE + ("Not answering the skype_call %s, because we are already in a skypiax call(%s) and not transferring, because no other skypiax interfaces are available\n", + SKYPIAX_P_LOG, id, tech_pvt->skype_call_id); + sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id); + skypiax_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 skypiax call (%s)\n", + SKYPIAX_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_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(skypiax_chat_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + //int tried =0; + int i; + int found = 0; + //char skype_msg[1024]; + + 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", SKYPIAX_CHAT_SYNTAX); + goto end; + } + + if (argc < 3) { + stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_CHAT_SYNTAX); + goto end; + } + + if (argv[0]) { + for (i = 0; !found && i < SKYPIAX_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.SKYPIAX_INTERFACES[i].name) + && (strncmp(globals.SKYPIAX_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPIAX_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A Skypiax interface with name='%s' was not found\n", argv[0]); + goto end; + } else { + + //chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint); + //chat_send(p*roto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint); + //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, hint); + + NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPIAX_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); + + //NOTICA("TEXT is: %s\n", SKYPIAX_P_LOG, (char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1] ); + //snprintf(skype_msg, sizeof(skype_msg), "CHAT CREATE %s", argv[1]); + //skypiax_signaling_write(tech_pvt, skype_msg); + //switch_sleep(100); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_CHAT_SYNTAX); + goto end; + } + +#ifdef NOTDEF + + found = 0; + + while (!found) { + for (i = 0; i < MAX_CHATS; i++) { + if (!strcmp(tech_pvt->chats[i].dialog_partner, argv[1])) { + snprintf(skype_msg, sizeof(skype_msg), "CHATMESSAGE %s %s", tech_pvt->chats[i].chatname, + (char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]); + skypiax_signaling_write(tech_pvt, skype_msg); + found = 1; + break; + } + } + if (found) { + break; + } + if (tried > 1000) { + stream->write_function(stream, "ERROR: no chat with dialog_partner='%s' was found\n", argv[1]); + break; + } + switch_sleep(1000); + } +#endif //NOTDEF + + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/mod/endpoints/mod_skypiax/103/skypiax.h b/src/mod/endpoints/mod_skypiax/103/skypiax.h new file mode 100644 index 0000000000..8ebafa31e9 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/103/skypiax.h @@ -0,0 +1,332 @@ +/* + * 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_skypiax) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * + * mod_skypiax.c -- Skype compatible Endpoint Module + * + */ + +#include +#include + +#ifndef WIN32 +#include +#include +#include +#include +#endif //WIN32 + +#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 "skypiax::incoming_chatmessage" + +#define SAMPLERATE_SKYPIAX 16000 +#define SAMPLES_PER_FRAME SAMPLERATE_SKYPIAX/50 + +#ifndef SKYPIAX_SVN_VERSION +#define SKYPIAX_SVN_VERSION SWITCH_VERSION_REVISION +#endif /* SKYPIAX_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 "SKYPIAX_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 "SKYPIAX_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 "SKYPIAX_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 "SKYPIAX_SVN_VERSION "[%p|%-7lx][ERRORA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define WARNINGA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][WARNINGA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define NOTICA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][NOTICA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); + +#define SKYPIAX_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 SKYPIAX_CAUSE_NORMAL 1 +/*********************************/ +#define SKYPIAX_FRAME_DTMF 1 +/*********************************/ +#define SKYPIAX_CONTROL_RINGING 1 +#define SKYPIAX_CONTROL_ANSWER 2 + +/*********************************/ +#define SKYPIAX_STATE_IDLE 0 +#define SKYPIAX_STATE_DOWN 1 +#define SKYPIAX_STATE_RING 2 +#define SKYPIAX_STATE_DIALING 3 +#define SKYPIAX_STATE_BUSY 4 +#define SKYPIAX_STATE_UP 5 +#define SKYPIAX_STATE_RINGING 6 +#define SKYPIAX_STATE_PRERING 7 +#define SKYPIAX_STATE_ERROR_DOUBLE_CALL 8 +#define SKYPIAX_STATE_SELECTED 9 +#define SKYPIAX_STATE_HANGUP_REQUESTED 10 +#define SKYPIAX_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 SKYPIAX_MAX_INTERFACES 64 + +#ifndef WIN32 +struct SkypiaxHandles { + Window skype_win; + Display *disp; + Window win; + int currentuserhandle; + int api_connected; + int fdesc[2]; +}; +#else //WIN32 + +struct SkypiaxHandles { + 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; + switch_mutex_t *flag_audio_cli_mutex; + switch_mutex_t *flag_audio_srv_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 SkypiaxHandles SkypiaxHandles; + + int interface_state; /*!< \brief 'state' of the interface (channel) */ + char language[80]; /*!< \brief default Asterisk dialplan language for this interface */ + char exten[80]; /*!< \brief default Asterisk dialplan extension for this interface */ + int skypiax_sound_rate; /*!< \brief rate of the sound device, in Hz, eg: 8000 */ + 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; /*!< \brief 'callflow' of the skype interface (as opposed to phone interface) */ + int skype; /*!< \brief config flag, bool, Skype support on this interface (0 if false, -1 if true) */ + int control_to_send; +#ifdef WIN32 + switch_file_t *audiopipe_srv[2]; + switch_file_t *audiopipe_cli[2]; + switch_file_t *skypiax_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ +#else /* WIN32 */ + int audiopipe_srv[2]; + int audiopipe_cli[2]; + int skypiax_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ +#endif /* WIN32 */ + switch_thread_t *tcp_srv_thread; + switch_thread_t *tcp_cli_thread; + switch_thread_t *skypiax_signaling_thread; + switch_thread_t *skypiax_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; + + //int phonebook_listing; + //int phonebook_querying; + //int phonebook_listing_received_calls; + + //int phonebook_first_entry; + //int phonebook_last_entry; + //int phonebook_number_lenght; + //int phonebook_text_lenght; + FILE *phonebook_writing_fp; + int skypiax_dir_entry_extension_prefix; + char 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; +unsigned int writefd; +unsigned int readfd; +}; + +typedef struct private_object private_t; + +void *SWITCH_THREAD_FUNC skypiax_api_thread_func(switch_thread_t * thread, void *obj); +int skypiax_audio_read(private_t * tech_pvt); +int skypiax_audio_init(private_t * tech_pvt); +int skypiax_signaling_write(private_t * tech_pvt, char *msg_to_skype); +int skypiax_signaling_read(private_t * tech_pvt); + +int skypiax_call(private_t * tech_pvt, char *idest, int timeout); +int skypiax_senddigit(private_t * tech_pvt, char digit); + +void *skypiax_do_tcp_srv_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypiax_do_tcp_srv_thread(switch_thread_t * thread, void *obj); + +void *skypiax_do_tcp_cli_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypiax_do_tcp_cli_thread(switch_thread_t * thread, void *obj); + +void *skypiax_do_skypeapi_thread_func(void *obj); +void *SWITCH_THREAD_FUNC skypiax_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 skypiax_signaling_write(private_t * tech_pvt, char *msg_to_skype); +#if defined(WIN32) && !defined(__CYGWIN__) +int skypiax_pipe_read(switch_file_t * pipe, short *buf, int howmany); +int skypiax_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 skypiax_pipe_read(int pipe, short *buf, int howmany); +int skypiax_pipe_write(int pipe, short *buf, int howmany); +#endif /* WIN32 */ +int skypiax_close_socket(unsigned int fd); +private_t *find_available_skypiax_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 skypiax_answer(private_t * tech_pvt, char *id, char *value); +int skypiax_transfer(private_t * tech_pvt, char *id, char *value); +#ifndef WIN32 +int skypiax_socket_create_and_bind(private_t * tech_pvt, int *which_port); +#else +int skypiax_socket_create_and_bind(private_t * tech_pvt, unsigned short *which_port); +#endif //WIN32 +int incoming_chatmessage(private_t * tech_pvt, int which); diff --git a/src/mod/endpoints/mod_skypiax/103/skypiax_protocol.c b/src/mod/endpoints/mod_skypiax/103/skypiax_protocol.c new file mode 100644 index 0000000000..9307156dc8 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/103/skypiax_protocol.c @@ -0,0 +1,1926 @@ +#include "skypiax.h" + +#ifdef ASTERISK +#define skypiax_sleep usleep +#define skypiax_strncpy strncpy +#define tech_pvt p +extern int skypiax_debug; +extern char *skypiax_console_active; +#else /* FREESWITCH */ +#define skypiax_sleep switch_sleep +#define skypiax_strncpy switch_copy_string +extern switch_memory_pool_t *skypiax_module_pool; +extern switch_endpoint_interface_t *skypiax_endpoint_interface; +#endif /* ASTERISK */ +int samplerate_skypiax = SAMPLERATE_SKYPIAX; + +extern int running; + +/*************************************/ +/* suspicious globals FIXME */ +#ifdef WIN32 +DWORD win32_dwThreadId; +#else +XErrorHandler old_handler = 0; +int xerror = 0; +#endif /* WIN32 */ +/*************************************/ +#ifndef WIN32 +int skypiax_socket_create_and_bind(private_t * tech_pvt, int *which_port) +#else +int skypiax_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; +#else + unsigned short start_port = 6001; +#endif //WIN32 + int sockbufsize = 0; + unsigned int size = sizeof(int); + + + 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", SKYPIAX_P_LOG); + return -1; + } + + if (*which_port != 0) + start_port = *which_port; + + my_addr.sin_port = htons(start_port); +/* NONBLOCKING ? */ + fcntl(s, F_SETFL, O_NONBLOCK); + + *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", SKYPIAX_P_LOG, *which_port, tech_pvt->tcp_cli_port, + tech_pvt->tcp_srv_port); + DEBUGA_SKYPE("bind errno=%d, error: %s\n", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG, *which_port, + tech_pvt->tcp_cli_port, tech_pvt->tcp_srv_port); + return -1; + } + } + + DEBUGA_SKYPE("SUCCESS! *which_port=%d, tech_pvt->tcp_cli_port=%d, tech_pvt->tcp_srv_port=%d\n", SKYPIAX_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", SKYPIAX_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", SKYPIAX_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 * 3; +#else + sockbufsize = SAMPLES_PER_FRAME * 8 * 3; +#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", SKYPIAX_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 * 3; +#else + sockbufsize = SAMPLES_PER_FRAME * 8 * 3; +#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", SKYPIAX_P_LOG, sockbufsize, size); + + + + return s; +} + +int skypiax_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 = skypiax_pipe_read(tech_pvt->SkypiaxHandles.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", SKYPIAX_P_LOG, message); + //} + + if (!strcasecmp(message, "ERROR 68")) { + DEBUGA_SKYPE + ("If I don't connect immediately, please give the Skype client authorization to be connected by Skypiax (and to not ask you again)\n", + SKYPIAX_P_LOG); + skypiax_sleep(1000000); + skypiax_signaling_write(tech_pvt, "PROTOCOL 7"); + skypiax_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", SKYPIAX_P_LOG, message); + tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + DEBUGA_SKYPE("skype_call now is DOWN\n", SKYPIAX_P_LOG); + tech_pvt->skype_call_id[0] = '\0'; + + if (tech_pvt->interface_state != SKYPIAX_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPIAX_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", + SKYPIAX_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", SKYPIAX_P_LOG, message); + tech_pvt->interface_state = SKYPIAX_STATE_ERROR_DOUBLE_CALL; + } else if (!strncasecmp(message, "ERROR 592 ALTER CALL", 19)) { + ERRORA("Skype got ERROR about TRANSFERRING, no problem: |||%s|||\n", SKYPIAX_P_LOG, message); + } else if (!strncasecmp(message, "ERROR 559 CALL", 13)) { + DEBUGA_SKYPE("Skype got ERROR about a failed action (probably TRYING to HANGUP A CALL), no problem: |||%s|||\n", SKYPIAX_P_LOG, + message); + } else { + ERRORA("Skype got ERROR: |||%s|||\n", SKYPIAX_P_LOG, message); + tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + ERRORA("skype_call now is DOWN\n", SKYPIAX_P_LOG); + tech_pvt->skype_call_id[0] = '\0'; + + if (tech_pvt->interface_state != SKYPIAX_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + } + } + } + + + + + skypiax_strncpy(message_2, message, sizeof(message) - 1); + buf = message; + stringp = &buf; + where = strsep(stringp, " "); + if (!where) { + WARNINGA("Skype MSG without spaces: %s\n", SKYPIAX_P_LOG, message); + } + + + + + + + + + if (!strcasecmp(message, "CURRENTUSERHANDLE")) { + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + if (!strcasecmp(id, tech_pvt->skype_user)) { + tech_pvt->SkypiaxHandles.currentuserhandle = 1; + DEBUGA_SKYPE + ("Skype MSG: message: %s, currentuserhandle: %s, cuh: %s, skype_user: %s!\n", + SKYPIAX_P_LOG, message, obj, id, tech_pvt->skype_user); + } + } + if (!strcasecmp(message, "USER")) { + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypiax_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", SKYPIAX_P_LOG, message, obj, id, prop); + //TODO: allow authorization based on config param + sprintf(msg_to_skype, "SET USER %s ISAUTHORIZED TRUE", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + } + if (!strcasecmp(message, "MESSAGE")) { + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(prop, where, sizeof(prop) - 1); + if (!strcasecmp(prop, "STATUS")) { + where = strsep(stringp, " "); + skypiax_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", SKYPIAX_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); + skypiax_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", SKYPIAX_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); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + } + if (!strcasecmp(message, "CHAT")) { + char msg_to_skype[256]; + int i; + int found; + + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(prop, where, sizeof(prop) - 1); + skypiax_strncpy(value, *stringp, sizeof(value) - 1); + + if (!strcasecmp(prop, "STATUS") && !strcasecmp(value, "DIALOG")) { + DEBUGA_SKYPE("CHAT %s is DIALOG\n", SKYPIAX_P_LOG, id); + sprintf(msg_to_skype, "GET CHAT %s DIALOG_PARTNER", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + + if (!strcasecmp(prop, "DIALOG_PARTNER")) { + DEBUGA_SKYPE("CHAT %s has DIALOG_PARTNER %s\n", SKYPIAX_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", SKYPIAX_P_LOG, MAX_CHATS); + } + + DEBUGA_SKYPE("CHAT %s is in position %d in the chats array, chatname=%s, dialog_partner=%s\n", SKYPIAX_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; + + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(prop, where, sizeof(prop) - 1); + skypiax_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", SKYPIAX_P_LOG, id); + sprintf(msg_to_skype, "GET CHATMESSAGE %s TYPE", id); + skypiax_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", SKYPIAX_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", SKYPIAX_P_LOG, + MAX_CHATMESSAGES); + } else { + DEBUGA_SKYPE("CHATMESSAGE %s is in position %d in the chatmessages array, type=%s, id=%s\n", SKYPIAX_P_LOG, id, i, + tech_pvt->chatmessages[i].type, tech_pvt->chatmessages[i].id); + sprintf(msg_to_skype, "GET CHATMESSAGE %s CHATNAME", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s FROM_HANDLE", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s FROM_DISPNAME", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(1000); + sprintf(msg_to_skype, "GET CHATMESSAGE %s BODY", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + } + } + + if (!strcasecmp(prop, "CHATNAME")) { + DEBUGA_SKYPE("CHATMESSAGE %s belongs to the CHAT %s\n", SKYPIAX_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", SKYPIAX_P_LOG, id); + } + } + if (!strcasecmp(prop, "FROM_HANDLE")) { + DEBUGA_SKYPE("CHATMESSAGE %s was sent by FROM_HANDLE %s\n", SKYPIAX_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", SKYPIAX_P_LOG, id); + } + + } + if (!strcasecmp(prop, "FROM_DISPNAME")) { + DEBUGA_SKYPE("CHATMESSAGE %s was sent by FROM_DISPNAME %s\n", SKYPIAX_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", SKYPIAX_P_LOG, id); + } + + } + if (!strcasecmp(prop, "BODY")) { + DEBUGA_SKYPE("CHATMESSAGE %s has BODY %s\n", SKYPIAX_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", SKYPIAX_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", + SKYPIAX_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")) { + skypiax_strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + skypiax_strncpy(prop, where, sizeof(prop) - 1); + where = strsep(stringp, " "); + skypiax_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", + //SKYPIAX_P_LOG, message, obj, id, prop, value, where ? where : "NULL"); + + if (!strcasecmp(prop, "PARTNER_HANDLE")) { + if (tech_pvt->interface_state != SKYPIAX_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", SKYPIAX_P_LOG, id); + skypiax_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", SKYPIAX_P_LOG, id); + } else { + skypiax_sleep(400000); //0.4 seconds + DEBUGA_SKYPE("Call %s TRY TRANSFER\n", SKYPIAX_P_LOG, id); + skypiax_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 : ""); + //DEBUGA_SKYPE + //("the skype_call %s caller PARTNER_DISPNAME (tech_pvt->callid_name) is: %s\n", + //SKYPIAX_P_LOG, id, tech_pvt->callid_name); + } + if (!strcasecmp(prop, "CONF_ID") && !strcasecmp(value, "0")) { + //DEBUGA_SKYPE("the skype_call %s is NOT a conference call\n", SKYPIAX_P_LOG, id); + //if (tech_pvt->interface_state == SKYPIAX_STATE_DOWN) + //tech_pvt->interface_state = SKYPIAX_STATE_PRERING; + } + if (!strcasecmp(prop, "CONF_ID") && strcasecmp(value, "0")) { + DEBUGA_SKYPE("the skype_call %s is a conference call\n", SKYPIAX_P_LOG, id); + //if (tech_pvt->interface_state == SKYPIAX_STATE_DOWN) + //tech_pvt->interface_state = SKYPIAX_STATE_PRERING; + } + if (!strcasecmp(prop, "DTMF")) { + DEBUGA_SKYPE("Call %s received a DTMF: %s\n", SKYPIAX_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", SKYPIAX_P_LOG, id); + } + if (!strcasecmp(prop, "DURATION") && (!strcasecmp(value, "1"))) { + if (strcasecmp(id, tech_pvt->skype_call_id)) { + skypiax_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", SKYPIAX_P_LOG, id); + } + } + + if (!strcasecmp(prop, "DURATION") && (tech_pvt->interface_state == SKYPIAX_STATE_ERROR_DOUBLE_CALL)) { + char msg_to_skype[1024]; + skypiax_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", SKYPIAX_P_LOG, id); + sprintf(msg_to_skype, "ALTER CALL %s HANGUP", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(10000); + //return CALLFLOW_INCOMING_HANGUP; + } + + if (!strcasecmp(prop, "STATUS")) { + + if (!strcasecmp(value, "RINGING")) { + char msg_to_skype[1024]; + if ((tech_pvt->interface_state != SKYPIAX_STATE_SELECTED && tech_pvt->interface_state != SKYPIAX_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", SKYPIAX_P_LOG, id); + sprintf(msg_to_skype, "GET CALL %s PARTNER_HANDLE", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_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 = SKYPIAX_STATE_RINGING; + skypiax_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", SKYPIAX_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", + SKYPIAX_P_LOG, id); + sprintf(msg_to_skype, "GET CALL %s PARTNER_HANDLE", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(10000); + } + } + } else if (!strcasecmp(value, "EARLYMEDIA")) { + char msg_to_skype[1024]; + tech_pvt->skype_callflow = CALLFLOW_STATUS_EARLYMEDIA; + tech_pvt->interface_state = SKYPIAX_STATE_DIALING; + DEBUGA_SKYPE("Our remote party in skype_call %s is EARLYMEDIA\n", SKYPIAX_P_LOG, id); + if (tech_pvt->tcp_cli_thread == NULL) { + DEBUGA_SKYPE("START start_audio_threads\n", SKYPIAX_P_LOG); + if (start_audio_threads(tech_pvt)) { + ERRORA("start_audio_threads FAILED\n", SKYPIAX_P_LOG); + return CALLFLOW_INCOMING_HANGUP; + } + } + skypiax_sleep(1000); + sprintf(msg_to_skype, "ALTER CALL %s SET_INPUT PORT=\"%d\"", id, tech_pvt->tcp_cli_port); + skypiax_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); + skypiax_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", SKYPIAX_P_LOG, id); + } else if (!strcasecmp(value, "FINISHED")) { + //DEBUGA_SKYPE("skype_call %s now is DOWN\n", SKYPIAX_P_LOG, id); + if (!strcasecmp(tech_pvt->skype_call_id, id)) { + //tech_pvt->skype_callflow = CALLFLOW_STATUS_FINISHED; + DEBUGA_SKYPE("skype_call %s is MY call, now I'm going DOWN\n", SKYPIAX_P_LOG, id); + //tech_pvt->skype_call_id[0] = '\0'; + if (tech_pvt->interface_state != SKYPIAX_STATE_HANGUP_REQUESTED) { + //tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + } + } else { + DEBUGA_SKYPE("skype_call %s is NOT MY call, ignoring\n", SKYPIAX_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", SKYPIAX_P_LOG, id); + tech_pvt->skype_call_id[0] = '\0'; + if (tech_pvt->interface_state != SKYPIAX_STATE_HANGUP_REQUESTED) { + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + return CALLFLOW_INCOMING_HANGUP; + } else { + tech_pvt->interface_state = SKYPIAX_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", SKYPIAX_P_LOG, id); + tech_pvt->skype_call_id[0] = '\0'; + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPIAX_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", SKYPIAX_P_LOG, id); + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPIAX_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", SKYPIAX_P_LOG, id); + } + } else if (!strcasecmp(value, "TRANSFERRING")) { + DEBUGA_SKYPE("skype_call %s is transferring\n", SKYPIAX_P_LOG, id); + } else if (!strcasecmp(value, "TRANSFERRED")) { + DEBUGA_SKYPE("skype_call %s has been transferred\n", SKYPIAX_P_LOG, id); + } else if (!strcasecmp(value, "ROUTING")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_ROUTING; + tech_pvt->interface_state = SKYPIAX_STATE_DIALING; + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now ROUTING\n", SKYPIAX_P_LOG, id); + } else if (!strcasecmp(value, "UNPLACED")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_UNPLACED; + tech_pvt->interface_state = SKYPIAX_STATE_DIALING; + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now UNPLACED\n", SKYPIAX_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", SKYPIAX_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)) { + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + DEBUGA_SKYPE("skype_call: %s is now active\n", SKYPIAX_P_LOG, id); + + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_EARLYMEDIA) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + tech_pvt->interface_state = SKYPIAX_STATE_UP; + + if (tech_pvt->tcp_cli_thread == NULL) { + DEBUGA_SKYPE("START start_audio_threads\n", SKYPIAX_P_LOG); + if (start_audio_threads(tech_pvt)) { + ERRORA("start_audio_threads FAILED\n", SKYPIAX_P_LOG); + return CALLFLOW_INCOMING_HANGUP; + } + } + skypiax_sleep(1000); //FIXME + sprintf(msg_to_skype, "ALTER CALL %s SET_INPUT PORT=\"%d\"", id, tech_pvt->tcp_cli_port); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(1000); //FIXME + sprintf(msg_to_skype, "#output ALTER CALL %s SET_OUTPUT PORT=\"%d\"", id, tech_pvt->tcp_srv_port); + skypiax_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", SKYPIAX_P_LOG); + new_inbound_channel(tech_pvt); + } else { + tech_pvt->interface_state = SKYPIAX_STATE_UP; + DEBUGA_SKYPE("Outbound Channel Answered! session_uuid_str=%s\n", SKYPIAX_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", SKYPIAX_P_LOG, tech_pvt->skype_call_id, id); + } + } else { + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + DEBUGA_SKYPE("Back from REMOTEHOLD!\n", SKYPIAX_P_LOG); + } + + } else if (!strcasecmp(value, "REMOTEHOLD")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_REMOTEHOLD; + DEBUGA_SKYPE("skype_call: %s is now REMOTEHOLD\n", SKYPIAX_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", + SKYPIAX_P_LOG, id); + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + tech_pvt->skype_call_id[0] = '\0'; + skypiax_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", + SKYPIAX_P_LOG, id); + skypiax_strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + tech_pvt->skype_call_id[0] = '\0'; + skypiax_sleep(1000); + return CALLFLOW_INCOMING_HANGUP; + } else { + WARNINGA("skype_call: %s, STATUS: %s is not recognized\n", SKYPIAX_P_LOG, id, value); + } + } //STATUS + } //CALL + /* the "numbered" messages that follows are used by the directory application, not yet ported */ + if (!strcasecmp(message, "#333")) { + /* DEBUGA_SKYPE("Skype MSG: message_2: %s, message2[11]: %s\n", SKYPIAX_P_LOG, + * message_2, &message_2[11]); */ + memset(tech_pvt->skype_friends, 0, 4096); + skypiax_strncpy(tech_pvt->skype_friends, &message_2[11], 4095); + } + if (!strcasecmp(message, "#222")) { + /* DEBUGA_SKYPE("Skype MSG: message_2: %s, message2[10]: %s\n", SKYPIAX_P_LOG, + * message_2, &message_2[10]); */ + memset(tech_pvt->skype_fullname, 0, 512); + skypiax_strncpy(tech_pvt->skype_fullname, &message_2[10], 511); + } + if (!strcasecmp(message, "#765")) { + /* DEBUGA_SKYPE("Skype MSG: message_2: %s, message2[10]: %s\n", SKYPIAX_P_LOG, + * message_2, &message_2[10]); */ + memset(tech_pvt->skype_displayname, 0, 512); + skypiax_strncpy(tech_pvt->skype_displayname, &message_2[10], 511); + } + a = 0; + } //message end + } //read_from_pipe + return 0; +} + +void *skypiax_do_tcp_srv_thread_func(void *obj) +{ + private_t *tech_pvt = obj; + int s; + //unsigned int len; + //unsigned int i; + //unsigned int a; +#if defined(WIN32) && !defined(__CYGWIN__) + int sin_size; +#else /* WIN32 */ + unsigned int sin_size; +#endif /* WIN32 */ + unsigned int fd; + //short srv_in[SAMPLES_PER_FRAME]; + //short srv_out[SAMPLES_PER_FRAME / 2]; + //struct sockaddr_in my_addr; + struct sockaddr_in remote_addr; + //int exit = 0; + //unsigned int kill_cli_size; + //short kill_cli_buff[SAMPLES_PER_FRAME]; + //short totalbuf[SAMPLES_PER_FRAME]; + int sockbufsize = 0; + unsigned int size = sizeof(int); + + s = skypiax_socket_create_and_bind(tech_pvt, &tech_pvt->tcp_srv_port); + if (s < 0) { + ERRORA("skypiax_socket_create_and_bind error!\n", SKYPIAX_P_LOG); + return NULL; + } + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + + listen(s, 6); + + sin_size = sizeof(remote_addr); + + /****************************/ + while (tech_pvt->interface_state != SKYPIAX_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 == SKYPIAX_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", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG, sockbufsize, size); + + + if (!(running && tech_pvt->running)) + break; + while (tech_pvt->interface_state != SKYPIAX_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 == SKYPIAX_STATE_UP)) { + + tech_pvt->readfd = fd; + //WARNINGA("read HERE\n", SKYPIAX_P_LOG); + skypiax_sleep(100000); + + +#if 0 + + unsigned int fdselect; + int rt; + fd_set fs; + struct timeval to; + + if (!(running && tech_pvt->running)) + break; + //exit = 1; + + fdselect = fd; + FD_ZERO(&fs); + FD_SET(fdselect, &fs); + //to.tv_usec = 2000000; //2000 msec + 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, 320, 0); //seems that Skype only sends 320 bytes at time + } else { + len = 0; + } + + if (len == 320) { + unsigned int howmany; + + if (samplerate_skypiax == 8000) { + /* we're downsampling from 16khz to 8khz, srv_out will contain each other sample from srv_in */ + a = 0; + for (i = 0; i < len / sizeof(short); i++) { + srv_out[a] = srv_in[i]; + i++; + a++; + } + } else if (samplerate_skypiax == 16000) { + /* we're NOT downsampling, srv_out will contain ALL samples from srv_in */ + for (i = 0; i < len / sizeof(short); i++) { + srv_out[i] = srv_in[i]; + } + } else { + ERRORA("SAMPLERATE_SKYPIAX can only be 8000 or 16000\n", SKYPIAX_P_LOG); + } + /* if not yet done, let's store the half incoming frame */ + if (!tech_pvt->audiobuf_is_loaded) { + for (i = 0; i < SAMPLES_PER_FRAME / 2; i++) { + tech_pvt->audiobuf[i] = srv_out[i]; + } + tech_pvt->audiobuf_is_loaded = 1; + } else { + /* we got a stored half frame, build a complete frame in totalbuf using the stored half frame and the current half frame */ + for (i = 0; i < SAMPLES_PER_FRAME / 2; i++) { + totalbuf[i] = tech_pvt->audiobuf[i]; + } + for (a = 0; a < SAMPLES_PER_FRAME / 2; a++) { + totalbuf[i] = srv_out[a]; + i++; + } + /* send the complete frame through the pipe to our code waiting for incoming audio */ + //howmany = skypiax_pipe_write(tech_pvt->audiopipe_srv[1], totalbuf, SAMPLES_PER_FRAME * sizeof(short)); + //FIXME while(tech_pvt->flag_audio_srv == 1){ + //FIXME switch_sleep(100); //1 millisec + //NOTICA("read now is 1\n", SKYPIAX_P_LOG); + //FIXME } + //WARNINGA("read is now 0\n", SKYPIAX_P_LOG); + + + howmany = SAMPLES_PER_FRAME * sizeof(short); + //while (tech_pvt->flag_audio_srv == 1) { + //switch_sleep(1000); //10 millisec + //WARNINGA("read now is 1\n", SKYPIAX_P_LOG); + //} + //if (tech_pvt->flag_audio_srv == 0) { + //switch_mutex_lock(tech_pvt->flag_audio_srv_mutex); + memcpy(tech_pvt->audiobuf_srv, totalbuf, SAMPLES_PER_FRAME * sizeof(short)); + tech_pvt->flag_audio_srv = 1; + //switch_mutex_unlock(tech_pvt->flag_audio_srv_mutex); + //} + //NOTICA("read \n", SKYPIAX_P_LOG); + if (howmany != SAMPLES_PER_FRAME * sizeof(short)) { + ERRORA("howmany is %d, but was expected to be %d\n", SKYPIAX_P_LOG, + howmany, (int) (SAMPLES_PER_FRAME * sizeof(short))); + } + /* done with the stored half frame */ + tech_pvt->audiobuf_is_loaded = 0; + } + + } else if (len == 0) { + skypiax_sleep(1000); + } else { + DEBUGA_SKYPE("len=%d, expected 320\n", SKYPIAX_P_LOG, len); + } + + } else { + if (rt) + ERRORA("SRV rt=%d\n", SKYPIAX_P_LOG, rt); + skypiax_sleep(10000); + } + +#endif// 0 + } + + skypiax_sleep(2000); + DEBUGA_SKYPE("Skype incoming audio GONE\n", SKYPIAX_P_LOG); + skypiax_close_socket(fd); + fd = -1; + break; + } + } + } + + DEBUGA_SKYPE("incoming audio server (I am it) EXITING\n", SKYPIAX_P_LOG); + skypiax_close_socket(s); + s = -1; + return NULL; +} + +void *skypiax_do_tcp_cli_thread_func(void *obj) +{ + private_t *tech_pvt = obj; + int s=0; + //struct sockaddr_in my_addr; + struct sockaddr_in remote_addr; +#if 0 + unsigned int got; + unsigned int len; + unsigned int i; + unsigned int a; + short cli_out[SAMPLES_PER_FRAME * 2]; + short cli_in[SAMPLES_PER_FRAME]; +#endif// 0 + unsigned int fd=0; +#ifdef WIN32 + int sin_size; +#else + unsigned int sin_size; +#endif /* WIN32 */ + int sockbufsize = 0; + unsigned int size = sizeof(int); + + s = skypiax_socket_create_and_bind(tech_pvt, &tech_pvt->tcp_cli_port); + if (s < 0) { + ERRORA("skypiax_socket_create_and_bind error!\n", SKYPIAX_P_LOG); + return NULL; + } + + + + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + + listen(s, 6); + + sin_size = sizeof(remote_addr); + + /****************************/ + while (tech_pvt->interface_state != SKYPIAX_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 == SKYPIAX_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 you send me %d\n", SKYPIAX_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", SKYPIAX_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", SKYPIAX_P_LOG, sockbufsize, size); + + + + if (!(running && tech_pvt->running)) + break; + + + + + while (tech_pvt->interface_state != SKYPIAX_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 == SKYPIAX_STATE_UP)) { + tech_pvt->writefd = fd; + //NOTICA("write HERE\n", SKYPIAX_P_LOG); + skypiax_sleep(100000); +#if 0 +#ifdef NOVARS + unsigned int fdselect; + int rt; + fd_set fs; + struct timeval to; + + if (!(running && tech_pvt->running)) + break; + FD_ZERO(&fs); + to.tv_usec = 120000; //120msec + to.tv_sec = 0; +#if defined(WIN32) && !defined(__CYGWIN__) +/* on win32 we cannot select from the apr "pipe", so we select on socket writability */ + fdselect = fd; + FD_SET(fdselect, &fs); + + //rt = select(fdselect + 1, NULL, &fs, NULL, &to); +#else +/* on *unix and cygwin we select from the real pipe */ + //XXX fdselect = tech_pvt->audiopipe_cli[0]; + //XXX FD_SET(fdselect, &fs); + + //rt = select(fdselect + 1, &fs, NULL, NULL, &to); +#endif + fdselect = fd; + FD_SET(fdselect, &fs); + +#endif// NOVARS +#if 0 + rt = select(fdselect + 1, NULL, &fs, NULL, NULL); + while (tech_pvt->flag_audio_cli == 0) { +#ifdef WIN32 + skypiax_sleep(100); //0.1 millisec +#else + skypiax_sleep(1000); //10 millisec +#endif //WIN32 + NOTICA("write now is 0\n", SKYPIAX_P_LOG); + } + //ERRORA("write is now 1\n", SKYPIAX_P_LOG); + + rt = 1; +#endif //0 + //rt = select(fdselect + 1, NULL, &fs, NULL, NULL); + +#ifdef NOLOOP + if (rt > 0) { + int counter; +#if 0 + while (tech_pvt->flag_audio_cli == 0) { +#ifdef WIN32 + skypiax_sleep(100); //0.1 millisec +#else + skypiax_sleep(10000); //10 millisec +#endif //WIN32 + WARNINGA("write now is 0\n", SKYPIAX_P_LOG); + } +#endif //0 + + /* until we drained the pipe to empty */ + for (counter = 0; counter < 1; counter++) { + /* read from the pipe the audio frame we are supposed to send out */ + //got = skypiax_pipe_read(tech_pvt->audiopipe_cli[0], cli_in, SAMPLES_PER_FRAME * sizeof(short)); + + + got = SAMPLES_PER_FRAME * sizeof(short); + switch_mutex_lock(tech_pvt->flag_audio_cli_mutex); + memcpy(cli_in, tech_pvt->audiobuf_cli, SAMPLES_PER_FRAME * sizeof(short)); + + + + + if (got == -1) + break; + + if (got != SAMPLES_PER_FRAME * sizeof(short)) { + WARNINGA("got is %d, but was expected to be %d\n", SKYPIAX_P_LOG, got, (int) (SAMPLES_PER_FRAME * sizeof(short))); + } + + if (got == SAMPLES_PER_FRAME * sizeof(short)) { + if (samplerate_skypiax == 8000) { + + /* we're upsampling from 8khz to 16khz, cli_out will contain two times each sample from cli_in */ + a = 0; + for (i = 0; i < got / sizeof(short); i++) { + cli_out[a] = cli_in[i]; + a++; + cli_out[a] = cli_in[i]; + a++; + } + got = got * 2; + } else if (samplerate_skypiax == 16000) { + /* we're NOT upsampling, cli_out will contain just ALL samples from cli_in */ + for (i = 0; i < got / sizeof(short); i++) { + cli_out[i] = cli_in[i]; + } + } else { + ERRORA("SAMPLERATE_SKYPIAX can only be 8000 or 16000\n", SKYPIAX_P_LOG); + } + + /* 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, got, 0); + tech_pvt->flag_audio_cli = 0; + //skypiax_sleep(5000); //5 msec + + if (len == -1) { + break; + } else if (len != got) { + ERRORA("len=%d\n", SKYPIAX_P_LOG, len); + skypiax_sleep(1000); + break; + } + } + + switch_mutex_unlock(tech_pvt->flag_audio_cli_mutex); + } else { + + WARNINGA("got is %d, but was expected to be %d\n", SKYPIAX_P_LOG, got, (int) (SAMPLES_PER_FRAME * sizeof(short))); + } + } + } else { + if (rt) + ERRORA("CLI rt=%d\n", SKYPIAX_P_LOG, rt); + memset(cli_out, 0, sizeof(cli_out)); + if (tech_pvt->skype_callflow != CALLFLOW_STATUS_REMOTEHOLD) { + len = send(fd, (char *) cli_out, sizeof(cli_out), 0); + len = send(fd, (char *) cli_out, sizeof(cli_out) / 2, 0); + //WARNINGA("sent %d of zeros to keep the Skype client socket busy\n", SKYPIAX_P_LOG, sizeof(cli_out) + sizeof(cli_out)/2); + } else { + /* + XXX do nothing + */ + //WARNINGA("we don't send it\n", SKYPIAX_P_LOG); + } + skypiax_sleep(1000); + } + +#endif// NOLOOP +#endif// 0 + } + DEBUGA_SKYPE("Skype outbound audio GONE\n", SKYPIAX_P_LOG); + skypiax_close_socket(fd); + fd = -1; + break; + } + } + } + + DEBUGA_SKYPE("outbound audio server (I am it) EXITING\n", SKYPIAX_P_LOG); + skypiax_close_socket(s); + s = -1; + return NULL; +} + +int skypiax_audio_read(private_t * tech_pvt) +{ + unsigned int samples; + +#if 0 + while (tech_pvt->flag_audio_srv == 0) { +#ifdef WIN32 + skypiax_sleep(100); //0.1 millisec +#else + skypiax_sleep(1000); //10 millisec +#endif //WIN32 + + WARNINGA("read now is 0\n", SKYPIAX_P_LOG); + } +#endif //0 + //ERRORA("read is now 1\n", SKYPIAX_P_LOG); + //samples = skypiax_pipe_read(tech_pvt->audiopipe_srv[0], tech_pvt->read_frame.data, SAMPLES_PER_FRAME * sizeof(short)); + samples = SAMPLES_PER_FRAME * sizeof(short); + //switch_mutex_lock(tech_pvt->flag_audio_srv_mutex); + memcpy(tech_pvt->read_frame.data, tech_pvt->audiobuf_srv, SAMPLES_PER_FRAME * sizeof(short)); + tech_pvt->flag_audio_srv = 0; + //switch_mutex_unlock(tech_pvt->flag_audio_srv_mutex); + + if (samples != SAMPLES_PER_FRAME * sizeof(short)) { + if (samples) + WARNINGA("read samples=%u expected=%u\n", SKYPIAX_P_LOG, samples, (int) (SAMPLES_PER_FRAME * sizeof(short))); + return 0; + } else { + /* A real frame */ + tech_pvt->read_frame.datalen = samples; + } + return 1; +} + +int skypiax_senddigit(private_t * tech_pvt, char digit) +{ + char msg_to_skype[1024]; + + DEBUGA_SKYPE("DIGIT received: %c\n", SKYPIAX_P_LOG, digit); + sprintf(msg_to_skype, "SET CALL %s DTMF %c", tech_pvt->skype_call_id, digit); + skypiax_signaling_write(tech_pvt, msg_to_skype); + + return 0; +} + +int skypiax_call(private_t * tech_pvt, char *rdest, int timeout) +{ + char msg_to_skype[1024]; + + //skypiax_sleep(5000); + DEBUGA_SKYPE("Calling Skype, rdest is: %s\n", SKYPIAX_P_LOG, rdest); + //skypiax_signaling_write(tech_pvt, "SET AGC OFF"); + //skypiax_sleep(10000); + //skypiax_signaling_write(tech_pvt, "SET AEC OFF"); + //skypiax_sleep(10000); + + sprintf(msg_to_skype, "CALL %s", rdest); + if (skypiax_signaling_write(tech_pvt, msg_to_skype) < 0) { + ERRORA("failed to communicate with Skype client, now exit\n", SKYPIAX_P_LOG); + return -1; + } + return 0; +} + +/***************************/ +/* PLATFORM SPECIFIC */ +/***************************/ +#if defined(WIN32) && !defined(__CYGWIN__) +int skypiax_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 skypiax_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 skypiax_close_socket(unsigned int fd) +{ + int res; + + res = closesocket(fd); + + return res; +} + +int skypiax_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], skypiax_module_pool); + rv = switch_file_pipe_create(&tech_pvt->audiopipe_cli[0], &tech_pvt->audiopipe_cli[1], skypiax_module_pool); + return 0; +} +#else /* WIN32 */ +int skypiax_pipe_read(int pipe, short *buf, int howmany) +{ + howmany = read(pipe, buf, howmany); + return howmany; +} + +int skypiax_pipe_write(int pipe, short *buf, int howmany) +{ + if (buf) { + howmany = write(pipe, buf, howmany); + return howmany; + } else { + return 0; + } +} + +int skypiax_close_socket(unsigned int fd) +{ + int res; + + res = close(fd); + + return res; +} + +int skypiax_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->skypiax_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 skypiax_signaling_write(private_t * tech_pvt, char *msg_to_skype) +{ + static char acInputRow[1024]; + COPYDATASTRUCT oCopyData; + + DEBUGA_SKYPE("SENDING: |||%s||||\n", SKYPIAX_P_LOG, msg_to_skype); + + sprintf(acInputRow, "%s", msg_to_skype); + DEBUGA_SKYPE("acInputRow: |||%s||||\n", SKYPIAX_P_LOG, acInputRow); + /* send command to skype */ + oCopyData.dwData = 0; + oCopyData.lpData = acInputRow; + oCopyData.cbData = strlen(acInputRow) + 1; + if (oCopyData.cbData != 1) { + if (SendMessage + (tech_pvt->SkypiaxHandles.win32_hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, + (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, (LPARAM) & oCopyData) == FALSE) { + ERRORA("Sending message failed - probably Skype crashed.\n\nPlease shutdown Skypiax, then launch Skypiax and try again.\n", SKYPIAX_P_LOG); + return -1; + } + } + + return 0; + +} + +LRESULT APIENTRY skypiax_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) + 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", SKYPIAX_P_LOG); + break; + case WM_DESTROY: + DEBUGA_SKYPE("got DESTROY\n", SKYPIAX_P_LOG); + tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle = NULL; + PostQuitMessage(0); + break; + case WM_COPYDATA: + if (tech_pvt->SkypiaxHandles.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)); + skypiax_strncpy(msg_from_skype, (const char *) poCopyData->lpData, sizeof(msg_from_skype) - 2); + + howmany = strlen(msg_from_skype) + 1; + howmany = skypiax_pipe_write(tech_pvt->SkypiaxHandles.fdesc[1], (short *) msg_from_skype, howmany); + //DEBUGA_SKYPE("From Skype API: %s\n", SKYPIAX_P_LOG, msg_from_skype); + lReturnCode = 1; + } + break; + default: + if (tech_pvt && tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach) { + if (uiMessage == tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach) { + switch (ulParam) { + case SKYPECONTROLAPI_ATTACH_SUCCESS: + if (!tech_pvt->SkypiaxHandles.currentuserhandle) { + //DEBUGA_SKYPE("\n\n\tConnected to Skype API!\n", SKYPIAX_P_LOG); + tech_pvt->SkypiaxHandles.api_connected = 1; + tech_pvt->SkypiaxHandles.win32_hGlobal_SkypeAPIWindowHandle = (HWND) uiParam; + tech_pvt->SkypiaxHandles.win32_hGlobal_SkypeAPIWindowHandle = tech_pvt->SkypiaxHandles.win32_hGlobal_SkypeAPIWindowHandle; + } + break; + case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION: + //DEBUGA_SKYPE ("\n\n\tIf I do not (almost) immediately connect to Skype API,\n\tplease give the Skype client authorization to be connected \n\tby Asterisk and to not ask you again.\n\n", SKYPIAX_P_LOG); + skypiax_sleep(5000); +#if 0 + if (!tech_pvt->SkypiaxHandles.currentuserhandle) { + SendMessage(HWND_BROADCAST, + tech_pvt->SkypiaxHandles. + win32_uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, 0); + } +#endif + break; + case SKYPECONTROLAPI_ATTACH_REFUSED: + ERRORA("Skype client refused to be connected by Skypiax!\n", SKYPIAX_P_LOG); + break; + case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE: + ERRORA("Skype API not (yet?) available\n", SKYPIAX_P_LOG); + break; + case SKYPECONTROLAPI_ATTACH_API_AVAILABLE: + DEBUGA_SKYPE("Skype API available\n", SKYPIAX_P_LOG); + skypiax_sleep(5000); +#if 0 + if (!tech_pvt->SkypiaxHandles.currentuserhandle) { + SendMessage(HWND_BROADCAST, + tech_pvt->SkypiaxHandles. + win32_uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, 0); + } +#endif + break; + default: + WARNINGA("GOT AN UNKNOWN SKYPE WINDOWS MSG\n", SKYPIAX_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->SkypiaxHandles.win32_hInit_ProcessHandle = (HINSTANCE) OpenProcess(PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId()); + if (tech_pvt->SkypiaxHandles.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->SkypiaxHandles.win32_acInit_WindowClassName, "Skype-API-Skypiax-"); + strcat(tech_pvt->SkypiaxHandles.win32_acInit_WindowClassName, (char *) paucUUIDString); + + oWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + oWindowClass.lpfnWndProc = (WNDPROC) & skypiax_present; + oWindowClass.cbClsExtra = 0; + oWindowClass.cbWndExtra = 0; + oWindowClass.hInstance = tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle; + oWindowClass.hIcon = NULL; + oWindowClass.hCursor = NULL; + oWindowClass.hbrBackground = NULL; + oWindowClass.lpszMenuName = NULL; + oWindowClass.lpszClassName = tech_pvt->SkypiaxHandles.win32_acInit_WindowClassName; + + if (RegisterClass(&oWindowClass) != 0) + fReturnStatus = 1; + + RpcStringFree(&paucUUIDString); + } + } + if (fReturnStatus == 0) + CloseHandle(tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle); + tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle = NULL; + return (fReturnStatus); +} + +void win32_DeInitialize_DestroyWindowClass(private_t * tech_pvt) +{ + UnregisterClass(tech_pvt->SkypiaxHandles.win32_acInit_WindowClassName, tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle); + CloseHandle(tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle); + tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle = NULL; +} + +int win32_Initialize_CreateMainWindow(private_t * tech_pvt) +{ + tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle = + CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, + tech_pvt->SkypiaxHandles.win32_acInit_WindowClassName, "", + WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, + 128, 128, NULL, 0, tech_pvt->SkypiaxHandles.win32_hInit_ProcessHandle, tech_pvt); + return (tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle != NULL ? 1 : 0); +} + +void win32_DeInitialize_DestroyMainWindow(private_t * tech_pvt) +{ + if (tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle != NULL) + DestroyWindow(tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle), tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle = NULL; +} + +void *skypiax_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->SkypiaxHandles.fdesc[0], &tech_pvt->SkypiaxHandles.fdesc[1], skypiax_module_pool); + rv = switch_file_pipe_create(&tech_pvt->SkypiaxHandles.fdesc[0], &tech_pvt->SkypiaxHandles.fdesc[1], skypiax_module_pool); +#else /* WIN32 */ + if (pipe(tech_pvt->SkypiaxHandles.fdesc)) { + fcntl(tech_pvt->SkypiaxHandles.fdesc[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->SkypiaxHandles.fdesc[1], F_SETFL, O_NONBLOCK); + } +#endif /* WIN32 */ + + tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach"); + tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover"); + + skypiax_sleep(200000); //0,2 sec + + if (tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIAttach != 0 + && tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover != 0) { + if (win32_Initialize_CreateWindowClass(tech_pvt)) { + if (win32_Initialize_CreateMainWindow(tech_pvt)) { + if (SendMessage + (HWND_BROADCAST, + tech_pvt->SkypiaxHandles.win32_uiGlobal_MsgID_SkypeControlAPIDiscover, + (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, 0) != 0) { + tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle = tech_pvt->SkypiaxHandles.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); + } + } + + 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", SKYPIAX_P_LOG, xerror); ///FIXME why crash the entire skypiax? just crash the interface, instead + //running = 0; + 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 skypiax_send_message(private_t * tech_pvt, const char *message_P) +{ + struct SkypiaxHandles *SkypiaxHandles = &tech_pvt->SkypiaxHandles; + Window w_P = SkypiaxHandles->skype_win; + Display *disp = SkypiaxHandles->disp; + Window handle_P = SkypiaxHandles->win; + int ok; + //private_t *tech_pvt = NULL; + + + 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(); + //XLockDisplay(disp); + 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", SKYPIAX_P_LOG, xerror); + tech_pvt->running = 0; + return 0; + } + //XUnlockDisplay(disp); + + return 1; +} + +int skypiax_signaling_write(private_t * tech_pvt, char *msg_to_skype) +{ + + DEBUGA_SKYPE("SENDING: |||%s||||\n", SKYPIAX_P_LOG, msg_to_skype); + + + if (!skypiax_send_message(tech_pvt, msg_to_skype)) { + ERRORA + ("Sending message failed - probably Skype crashed.\n\nPlease shutdown Skypiax, then restart Skype, then launch Skypiax and try again.\n", + SKYPIAX_P_LOG); + return -1; + } + + return 0; + +} + +int skypiax_present(struct SkypiaxHandles *SkypiaxHandles) +{ + Atom skype_inst = XInternAtom(SkypiaxHandles->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(); + //XLockDisplay(disp); + status = + XGetWindowProperty(SkypiaxHandles->disp, DefaultRootWindow(SkypiaxHandles->disp), + skype_inst, 0, 1, False, XA_WINDOW, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &prop); + //XUnlockDisplay(disp); + X11_errors_untrap(); + + /* sanity check */ + if (status != Success || format_ret != 32 || nitems_ret != 1) { + SkypiaxHandles->skype_win = (Window) - 1; + DEBUGA_SKYPE("Skype instance not found\n", SKYPIAX_P_LOG); + running = 0; + SkypiaxHandles->api_connected = 0; + return 0; + } + + SkypiaxHandles->skype_win = *(const unsigned long *) prop & 0xffffffff; + DEBUGA_SKYPE("Skype instance found with id #%d\n", SKYPIAX_P_LOG, (unsigned int) SkypiaxHandles->skype_win); + SkypiaxHandles->api_connected = 1; + return 1; +} + +void skypiax_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", SKYPIAX_P_LOG, disp); + close(disp); + DEBUGA_SKYPE("destroyed disp\n", SKYPIAX_P_LOG); + } else { + DEBUGA_SKYPE("NOT destroyed disp\n", SKYPIAX_P_LOG); + } + DEBUGA_SKYPE("OUT destroyed disp\n", SKYPIAX_P_LOG); + skypiax_sleep(1000); +} + +void *skypiax_do_skypeapi_thread_func(void *obj) +{ + + private_t *tech_pvt = obj; + struct SkypiaxHandles *SkypiaxHandles; + 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->SkypiaxHandles.fdesc)) { + fcntl(tech_pvt->SkypiaxHandles.fdesc[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->SkypiaxHandles.fdesc[1], F_SETFL, O_NONBLOCK); + } + SkypiaxHandles = &tech_pvt->SkypiaxHandles; + disp = XOpenDisplay(tech_pvt->X11_display); + if (!disp) { + ERRORA("Cannot open X Display '%s', exiting skype thread\n", SKYPIAX_P_LOG, tech_pvt->X11_display); + running = 0; + return NULL; + } else { + DEBUGA_SKYPE("X Display '%s' opened\n", SKYPIAX_P_LOG, tech_pvt->X11_display); + } + + xfd = XConnectionNumber(disp); + fcntl(xfd, F_SETFD, FD_CLOEXEC); + + SkypiaxHandles->disp = disp; + + if (skypiax_present(SkypiaxHandles)) { + root = DefaultRootWindow(disp); + win = XCreateSimpleWindow(disp, root, 0, 0, 1, 1, 0, BlackPixel(disp, DefaultScreen(disp)), BlackPixel(disp, DefaultScreen(disp))); + + SkypiaxHandles->win = win; + + snprintf(buf, 512, "NAME skypiax"); + + if (!skypiax_send_message(tech_pvt, buf)) { + ERRORA("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypiax again\n", SKYPIAX_P_LOG); + running = 0; + //if(disp) + //XCloseDisplay(disp); + return NULL; + } + + snprintf(buf, 512, "PROTOCOL 7"); + if (!skypiax_send_message(tech_pvt, buf)) { + ERRORA("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypiax again\n", SKYPIAX_P_LOG); + running = 0; + //if(disp) + //XCloseDisplay(disp); + 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) { + skypiax_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'; + + //DEBUGA_SKYPE ("BUF=|||%s|||\n", SKYPIAX_P_LOG, buf); + + if (an_event.xclient.message_type == atom_begin) { + //DEBUGA_SKYPE ("BEGIN BUF=|||%s|||\n", SKYPIAX_P_LOG, buf); + + if (strlen(buffer)) { + unsigned int howmany; + howmany = strlen(b) + 1; + howmany = write(SkypiaxHandles->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", + SKYPIAX_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) { + //DEBUGA_SKYPE ("CONTINUE BUF=|||%s|||\n", SKYPIAX_P_LOG, buf); + + 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", + SKYPIAX_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", + SKYPIAX_P_LOG, buf); + skypiax_sleep(1000000); //1 sec + } + skypiax_sleep(1000); //0.1 msec + break; + } + } + if (continue_is_broken) { + XFlush(disp); + skypiax_sleep(1000); //0.1 msec + continue; + } + //DEBUGA_SKYPE ("i=%d, buffer=|||%s|||\n", SKYPIAX_P_LOG, i, buffer); + strcat(buffer, buf); + //DEBUGA_SKYPE ("i=%d, buffer=|||%s|||\n", SKYPIAX_P_LOG, i, buffer); + strcat(buffer, continuebuffer); + //DEBUGA_SKYPE ("i=%d, buffer=|||%s|||\n", SKYPIAX_P_LOG, i, buffer); + memset(continuebuffer, '\0', 17000); + + if (i < 20 || there_were_continues) { /* last fragment */ + unsigned int howmany; + + howmany = strlen(b) + 1; + + howmany = write(SkypiaxHandles->fdesc[1], b, howmany); + //DEBUGA_SKYPE ("RECEIVED=|||%s|||\n", SKYPIAX_P_LOG, buffer); + memset(buffer, '\0', 17000); + XFlush(disp); + there_were_continues = 0; + } + + skypiax_sleep(1000); //0.1 msec + break; + default: + skypiax_sleep(1000); //0.1 msec + break; + } + } + } + } else { + ERRORA("Skype is not running, maybe crashed. Please run/restart Skype and relaunch Skypiax\n", SKYPIAX_P_LOG); + running = 0; + //if(disp) + //XCloseDisplay(disp); + return NULL; + } + //running = 0; + //if(disp) + //XCloseDisplay(disp); + return NULL; + +} +#endif // WIN32