diff --git a/src/mod/endpoints/mod_skypiax/Makefile b/src/mod/endpoints/mod_skypiax/Makefile new file mode 100644 index 0000000000..fe7a9a46ab --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/Makefile @@ -0,0 +1,6 @@ +MODNAME=mod_skypiax +SVNDEF := -D'SKYPIAX_SVN_VERSION="$(shell svnversion -n .)"' +LOCAL_CFLAGS += $(SVNDEF) +LOCAL_LDFLAGS=-lX11 +LOCAL_OBJS=skypiax_protocol.o +include ../../../../build/modmake.rules diff --git a/src/mod/endpoints/mod_skypiax/README b/src/mod/endpoints/mod_skypiax/README new file mode 100644 index 0000000000..5eb4c2ce24 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/README @@ -0,0 +1,258 @@ +######################################################### +######################################################### +######################################################### + +WHAT IS SKYPIAX + +This software (Skypiax) uses the Skype API but is not endorsed, + certified or otherwise approved in any way by Skype. + +######################################################### +######################################################### +######################################################### + +Skypiax is an endpoint (channel driver) that use the Skype client as an interface to the Skype network, and allows incoming and outgoing Skype calls from/to FreeSWITCH (that can be bridged, originated, answered, etc. as in all other endpoints, eg sofia/SIP). + +Think at Skypiax as similar to OpenZAP for analog lines: for each channel you need an interface (a Skype client). So, for eg, for two concurrent calls, you will need two channels, two Skype clients running on server. + +If your server's Skype client(s) has got the Skype credits, Skypiax works for SkypeOut calls too. + +You can use it from the dialplan, eg with the provided modified "default.xml" dialplan, you can call a "skype uri" that's the word 'skype' followed by a slash and a destination: "skype/remote_skypename__OR__skypeout_phonenumber" for calling via the Skype network. Eg "skype/echo123" or "skype/+18007844444". + +You can use the "skype uri" of the provided dialplan also directly for a SIP softphone, eg X-Lite. + +With the provided skypiax.conf.xml all incoming Skype calls will be routed to the "5000" extension, the IVR in default FreeSWITCH installation. + +On Linux the Skype client uses a lot of CPU. To lower its CPU consumption, you can use the Xvfb "fake" X server and (more important) the snd-dummy ALSA "fake" sound driver. Scripts are provided for this. But for a low number of channels it would works with regular X servers and ALSA drivers. + +On a Linux machine with 3GB ram and a quad core intel6600, we got no problem with 20 concurrent calls, and plenty of room for adding more Skypiax channels (100? not tested). + +On Windows, no need to do anything special, the Skype client is lighter on CPU. + + +''Skypiax is now pre-beta, but usable for testing and finding bugs :-).'' + + +'''You can download Skypiax source code with subversion with the command:''' + +svn co http://svn.freeswitch.org/svn/freeswitch/branches/gmaruzz/src/mod/endpoints/mod_skypiax mod_skypiax +then, follow the README file in the mod_skypiax directory. + +More info on skypiax: +http://wiki.freeswitch.org/wiki/Skypiax +http://www.celliax.org + +Skypiax has been contributed to the community by: +Giovanni Maruzzelli (gmaruzz at gmail dot com) + +######################################################### +######################################################### +######################################################### + + An example of Skypiax installation on FreeSWITCH Linux + +######################################################### +######################################################### +######################################################### + +Install ubuntu 8.04 LTS server (Released April 2008 and maintained until April 2013) with *only* OpenSSH Server. (we used the 64bit edition) + +Login at the real keyboard as the user you choose during install + +Check the IP address with ifconfig + +Logout + +=============================== +Login via ssh as the user you choose during install + +maruzz@8-04-srv:~$ +maruzz@8-04-srv:~$ sudo su +[sudo] password for maruzz: +root@8-04-srv:/home/maruzz# passwd +Enter new UNIX password: +Retype new UNIX password: +passwd: password updated successfully +root@8-04-srv:/home/maruzz# +root@8-04-srv:/home/maruzz# apt-get update && apt-get -y upgrade +root@8-04-srv:/home/maruzz# apt-get update && apt-get -y dist-upgrade +root@8-04-srv:/home/maruzz# reboot + +=============================== +Login as root via ssh + +root@8-04-srv:~# apt-get update && apt-get -y dist-upgrade +root@8-04-srv:/home/maruzz# reboot + +=============================== + +Now, let's begin the real installation + +root@8-04-srv:~# apt-get -y install build-essential subversion automake autoconf wget libtool libncurses5-dev xvfb libx11-dev libasound2-dev +root@8-04-srv:~# svn co http://svn.freeswitch.org/svn/freeswitch/branches/gmaruzz freeswitch_gmaruzz +root@8-04-srv:~# cd freeswitch_gmaruzz/ +root@8-04-srv:~/freeswitch_gmaruzz# ./bootstrap.sh +root@8-04-srv:~/freeswitch_gmaruzz# ./configure +root@8-04-srv:~/freeswitch_gmaruzz# make && make install && make hd-sounds-install && make hd-moh-install && make samples + +*** go to have something to drink-eat-read-whatever +*** test that FS can be started + +root@8-04-srv:~/freeswitch_gmaruzz# /usr/local/freeswitch/bin/freeswitch +freeswitch@8-04-srv> ... + +*** copy the skypiax configuration file +root@8-04-srv:~/freeswitch_gmaruzz# cp src/mod/endpoints/mod_skypiax/configs/skypiax.conf.xml /usr/local/freeswitch/conf/autoload_configs/ + +*** optionally, copy the dialplan configuration file that contains the "skypiax" modifications (eg: you can call "sip:skype/remote_skypename__OR__skypeout_phonenumber" for calling via the Skype network from a SIP softphone to remote_skypename or to a phone number via SkypeOut, or you can call the "2908" extension from any phone to be bridged to the Skype Test Call) +root@8-04-srv:~/freeswitch_gmaruzz# cp src/mod/endpoints/mod_skypiax/configs/default.xml /usr/local/freeswitch/conf/dialplan/ + +*** now, let's get and install the Skype client +root@8-04-srv:~/freeswitch_gmaruzz# cd .. +root@8-04-srv:~# +root@8-04-srv:~# apt-get -y install ia32-libs lib32asound2 libc6-i386 lib32gcc1 lib32stdc++6 lib32ncurses5 lib32z1 libasound2-plugins +root@8-04-srv:~# wget -c http://www.skype.com/go/getskype-linux-ubuntu-amd64 +root@8-04-srv:~# dpkg -i skype_ubuntu-2.0.0.72-1_amd64.deb + +*** now, let's get the skype configuration directory we prepared on another (desktop) machine +*** on "How to prepare the configuration directory of Skype clients on Linux", see configs/README.skypiax_auth +root@8-04-srv:~# cp /mnt/root/configskypenew.tgz ./ +root@8-04-srv:~# tar xzf configskypenew.tgz +root@8-04-srv:~# chown root.root .Skype +root@8-04-srv:~# + +**** almost ready! + +**** let's edit the skypiax config +root@8-04-srv:~/freeswitch_gmaruzz# vi /usr/local/freeswitch/conf/autoload_configs/skypiax.conf.xml + +**** let's edit the startskype script +**** remember to add the removing of all the installed snd-* modules +root@8-04-srv:~/freeswitch_gmaruzz# cp src/mod/endpoints/mod_skypiax/configs/startskype.sh ./ +root@8-04-srv:~/freeswitch_gmaruzz# vi startskype.sh + +**** let's copy the X servers' auth config file +root@8-04-srv:~/freeswitch_gmaruzz#cp src/mod/endpoints/mod_skypiax/configs/skypiax.X.conf /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf + +**** start the X servers and the Skype clients +root@8-04-srv:~/freeswitch_gmaruzz# sh ./startskype.sh + +**** this is the situation you want after startskype.sh +root@8-04-srv:~/freeswitch_gmaruzz# lsmod | grep snd +snd_dummy 22016 0 +snd_pcm 99336 1 snd_dummy +snd_timer 35080 1 snd_pcm +snd 78024 3 snd_dummy,snd_pcm,snd_timer +snd_page_alloc 20368 1 snd_pcm +soundcore 17568 1 snd +root@8-04-srv:~/freeswitch_gmaruzz# + + +**** start FS and the load skypiax! +root@8-04-srv:~/freeswitch_gmaruzz# /usr/local/freeswitch/bin/freeswitch +freeswitch@8-04-srv> load mod_skypiax + + + +============================================= +20 idle skypiax channels: + +top - 18:14:26 up 2:02, 2 users, load average: 0.03, 0.08, 0.04 +Tasks: 111 total, 1 running, 110 sleeping, 0 stopped, 0 zombie +Cpu(s): 0.1%us, 0.3%sy, 0.0%ni, 99.6%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st +Mem: 3096688k total, 1835100k used, 1261588k free, 137688k buffers +Swap: 248968k total, 0k used, 248968k free, 1020888k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 8272 root 20 0 320m 30m 4848 S 0 1.0 0:00.56 freeswitch + 7905 root 20 0 58520 27m 10m S 0 0.9 0:03.25 skype + 7937 root 20 0 59356 26m 10m S 0 0.9 0:03.01 skype + 8093 root 20 0 58524 26m 10m S 0 0.9 0:03.03 skype + 7981 root 20 0 58376 26m 10m S 0 0.9 0:02.72 skype + 8157 root 20 0 59452 26m 10m S 0 0.9 0:02.49 skype + 7889 root 20 0 57888 26m 10m S 0 0.9 0:03.03 skype + 8189 root 20 0 58544 26m 10m S 0 0.9 0:02.62 skype + 7953 root 20 0 58400 26m 10m S 0 0.9 0:03.27 skype + 7997 root 20 0 57988 26m 10m S 1 0.9 0:03.00 skype + 8029 root 20 0 58456 26m 10m S 0 0.9 0:02.57 skype + 8045 root 20 0 58368 26m 10m S 0 0.9 0:02.70 skype + 8077 root 20 0 58444 26m 10m S 0 0.9 0:02.69 skype + 8109 root 20 0 59564 26m 10m S 0 0.9 0:02.75 skype + 8061 root 20 0 57984 26m 10m S 0 0.9 0:02.67 skype + 7921 root 20 0 58388 26m 10m S 0 0.9 0:02.92 skype + 7873 root 20 0 58492 26m 10m S 1 0.9 0:03.83 skype + 8125 root 20 0 58404 26m 10m S 1 0.9 0:02.89 skype + 8013 root 20 0 58460 26m 10m S 0 0.9 0:03.88 skype + 8173 root 20 0 58468 26m 10m S 0 0.9 0:02.55 skype + 8141 root 20 0 58232 26m 10m S 0 0.9 0:02.88 skype + 7884 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.33 Xvfb + 8024 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.30 Xvfb + 8040 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.32 Xvfb + 8072 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.29 Xvfb + 7900 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.32 Xvfb + 7916 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.32 Xvfb + 8120 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.31 Xvfb + 8152 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.29 Xvfb + 8168 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.28 Xvfb + 8184 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.28 Xvfb + 7868 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.31 Xvfb + 7932 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.32 Xvfb + 7948 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.32 Xvfb + 7964 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.33 Xvfb + 7992 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.31 Xvfb + 8008 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.31 Xvfb + 8056 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.30 Xvfb +...some more Xvfb + +============================================= + +20 incoming concurrent skype calls listening to moh at 16khz: + +top - 18:40:04 up 2:27, 2 users, load average: 0.14, 0.13, 0.06 +Tasks: 111 total, 1 running, 110 sleeping, 0 stopped, 0 zombie +Cpu(s): 18.6%us, 4.5%sy, 0.0%ni, 76.4%id, 0.0%wa, 0.1%hi, 0.4%si, 0.0%st +Mem: 3096688k total, 1858460k used, 1238228k free, 137692k buffers +Swap: 248968k total, 0k used, 248968k free, 1023148k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 8866 root 20 0 336m 35m 5016 S 9 1.2 0:21.23 freeswitch + 8812 root 20 0 84632 28m 10m S 6 0.9 0:14.02 skype + 8580 root 20 0 84252 27m 10m S 6 0.9 0:28.19 skype + 8713 root 20 0 84716 27m 10m S 6 0.9 0:18.06 skype + 8628 root 20 0 84268 27m 10m S 7 0.9 0:24.37 skype + 8793 root 20 0 84756 27m 10m S 6 0.9 0:14.24 skype + 8664 root 20 0 84140 27m 10m S 6 0.9 0:23.19 skype + 8745 root 20 0 84628 27m 10m S 6 0.9 0:16.85 skype + 8644 root 20 0 84588 27m 10m S 6 0.9 0:22.81 skype + 8697 root 20 0 84516 27m 10m S 6 0.9 0:21.03 skype + 8514 root 20 0 83164 27m 10m S 7 0.9 0:29.47 skype + 8564 root 20 0 84496 27m 10m S 4 0.9 0:28.65 skype + 8681 root 20 0 84632 27m 10m S 6 0.9 0:23.20 skype + 8532 root 20 0 83528 27m 10m S 6 0.9 0:28.39 skype + 8612 root 20 0 84460 27m 10m S 6 0.9 0:24.70 skype + 8729 root 20 0 84368 27m 10m S 8 0.9 0:18.86 skype + 8761 root 20 0 84644 27m 10m S 6 0.9 0:16.70 skype + 8777 root 20 0 83752 27m 10m S 7 0.9 0:16.19 skype + 8596 root 20 0 83692 27m 10m S 7 0.9 0:26.49 skype + 8831 root 20 0 83568 27m 10m S 7 0.9 0:13.13 skype + 8548 root 20 0 83336 27m 10m S 6 0.9 0:30.67 skype + 8575 root 20 0 43636 9.8m 2232 S 0 0.3 0:00.63 Xvfb + 8543 root 20 0 43636 9.8m 2232 S 0 0.3 0:00.64 Xvfb + 8527 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.65 Xvfb + 8591 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.60 Xvfb + 8658 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.58 Xvfb + 8724 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.64 Xvfb + 8740 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.65 Xvfb + 8509 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.66 Xvfb + 8607 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.61 Xvfb + 8623 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.57 Xvfb + 8692 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.55 Xvfb + 8756 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.55 Xvfb + 8772 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.60 Xvfb + 8788 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.56 Xvfb + 8823 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.59 Xvfb + 8639 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.58 Xvfb + 8676 root 20 0 43624 9.8m 2232 S 0 0.3 0:00.55 Xvfb +...some more Xvfb + + diff --git a/src/mod/endpoints/mod_skypiax/asterisk/Makefile b/src/mod/endpoints/mod_skypiax/asterisk/Makefile new file mode 100644 index 0000000000..86aab9175e --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/Makefile @@ -0,0 +1,93 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Makefile for channel drivers +# +# Copyright (C) 1999-2005, Mark Spencer +# +# Mark Spencer +# +# Edited By Belgarath <> Aug 28 2004 +# Added bare bones ultrasparc-linux support. +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +#ASTERISK INCLUDE FILES +#The directory that contains the Asterisk include files (eg: /usr/include or /usr/include/asterisk or /usr/src/asterisk/include or ...) +#AST_INCLUDE_DIR=/usr/src/asterisk/include +#AST_INCLUDE_DIR=/home/maruzz/devel/svn_asterisk_trunk/include +#AST_INCLUDE_DIR=/home/maruzz/devel/svn_asterisk_branches_160/include +#AST_INCLUDE_DIR=/home/maruzz/devel/svn_asterisk_branches_12/include +#AST_INCLUDE_DIR=/home/maruzz/devel/svn_asterisk_branches_14/include +#AST_INCLUDE_DIR=/home/maruzz/devel/svn_celliax_trunk/asterisk-1.2.rev137401/include +AST_INCLUDE_DIR=/home/user/devel/asterisk-1.4.23.1/include + +#ASTERISK +CFLAGS+=-DASTERISK + +#ASTERISK VERSION +#Uncomment one of the following lines to match your Asterisk series +CFLAGS+=-DASTERISK_VERSION_1_4 +#CFLAGS+=-DASTERISK_VERSION_1_6 +#CFLAGS+=-DASTERISK_VERSION_1_2 + +#LINUX SKYPE SUPPORT (Celliax for Cygwin always supports Skype) +SKYPE_LIB=-L/usr/X11R6/lib -lX11 + +CFLAGS+=-pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations +CFLAGS+=-g3 + + + +CFLAGS+=-I$(AST_INCLUDE_DIR) -I. +CFLAGS+=-D_REENTRANT -D_GNU_SOURCE +#CFLAGS+=-O6 +CFLAGS+=-march=i686 +CFLAGS+=-fomit-frame-pointer +ifeq ($(shell uname -m),x86_64) +CFLAGS+=-fPIC +endif + +SVNDEF := -D'SKYPIAX_SVN_VERSION="$(shell svnversion -n ..)"' +CFLAGS += $(SVNDEF) + + +SOLINK=-shared -Xlinker -x +CHANNEL_LIBS=chan_skypiax.so +CC=gcc + +OSARCH=$(shell uname -s) + +ifeq ($(findstring CYGWIN,$(OSARCH)),CYGWIN) +# definition of pthread_kill as a printf (or as a noop) is required for Asterisk (and skypiax) to run on Cygwin +# without it, each time (often) pthread_kill is called (by any thread, with any signal, URG included), bad things happen +CC=gcc -D pthread_kill=cyg_no_pthreadkill +AST_DLL_DIR=/home/maruzz/devel/svn_asterisk_branches_12 +CYGSOLINK=-Wl,--out-implib=lib$@.a -Wl,--export-all-symbols cyg_no_pthread_kill.o +CYGSOLIB=-L/usr/lib/w32api -lrpcrt4 -L/lib/mingw -lwinmm -L$(AST_DLL_DIR) -lasterisk.dll -L$(AST_DLL_DIR)/res -lres_features.so +SKYPE_LIB= +CHANNEL_LIBS=cyg_no_pthread_kill.o chan_skypiax.so +endif + +all: $(CHANNEL_LIBS) + +clean: + rm -f *.so *.o *.so.a + + +#chan_skypiax section begins + +#to debug threads and lock on 1.4 uncomment the following +#CFLAGS+=-include /usr/src/asterisk/include/asterisk/autoconfig.h + + +cyg_no_pthread_kill.o: cyg_no_pthread_kill.c + $(CC) $(CFLAGS) -c -o cyg_no_pthread_kill.o cyg_no_pthread_kill.c +chan_skypiax.o: chan_skypiax.c + $(CC) $(CFLAGS) -c -o chan_skypiax.o chan_skypiax.c +chan_skypiax.so: chan_skypiax.o skypiax_protocol.o + $(CC) $(SOLINK) -o $@ ${CYGSOLINK} chan_skypiax.o skypiax_protocol.o -lm -ldl $(SKYPE_LIB) ${CYGSOLIB} +#chan_skypiax section ends + diff --git a/src/mod/endpoints/mod_skypiax/asterisk/chan_skypiax.c b/src/mod/endpoints/mod_skypiax/asterisk/chan_skypiax.c new file mode 100644 index 0000000000..954fea5cb4 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/chan_skypiax.c @@ -0,0 +1,2384 @@ +//indent -gnu -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -bbo -nhnl -nut -sob -l90 +#include "skypiax.h" + +/* LOCKS */ +/*! \brief Protect the skypiax_usecnt */ +AST_MUTEX_DEFINE_STATIC(skypiax_usecnt_lock); +/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not + * when it's doing something critical. */ +AST_MUTEX_DEFINE_STATIC(skypiax_monlock); +/*! \brief Protect the interfaces list */ +AST_MUTEX_DEFINE_STATIC(skypiax_iflock); + +/* GLOBAL VARIABLES */ +int running = 1; +int skypiax_dir_entry_extension = 1; //FIXME one var for each interface! +char skypiax_console_active_array[50] = ""; +char *skypiax_console_active = skypiax_console_active_array; +/*! \brief Count of active channels for this module */ +int skypiax_usecnt = 0; +int skypiax_debug = 0; +/*! \brief This is the thread for the monitor which checks for input on the channels + * which are not currently in use. */ +pthread_t skypiax_monitor_thread = AST_PTHREADT_NULL; +pthread_t skypiax_monitor_audio_thread = AST_PTHREADT_NULL; + +/* CONSTANTS */ +/*! \brief Textual description for this module */ +const char skypiax_desc[] = "Skypiax, Skype Driver"; +/*! \brief Textual type for this module */ +const char skypiax_type[] = "Skypiax"; +/*! \brief Name of configuration file for this module */ +const char skypiax_config[] = "skypiax.conf"; + +char skypiax_console_skypiax_usage[] = + " \n" "chan_skypiax commands info\n" " \n" + " chan_skypiax adds to Asterisk the following CLI commands:\n" " \n" + " CLI COMMANDS:\n" " skypiax_hangup\n" " skypiax_dial\n" + " skypiax_console\n" " skypiax_playback_boost\n" + " skypiax_capture_boost\n" " skypiax_skype\n" + " skypiax_dir_import\n" "\n" + " You can type 'help [command]' to obtain more specific info on usage.\n" + " \n"; +char skypiax_console_hangup_usage[] = + "Usage: skypiax_hangup\n" + " Hangs up any call currently placed on the \"current\" skypiax_console (Skypiax) channel.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; +char skypiax_console_playback_boost_usage[] = + "Usage: skypiax_playback_boost [value]\n" + " Shows or set the value of boost applied to the outgoing sound (voice). Possible values are: 0 (no boost applied), -40 to 40 (negative to positive range, in db). Without specifying a value, it just shows the current value. The value is for the \"current\" skypiax_console (Skypiax) channel.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; +char skypiax_console_capture_boost_usage[] = + "Usage: skypiax_capture_boost [value]\n" + " Shows or set the value of boost applied to the incoming sound (voice). Possible values are: 0 (no boost applied), -40 to 40 (negative to positive range, in db). Without specifying a value, it just shows the current value. The value is for the \"current\" skypiax_console (Skypiax) channel.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; + +char skypiax_console_dial_usage[] = + "Usage: skypiax_dial [DTMFs]\n" + " Dials a given DTMF string in the call currently placed on the\n" + " \"current\" skypiax_console (Skypiax) channel.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; + +char skypiax_console_skypiax_console_usage[] = + "Usage: skypiax_console [interface] | [show]\n" + " If used without a parameter, displays which interface is the \"current\"\n" + " skypiax_console. If a device is specified, the \"current\" skypiax_console is changed to\n" + " the interface specified.\n" + " If the parameter is \"show\", the available interfaces are listed\n"; + +char skypiax_console_skype_usage[] = + "Usage: skypiax_skype [command string]\n" + " Send the 'command string' skype_msg to the Skype client connected to the \"current\" skypiax_console (Skypiax) channel.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; + +char skypiax_console_skypiax_dir_import_usage[] = + "Usage: skypiax_dir_import [add | replace]\n" + " Write in the directoriax.conf config file all the entries found in 'Contacts' list of the Skype client connected to the \"current\" skypiax_console.\n" + " You can choose between 'add' to the end of the directoriax.conf file, or 'replace' the whole file with this new content.\n" + " Enter 'help skypiax_console' on how to change the \"current\" skypiax_console\n"; + +/*! \brief Definition of this channel for PBX channel registration */ +const struct ast_channel_tech skypiax_tech = { + .type = skypiax_type, + .description = skypiax_desc, + .capabilities = AST_FORMAT_SLINEAR, + .requester = skypiax_request, + .hangup = skypiax_hangup, + .answer = skypiax_answer, + .read = skypiax_read, + .call = skypiax_originate_call, + .write = skypiax_write, + .indicate = skypiax_indicate, + .fixup = skypiax_fixup, + .devicestate = skypiax_devicestate, +#ifdef ASTERISK_VERSION_1_4 + .send_digit_begin = skypiax_digitsend_begin, + .send_digit_end = skypiax_digitsend_end, +#else /* ASTERISK_VERSION_1_4 */ + .send_digit = skypiax_digitsend, +#endif /* ASTERISK_VERSION_1_4 */ +}; + +/*! \brief fake skypiax_pvt structure values, + * just for logging purposes */ +struct skypiax_pvt skypiax_log_struct = { + .name = "none", +}; + +/*! \brief Default skypiax_pvt structure values, + * used by skypiax_mkif to initialize the interfaces */ +struct skypiax_pvt skypiax_default = { + .interface_state = SKYPIAX_STATE_DOWN, + .skype_callflow = 0, + .context = "default", + .language = "en", + .exten = "s", + .next = NULL, + .owner = NULL, + .controldev_thread = AST_PTHREADT_NULL, + .skypiax_sound_rate = 8000, + .skypiax_sound_capt_fd = -1, + .capture_boost = 0, + .playback_boost = 0, + .stripmsd = 0, + .skype = 0, + .skypiax_dir_entry_extension_prefix = 6, +}; + +/*! + * \brief PVT structure for a skypiax interface (channel), created by skypiax_mkif + */ +struct skypiax_pvt *skypiax_iflist = NULL; + +#ifdef ASTERISK_VERSION_1_6 +struct ast_cli_entry myclis[] = { +/* + * CLI do not works since some time on 1.6, they changed the CLI mechanism + */ +#if 0 + AST_CLI_DEFINE(skypiax_console_hangup, "Hangup a call on the console"), + AST_CLI_DEFINE(skypiax_console_dial, "Dial an extension on the console"), + AST_CLI_DEFINE(skypiax_console_playback_boost, "Sets/displays spk boost in dB"), + AST_CLI_DEFINE(skypiax_console_capture_boost, "Sets/displays mic boost in dB"), + AST_CLI_DEFINE(skypiax_console_set_active, "Sets/displays active console"), + AST_CLI_DEFINE(skypiax_console_skype, "Sends a Skype command"), + AST_CLI_DEFINE(skypiax_console_skypiax_dir_import, "imports entries from cellphone"), + AST_CLI_DEFINE(skypiax_console_skypiax, "all things skypiax"), + #endif +}; +#else +struct ast_cli_entry myclis[] = { + {{"skypiax_hangup", NULL}, skypiax_console_hangup, + "Hangup a call on the skypiax_console", + skypiax_console_hangup_usage}, + {{"skypiax_playback_boost", NULL}, skypiax_console_playback_boost, "playback boost", + skypiax_console_playback_boost_usage}, + {{"skypiax_capture_boost", NULL}, skypiax_console_capture_boost, "capture boost", + skypiax_console_capture_boost_usage}, + {{"skypiax_usage", NULL}, skypiax_console_skypiax, "chan_skypiax commands info", + skypiax_console_skypiax_usage}, + {{"skypiax_skype", NULL}, skypiax_console_skype, "Skype msg", + skypiax_console_skype_usage}, + {{"skypiax_dial", NULL}, skypiax_console_dial, + "Dial an extension on the skypiax_console", + skypiax_console_dial_usage}, + {{"skypiax_console", NULL}, skypiax_console_set_active, + "Sets/displays active skypiax_console", + skypiax_console_skypiax_console_usage}, + {{"skypiax_dir_import", NULL}, skypiax_console_skypiax_dir_import, + "Write the directoriax.conf file, used by directoriax app", + skypiax_console_skypiax_dir_import_usage}, +}; +#endif + +/* IMPLEMENTATION */ + +void skypiax_unlocka_log(void *x) +{ + ast_mutex_t *y; + y = x; + int i; + + for (i = 0; i < 5; i++) { //let's be generous + + ast_log(LOG_DEBUG, + SKYPIAX_SVN_VERSION + "[%-7lx] I'm a dying thread, and I'm to go unlocking mutex %p for the %dth time\n", + (unsigned long int) pthread_self(), y, i); + + ast_mutex_unlock(y); + } + ast_log(LOG_DEBUG, + SKYPIAX_SVN_VERSION + "[%-7lx] I'm a dying thread, I've finished unlocking mutex %p\n", + (unsigned long int) pthread_self(), y); +} + +int skypiax_queue_control(struct ast_channel *c, int control) +{ + struct skypiax_pvt *p = c->tech_pvt; + +/* queue the frame */ + if (p) + p->control_to_send = control; + else { + return 0; + } + DEBUGA_PBX("Queued CONTROL FRAME %d\n", SKYPIAX_P_LOG, control); + +/* wait for the frame to be sent */ + while (p->control_to_send) + usleep(1); + + return 0; +} + +int skypiax_devicestate(void *data) +{ + struct skypiax_pvt *p = NULL; + char *name = data; + int res = AST_DEVICE_INVALID; + + if (!data) { + ERRORA("Devicestate requested with no data\n", SKYPIAX_P_LOG); + return res; + } + + /* lock the interfaces' list */ + LOKKA(&skypiax_iflock); + /* make a pointer to the first interface in the interfaces list */ + p = skypiax_iflist; + /* Search for the requested interface and verify if is unowned */ + while (p) { + size_t length = strlen(p->name); + /* is this the requested interface? */ + if (strncmp(name, p->name, length) == 0) { + /* is this interface unowned? */ + if (!p->owner) { + res = AST_DEVICE_NOT_INUSE; + DEBUGA_PBX("Interface is NOT OWNED by a channel\n", SKYPIAX_P_LOG); + } else { + /* interface owned by a channel */ + res = AST_DEVICE_INUSE; + DEBUGA_PBX("Interface is OWNED by a channel\n", SKYPIAX_P_LOG); + } + + /* we found the requested interface, bail out from the while loop */ + break; + } + /* not yet found, next please */ + p = p->next; + } + /* unlock the interfaces' list */ + UNLOCKA(&skypiax_iflock); + + if (res == AST_DEVICE_INVALID) { + ERRORA("Checking device state for interface [%s] returning AST_DEVICE_INVALID\n", + SKYPIAX_P_LOG, name); + } + return res; +} + +#ifndef ASTERISK_VERSION_1_4 +int skypiax_indicate(struct ast_channel *c, int cond) +#else +int skypiax_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen) +#endif +{ + struct skypiax_pvt *p = c->tech_pvt; + int res = 0; + + NOTICA("Let's INDICATE %d\n", SKYPIAX_P_LOG, cond); + + switch (cond) { + case AST_CONTROL_BUSY: + case AST_CONTROL_CONGESTION: + case AST_CONTROL_RINGING: + case -1: + res = -1; /* Ask for inband indications */ + break; + case AST_CONTROL_PROGRESS: + case AST_CONTROL_PROCEEDING: + case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: +#ifdef ASTERISK_VERSION_1_4 + case AST_CONTROL_SRCUPDATE: +#endif /* ASTERISK_VERSION_1_4 */ + break; + default: + WARNINGA("Don't know how to display condition %d on %s\n", SKYPIAX_P_LOG, cond, + c->name); + /* The core will play inband indications for us if appropriate */ + res = -1; + } + + return res; +} + +/*! \brief PBX interface function -build skypiax pvt structure + * skypiax calls initiated by the PBX arrive here */ +struct ast_channel *skypiax_request(const char *type, int format, void *data, int *cause) +{ + struct skypiax_pvt *p = NULL; + struct ast_channel *tmp = NULL; + char *name = data; + int found = 0; + + DEBUGA_PBX("Try to request type: %s, name: %s, cause: %d," " format: %d\n", + SKYPIAX_P_LOG, type, name, *cause, format); + + if (!data) { + ERRORA("Channel requested with no data\n", SKYPIAX_P_LOG); + return NULL; + } + + /* lock the interfaces' list */ + LOKKA(&skypiax_iflock); + /* make a pointer to the first interface in the interfaces list */ + p = skypiax_iflist; + + if (strncmp("ANY", name, 3) == 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); + p = find_available_skypiax_interface(); + if (p) { + found = 1; + + /* create a new channel owning this interface */ + tmp = skypiax_new(p, SKYPIAX_STATE_DOWN, p->context); + if (!tmp) { + /* the channel was not created, probable memory allocation error */ + *cause = AST_CAUSE_SWITCH_CONGESTION; + } + + } + + } + + /* Search for the requested interface and verify if is unowned and format compatible */ + while (p && !found) { + size_t length = strlen(p->name); + /* is this the requested interface? */ + if (strncmp(name, p->name, length) == 0) { + /* is the requested format supported by this interface? */ + if ((format & AST_FORMAT_SLINEAR) != 0) { + /* is this interface unowned? */ + if (!p->owner) { + DEBUGA_PBX("Requesting: %s, name: %s, format: %d\n", SKYPIAX_P_LOG, type, name, + format); + /* create a new channel owning this interface */ + tmp = skypiax_new(p, SKYPIAX_STATE_DOWN, p->context); + if (!tmp) { + /* the channel was not created, probable memory allocation error */ + *cause = AST_CAUSE_SWITCH_CONGESTION; + } + } else { + /* interface owned by another channel */ + WARNINGA("owned by another channel\n", SKYPIAX_P_LOG); + *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; + } + } else { + /* requested format not supported */ + WARNINGA("format %d not supported\n", SKYPIAX_P_LOG, format); + *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; + } + /* we found the requested interface, bail out from the while loop */ + break; + } + /* not yet found, next please */ + p = p->next; + } + /* unlock the interfaces' list */ + UNLOCKA(&skypiax_iflock); + /* restart the monitor so it will watch only the remaining unowned interfaces */ + skypiax_restart_monitor(); + if (tmp == NULL) { + /* new channel was not created */ + WARNINGA("Unable to create new Skypiax channel %s\n", SKYPIAX_P_LOG, name); + } + /* return the newly created channel */ + return tmp; +} + +/*! \brief Hangup skypiax call + * Part of PBX interface, called from ast_hangup */ + +int skypiax_hangup(struct ast_channel *c) +{ + struct skypiax_pvt *p; + + /* get our skypiax pvt interface from channel */ + p = c->tech_pvt; + /* if there is not skypiax pvt why we are here ? */ + if (!p) { + ERRORA("Asked to hangup channel not connected\n", SKYPIAX_P_LOG); + return 0; + } + + if (p->skype && p->interface_state != SKYPIAX_STATE_DOWN) { + char msg_to_skype[1024]; + p->interface_state = SKYPIAX_STATE_HANGUP_REQUESTED; + DEBUGA_SKYPE("hanging up skype call: %s\n", SKYPIAX_P_LOG, p->skype_call_id); + //sprintf(msg_to_skype, "SET CALL %s STATUS FINISHED", p->skype_call_id); + sprintf(msg_to_skype, "ALTER CALL %s HANGUP", p->skype_call_id); + skypiax_signaling_write(p, msg_to_skype); + } + + while (p->interface_state != SKYPIAX_STATE_DOWN) { + usleep(10000); + } + DEBUGA_SKYPE("Now is really DOWN\n", SKYPIAX_P_LOG); + /* shutdown the serial monitoring thread */ + if (p->controldev_thread && (p->controldev_thread != AST_PTHREADT_NULL) + && (p->controldev_thread != AST_PTHREADT_STOP)) { + if (pthread_cancel(p->controldev_thread)) { + ERRORA("controldev_thread pthread_cancel failed, maybe he killed himself?\n", + SKYPIAX_P_LOG); + } + /* push it, maybe is stuck in a select or so */ + if (pthread_kill(p->controldev_thread, SIGURG)) { + DEBUGA_SERIAL("controldev_thread pthread_kill failed, no problem\n", SKYPIAX_P_LOG); + } +#ifndef __CYGWIN__ /* under cygwin, this seems to be not reliable, get stuck at times */ + /* wait for it to die */ + if (pthread_join(p->controldev_thread, NULL)) { + ERRORA("controldev_thread pthread_join failed, BAD\n", SKYPIAX_P_LOG); + } +#else /* __CYGWIN__ */ +/* allow the serial thread to die */ + usleep(300000); //300msecs +#endif /* __CYGWIN__ */ + } + p->controldev_thread = AST_PTHREADT_NULL; + + p->interface_state = SKYPIAX_STATE_DOWN; + p->skype_callflow = CALLFLOW_CALL_IDLE; + + DEBUGA_PBX("I'll send AST_CONTROL_HANGUP\n", SKYPIAX_P_LOG); + ast_queue_control(p->owner, AST_CONTROL_HANGUP); + DEBUGA_PBX("I've sent AST_CONTROL_HANGUP\n", SKYPIAX_P_LOG); + + /* subtract one to the usage count of Skypiax-type channels */ + LOKKA(&skypiax_usecnt_lock); + skypiax_usecnt--; + if (skypiax_usecnt < 0) + ERRORA("Usecnt < 0???\n", SKYPIAX_P_LOG); + UNLOCKA(&skypiax_usecnt_lock); + ast_update_use_count(); + + /* our skypiax pvt interface is no more part of a channel */ + p->owner = NULL; + /* our channel has no more this skypiax pvt interface to manage */ + c->tech_pvt = NULL; + /* set the channel state to DOWN, eg. available, not in active use */ + if (ast_setstate(c, SKYPIAX_STATE_DOWN)) { + ERRORA("ast_setstate failed, BAD\n", SKYPIAX_P_LOG); + return -1; + } + + /* restart the monitor thread, so it can recheck which interfaces it have to watch during its loop (the interfaces that are not owned by channels) */ + if (skypiax_restart_monitor()) { + ERRORA("skypiax_restart_monitor failed, BAD\n", SKYPIAX_P_LOG); + return -1; + } + + return 0; +} + +/*! \brief Answer incoming call, + * Part of PBX interface */ +int skypiax_answer(struct ast_channel *c) +{ + struct skypiax_pvt *p = c->tech_pvt; + int res; + + /* whle ringing, we just wait, the skype thread will answer */ + while (p->interface_state == SKYPIAX_STATE_RING) { + usleep(10000); //10msec + } + if (p->interface_state != SKYPIAX_STATE_UP) { + ERRORA("call answering failed, we want it to be into interface_state=%d, got %d\n", + SKYPIAX_P_LOG, SKYPIAX_STATE_UP, p->interface_state); + res = -1; + } else { + DEBUGA_PBX("call answered\n", SKYPIAX_P_LOG); + res = 0; + } + return res; +} + +#ifdef ASTERISK_VERSION_1_4 +int skypiax_digitsend_begin(struct ast_channel *c, char digit) +{ + struct skypiax_pvt *p = c->tech_pvt; + + DEBUGA_PBX("DIGIT BEGIN received: %c\n", SKYPIAX_P_LOG, digit); + + return 0; +} + +int skypiax_digitsend_end(struct ast_channel *c, char digit, unsigned int duration) +{ + struct skypiax_pvt *p = c->tech_pvt; + char msg_to_skype[1024]; + + NOTICA("DIGIT END received: %c %d\n", SKYPIAX_P_LOG, digit, duration); + + sprintf(msg_to_skype, "SET CALL %s DTMF %c", p->skype_call_id, digit); + + skypiax_signaling_write(p, msg_to_skype); + + return 0; +} +#else /* ASTERISK_VERSION_1_4 */ +int skypiax_digitsend(struct ast_channel *c, char digit) +{ + struct skypiax_pvt *p = c->tech_pvt; + char msg_to_skype[1024]; + + NOTICA("DIGIT received: %c\n", SKYPIAX_P_LOG, digit); + + sprintf(msg_to_skype, "SET CALL %s DTMF %c", p->skype_call_id, digit); + + skypiax_signaling_write(p, msg_to_skype); + + return 0; +} + +#endif /* ASTERISK_VERSION_1_4 */ +//struct ast_frame *skypiax_audio_read(struct skypiax_pvt *p) +//#define SAMPLES_PER_FRAME 160 +/*! \brief Read audio frames from channel */ +struct ast_frame *skypiax_read(struct ast_channel *c) +{ + struct skypiax_pvt *p = c->tech_pvt; + static struct ast_frame f; + static short __buf[SKYPIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2]; + short *buf; + int samples; + +/* if there are control frames queued to be sent by skypiax_queue_control, send it the first */ +//TODO maybe better a real queue? + if (p && p->owner && p->control_to_send) { + ast_queue_control(p->owner, p->control_to_send); + DEBUGA_PBX("Sent CONTROL FRAME %d\n", SKYPIAX_P_LOG, p->control_to_send); + p->control_to_send = 0; + } + + memset(__buf, '\0', (SKYPIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2)); + + buf = __buf + AST_FRIENDLY_OFFSET / 2; + + f.frametype = AST_FRAME_NULL; + f.subclass = 0; + f.samples = 0; + f.datalen = 0; + f.data = NULL; + f.offset = 0; + f.src = skypiax_type; + f.mallocd = 0; + f.delivery.tv_sec = 0; + f.delivery.tv_usec = 0; + +/* if the call is not active (ie: answered), do not send audio frames, they would pile up in a lag queue */ + if (p->owner && p->owner->_state != SKYPIAX_STATE_UP) { + return &f; + } + + if ((samples = read(p->audiopipe[0], buf, SAMPLES_PER_FRAME * sizeof(short))) != 320) { + DEBUGA_SOUND("read=====> NOT GOOD samples=%d expected=%d\n", SKYPIAX_P_LOG, samples, + SAMPLES_PER_FRAME * sizeof(short)); + usleep(100); + //do nothing + } else { + //DEBUGA_SOUND("read=====> GOOD samples=%d\n", SKYPIAX_P_LOG, samples); + /* A real frame */ + f.frametype = AST_FRAME_VOICE; + f.subclass = AST_FORMAT_SLINEAR; + f.samples = SKYPIAX_FRAME_SIZE; + f.datalen = SKYPIAX_FRAME_SIZE * 2; + f.data = buf; + f.offset = AST_FRIENDLY_OFFSET; + f.src = skypiax_type; + f.mallocd = 0; + + if (p->capture_boost) + skypiax_sound_boost(&f, p->capture_boost); + } + + return &f; +} + +/*! \brief Initiate skypiax call from PBX + * used from the dial() application + */ +int skypiax_originate_call(struct ast_channel *c, char *idest, int timeout) +{ + struct skypiax_pvt *p = NULL; + p = c->tech_pvt; + char rdest[80], *where, dstr[100] = ""; + char *stringp = NULL; + int status; + + if ((c->_state != SKYPIAX_STATE_DOWN) + && (c->_state != SKYPIAX_STATE_RESERVED)) { + ERRORA("skypiax_originate_call called on %s, neither down nor reserved\n", + SKYPIAX_P_LOG, c->name); + return -1; + } + + DEBUGA_PBX("skypiax_originate_call to call idest: %s, timeout: %d!\n", SKYPIAX_P_LOG, + idest, timeout); + + strncpy(rdest, idest, sizeof(rdest) - 1); + stringp = rdest; + strsep(&stringp, "/"); + where = strsep(&stringp, "/"); + if (!where) { + ERRORA("Destination %s requires an actual destination (Skypiax/device/destination)\n", + SKYPIAX_P_LOG, idest); + return -1; + } + + strncpy(dstr, where + p->stripmsd, sizeof(dstr) - 1); + DEBUGA_PBX("skypiax_originate_call dialing idest: %s, timeout: %d, dstr: %s!\n", + SKYPIAX_P_LOG, idest, timeout, dstr); + + strcpy(p->session_uuid_str, "dialing"); + status = skypiax_call(p, dstr, timeout); + if (status) { + WARNINGA("skypiax_originate_call dialing failed: %d!\n", SKYPIAX_P_LOG, status); + return -1; + } + + DEBUGA_PBX("skypiax_originate_call dialed idest: %s, timeout: %d, dstr: %s!\n", + SKYPIAX_P_LOG, idest, timeout, dstr); + + ast_setstate(p->owner, SKYPIAX_STATE_DIALING); + return 0; +} + +int skypiax_sound_boost(struct ast_frame *f, double boost) +{ +/* LUIGI RIZZO's magic */ + if (boost != 0) { /* scale and clip values */ + int i, x; + int16_t *ptr = (int16_t *) f->data; + for (i = 0; i < f->samples; i++) { + x = (ptr[i] * boost) / BOOST_SCALE; + if (x > 32767) { + x = 32767; + } else if (x < -32768) { + x = -32768; + } + ptr[i] = x; + } + } + return 0; +} + +/*! \brief Send audio frame to channel */ +int skypiax_write(struct ast_channel *c, struct ast_frame *f) +{ + struct skypiax_pvt *p = c->tech_pvt; + int sent; + + if (p->owner && p->owner->_state != SKYPIAX_STATE_UP) { + return 0; + } + if (p->playback_boost) + skypiax_sound_boost(f, p->playback_boost); + + sent = write(p->audioskypepipe[1], (short *) f->data, f->datalen); + //skypiax_sound_write(p, f); + + return 0; +} + +/*! \brief Fix up a channel: If a channel is consumed, this is called. + * Basically update any ->owner links */ +int skypiax_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + struct skypiax_pvt *p = newchan->tech_pvt; + + if (!p) { + ERRORA("No pvt after masquerade. Strange things may happen\n", SKYPIAX_P_LOG); + return -1; + } + + if (p->owner != oldchan) { + ERRORA("old channel wasn't %p but was %p\n", SKYPIAX_P_LOG, oldchan, p->owner); + return -1; + } + + p->owner = newchan; + return 0; +} + +struct ast_channel *skypiax_new(struct skypiax_pvt *p, int state, char *context) +{ + struct ast_channel *tmp; + + /* alloc a generic channel struct */ +#ifndef ASTERISK_VERSION_1_4 + tmp = ast_channel_alloc(1); +#else + //tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, ""); + //tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "Skypiax/%s", p->name); + tmp = + ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "Skypiax/%s", p->name); + +#endif /* ASTERISK_VERSION_1_4 */ + if (tmp) { + + /* give a name to the newly created channel */ +#ifndef ASTERISK_VERSION_1_4 + snprintf(tmp->name, sizeof(tmp->name), "Skypiax/%s", p->name); + tmp->type = skypiax_type; +#else /* ASTERISK_VERSION_1_4 */ + ast_string_field_build(tmp, name, "Skypiax/%s", p->name); +#endif /* ASTERISK_VERSION_1_4 */ + + DEBUGA_PBX("new channel: name=%s requested_state=%d\n", SKYPIAX_P_LOG, tmp->name, + state); + + /* fd for the channel to poll for incoming audio */ + tmp->fds[0] = p->skypiax_sound_capt_fd; + + /* audio formats managed */ + tmp->nativeformats = AST_FORMAT_SLINEAR; + tmp->readformat = AST_FORMAT_SLINEAR; + tmp->writeformat = AST_FORMAT_SLINEAR; + /* the technology description (eg. the interface type) of the newly created channel is the Skypiax's one */ + tmp->tech = &skypiax_tech; + /* the technology pvt (eg. the interface) of the newly created channel is this interface pvt */ + tmp->tech_pvt = p; + + /* copy this interface default context, extension, language to the newly created channel */ + if (strlen(p->context)) + strncpy(tmp->context, p->context, sizeof(tmp->context) - 1); + if (strlen(p->exten)) + strncpy(tmp->exten, p->exten, sizeof(tmp->exten) - 1); +#ifndef ASTERISK_VERSION_1_4 + if (strlen(p->language)) + strncpy(tmp->language, p->language, sizeof(tmp->language) - 1); +#else + if (strlen(p->language)) + ast_string_field_set(tmp, language, p->language); +#endif /* ASTERISK_VERSION_1_4 */ + /* copy the requested context (not necessarily the interface default) to the newly created channel */ + if (strlen(context)) + strncpy(tmp->context, context, sizeof(tmp->context) - 1); + + /* copy this interface default callerid in the newly created channel */ + ast_set_callerid(tmp, !ast_strlen_zero(p->callid_number) ? p->callid_number : NULL, + !ast_strlen_zero(p->callid_name) ? p->callid_name : NULL, + !ast_strlen_zero(p->callid_number) ? p->callid_number : NULL); + + DEBUGA_PBX("callid_number=%s, callid_name=%s\n", SKYPIAX_P_LOG, p->callid_number, + p->callid_name); + + /* the owner of this interface pvt is the newly created channel */ + p->owner = tmp; + /* set the newly created channel state to the requested state */ + if (ast_setstate(tmp, state)) { + ERRORA("ast_setstate failed, BAD\n", SKYPIAX_P_LOG); + //ast_dsp_free(p->dsp); + ast_channel_free(tmp); + return NULL; + } + /* if the requested state is different from DOWN, let the pbx manage this interface (now part of the newly created channel) */ + if (state != SKYPIAX_STATE_DOWN) { + DEBUGA_PBX("Try to start PBX on %s, state=%d\n", SKYPIAX_P_LOG, tmp->name, state); + if (ast_pbx_start(tmp)) { + ERRORA("Unable to start PBX on %s\n", SKYPIAX_P_LOG, tmp->name); + ast_channel_free(tmp); + return NULL; + } + } + /* let's start the serial monitoring thread too, so we can have serial signaling */ + if (ast_pthread_create(&p->controldev_thread, NULL, skypiax_do_controldev_thread, p) < + 0) { + ERRORA("Unable to start controldev thread.\n", SKYPIAX_P_LOG); + ast_channel_free(tmp); + tmp = NULL; + } + DEBUGA_SERIAL("STARTED controldev_thread=%lu STOP=%lu NULL=%lu\n", SKYPIAX_P_LOG, + (unsigned long) p->controldev_thread, (unsigned long) AST_PTHREADT_STOP, + (unsigned long) AST_PTHREADT_NULL); + + /* add one to the usage count of Skypiax-type channels */ + LOKKA(&skypiax_usecnt_lock); + skypiax_usecnt++; + UNLOCKA(&skypiax_usecnt_lock); + ast_update_use_count(); + + /* return the newly created channel */ + return tmp; + } + ERRORA("failed memory allocation for Skypiax channel\n", SKYPIAX_P_LOG); + return NULL; +} + +/*! + * \brief Load the module into Asterisk and start its threads + * + * This function register the module into Asterisk, + * create the interfaces for the channels, + * start the auxiliary threads for the interfaces, + * then start a monitor thread. The monitor thread + * will signal Asterisk when an interface receive a call. + * + * + * \return zero on success, -1 on error. + */ +int load_module(void) +{ + int i; + struct ast_config *cfg; + struct skypiax_pvt *tmp; + struct skypiax_pvt *p = NULL; + struct skypiax_pvt *p2 = NULL; +#ifdef ASTERISK_VERSION_1_6 + struct ast_flags config_flags = { 0 }; +#endif /* ASTERISK_VERSION_1_6 */ + +#if defined(WANT_SKYPE_X11) || defined(__CYGWIN__) +#ifndef __CYGWIN__ + if (!XInitThreads()) + ast_log(LOG_ERROR, "Not initialized XInitThreads!\n"); +#endif /* __CYGWIN__ */ +#if 0 + ast_register_atexit(skypiax_disconnect); + ast_register_application(skype2skypiaxapp, skype2skypiax, skype2skypiaxsynopsis, + skype2skypiaxdescrip); + ast_register_application(skypiax2skypeapp, skypiax2skype, skypiax2skypesynopsis, + skypiax2skypedescrip); +#endif +#endif /* defined(WANT_SKYPE_X11) || defined(__CYGWIN__) */ + + /* make sure we can register our channel type with Asterisk */ + i = ast_channel_register(&skypiax_tech); + if (i < 0) { + ERRORA("Unable to register channel type '%s'\n", SKYPIAX_P_LOG, skypiax_type); + return -1; + } + /* load skypiax.conf config file */ +#ifdef ASTERISK_VERSION_1_6 + cfg = ast_config_load(skypiax_config, config_flags); +#else + cfg = ast_config_load(skypiax_config); +#endif /* ASTERISK_VERSION_1_6 */ + if (cfg != NULL) { + char *ctg = NULL; + int is_first_category = 1; + while ((ctg = ast_category_browse(cfg, ctg)) != NULL) { + /* create one interface for each category in skypiax.conf config file, first one set the defaults */ + tmp = skypiax_mkif(cfg, ctg, is_first_category); + if (tmp) { + DEBUGA_PBX + ("Created channel Skypiax: skypiax.conf category '[%s]', channel name '%s'" + "\n", SKYPIAX_P_LOG, ctg, tmp->name); + /* add interface to skypiax_iflist */ + tmp->next = skypiax_iflist; + skypiax_iflist = tmp; + /* next one will not be the first ;) */ + if (is_first_category == 1) { + is_first_category = 0; + skypiax_console_active = tmp->name; + } + } else { + ERRORA("Unable to create channel Skypiax from skypiax.conf category '[%s]'\n", + SKYPIAX_P_LOG, ctg); + /* if error, unload config from memory and return */ + ast_config_destroy(cfg); + ast_channel_unregister(&skypiax_tech); + return -1; + } + /* do it for each category described in config */ + } + + /* we finished, unload config from memory */ + ast_config_destroy(cfg); + } else { + ERRORA("Unable to load skypiax_config skypiax.conf\n", SKYPIAX_P_LOG); + ast_channel_unregister(&skypiax_tech); + return -1; + } +#ifndef ASTERISK_VERSION_1_6 + ast_cli_register_multiple(myclis, sizeof(myclis) / sizeof(struct ast_cli_entry)); +#endif /* ASTERISK_VERSION_1_6 */ + /* start to monitor the interfaces (skypiax_iflist) for the first time */ + if (skypiax_restart_monitor()) { + ERRORA("skypiax_restart_monitor failed, BAD\n", SKYPIAX_P_LOG); + return -1; + } + /* go through the interfaces list (skypiax_iflist) WITHOUT locking */ + p = skypiax_iflist; + while (p) { + int i; + /* for each interface in list */ + p2 = p->next; + NOTICA("STARTING interface %s, please be patient\n", SKYPIAX_P_LOG, p->name); + i = 0; + while (p->SkypiaxHandles.api_connected == 0 && running && i < 60000) { // 60sec FIXME + usleep(1000); + i++; + } + if (p->SkypiaxHandles.api_connected) { + NOTICA("STARTED interface %s\n", SKYPIAX_P_LOG, p->name); + } else { + ERRORA("Interface %s FAILED to start\n", SKYPIAX_P_LOG, p->name); + running = 0; + return -1; + } + /* next one, please */ + p = p2; + } + return 0; +} + +/*! + * \brief Unload the module from Asterisk and shutdown its threads + * + * This function unregister the module from Asterisk, + * destroy the interfaces for the channels, + * shutdown the auxiliary threads for the interfaces, + * then shutdown its monitor thread. + * + * \return zero on success, -1 on error. + */ +int unload_module(void) +{ + struct skypiax_pvt *p = NULL, *p2 = NULL; + int res; + + /* unregister our channel type with Asterisk */ + ast_channel_unregister(&skypiax_tech); + ast_cli_unregister_multiple(myclis, sizeof(myclis) / sizeof(struct ast_cli_entry)); + +#if defined(WANT_SKYPE_X11) || defined(__CYGWIN__) +#ifndef __CYGWIN__ + //FIXME what to do? if (!XInitThreads()) + //FIXME what to do? ast_log(LOG_ERROR, "Not initialized XInitThreads!\n"); +#endif /* __CYGWIN__ */ +#if 0 + ast_unregister_atexit(skypiax_disconnect); + ast_unregister_application(skype2skypiaxapp); + ast_unregister_application(skypiax2skypeapp); +#endif +#endif /* defined(WANT_SKYPE_X11) || defined(__CYGWIN__) */ + + /* lock the skypiax_monlock, kill the monitor thread, unlock the skypiax_monlock */ + LOKKA(&skypiax_monlock); + if (skypiax_monitor_thread && (skypiax_monitor_thread != AST_PTHREADT_NULL) + && (skypiax_monitor_thread != AST_PTHREADT_STOP)) { + if (pthread_cancel(skypiax_monitor_thread)) { + ERRORA("pthread_cancel failed, BAD\n", SKYPIAX_P_LOG); + return -1; + } + if (pthread_kill(skypiax_monitor_thread, SIGURG)) { + DEBUGA_PBX("pthread_kill failed\n", SKYPIAX_P_LOG); //maybe it just died + } +#ifndef __CYGWIN__ /* under cygwin, this seems to be not reliable, get stuck at times */ + if (pthread_join(skypiax_monitor_thread, NULL)) { + ERRORA("pthread_join failed, BAD\n", SKYPIAX_P_LOG); + return -1; + } +#endif /* __CYGWIN__ */ + } + skypiax_monitor_thread = AST_PTHREADT_STOP; + UNLOCKA(&skypiax_monlock); + + if (skypiax_monitor_audio_thread && (skypiax_monitor_audio_thread != AST_PTHREADT_NULL) + && (skypiax_monitor_audio_thread != AST_PTHREADT_STOP)) { + + if (pthread_cancel(skypiax_monitor_audio_thread)) { + ERRORA("pthread_cancel skypiax_monitor_audio_thread failed, BAD\n", SKYPIAX_P_LOG); + } + if (pthread_kill(skypiax_monitor_audio_thread, SIGURG)) { + DEBUGA_PBX("pthread_kill skypiax_monitor_audio_thread failed, no problem\n", SKYPIAX_P_LOG); //maybe it just died + } + + if (pthread_join(skypiax_monitor_audio_thread, NULL)) { + ERRORA("pthread_join failed, BAD\n", SKYPIAX_P_LOG); + } + } + /* lock the skypiax_iflock, and go through the interfaces list (skypiax_iflist) */ + LOKKA(&skypiax_iflock); + p = skypiax_iflist; + while (p) { + /* for each interface in list */ + p2 = p->next; + /* shutdown the sound system, close sound fds, and if exist shutdown the sound managing threads */ + DEBUGA_SOUND("shutting down sound\n", SKYPIAX_P_LOG); + res = skypiax_sound_shutdown(p); + if (res == -1) { + ERRORA("Failed to shutdown sound\n", SKYPIAX_P_LOG); + } +#if 0 + /* if a dsp struct has been allocated, free it */ + if (p->dsp) { + ast_dsp_free(p->dsp); + p->dsp = NULL; + } +#endif + DEBUGA_PBX("freeing PVT\n", SKYPIAX_P_LOG); + /* free the pvt allocated memory */ + free(p); + /* next one, please */ + p = p2; + } + /* finished with the interfaces list, unlock the skypiax_iflock */ + UNLOCKA(&skypiax_iflock); + +#ifdef __CYGWIN__ + NOTICA("Sleping 5 secs, please wait...\n", SKYPIAX_P_LOG); + sleep(5); /* without this pause, for some unknown (to me) reason it crashes on cygwin */ +#endif /* __CYGWIN__ */ + NOTICA("Unloaded Skypiax Module\n", SKYPIAX_P_LOG); + return 0; +} + +/*! + * \brief Return the count of active channels for this module + * + * \return the count of active channels for this module + */ +int usecount() +{ + int res; + static struct skypiax_pvt *p = &skypiax_log_struct; +/* lock the skypiax_usecnt lock */ + LOKKA(&skypiax_usecnt_lock); + /* retrieve the skypiax_usecnt */ + res = skypiax_usecnt; +/* unlock the skypiax_usecnt lock */ + UNLOCKA(&skypiax_usecnt_lock); + /* return the skypiax_usecnt */ + return res; +} + +/*! + * \brief Return the textual description of the module + * + * \return the textual description of the module + */ +char *description() +{ + return (char *) skypiax_desc; +} + +/*! + * \brief Return the ASTERISK_GPL_KEY + * + * \return the ASTERISK_GPL_KEY + */ +char *key() +{ + struct skypiax_pvt *p = NULL; + + NOTICA("Returning Key\n", SKYPIAX_P_LOG); + + return ASTERISK_GPL_KEY; +} + +/*! + * \brief Create and initialize one interface for the module + * \param cfg pointer to configuration data from skypiax.conf + * \param ctg pointer to a category name to be found in cfg + * \param is_first_category is this the first category in cfg + * + * This function create and initialize one interface for the module + * + * \return a pointer to the PVT structure of interface on success, NULL on error. + */ +struct skypiax_pvt *skypiax_mkif(struct ast_config *cfg, char *ctg, int is_first_category) +{ + struct skypiax_pvt *tmp; + struct ast_variable *v; + int res; + + int debug_all = 0; + int debug_at = 0; + int debug_fbus2 = 0; + int debug_serial = 0; + int debug_sound = 0; + int debug_pbx = 0; + int debug_skype = 0; + int debug_call = 0; + int debug_locks = 0; + int debug_monitorlocks = 0; + + ast_log(LOG_DEBUG, "ENTERING FUNC\n"); + /* alloc memory for PVT */ + tmp = malloc(sizeof(struct skypiax_pvt)); + if (tmp == NULL) { /* fail */ + return NULL; + } + /* clear memory for PVT */ + memset(tmp, 0, sizeof(struct skypiax_pvt)); + + /* if we are reading the "first" category of the config file, take SELECTED values as defaults, overriding the values in skypiax_default */ + if (is_first_category == 1) { + /* for each variable in category, copy it in the skypiax_default struct */ + for (v = ast_variable_browse(cfg, ctg); v; v = v->next) { + M_START(v->name, v->value); + + M_STR("context", skypiax_default.context) + M_STR("language", skypiax_default.language) + M_STR("extension", skypiax_default.exten) + M_F("playback_boost", + skypiax_store_boost(v->value, &skypiax_default.playback_boost)) + M_F("capture_boost", + skypiax_store_boost(v->value, &skypiax_default.capture_boost)) + M_UINT("skypiax_dir_entry_extension_prefix", + skypiax_default.skypiax_dir_entry_extension_prefix) + M_END(; + ); + } + } + + /* initialize the newly created PVT from the skypiax_default values */ + *tmp = skypiax_default; + + /* the category name becomes the interface name */ + tmp->name = strdup(ctg); + + /* for each category in config file, "first" included, read in ALL the values */ + for (v = ast_variable_browse(cfg, ctg); v; v = v->next) { + M_START(v->name, v->value); + + M_BOOL("debug_all", debug_all) + M_BOOL("debug_at", debug_at) + M_BOOL("debug_fbus2", debug_fbus2) + M_BOOL("debug_serial", debug_serial) + M_BOOL("debug_sound", debug_sound) + M_BOOL("debug_pbx", debug_pbx) + M_BOOL("debug_skype", debug_skype) + M_BOOL("debug_call", debug_call) + M_BOOL("debug_locks", debug_locks) + M_BOOL("debug_monitorlocks", debug_monitorlocks) + M_BOOL("skype", tmp->skype) + M_STR("context", tmp->context) + M_STR("language", tmp->language) + M_STR("extension", tmp->exten) + M_STR("X11_display", tmp->X11_display) + M_UINT("tcp_cli_port", tmp->tcp_cli_port) + M_UINT("tcp_srv_port", tmp->tcp_srv_port) + M_F("playback_boost", skypiax_store_boost(v->value, &tmp->playback_boost)) + M_F("capture_boost", skypiax_store_boost(v->value, &tmp->capture_boost)) + M_STR("skype_user", tmp->skype_user) + M_UINT("skypiax_dir_entry_extension_prefix", + tmp->skypiax_dir_entry_extension_prefix) + M_END(; + ); + } + + if (debug_all) { + skypiax_debug = skypiax_debug | DEBUG_ALL; + if (!option_debug) { + WARNINGA + ("DEBUG_ALL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_ALL debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_ALL activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_fbus2) { + skypiax_debug = skypiax_debug | DEBUG_FBUS2; + if (!option_debug) { + WARNINGA + ("DEBUG_FBUS2 activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_FBUS2 debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_FBUS2 activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_serial) { + skypiax_debug = skypiax_debug | DEBUG_SERIAL; + if (!option_debug) { + WARNINGA + ("DEBUG_SERIAL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_SERIAL debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_SERIAL activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_sound) { + skypiax_debug = skypiax_debug | DEBUG_SOUND; + if (!option_debug) { + WARNINGA + ("DEBUG_SOUND activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_SOUND debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_SOUND activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_pbx) { + skypiax_debug = skypiax_debug | DEBUG_PBX; + if (!option_debug) { + WARNINGA + ("DEBUG_PBX activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_PBX debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_PBX activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_skype) { + skypiax_debug = skypiax_debug | DEBUG_SKYPE; + if (!option_debug) { + WARNINGA + ("DEBUG_SKYPE activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_SKYPE debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_SKYPE activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_call) { + skypiax_debug = skypiax_debug | DEBUG_CALL; + if (!option_debug) { + WARNINGA + ("DEBUG_CALL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_CALL debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_CALL activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_locks) { + skypiax_debug = skypiax_debug | DEBUG_LOCKS; + if (!option_debug) { + WARNINGA + ("DEBUG_LOCKS activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_LOCKS debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_LOCKS activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (debug_monitorlocks) { + skypiax_debug = skypiax_debug | DEBUG_MONITORLOCKS; + if (!option_debug) { + WARNINGA + ("DEBUG_MONITORLOCKS activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \"set debug 10\" or start Asterisk with \"-dddddddddd\" option for full DEBUG_MONITORLOCKS debugging output.\n", + SKYPIAX_TMP_LOG); + } else { + NOTICA("DEBUG_MONITORLOCKS activated. \n", SKYPIAX_TMP_LOG); + } + } + + if (option_debug > 1) { + DEBUGA_SOUND("playback_boost is %f\n", SKYPIAX_TMP_LOG, tmp->playback_boost); + DEBUGA_SOUND("capture_boost is %f\n", SKYPIAX_TMP_LOG, tmp->capture_boost); + } +/* initialize the soundcard channels (input and output) used by this interface (a multichannel soundcard can be used by multiple interfaces), optionally starting the sound managing threads */ + res = skypiax_sound_init(tmp); + if (res == -1) { + ERRORA("Failed initializing sound device\n", SKYPIAX_TMP_LOG); + /* we failed, free the PVT */ + free(tmp); + return NULL; + } + /* + res = pipe(tmp->SkypiaxHandles.fdesc); + if (res) { + ast_log(LOG_ERROR, "Unable to create skype pipe\n"); + if (option_debug > 10) { + DEBUGA_PBX("EXITING FUNC\n", SKYPIAX_TMP_LOG); + } + free(tmp); + return NULL; + } + fcntl(tmp->SkypiaxHandles.fdesc[0], F_SETFL, O_NONBLOCK); + fcntl(tmp->SkypiaxHandles.fdesc[1], F_SETFL, O_NONBLOCK); + */ + tmp->skype_thread = AST_PTHREADT_NULL; + + if (tmp->skype) { + ast_log(LOG_DEBUG, "TO BE started skype_thread=%lu STOP=%lu NULL=%lu\n", + (unsigned long) tmp->skype_thread, (unsigned long) AST_PTHREADT_STOP, + (unsigned long) AST_PTHREADT_NULL); +#ifdef __CYGWIN__ + if (ast_pthread_create(&tmp->skype_thread, NULL, do_skypeapi_thread, tmp) < 0) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + free(tmp); + return NULL; + } +#else /* __CYGWIN__ */ +#ifdef WANT_SKYPE_X11 + ast_log(LOG_DEBUG, "AsteriskHandlesfd: %d\n", tmp->SkypiaxHandles.fdesc[1]); + if (ast_pthread_create(&tmp->skype_thread, NULL, do_skypeapi_thread, tmp) < 0) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + free(tmp); + return NULL; + } +#endif /* WANT_SKYPE_X11 */ +#endif /* __CYGWIN__ */ + usleep(100000); //0.1 sec + if (tmp->skype_thread == AST_PTHREADT_NULL) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + free(tmp); + return NULL; + } + ast_log(LOG_DEBUG, "STARTED skype_thread=%lu STOP=%lu NULL=%lu\n", + (unsigned long) tmp->skype_thread, (unsigned long) AST_PTHREADT_STOP, + (unsigned long) AST_PTHREADT_NULL); + } +#if 0 + if (tmp->skype) { +#if 0 + if (option_debug > 1) + ast_log(LOG_DEBUG, "TO BE started skype_thread=%lu STOP=%lu NULL=%lu\n", + (unsigned long) tmp->skype_thread, (unsigned long) AST_PTHREADT_STOP, + (unsigned long) AST_PTHREADT_NULL); +#endif +#ifdef __CYGWIN__ + if (ast_pthread_create + (&tmp->skype_thread, NULL, do_skypeapi_thread, &tmp->SkypiaxHandles) < 0) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + if (option_debug > 10) { + DEBUGA_PBX("EXITING FUNC\n", SKYPIAX_TMP_LOG); + } + free(tmp); + return NULL; + } +#else /* __CYGWIN__ */ +#ifdef WANT_SKYPE_X11 + if (ast_pthread_create(&tmp->signaling_thread, NULL, do_signaling_thread_fnc, tmp) < + 0) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + if (option_debug > 10) { + DEBUGA_PBX("EXITING FUNC\n", SKYPIAX_TMP_LOG); + } + free(tmp); + return NULL; + } +#endif /* WANT_SKYPE_X11 */ +#endif /* __CYGWIN__ */ + usleep(100000); //0.1 sec + if (tmp->skype_thread == AST_PTHREADT_NULL) { + ast_log(LOG_ERROR, "Unable to start skype_main thread.\n"); + if (option_debug > 10) { + DEBUGA_PBX("EXITING FUNC\n", SKYPIAX_TMP_LOG); + } + free(tmp); + return NULL; + } + if (option_debug > 1) + ast_log(LOG_DEBUG, "STARTED signaling_thread=%lu STOP=%lu NULL=%lu\n", + (unsigned long) tmp->signaling_thread, (unsigned long) AST_PTHREADT_STOP, + (unsigned long) AST_PTHREADT_NULL); + } +#endif + + /* return the newly created skypiax_pvt */ + return tmp; +} + +/*! \brief (Re)Start the module main monitor thread, watching for incoming calls on the interfaces */ +int skypiax_restart_monitor(void) +{ + static struct skypiax_pvt *p = &skypiax_log_struct; + + /* If we're supposed to be stopped -- stay stopped */ + if (skypiax_monitor_thread == AST_PTHREADT_STOP) { + return 0; + } + LOKKA(&skypiax_monlock); + /* Do not seems possible to me that this function can be called by the very same monitor thread, but let's be paranoid */ + if (skypiax_monitor_thread == pthread_self()) { + UNLOCKA(&skypiax_monlock); + ERRORA("Cannot kill myself\n", SKYPIAX_P_LOG); + return -1; + } + /* if the monitor thread exists */ + if (skypiax_monitor_thread != AST_PTHREADT_NULL) { + /* Wake up the thread, it can be stuck waiting in a select or so */ + pthread_kill(skypiax_monitor_thread, SIGURG); + } else { + /* the monitor thread does not exists, start a new monitor */ + if (ast_pthread_create(&skypiax_monitor_thread, NULL, skypiax_do_monitor, NULL) < 0) { + UNLOCKA(&skypiax_monlock); + ERRORA("Unable to start monitor thread.\n", SKYPIAX_P_LOG); + return -1; + } + } + UNLOCKA(&skypiax_monlock); + return 0; +} + +/*! \brief The skypiax monitoring thread + * \note This thread monitors all the skypiax interfaces that are not in a call + * (and thus do not have a separate thread) indefinitely + * */ +void *skypiax_do_monitor(void *data) +{ + fd_set rfds; + int res; + struct skypiax_pvt *p = NULL; + int max = -1; + struct timeval to; + time_t now_timestamp; + + if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) { + ERRORA("Unable to set cancel type to deferred\n", SKYPIAX_P_LOG); + return NULL; + } + + for (;;) { + pthread_testcancel(); + /* Don't let anybody kill us right away. Nobody should lock the interface list + and wait for the monitor list, but the other way around is okay. */ + PUSHA_UNLOCKA(&skypiax_monlock); + MONITORLOKKA(&skypiax_monlock); + /* Lock the interface list */ + PUSHA_UNLOCKA(&skypiax_iflock); + MONITORLOKKA(&skypiax_iflock); + /* Build the stuff we're going to select on, that is the skypiax_serial_fd of every + skypiax_pvt that does not have an associated owner channel. In the case of FBUS2 3310 + and in the case of PROTOCOL_NO_SERIAL we add the audio_fd as well, because there is not serial signaling of incoming calls */ + FD_ZERO(&rfds); + + time(&now_timestamp); + p = skypiax_iflist; + while (p) { + if (!p->owner) { + /* This interface needs to be watched, as it lacks an owner */ + + if (p->skype) { + if (FD_ISSET(p->SkypiaxHandles.fdesc[0], &rfds)) + ERRORA("Descriptor %d (SkypiaxHandles.fdesc[0]) appears twice ?\n", + SKYPIAX_P_LOG, p->SkypiaxHandles.fdesc[0]); + + if (p->SkypiaxHandles.fdesc[0] > 0) { + FD_SET(p->SkypiaxHandles.fdesc[0], &rfds); + if (p->SkypiaxHandles.fdesc[0] > max) + max = p->SkypiaxHandles.fdesc[0]; + + } + } + + } + /* next interface, please */ + p = p->next; + } + /* Okay, now that we know what to do, release the interface lock */ + MONITORUNLOCKA(&skypiax_iflock); + POPPA_UNLOCKA(&skypiax_iflock); + /* And from now on, we're okay to be killed, so release the monitor lock as well */ + MONITORUNLOCKA(&skypiax_monlock); + POPPA_UNLOCKA(&skypiax_monlock); + + /* you want me to die? */ + pthread_testcancel(); + + /* Wait for something to happen */ + to.tv_sec = 0; + to.tv_usec = 500000; /* we select with this timeout because under cygwin we avoid the signal usage, so there is no way to end the thread if it is stuck waiting for select */ + res = ast_select(max + 1, &rfds, NULL, NULL, &to); + + /* you want me to die? */ + pthread_testcancel(); + + /* Okay, select has finished. Let's see what happened. */ + + /* If there are errors... */ + if (res < 0) { + if (errno == EINTR) /* EINTR is just the select + being interrupted by a SIGURG, or so */ + continue; + else { + ERRORA("select returned %d: %s\n", SKYPIAX_P_LOG, res, strerror(errno)); + return NULL; + } + } + + /* must not be killed while skypiax_iflist is locked */ + PUSHA_UNLOCKA(&skypiax_monlock); + MONITORLOKKA(&skypiax_monlock); + /* Alright, lock the interface list again, and let's look and see what has + happened */ + PUSHA_UNLOCKA(&skypiax_iflock); + MONITORLOKKA(&skypiax_iflock); + + p = skypiax_iflist; + for (; p; p = p->next) { + + if (p->skype) { + if (FD_ISSET(p->SkypiaxHandles.fdesc[0], &rfds)) { + res = skypiax_signaling_read(p); + if (res == CALLFLOW_INCOMING_CALLID || res == CALLFLOW_INCOMING_RING) { + //ast_log(LOG_NOTICE, "CALLFLOW_INCOMING_RING SKYPE\n"); + DEBUGA_SKYPE("CALLFLOW_INCOMING_RING\n", SKYPIAX_P_LOG); + skypiax_new(p, SKYPIAX_STATE_RING, p->context /* p->context */ ); + } + } + } + + } + MONITORUNLOCKA(&skypiax_iflock); + POPPA_UNLOCKA(&skypiax_iflock); + MONITORUNLOCKA(&skypiax_monlock); + POPPA_UNLOCKA(&skypiax_monlock); + pthread_testcancel(); + } +/* Never reached */ + return NULL; + +} + +/*! + * \brief Initialize the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces) + * \param p the skypiax_pvt of the interface + * + * This function initialize the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces). It simply pass its parameters to the right function for the sound system for which has been compiled, eg. alsa_init for ALSA, oss_init for OSS, winmm_init for Windows Multimedia, etc and return the result + * + * \return zero on success, -1 on error. + */ + +int skypiax_sound_init(struct skypiax_pvt *p) +{ + return skypiax_audio_init(p); +} + +/*! + * \brief Shutdown the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces) + * \param p the skypiax_pvt of the interface + * + * This function shutdown the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces). It simply pass its parameters to the right function for the sound system for which has been compiled, eg. alsa_shutdown for ALSA, oss_shutdown for OSS, winmm_shutdown for Windows Multimedia, etc and return the result + * + * \return zero on success, -1 on error. + */ + +int skypiax_sound_shutdown(struct skypiax_pvt *p) +{ + + //return skypiax_portaudio_shutdown(p); + + return -1; +} + +/*! \brief Read audio frames from interface */ +struct ast_frame *skypiax_sound_read(struct skypiax_pvt *p) +{ + struct ast_frame *f = NULL; + int res; + + res = skypiax_audio_read(p); + f = &p->read_frame; + return f; +} + +/*! \brief Send audio frame to interface */ +int skypiax_sound_write(struct skypiax_pvt *p, struct ast_frame *f) +{ + int ret = -1; + + ret = skypiax_audio_write(p, f); + return ret; +} + +/*! + * \brief This thread runs during a call, and monitor the interface serial port for signaling, like hangup, caller id, etc + * + */ +void *skypiax_do_controldev_thread(void *data) +{ + struct skypiax_pvt *p = data; + int res; + + DEBUGA_SERIAL("In skypiax_do_controldev_thread: started, p=%p\n", SKYPIAX_P_LOG, p); + + if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) { + ERRORA("Unable to set cancel type to deferred\n", SKYPIAX_P_LOG); + return NULL; + } + + while (1) { + usleep(1000); + pthread_testcancel(); + if (p->skype) { + res = skypiax_signaling_read(p); + if (res == CALLFLOW_INCOMING_HANGUP) { + DEBUGA_SKYPE("skype call ended\n", SKYPIAX_P_LOG); + if (p->owner) { + pthread_testcancel(); + ast_queue_control(p->owner, AST_CONTROL_HANGUP); + } + } + } + } + + return NULL; + +} + +/************************************************/ + +/* LUIGI RIZZO's magic */ +/* + * store the boost factor + */ +#ifdef ASTERISK_VERSION_1_6 +void skypiax_store_boost(const char *s, double *boost) +#else +void skypiax_store_boost(char *s, double *boost) +#endif /* ASTERISK_VERSION_1_6 */ +{ + struct skypiax_pvt *p = NULL; + + if (sscanf(s, "%lf", boost) != 1) { + ERRORA("invalid boost <%s>\n", SKYPIAX_P_LOG, s); + return; + } + if (*boost < -BOOST_MAX) { + WARNINGA("boost %s too small, using %d\n", SKYPIAX_P_LOG, s, -BOOST_MAX); + *boost = -BOOST_MAX; + } else if (*boost > BOOST_MAX) { + WARNINGA("boost %s too large, using %d\n", SKYPIAX_P_LOG, s, BOOST_MAX); + *boost = BOOST_MAX; + } + *boost = exp(log(10) * *boost / 20) * BOOST_SCALE; + DEBUGA_SOUND("setting boost %s to %f\n", SKYPIAX_P_LOG, s, *boost); +} + +/* + * returns a pointer to the descriptor with the given name + */ +struct skypiax_pvt *skypiax_console_find_desc(char *dev) +{ + struct skypiax_pvt *p = NULL; + + for (p = skypiax_iflist; p && strcmp(p->name, dev) != 0; p = p->next); + if (p == NULL) + WARNINGA("could not find <%s>\n", SKYPIAX_P_LOG, dev); + + return p; +} +int skypiax_console_playback_boost(int fd, int argc, char *argv[]) +{ + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + + if (argc > 2) { + return RESULT_SHOWUSAGE; + } + if (!p) { + ast_cli(fd, + "No \"current\" skypiax_console for playback_boost, please enter 'help skypiax_console'\n"); + return RESULT_SUCCESS; + } + + if (argc == 1) { + ast_cli(fd, "playback_boost on the active skypiax_console, that is [%s], is: %5.1f\n", + skypiax_console_active, + 20 * log10(((double) p->playback_boost / (double) BOOST_SCALE))); + } else if (argc == 2) { + skypiax_store_boost(argv[1], &p->playback_boost); + + ast_cli(fd, + "playback_boost on the active skypiax_console, that is [%s], is now: %5.1f\n", + skypiax_console_active, + 20 * log10(((double) p->playback_boost / (double) BOOST_SCALE))); + } + + return RESULT_SUCCESS; +} +int skypiax_console_capture_boost(int fd, int argc, char *argv[]) +{ + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + + if (argc > 2) { + return RESULT_SHOWUSAGE; + } + if (!p) { + ast_cli(fd, + "No \"current\" skypiax_console for capture_boost, please enter 'help skypiax_console'\n"); + return RESULT_SUCCESS; + } + + if (argc == 1) { + ast_cli(fd, "capture_boost on the active skypiax_console, that is [%s], is: %5.1f\n", + skypiax_console_active, + 20 * log10(((double) p->capture_boost / (double) BOOST_SCALE))); + } else if (argc == 2) { + skypiax_store_boost(argv[1], &p->capture_boost); + + ast_cli(fd, + "capture_boost on the active skypiax_console, that is [%s], is now: %5.1f\n", + skypiax_console_active, + 20 * log10(((double) p->capture_boost / (double) BOOST_SCALE))); + } + + return RESULT_SUCCESS; +} + +int skypiax_console_hangup(int fd, int argc, char *argv[]) +{ + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + + if (argc != 1) { + return RESULT_SHOWUSAGE; + } + if (!p) { + ast_cli(fd, + "No \"current\" skypiax_console for hanging up, please enter 'help skypiax_console'\n"); + return RESULT_SUCCESS; + } + if (!p->owner) { + ast_cli(fd, "No call to hangup on the active skypiax_console, that is [%s]\n", + skypiax_console_active); + return RESULT_FAILURE; + } + if (p->owner) + ast_queue_hangup(p->owner); + return RESULT_SUCCESS; +} + +int skypiax_console_dial(int fd, int argc, char *argv[]) +{ + char *s = NULL; + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + + if (argc != 2) { + return RESULT_SHOWUSAGE; + } + if (!p) { + ast_cli(fd, + "No \"current\" skypiax_console for dialing, please enter 'help skypiax_console'\n"); + return RESULT_SUCCESS; + } + + if (p->owner) { /* already in a call */ + int i; + struct ast_frame f = { AST_FRAME_DTMF, 0 }; + + s = argv[1]; + /* send the string one char at a time */ + for (i = 0; i < strlen(s); i++) { + f.subclass = s[i]; + ast_queue_frame(p->owner, &f); + } + return RESULT_SUCCESS; + } else + ast_cli(fd, + "No call in which to dial on the \"current\" skypiax_console, that is [%s]\n", + skypiax_console_active); + if (s) + free(s); + return RESULT_SUCCESS; +} +int skypiax_console_set_active(int fd, int argc, char *argv[]) +{ + if (argc == 1) + ast_cli(fd, + "\"current\" skypiax_console is [%s]\n Enter 'skypiax_console show' to see the available interfaces.\n Enter 'skypiax_console interfacename' to change the \"current\" skypiax_console.\n", + skypiax_console_active); + else if (argc != 2) { + return RESULT_SHOWUSAGE; + } else { + struct skypiax_pvt *p; + if (strcmp(argv[1], "show") == 0) { + ast_cli(fd, "Available interfaces:\n"); + for (p = skypiax_iflist; p; p = p->next) + ast_cli(fd, " [%s]\n", p->name); + return RESULT_SUCCESS; + } + p = skypiax_console_find_desc(argv[1]); + if (p == NULL) + ast_cli(fd, "Interface [%s] do not exists!\n", argv[1]); + else { + skypiax_console_active = p->name; + ast_cli(fd, "\"current\" skypiax_console is now: [%s]\n", argv[1]); + } + } + return RESULT_SUCCESS; +} + +int skypiax_console_skypiax(int fd, int argc, char *argv[]) +{ + return RESULT_SHOWUSAGE; +} + +void *do_skypeapi_thread(void *obj) +{ + return skypiax_do_skypeapi_thread_func(obj); +} + +int dtmf_received(private_t * p, char *value) +{ + + struct ast_frame f2 = { AST_FRAME_DTMF, value[0], }; + DEBUGA_SKYPE("Received DTMF: %s\n", SKYPIAX_P_LOG, value); + ast_queue_frame(p->owner, &f2); + + return 0; + +} + +int start_audio_threads(private_t * p) +{ + //if (!p->tcp_srv_thread) { + if (ast_pthread_create(&p->tcp_srv_thread, NULL, skypiax_do_tcp_srv_thread, p) < 0) { + ERRORA("Unable to start tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + return -1; + } else { + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + } + //} + //if (!p->tcp_cli_thread) { + if (ast_pthread_create(&p->tcp_cli_thread, NULL, skypiax_do_tcp_cli_thread, p) < 0) { + ERRORA("Unable to start tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + return -1; + } else { + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + } + //} + +#ifdef NOTDEF + 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); + switch_thread_create(&tech_pvt->tcp_srv_thread, thd_attr, skypiax_do_tcp_srv_thread, + tech_pvt, skypiax_module_pool); + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + + switch_threadattr_create(&thd_attr, skypiax_module_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&tech_pvt->tcp_cli_thread, thd_attr, skypiax_do_tcp_cli_thread, + tech_pvt, skypiax_module_pool); + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + switch_sleep(100000); + +#endif + + return 0; +} + +int new_inbound_channel(private_t * p) +{ + +#ifdef NOTDEF + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if ((session = switch_core_session_request(skypiax_endpoint_interface, NULL)) != 0) { + switch_core_session_add_stream(session, NULL); + channel = switch_core_session_get_channel(session); + skypiax_tech_init(tech_pvt, session); + + 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); + } + } + switch_channel_mark_answered(channel); + +#endif + return 0; +} + +int outbound_channel_answered(private_t * p) +{ +#ifdef NOTDEF + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (strlen(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + } + 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); + +#endif + + if (p->owner) { + ast_queue_control(p->owner, AST_CONTROL_ANSWER); + } + + NOTICA("HERE!\n", SKYPIAX_P_LOG); + + return 0; +} +void *skypiax_do_tcp_srv_thread(void *obj) +{ + return skypiax_do_tcp_srv_thread_func(obj); +} +void *skypiax_do_tcp_cli_thread(void *obj) +{ + return skypiax_do_tcp_cli_thread_func(obj); +} + +int skypiax_audio_write(struct skypiax_pvt *p, struct ast_frame *f) +{ + int sent; + + sent = write(p->audioskypepipe[1], (short *) f->data, f->datalen); + + return 0; +} +int skypiax_console_skype(int fd, int argc, char *argv[]) +{ + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + char skype_msg[1024]; + int i, a, c; + + if (argc == 1) { + return RESULT_SHOWUSAGE; + } + if (!p) { + ast_cli(fd, "No \"current\" console for skypiax_, please enter 'help console'\n"); + return RESULT_SUCCESS; + } + if (!p->skype) { + ast_cli(fd, "The \"current\" console is not connected to a Skype client'\n"); + return RESULT_SUCCESS; + } + + memset(skype_msg, 0, sizeof(skype_msg)); + c = 0; + for (i = 1; i < argc; i++) { + for (a = 0; a < strlen(argv[i]); a++) { + skype_msg[c] = argv[i][a]; + c++; + if (c == 1022) + break; + } + if (i != argc - 1) { + skype_msg[c] = ' '; + c++; + } + if (c == 1023) + break; + } + skypiax_signaling_write(p, skype_msg); + return RESULT_SUCCESS; +} + +int skypiax_console_skypiax_dir_import(int fd, int argc, char *argv[]) +{ + //int res; + struct skypiax_pvt *p = skypiax_console_find_desc(skypiax_console_active); + //char list_command[64]; + char fn[256]; + char date[256] = ""; + time_t t; + char *configfile = SKYPIAX_DIR_CONFIG; + int add_to_skypiax_dir_conf = 1; + //int fromskype = 0; + //int fromcell = 0; + +#if 0 + if (directoriax_entry_extension) { + skypiax_dir_entry_extension = directoriax_entry_extension; + } else { + ast_cli(fd, + "No 'directoriax_entry_extension', you MUST have loaded directoriax.so\n"); + return RESULT_SUCCESS; + } +#endif + + if (argc != 2) + return RESULT_SHOWUSAGE; + if (!p) { + ast_cli(fd, "No \"current\" console ???, please enter 'help skypiax_console'\n"); + return RESULT_SUCCESS; + } + + if (!strcasecmp(argv[1], "add")) + add_to_skypiax_dir_conf = 1; + else if (!strcasecmp(argv[1], "replace")) + add_to_skypiax_dir_conf = 0; + else { + ast_cli(fd, "\n\nYou have neither specified 'add' nor 'replace'\n\n"); + return RESULT_SHOWUSAGE; + } + +#if 0 + if (!strcasecmp(argv[2], "fromskype")) + fromskype = 1; + else if (!strcasecmp(argv[2], "fromcell")) + fromcell = 1; + else { + ast_cli(fd, "\n\nYou have neither specified 'fromskype' nor 'fromcell'\n\n"); + return RESULT_SHOWUSAGE; + } + + if (fromcell) { + ast_cli(fd, + "Importing from cellphone is currently supported only on \"AT\" cellphones :( !\n"); + //fclose(p->phonebook_writing_fp); + //skypiax_dir_create_extensions(); + return RESULT_SUCCESS; + } + + if (fromskype) + if (!p->skype) { + ast_cli(fd, "Importing from skype is supported by skypiax_dir on chan_skypiax!\n"); + //fclose(p->phonebook_writing_fp); + //skypiax_dir_create_extensions(); + return RESULT_SUCCESS; + } + + if (fromcell || fromskype) + if (argc != 3) { + ast_cli(fd, + "\n\nYou don't have to specify a filename with 'fromcell' or with 'fromskype'\n\n"); + return RESULT_SHOWUSAGE; + } +#endif + + /*******************************************************************************************/ + + if (configfile[0] == '/') { + ast_copy_string(fn, configfile, sizeof(fn)); + } else { + snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile); + } + NOTICA("Opening '%s'\n", SKYPIAX_P_LOG, fn); + time(&t); + ast_copy_string(date, ctime(&t), sizeof(date)); + + if (add_to_skypiax_dir_conf) + p->phonebook_writing_fp = fopen(fn, "a+"); + else + p->phonebook_writing_fp = fopen(fn, "w+"); + + if (p->phonebook_writing_fp) { + if (add_to_skypiax_dir_conf) { + NOTICA("Opened '%s' for appending \n", SKYPIAX_P_LOG, fn); + fprintf(p->phonebook_writing_fp, ";!\n"); + fprintf(p->phonebook_writing_fp, ";! Update Date: %s", date); + fprintf(p->phonebook_writing_fp, ";! Updated by: %s, %d\n", __FILE__, __LINE__); + fprintf(p->phonebook_writing_fp, ";!\n"); + } else { + NOTICA("Opened '%s' for writing \n", SKYPIAX_P_LOG, fn); + fprintf(p->phonebook_writing_fp, ";!\n"); + fprintf(p->phonebook_writing_fp, ";! Automatically generated configuration file\n"); + fprintf(p->phonebook_writing_fp, ";! Filename: %s (%s)\n", configfile, fn); + fprintf(p->phonebook_writing_fp, ";! Creation Date: %s", date); + fprintf(p->phonebook_writing_fp, ";! Generated by: %s, %d\n", __FILE__, __LINE__); + fprintf(p->phonebook_writing_fp, ";!\n"); + fprintf(p->phonebook_writing_fp, "[general]\n\n"); + fprintf(p->phonebook_writing_fp, "[default]\n"); + } + + /*******************************************************************************************/ + //if (fromskype) { + if (p->skype) { + WARNINGA + ("About to querying the Skype client 'Contacts', it may take some moments... Don't worry.\n", + SKYPIAX_P_LOG); + if (p->skype_thread != AST_PTHREADT_NULL) { + char msg_to_skype[1024]; + + p->skype_friends[0] = '\0'; + sprintf(msg_to_skype, "#333 SEARCH FRIENDS"); + if (skypiax_signaling_write(p, msg_to_skype) < 0) { + return -1; + } + + int friends_count = 0; + while (p->skype_friends[0] == '\0') { + /* FIXME needs a timeout, can't wait forever! + * eg. when skype is running but not connected! */ + usleep(100); + friends_count++; + if (friends_count > 20000) { + return -1; /* FIXME */ + } + } + + } + + if (p->skype_thread != AST_PTHREADT_NULL) { + char msg_to_skype[1024]; + + if (p->skype_friends[0] != '\0') { + char *buf, *where; + char **stringp; + int skype_dir_file_written = 0; + + buf = p->skype_friends; + stringp = &buf; + where = strsep(stringp, ", "); + while (where) { + if (where[0] != '\0') { + /* + * So, we have the Skype username (the HANDLE, I think is called). + * But we want to call the names we see in the Skype contact list + * So, let's check the DISPLAYNAME (the end user modified contact name) + * Then, we check the FULLNAME (that appears as it was the DISPLAYNAME + * if the end user has not modify it) + * If we still have neither DISPLAYNAME nor FULLNAME, we'll use the + * Skipe username (the HANDLE) + */ + + p->skype_displayname[0] = '\0'; + sprintf(msg_to_skype, "#765 GET USER %s DISPLAYNAME", where); + skypiax_signaling_write(p, msg_to_skype); + int displayname_count = 0; + while (p->skype_displayname[0] == '\0') { + /* FIXME needs a timeout, can't wait forever! + * eg. when skype is running but not connected! */ + usleep(100); + displayname_count++; + if (displayname_count > 20000) + return -1; /* FIXME */ + } + if (p->skype_displayname[0] != '\0') { + char *where2; + char sanitized[300]; + + sanitized[0] = '\0'; + + where2 = strstr(p->skype_displayname, "DISPLAYNAME "); + if (where2) { + + /* there can be some *smart* that makes a displayname + * that is different than firstlast, */ + /* maybe initials, simbols, slashes, + * something smartish... let's check */ + + if (where2[12] != '\0') { + int i = 12; + int x = 0; + int spaces = 0; + int last_char_was_space = 0; + + for (i = 12; i < strlen(where2) && x < 299; i++) { + if (!isalnum(where2[i])) { + if (!isblank(where2[i])) { + /* bad char */ + continue; + } + /* is a space */ + if (last_char_was_space == 1) /* do not write 2 consecutive spaces */ + continue; + last_char_was_space = 1; + sanitized[x] = ' '; + x++; + continue; + } + /* is alphanum */ + last_char_was_space = 0; + sanitized[x] = where2[i]; + x++; + continue; + } + + sanitized[x] = '\0'; + if (spaces == 0) { + } + DEBUGA_SKYPE("sanitized=|%s|, where=|%s|, where2=|%s|\n", + SKYPIAX_P_LOG, sanitized, where, &where2[12]); + } + + if (where2[12] != '\0') { + skypiax_dir_entry_extension++; + if (where[0] == '+' || isdigit(where[0])) { /* is a skypeout number */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, sanitized, "no", + p->skypiax_dir_entry_extension_prefix, "2", + skypiax_dir_entry_extension, "yes", "not_specified"); + } else { /* is a skype name */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKY,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, sanitized, "no", + p->skypiax_dir_entry_extension_prefix, "1", + skypiax_dir_entry_extension, "yes", "not_specified"); + } + skype_dir_file_written = 1; + + } + } + } + p->skype_displayname[0] = '\0'; + + p->skype_fullname[0] = '\0'; + sprintf(msg_to_skype, "#222 GET USER %s FULLNAME", where); + skypiax_signaling_write(p, msg_to_skype); + int fullname_count = 0; + while (p->skype_fullname[0] == '\0') { + /* FIXME needs a timeout, can't wait forever! + * eg. when skype is running but not connected! */ + usleep(100); + fullname_count++; + if (fullname_count > 20000) + return -1; /* FIXME */ + } + if (p->skype_fullname[0] != '\0') { + char *where2; + char sanitized[300]; + + where2 = strstr(p->skype_fullname, "FULLNAME "); + if (where2) { + + /* there can be some *smart* that makes a fullname + * that is different than firstlast, */ + /* maybe initials, simbols, slashes, + * something smartish... let's check */ + + if (where2[9] != '\0') { + int i = 9; + int x = 0; + int spaces = 0; + int last_char_was_space = 0; + + for (i = 9; i < strlen(where2) && x < 299; i++) { + if (!isalnum(where2[i])) { + if (!isblank(where2[i])) { + /* bad char */ + continue; + } + /* is a space */ + if (last_char_was_space == 1) /* do not write 2 consecutive spaces */ + continue; + last_char_was_space = 1; + sanitized[x] = ' '; + x++; + continue; + } + /* alphanum */ + last_char_was_space = 0; + sanitized[x] = where2[i]; + x++; + continue; + } + + sanitized[x] = '\0'; + if (spaces == 0) { + } + DEBUGA_SKYPE("sanitized=|%s|, where=|%s|, where2=|%s|\n", + SKYPIAX_P_LOG, sanitized, where, &where2[9]); + } + + if (skype_dir_file_written == 0) { + skypiax_dir_entry_extension++; + if (where2[9] != '\0') { + if (where[0] == '+' || isdigit(where[0])) { /* is a skypeout number */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, sanitized, "no", + p->skypiax_dir_entry_extension_prefix, "2", + skypiax_dir_entry_extension, "yes", "not_specified"); + } else { /* is a skype name */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKY,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, sanitized, "no", + p->skypiax_dir_entry_extension_prefix, "1", + skypiax_dir_entry_extension, "yes", "not_specified"); + + } + + } else { + if (where[0] == '+' || isdigit(where[0])) { /* is a skypeout number */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, where, "no", p->skypiax_dir_entry_extension_prefix, + "2", skypiax_dir_entry_extension, "yes", "not_specified"); + } else { /* is a skype name */ + fprintf(p->phonebook_writing_fp, + "%s => ,%sSKY,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromskype=%s|phonebook_entry_owner=%s\n", + where, where, "no", p->skypiax_dir_entry_extension_prefix, + "1", skypiax_dir_entry_extension, "yes", "not_specified"); + + } + } + } + + skype_dir_file_written = 0; + + } + + } + p->skype_fullname[0] = '\0'; + + } + where = strsep(stringp, ", "); + } + + p->skype_friends[0] = '\0'; + } + } + } else { + + ast_cli(fd, + "Skype not configured on the 'current' console, not importing from Skype client!\n"); + } + //} + /*******************************************************************************************/ + /*******************************************************************************************/ + } else { + ast_cli(fd, "\n\nfailed to open the skypiax_dir.conf configuration file: %s\n", fn); + ERRORA("failed to open the skypiax_dir.conf configuration file: %s\n", SKYPIAX_P_LOG, + fn); + return RESULT_FAILURE; + } + + fclose(p->phonebook_writing_fp); + //skypiax_dir_create_extensions(); + + return RESULT_SUCCESS; +} + +private_t *find_available_skypiax_interface(void) +{ + private_t *p; + int found = 0; + + /* lock the interfaces' list */ + LOKKA(&skypiax_iflock); + /* make a pointer to the first interface in the interfaces list */ + p = skypiax_iflist; + /* Search for the requested interface and verify if is unowned */ + while (p) { + if (!p->owner) { + DEBUGA_PBX("Interface is NOT OWNED by a channel\n", SKYPIAX_P_LOG); + found = 1; + /* we found the requested interface, bail out from the while loop */ + break; + } else { + /* interface owned by a channel */ + DEBUGA_PBX("Interface is OWNED by a channel\n", SKYPIAX_P_LOG); + } + /* not yet found, next please */ + p = p->next; + } + + /* lock the interfaces' list */ + UNLOCKA(&skypiax_iflock); + + if (found) + return p; + else + return NULL; +} + +/************************************************/ +#ifdef ASTERISK_VERSION_1_4 +#ifndef AST_MODULE +#define AST_MODULE "chan_skypiax" +#endif +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skypiax, Audio-Serial Driver"); +#endif /* ASTERISK_VERSION_1_4 */ + +/* rewriting end */ +/*******************************************************************************/ diff --git a/src/mod/endpoints/mod_skypiax/asterisk/cyg_no_pthread_kill.c b/src/mod/endpoints/mod_skypiax/asterisk/cyg_no_pthread_kill.c new file mode 100644 index 0000000000..950d66c52b --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/cyg_no_pthread_kill.c @@ -0,0 +1,17 @@ +#include +#define PRINTMSGCYG + +extern int option_debug; +int cyg_no_pthreadkill(int thread, int sig); + +int cyg_no_pthreadkill(int thread, int sig) +{ +#ifdef PRINTMSGCYG + if (option_debug) { + printf + ("\n\nHere there would have been a pthread_kill() on thread [%-7lx], with sig=%d, but it has been substituted by this printf in file cyg_no_pthread_kill.c because CYGWIN does not support sending a signal to a one only thread :-(\n\n", + (unsigned long int) thread, sig); + } +#endif // PRINTMSGCYG + return 0; +} diff --git a/src/mod/endpoints/mod_skypiax/asterisk/skypiax.conf b/src/mod/endpoints/mod_skypiax/asterisk/skypiax.conf new file mode 100644 index 0000000000..445c65536a --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/skypiax.conf @@ -0,0 +1,207 @@ +;;;;;;;; +;;;;;;;; +;;;;;;; Skypiax Asterisk Driver +;;;;;;; +;;;;;;; Configuration file +;;;;;;; lines beginning with semicolon (" are ignored (commented out) +;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;; +;;;;;;; The first interface (named skypeclient) +;;;;;;[skypeclient] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;; general settings, valid on all platforms +;;;;;;; +;;;;;;; +;;;;;;; Default language +;;;;;;; +;;;;;;language=en +;;;;;;; +;;;;;;; Default context (in extensions.conf, can be overridden with @context syntax) +;;;;;;; +;;;;;;context=default +;;;;;;; +;;;;;;; Default extension (in extensions.conf) where incoming calls land +;;;;;;; +;;;;;;extension=s +;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;; Debugging settings, valid globally for all interfaces on all platforms +;;;;;;; +;;;;;;; the debug values are global for all the interfaces. +;;;;;;; +;;;;;;; default is no skypiax debugging output, you **have** to activate debugging here to obtain debugging from skypiax +;;;;;;; +;;;;;;; To see the debugging output you have to "set debug 100" from the Asterisk CLI or launch +;;;;;;; Asterisk with -ddddddddddd option, and have the logger.conf file activating debug info for console and messages +;;;;;;; +;;;;;;; You can activate each of the following separately, but you can't disactivate. Eg: debug_at=no does not subtract debug_at from debug_all +;;;;;;; debug_all activate all possible debugging info +;;;;;;; +;;;;;;;debug_all=yes +;;;;;;debug_skype=yes +;;;;;;debug_pbx=yes +;;;;;;;debug_sound=yes +;;;;;;;debug_locks=yes +;;;;;;;debug_monitorlocks=yes +;;;;;; +;;;;;;skype=yes ; legacy setting, leave it to yes +;;;;;;X11_display=:101 +;;;;;;tcp_cli_port=11234 +;;;;;;tcp_srv_port=11235 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;; audio boost settings, valid for all platforms, to compensate for different input/output audio signal levels +;;;;;;; tweak it if you get horrible (or not hearable) sound +;;;;;;; +;;;;;;;boost can be positive or negative (-40 to +40) in db +;;;;;;;experiment to find which values are best for your computer +;;;;;;playback_boost=0 ; +;;;;;;capture_boost=0 ; +;;;;;; +;;; [skypiax1] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:101 +;;; tcp_cli_port=15576 +;;; tcp_srv_port=15577 +;;; skype_user=skypiax1 +;;; +;;; [skypiax2] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:102 +;;; tcp_cli_port=15578 +;;; tcp_srv_port=15579 +;;; skype_user=skypiax2 +;;; +;;; [skypiax3] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:103 +;;; tcp_cli_port=15580 +;;; tcp_srv_port=15581 +;;; skype_user=skypiax3 +;;; +[skypiax4] +language=en +context=default +extension=s +debug_skype=yes +debug_pbx=yes +skype=yes ; legacy setting, leave it to yes +playback_boost=0 ; +capture_boost=0 ; +X11_display=:104 +tcp_cli_port=15582 +tcp_srv_port=15583 +skype_user=skypiax4 + +[skypiax5] +language=en +context=default +extension=s +debug_skype=yes +debug_pbx=yes +skype=yes ; legacy setting, leave it to yes +playback_boost=0 ; +capture_boost=0 ; +X11_display=:105 +tcp_cli_port=15584 +tcp_srv_port=15585 +skype_user=skypiax5 + +[skypiax6] +language=en +context=default +extension=s +debug_skype=yes +debug_pbx=yes +skype=yes ; legacy setting, leave it to yes +playback_boost=0 ; +capture_boost=0 ; +X11_display=:106 +tcp_cli_port=15586 +tcp_srv_port=16586 +skype_user=skypiax6 + +;;; [skypiax17] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:117 +;;; tcp_cli_port=15587 +;;; tcp_srv_port=15588 +;;; skype_user=skypiax17 +;;; +;;; [skypiax18] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:118 +;;; tcp_cli_port=15589 +;;; tcp_srv_port=15590 +;;; skype_user=skypiax18 +;;; +;;; [skypiax19] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:119 +;;; tcp_cli_port=15591 +;;; tcp_srv_port=15592 +;;; skype_user=skypiax19 +;;; +;;; [skypiax20] +;;; language=en +;;; context=default +;;; extension=s +;;; debug_skype=yes +;;; debug_pbx=yes +;;; skype=yes ; legacy setting, leave it to yes +;;; playback_boost=0 ; +;;; capture_boost=0 ; +;;; X11_display=:120 +;;; tcp_cli_port=15593 +;;; tcp_srv_port=15594 +;;; skype_user=skypiax20 +;;; +;;; +;;; diff --git a/src/mod/endpoints/mod_skypiax/asterisk/skypiax.h b/src/mod/endpoints/mod_skypiax/asterisk/skypiax.h new file mode 100644 index 0000000000..f23774b9b0 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/skypiax.h @@ -0,0 +1,426 @@ +//indent -gnu -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -bbo -nhnl -nut -sob -l90 +#ifndef _SKYPIAX_H_ +#define _SKYPIAX_H_ + +#ifndef SKYPIAX_SVN_VERSION +#define SKYPIAX_SVN_VERSION "????NO_REVISION???" +#endif + +#include /* needed here for conditional compilation on version.h */ + /* the following #defs are for LINUX */ +#ifndef __CYGWIN__ +#ifndef ASTERISK_VERSION_1_6 +#ifndef ASTERISK_VERSION_1_4 +#ifndef ASTERISK_VERSION_1_2 +#define ASTERISK_VERSION_1_4 +#if(ASTERISK_VERSION_NUM == 999999) +#undef ASTERISK_VERSION_1_4 +#elif(ASTERISK_VERSION_NUM < 10400) +#undef ASTERISK_VERSION_1_4 +#endif /* ASTERISK_VERSION_NUM == 999999 || ASTERISK_VERSION_NUM < 10400 */ +#endif /* ASTERISK_VERSION_1_2 */ +#endif /* ASTERISK_VERSION_1_4 */ +#endif /* ASTERISK_VERSION_1_6 */ +#ifdef ASTERISK_VERSION_1_2 +#undef ASTERISK_VERSION_1_4 +#endif /* ASTERISK_VERSION_1_2 */ +#ifdef ASTERISK_VERSION_1_6 +#define ASTERISK_VERSION_1_4 +#endif /* ASTERISK_VERSION_1_6 */ +#define SKYPIAX_SKYPE +#define WANT_SKYPE_X11 +#endif /* NOT __CYGWIN__ */ + /* the following #defs are for WINDOWS */ +#ifdef __CYGWIN__ +#undef ASTERISK_VERSION_1_4 +#undef ASTERISK_VERSION_1_6 +#define SKYPIAX_SKYPE +#undef WANT_SKYPE_X11 +#endif /* __CYGWIN__ */ + +/* INCLUDES */ +#ifdef ASTERISK_VERSION_1_6 +#include /* some asterisk-devel package do not contains asterisk.h, but seems that is needed for the 1.6 series, at least from trunk */ +#endif /* ASTERISK_VERSION_1_6 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef ASTERISK_VERSION_1_4 +#include +#include +#endif /* ASTERISK_VERSION_1_4 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ASTERISK_VERSION_1_6 +#include +#include +#endif /* ASTERISK_VERSION_1_6 */ +#ifdef ASTERISK_VERSION_1_4 +#include +#include +#include +#include +#endif /* ASTERISK_VERSION_1_4 */ +#ifdef ASTERISK_VERSION_1_2 +#include +#include +#endif /* ASTERISK_VERSION_1_2 */ +#ifdef HAVE_CONFIG_H +#include +#endif +//#include "skypiax_spandsp.h" +#ifdef __CYGWIN__ +#include +#endif /* __CYGWIN__ */ +#ifdef WANT_SKYPE_X11 +#include +#include +#include +#endif /* WANT_SKYPE_X11 */ +#ifndef AST_DIGIT_ANYDIG +#define AST_DIGIT_ANYDIG "0123456789*#" +#else +#warning Please review Skypiax AST_DIGIT_ANYDIG +#endif +#ifndef _ASTERISK_H +#define AST_CONFIG_MAX_PATH 255 /* defined in asterisk.h, but some asterisk-devel package do not contains asterisk.h */ +extern char ast_config_AST_CONFIG_DIR[AST_CONFIG_MAX_PATH]; +int ast_register_atexit(void (*func) (void)); /* in asterisk.h, but some asterisk-devel package do not contains asterisk.h */ +void ast_unregister_atexit(void (*func) (void)); /* in asterisk.h, but some asterisk-devel package do not contains asterisk.h */ +#endif + +/* DEFINITIONS */ +#define SAMPLERATE_SKYPIAX 8000 +#define SAMPLES_PER_FRAME SAMPLERATE_SKYPIAX/50 +#define SKYPIAX_DIR_CONFIG "directoriax.conf" + +/* LUIGI RIZZO's magic */ +/* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must + * be representable in 16 bits to avoid overflows. + */ +#define BOOST_SCALE (1<<9) +#define BOOST_MAX 40 /* slightly less than 7 bits */ +/* call flow from the device */ +#define CALLFLOW_CALL_IDLE AST_STATE_DOWN +#define CALLFLOW_INCOMING_RING AST_STATE_RING +#define CALLFLOW_CALL_DIALING AST_STATE_DIALING +#define CALLFLOW_CALL_LINEBUSY AST_STATE_BUSY +#define CALLFLOW_CALL_ACTIVE 300 +#define CALLFLOW_INCOMING_HANGUP 100 +#define CALLFLOW_CALL_RELEASED 101 +#define CALLFLOW_CALL_NOCARRIER 102 +#define CALLFLOW_CALL_INFLUX 103 +#define CALLFLOW_CALL_INCOMING 104 +#define CALLFLOW_CALL_FAILED 105 +#define CALLFLOW_CALL_NOSERVICE 106 +#define CALLFLOW_CALL_OUTGOINGRESTRICTED 107 +#define CALLFLOW_CALL_SECURITYFAIL 108 +#define CALLFLOW_CALL_NOANSWER 109 +#define CALLFLOW_STATUS_FINISHED 110 +#define CALLFLOW_STATUS_CANCELLED 111 +#define CALLFLOW_STATUS_FAILED 112 +#define CALLFLOW_STATUS_REFUSED 113 +#define CALLFLOW_STATUS_RINGING 114 +#define CALLFLOW_STATUS_INPROGRESS 115 +#define CALLFLOW_STATUS_UNPLACED 116 +#define CALLFLOW_STATUS_ROUTING 117 +#define CALLFLOW_STATUS_EARLYMEDIA 118 +#define AST_STATE_HANGUP_REQUESTED 200 + //FIXME CALLFLOW_INCOMING_CALLID to be removed +#define CALLFLOW_INCOMING_CALLID 1019 +/* debugging bitmask */ +#define DEBUG_SOUND 1 +#define DEBUG_SERIAL 2 +#define DEBUG_SKYPE 4 +#define DEBUG_AT 8 +#define DEBUG_FBUS2 16 +#define DEBUG_CALL 32 +#define DEBUG_LOCKS 64 +#define DEBUG_PBX 128 +#define DEBUG_MONITORLOCKS 256 +#define DEBUG_ALL DEBUG_SOUND|DEBUG_SERIAL|DEBUG_SKYPE|DEBUG_AT|DEBUG_FBUS2|DEBUG_CALL|DEBUG_PBX|DEBUG_LOCKS|DEBUG_MONITORLOCKS +/* wrappers for ast_log */ +#define DEBUGA_SOUND(...) if (skypiax_debug & DEBUG_SOUND) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_SOUND %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_SERIAL(...) if (skypiax_debug & DEBUG_SERIAL) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_SERIAL %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_SKYPE(...) if (skypiax_debug & DEBUG_SKYPE) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_SKYPE %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_AT(...) if (skypiax_debug & DEBUG_AT) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_AT %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_FBUS2(...) if (skypiax_debug & DEBUG_FBUS2) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_FBUS2 %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_CALL(...) if (skypiax_debug & DEBUG_CALL) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_CALL %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_PBX(...) if (skypiax_debug & DEBUG_PBX) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_PBX %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define ERRORA(...) ast_log(LOG_ERROR, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][ERROR %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define NOTICA(...) ast_log(LOG_NOTICE, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][NOTICE %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define WARNINGA(...) ast_log(LOG_WARNING, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][WARNING %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +/* macros for logging */ +#define SKYPIAX_P_LOG p ? p->owner : NULL, (unsigned long)pthread_self(), __LINE__, p ? p->name ? p->name : "none" : "none", p ? p->owner ? p->owner->_state : -1 : -1, p ? p->interface_state : -1, p ? p->skype_callflow : -1 +#define SKYPIAX_TMP_LOG tmp ? tmp->owner : NULL, (unsigned long)pthread_self(), __LINE__, tmp ? tmp->name ? tmp->name : "none" : "none", tmp ? tmp->owner ? tmp->owner->_state : -1 : -1, tmp ? tmp->interface_state : -1, tmp ? tmp->skype_callflow : -1 +/* logging wrappers for ast_mutex_lock and ast_mutex_unlock */ +#define LOKKA(x) if (skypiax_debug & DEBUG_LOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_LOCKS %-5d][%-10s][%2d,%2d,%2d] going to lock %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); if (ast_mutex_lock(x)) ast_log(LOG_ERROR, "ast_mutex_lock failed, BAD\n"); if (skypiax_debug & DEBUG_LOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_LOCKS %-5d][%-10s][%2d,%2d,%2d] locked %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); +#define UNLOCKA(x) if (skypiax_debug & DEBUG_LOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_LOCKS %-5d][%-10s][%2d,%2d,%2d] going to unlock %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); if (ast_mutex_unlock(x)) ast_log(LOG_ERROR, "ast_mutex_lock failed, BAD\n"); if (skypiax_debug & DEBUG_LOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_LOCKS %-5d][%-10s][%2d,%2d,%2d] unlocked %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); +#define PUSHA_UNLOCKA(x) pthread_cleanup_push(skypiax_unlocka_log, (void *) x); +#define POPPA_UNLOCKA(x) pthread_cleanup_pop(0); +#define MONITORLOKKA(x) if (skypiax_debug & DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_MONITORLOCKS %-5d][%-10s][%2d,%2d,%2d] going to lock %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); if (ast_mutex_lock(x)) ast_log(LOG_ERROR, "ast_mutex_lock failed, BAD\n"); if (skypiax_debug & DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_MONITORLOCKS %-5d][%-10s][%2d,%2d,%2d] locked %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); +#define MONITORUNLOCKA(x) if (skypiax_debug & DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_MONITORLOCKS %-5d][%-10s][%2d,%2d,%2d] going to unlock %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); if (ast_mutex_unlock(x)) ast_log(LOG_ERROR, "ast_mutex_lock failed, BAD\n"); if (skypiax_debug & DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, "rev "SKYPIAX_SVN_VERSION "[%p|%-7lx][DEBUG_MONITORLOCKS %-5d][%-10s][%2d,%2d,%2d] unlocked %p (%s)\n", SKYPIAX_P_LOG, x, x == &skypiax_monlock ? "MONLOCK" : x == &skypiax_iflock ? "IFLOCK" : x == &skypiax_usecnt_lock ? "USECNT_LOCK" : "?????"); +/* macros used for config file parsing (luigi rizzo)*/ +#define M_BOOL(tag, dst) M_F(tag, (dst) = ast_true(__val) ) +#define M_END(x) x; +#define M_F(tag, f) if (!strcasecmp((__s), tag)) { f; } else +#ifdef ASTERISK_VERSION_1_6 +#define M_START(var, val) const char *__s = var; const char *__val = val; +#else +#define M_START(var, val) char *__s = var; char *__val = val; +#endif /* ASTERISK_VERSION_1_6 */ +#define M_STR(tag, dst) M_F(tag, ast_copy_string(dst, __val, sizeof(dst))) +#define M_UINT(tag, dst) M_F(tag, (dst) = strtoul(__val, NULL, 0) ) + +#define SKYPIAX_FRAME_SIZE 160 + +/* SKYPIAX INTERNAL STRUCTS */ +/*! + * \brief structure for exchanging messages with the skype client + */ +#ifdef WANT_SKYPE_X11 +struct AsteriskHandles { + Window skype_win; + Display *disp; + Window win; + int fdesc[2]; +}; +#else /* WANT_SKYPE_X11 */ +struct AsteriskHandles { + HWND win32_hInit_MainWindowHandle; + HWND win32_hGlobal_SkypeAPIWindowHandle; + int fdesc[2]; +}; +#endif /* WANT_SKYPE_X11 */ + +#ifndef WIN32 +struct SkypiaxHandles { + Window skype_win; + Display *disp; + Window win; + 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 api_connected; + int fdesc[2]; +}; + +#endif //WIN32 + +/*! + * \brief PVT structure for a skypiax interface (channel), created by skypiax_mkif + */ +struct skypiax_pvt { + char *name; /*!< \brief 'name' of the interface (channel) */ + int interface_state; /*!< \brief 'state' of the interface (channel) */ + struct ast_channel *owner; /*!< \brief channel we belong to, possibly NULL */ + struct skypiax_pvt *next; /*!< \brief Next interface (channel) in list */ + char context[AST_MAX_EXTENSION]; /*!< \brief default Asterisk dialplan context for this interface */ + char language[MAX_LANGUAGE]; /*!< \brief default Asterisk dialplan language for this interface */ + char exten[AST_MAX_EXTENSION]; /*!< \brief default Asterisk dialplan extension for this interface */ + int skypiax_sound_rate; /*!< \brief rate of the sound device, in Hz, eg: 8000 */ + int skypiax_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ + char callid_name[50]; + char callid_number[50]; + pthread_t controldev_thread; /*!< \brief serial control thread for this interface, running during the call */ + double playback_boost; + double capture_boost; + int stripmsd; + pthread_t skype_thread; + struct AsteriskHandles AsteriskHandlesAst; + struct SkypiaxHandles SkypiaxHandles; + 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; + int audiopipe[2]; + int audioskypepipe[2]; + pthread_t tcp_srv_thread; + pthread_t tcp_cli_thread; + short audiobuf[160]; + int audiobuf_is_loaded; + + //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; +#ifdef WIN32 + unsigned short tcp_cli_port; + unsigned short tcp_srv_port; +#else + int tcp_cli_port; + int tcp_srv_port; +#endif + char X11_display[256]; + + struct ast_frame read_frame; + + char skype_user[256]; + char skype_password[256]; + char destination[256]; + char session_uuid_str[512 + 1]; + pthread_t signaling_thread; +}; + +typedef struct skypiax_pvt private_t; +/* FUNCTIONS */ + +/* module helpers functions */ +int load_module(void); +int unload_module(void); +int usecount(void); +char *description(void); +char *key(void); + +/* chan_skypiax internal functions */ +void skypiax_unlocka_log(void *x); + +void *do_skypeapi_thread(void *data); +//int skypiax2skype(struct ast_channel *c, void *data); +//int skype2skypiax(struct ast_channel *c, void *data); +//void skypiax_disconnect(void); +int skypiax_signaling_write(struct skypiax_pvt *p, char *msg_to_skype); +int skypiax_signaling_read(struct skypiax_pvt *p); +int skypiax_console_skype(int fd, int argc, char *argv[]); +#ifdef WANT_SKYPE_X11 +int X11_errors_handler(Display * dpy, XErrorEvent * err); +int skypiax_send_message(struct SkypiaxHandles *SkypiaxHandles, const char *message_P); +int skypiax_present(struct SkypiaxHandles *SkypiaxHandles); +void skypiax_clean_disp(void *data); +#endif /* WANT_SKYPE_X11 */ +#ifdef __CYGWIN__ + +int win32_Initialize_CreateWindowClass(private_t * tech_pvt); +void win32_DeInitialize_DestroyWindowClass(private_t * tech_pvt); +int win32_Initialize_CreateMainWindow(private_t * tech_pvt); +void win32_DeInitialize_DestroyMainWindow(private_t * tech_pvt); +#endif /* __CYGWIN__ */ + +/* CHAN_SKYPIAX.C */ +int skypiax_queue_control(struct ast_channel *chan, int control); +struct skypiax_pvt *skypiax_console_find_desc(char *dev); +int skypiax_serial_call(struct skypiax_pvt *p, char *dstr); + +/* FUNCTIONS */ +/* PBX interface functions */ +struct ast_channel *skypiax_request(const char *type, int format, void *data, int *cause); +int skypiax_answer(struct ast_channel *c); +int skypiax_hangup(struct ast_channel *c); +int skypiax_originate_call(struct ast_channel *c, char *idest, int timeout); +struct ast_frame *skypiax_read(struct ast_channel *chan); +int skypiax_write(struct ast_channel *c, struct ast_frame *f); +int skypiax_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); +#ifndef ASTERISK_VERSION_1_4 +int skypiax_indicate(struct ast_channel *c, int cond); +#else +int skypiax_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen); +#endif +int skypiax_devicestate(void *data); +#ifdef ASTERISK_VERSION_1_4 +int skypiax_digitsend_begin(struct ast_channel *ast, char digit); +int skypiax_digitsend_end(struct ast_channel *ast, char digit, unsigned int duration); +#else /* ASTERISK_VERSION_1_4 */ +int skypiax_digitsend(struct ast_channel *ast, char digit); +#endif /* ASTERISK_VERSION_1_4 */ + +/* chan_skypiax internal functions */ + +struct skypiax_pvt *skypiax_mkif(struct ast_config *cfg, char *ctg, + int is_first_category); +struct ast_channel *skypiax_new(struct skypiax_pvt *p, int state, char *context); +int skypiax_restart_monitor(void); +void *skypiax_do_monitor(void *data); +int skypiax_sound_boost(struct ast_frame *f, double boost); +int skypiax_sound_init(struct skypiax_pvt *p); +int skypiax_sound_shutdown(struct skypiax_pvt *p); +struct ast_frame *skypiax_sound_read(struct skypiax_pvt *p); +int skypiax_sound_write(struct skypiax_pvt *p, struct ast_frame *f); +void *skypiax_do_controldev_thread(void *data); +#ifdef ASTERISK_VERSION_1_6 +void skypiax_store_boost(const char *s, double *boost); +#else +void skypiax_store_boost(char *s, double *boost); +#endif /* ASTERISK_VERSION_1_6 */ +int skypiax_console_set_active(int fd, int argc, char *argv[]); +int skypiax_console_hangup(int fd, int argc, char *argv[]); +int skypiax_console_playback_boost(int fd, int argc, char *argv[]); +int skypiax_console_capture_boost(int fd, int argc, char *argv[]); +int skypiax_console_skypiax(int fd, int argc, char *argv[]); +int skypiax_console_dial(int fd, int argc, char *argv[]); +int skypiax_audio_init(struct skypiax_pvt *p); +//struct ast_frame *skypiax_audio_read(struct skypiax_pvt *p); +int skypiax_audio_read(struct skypiax_pvt *p); +void *skypiax_do_tcp_srv_thread(void *data); +int skypiax_audio_write(struct skypiax_pvt *p, struct ast_frame *f); +void *skypiax_do_tcp_cli_thread(void *data); +int skypiax_call(struct skypiax_pvt *p, char *idest, int timeout); +int skypiax_console_skypiax_dir_import(int fd, int argc, char *argv[]); + +void *skypiax_do_tcp_srv_thread_func(void *obj); +void *skypiax_do_tcp_cli_thread_func(void *obj); +void *skypiax_do_skypeapi_thread_func(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_senddigit(struct skypiax_pvt *p, char digit); +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(void); +#define SKYPIAX_STATE_DOWN AST_STATE_DOWN +#define SKYPIAX_STATE_RING AST_STATE_RING +#define SKYPIAX_STATE_DIALING AST_STATE_DIALING +#define SKYPIAX_STATE_BUSY AST_STATE_BUSY +#define SKYPIAX_STATE_UP AST_STATE_UP +#define SKYPIAX_STATE_RINGING AST_STATE_RINGING +#define SKYPIAX_STATE_PRERING AST_STATE_PRERING +#define SKYPIAX_STATE_RESERVED AST_STATE_RESERVED +#define SKYPIAX_STATE_HANGUP_REQUESTED 200 +#endif /* _SKYPIAX_H_ */ diff --git a/src/mod/endpoints/mod_skypiax/asterisk/skypiax_protocol.c b/src/mod/endpoints/mod_skypiax/asterisk/skypiax_protocol.c new file mode 120000 index 0000000000..85619eb428 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/asterisk/skypiax_protocol.c @@ -0,0 +1 @@ +../skypiax_protocol.c \ No newline at end of file diff --git a/src/mod/endpoints/mod_skypiax/configs/README.skypiax_auth b/src/mod/endpoints/mod_skypiax/configs/README.skypiax_auth new file mode 100644 index 0000000000..bbbd27ff91 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/README.skypiax_auth @@ -0,0 +1,57 @@ +####################################### +HOW TO USE SKYPIAX_AUTH +####################################### + +You will use skypiax_auth only at the setup moment, to force the Skype client to ask you to authorize "skypiax" to connect to it. + +Then you copy the .Skype configuration directory of the user that has launched Skype (eg: /home/maruzz/.Skype if you are maruzz) on the home directory of the user that will start Skype on the server (eg: root). + +Compile skypiax_auth: +$ gcc -Wall -ggdb skypiax_auth.c -o skypiax_auth -lX11 + +Then: + +1) on a Linux desktop machine, rmmod all the snd* modules +2) on the desktop machine, modprobe snd-dummy +3) on the desktop machine, logout from your autologin username if any, launch the Skype client and login as the username you'll use on server +4) on the desktop machine, set the desktop client to use the "hw:dummy" audio device, to not update, to not make "events", etc etc... Make and receive a couple of test calls. Please note that you (and the remote party) will hear nothing (you're using the snd-dummy "fake" audio driver), that's ok. +3) on the desktop machine, when satisfied of the Skype client setup, use skypiax_auth (that simulates FS-skypiax connecting to the Skype client). The Skype client will ask you to be authorized to let "skypiax" connect. +4) Give the authorization and check the "not ask me again" option. +5) Close (Quit) the Skype client from the tray icon, so it saves its config. +6) Then, relaunch the Skype client and use skypiax_auth again, just to be sure it now succeed. +7) Close (Quit) the Skype client from the tray icon, so it saves its config. + +*** Do the steps 1-7 for all Skype usernames you will want to use on the server (eg: one Skype username per channel) + +When finished with all the Skype usernames: +Copy or targzip the .Skype directory and all its content on the home directory of the server user that will launch the Skype client(s). + +############################ +first time you use skypiax_auth +############################ +$ ./skypiax_auth +Skype instance found with id #27263062 +RECEIVED==> ERROR 68 +RECEIVED==> OK + +############################ +Give the auth to the Skype client, and tell him not to ask again +Then Ctrl-C to exit from skypiax_auth +############################ + + +Close (Quit) the Skype client from the tray icon, so it saves its config. +Then, relaunch the Skype client + +############################ +you use skypiax_auth again +############################ +$ ./skypiax_auth +Skype instance found with id #27263062 +RECEIVED==> OK +RECEIVED==> PROTOCOL 6 +RECEIVED==> CONNSTATUS ONLINE +RECEIVED==> CURRENTUSERHANDLE gmaruzz3 +RECEIVED==> USERSTATUS INVISIBLE + + diff --git a/src/mod/endpoints/mod_skypiax/configs/default.xml b/src/mod/endpoints/mod_skypiax/configs/default.xml new file mode 100644 index 0000000000..4fbe72f7e2 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/default.xml @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;answer-after=0]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_skypiax/configs/skypiax.X.conf b/src/mod/endpoints/mod_skypiax/configs/skypiax.X.conf new file mode 100644 index 0000000000..2fbb50c4a8 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/skypiax.X.conf @@ -0,0 +1 @@ +localhost diff --git a/src/mod/endpoints/mod_skypiax/configs/skypiax.conf.xml b/src/mod/endpoints/mod_skypiax/configs/skypiax.conf.xml new file mode 100644 index 0000000000..c8b73fde1c --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/skypiax.conf.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_skypiax/configs/skypiax_auth.c b/src/mod/endpoints/mod_skypiax/configs/skypiax_auth.c new file mode 100644 index 0000000000..672ca768fa --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/skypiax_auth.c @@ -0,0 +1,230 @@ +//gcc -Wall -ggdb skypiax_auth.c -o skypiax_auth -lX11 +#include +#include +#include +#include +#include + +struct SkypiaxHandles { + Window skype_win; + Display *disp; + Window win; + int api_connected; + int fdesc[2]; +}; + +XErrorHandler old_handler = 0; +int xerror = 0; +char *dispname; + +int X11_errors_handler(Display * dpy, XErrorEvent * err) +{ + (void) dpy; + + xerror = err->error_code; + printf("\n\nReceived error code %d from X Server on display '%s'\n\n", xerror, + dispname); + 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(struct SkypiaxHandles *SkypiaxHandles, const char *message_P) +{ + + Window w_P; + Display *disp; + Window handle_P; + int ok; + + w_P = SkypiaxHandles->skype_win; + disp = SkypiaxHandles->disp; + handle_P = SkypiaxHandles->win; + + Atom atom1 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False); + Atom atom2 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE", False); + unsigned int pos = 0; + unsigned int len = strlen(message_P); + XEvent e; + + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom1; /* leading message */ + e.xclient.display = disp; + e.xclient.window = handle_P; + e.xclient.format = 8; + + X11_errors_trap(); + do { + unsigned int i; + for (i = 0; i < 20 && i + pos <= len; ++i) + e.xclient.data.b[i] = message_P[i + pos]; + XSendEvent(disp, w_P, False, 0, &e); + + e.xclient.message_type = atom2; /* following messages */ + pos += i; + } while (pos <= len); + + XSync(disp, False); + ok = X11_errors_untrap(); + + if (!ok) + printf("Sending message failed with status %d\n", xerror); + + return 1; +} + +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; + + X11_errors_trap(); + status = + XGetWindowProperty(SkypiaxHandles->disp, DefaultRootWindow(SkypiaxHandles->disp), + skype_inst, 0, 1, False, XA_WINDOW, &type_ret, &format_ret, + &nitems_ret, &bytes_after_ret, &prop); + + X11_errors_untrap(); + /* sanity check */ + if (status != Success || format_ret != 32 || nitems_ret != 1) { + SkypiaxHandles->skype_win = (Window) - 1; + printf("Skype instance not found on display '%s'\n", dispname); + return 0; + } + + SkypiaxHandles->skype_win = *(const unsigned long *) prop & 0xffffffff; + printf("Skype instance found on display '%s', with id #%d\n", dispname, + (unsigned int) SkypiaxHandles->skype_win); + return 1; +} + +void skypiax_clean_disp(void *data) +{ + + int *dispptr; + int disp; + + dispptr = data; + disp = *dispptr; + + if (disp) { + close(disp); + } else { + } + usleep(1000); +} + +int main(int argc, char *argv[]) +{ + + struct SkypiaxHandles SkypiaxHandles; + char buf[512]; + Display *disp = NULL; + Window root = -1; + Window win = -1; + + if (argc == 2) + dispname = argv[1]; + else + dispname = ":0.0"; + + disp = XOpenDisplay(dispname); + if (!disp) { + printf("Cannot open X Display '%s', exiting\n", dispname); + return -1; + } + + int xfd; + xfd = XConnectionNumber(disp); + + 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(&SkypiaxHandles, buf)) { + printf + ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch skypiax_auth again\n"); + return -1; + } + + snprintf(buf, 512, "PROTOCOL 6"); + if (!skypiax_send_message(&SkypiaxHandles, buf)) { + printf + ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch skypiax_auth again\n"); + return -1; + } + + /* perform an events loop */ + XEvent an_event; + char buf[21]; /* can't be longer */ + char buffer[17000]; + char *b; + int i; + + b = buffer; + + while (1) { + XNextEvent(disp, &an_event); + switch (an_event.type) { + case ClientMessage: + + if (an_event.xclient.format != 8) + 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'; + + strcat(buffer, buf); + + if (i < 20) { /* last fragment */ + unsigned int howmany; + + howmany = strlen(b) + 1; + + printf("RECEIVED==> %s\n", b); + memset(buffer, '\0', 17000); + } + + break; + default: + break; + } + } + } else { + printf + ("Skype client not found on display '%s'. Please run/restart Skype manually and launch skypiax_auth again\n\n\n", + dispname); + return -1; + } + return 0; + +} diff --git a/src/mod/endpoints/mod_skypiax/configs/startskype.bat b/src/mod/endpoints/mod_skypiax/configs/startskype.bat new file mode 100644 index 0000000000..40042fb698 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/startskype.bat @@ -0,0 +1,52 @@ +echo off +REM +REM you MUST use the new Skype BETA (4.x) for Windows, older versions (3.x) cannot be started this way +REM +REM you have to adjust PATH to where the Skype executable is +set PATH=%PATH%;C:\Program Files\Skype\Phone\ + +echo %PATH% + +REM start a Skype client instance that will login to the Skype network using the "username password" you give to it. Here xxx would be the password and skypiax1 the username +start Skype.exe /secondary /username:skypiax1 /password:xxx +call wait 7 +start Skype.exe /secondary /username:skypiax2 /password:xxx +call wait 7 +REM +REM Following Skype client instances are commented out +REM +REM start Skype.exe /secondary /username:skypiax3 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax4 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax5 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax6 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax7 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax8 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax9 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax10 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax11 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax12 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax13 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax14 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax15 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax16 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax17 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax18 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax19 /password:xxx +REM call wait 7 +REM start Skype.exe /secondary /username:skypiax20 /password:xxx diff --git a/src/mod/endpoints/mod_skypiax/configs/startskype.sh b/src/mod/endpoints/mod_skypiax/configs/startskype.sh new file mode 100644 index 0000000000..e0547a856e --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/startskype.sh @@ -0,0 +1,136 @@ +# remember to add here the removing of all the installed snd-* modules, so you're sure only the snd-dummy driver will be around +rmmod snd_hda_intel + +# you need three dummy soundcard for 20 Skype client instances, because each dummy soundcard can handle a max of 8 Skype instances +# the enable= module parameter tells how many cards to start. For each additional card, add a comma and a 1 +# manually configure the first 8 Skype client instances to use the hw:Dummy_0, the next 8 instances to use hw:Dummy_1, etc for all three devices (Play, Capture, Ring) +modprobe snd-dummy enable=1,1,1 +sleep 3 + +#start the fake X server on a given port +/usr/bin/Xvfb :101 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +sleep 3 + +# start a Skype client instance that will connect to the X server above, and will login to the Skype network using the "username password" you send to it on stdin. Here xxx would be the password and skypiax1 the username +echo "skypiax1 xxx"| DISPLAY=:101 /usr/bin/skype --pipelogin & + +sleep 7 +/usr/bin/Xvfb :102 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +sleep 3 + +echo "skypiax2 xxx"| DISPLAY=:102 /usr/bin/skype --pipelogin & + +sleep 7 + +################################################################# +# Following X server Skype client instances are commented out +################################################################# +###/usr/bin/Xvfb :103 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax3 xxx"| DISPLAY=:103 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :104 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax4 xxx"| DISPLAY=:104 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :105 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax5 xxx"| DISPLAY=:105 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :106 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax6 xxx"| DISPLAY=:106 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :107 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax7 xxx"| DISPLAY=:107 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :108 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax8 xxx"| DISPLAY=:108 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :109 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax9 xxx"| DISPLAY=:109 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :110 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax10 xxx"| DISPLAY=:110 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :111 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax11 xxx"| DISPLAY=:111 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :112 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax12 xxx"| DISPLAY=:112 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :113 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax13 xxx"| DISPLAY=:113 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :114 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax14 xxx"| DISPLAY=:114 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :115 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax15 xxx"| DISPLAY=:115 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :116 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax16 xxx"| DISPLAY=:116 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :117 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax17 xxx"| DISPLAY=:117 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :118 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax18 xxx"| DISPLAY=:118 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :119 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax19 xxx"| DISPLAY=:119 /usr/bin/skype --pipelogin & +### +###sleep 7 +###/usr/bin/Xvfb :120 -auth /usr/local/freeswitch/conf/autoload_configs/skypiax.X.conf & +###sleep 3 +### +###echo "skypiax20 xxx"| DISPLAY=:120 /usr/bin/skype --pipelogin & +### +###sleep 7 +### diff --git a/src/mod/endpoints/mod_skypiax/configs/wait.bat b/src/mod/endpoints/mod_skypiax/configs/wait.bat new file mode 100644 index 0000000000..8381230f47 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/configs/wait.bat @@ -0,0 +1,4 @@ +REM would you believe there is no sleep() in standard windows batchfiles? +@ping 127.0.0.1 -n 2 -w 1000 > nul +@ping 127.0.0.1 -n %1% -w 1000> nul + diff --git a/src/mod/endpoints/mod_skypiax/mod_skypiax.2008.vcproj b/src/mod/endpoints/mod_skypiax/mod_skypiax.2008.vcproj new file mode 100644 index 0000000000..d7493d63d2 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypiax.2008.vcproj @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_skypiax/mod_skypiax.c b/src/mod/endpoints/mod_skypiax/mod_skypiax.c new file mode 100644 index 0000000000..c3e08301a1 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/mod_skypiax.c @@ -0,0 +1,1236 @@ +/* + * 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" + +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); + +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; + char hold_music[256]; + private_t SKYPIAX_INTERFACES[SKYPIAX_MAX_INTERFACES]; + switch_mutex_t *mutex; +} 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); + +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_routing(switch_core_session_t * session); +static switch_status_t channel_on_exchange_media(switch_core_session_t * session); +static switch_status_t channel_on_soft_execute(switch_core_session_t * session); +static switch_call_cause_t channel_outgoing_channel(switch_core_session_t * session, + switch_event_t * var_event, + switch_caller_profile_t * + outbound_profile, + switch_core_session_t ** new_session, + switch_memory_pool_t ** pool, + switch_originate_flag_t flags); +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_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); + + 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); + + return SWITCH_STATUS_SUCCESS; + +} + +void skypiax_tech_init(private_t * tech_pvt, switch_core_session_t * session) +{ + + tech_pvt->read_frame.data = tech_pvt->databuf; + tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); + switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, + switch_core_session_get_pool(session)); + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, + switch_core_session_get_pool(session)); + 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 (skypiax_codec(tech_pvt, SAMPLERATE_SKYPIAX, 20) != SWITCH_STATUS_SUCCESS) { + ERRORA("skypiax_codec FAILED\n", SKYPIAX_P_LOG); + } else { + DEBUGA_SKYPE("skypiax_codec SUCCESS\n", SKYPIAX_P_LOG); + } + +} + +/* + State methods they get called when the state changes to the specific state + returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next + so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it. +*/ +static switch_status_t channel_on_init(switch_core_session_t * session) +{ + switch_channel_t *channel; + private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + switch_set_flag_locked(tech_pvt, TFLAG_IO); + + /* Move channel's state machine to ROUTING. This means the call is trying + to get from the initial start where the call because, to the point + where a destination has been identified. If the channel is simply + left in the initial state, nothing will happen. */ + switch_channel_set_state(channel, CS_ROUTING); + switch_mutex_lock(globals.mutex); + globals.calls++; + switch_mutex_unlock(globals.mutex); + + DEBUGA_SKYPE("%s CHANNEL INIT\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_hangup(switch_core_session_t * session) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + char msg_to_skype[256]; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + switch_clear_flag_locked(tech_pvt, TFLAG_VOICE); + //switch_set_flag_locked(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); + } + + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + + memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str)); + DEBUGA_SKYPE("%s CHANNEL HANGUP\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + 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); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + DEBUGA_SKYPE("%s CHANNEL ROUTING\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_execute(switch_core_session_t * session) +{ + + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + DEBUGA_SKYPE("%s CHANNEL EXECUTE\n", SKYPIAX_P_LOG, switch_channel_get_name(channel)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_kill_channel(switch_core_session_t * session, int sig) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch (sig) { + case SWITCH_SIG_KILL: + DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_KILL\n", SKYPIAX_P_LOG, + switch_channel_get_name(channel)); + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + switch_clear_flag_locked(tech_pvt, TFLAG_VOICE); + switch_set_flag_locked(tech_pvt, TFLAG_HANGUP); + 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_locked(tech_pvt, TFLAG_BREAK); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_exchange_media(switch_core_session_t * session) +{ + private_t *tech_pvt = NULL; + DEBUGA_SKYPE("CHANNEL LOOPBACK\n", SKYPIAX_P_LOG); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_soft_execute(switch_core_session_t * session) +{ + private_t *tech_pvt = NULL; + DEBUGA_SKYPE("CHANNEL TRANSMIT\n", SKYPIAX_P_LOG); + 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("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; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + tech_pvt->read_frame.flags = SFF_NONE; + *frame = NULL; + + if (!skypiax_audio_read(tech_pvt)) { + + ERRORA("skypiax_audio_read ERROR\n", SKYPIAX_P_LOG); + + } else { + switch_set_flag_locked(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_locked(tech_pvt, TFLAG_VOICE); + if (!tech_pvt->read_frame.datalen) { + DEBUGA_SKYPE("CHANNEL READ CONTINUE\n", SKYPIAX_P_LOG); + continue; + } + *frame = &tech_pvt->read_frame; +#ifdef BIGENDIAN + 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; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + ERRORA("CIAPA \n", SKYPIAX_P_LOG); + return SWITCH_STATUS_FALSE; + } +#ifdef BIGENDIAN + if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { + switch_swap_linear(frame->data, (int) frame->datalen / 2); + } +#endif + + sent = frame->datalen; +#ifdef WIN32 + switch_file_write(tech_pvt->audioskypepipe[1], frame->data, &sent); +#else /* WIN32 */ + sent = write(tech_pvt->audioskypepipe[1], frame->data, sent); +#endif /* WIN32 */ + 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); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + 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; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + DEBUGA_SKYPE("MSG_ID=%d, TO BE ANSWERED!\n", SKYPIAX_P_LOG, msg->message_id); + channel_answer_channel(session); + } + break; + default: + { + 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 +}; + +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) +{ + if ((*new_session = switch_core_session_request(skypiax_endpoint_interface, pool)) != 0) { + private_t *tech_pvt; + switch_channel_t *channel; + switch_caller_profile_t *caller_profile; + char *rdest; + + switch_core_session_add_stream(*new_session, NULL); + + if ((tech_pvt = + (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { + int found = 0; + char interface_name[256]; + + if (strlen(outbound_profile->destination_number)) { + int i; + char *slash; + + strncpy(interface_name, outbound_profile->destination_number, 255); + slash = strrchr(interface_name, '/'); + *slash = '\0'; + + if (strncmp("ANY", 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(); + 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); + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE; + } + + 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) { + ERRORA("Doh! no matching interface for |||%s|||?\n", SKYPIAX_P_LOG, + interface_name); + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + + } + channel = switch_core_session_get_channel(*new_session); + skypiax_tech_init(tech_pvt, *new_session); + } else { + ERRORA("Hey where is my memory pool?\n", SKYPIAX_P_LOG); + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (outbound_profile) { + char name[128]; + + 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); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + 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_locked(tech_pvt, TFLAG_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + return SWITCH_CAUSE_SUCCESS; + } + + 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; + + DEBUGA_SKYPE("In skypiax_signaling_thread_func: started, p=%p\n", SKYPIAX_P_LOG, + (void *) tech_pvt); + + while (forever) { + if (!running) + break; + res = skypiax_signaling_read(tech_pvt); + if (res == CALLFLOW_INCOMING_HANGUP) { + switch_core_session_t *session = NULL; + //private_t *tech_pvt = NULL; + switch_channel_t *channel = 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_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); + } else { + ERRORA("no channel?\n", SKYPIAX_P_LOG); + switch_core_session_rwunlock(session); + } + } else { + DEBUGA_SKYPE("no session\n", SKYPIAX_P_LOG); + } + } else { + ERRORA("no tech_pvt?\n", SKYPIAX_P_LOG); + } + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str)); + } + } + return NULL; +} + +static switch_status_t load_config(void) +{ + 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; + return SWITCH_STATUS_TERM; + } + + 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; + + 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, "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; + } + + 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(100); +#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; + + 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); + + 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); + NOTICA("STARTING interface_id=%d\n", SKYPIAX_P_LOG, interface_id); + + switch_threadattr_create(&skypiax_api_thread_attr, skypiax_module_pool); + 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_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]); + + i = 0; + while (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected == 0 && running && i < 60000) { // 60sec FIXME + switch_sleep(1000); + i++; + } + if (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected) { + NOTICA("STARTED interface_id=%d\n", SKYPIAX_P_LOG, interface_id); + } else { + ERRORA("FAILED to start interface_id=%d\n", SKYPIAX_P_LOG, interface_id); + 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)) { + 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); + } + } + } + + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_skypiax_load) +{ + skypiax_module_pool = pool; + + memset(&globals, '\0', sizeof(globals)); + + running = 1; + + if (load_config() != SWITCH_STATUS_SUCCESS) + return SWITCH_STATUS_FALSE; + + *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) + /* 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 = 100; + 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 (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()); + } +#else + 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(20000); + } + 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); + } + } + 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) { + 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 { + 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); + switch_thread_create(&tech_pvt->tcp_srv_thread, thd_attr, skypiax_do_tcp_srv_thread, + tech_pvt, skypiax_module_pool); + DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG); + + switch_threadattr_create(&thd_attr, skypiax_module_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&tech_pvt->tcp_cli_thread, thd_attr, skypiax_do_tcp_cli_thread, + tech_pvt, skypiax_module_pool); + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + switch_sleep(100000); + + 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, NULL)) != 0) { + switch_core_session_add_stream(session, NULL); + channel = switch_core_session_get_channel(session); + skypiax_tech_init(tech_pvt, session); + + 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); + } + } + switch_channel_mark_answered(channel); + + DEBUGA_SKYPE("Here\n", SKYPIAX_P_LOG); + + return 0; +} + +int outbound_channel_answered(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (strlen(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No session???\n", SKYPIAX_P_LOG); + } + 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); + DEBUGA_SKYPE("HERE!\n", SKYPIAX_P_LOG); + + return 0; +} + +private_t *find_available_skypiax_interface(void) +{ + private_t *tech_pvt = NULL; + int found = 0; + int i; + + for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) { + if (strlen(globals.SKYPIAX_INTERFACES[i].name)) { + int skype_state = 0; + + tech_pvt = &globals.SKYPIAX_INTERFACES[i]; + skype_state = tech_pvt->interface_state; + DEBUGA_SKYPE("skype interface: %d, name: %s, state: %d\n", SKYPIAX_P_LOG, i, + globals.SKYPIAX_INTERFACES[i].name, skype_state); + if (SKYPIAX_STATE_DOWN == skype_state || 0 == skype_state) { + found = 1; + break; + } + } + } + + if (found) + return tech_pvt; + else + return NULL; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ diff --git a/src/mod/endpoints/mod_skypiax/skypiax.h b/src/mod/endpoints/mod_skypiax/skypiax.h new file mode 100644 index 0000000000..849382c344 --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/skypiax.h @@ -0,0 +1,270 @@ +/* + * 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 +#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 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_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 +/*********************************/ +/* call flow from the device */ +#define CALLFLOW_CALL_IDLE SKYPIAX_STATE_DOWN +#define CALLFLOW_INCOMING_RING SKYPIAX_STATE_RING +#define CALLFLOW_CALL_DIALING SKYPIAX_STATE_DIALING +#define CALLFLOW_CALL_LINEBUSY SKYPIAX_STATE_BUSY +#define CALLFLOW_CALL_ACTIVE 300 +#define CALLFLOW_INCOMING_HANGUP 100 +#define CALLFLOW_CALL_RELEASED 101 +#define CALLFLOW_CALL_NOCARRIER 102 +#define CALLFLOW_CALL_INFLUX 103 +#define CALLFLOW_CALL_INCOMING 104 +#define CALLFLOW_CALL_FAILED 105 +#define CALLFLOW_CALL_NOSERVICE 106 +#define CALLFLOW_CALL_OUTGOINGRESTRICTED 107 +#define CALLFLOW_CALL_SECURITYFAIL 108 +#define CALLFLOW_CALL_NOANSWER 109 +#define CALLFLOW_STATUS_FINISHED 110 +#define CALLFLOW_STATUS_CANCELLED 111 +#define CALLFLOW_STATUS_FAILED 112 +#define CALLFLOW_STATUS_REFUSED 113 +#define CALLFLOW_STATUS_RINGING 114 +#define CALLFLOW_STATUS_INPROGRESS 115 +#define CALLFLOW_STATUS_UNPLACED 116 +#define CALLFLOW_STATUS_ROUTING 117 +#define CALLFLOW_STATUS_EARLYMEDIA 118 +#define SKYPIAX_STATE_HANGUP_REQUESTED 200 + //FIXME CALLFLOW_INCOMING_CALLID to be removed +#define CALLFLOW_INCOMING_CALLID 1019 + +/*********************************/ + +#define SKYPIAX_MAX_INTERFACES 64 + +#ifndef WIN32 +struct SkypiaxHandles { + Window skype_win; + Display *disp; + Window win; + 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 api_connected; + switch_file_t *fdesc[2]; +}; + +#endif //WIN32 + +struct private_object { + unsigned int flags; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + char session_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_caller_profile_t *caller_profile; + switch_mutex_t *mutex; + switch_mutex_t *flag_mutex; + + char interface_id[80]; + char name[80]; + char dialplan[80]; + char context[80]; + char dial_regex[256]; + char fail_dial_regex[256]; + char hold_music[256]; + char type[256]; + char X11_display[256]; +#ifdef WIN32 + unsigned short tcp_cli_port; + unsigned short tcp_srv_port; +#else + int tcp_cli_port; + int tcp_srv_port; +#endif + struct 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[2]; + switch_file_t *audioskypepipe[2]; + switch_file_t *skypiax_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ +#else /* WIN32 */ + int audiopipe[2]; + int audioskypepipe[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; + + //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]; + +}; + +typedef struct private_object private_t; + +void *SWITCH_THREAD_FUNC skypiax_api_thread_func(switch_thread_t * thread, void *obj); +void skypiax_tech_init(private_t * tech_pvt, switch_core_session_t * session); +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(void); diff --git a/src/mod/endpoints/mod_skypiax/skypiax_protocol.c b/src/mod/endpoints/mod_skypiax/skypiax_protocol.c new file mode 100644 index 0000000000..2aff9d1ccf --- /dev/null +++ b/src/mod/endpoints/mod_skypiax/skypiax_protocol.c @@ -0,0 +1,1385 @@ +#include "skypiax.h" + +#ifdef ASTERISK +#define skypiax_sleep usleep +#define tech_pvt p +extern int skypiax_debug; +extern char *skypiax_console_active; +#else /* FREESWITCH */ +#define skypiax_sleep switch_sleep +extern switch_memory_pool_t *skypiax_module_pool; +extern switch_endpoint_interface_t *skypiax_endpoint_interface; +#endif /* ASTERISK */ + +extern int running; + +/*************************************/ +/* suspicious globals FIXME */ +#ifdef WIN32 +DWORD win32_dwThreadId; +#else +XErrorHandler old_handler = 0; +int xerror = 0; +#endif /* WIN32 */ +/*************************************/ + +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') { + + 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; + } + } + 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, "ERROR")) { + DEBUGA_SKYPE("Skype got ERROR: |||%s|||\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 (!strcasecmp(message, "CURRENTUSERHANDLE")) { + strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + strncpy(id, where, sizeof(id) - 1); + if (!strcasecmp(id, tech_pvt->skype_user)) { + tech_pvt->SkypiaxHandles.api_connected = 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")) { + strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + 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")) { + strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + strncpy(prop, where, sizeof(prop) - 1); + if (!strcasecmp(prop, "STATUS")) { + where = strsep(stringp, " "); + 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, "CALL")) { + strncpy(obj, where, sizeof(obj) - 1); + where = strsep(stringp, " "); + strncpy(id, where, sizeof(id) - 1); + where = strsep(stringp, " "); + strncpy(prop, where, sizeof(prop) - 1); + where = strsep(stringp, " "); + 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")) { + strncpy(tech_pvt->callid_number, value, sizeof(tech_pvt->callid_number) - 1); + DEBUGA_SKYPE + ("the skype_call %s caller PARTNER_HANDLE (tech_pvt->callid_number) is: %s\n", + SKYPIAX_P_LOG, id, tech_pvt->callid_number); + return CALLFLOW_INCOMING_RING; + } + 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)) { + 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, "STATUS")) { + + if (!strcasecmp(value, "RINGING")) { + char msg_to_skype[1024]; + if (tech_pvt->interface_state != SKYPIAX_STATE_DIALING) { + /* we are not calling out */ + if (!strlen(tech_pvt->skype_call_id)) { + /* we are not inside an active call */ + tech_pvt->skype_callflow = CALLFLOW_STATUS_RINGING; + tech_pvt->interface_state = SKYPIAX_STATE_RING; + /* no owner, no active call, let's answer */ + 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, "GET CALL %s PARTNER_DISPNAME", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(10000); + sprintf(msg_to_skype, "GET CALL %s PARTNER_HANDLE", id); + skypiax_signaling_write(tech_pvt, msg_to_skype); + skypiax_sleep(10000); + 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); + strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + } 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; + + available_skypiax_interface = find_available_skypiax_interface(); + if (available_skypiax_interface) { + /* there is a skypiax interface idle, let's transfer the call to it */ + DEBUGA_SKYPE + ("Let's transfer the skype_call %s to %s interface, because we are already in a skypiax call(%s)\n", + SKYPIAX_P_LOG, tech_pvt->skype_call_id, + available_skypiax_interface->name, id); + sprintf(msg_to_skype, "ALTER CALL %s TRANSFER %s", id, + available_skypiax_interface->name); + } else { + /* no skypiax interfaces idle, let's refuse the call */ + DEBUGA_SKYPE + ("Let's refuse the skype_call %s, because we are already in a skypiax call(%s) and no other skypiax interfaces are available\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); + skypiax_sleep(10000); + DEBUGA_SKYPE + ("We (%s) have NOT answered a Skype RING on skype_call %s, because we are already in a skypiax call\n", + SKYPIAX_P_LOG, tech_pvt->skype_call_id, id); + } + } else { + /* we are calling out */ + tech_pvt->skype_callflow = CALLFLOW_STATUS_RINGING; + tech_pvt->interface_state = SKYPIAX_STATE_RINGING; + 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); + } + } else if (!strcasecmp(value, "EARLYMEDIA")) { + 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); + } 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)) { + 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'; + 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); + 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, "ROUTING")) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_ROUTING; + tech_pvt->interface_state = SKYPIAX_STATE_DIALING; + 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; + 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) || !strlen(tech_pvt->skype_call_id) + || !strcasecmp(tech_pvt->skype_call_id, id)) { + tech_pvt->skype_callflow = CALLFLOW_STATUS_INPROGRESS; + strncpy(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1); + tech_pvt->interface_state = SKYPIAX_STATE_UP; + DEBUGA_SKYPE("skype_call: %s is now active\n", SKYPIAX_P_LOG, id); + 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); + start_audio_threads(tech_pvt); + sprintf(msg_to_skype, "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 = SKYPIAX_STATE_UP; + if (!strlen(tech_pvt->session_uuid_str)) { + DEBUGA_SKYPE("New Inbound Channel!\n", SKYPIAX_P_LOG); + new_inbound_channel(tech_pvt); + } else { + DEBUGA_SKYPE("Outbound Channel Answered!\n", SKYPIAX_P_LOG); + 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 { + 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); + 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); + 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); + 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]; + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_addr.s_addr = htonl(0x7f000001); /* use the localhost */ + my_addr.sin_port = htons(tech_pvt->tcp_srv_port); + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ERRORA("socket Error\n", SKYPIAX_P_LOG); + return NULL; + } + + if (bind(s, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) < 0) { + ERRORA("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 ((fd = accept(s, (struct sockaddr *) &remote_addr, &sin_size)) > 0) { + DEBUGA_SKYPE("ACCEPTED\n", SKYPIAX_P_LOG); + if (!running) + break; + while (tech_pvt->interface_state != SKYPIAX_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == SKYPIAX_STATE_UP)) { + + unsigned int fdselect; + int rt; + fd_set fs; + struct timeval to; + + if (!running) + break; + exit = 1; + + fdselect = fd; + FD_ZERO(&fs); + FD_SET(fdselect, &fs); + to.tv_usec = 2000000; //2000 msec + to.tv_sec = 0; + + rt = select(fdselect + 1, &fs, NULL, NULL, &to); + if (rt > 0) { + + len = recv(fd, (char *) srv_in, 320, 0); //seems that Skype only sends 320 bytes at time + + 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[1], totalbuf, + SAMPLES_PER_FRAME * sizeof(short)); + 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 { + ERRORA("len=%d\n", SKYPIAX_P_LOG, len); + exit = 1; + break; + } + + } else { + if (rt) + ERRORA("SRV rt=%d\n", SKYPIAX_P_LOG, rt); + skypiax_sleep(10000); + } + + } + + /* let's send some frame in the pipes, so both tcp_cli and tcp_srv will have an occasion to die */ + kill_cli_size = SAMPLES_PER_FRAME * sizeof(short); + len = skypiax_pipe_write(tech_pvt->audiopipe[1], kill_cli_buff, kill_cli_size); + kill_cli_size = SAMPLES_PER_FRAME * sizeof(short); + len = skypiax_pipe_write(tech_pvt->audioskypepipe[1], kill_cli_buff, kill_cli_size); + tech_pvt->interface_state = SKYPIAX_STATE_DOWN; + kill_cli_size = SAMPLES_PER_FRAME * sizeof(short); + len = skypiax_pipe_write(tech_pvt->audiopipe[1], kill_cli_buff, kill_cli_size); + kill_cli_size = SAMPLES_PER_FRAME * sizeof(short); + len = skypiax_pipe_write(tech_pvt->audioskypepipe[1], kill_cli_buff, kill_cli_size); + + DEBUGA_SKYPE("Skype incoming audio GONE\n", SKYPIAX_P_LOG); + skypiax_close_socket(fd); + if (exit) + break; + } + + DEBUGA_SKYPE("incoming audio server (I am it) EXITING\n", SKYPIAX_P_LOG); + skypiax_close_socket(s); + return NULL; +} + +void *skypiax_do_tcp_cli_thread_func(void *obj) +{ + private_t *tech_pvt = obj; + int s; + struct sockaddr_in my_addr; + struct sockaddr_in remote_addr; + unsigned int got; + unsigned int len; + unsigned int i; + unsigned int a; + unsigned int fd; + short cli_out[SAMPLES_PER_FRAME * 2]; + short cli_in[SAMPLES_PER_FRAME]; +#ifdef WIN32 + int sin_size; +#else + unsigned int sin_size; +#endif /* WIN32 */ + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_addr.s_addr = htonl(0x7f000001); /* use the localhost */ + my_addr.sin_port = htons(tech_pvt->tcp_cli_port); + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ERRORA("socket Error\n", SKYPIAX_P_LOG); + return NULL; + } + + if (bind(s, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) < 0) { + ERRORA("bind Error\n", SKYPIAX_P_LOG); + skypiax_close_socket(s); + return NULL; + } + DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG); + + listen(s, 6); + + sin_size = sizeof(remote_addr); + while ((fd = accept(s, (struct sockaddr *) &remote_addr, &sin_size)) > 0) { + DEBUGA_SKYPE("ACCEPTED\n", SKYPIAX_P_LOG); + if (!running) + break; + while (tech_pvt->interface_state != SKYPIAX_STATE_DOWN + && (tech_pvt->skype_callflow == CALLFLOW_STATUS_INPROGRESS + || tech_pvt->skype_callflow == SKYPIAX_STATE_UP)) { + unsigned int fdselect; + int rt; + fd_set fs; + struct timeval to; + + if (!running) + break; + FD_ZERO(&fs); + to.tv_usec = 60000; //60msec + 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 */ + fdselect = tech_pvt->audioskypepipe[0]; + FD_SET(fdselect, &fs); + + rt = select(fdselect + 1, &fs, NULL, NULL, &to); +#endif + + if (rt > 0) { + /* read from the pipe the audio frame we are supposed to send out */ + got = + skypiax_pipe_read(tech_pvt->audioskypepipe[0], cli_in, + SAMPLES_PER_FRAME * sizeof(short)); + 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 */ + len = send(fd, (char *) cli_out, got, 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; + } + + } 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)); + len = send(fd, (char *) cli_out, sizeof(cli_out), 0); + len = send(fd, (char *) cli_out, sizeof(cli_out) / 2, 0); + //DEBUGA_SKYPE("sent %d of zeros to keep the Skype client socket busy\n", SKYPIAX_P_LOG, sizeof(cli_out) + sizeof(cli_out)/2); + } + + } + DEBUGA_SKYPE("Skype outbound audio GONE\n", SKYPIAX_P_LOG); + skypiax_close_socket(fd); + break; + } + + DEBUGA_SKYPE("outbound audio server (I am it) EXITING\n", SKYPIAX_P_LOG); + skypiax_close_socket(s); + return NULL; +} + +int skypiax_audio_read(private_t * tech_pvt) +{ + unsigned int samples; + + samples = + skypiax_pipe_read(tech_pvt->audiopipe[0], tech_pvt->read_frame.data, + SAMPLES_PER_FRAME * sizeof(short)); + + 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[0], &tech_pvt->audiopipe[1], + skypiax_module_pool); + rv = + switch_file_pipe_create(&tech_pvt->audioskypepipe[0], &tech_pvt->audioskypepipe[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) +{ + howmany = write(pipe, buf, howmany); + return howmany; +} +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)) { + fcntl(tech_pvt->audiopipe[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->audiopipe[1], F_SETFL, O_NONBLOCK); + } + if (pipe(tech_pvt->audioskypepipe)) { + fcntl(tech_pvt->audioskypepipe[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->audioskypepipe[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[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); + 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)); + 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.api_connected) { + DEBUGA_SKYPE("\n\n\tConnected to Skype API!\n", SKYPIAX_P_LOG); + 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 (!tech_pvt->SkypiaxHandles.api_connected) { + SendMessage(HWND_BROADCAST, + tech_pvt->SkypiaxHandles. + win32_uiGlobal_MsgID_SkypeControlAPIDiscover, + (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, + 0); + } + 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 (!tech_pvt->SkypiaxHandles.api_connected) { + SendMessage(HWND_BROADCAST, + tech_pvt->SkypiaxHandles. + win32_uiGlobal_MsgID_SkypeControlAPIDiscover, + (WPARAM) tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, + 0); + } + 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(2000000); + + 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) { + MSG oMessage; + if (!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) +{ + (void) dpy; + private_t *tech_pvt = NULL; + + xerror = err->error_code; + ERRORA("Received error code %d from X Server\n\n", SKYPIAX_P_LOG, xerror); + return 0; /* ignore the error */ +} + +static void X11_errors_trap(void) +{ + xerror = 0; + old_handler = XSetErrorHandler(X11_errors_handler); +} + +static int X11_errors_untrap(void) +{ + XSetErrorHandler(old_handler); + return (xerror != BadValue) && (xerror != BadWindow); +} + +int skypiax_send_message(struct SkypiaxHandles *SkypiaxHandles, const char *message_P) +{ + + Window w_P; + Display *disp; + Window handle_P; + int ok; + private_t *tech_pvt = NULL; + + w_P = SkypiaxHandles->skype_win; + disp = SkypiaxHandles->disp; + handle_P = SkypiaxHandles->win; + + 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); + //XUnlockDisplay(disp); + + return 1; +} +int skypiax_signaling_write(private_t * tech_pvt, char *msg_to_skype) +{ + struct SkypiaxHandles *SkypiaxHandles; + + DEBUGA_SKYPE("SENDING: |||%s||||\n", SKYPIAX_P_LOG, msg_to_skype); + + SkypiaxHandles = &tech_pvt->SkypiaxHandles; + + if (!skypiax_send_message(SkypiaxHandles, 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); + 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); + 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; + + 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); + } + + int xfd; + 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(SkypiaxHandles, buf)) { + ERRORA + ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypiax again\n", + SKYPIAX_P_LOG); + running = 0; + return NULL; + } + + snprintf(buf, 512, "PROTOCOL 7"); + if (!skypiax_send_message(SkypiaxHandles, buf)) { + ERRORA + ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch Skypiax again\n", + SKYPIAX_P_LOG); + running = 0; + return NULL; + } + + /* perform an events loop */ + XEvent an_event; + char buf[21]; /* can't be longer */ + char buffer[17000]; + char *b; + int i; + + b = buffer; + + while (1) { + XNextEvent(disp, &an_event); + if (!running) + break; + switch (an_event.type) { + case ClientMessage: + + if (an_event.xclient.format != 8) + 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'; + + strcat(buffer, buf); + + if (i < 20) { /* last fragment */ + unsigned int howmany; + + howmany = strlen(b) + 1; + + howmany = write(SkypiaxHandles->fdesc[1], b, howmany); + memset(buffer, '\0', 17000); + } + + break; + default: + break; + } + } + } else { + ERRORA + ("Skype is not running, maybe crashed. Please run/restart Skype and relaunch Skypiax\n", + SKYPIAX_P_LOG); + running = 0; + return NULL; + } + running = 0; + return NULL; + +} +#endif // WIN32