diff --git a/Freeswitch.2017.sln b/Freeswitch.2017.sln
index 1962a9b283..be107fde3e 100644
--- a/Freeswitch.2017.sln
+++ b/Freeswitch.2017.sln
@@ -576,6 +576,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "48khz", "libs\win32\Sound_F
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "48khz music", "libs\win32\Sound_Files\48khzmusic.2017.vcxproj", "{EBD0B6B4-C5CA-46B0-BBC7-DBA71DF05D31}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_signalwire", "src\mod\applications\mod_signalwire\mod_signalwire.2017.vcxproj", "{B19AE6FC-BFFF-428D-B483-3BBEAECCC618}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
All|Win32 = All|Win32
@@ -2627,6 +2629,18 @@ Global
{EBD0B6B4-C5CA-46B0-BBC7-DBA71DF05D31}.Debug|x64.Build.0 = Debug|x64
{EBD0B6B4-C5CA-46B0-BBC7-DBA71DF05D31}.Release|Win32.ActiveCfg = Release|Win32
{EBD0B6B4-C5CA-46B0-BBC7-DBA71DF05D31}.Release|x64.ActiveCfg = Release|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.All|Win32.ActiveCfg = Release|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.All|Win32.Build.0 = Release|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.All|x64.ActiveCfg = Release|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.All|x64.Build.0 = Release|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Debug|Win32.Build.0 = Debug|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Debug|x64.ActiveCfg = Debug|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Debug|x64.Build.0 = Debug|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Release|Win32.ActiveCfg = Release|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Release|Win32.Build.0 = Release|Win32
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Release|x64.ActiveCfg = Release|x64
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2836,6 +2850,7 @@ Global
{EF62B845-A0CE-44FD-B8E6-475FE87D06C3} = {9388C266-C3FC-468A-92EF-0CBC35941412}
{8154C82D-58EE-4145-9DEC-A445A5AA3D6B} = {4F227C26-768F-46A3-8684-1D08A46FB374}
{EBD0B6B4-C5CA-46B0-BBC7-DBA71DF05D31} = {4F227C26-768F-46A3-8684-1D08A46FB374}
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618} = {E72B5BCB-6462-4D23-B419-3AF1A4AC3D78}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {09840DE7-9208-45AA-9667-1A71EE93BD1E}
diff --git a/build/modules.conf.in b/build/modules.conf.in
index d998e5dba8..a3297e744c 100644
--- a/build/modules.conf.in
+++ b/build/modules.conf.in
@@ -39,6 +39,7 @@ applications/mod_httapi
#applications/mod_rad_auth
#applications/mod_redis
#applications/mod_rss
+applications/mod_signalwire
applications/mod_sms
#applications/mod_sms_flowroute
#applications/mod_snapshot
diff --git a/build/modules.conf.most b/build/modules.conf.most
index b442b966e6..a4946004d2 100644
--- a/build/modules.conf.most
+++ b/build/modules.conf.most
@@ -38,6 +38,7 @@ applications/mod_prefix
#applications/mod_rad_auth
applications/mod_redis
applications/mod_rss
+applications/mod_signalwire
applications/mod_sms
applications/mod_snapshot
applications/mod_snom
diff --git a/conf/insideout/autoload_configs/modules.conf.xml b/conf/insideout/autoload_configs/modules.conf.xml
index 3e2b09cdcb..5078503489 100644
--- a/conf/insideout/autoload_configs/modules.conf.xml
+++ b/conf/insideout/autoload_configs/modules.conf.xml
@@ -37,6 +37,7 @@
+
diff --git a/conf/minimal/autoload_configs/modules.conf.xml b/conf/minimal/autoload_configs/modules.conf.xml
index 37ece9d202..a57544f2fe 100644
--- a/conf/minimal/autoload_configs/modules.conf.xml
+++ b/conf/minimal/autoload_configs/modules.conf.xml
@@ -17,6 +17,7 @@
+
diff --git a/conf/rayo/autoload_configs/modules.conf.xml b/conf/rayo/autoload_configs/modules.conf.xml
index f6c58680bc..ee44e06c57 100644
--- a/conf/rayo/autoload_configs/modules.conf.xml
+++ b/conf/rayo/autoload_configs/modules.conf.xml
@@ -14,6 +14,7 @@
+
diff --git a/conf/sbc/autoload_configs/modules.conf.xml b/conf/sbc/autoload_configs/modules.conf.xml
index 579b1d33be..31f46a4128 100644
--- a/conf/sbc/autoload_configs/modules.conf.xml
+++ b/conf/sbc/autoload_configs/modules.conf.xml
@@ -26,6 +26,7 @@
+
diff --git a/conf/softphone/freeswitch.xml b/conf/softphone/freeswitch.xml
index 771efd9544..5fbc0cbd39 100644
--- a/conf/softphone/freeswitch.xml
+++ b/conf/softphone/freeswitch.xml
@@ -89,6 +89,7 @@
+
diff --git a/conf/vanilla/autoload_configs/modules.conf.xml b/conf/vanilla/autoload_configs/modules.conf.xml
index 2c465d1231..0b336a168d 100644
--- a/conf/vanilla/autoload_configs/modules.conf.xml
+++ b/conf/vanilla/autoload_configs/modules.conf.xml
@@ -50,6 +50,7 @@
+
diff --git a/configure.ac b/configure.ac
index 1144a5faac..4045e72f62 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1498,6 +1498,14 @@ PKG_CHECK_MODULES([V8FS_STATIC], [v8-6.1_static >= 6.1.298],[
])
])
+PKG_CHECK_MODULES([KS], [libks >= 1.1.0],[
+ AM_CONDITIONAL([HAVE_KS],[true])],[
+ AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_KS],[false])])
+
+PKG_CHECK_MODULES([SIGNALWIRE_CLIENT], [signalwire_client >= 1.0.0],[
+ AM_CONDITIONAL([HAVE_SIGNALWIRE_CLIENT],[true])],[
+ AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_SIGNALWIRE_CLIENT],[false])])
+
PKG_CHECK_MODULES([AMQP], [librabbitmq >= 0.5.2],[
AM_CONDITIONAL([HAVE_AMQP],[true])],[
AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_AMQP],[false])])
@@ -1853,6 +1861,7 @@ AC_CONFIG_FILES([Makefile
src/mod/applications/mod_redis/Makefile
src/mod/applications/mod_rss/Makefile
src/mod/applications/mod_skel/Makefile
+ src/mod/applications/mod_signalwire/Makefile
src/mod/applications/mod_sms/Makefile
src/mod/applications/mod_sms_flowroute/Makefile
src/mod/applications/mod_snapshot/Makefile
diff --git a/debian/control-modules b/debian/control-modules
index b19e12e575..d27ebbbc37 100644
--- a/debian/control-modules
+++ b/debian/control-modules
@@ -206,6 +206,11 @@ Module: applications/mod_skel
Description: Adds mod_skel
Adds mod_skel.
+Module: applications/mod_signalwire
+Description: mod_signalwire
+ Adds mod_signalwire.
+Build-Depends: libks, signalwire-client-c
+
Module: applications/mod_sms
Description: Astract SMS
This module provides an abstract facility for interfacing with SMS
diff --git a/freeswitch.spec b/freeswitch.spec
index 0cc0462323..f0fee3ca72 100644
--- a/freeswitch.spec
+++ b/freeswitch.spec
@@ -3,7 +3,7 @@
# spec file for package freeswitch
#
# includes module(s): freeswitch-devel freeswitch-codec-passthru-amr freeswitch-codec-passthru-amrwb freeswitch-codec-passthru-g729
-# freeswitch-codec-passthru-g7231 freeswitch-lua freeswitch-perl freeswitch-python freeswitch-v8
+# freeswitch-codec-passthru-g7231 freeswitch-lua freeswitch-perl freeswitch-python freeswitch-v8 freeswitch-signalwire
# freeswitch-lan-de freeswitch-lang-en freeswitch-lang-fr freeswitch-lang-hu freeswitch-lang-ru freeswitch-freetdm
# and others
#
@@ -536,6 +536,14 @@ Requires: %{name} = %{version}-%{release}
Provides FreeSWITCH mod_rss, edisrse and read an XML based RSS feed, then read
the entries aloud via a TTS engine
+%package application-signalwire
+Summary: FreeSWITCH mod_signalwire
+Group: System/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description application-signalwire
+Provides FreeSWITCH mod_signalwire
+
%package application-sms
Summary: FreeSWITCH mod_sms
Group: System/Libraries
@@ -1373,6 +1381,7 @@ Requires: freeswitch-application-memcache
Requires: freeswitch-application-nibblebill
Requires: freeswitch-application-redis
Requires: freeswitch-application-rss
+Requires: freeswitch-application-signalwire
Requires: freeswitch-application-sms
Requires: freeswitch-application-snapshot
Requires: freeswitch-application-snom
@@ -1458,7 +1467,7 @@ APPLICATION_MODULES_FR="applications/mod_fifo applications/mod_fsk applications/
applications/mod_memcache applications/mod_mongo applications/mod_nibblebill applications/mod_rad_auth \
applications/mod_redis applications/mod_rss "
-APPLICATION_MODULES_SZ="applications/mod_sms applications/mod_snapshot applications/mod_snom applications/mod_soundtouch \
+APPLICATION_MODULES_SZ="applications/mod_signalwire applications/mod_sms applications/mod_snapshot applications/mod_snom applications/mod_soundtouch \
applications/mod_spandsp applications/mod_spy applications/mod_stress \
applications/mod_valet_parking applications/mod_translate applications/mod_voicemail \
applications/mod_voicemail_ivr applications/mod_video_filter"
@@ -2115,6 +2124,9 @@ fi
%files application-rss
%{MODINSTDIR}/mod_rss.so*
+%files application-signalwire
+%{MODINSTDIR}/mod_signalwire.so*
+
%files application-sms
%{MODINSTDIR}/mod_sms.so*
@@ -2541,6 +2553,8 @@ fi
#
######################################################################################################################
%changelog
+* Tue Dec 11 2018 - Andrey Volk
+- add mod_signalwire
* Sun Mar 13 2016 - Matthew Vale
- add perl and python ESL language module packages
* Thu Jul 09 2015 - Artur ZaprzaĆa
diff --git a/libs/.gitignore b/libs/.gitignore
index 8555f1a4f1..10b50dda73 100644
--- a/libs/.gitignore
+++ b/libs/.gitignore
@@ -858,3 +858,8 @@ libsndfile-*/
libsndfile-*
opencv-*/
opencv-*
+libks-*/
+libks*
+signalwire-client-c-*/
+signalwire-client-c-*
+
diff --git a/src/mod/applications/mod_signalwire/Makefile.am b/src/mod/applications/mod_signalwire/Makefile.am
new file mode 100644
index 0000000000..d46251b832
--- /dev/null
+++ b/src/mod/applications/mod_signalwire/Makefile.am
@@ -0,0 +1,27 @@
+include $(top_srcdir)/build/modmake.rulesam
+MODNAME=mod_signalwire
+
+if HAVE_KS
+if HAVE_SIGNALWIRE_CLIENT
+
+mod_LTLIBRARIES = mod_signalwire.la
+mod_signalwire_la_SOURCES = mod_signalwire.c
+mod_signalwire_la_CFLAGS = $(AM_CFLAGS)
+mod_signalwire_la_CFLAGS += $(KS_CFLAGS) $(SIGNALWIRE_CLIENT_CFLAGS)
+mod_signalwire_la_LIBADD = $(switch_builddir)/libfreeswitch.la
+mod_signalwire_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(KS_LIBS) $(SIGNALWIRE_CLIENT_LIBS)
+
+else
+install: error
+all: error
+error:
+ $(error You must install signalwire-client-c to build mod_signalwire)
+endif
+
+else
+install: error
+all: error
+error:
+ $(error You must install libks to build mod_signalwire)
+endif
+
diff --git a/src/mod/applications/mod_signalwire/conf/autoload_configs/signalwire.conf.xml b/src/mod/applications/mod_signalwire/conf/autoload_configs/signalwire.conf.xml
new file mode 100644
index 0000000000..c62ad72ff0
--- /dev/null
+++ b/src/mod/applications/mod_signalwire/conf/autoload_configs/signalwire.conf.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mod/applications/mod_signalwire/mod_signalwire.2017.vcxproj b/src/mod/applications/mod_signalwire/mod_signalwire.2017.vcxproj
new file mode 100644
index 0000000000..192c82d51d
--- /dev/null
+++ b/src/mod/applications/mod_signalwire/mod_signalwire.2017.vcxproj
@@ -0,0 +1,149 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ mod_signalwire
+ mod_signalwire
+ Win32Proj
+ {B19AE6FC-BFFF-428D-B483-3BBEAECCC618}
+
+
+
+ DynamicLibrary
+ MultiByte
+ v141
+
+
+ DynamicLibrary
+ MultiByte
+ v141
+
+
+ DynamicLibrary
+ MultiByte
+ v141
+
+
+ DynamicLibrary
+ MultiByte
+ v141
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+
+
+
+ .;%(AdditionalIncludeDirectories)
+
+
+
+
+
+
+
+
+ _DEBUG;DEBUG;%(PreprocessorDefinitions)
+
+
+ false
+
+
+
+
+
+
+ X64
+
+
+
+
+ _DEBUG;DEBUG;%(PreprocessorDefinitions)
+
+
+ false
+
+
+ MachineX64
+
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ false
+
+
+
+
+
+
+ X64
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ false
+
+
+ MachineX64
+
+
+
+
+
+
+
+ {202d7a4e-760d-4d0e-afa1-d7459ced30ff}
+ false
+
+
+
+
+
+
diff --git a/src/mod/applications/mod_signalwire/mod_signalwire.c b/src/mod/applications/mod_signalwire/mod_signalwire.c
new file mode 100644
index 0000000000..5fd2fe828e
--- /dev/null
+++ b/src/mod/applications/mod_signalwire/mod_signalwire.c
@@ -0,0 +1,1308 @@
+/*
+ * mod_signalwire.c -- SignalWire module
+ *
+ * Copyright (c) 2018 SignalWire, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+#include
+#endif
+
+#define SW_KS_JSON_PRINT(_h, _j) do { \
+ char *_json = ks_json_print(_j); \
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT, "--- %s ---\n%s\n---\n", _h, _json); \
+ ks_json_free(&_json); \
+ } while (0)
+
+static int debug_level = 7;
+
+static int signalwire_gateway_exists(void);
+
+/* Prototypes */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_signalwire_shutdown);
+SWITCH_MODULE_LOAD_FUNCTION(mod_signalwire_load);
+SWITCH_MODULE_RUNTIME_FUNCTION(mod_signalwire_runtime);
+
+/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
+ * Defines a switch_loadable_module_function_table_t and a static const char[] modname
+ */
+SWITCH_MODULE_DEFINITION(mod_signalwire, mod_signalwire_load, mod_signalwire_shutdown, mod_signalwire_runtime);
+
+typedef enum {
+ SW_STATE_ADOPTION,
+ SW_STATE_OFFLINE,
+ SW_STATE_ONLINE,
+ SW_STATE_CONFIGURE,
+ SW_STATE_START_PROFILE,
+ SW_STATE_REGISTER,
+ SW_STATE_READY,
+} sw_state_t;
+
+static struct {
+ int ssl_verify;
+ ks_bool_t shutdown;
+ ks_bool_t restarting;
+ swclt_config_t *config;
+ char blade_bootstrap[1024];
+ char adoption_service[1024];
+ char stun_server[1024];
+ char adoption_token[64];
+ ks_size_t adoption_backoff;
+ ks_time_t adoption_next;
+
+ char adoption_data_local_ip[256];
+ char adoption_data_external_ip[256];
+ char adoption_data_uname[1024];
+
+ char relay_connector_id[256];
+
+ swclt_sess_t signalwire_session;
+ swclt_hmon_t signalwire_session_monitor;
+ sw_state_t state;
+ ks_bool_t profile_update;
+ ks_bool_t profile_reload;
+ ks_bool_t signalwire_reconnected;
+ switch_xml_t signalwire_profile;
+ char signalwire_profile_md5[SWITCH_MD5_DIGEST_STRING_SIZE];
+
+ ks_bool_t kslog_on;
+
+ switch_mutex_t *mutex; // general mutex for this mod
+ char gateway_ip[80];
+ char gateway_port[10];
+} globals;
+
+static void mod_signalwire_kslogger(const char *file, const char *func, int line, int level, const char *fmt, ...)
+{
+ const char *fp;
+ va_list ap;
+ char buf[32768];
+
+ if (level > debug_level) return;
+
+ fp = switch_cut_path(file);
+
+ va_start(ap, fmt);
+
+ vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
+ buf[sizeof(buf) - 1] = '\0';
+
+ va_end(ap);
+
+ switch_log_printf(SWITCH_CHANNEL_ID_LOG, fp, func, line, NULL, level, "%s\n", buf);
+}
+
+static switch_status_t switch_find_available_port(switch_port_t *port, const char *ip, int type)
+{
+ switch_status_t ret = SWITCH_STATUS_SUCCESS;
+ switch_memory_pool_t *pool = NULL;
+ switch_sockaddr_t *addr = NULL;
+ switch_socket_t *sock = NULL;
+ switch_bool_t found = SWITCH_FALSE;
+
+ if ((ret = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ while (!found) {
+ if ((ret = switch_sockaddr_info_get(&addr, ip, SWITCH_UNSPEC, *port, 0, pool)) != SWITCH_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if ((ret = switch_socket_create(&sock, switch_sockaddr_get_family(addr), type, 0, pool)) != SWITCH_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if (!(found = (switch_socket_bind(sock, addr) == SWITCH_STATUS_SUCCESS))) {
+ *port = *port + 1;
+ }
+
+ switch_socket_close(sock);
+ }
+
+done:
+ if (pool) switch_core_destroy_memory_pool(&pool);
+
+ return ret;
+}
+
+struct response_data {
+ char *data;
+ size_t size;
+};
+
+static size_t response_data_handler(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ size_t received = size * nmemb;
+ struct response_data *rd = (struct response_data *)userp;
+
+ if (!rd->data) rd->data = ks_pool_alloc(NULL, received + 1);
+ else rd->data = ks_pool_resize(rd->data, rd->size + received + 1);
+
+ memcpy(rd->data + rd->size, contents, received);
+ rd->size += received;
+ rd->data[rd->size] = 0;
+
+ return received;
+}
+
+static void save_sip_config(const char *config)
+{
+ char confpath[1024];
+ FILE *fp = NULL;
+
+ switch_snprintf(confpath, sizeof(confpath), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "signalwire-conf.dat");
+ fp = fopen(confpath, "w");
+ if (!fp) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open %s to save SignalWire SIP configuration\n", confpath);
+ return;
+ }
+
+ fputs(config, fp);
+ fclose(fp);
+}
+
+static void load_sip_config(void)
+{
+ char confpath[1024];
+ char data[32767] = { 0 };
+ FILE *fp = NULL;
+
+ switch_snprintf(confpath, sizeof(confpath), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "signalwire-conf.dat");
+ if (!(fp = fopen(confpath, "r"))) return;
+
+ if (!fread(data, 1, sizeof(data), fp)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read SignalWire SIP configuration from %s\n", confpath);
+ }
+ fclose(fp);
+ if (!zstr_buf(data)) {
+ switch_md5_string(globals.signalwire_profile_md5, (void *) data, strlen(data));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "saved profile MD5 = \"%s\"\n", globals.signalwire_profile_md5);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "saved profile = \"%s\"\n", (char *)data);
+ globals.signalwire_profile = switch_xml_parse_str_dynamic((char *)data, SWITCH_TRUE);
+ }
+}
+
+static ks_status_t load_credentials_from_json(ks_json_t *json)
+{
+ ks_status_t status = KS_STATUS_SUCCESS;
+ ks_json_t *authentication = NULL;
+ const char *authentication_str = NULL;
+ const char *bootstrap = NULL;
+ const char *relay_connector_id = NULL;
+
+ if ((bootstrap = ks_json_get_object_cstr(json, "bootstrap")) == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to connect to SignalWire: missing bootstrap URL\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ if ((relay_connector_id = ks_json_get_object_cstr(json, "relay_connector_id")) == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to connect to SignalWire: missing relay_connector_id\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ if ((authentication = ks_json_get_object_item(json, "authentication")) == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to connect to SignalWire: missing authentication\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ // update the internal connection target, which is normally assigned in swclt_sess_create()
+ if (swclt_sess_target_set(globals.signalwire_session, bootstrap) != KS_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to connect to SignalWire at %s\n", bootstrap);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ // update the relay_connector_id passed to profile configuration
+ strncpy(globals.relay_connector_id, relay_connector_id, sizeof(globals.relay_connector_id) - 1);
+ strncpy(globals.blade_bootstrap, bootstrap, sizeof(globals.blade_bootstrap) - 1);
+
+ // got adopted, update the client config authentication
+ authentication_str = ks_json_pprint_unformatted(NULL, authentication);
+ swclt_config_set_authentication(globals.config, authentication_str);
+
+ ks_pool_free(&authentication_str);
+done:
+
+ return status;
+}
+
+static ks_status_t mod_signalwire_adoption_post(void)
+{
+ ks_status_t status = KS_STATUS_SUCCESS;
+ switch_memory_pool_t *pool = NULL;
+ switch_CURL *curl = NULL;
+ switch_curl_slist_t *headers = NULL;
+ char url[1024];
+ char errbuf[CURL_ERROR_SIZE];
+ CURLcode res;
+ long rescode;
+ ks_json_t *json = ks_json_create_object();
+ struct response_data rd = { 0 };
+ char *jsonstr = NULL;
+
+ // Determine and cache adoption data values that are heavier to figure out
+ if (!globals.adoption_data_local_ip[0]) {
+ switch_find_local_ip(globals.adoption_data_local_ip, sizeof(globals.adoption_data_local_ip), NULL, AF_INET);
+ }
+
+ if (!globals.adoption_data_external_ip[0]) {
+ switch_port_t local_port = 6050;
+ char *error = NULL;
+ char *external_ip;
+ switch_port_t external_port;
+
+ if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SignalWire adoption failed: could not allocate memory pool\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+ if (switch_find_available_port(&local_port, globals.adoption_data_local_ip, SOCK_STREAM) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire adoption failed: could not get available local port\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ external_ip = globals.adoption_data_local_ip;
+ external_port = local_port;
+ if (switch_stun_lookup(&external_ip, &external_port, globals.stun_server, SWITCH_STUN_DEFAULT_PORT, &error, pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire adoption failed: stun [%s] lookup error: %s\n", globals.stun_server, error);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+ strncpy(globals.adoption_data_external_ip, external_ip, sizeof(globals.adoption_data_external_ip));
+ }
+
+ if (!globals.adoption_data_uname[0]) {
+#ifndef WIN32
+ struct utsname buf;
+ if (uname(&buf)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire adoption failed: could not get uname\n");
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+ switch_snprintf(globals.adoption_data_uname,
+ sizeof(globals.adoption_data_uname),
+ "%s %s %s %s %s",
+ buf.sysname,
+ buf.nodename,
+ buf.release,
+ buf.version,
+ buf.machine);
+#else
+ // @todo set globals.adoption_data_uname from GetVersion Win32API
+#endif
+ }
+
+
+ ks_json_add_string_to_object(json, "client_uuid", globals.adoption_token);
+ ks_json_add_string_to_object(json, "hostname", switch_core_get_hostname());
+ ks_json_add_string_to_object(json, "ip", globals.adoption_data_local_ip);
+ ks_json_add_string_to_object(json, "ext_ip", globals.adoption_data_external_ip);
+ ks_json_add_string_to_object(json, "version", switch_version_full());
+ ks_json_add_string_to_object(json, "uname", globals.adoption_data_uname);
+
+ jsonstr = ks_json_print_unformatted(json);
+ ks_json_delete(&json);
+
+ switch_snprintf(url, sizeof(url), "%s/%s", globals.adoption_service, globals.adoption_token);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Checking %s for SignalWire adoption of this FreeSWITCH\n", url);
+
+ curl = switch_curl_easy_init();
+
+ headers = switch_curl_slist_append(headers, "Accept: application/json");
+ headers = switch_curl_slist_append(headers, "Accept-Charset: utf-8");
+ headers = switch_curl_slist_append(headers, "Content-Type: application/json");
+
+ switch_curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
+ switch_curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
+
+ if (!strncasecmp(url, "https", 5)) {
+ switch_curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, globals.ssl_verify);
+ switch_curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, globals.ssl_verify);
+ }
+
+ switch_curl_easy_setopt(curl, CURLOPT_URL, url);
+ switch_curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ switch_curl_easy_setopt(curl, CURLOPT_USERAGENT, "mod_signalwire/1");
+ switch_curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonstr);
+ switch_curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
+ switch_curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&rd);
+ switch_curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, response_data_handler);
+
+ if ((res = switch_curl_easy_perform(curl))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Curl Result %d, Error: %s\n", res, errbuf);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ switch_curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rescode);
+
+ if (rescode == 404) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
+ "Go to https://signalwire.com to set up your Connector now! Enter connection token %s\n", globals.adoption_token);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ if (rescode != 200) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire adoption failed with HTTP code %ld, %s\n", rescode, rd.data);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ json = ks_json_parse(rd.data);
+ if (!json) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received bad SignalWire adoption response\n%s\n", rd.data);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ if ((status = load_credentials_from_json(json)) != KS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ks_json_delete(&json);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire adoption of this FreeSWITCH completed\n");
+
+ // write out the data to save it for reloading in the future
+ {
+ char authpath[1024];
+ FILE *fp = NULL;
+
+ switch_snprintf(authpath, sizeof(authpath), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "adoption-auth.dat");
+ fp = fopen(authpath, "w");
+ if (!fp) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open %s to save SignalWire creds\n", authpath);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ fputs(rd.data, fp);
+ fclose(fp);
+ }
+
+ globals.state = SW_STATE_OFFLINE;
+ swclt_sess_connect(globals.signalwire_session);
+
+done:
+ if (rd.data) ks_pool_free(&rd.data);
+ if (jsonstr) ks_json_free_ex((void **)&jsonstr);
+ if (json) ks_json_delete(&json);
+ if (curl) {
+ curl_easy_cleanup(curl);
+ if (headers) curl_slist_free_all(headers);
+ }
+ if (pool) switch_core_destroy_memory_pool(&pool);
+ return status;
+}
+
+#define SIGNALWIRE_SYNTAX "token | adoption | adopted | reload | update | debug | kslog "
+SWITCH_STANDARD_API(mod_signalwire_api_function)
+{
+ int argc = 0;
+ char *argv[2] = { 0 };
+ char *buf = NULL;
+
+
+ if (!cmd || !(buf = strdup(cmd))) {
+ stream->write_function(stream, "-USAGE: signalwire %s\n", SIGNALWIRE_SYNTAX);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if ((argc = switch_separate_string(buf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+ if (!strcmp(argv[0], "token")) {
+ if (globals.adoption_token[0]) {
+ stream->write_function(stream,
+ " _____ _ ___ ___\n"
+ " / ___/(_)___ _____ ____ _/ / | / (_)_______\n"
+ " \\__ \\/ / __ `/ __ \\/ __ `/ /| | /| / / / ___/ _ \\\n"
+ " ___/ / / /_/ / / / / /_/ / / | |/ |/ / / / / __/\n"
+ " /____/_/\\__, /_/ /_/\\__,_/_/ |__/|__/_/_/ \\___/\n"
+ " /____/\n"
+ "\n /=====================================================================\\\n"
+ "| Connection Token: %s |\n"
+ " \\=====================================================================/\n"
+ " Go to https://signalwire.com to set up your Connector now!\n", globals.adoption_token);
+ } else {
+ stream->write_function(stream, "-ERR connection token not available\n");
+ }
+ goto done;
+ }
+ else if (!strcmp(argv[0], "adoption")) {
+ if (globals.state == SW_STATE_ADOPTION) {
+ globals.adoption_next = ks_time_now();
+ stream->write_function(stream, "+OK\n");
+ } else {
+ stream->write_function(stream, "-ERR adoption not currently pending\n");
+ }
+ goto done;
+ }
+ else if (!strcmp(argv[0], "adopted")) {
+ stream->write_function(stream, "+OK %s\n", globals.state == SW_STATE_ADOPTION ? "Not Adopted" : "Adopted");
+ goto done;
+ }
+ else if (!strcmp(argv[0], "debug")) {
+ if (argv[1]) {
+ debug_level = atoi(argv[1]);
+ }
+
+ stream->write_function(stream, "+OK debug %d\n", debug_level);
+ goto done;
+ } else if (!strcmp(argv[0], "kslog")) {
+ if (argv[1]) {
+ if (!strcmp(argv[1], "on")) {
+ ks_global_set_logger(mod_signalwire_kslogger);
+ } else if (!strcmp(argv[1], "off")) {
+ ks_global_set_logger(NULL);
+ }
+ }
+
+ stream->write_function(stream, "+OK %s\n", argv[1]);
+ goto done;
+ } else if (!strcmp(argv[0], "reload")) {
+ globals.profile_reload = KS_TRUE;
+ stream->write_function(stream, "+OK\n");
+ goto done;
+ } else if (!strcmp(argv[0], "update")) {
+ globals.profile_update = KS_TRUE;
+ stream->write_function(stream, "+OK\n");
+ goto done;
+ }
+ }
+
+ stream->write_function(stream, "-USAGE: signalwire %s\n", SIGNALWIRE_SYNTAX);
+
+done:
+ switch_safe_free(buf);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void mod_signalwire_session_state_handler(swclt_sess_t sess, swclt_hstate_change_t *state_change_info, const char *cb_data)
+{
+ SWCLT_HSTATE new_state = state_change_info->new_state;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire Session State Change: %s\n", swclt_hstate_describe_change(state_change_info));
+
+ if (new_state == SWCLT_HSTATE_ONLINE) {
+ // Connected with NEW or RESTORED session
+ globals.signalwire_reconnected = KS_TRUE;
+ } else if (new_state == SWCLT_HSTATE_OFFLINE) {
+ // Disconnected
+ }
+}
+
+static void __on_provisioning_events(swclt_sess_t sess, blade_broadcast_rqu_t *rqu, void *cb_data)
+{
+ if (!strcmp(rqu->event, "update")) {
+ globals.profile_update = KS_TRUE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire SIP profile update requested\n");
+ }
+}
+
+static switch_xml_t xml_config_handler(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params,
+ void *user_data)
+{
+ char *profileName = NULL;
+ char *reconfigValue = NULL;
+ switch_xml_t signalwire_profile_dup = NULL;
+
+ if (!section || strcmp(section, "configuration")) return NULL;
+ if (!key_name || strcmp(key_name, "name")) return NULL;
+ if (!key_value || strcmp(key_value, "sofia.conf")) return NULL;
+ if (!params) return NULL;
+ profileName = switch_event_get_header(params, "profile");
+ if (!profileName || strcmp(profileName, "signalwire")) return NULL;
+ reconfigValue = switch_event_get_header(params, "reconfig");
+ if (!reconfigValue || strcmp(reconfigValue, "true")) return NULL;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received XML lookup for SignalWire SIP profile\n");
+
+ if (globals.signalwire_profile) {
+ signalwire_profile_dup = switch_xml_dup(globals.signalwire_profile);
+ }
+ return signalwire_profile_dup;
+}
+
+static switch_status_t mod_signalwire_load_or_generate_token(void)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ char tokenpath[1024];
+
+ switch_snprintf(tokenpath, sizeof(tokenpath), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "adoption-token.dat");
+ if (switch_file_exists(tokenpath, NULL) != SWITCH_STATUS_SUCCESS) {
+ // generate first time uuid
+ ks_uuid_t uuid;
+ const char *token;
+ FILE *fp = fopen(tokenpath, "w");
+ if (!fp) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open %s to save SignalWire connection token\n", tokenpath);
+ status = SWITCH_STATUS_TERM;
+ goto done;
+ }
+
+ ks_uuid(&uuid);
+ token = ks_uuid_str(NULL, &uuid);
+
+ fputs(token, fp);
+ fclose(fp);
+
+ strncpy(globals.adoption_token, token, sizeof(globals.adoption_token) - 1);
+
+ ks_pool_free(&token);
+ } else {
+ char token[64];
+ FILE *fp = fopen(tokenpath, "r");
+ if (!fp) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open %s to read SignalWire connection token\n", tokenpath);
+ status = SWITCH_STATUS_TERM;
+ goto done;
+ }
+ if (!fgets(token, sizeof(token), fp)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read SignalWire connection token from %s\n", tokenpath);
+ fclose(fp);
+ status = SWITCH_STATUS_TERM;
+ goto done;
+ }
+ fclose(fp);
+
+ // trim newline markers in case they exist, only want the token
+ for (size_t len = strlen(token); len > 0 && (token[len - 1] == '\r' || token[len - 1] == '\n'); --len) {
+ token[len - 1] = '\0';
+ }
+
+ strncpy(globals.adoption_token, token, sizeof(globals.adoption_token) - 1);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
+ "\n /=====================================================================\\\n"
+ "| Connection Token: %s |\n"
+ " \\=====================================================================/\n"
+ " Go to https://signalwire.com to set up your Connector now!\n", globals.adoption_token);
+
+done:
+ return status;
+}
+
+static switch_status_t load_config()
+{
+ char *cf = "signalwire.conf";
+ switch_xml_t cfg, xml;
+ const char *data;
+
+ globals.ssl_verify = 1;
+ switch_set_string(globals.blade_bootstrap, "edge..signalwire.com/api/relay/wss");
+ switch_set_string(globals.adoption_service, "https://adopt.signalwire.com/adoption");
+ switch_set_string(globals.stun_server, "stun.freeswitch.org");
+
+ if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "open of %s failed\n", cf);
+ // don't need the config
+ } else {
+ switch_xml_t settings, param, tmp;
+ if ((settings = switch_xml_child(cfg, "settings"))) {
+ for (param = switch_xml_child(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, "kslog") && !ks_zstr(val)) {
+ if (!strcmp(val, "off")) {
+ globals.kslog_on = KS_FALSE;
+ } else if (!strcmp(val, "on")) {
+ globals.kslog_on = KS_TRUE;
+ }
+ } else if (!strcasecmp(var, "blade-bootstrap") && !ks_zstr(val)) {
+ switch_set_string(globals.blade_bootstrap, val);
+ } else if (!strcasecmp(var, "adoption-service") && !ks_zstr(val)) {
+ switch_set_string(globals.adoption_service, val);
+ } else if (!strcasecmp(var, "stun-server") && !ks_zstr(val)) {
+ switch_set_string(globals.stun_server, val);
+ } else if (!strcasecmp(var, "ssl-verify")) {
+ globals.ssl_verify = switch_true(val) ? 1 : 0;
+ }
+ }
+ if ((tmp = switch_xml_child(settings, "authentication"))) {
+ const char *txt = switch_xml_txt(tmp);
+ if (!ks_zstr(txt)) {
+ swclt_config_set_authentication(globals.config, txt);
+ }
+ }
+ }
+ switch_xml_free(xml);
+ }
+
+ if ((data = getenv("SW_BLADE_BOOTSTRAP"))) {
+ switch_set_string(globals.blade_bootstrap, data);
+ }
+
+ if ((data = getenv("SW_ADOPTION_SERVICE"))) {
+ strncpy(globals.adoption_service, data, sizeof(globals.adoption_service));
+ }
+
+ swclt_config_load_from_env(globals.config);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static ks_status_t load_credentials(void)
+{
+ ks_status_t status = KS_STATUS_SUCCESS;
+ char authpath[1024];
+ char data[2048];
+ FILE *fp = NULL;
+ ks_json_t *json = NULL;
+
+ switch_snprintf(authpath, sizeof(authpath), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "adoption-auth.dat");
+ if (!(fp = fopen(authpath, "r"))) goto done;
+
+ if (!fgets(data, sizeof(data), fp)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read SignalWire authentication data from %s\n", authpath);
+ fclose(fp);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+ fclose(fp);
+
+ json = ks_json_parse(data);
+ if (!json) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to parse SignalWire authentication data from %s\n", authpath);
+ status = KS_STATUS_FAIL;
+ goto done;
+ }
+ status = load_credentials_from_json(json);
+ ks_json_delete(&json);
+
+done:
+ return status;
+}
+
+static void mod_signalwire_session_auth_failed_handler(swclt_sess_t sess)
+{
+ char path[1024];
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire authentication failed\n");
+
+ switch_snprintf(path, sizeof(path), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "adoption-auth.dat");
+ unlink(path);
+
+ switch_snprintf(path, sizeof(path), "%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR, "signalwire-conf.dat");
+ unlink(path);
+
+ globals.restarting = KS_TRUE;
+
+ globals.adoption_backoff = 0;
+ globals.adoption_next = 0;
+
+ globals.state = SW_STATE_ADOPTION;
+}
+
+/* Dialplan INTERFACE */
+SWITCH_STANDARD_DIALPLAN(dialplan_hunt)
+{
+ switch_caller_extension_t *extension = NULL;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ const char *network_ip = switch_channel_get_variable(channel, "sip_network_ip");
+ const char *network_port = switch_channel_get_variable(channel, "sip_network_port");
+
+ if (!caller_profile) {
+ if (!(caller_profile = switch_channel_get_caller_profile(channel))) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error obtaining caller profile!\n");
+ goto done;
+ }
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in context %s\n",
+ caller_profile->caller_id_name, caller_profile->caller_id_number, caller_profile->destination_number, caller_profile->context);
+
+ if ((extension = switch_caller_extension_new(session, "signalwire", caller_profile->destination_number)) == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Memory Error!\n");
+ goto done;
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "call from %s:%s\n", network_ip, network_port);
+
+ switch_mutex_lock(globals.mutex);
+
+ if (network_ip &&
+ !zstr_buf(globals.gateway_ip) && !strcmp(globals.gateway_ip, network_ip)) {
+ // good to go
+ char transfer_to[1024];
+
+ switch_snprintf(transfer_to, sizeof(transfer_to), "%s %s %s", caller_profile->destination_number, "XML", caller_profile->context);
+ switch_caller_extension_add_application(session, extension, "transfer", transfer_to);
+ } else {
+ switch_caller_extension_add_application(session, extension, "respond", "500");
+ }
+
+ switch_mutex_unlock(globals.mutex);
+
+done:
+ return extension;
+}
+
+/**
+ * Module load or unload callback from core
+ * @param event the event
+ */
+static void on_module_load_unload(switch_event_t *event)
+{
+ const char *type = switch_event_get_header(event, "type");
+ const char *name = switch_event_get_header(event, "name");
+ if (!zstr(type) && !zstr(name) && !strcmp(type, "endpoint") && !strcmp(name, "sofia")) {
+ globals.profile_reload = KS_TRUE;
+ }
+}
+
+/**
+ * Sofia sofia::gateway_state change callback
+ * @param event the event
+ */
+static void on_sofia_gateway_state(switch_event_t *event)
+{
+ const char *ip = switch_event_get_header(event, "Register-Network-IP");
+ const char *port = switch_event_get_header(event, "Register-Network-Port");
+ const char *state = switch_event_get_header(event, "State");
+ const char *gateway = switch_event_get_header(event, "Gateway");
+
+ if (!ip || !port || !state || !gateway) {
+ return;
+ }
+
+ if (!strcmp(gateway, "signalwire")) {
+ switch_mutex_lock(globals.mutex);
+
+ if (!strcmp(state, "REGED")) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SignalWire SIP Gateway registered to %s:%s\n", ip, port);
+ switch_set_string(globals.gateway_ip, ip);
+ switch_set_string(globals.gateway_port, port);
+ } else if (!strcmp(state, "NOREG")) {
+ globals.gateway_ip[0] = '\0';
+ globals.gateway_port[0] = '\0';
+ }
+
+ switch_mutex_unlock(globals.mutex);
+ }
+}
+
+/* Macro expands to: switch_status_t mod_signalwire_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
+SWITCH_MODULE_LOAD_FUNCTION(mod_signalwire_load)
+{
+ switch_api_interface_t *api_interface = NULL;
+ switch_dialplan_interface_t *dialplan_interface;
+ const char *kslog_env = NULL;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ memset(&globals, 0, sizeof(globals));
+
+ kslog_env = getenv("KSLOG");
+ if (kslog_env && kslog_env[0] && kslog_env[0] != '0') globals.kslog_on = KS_TRUE;
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ ks_global_set_logger(mod_signalwire_kslogger);
+
+ SWITCH_ADD_API(api_interface, "signalwire", "SignalWire API", mod_signalwire_api_function, SIGNALWIRE_SYNTAX);
+ switch_console_set_complete("add signalwire debug");
+ switch_console_set_complete("add signalwire debug 1");
+ switch_console_set_complete("add signalwire debug 2");
+ switch_console_set_complete("add signalwire debug 3");
+ switch_console_set_complete("add signalwire debug 4");
+ switch_console_set_complete("add signalwire debug 5");
+ switch_console_set_complete("add signalwire debug 6");
+ switch_console_set_complete("add signalwire debug 7");
+ switch_console_set_complete("add signalwire kslog");
+ switch_console_set_complete("add signalwire kslog on");
+ switch_console_set_complete("add signalwire kslog off");
+ switch_console_set_complete("add signalwire token");
+ switch_console_set_complete("add signalwire adoption");
+ switch_console_set_complete("add signalwire adopted");
+ switch_console_set_complete("add signalwire update");
+ switch_console_set_complete("add signalwire reload");
+
+ switch_xml_bind_search_function(xml_config_handler, SWITCH_XML_SECTION_CONFIG, NULL);
+
+ ks_ssl_init_skip(KS_TRUE);
+
+ swclt_init(KS_LOG_LEVEL_DEBUG);
+
+ if (globals.kslog_on == KS_FALSE) {
+ ks_global_set_logger(NULL);
+ } else {
+ ks_global_set_logger(mod_signalwire_kslogger);
+ }
+
+ // Configuration
+ swclt_config_create(&globals.config);
+ load_config();
+
+ switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool);
+
+ switch_event_bind("mod_signalwire", SWITCH_EVENT_MODULE_LOAD, NULL, on_module_load_unload, NULL);
+ switch_event_bind("mod_signalwire", SWITCH_EVENT_MODULE_UNLOAD, NULL, on_module_load_unload, NULL);
+ switch_event_bind("mod_signalwire", SWITCH_EVENT_CUSTOM, "sofia::gateway_state", on_sofia_gateway_state, NULL);
+
+ SWITCH_ADD_DIALPLAN(dialplan_interface, "signalwire", dialplan_hunt);
+
+ // Load credentials if they exist from a prior adoption
+ load_credentials();
+
+ // SignalWire
+ swclt_sess_create(&globals.signalwire_session,
+ globals.blade_bootstrap,
+ globals.config);
+ swclt_sess_set_auth_failed_cb(globals.signalwire_session, mod_signalwire_session_auth_failed_handler);
+
+ if (!globals.signalwire_session) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "signalwire_session create error\n");
+ switch_goto_status(SWITCH_STATUS_TERM, err);
+ }
+
+ swclt_hmon_register(&globals.signalwire_session_monitor, globals.signalwire_session, mod_signalwire_session_state_handler, NULL);
+
+ // @todo register nodestore callbacks here if needed
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Welcome to\n"
+ " _____ _ ___ ___\n"
+ " / ___/(_)___ _____ ____ _/ / | / (_)_______\n"
+ " \\__ \\/ / __ `/ __ \\/ __ `/ /| | /| / / / ___/ _ \\\n"
+ " ___/ / / /_/ / / / / /_/ / / | |/ |/ / / / / __/\n"
+ " /____/_/\\__, /_/ /_/\\__,_/_/ |__/|__/_/_/ \\___/\n"
+ " /____/\n");
+
+ // storage_dir was missing in clean install
+ switch_dir_make_recursive(SWITCH_GLOBAL_dirs.storage_dir, SWITCH_DEFAULT_DIR_PERMS, pool);
+
+ if ((status = mod_signalwire_load_or_generate_token()) != SWITCH_STATUS_SUCCESS) {
+ goto err;
+ }
+
+ if (swclt_sess_has_authentication(globals.signalwire_session)) {
+ // Load cached profile if we already have one. We'll still connect to SignalWire and
+ // fetch a new profile in the background.
+ load_sip_config();
+ if (globals.signalwire_profile) {
+ globals.state = SW_STATE_START_PROFILE;
+ } else {
+ globals.state = SW_STATE_OFFLINE;
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Connecting to SignalWire\n");
+ swclt_sess_connect(globals.signalwire_session);
+ } else {
+ globals.state = SW_STATE_ADOPTION;
+ }
+
+ goto done;
+
+err:
+ if (globals.signalwire_session) ks_handle_destroy(&globals.signalwire_session);
+ swclt_config_destroy(&globals.config);
+ ks_global_set_logger(NULL);
+
+done:
+
+ return status;
+}
+
+/*
+ Called when the system shuts down
+ Macro expands to: switch_status_t mod_signalwire_shutdown() */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_signalwire_shutdown)
+{
+ /* Cleanup dynamically allocated config settings */
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Disconnecting from SignalWire\n");
+
+ switch_event_unbind_callback(on_module_load_unload);
+ switch_event_unbind_callback(on_sofia_gateway_state);
+
+ // stop things that might try to use blade or kafka while they are shutting down
+ globals.shutdown = KS_TRUE;
+
+ swclt_sess_disconnect(globals.signalwire_session);
+ while (swclt_hstate_current_get(globals.signalwire_session) == SWCLT_HSTATE_ONLINE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sleeping for pending disconnect\n");
+ ks_sleep_ms(1000);
+ }
+
+ //signalwire_dialplan_shutdown();
+ // @todo signalwire profile unbinding and unloading
+ switch_xml_unbind_search_function_ptr(xml_config_handler);
+
+ // kill signalwire, so nothing more can come into the system
+ ks_handle_destroy(&globals.signalwire_session);
+
+ // cleanup config
+ swclt_config_destroy(&globals.config);
+
+ // shutdown libblade (but not libks?)
+ swclt_shutdown();
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void mod_signalwire_state_adoption(void)
+{
+ // keep trying to check adoption token for authentication
+ if (ks_time_now() >= globals.adoption_next) {
+ // Use a very very simple backoff algorithm, every time we try, backoff another minute
+ // so that after first try we wait 1 minute, after next try we wait 2 minutes, at third
+ // try we are waiting 3 minutes, upto a max backoff of 15 minutes between adoption checks
+ if (globals.adoption_backoff < 15) globals.adoption_backoff++;
+ globals.adoption_next = ks_time_now() + (globals.adoption_backoff * 60 * KS_USEC_PER_SEC);
+ if (mod_signalwire_adoption_post() != KS_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Next SignalWire adoption check in %"SWITCH_SIZE_T_FMT" minutes\n", globals.adoption_backoff);
+ }
+ }
+ if (globals.signalwire_reconnected) {
+ // OK to continue as is
+ globals.signalwire_reconnected = KS_FALSE;
+ }
+}
+
+static void mod_signalwire_state_offline(void)
+{
+ if (globals.signalwire_reconnected) {
+ globals.signalwire_reconnected = KS_FALSE;
+ globals.state = SW_STATE_ONLINE;
+ }
+}
+
+static void mod_signalwire_state_online(void)
+{
+ globals.signalwire_reconnected = KS_FALSE;
+ if (!swclt_sess_provisioning_setup(globals.signalwire_session, __on_provisioning_events, NULL)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to SignalWire\n");
+ globals.state = SW_STATE_CONFIGURE;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Failed to connect to SignalWire\n");
+ ks_sleep_ms(4000);
+ globals.state = SW_STATE_OFFLINE;
+ globals.restarting = KS_TRUE;
+ }
+}
+
+static void mod_signalwire_state_configure(void)
+{
+ switch_memory_pool_t *pool = NULL;
+ char local_ip[256];
+ switch_port_t local_port = 6050;
+ char local_endpoint[256];
+ char *external_ip;
+ switch_port_t external_port;
+ char external_endpoint[256];
+ char *error = NULL;
+ swclt_cmd_t cmd;
+
+ if (globals.signalwire_reconnected) {
+ globals.signalwire_reconnected = KS_FALSE;
+ globals.state = SW_STATE_ONLINE;
+ }
+
+ // already restarting/updating...
+ globals.profile_reload = KS_FALSE;
+ globals.profile_update = KS_FALSE;
+
+ if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SignalWire configure failed: could not allocate memory pool\n");
+ goto done;
+ }
+
+ switch_find_local_ip(local_ip, sizeof(local_ip), NULL, AF_INET);
+
+ if (switch_find_available_port(&local_port, local_ip, SOCK_STREAM) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire configure failed: could not get available local port\n");
+ ks_sleep_ms(4000);
+ goto done;
+ }
+
+ snprintf(local_endpoint, sizeof(local_endpoint), "%s:%u", local_ip, local_port);
+
+ external_ip = local_ip;
+ external_port = local_port;
+
+ if (switch_stun_lookup(&external_ip, &external_port, globals.stun_server, SWITCH_STUN_DEFAULT_PORT, &error, pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SignalWire configure failed: stun [%s] lookup error: %s\n", globals.stun_server, error);
+ ks_sleep_ms(4000);
+ goto done;
+ }
+
+ snprintf(external_endpoint, sizeof(external_endpoint), "%s:%u", external_ip, external_port);
+
+ if (!swclt_sess_provisioning_configure(globals.signalwire_session, "freeswitch", local_endpoint, external_endpoint, globals.relay_connector_id, &cmd)) {
+ SWCLT_CMD_TYPE cmd_type;
+ swclt_cmd_type(cmd, &cmd_type);
+ if (cmd_type == SWCLT_CMD_TYPE_RESULT) {
+ const ks_json_t *result;
+ signalwire_provisioning_configure_response_t *configure_res;
+
+ swclt_cmd_result(cmd, &result);
+ result = ks_json_get_object_item(result, "result");
+ if (!SIGNALWIRE_PROVISIONING_CONFIGURE_RESPONSE_PARSE(ks_handle_pool(cmd), result, &configure_res)) {
+ const ks_json_t *configuration = configure_res->configuration;
+ const char *configuration_profile = ks_json_get_object_cstr(configuration, "profile");
+ if (globals.signalwire_profile) {
+ switch_xml_free(globals.signalwire_profile);
+ globals.signalwire_profile = NULL;
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\"%s\"\n", configuration_profile);
+ globals.signalwire_profile = switch_xml_parse_str_dynamic((char *)configuration_profile, SWITCH_TRUE);
+ if (!globals.signalwire_profile) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to parse configuration profile\n");
+ } else {
+ char digest[SWITCH_MD5_DIGEST_STRING_SIZE] = { 0 };
+ switch_md5_string(digest, (void *) configuration_profile, strlen(configuration_profile));
+ save_sip_config(configuration_profile);
+ if (!signalwire_gateway_exists() || zstr_buf(globals.signalwire_profile_md5) || strcmp(globals.signalwire_profile_md5, digest)) {
+ // not registered or new profile - update md5 and load it
+ strcpy(globals.signalwire_profile_md5, digest);
+ globals.state = SW_STATE_START_PROFILE;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "profile MD5 = \"%s\"\n", globals.signalwire_profile_md5);
+ } else {
+ // already registered
+ globals.state = SW_STATE_READY;
+ }
+ }
+ }
+ }
+ }
+ ks_handle_destroy(&cmd);
+ if (globals.state == SW_STATE_CONFIGURE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to receive valid configuration from SignalWire\n");
+ ks_sleep_ms(4000);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Received configuration from SignalWire\n");
+ }
+
+done:
+ if (pool) switch_core_destroy_memory_pool(&pool);
+}
+
+static int signalwire_gateway_exists(void)
+{
+ int exists = 0;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ if (switch_api_execute("sofia", "profile signalwire gwlist", NULL, &stream) == SWITCH_STATUS_SUCCESS && stream.data) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "gwlist = \"%s\"\n", (char *)stream.data);
+ exists = (strstr((char *)stream.data, "Invalid Profile") == NULL) &&
+ (strstr((char*)stream.data, "signalwire") != NULL);
+ }
+ switch_safe_free(stream.data);
+ return exists;
+}
+
+static int signalwire_profile_is_started(void)
+{
+ int started = 0;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ if (switch_api_execute("sofia", "status profile signalwire", NULL, &stream) == SWITCH_STATUS_SUCCESS && stream.data) {
+ started = (strstr((char *)stream.data, "Invalid Profile") == NULL) &&
+ (strstr((char *)stream.data, "signalwire") != NULL);
+ }
+ switch_safe_free(stream.data);
+ return started;
+}
+
+static int signalwire_profile_rescan(void)
+{
+ int success = 0;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ if (switch_api_execute("sofia", "profile signalwire rescan", NULL, &stream) == SWITCH_STATUS_SUCCESS) {
+ success = signalwire_profile_is_started();
+ }
+ switch_safe_free(stream.data);
+ return success;
+}
+
+static int signalwire_profile_start(void)
+{
+ int success = 0;
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ if (switch_api_execute("sofia", "profile signalwire start", NULL, &stream) == SWITCH_STATUS_SUCCESS) {
+ success = signalwire_profile_is_started();
+ }
+ switch_safe_free(stream.data);
+ return success;
+}
+
+static void signalwire_profile_killgw(void)
+{
+ switch_stream_handle_t stream = { 0 };
+ SWITCH_STANDARD_STREAM(stream);
+ switch_api_execute("sofia", "profile signalwire killgw signalwire", NULL, &stream);
+ switch_safe_free(stream.data);
+}
+
+static void mod_signalwire_state_start_profile(void)
+{
+ if (globals.profile_update) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire SIP profile update initiated\n");
+ globals.state = SW_STATE_CONFIGURE;
+ globals.profile_update = KS_FALSE;
+ return;
+ }
+ globals.profile_reload = KS_FALSE; // already here
+
+ // ignore SignalWire reconnections until register is attempted
+
+ if (switch_loadable_module_exists("mod_sofia") != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for mod_sofia to load\n");
+ } else if (signalwire_profile_is_started()) {
+ // kill gateway if already up and rescan the profile
+ if (signalwire_gateway_exists()) {
+ signalwire_profile_killgw();
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SignalWire SIP gateway killed\n");
+ }
+ if (signalwire_profile_rescan()) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SignalWire SIP profile rescanned\n");
+ globals.state = SW_STATE_REGISTER;
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting SignalWire SIP profile\n");
+ signalwire_profile_start(); // assume success - it gets checked in next state
+ globals.state = SW_STATE_REGISTER;
+ }
+}
+
+static void mod_signalwire_state_register(void)
+{
+ if (globals.profile_update) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire SIP profile update initiated\n");
+ globals.state = SW_STATE_CONFIGURE;
+ globals.profile_update = KS_FALSE;
+ return;
+ } else if (globals.profile_reload) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire SIP profile reload initiated\n");
+ globals.state = SW_STATE_START_PROFILE;
+ globals.profile_reload = KS_FALSE;
+ return;
+ }
+ // ignore SignalWire reconnections until register is attempted
+
+ if (switch_loadable_module_exists("mod_sofia") != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for mod_sofia to load\n");
+ globals.state = SW_STATE_START_PROFILE;
+ } else if (signalwire_gateway_exists() || signalwire_profile_rescan()) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SignalWire SIP gateway started\n");
+ globals.state = SW_STATE_READY;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to start SignalWire SIP gateway\n");
+ globals.state = SW_STATE_CONFIGURE;
+ ks_sleep_ms(5000);
+ }
+}
+
+static void mod_signalwire_state_ready()
+{
+ if (globals.profile_update) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Signalwire SIP profile update initiated\n");
+ globals.state = SW_STATE_CONFIGURE;
+ globals.profile_update = KS_FALSE;
+ } else if (globals.profile_reload) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SignalWire SIP profile reload initiated\n");
+ globals.state = SW_STATE_START_PROFILE;
+ globals.profile_reload = KS_FALSE;
+ } else if (globals.signalwire_reconnected) {
+ globals.signalwire_reconnected = KS_FALSE;
+ globals.state = SW_STATE_ONLINE;
+ }
+}
+
+SWITCH_MODULE_RUNTIME_FUNCTION(mod_signalwire_runtime)
+{
+ while (!globals.shutdown) {
+ if (globals.restarting) {
+ swclt_sess_disconnect(globals.signalwire_session);
+ while (swclt_hstate_current_get(globals.signalwire_session) == SWCLT_HSTATE_ONLINE) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sleeping for pending disconnect\n");
+ ks_sleep_ms(1000);
+ }
+
+ // kill signalwire, so nothing more can come into the system
+ ks_handle_destroy(&globals.signalwire_session);
+
+ // Create a new session and start over
+ swclt_sess_create(&globals.signalwire_session,
+ globals.blade_bootstrap,
+ globals.config);
+ swclt_sess_set_auth_failed_cb(globals.signalwire_session, mod_signalwire_session_auth_failed_handler);
+
+ swclt_hmon_register(&globals.signalwire_session_monitor, globals.signalwire_session, mod_signalwire_session_state_handler, NULL);
+
+ globals.restarting = KS_FALSE;
+ continue;
+ }
+
+ switch(globals.state) {
+ case SW_STATE_ADOPTION: // waiting for adoption to occur
+ mod_signalwire_state_adoption();
+ break;
+ case SW_STATE_OFFLINE: // waiting for session to go online
+ mod_signalwire_state_offline();
+ break;
+ case SW_STATE_ONLINE: // provisioning service setup
+ mod_signalwire_state_online();
+ break;
+ case SW_STATE_CONFIGURE: // provisioning configuration
+ mod_signalwire_state_configure();
+ break;
+ case SW_STATE_START_PROFILE:
+ mod_signalwire_state_start_profile();
+ break;
+ case SW_STATE_REGISTER:
+ mod_signalwire_state_register();
+ break;
+ case SW_STATE_READY: // ready for runtime
+ mod_signalwire_state_ready();
+ break;
+ default: break;
+ }
+ ks_sleep_ms(1000);
+ }
+
+ return SWITCH_STATUS_TERM;
+}
+
+/* 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 noet
+ */
diff --git a/w32/Setup/Setup.2017.wixproj b/w32/Setup/Setup.2017.wixproj
index 0017c5acef..cb65aa947c 100644
--- a/w32/Setup/Setup.2017.wixproj
+++ b/w32/Setup/Setup.2017.wixproj
@@ -293,6 +293,14 @@
Binaries;Content;Satellites
INSTALLFOLDER
+
+ mod_signalwire
+ {b19ae6fc-bfff-428d-b483-3bbeaeccc618}
+ True
+ True
+ Binaries;Content;Satellites
+ INSTALLFOLDER
+
mod_sms
{2469b306-b027-4ff2-8815-c9c1ea2cae79}
diff --git a/w32/libks-version.props b/w32/libks-version.props
new file mode 100644
index 0000000000..3b9fe4b776
--- /dev/null
+++ b/w32/libks-version.props
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ 0.1.1
+
+
+ true
+
+
+
+
+
+ $(libksVersion)
+
+
+
diff --git a/w32/libks.props b/w32/libks.props
new file mode 100644
index 0000000000..c2e2565172
--- /dev/null
+++ b/w32/libks.props
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+ true
+
+
+
+ $(BaseDir)libs\libks-$(libksVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(libksDir)\src\include;%(AdditionalIncludeDirectories)
+ __PRETTY_FUNCTION__=__FUNCSIG__;WIN32;_WINDOWS;SWCLT_VERSION_MAJOR=1;SWCLT_VERSION_MINOR=0;SWCLT_VERSION_REVISION=0;_WIN32_WINNT=0x0600;_WINSOCK_DEPRECATED_NO_WARNINGS=1;WIN32_LEAN_AND_MEAN=1;KS_PLAT_WIN=1;NOMAXMIN=1;_CRT_SECURE_NO_WARNINGS=1;SWCLT_EXPORTS;%(PreprocessorDefinitions)
+
+
+ $(libksDir)\binaries\$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories)
+ ks.lib;%(AdditionalDependencies)
+
+
+
diff --git a/w32/signalwire-client-c-version.props b/w32/signalwire-client-c-version.props
new file mode 100644
index 0000000000..0b0de97606
--- /dev/null
+++ b/w32/signalwire-client-c-version.props
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ 1.0.0
+
+
+ true
+
+
+
+
+
+ $(signalwire-client-cVersion)
+
+
+
diff --git a/w32/signalwire-client-c.props b/w32/signalwire-client-c.props
new file mode 100644
index 0000000000..e7e40e2c4a
--- /dev/null
+++ b/w32/signalwire-client-c.props
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+ true
+
+
+
+ $(BaseDir)libs\signalwire-client-c-$(signalwire-client-cVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(signalwire-client-cDir)\include;%(AdditionalIncludeDirectories)
+ %(PreprocessorDefinitions)
+
+
+ $(signalwire-client-cDir)\binaries\$(Platform)\$(Configuration)\;%(AdditionalLibraryDirectories)
+ signalwire_client.lib;%(AdditionalDependencies)
+
+
+